code: #!/usr/bin/python
  # Copyright (c) 2017 Ansible Project
  # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
  
  from __future__ import (absolute_import, division, print_function)
  __metaclass__ = type
  
  ANSIBLE_METADATA = {'metadata_version': '1.1',
                      'status': ['preview'],
                      'supported_by': 'community'}
  
  DOCUMENTATION = '''
  ---
  
  module: cloudfront_invalidation
  
  short_description: create invalidations for AWS CloudFront distributions
  description:
      - Allows for invalidation of a batch of paths for a CloudFront distribution.
  
  requirements:
    - boto3 >= 1.0.0
    - python >= 2.6
  
  version_added: "2.5"
  
  author: Willem van Ketwich (@wilvk)
  
  extends_documentation_fragment:
    - aws
    - ec2
  
  options:
      distribution_id:
        description:
          - The ID of the CloudFront distribution to invalidate paths for. Can be specified instead of the alias.
        required: false
        type: str
      alias:
        description:
          - The alias of the CloudFront distribution to invalidate paths for. Can be specified instead of distribution_id.
        required: false
        type: str
      caller_reference:
        description:
          - A unique reference identifier for the invalidation paths.
          - Defaults to current datetime stamp.
        required: false
        default:
        type: str
      target_paths:
        description:
          - A list of paths on the distribution to invalidate. Each path should begin with '/'. Wildcards are allowed. eg. '/foo/bar/*'
        required: true
        type: list
        elements: str
  
  notes:
    - does not support check mode
  
  '''
  
  EXAMPLES = '''
  
  - name: create a batch of invalidations using a distribution_id for a reference
    cloudfront_invalidation:
      distribution_id: E15BU8SDCGSG57
      caller_reference: testing 123
      target_paths:
        - /testpathone/test1.css
        - /testpathtwo/test2.js
        - /testpaththree/test3.ss
  
  - name: create a batch of invalidations using an alias as a reference and one path using a wildcard match
    cloudfront_invalidation:
      alias: alias.test.com
      caller_reference: testing 123
      target_paths:
        - /testpathone/test4.css
        - /testpathtwo/test5.js
        - /testpaththree/*
  
  '''
  
  RETURN = '''
  invalidation:
    description: The invalidation's information.
    returned: always
    type: complex
    contains:
      create_time:
        description: The date and time the invalidation request was first made.
        returned: always
        type: str
        sample: '2018-02-01T15:50:41.159000+00:00'
      id:
        description: The identifier for the invalidation request.
        returned: always
        type: str
        sample: I2G9MOWJZFV612
      invalidation_batch:
        description: The current invalidation information for the batch request.
        returned: always
        type: complex
        contains:
          caller_reference:
            description: The value used to uniquely identify an invalidation request.
            returned: always
            type: str
            sample: testing 123
          paths:
            description: A dict that contains information about the objects that you want to invalidate.
            returned: always
            type: complex
            contains:
              items:
                description: A list of the paths that you want to invalidate.
                returned: always
                type: list
                sample:
                - /testpathtwo/test2.js
                - /testpathone/test1.css
                - /testpaththree/test3.ss
              quantity:
                description: The number of objects that you want to invalidate.
                returned: always
                type: int
                sample: 3
      status:
        description: The status of the invalidation request.
        returned: always
        type: str
        sample: Completed
  location:
    description: The fully qualified URI of the distribution and invalidation batch request.
    returned: always
    type: str
    sample: https://cloudfront.amazonaws.PI:KEY
  '''
  
  from ansible.module_utils.ec2 import get_aws_connection_info
  from ansible.module_utils.ec2 import ec2_argument_spec, boto3_conn
  from ansible.module_utils.ec2 import snake_dict_to_camel_dict
  from ansible.module_utils.ec2 import camel_dict_to_snake_dict
  from ansible.module_utils.aws.core import AnsibleAWSModule
  from ansible.module_utils.aws.cloudfront_facts import CloudFrontFactsServiceManager
  import datetime
  
  try:
      from botocore.exceptions import ClientError, BotoCoreError
  except ImportError:
      pass  # caught by imported AnsibleAWSModule
  
  
  class CloudFrontInvalidationServiceManager(object):
      """
      Handles CloudFront service calls to AWS for invalidations
      """
  
      def __init__(self, module):
          self.module = module
          self.create_client('cloudfront')
  
      def create_client(self, resource):
          region, ec2_url, aws_connect_kwargs = get_aws_connection_info(self.module, boto3=True)
          self.client = boto3_conn(self.module, conn_type='client', resource=resource, region=region, endpoint=ec2_url, **aws_connect_kwargs)
  
      def create_invalidation(self, distribution_id, invalidation_batch):
          current_invalidation_response = self.get_invalidation(distribution_id, invalidation_batch['CallerReference'])
          try:
              response = self.client.create_invalidation(DistributionId=distribution_id, InvalidationBatch=invalidation_batch)
              response.pop('ResponseMetadata', None)
              if current_invalidation_response:
                  return response, False
              else:
                  return response, True
          except BotoCoreError as e:
              self.module.fail_json_aws(e, msg="Error creating CloudFront invalidations.")
          except ClientError as e:
              if ('Your request contains a caller reference that was used for a previous invalidation batch '
                      'for the same distribution.' in e.response['Error']['Message']):
                  self.module.warn("InvalidationBatch target paths are not modifiable. "
                                   "To make a new invalidation please update caller_reference.")
                  return current_invalidation_response, False
              else:
                  self.module.fail_json_aws(e, msg="Error creating CloudFront invalidations.")
  
      def get_invalidation(self, distribution_id, caller_reference):
          current_invalidation = {}
          # find all invalidations for the distribution
          try:
              paginator = self.client.get_paginator('list_invalidations')
              invalidations = paginator.paginate(DistributionId=distribution_id).build_full_result().get('InvalidationList', {}).get('Items', [])
              invalidation_ids = [inv['Id'] for inv in invalidations]
          except (BotoCoreError, ClientError) as e:
              self.module.fail_json_aws(e, msg="Error listing CloudFront invalidations.")
  
          # check if there is an invalidation with the same caller reference
          for inv_id in invalidation_ids:
              try:
                  invalidation = self.client.get_invalidation(DistributionId=distribution_id, Id=inv_id)['Invalidation']
                  caller_ref = invalidation.get('InvalidationBatch', {}).get('CallerReference')
              except (BotoCoreError, ClientError) as e:
                  self.module.fail_json_aws(e, msg="Error getting CloudFront invalidation {0}".format(inv_id))
              if caller_ref == caller_reference:
                  current_invalidation = invalidation
                  break
  
          current_invalidation.pop('ResponseMetadata', None)
          return current_invalidation
  
  
  class CloudFrontInvalidationValidationManager(object):
      """
      Manages CloudFront validations for invalidation batches
      """
  
      def __init__(self, module):
          self.module = module
          self.__cloudfront_facts_mgr = CloudFrontFactsServiceManager(module)
  
      def validate_distribution_id(self, distribution_id, alias):
          try:
              if distribution_id is None and alias is None:
                  self.module.fail_json(msg="distribution_id or alias must be specified")
              if distribution_id is None:
                  distribution_id = self.__cloudfront_facts_mgr.get_distribution_id_from_domain_name(alias)
              return distribution_id
          except (ClientError, BotoCoreError) as e:
              self.module.fail_json_aws(e, msg="Error validating parameters.")
  
      def create_aws_list(self, invalidation_batch):
          aws_list = {}
          aws_list["Quantity"] = len(invalidation_batch)
          aws_list["Items"] = invalidation_batch
          return aws_list
  
      def validate_invalidation_batch(self, invalidation_batch, caller_reference):
          try:
              if caller_reference is not None:
                  valid_caller_reference = caller_reference
              else:
                  valid_caller_reference = datetime.datetime.now().isoformat()
              valid_invalidation_batch = {
                  'paths': self.create_aws_list(invalidation_batch),
                  'caller_reference': valid_caller_reference
              }
              return valid_invalidation_batch
          except (ClientError, BotoCoreError) as e:
              self.module.fail_json_aws(e, msg="Error validating invalidation batch.")
  
  
  def main():
      argument_spec = ec2_argument_spec()
  
      argument_spec.update(dict(
          caller_reference=dict(),
          distribution_id=dict(),
          alias=dict(),
          target_paths=dict(required=True, type='list')
      ))
  
      module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=False, mutually_exclusive=[['distribution_id', 'alias']])
  
      validation_mgr = CloudFrontInvalidationValidationManager(module)
      service_mgr = CloudFrontInvalidationServiceManager(module)
  
      caller_reference = module.params.get('caller_reference')
      distribution_id = module.params.get('distribution_id')
      alias = module.params.get('alias')
      target_paths = module.params.get('target_paths')
  
      result = {}
  
      distribution_id = validation_mgr.validate_distribution_id(distribution_id, alias)
      valid_target_paths = validation_mgr.validate_invalidation_batch(target_paths, caller_reference)
      valid_pascal_target_paths = snake_dict_to_camel_dict(valid_target_paths, True)
      result, changed = service_mgr.create_invalidation(distribution_id, valid_pascal_target_paths)
  
      module.exit_json(changed=changed, **camel_dict_to_snake_dict(result))
  
  
  if __name__ == '__main__':
      main()
  
entities: [['DATE_TIME', '2017'], ['PERSON', 'Willem van Ketwich'], ['DATE_TIME', '2018-02-01T15:50:41.159000+'], ['DATE_TIME', "00:00'\n    i"], ['PERSON', 'aws_connect_kwargs = get_aws_connection_info(self.module'], ['URL', 'self.mo'], ['LOCATION', 'BotoCoreError'], ['LOCATION', 'BotoCoreError'], ['PERSON', "caller_reference = module.params.get('caller_reference'"], ['LOCATION', 'validation_mgr.validate_invalidation_batch(target_paths'], ['URL', 'https://www.gnu.org/licenses/gpl-3.0.txt'], ['URL', 'https://cloudfront.am'], ['URL', 'alias.test.com'], ['URL', 'ansible.mo'], ['URL', 'utils.ec'], ['URL', 'ansible.mo'], ['URL', 'utils.ec'], ['URL', 'ansible.mo'], ['URL', 'utils.ec'], ['URL', 'ansible.mo'], ['URL', 'utils.ec'], ['URL', 'ansible.mo'], ['URL', 'utils.aws.co'], ['URL', 'ansible.mo'], ['URL', 'utils.aws.cl'], ['URL', 'self.mo'], ['URL', 'self.cr'], ['URL', 'self.cl'], ['URL', 'self.mo'], ['URL', 'self.ge'], ['URL', 'self.client.cr'], ['URL', 'self.mo'], ['URL', 'e.re'], ['URL', 'self.mo'], ['URL', 'self.mo'], ['URL', 'self.client.ge'], ['URL', 'paginator.pa'], ['URL', 'self.mo'], ['URL', 'self.client.ge'], ['URL', 'invalidation.ge'], ['URL', 'self.mo'], ['URL', 'self.mo'], ['URL', 'self.mo'], ['URL', 'mgr.ge'], ['URL', 'self.mo'], ['URL', 'datetime.datetime.no'], ['URL', 'self.cr'], ['URL', 'self.mo'], ['URL', 'module.params.ge'], ['URL', 'module.params.ge'], ['URL', 'module.params.ge'], ['URL', 'module.params.ge'], ['URL', 'mgr.va'], ['URL', 'mgr.va'], ['URL', 'mgr.cr']]