diff --git a/lib/ansible/modules/cloud/amazon/ec2_ami_copy.py b/lib/ansible/modules/cloud/amazon/ec2_ami_copy.py index df7b121db6..00a6023dc6 100644 --- a/lib/ansible/modules/cloud/amazon/ec2_ami_copy.py +++ b/lib/ansible/modules/cloud/amazon/ec2_ami_copy.py @@ -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 , Tim C " 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)