Repository: ansible/ansible

Issue:
Need ability to created tagged EBS volumes and have them attached with delete_on_termination = true
I first tried to create my EC2 instances with a volume dictionary, but that method doesn't let me tag the volumes.

I then tried to create the EC2 instances without the EBS volumes, then created and attached the EBS volumes, but that method doesn't let me set them to delete on termination.

One alternative would have been to create the EC2 instances one at a time with a volume dictionary, intending to loop through the volumes and tag them after the fact. But then I ran into the issue where I'm unable to invoke my create-one-instance include in a with_sequential loop.

The ideal scenario for me would be to make ec2 support volume tags in the dictionary but I'd be happy to get any method that works.



Discussion:
It would be nice if the tagging were included in ec2 and ec2_vol, but you can do this quite easily regardless

```
- name: create instance
  ec2:
    ...
    volumes:
      ...
      delete_on_termination: yes
  register: ec2_out

- name: get associated vols
  ec2_vol:
    instance: "{{ ec2_out.tagged_instances[0].id }}"
    state: list
    region: "{{ region }}"
  register: ec2_vol_out

- name: tag the volumes
  ec2_tag:
    resource: "{{ item.id }}"
    region:  "{{ region }}"
    tags: 
      Name: foo
  with_items: "{{ ec2_vol_out.volumes | default([]) }}"
```

May be the good idea would be to include a parameter which will allow to apply the instance tags to volumes too?

@Constantin07 I need unique instance tags per volume in order to support the rest of my automation. That said, my tags are constructed from the instance name + device name::

```
  - name: get volume info
    local_action:
      module: ec2_vol
      device_name: "{{item.device_name}}"
      instance: "{{ec2_id}}"
      region: "{{region}}"
      volume_size: "{{item.volume_size}}"
      zone: "{{ec2_placement}}"
    register: volume
    with_items: "{{instance_spec.volumes}}"

  - name: tag the volumes
    local_action: ec2_tag resource="{{item.volume_id}}"
    args:
      tags:
        Name: "{{ec2_tag_Name}}-{{item.item.device_name | basename}}"
        env: "{{env}}"
    with_items: volume.results
    when: instance_spec.volumes | length > 0
```


Resolving patch:
diff --git a/lib/ansible/modules/cloud/amazon/ec2_vol.py b/lib/ansible/modules/cloud/amazon/ec2_vol.py
--- a/lib/ansible/modules/cloud/amazon/ec2_vol.py
+++ b/lib/ansible/modules/cloud/amazon/ec2_vol.py
@@ -23,7 +23,9 @@
 module: ec2_vol
 short_description: create and attach a volume, return volume id and device map
 description:
-    - creates an EBS volume and optionally attaches it to an instance.  If both an instance ID and a device name is given and the instance has a device at the device name, then no volume is created and no attachment is made.  This module has a dependency on python-boto.
+    - creates an EBS volume and optionally attaches it to an instance.
+      If both an instance ID and a device name is given and the instance has a device at the device name, then no volume is created and no attachment is made.
+      This module has a dependency on python-boto.
 version_added: "1.1"
 options:
   instance:
@@ -50,7 +52,8 @@
     default: null
   volume_type:
     description:
-      - Type of EBS volume; standard (magnetic), gp2 (SSD), io1 (Provisioned IOPS), st1 (Throughput Optimized HDD), sc1 (Cold HDD). "Standard" is the old EBS default and continues to remain the Ansible default for backwards compatibility.
+      - Type of EBS volume; standard (magnetic), gp2 (SSD), io1 (Provisioned IOPS), st1 (Throughput Optimized HDD), sc1 (Cold HDD).
+        "Standard" is the old EBS default and continues to remain the Ansible default for backwards compatibility.
     required: false
     default: standard
     version_added: "1.9"
@@ -108,6 +111,13 @@
     default: present
     choices: ['absent', 'present', 'list']
     version_added: "1.6"
+  tags:
+    description:
+      - tag:value pairs to add to the volume after creation
+    type: dict
+    required: false
+    default: {}
+    version_added: "2.3"
 author: "Lester Wade (@lwade)"
 extends_documentation_fragment:
     - aws
@@ -341,6 +351,7 @@ def create_volume(module, ec2, zone):
     volume_size = module.params.get('volume_size')
     volume_type = module.params.get('volume_type')
     snapshot = module.params.get('snapshot')
+    tags = module.params.get('tags')
     # If custom iops is defined we use volume_type "io1" rather than the default of "standard"
     if iops:
         volume_type = 'io1'
@@ -363,7 +374,9 @@ def create_volume(module, ec2, zone):
                 volume.update()
 
             if name:
-                ec2.create_tags([volume.id], {"Name": name})
+                tags["Name"] = name
+            if tags:
+                ec2.create_tags([volume.id], tags)
         except boto.exception.BotoServerError as e:
             module.fail_json(msg = "%s: %s" % (e.error_code, e.error_message))
 
@@ -484,9 +497,9 @@ def get_volume_info(volume, state):
             'device': attachment.device,
             'instance_id': attachment.instance_id,
             'status': attachment.status
-            },
+        },
         'tags': volume.tags
-        }
+    }
     if hasattr(attachment, 'deleteOnTermination'):
         volume_info['attachment_set']['deleteOnTermination'] = attachment.deleteOnTermination
 
@@ -508,7 +521,8 @@ def main():
         delete_on_termination = dict(type='bool', default=False),
         zone = dict(aliases=['availability_zone', 'aws_zone', 'ec2_zone']),
         snapshot = dict(),
-        state = dict(choices=['absent', 'present', 'list'], default='present')
+        state = dict(choices=['absent', 'present', 'list'], default='present'),
+        tags = dict(type='dict', default={})
     )
     )
     module = AnsibleModule(argument_spec=argument_spec)
@@ -526,6 +540,7 @@ def main():
     zone = module.params.get('zone')
     snapshot = module.params.get('snapshot')
     state = module.params.get('state')
+    tags = module.params.get('tags')
 
     # Ensure we have the zone or can get the zone
     if instance is None and zone is None and state == 'present':
@@ -606,7 +621,8 @@ def main():
 
         # Add device, volume_id and volume_type parameters separately to maintain backward compatibility
         volume_info = get_volume_info(volume, state)
-        module.exit_json(changed=changed, volume=volume_info, device=volume_info['attachment_set']['device'], volume_id=volume_info['id'], volume_type=volume_info['type'])
+        module.exit_json(changed=changed, volume=volume_info, device=volume_info['attachment_set']['device'],
+                         volume_id=volume_info['id'], volume_type=volume_info['type'])
     elif state == 'absent':
         delete_volume(module, ec2)
 


Test patch:
diff --git a/test/sanity/pep8/legacy-files.txt b/test/sanity/pep8/legacy-files.txt
--- a/test/sanity/pep8/legacy-files.txt
+++ b/test/sanity/pep8/legacy-files.txt
@@ -41,7 +41,6 @@ lib/ansible/modules/cloud/amazon/ec2_remote_facts.py
 lib/ansible/modules/cloud/amazon/ec2_scaling_policy.py
 lib/ansible/modules/cloud/amazon/ec2_snapshot_facts.py
 lib/ansible/modules/cloud/amazon/ec2_tag.py
-lib/ansible/modules/cloud/amazon/ec2_vol.py
 lib/ansible/modules/cloud/amazon/ec2_vol_facts.py
 lib/ansible/modules/cloud/amazon/ec2_vpc_dhcp_options_facts.py
 lib/ansible/modules/cloud/amazon/ec2_vpc_nacl.py