ec2_ami_copy: add tag_equality option for idempotence using tags (#40088)

* Allow idempotent use of ec2_ami_copy

When `tag_equality` is set true, use tags to determine
if AMIs in different accounts are the same, and don't
copy the AMI twice if they are the same.

Use AnsibleAWSModule and make imports more consistent
with other modules

* Update version added

* More code review changes

* Review changes - Recommended way to start EC2 connection
This commit is contained in:
Ramki Subramanian 2018-05-14 13:11:48 -06:00 committed by Sloane Hertel
parent dbb4493f17
commit 3955e528b1

View file

@ -65,6 +65,12 @@ options:
tags:
description:
- A hash/dictionary of tags to add to the new copied AMI; '{"key":"value"}' and '{"key":"value","key":"value"}'
tag_equality:
description:
- Whether to use tags if the source AMI already exists in the target region. If this is set, and all tags match
in an existing AMI, the AMI will not be copied again.
default: false
version_added: 2.6
author: "Amir Moulavi <amir.moulavi@gmail.com>, Tim C <defunct@defunct.io>"
extends_documentation_fragment:
- aws
@ -97,7 +103,7 @@ EXAMPLES = '''
name: My-Awesome-AMI
description: latest patch
# Tagged AMI copy
# Tagged AMI copy (will not copy the same AMI twice)
- ec2_ami_copy:
source_region: us-east-1
region: eu-west-1
@ -105,6 +111,7 @@ EXAMPLES = '''
tags:
Name: My-Super-AMI
Patch: 1.2.3
tag_equality: yes
# Encrypted AMI copy
- ec2_ami_copy:
@ -130,13 +137,12 @@ image_id:
sample: ami-e689729e
'''
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.ec2 import (boto3_conn, ec2_argument_spec, get_aws_connection_info)
import traceback
from ansible.module_utils.aws.core import AnsibleAWSModule
from ansible.module_utils.ec2 import ec2_argument_spec
from ansible.module_utils.ec2 import camel_dict_to_snake_dict, ansible_dict_to_boto3_tag_list
try:
from botocore.exceptions import ClientError, NoCredentialsError, WaiterError
from botocore.exceptions import ClientError, NoCredentialsError, WaiterError, BotoCoreError
HAS_BOTO3 = True
except ImportError:
HAS_BOTO3 = False
@ -150,6 +156,10 @@ def copy_image(module, ec2):
ec2: ec2 connection object
"""
image = None
changed = False
tags = module.params.get('tags')
params = {'SourceRegion': module.params.get('source_region'),
'SourceImageId': module.params.get('source_image_id'),
'Name': module.params.get('name'),
@ -160,7 +170,20 @@ def copy_image(module, ec2):
params['KmsKeyId'] = module.params.get('kms_key_id')
try:
image_id = ec2.copy_image(**params)['ImageId']
if module.params.get('tag_equality'):
filters = [{'Name': 'tag:%s' % k, 'Values': [v]} for (k, v) in module.params.get('tags').items()]
filters.append(dict(Name='state', Values=['available', 'pending']))
images = ec2.describe_images(Filters=filters)
if len(images['Images']) > 0:
image = images['Images'][0]
if not image:
image = ec2.copy_image(**params)
image_id = image['ImageId']
if tags:
ec2.create_tags(Resources=[image_id],
Tags=ansible_dict_to_boto3_tag_list(tags))
changed = True
if module.params.get('wait'):
delay = 15
max_attempts = module.params.get('wait_timeout') // delay
@ -168,19 +191,12 @@ def copy_image(module, ec2):
ImageIds=[image_id],
WaiterConfig={'Delay': delay, 'MaxAttempts': max_attempts}
)
if module.params.get('tags'):
ec2.create_tags(
Resources=[image_id],
Tags=[{'Key': k, 'Value': v} for k, v in module.params.get('tags').items()]
)
module.exit_json(changed=True, image_id=image_id)
except WaiterError as we:
module.fail_json(msg='An error occurred waiting for the image to become available. (%s)' % str(we), exception=traceback.format_exc())
except ClientError as ce:
module.fail_json(msg=ce.message)
except NoCredentialsError:
module.fail_json(msg='Unable to authenticate, AWS credentials are invalid.')
module.exit_json(changed=changed, **camel_dict_to_snake_dict(image))
except WaiterError as e:
module.fail_json_aws(e, msg='An error occurred waiting for the image to become available')
except (ClientError, BotoCoreError) as e:
module.fail_json_aws(e, msg="Could not copy AMI")
except Exception as e:
module.fail_json(msg='Unhandled exception. (%s)' % str(e))
@ -196,14 +212,12 @@ def main():
kms_key_id=dict(type='str', required=False),
wait=dict(type='bool', default=False),
wait_timeout=dict(type='int', default=600),
tags=dict(type='dict')))
module = AnsibleModule(argument_spec=argument_spec)
region, ec2_url, aws_connect_params = get_aws_connection_info(module, boto3=True)
ec2 = boto3_conn(module, conn_type='client', resource='ec2', region=region, endpoint=ec2_url,
**aws_connect_params)
tags=dict(type='dict')),
tag_equality=dict(type='bool', default=False))
module = AnsibleAWSModule(argument_spec=argument_spec)
# TODO: Check botocore version
ec2 = module.client('ec2')
copy_image(module, ec2)