diff --git a/.zuul.yaml b/.zuul.yaml
index b48a96c45d762e2165b74427fbef35bdf7377a89..7002d002bf72fd4fd95e25ecfa24b82ce30d1c08 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -112,6 +112,7 @@
         user_role
         volume
         volume_backup
+        volume_snapshot
       # failing tags
       # neutron_rbac
 
diff --git a/ci/roles/volume/tasks/main.yml b/ci/roles/volume/tasks/main.yml
index fa4a54efd04d23311b5032cefa5cbefd83a2235c..604cdd28eedc1e85610ea30c4a55f363f59302d6 100644
--- a/ci/roles/volume/tasks/main.yml
+++ b/ci/roles/volume/tasks/main.yml
@@ -20,53 +20,18 @@
      volume: "{{ vol.volume.id }}"
      name: ansible_volume1
      description: Test volume
-  register: vol
-
-- name: Create volume snapshot
-  openstack.cloud.volume_snapshot:
-    cloud: "{{ cloud }}"
-    state: present
-    name: ansible_volume_snapshot
-    volume: ansible_volume
-  register: vol_snap
-
-- name: Get snapshot info
-  openstack.cloud.volume_snapshot_info:
-    cloud: "{{ cloud }}"
-    name: ansible_volume_snapshot
-  register: snap_info
-
-- name: Create volume from snapshot
-  openstack.cloud.volume:
-     cloud: "{{ cloud }}"
-     state: present
-     size: 1
-     snapshot: ansible_volume_snapshot
-     name: ansible_volume2
-     description: Test volume
-  register: vol
-
-- name: Delete volume snapshot
-  openstack.cloud.volume_snapshot:
-    cloud: "{{ cloud }}"
-    name: ansible_volume_snapshot
-    volume: ansible_volume
-    state: absent
 
 - name: Delete volume
   openstack.cloud.volume:
      cloud: "{{ cloud }}"
      state: absent
-     name: ansible_volume
+     name: ansible_volume1
 
-- name: Clean up
+- name: Delete volume
   openstack.cloud.volume:
      cloud: "{{ cloud }}"
      state: absent
-     name: "{{ item }}"
-  loop:
-    - ansible_volume1
-    - ansible_volume2
+     name: ansible_volume
 
 - name: Test images
   block:
diff --git a/ci/roles/volume_snapshot/defaults/main.yml b/ci/roles/volume_snapshot/defaults/main.yml
new file mode 100644
index 0000000000000000000000000000000000000000..f26281689addb9b5271809024ee40295a14763a9
--- /dev/null
+++ b/ci/roles/volume_snapshot/defaults/main.yml
@@ -0,0 +1,13 @@
+expected_fields:
+  - created_at
+  - description
+  - id
+  - is_forced
+  - metadata
+  - name
+  - progress
+  - project_id
+  - size
+  - status
+  - updated_at
+  - volume_id
diff --git a/ci/roles/volume_snapshot/tasks/main.yml b/ci/roles/volume_snapshot/tasks/main.yml
new file mode 100644
index 0000000000000000000000000000000000000000..05c10685a5ca26f2a48b2ffb2e99670616f48036
--- /dev/null
+++ b/ci/roles/volume_snapshot/tasks/main.yml
@@ -0,0 +1,96 @@
+---
+- name: Get existing snapshots
+  openstack.cloud.volume_snapshot_info:
+    cloud: "{{ cloud }}"
+  register: info
+
+- name: Assert volume_snapshot_info
+  assert:
+    that:
+      - info.volume_snapshots|length == 0
+
+- name: Get non-existing snapshot
+  openstack.cloud.volume_snapshot_info:
+    cloud: "{{ cloud }}"
+    name: non-existing-snapshot
+  register: info
+
+- name: Assert volume_snapshot_info
+  assert:
+    that:
+      - info.volume_snapshots|length == 0
+
+- name: Create volume
+  openstack.cloud.volume:
+     cloud: "{{ cloud }}"
+     state: present
+     size: 1
+     name: ansible_volume
+     description: Test volume
+  register: volume
+
+- name: Create volume snapshot
+  openstack.cloud.volume_snapshot:
+    cloud: "{{ cloud }}"
+    state: present
+    name: ansible_volume_snapshot
+    volume: ansible_volume
+  register: snapshot
+
+- name: Assert volume_snapshot
+  assert:
+    that:
+      - snapshot.volume_snapshot.name == "ansible_volume_snapshot"
+
+- name: Assert return values of volume_snapshot module
+  assert:
+    that:
+      # allow new fields to be introduced but prevent fields from being removed
+      - expected_fields|difference(snapshot.volume_snapshot.keys())|length == 0
+
+- name: Get snapshot info
+  openstack.cloud.volume_snapshot_info:
+    cloud: "{{ cloud }}"
+    name: ansible_volume_snapshot
+  register: info
+
+- name: Assert volume_snapshot_info
+  assert:
+    that:
+      - info.volume_snapshots|length == 1
+      - info.volume_snapshots[0].id == snapshot.volume_snapshot.id
+      - info.volume_snapshots[0].volume_id == volume.volume.id
+
+- name: Assert return values of volume_info module
+  assert:
+    that:
+      # allow new fields to be introduced but prevent fields from being removed
+      - expected_fields|difference(info.volume_snapshots[0].keys())|length == 0
+
+- name: Create volume from snapshot
+  openstack.cloud.volume:
+     cloud: "{{ cloud }}"
+     state: present
+     size: 1
+     snapshot: ansible_volume_snapshot
+     name: ansible_volume2
+     description: Test volume
+
+- name: Delete volume snapshot
+  openstack.cloud.volume_snapshot:
+    cloud: "{{ cloud }}"
+    name: ansible_volume_snapshot
+    volume: ansible_volume
+    state: absent
+
+- name: Delete volume
+  openstack.cloud.volume:
+     cloud: "{{ cloud }}"
+     state: absent
+     name: ansible_volume2
+
+- name: Delete volume
+  openstack.cloud.volume:
+     cloud: "{{ cloud }}"
+     state: absent
+     name: ansible_volume
diff --git a/ci/run-collection.yml b/ci/run-collection.yml
index d02bc480cdfe4398d27508f86dcde5e4b67df10a..508cedcc62ce4a9b3a654263ac70ae1abe73a5c7 100644
--- a/ci/run-collection.yml
+++ b/ci/run-collection.yml
@@ -66,6 +66,7 @@
     - { role: user_role, tags: user_role }
     - { role: volume, tags: volume }
     - { role: volume_backup, tags: volume_backup }
+    - { role: volume_snapshot, tags: volume_snapshot }
     - role: loadbalancer
       tags: loadbalancer
     - { role: quota, tags: quota }
diff --git a/plugins/modules/volume_snapshot.py b/plugins/modules/volume_snapshot.py
index 1f4349952fb06535d3a1f19d3cbfc06b18b2b74d..9837f0f8534800a27e633207b6c8f01a90e5671e 100644
--- a/plugins/modules/volume_snapshot.py
+++ b/plugins/modules/volume_snapshot.py
@@ -4,7 +4,7 @@
 # Copyright (c) 2016, Mario Santos <mario.rf.santos@gmail.com>
 # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
 
-DOCUMENTATION = '''
+DOCUMENTATION = r'''
 ---
 module: volume_snapshot
 short_description: Create/Delete Cinder Volume Snapshots
@@ -12,75 +12,109 @@ author: OpenStack Ansible SIG
 description:
    - Create or Delete cinder block storage volume snapshots
 options:
-   display_name:
-     description:
-        - Name of the snapshot
-     required: true
-     aliases: ['name']
-     type: str
-   display_description:
-     description:
-       - String describing the snapshot
-     aliases: ['description']
-     type: str
-   volume:
-     description:
-       - The volume name or id to create/delete the snapshot
-     required: True
-     type: str
-   force:
-     description:
-       - Allows or disallows snapshot of a volume to be created when the volume
-         is attached to an instance.
-     type: bool
-     default: 'no'
-   state:
-     description:
-       - Should the resource be present or absent.
-     choices: [present, absent]
-     default: present
-     type: str
+  description:
+    description:
+      - String describing the snapshot
+    aliases: ['display_description']
+    type: str
+  force:
+    description:
+       - Allows or disallows snapshot of a volume to be created,
+         when the volume is attached to an instance.
+    type: bool
+    default: 'no'
+  name:
+    description:
+      - Name of the snapshot
+    required: true
+    aliases: ['display_name']
+    type: str
+  state:
+    description:
+      - Should the snapshot be C(present) or C(absent).
+    choices: [present, absent]
+    default: present
+    type: str
+  volume:
+    description:
+      - Volume name or ID to create the snapshot from.
+      - Required when I(state) is C(present).
+    type: str
 requirements:
     - "python >= 3.6"
     - "openstacksdk"
 
+notes:
+    - Updating existing volume snapshots has not been implemented yet.
+
 extends_documentation_fragment:
 - openstack.cloud.openstack
 '''
 
-EXAMPLES = '''
-# Creates a snapshot on volume 'test_volume'
-- name: create and delete snapshot
-  hosts: localhost
-  tasks:
-  - name: create snapshot
-    openstack.cloud.volume_snapshot:
-      state: present
-      cloud: mordred
-      availability_zone: az2
-      display_name: test_snapshot
-      volume: test_volume
-  - name: delete snapshot
-    openstack.cloud.volume_snapshot:
-      state: absent
-      cloud: mordred
-      availability_zone: az2
-      display_name: test_snapshot
-      volume: test_volume
+EXAMPLES = r'''
+- name: create snapshot
+  openstack.cloud.volume_snapshot:
+    state: present
+    cloud: mordred
+    name: test_snapshot
+    volume: test_volume
+- name: delete snapshot
+  openstack.cloud.volume_snapshot:
+    state: absent
+    cloud: mordred
+    name: test_snapshot
+    volume: test_volume
 '''
 
-RETURN = '''
+RETURN = r'''
 snapshot:
-    description: The snapshot instance after the change
+    description: Same as C(volume_snapshot), kept for backward compatibility.
+    returned: On success when C(state=present)
+    type: dict
+volume_snapshot:
+    description: The snapshot instance
     returned: success
     type: dict
-    sample:
-      id: 837aca54-c0ee-47a2-bf9a-35e1b4fdac0c
-      name: test_snapshot
-      volume_id: ec646a7c-6a35-4857-b38b-808105a24be6
-      size: 2
-      status: available
-      display_name: test_snapshot
+    contains:
+        created_at:
+            description: Snapshot creation time.
+            type: str
+        description:
+            description: Snapshot desciption.
+            type: str
+        id:
+            description: Unique UUID.
+            type: str
+            sample: "39007a7e-ee4f-4d13-8283-b4da2e037c69"
+        is_forced:
+            description: Indicate whether to create snapshot,
+                         even if the volume is attached.
+            type: bool
+        metadata:
+            description: Snapshot metadata.
+            type: dict
+        name:
+            description: Snapshot Name.
+            type: str
+        progress:
+            description: The percentage of completeness the snapshot is
+                         currently at.
+            type: str
+        project_id:
+            description: The project ID this snapshot is associated with.
+            type: str
+        size:
+            description: The size of the volume, in GBs.
+            type: int
+        status:
+            description: Snapshot status.
+            type: str
+        updated_at:
+            description: Snapshot update time.
+            type: str
+        volume_id:
+            description: Volume ID.
+            type: str
 '''
 
 from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
@@ -88,74 +122,86 @@ from ansible_collections.openstack.cloud.plugins.module_utils.openstack import O
 
 class VolumeSnapshotModule(OpenStackModule):
     argument_spec = dict(
-        display_name=dict(required=True, aliases=['name']),
-        display_description=dict(aliases=['description']),
-        volume=dict(required=True),
+        description=dict(aliases=['display_description']),
+        name=dict(required=True, aliases=['display_name']),
         force=dict(default=False, type='bool'),
         state=dict(default='present', choices=['absent', 'present']),
+        volume=dict(),
     )
 
     module_kwargs = dict(
+        required_if=[
+            ('state', 'present', ['volume'])
+        ],
         supports_check_mode=True
     )
 
-    def _present_volume_snapshot(self):
-        volume = self.conn.get_volume(self.params['volume'])
-        snapshot = self.conn.get_volume_snapshot(
-            self.params['display_name'], filters={'volume_id': volume.id})
-        if not snapshot:
-            snapshot = self.conn.create_volume_snapshot(
-                volume.id,
-                force=self.params['force'],
-                wait=self.params['wait'],
-                timeout=self.params['timeout'],
-                name=self.params['display_name'],
-                description=self.params.get('display_description')
-            )
-            self.exit_json(changed=True, snapshot=snapshot)
-        else:
-            self.exit_json(changed=False, snapshot=snapshot)
-
-    def _absent_volume_snapshot(self):
-        volume = self.conn.get_volume(self.params['volume'])
-        snapshot = self.conn.get_volume_snapshot(
-            self.params['display_name'], filters={'volume_id': volume.id})
-        if not snapshot:
-            self.exit_json(changed=False)
-        else:
-            self.conn.delete_volume_snapshot(
-                name_or_id=snapshot.id,
-                wait=self.params['wait'],
-                timeout=self.params['timeout'],
-            )
-            self.exit_json(changed=True, snapshot_id=snapshot.id)
-
-    def _system_state_change(self):
-        volume = self.conn.get_volume(self.params['volume'])
-        snapshot = self.conn.get_volume_snapshot(
-            self.params['display_name'],
-            filters={'volume_id': volume.id})
+    def run(self):
+        name = self.params['name']
         state = self.params['state']
 
-        if state == 'present':
-            return snapshot is None
-        if state == 'absent':
-            return snapshot is not None
+        snapshot = self.conn.block_storage.find_snapshot(name)
 
-    def run(self):
-        state = self.params['state']
+        if self.ansible.check_mode:
+            self.exit_json(changed=self._will_change(state, snapshot))
+
+        if state == 'present' and not snapshot:
+            snapshot = self._create()
+            self.exit_json(changed=True,
+                           snapshot=snapshot.to_dict(computed=False),
+                           volume_snapshot=snapshot.to_dict(computed=False))
+
+        elif state == 'present' and snapshot:
+            # We do not support snapshot updates yet
+            # TODO: Implement module updates
+            self.exit_json(changed=False,
+                           snapshot=snapshot.to_dict(computed=False),
+                           volume_snapshot=snapshot.to_dict(computed=False))
+
+        elif state == 'absent' and snapshot:
+            self._delete(snapshot)
+            self.exit_json(changed=True)
+
+        else:  # state == 'absent' and not snapshot
+            self.exit_json(changed=False)
 
-        if self.conn.volume_exists(self.params['volume']):
-            if self.ansible.check_mode:
-                self.exit_json(changed=self._system_state_change())
-            if state == 'present':
-                self._present_volume_snapshot()
-            if state == 'absent':
-                self._absent_volume_snapshot()
+    def _create(self):
+        args = dict()
+        for k in ['description', 'force', 'name']:
+            if self.params[k] is not None:
+                args[k] = self.params[k]
+
+        volume_name_or_id = self.params['volume']
+        volume = self.conn.block_storage.find_volume(volume_name_or_id,
+                                                     ignore_missing=False)
+        args['volume_id'] = volume.id
+
+        snapshot = self.conn.block_storage.create_snapshot(**args)
+
+        if self.params['wait']:
+            snapshot = self.conn.block_storage.wait_for_status(
+                snapshot, wait=self.params['timeout'])
+
+        return snapshot
+
+    def _delete(self, snapshot):
+        self.conn.block_storage.delete_snapshot(snapshot)
+        if self.params['wait']:
+            self.conn.block_storage.wait_for_delete(
+                snapshot, wait=self.params['timeout'])
+
+    def _will_change(self, state, snapshot):
+        if state == 'present' and not snapshot:
+            return True
+        elif state == 'present' and snapshot:
+            # We do not support snapshot updates yet
+            # TODO: Implement module updates
+            return False
+        elif state == 'absent' and snapshot:
+            return True
         else:
-            self.fail_json(
-                msg="No volume with name or id '{0}' was found.".format(
-                    self.params['volume']))
+            # state == 'absent' and not snapshot:
+            return False
 
 
 def main():
diff --git a/plugins/modules/volume_snapshot_info.py b/plugins/modules/volume_snapshot_info.py
index 8710dd192365bd0779920981e3017b00f59ccaee..db9a810646d952e53160c9ed454fbf663ff45816 100644
--- a/plugins/modules/volume_snapshot_info.py
+++ b/plugins/modules/volume_snapshot_info.py
@@ -5,7 +5,7 @@
 # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
 
 
-DOCUMENTATION = '''
+DOCUMENTATION = r'''
 ---
 module: volume_snapshot_info
 short_description: Get volume snapshots
@@ -16,32 +16,35 @@ options:
   details:
     description: More detailed output
     type: bool
-    default: True
   name:
     description:
       - Name of the Snapshot.
     type: str
-  volume:
-    description:
-      - Name of the volume.
-    type: str
   status:
     description:
       - Specifies the snapshot status.
-    choices: [creating, available, error, deleting,
-              error_deleting, rollbacking, backing-up]
+    choices: ['available', 'backing-up', 'creating', 'deleted', 'deleting',
+              'error', 'error_deleting', 'restoring', 'unmanaging']
     type: str
-requirements: ["openstacksdk"]
+  volume:
+    description:
+      - Name or ID of the volume.
+    type: str
+
+requirements:
+  - "python >= 3.6"
+  - "openstacksdk"
+
 extends_documentation_fragment:
-- openstack.cloud.openstack
+  - openstack.cloud.openstack
 '''
 
-RETURN = '''
+RETURN = r'''
 volume_snapshots:
     description: List of dictionaries describing volume snapshots.
     type: list
     elements: dict
-    returned: always.
+    returned: always
     contains:
         created_at:
             description: Snapshot creation time.
@@ -53,12 +56,26 @@ volume_snapshots:
             description: Unique UUID.
             type: str
             sample: "39007a7e-ee4f-4d13-8283-b4da2e037c69"
+        is_forced:
+            description: Indicate whether to create snapshot,
+                         even if the volume is attached.
+            type: bool
         metadata:
             description: Snapshot metadata.
             type: dict
         name:
             description: Snapshot Name.
             type: str
+        progress:
+            description: The percentage of completeness the snapshot is
+                         currently at.
+            type: str
+        project_id:
+            description: The project ID this snapshot is associated with.
+            type: str
+        size:
+            description: The size of the volume, in GBs.
+            type: int
         status:
             description: Snapshot status.
             type: str
@@ -68,62 +85,53 @@ volume_snapshots:
         volume_id:
             description: Volume ID.
             type: str
-
 '''
 
-EXAMPLES = '''
-# Get snapshots.
-- openstack.cloud.volume_snapshot_info:
-  register: snapshots
+EXAMPLES = r'''
+- name: List all snapshots
+  openstack.cloud.volume_snapshot_info:
 
-- openstack.cloud.volume_snapshotbackup_info:
+- name: Fetch data about a single snapshot
+  openstack.cloud.volume_snapshot_info:
     name: my_fake_snapshot
-  register: snapshot
 '''
 
 from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
 
 
 class VolumeSnapshotInfoModule(OpenStackModule):
-    module_min_sdk_version = '0.49.0'
-
     argument_spec = dict(
-        details=dict(default=True, type='bool'),
+        details=dict(type='bool'),
         name=dict(),
+        status=dict(choices=['available', 'backing-up', 'creating', 'deleted',
+                             'deleting', 'error', 'error_deleting',
+                             'restoring', 'unmanaging']),
         volume=dict(),
-        status=dict(choices=['creating', 'available', 'error',
-                             'deleting', 'error_deleting', 'rollbacking',
-                             'backing-up']),
     )
+
     module_kwargs = dict(
         supports_check_mode=True
     )
 
     def run(self):
-
-        details_filter = self.params['details']
-        name_filter = self.params['name']
-        volume_filter = self.params['volume']
-        status_filter = self.params['status']
-
-        data = []
-        query = {}
-        if name_filter:
-            query['name'] = name_filter
-        if volume_filter:
-            query['volume_id'] = self.conn.block_storage.find_volume(volume_filter)
-        if status_filter:
-            query['status'] = status_filter.lower()
-
-        for raw in self.conn.block_storage.snapshots(details_filter, **query):
-            dt = raw.to_dict()
-            dt.pop('location')
-            data.append(dt)
-
-        self.exit_json(
-            changed=False,
-            volume_snapshots=data
-        )
+        kwargs = dict((k, self.params[k])
+                      for k in ['details', 'name', 'status']
+                      if self.params[k] is not None)
+
+        volume_name_or_id = self.params['volume']
+        volume = None
+        if volume_name_or_id:
+            volume = self.conn.block_storage.find_volume(volume_name_or_id)
+            if volume:
+                kwargs['volume_id'] = volume.id
+
+        if volume_name_or_id and not volume:
+            snapshots = []
+        else:
+            snapshots = [b.to_dict(computed=False)
+                         for b in self.conn.block_storage.snapshots(**kwargs)]
+
+        self.exit_json(changed=False, volume_snapshots=snapshots)
 
 
 def main():