def main():
    argument_spec = dict(dhcp_options_id=dict(type='str', default=None),
                         domain_name=dict(type='str', default=None),
                         dns_servers=dict(type='list', default=None),
                         ntp_servers=dict(type='list', default=None),
                         netbios_name_servers=dict(type='list', default=None),
                         netbios_node_type=dict(type='int', default=None),
                         vpc_id=dict(type='str', default=None),
                         delete_old=dict(type='bool', default=True),
                         inherit_existing=dict(type='bool', default=False),
                         tags=dict(type='dict',
                                   default=None,
                                   aliases=['resource_tags']),
                         state=dict(type='str',
                                    default='present',
                                    choices=['present', 'absent']))

    module = AnsibleAWSModule(argument_spec=argument_spec,
                              check_boto3=False,
                              supports_check_mode=True)

    params = module.params
    found = False
    changed = False
    new_options = collections.defaultdict(lambda: None)

    if not HAS_BOTO:
        module.fail_json(msg='boto is required for this module')

    region, ec2_url, boto_params = get_aws_connection_info(module)
    connection = connect_to_aws(boto.vpc, region, **boto_params)

    existing_options = None

    # First check if we were given a dhcp_options_id
    if not params['dhcp_options_id']:
        # No, so create new_options from the parameters
        if params['dns_servers'] is not None:
            new_options['domain-name-servers'] = params['dns_servers']
        if params['netbios_name_servers'] is not None:
            new_options['netbios-name-servers'] = params[
                'netbios_name_servers']
        if params['ntp_servers'] is not None:
            new_options['ntp-servers'] = params['ntp_servers']
        if params['domain_name'] is not None:
            # needs to be a list for comparison with boto objects later
            new_options['domain-name'] = [params['domain_name']]
        if params['netbios_node_type'] is not None:
            # needs to be a list for comparison with boto objects later
            new_options['netbios-node-type'] = [
                str(params['netbios_node_type'])
            ]
        # If we were given a vpc_id then we need to look at the options on that
        if params['vpc_id']:
            existing_options = fetch_dhcp_options_for_vpc(
                connection, params['vpc_id'])
            # if we've been asked to inherit existing options, do that now
            if params['inherit_existing']:
                if existing_options:
                    for option in [
                            'domain-name-servers', 'netbios-name-servers',
                            'ntp-servers', 'domain-name', 'netbios-node-type'
                    ]:
                        if existing_options.options.get(
                                option) and new_options[option] != [] and (
                                    not new_options[option]
                                    or [''] == new_options[option]):
                            new_options[option] = existing_options.options.get(
                                option)

            # Do the vpc's dhcp options already match what we're asked for? if so we are done
            if existing_options and new_options == existing_options.options:
                module.exit_json(changed=changed,
                                 new_options=new_options,
                                 dhcp_options_id=existing_options.id)

        # If no vpc_id was given, or the options don't match then look for an existing set using tags
        found, dhcp_option = match_dhcp_options(connection, params['tags'],
                                                new_options)

    # Now let's cover the case where there are existing options that we were told about by id
    # If a dhcp_options_id was supplied we don't look at options inside, just set tags (if given)
    else:
        supplied_options = connection.get_all_dhcp_options(
            filters={'dhcp-options-id': params['dhcp_options_id']})
        if len(supplied_options) != 1:
            if params['state'] != 'absent':
                module.fail_json(
                    msg=" a dhcp_options_id was supplied, but does not exist")
        else:
            found = True
            dhcp_option = supplied_options[0]
            if params['state'] != 'absent' and params['tags']:
                ensure_tags(module, connection, dhcp_option.id, params['tags'],
                            False, module.check_mode)

    # Now we have the dhcp options set, let's do the necessary

    # if we found options we were asked to remove then try to do so
    if params['state'] == 'absent':
        if not module.check_mode:
            if found:
                changed = remove_dhcp_options_by_id(connection, dhcp_option.id)
        module.exit_json(changed=changed, new_options={})

    # otherwise if we haven't found the required options we have something to do
    elif not module.check_mode and not found:

        # create some dhcp options if we weren't able to use existing ones
        if not found:
            # Convert netbios-node-type and domain-name back to strings
            if new_options['netbios-node-type']:
                new_options['netbios-node-type'] = new_options[
                    'netbios-node-type'][0]
            if new_options['domain-name']:
                new_options['domain-name'] = new_options['domain-name'][0]

            # create the new dhcp options set requested
            dhcp_option = connection.create_dhcp_options(
                new_options['domain-name'], new_options['domain-name-servers'],
                new_options['ntp-servers'],
                new_options['netbios-name-servers'],
                new_options['netbios-node-type'])

            # wait for dhcp option to be accessible
            found_dhcp_opt = False
            start_time = time()
            try:
                found_dhcp_opt = retry_not_found(
                    connection.get_all_dhcp_options,
                    dhcp_options_ids=[dhcp_option.id])
            except EC2ResponseError as e:
                module.fail_json_aws(e, msg="Failed to describe DHCP options")
            if not found_dhcp_opt:
                module.fail_json(msg="Failed to wait for {0} to be available.".
                                 format(dhcp_option.id))

            changed = True
            if params['tags']:
                ensure_tags(module, connection, dhcp_option.id, params['tags'],
                            False, module.check_mode)

    # If we were given a vpc_id, then attach the options we now have to that before we finish
    if params['vpc_id'] and not module.check_mode:
        changed = True
        connection.associate_dhcp_options(dhcp_option.id, params['vpc_id'])
        # and remove old ones if that was requested
        if params['delete_old'] and existing_options:
            remove_dhcp_options_by_id(connection, existing_options.id)

    module.exit_json(changed=changed,
                     new_options=new_options,
                     dhcp_options_id=dhcp_option.id)
Exemplo n.º 2
0
def main():
    argument_spec = dict(bucket=dict(required=True),
                         dest=dict(default=None, type='path'),
                         encrypt=dict(default=True, type='bool'),
                         encryption_mode=dict(choices=['AES256', 'aws:kms'],
                                              default='AES256'),
                         expiry=dict(default=600,
                                     type='int',
                                     aliases=['expiration']),
                         headers=dict(type='dict'),
                         marker=dict(default=""),
                         max_keys=dict(default=1000, type='int'),
                         metadata=dict(type='dict'),
                         mode=dict(choices=[
                             'get', 'put', 'delete', 'create', 'geturl',
                             'getstr', 'delobj', 'list'
                         ],
                                   required=True),
                         object=dict(),
                         permission=dict(type='list', default=['private']),
                         version=dict(default=None),
                         overwrite=dict(aliases=['force'], default='always'),
                         prefix=dict(default=""),
                         retries=dict(aliases=['retry'], type='int',
                                      default=0),
                         s3_url=dict(aliases=['S3_URL']),
                         dualstack=dict(default='no', type='bool'),
                         rgw=dict(default='no', type='bool'),
                         src=dict(),
                         ignore_nonexistent_bucket=dict(default=False,
                                                        type='bool'),
                         encryption_kms_key_id=dict())
    module = AnsibleAWSModule(
        argument_spec=argument_spec,
        supports_check_mode=True,
        required_if=[['mode', 'put', ['src', 'object']],
                     ['mode', 'get', ['dest', 'object']],
                     ['mode', 'getstr', ['object']],
                     ['mode', 'geturl', ['object']]],
    )

    bucket = module.params.get('bucket')
    encrypt = module.params.get('encrypt')
    expiry = module.params.get('expiry')
    dest = module.params.get('dest', '')
    headers = module.params.get('headers')
    marker = module.params.get('marker')
    max_keys = module.params.get('max_keys')
    metadata = module.params.get('metadata')
    mode = module.params.get('mode')
    obj = module.params.get('object')
    version = module.params.get('version')
    overwrite = module.params.get('overwrite')
    prefix = module.params.get('prefix')
    retries = module.params.get('retries')
    s3_url = module.params.get('s3_url')
    dualstack = module.params.get('dualstack')
    rgw = module.params.get('rgw')
    src = module.params.get('src')
    ignore_nonexistent_bucket = module.params.get('ignore_nonexistent_bucket')

    object_canned_acl = [
        "private", "public-read", "public-read-write", "aws-exec-read",
        "authenticated-read", "bucket-owner-read", "bucket-owner-full-control"
    ]
    bucket_canned_acl = [
        "private", "public-read", "public-read-write", "authenticated-read"
    ]

    if overwrite not in ['always', 'never', 'different']:
        if module.boolean(overwrite):
            overwrite = 'always'
        else:
            overwrite = 'never'

    if overwrite == 'different' and not HAS_MD5:
        module.fail_json(
            msg=
            'overwrite=different is unavailable: ETag calculation requires MD5 support'
        )

    region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module,
                                                                  boto3=True)

    if region in ('us-east-1', '', None):
        # default to US Standard region
        location = 'us-east-1'
    else:
        # Boto uses symbolic names for locations but region strings will
        # actually work fine for everything except us-east-1 (US Standard)
        location = region

    if module.params.get('object'):
        obj = module.params['object']
        # If there is a top level object, do nothing - if the object starts with /
        # remove the leading character to maintain compatibility with Ansible versions < 2.4
        if obj.startswith('/'):
            obj = obj[1:]

    # Bucket deletion does not require obj.  Prevents ambiguity with delobj.
    if obj and mode == "delete":
        module.fail_json(msg='Parameter obj cannot be used with mode=delete')

    # allow eucarc environment variables to be used if ansible vars aren't set
    if not s3_url and 'S3_URL' in os.environ:
        s3_url = os.environ['S3_URL']

    if dualstack and s3_url is not None and 'amazonaws.com' not in s3_url:
        module.fail_json(msg='dualstack only applies to AWS S3')

    if dualstack and not module.botocore_at_least('1.4.45'):
        module.fail_json(msg='dualstack requires botocore >= 1.4.45')

    # rgw requires an explicit url
    if rgw and not s3_url:
        module.fail_json(msg='rgw flavour requires s3_url')

    # Look at s3_url and tweak connection settings
    # if connecting to RGW, Walrus or fakes3
    if s3_url:
        for key in ['validate_certs', 'security_token', 'profile_name']:
            aws_connect_kwargs.pop(key, None)
    s3 = get_s3_connection(module, aws_connect_kwargs, location, rgw, s3_url)

    validate = not ignore_nonexistent_bucket

    # separate types of ACLs
    bucket_acl = [
        acl for acl in module.params.get('permission')
        if acl in bucket_canned_acl
    ]
    object_acl = [
        acl for acl in module.params.get('permission')
        if acl in object_canned_acl
    ]
    error_acl = [
        acl for acl in module.params.get('permission')
        if acl not in bucket_canned_acl and acl not in object_canned_acl
    ]
    if error_acl:
        module.fail_json(msg='Unknown permission specified: %s' % error_acl)

    # First, we check to see if the bucket exists, we get "bucket" returned.
    bucketrtn = bucket_check(module, s3, bucket, validate=validate)

    if validate and mode not in ('create', 'put', 'delete') and not bucketrtn:
        module.fail_json(msg="Source bucket cannot be found.")

    if mode == 'get':
        keyrtn = key_check(module,
                           s3,
                           bucket,
                           obj,
                           version=version,
                           validate=validate)
        if keyrtn is False:
            if version:
                module.fail_json(
                    msg="Key %s with version id %s does not exist." %
                    (obj, version))
            else:
                module.fail_json(msg="Key %s does not exist." % obj)

        if path_check(dest) and overwrite != 'always':
            if overwrite == 'never':
                module.exit_json(
                    msg=
                    "Local object already exists and overwrite is disabled.",
                    changed=False)
            if etag_compare(module, dest, s3, bucket, obj, version=version):
                module.exit_json(
                    msg=
                    "Local and remote object are identical, ignoring. Use overwrite=always parameter to force.",
                    changed=False)

        try:
            download_s3file(module,
                            s3,
                            bucket,
                            obj,
                            dest,
                            retries,
                            version=version)
        except Sigv4Required:
            s3 = get_s3_connection(module,
                                   aws_connect_kwargs,
                                   location,
                                   rgw,
                                   s3_url,
                                   sig_4=True)
            download_s3file(module,
                            s3,
                            bucket,
                            obj,
                            dest,
                            retries,
                            version=version)

    if mode == 'put':

        # if putting an object in a bucket yet to be created, acls for the bucket and/or the object may be specified
        # these were separated into the variables bucket_acl and object_acl above

        if not path_check(src):
            module.fail_json(msg="Local object for PUT does not exist")

        if bucketrtn:
            keyrtn = key_check(module,
                               s3,
                               bucket,
                               obj,
                               version=version,
                               validate=validate)
        else:
            # If the bucket doesn't exist we should create it.
            # only use valid bucket acls for create_bucket function
            module.params['permission'] = bucket_acl
            create_bucket(module, s3, bucket, location)

        if keyrtn and overwrite != 'always':
            if overwrite == 'never' or etag_compare(module, src, s3, bucket,
                                                    obj):
                # Return the download URL for the existing object
                get_download_url(module,
                                 s3,
                                 bucket,
                                 obj,
                                 expiry,
                                 changed=False)

        # only use valid object acls for the upload_s3file function
        module.params['permission'] = object_acl
        upload_s3file(module, s3, bucket, obj, src, expiry, metadata, encrypt,
                      headers)

    # Delete an object from a bucket, not the entire bucket
    if mode == 'delobj':
        if obj is None:
            module.fail_json(msg="object parameter is required")
        if bucket:
            deletertn = delete_key(module, s3, bucket, obj)
            if deletertn is True:
                module.exit_json(msg="Object deleted from bucket %s." % bucket,
                                 changed=True)
        else:
            module.fail_json(msg="Bucket parameter is required.")

    # Delete an entire bucket, including all objects in the bucket
    if mode == 'delete':
        if bucket:
            deletertn = delete_bucket(module, s3, bucket)
            if deletertn is True:
                module.exit_json(
                    msg="Bucket %s and all keys have been deleted." % bucket,
                    changed=True)
        else:
            module.fail_json(msg="Bucket parameter is required.")

    # Support for listing a set of keys
    if mode == 'list':
        exists = bucket_check(module, s3, bucket)

        # If the bucket does not exist then bail out
        if not exists:
            module.fail_json(msg="Target bucket (%s) cannot be found" % bucket)

        list_keys(module, s3, bucket, prefix, marker, max_keys)

    # Need to research how to create directories without "populating" a key, so this should just do bucket creation for now.
    # WE SHOULD ENABLE SOME WAY OF CREATING AN EMPTY KEY TO CREATE "DIRECTORY" STRUCTURE, AWS CONSOLE DOES THIS.
    if mode == 'create':

        # if both creating a bucket and putting an object in it, acls for the bucket and/or the object may be specified
        # these were separated above into the variables bucket_acl and object_acl

        if bucket and not obj:
            if bucketrtn:
                module.exit_json(msg="Bucket already exists.", changed=False)
            else:
                # only use valid bucket acls when creating the bucket
                module.params['permission'] = bucket_acl
                module.exit_json(msg="Bucket created successfully",
                                 changed=create_bucket(module, s3, bucket,
                                                       location))
        if bucket and obj:
            if obj.endswith('/'):
                dirobj = obj
            else:
                dirobj = obj + "/"
            if bucketrtn:
                if key_check(module, s3, bucket, dirobj):
                    module.exit_json(
                        msg="Bucket %s and key %s already exists." %
                        (bucket, obj),
                        changed=False)
                else:
                    # setting valid object acls for the create_dirkey function
                    module.params['permission'] = object_acl
                    create_dirkey(module, s3, bucket, dirobj, encrypt)
            else:
                # only use valid bucket acls for the create_bucket function
                module.params['permission'] = bucket_acl
                created = create_bucket(module, s3, bucket, location)
                # only use valid object acls for the create_dirkey function
                module.params['permission'] = object_acl
                create_dirkey(module, s3, bucket, dirobj, encrypt)

    # Support for grabbing the time-expired URL for an object in S3/Walrus.
    if mode == 'geturl':
        if not bucket and not obj:
            module.fail_json(msg="Bucket and Object parameters must be set")

        keyrtn = key_check(module,
                           s3,
                           bucket,
                           obj,
                           version=version,
                           validate=validate)
        if keyrtn:
            get_download_url(module, s3, bucket, obj, expiry)
        else:
            module.fail_json(msg="Key %s does not exist." % obj)

    if mode == 'getstr':
        if bucket and obj:
            keyrtn = key_check(module,
                               s3,
                               bucket,
                               obj,
                               version=version,
                               validate=validate)
            if keyrtn:
                try:
                    download_s3str(module, s3, bucket, obj, version=version)
                except Sigv4Required:
                    s3 = get_s3_connection(module,
                                           aws_connect_kwargs,
                                           location,
                                           rgw,
                                           s3_url,
                                           sig_4=True)
                    download_s3str(module, s3, bucket, obj, version=version)
            elif version is not None:
                module.fail_json(
                    msg="Key %s with version id %s does not exist." %
                    (obj, version))
            else:
                module.fail_json(msg="Key %s does not exist." % obj)

    module.exit_json(failed=False)
def main():
    module = AnsibleAWSModule(
        argument_spec={
            'name': dict(type='str', required=True),
            'state': dict(type='str', choices=['present', 'absent'], default='present'),
            'account_sources': dict(type='list', required=True),
            'organization_source': dict(type='dict', required=True)
        },
        supports_check_mode=False,
    )

    result = {
        'changed': False
    }

    name = module.params.get('name')
    state = module.params.get('state')

    params = {}
    if name:
        params['ConfigurationAggregatorName'] = name
    if module.params.get('account_sources'):
        params['AccountAggregationSources'] = []
        for i in module.params.get('account_sources'):
            tmp_dict = {}
            if i.get('account_ids'):
                tmp_dict['AccountIds'] = i.get('account_ids')
            if i.get('aws_regions'):
                tmp_dict['AwsRegions'] = i.get('aws_regions')
            if i.get('all_aws_regions') is not None:
                tmp_dict['AllAwsRegions'] = i.get('all_aws_regions')
            params['AccountAggregationSources'].append(tmp_dict)
    if module.params.get('organization_source'):
        params['OrganizationAggregationSource'] = {}
        if module.params.get('organization_source').get('role_arn'):
            params['OrganizationAggregationSource'].update({
                'RoleArn': module.params.get('organization_source').get('role_arn')
            })
        if module.params.get('organization_source').get('aws_regions'):
            params['OrganizationAggregationSource'].update({
                'AwsRegions': module.params.get('organization_source').get('aws_regions')
            })
        if module.params.get('organization_source').get('all_aws_regions') is not None:
            params['OrganizationAggregationSourcep'].update({
                'AllAwsRegions': module.params.get('organization_source').get('all_aws_regions')
            })

    client = module.client('config', retry_decorator=AWSRetry.jittered_backoff())

    resource_status = resource_exists(client, module, params)

    if state == 'present':
        if not resource_status:
            create_resource(client, module, params, result)
        else:
            update_resource(client, module, params, result)

    if state == 'absent':
        if resource_status:
            delete_resource(client, module, params, result)

    module.exit_json(changed=result['changed'])
def main():
    argument_spec = dict(
        state=dict(required=True, choices=['present', 'absent']),
        arn=dict(required=False, type='str'),
        family=dict(required=False, type='str'),
        revision=dict(required=False, type='int'),
        force_create=dict(required=False, default=False, type='bool'),
        containers=dict(required=False, type='list'),
        network_mode=dict(required=False, default='bridge', choices=['default', 'bridge', 'host', 'none', 'awsvpc'], type='str'),
        task_role_arn=dict(required=False, default='', type='str'),
        execution_role_arn=dict(required=False, default='', type='str'),
        volumes=dict(required=False, type='list'),
        launch_type=dict(required=False, choices=['EC2', 'FARGATE']),
        cpu=dict(),
        memory=dict(required=False, type='str')
    )

    module = AnsibleAWSModule(argument_spec=argument_spec,
                              supports_check_mode=True,
                              required_if=[('launch_type', 'FARGATE', ['cpu', 'memory'])]
                              )

    task_to_describe = None
    task_mgr = EcsTaskManager(module)
    results = dict(changed=False)

    if module.params['launch_type']:
        if not module.botocore_at_least('1.8.4'):
            module.fail_json(msg='botocore needs to be version 1.8.4 or higher to use launch_type')

    if module.params['execution_role_arn']:
        if not module.botocore_at_least('1.10.44'):
            module.fail_json(msg='botocore needs to be version 1.10.44 or higher to use execution_role_arn')

    if module.params['containers']:
        for container in module.params['containers']:
            for environment in container.get('environment', []):
                environment['value'] = to_text(environment['value'])

    if module.params['state'] == 'present':
        if 'containers' not in module.params or not module.params['containers']:
            module.fail_json(msg="To use task definitions, a list of containers must be specified")

        if 'family' not in module.params or not module.params['family']:
            module.fail_json(msg="To use task definitions, a family must be specified")

        network_mode = module.params['network_mode']
        launch_type = module.params['launch_type']
        if launch_type == 'FARGATE' and network_mode != 'awsvpc':
            module.fail_json(msg="To use FARGATE launch type, network_mode must be awsvpc")

        family = module.params['family']
        existing_definitions_in_family = task_mgr.describe_task_definitions(module.params['family'])

        if 'revision' in module.params and module.params['revision']:
            # The definition specifies revision. We must guarantee that an active revision of that number will result from this.
            revision = int(module.params['revision'])

            # A revision has been explicitly specified. Attempt to locate a matching revision
            tasks_defs_for_revision = [td for td in existing_definitions_in_family if td['revision'] == revision]
            existing = tasks_defs_for_revision[0] if len(tasks_defs_for_revision) > 0 else None

            if existing and existing['status'] != "ACTIVE":
                # We cannot reactivate an inactive revision
                module.fail_json(msg="A task in family '%s' already exists for revision %d, but it is inactive" % (family, revision))
            elif not existing:
                if not existing_definitions_in_family and revision != 1:
                    module.fail_json(msg="You have specified a revision of %d but a created revision would be 1" % revision)
                elif existing_definitions_in_family and existing_definitions_in_family[-1]['revision'] + 1 != revision:
                    module.fail_json(msg="You have specified a revision of %d but a created revision would be %d" %
                                         (revision, existing_definitions_in_family[-1]['revision'] + 1))
        else:
            existing = None

            def _right_has_values_of_left(left, right):
                # Make sure the values are equivalent for everything left has
                for k, v in left.items():
                    if not ((not v and (k not in right or not right[k])) or (k in right and v == right[k])):
                        # We don't care about list ordering because ECS can change things
                        if isinstance(v, list) and k in right:
                            left_list = v
                            right_list = right[k] or []

                            if len(left_list) != len(right_list):
                                return False

                            for list_val in left_list:
                                if list_val not in right_list:
                                    return False
                        else:
                            return False

                # Make sure right doesn't have anything that left doesn't
                for k, v in right.items():
                    if v and k not in left:
                        return False

                return True

            def _task_definition_matches(requested_volumes, requested_containers, requested_task_role_arn, existing_task_definition):
                if td['status'] != "ACTIVE":
                    return None

                if requested_task_role_arn != td.get('taskRoleArn', ""):
                    return None

                existing_volumes = td.get('volumes', []) or []

                if len(requested_volumes) != len(existing_volumes):
                    # Nope.
                    return None

                if len(requested_volumes) > 0:
                    for requested_vol in requested_volumes:
                        found = False

                        for actual_vol in existing_volumes:
                            if _right_has_values_of_left(requested_vol, actual_vol):
                                found = True
                                break

                        if not found:
                            return None

                existing_containers = td.get('containerDefinitions', []) or []

                if len(requested_containers) != len(existing_containers):
                    # Nope.
                    return None

                for requested_container in requested_containers:
                    found = False

                    for actual_container in existing_containers:
                        if _right_has_values_of_left(requested_container, actual_container):
                            found = True
                            break

                    if not found:
                        return None

                return existing_task_definition

            # No revision explicitly specified. Attempt to find an active, matching revision that has all the properties requested
            for td in existing_definitions_in_family:
                requested_volumes = module.params['volumes'] or []
                requested_containers = module.params['containers'] or []
                requested_task_role_arn = module.params['task_role_arn']
                existing = _task_definition_matches(requested_volumes, requested_containers, requested_task_role_arn, td)

                if existing:
                    break

        if existing and not module.params.get('force_create'):
            # Awesome. Have an existing one. Nothing to do.
            results['taskdefinition'] = existing
        else:
            if not module.check_mode:
                # Doesn't exist. create it.
                volumes = module.params.get('volumes', []) or []
                results['taskdefinition'] = task_mgr.register_task(module.params['family'],
                                                                   module.params['task_role_arn'],
                                                                   module.params['execution_role_arn'],
                                                                   module.params['network_mode'],
                                                                   module.params['containers'],
                                                                   volumes,
                                                                   module.params['launch_type'],
                                                                   module.params['cpu'],
                                                                   module.params['memory'])
            results['changed'] = True

    elif module.params['state'] == 'absent':
        # When de-registering a task definition, we can specify the ARN OR the family and revision.
        if module.params['state'] == 'absent':
            if 'arn' in module.params and module.params['arn'] is not None:
                task_to_describe = module.params['arn']
            elif 'family' in module.params and module.params['family'] is not None and 'revision' in module.params and \
                    module.params['revision'] is not None:
                task_to_describe = module.params['family'] + ":" + str(module.params['revision'])
            else:
                module.fail_json(msg="To use task definitions, an arn or family and revision must be specified")

        existing = task_mgr.describe_task(task_to_describe)

        if not existing:
            pass
        else:
            # It exists, so we should delete it and mark changed. Return info about the task definition deleted
            results['taskdefinition'] = existing
            if 'status' in existing and existing['status'] == "INACTIVE":
                results['changed'] = False
            else:
                if not module.check_mode:
                    task_mgr.deregister_task(task_to_describe)
                results['changed'] = True

    module.exit_json(**results)
Exemplo n.º 5
0
def main():
    argument_spec = dict(certificate=dict(),
                         certificate_arn=dict(aliases=['arn']),
                         certificate_chain=dict(),
                         domain_name=dict(aliases=['domain']),
                         name_tag=dict(aliases=['name']),
                         private_key=dict(no_log=True),
                         state=dict(default='present',
                                    choices=['present', 'absent']))
    required_if = [
        ['state', 'present', ['certificate', 'name_tag', 'private_key']],
    ]
    module = AnsibleAWSModule(argument_spec=argument_spec,
                              supports_check_mode=True,
                              required_if=required_if)
    acm = ACMServiceManager(module)

    # Check argument requirements
    if module.params['state'] == 'present':
        if module.params['certificate_arn']:
            module.fail_json(
                msg=
                "Parameter 'certificate_arn' is only valid if parameter 'state' is specified as 'absent'"
            )
    else:  # absent
        # exactly one of these should be specified
        absent_args = ['certificate_arn', 'domain_name', 'name_tag']
        if sum([(module.params[a] is not None) for a in absent_args]) != 1:
            for a in absent_args:
                module.debug("%s is %s" % (a, module.params[a]))
            module.fail_json(
                msg=
                "If 'state' is specified as 'absent' then exactly one of 'name_tag', certificate_arn' or 'domain_name' must be specified"
            )

    if module.params['name_tag']:
        tags = dict(Name=module.params['name_tag'])
    else:
        tags = None

    client = module.client('acm')

    # fetch the list of certificates currently in ACM
    certificates = acm.get_certificates(
        client=client,
        module=module,
        domain_name=module.params['domain_name'],
        arn=module.params['certificate_arn'],
        only_tags=tags)

    module.debug("Found %d corresponding certificates in ACM" %
                 len(certificates))

    if module.params['state'] == 'present':
        if len(certificates) > 1:
            msg = "More than one certificate with Name=%s exists in ACM in this region" % module.params[
                'name_tag']
            module.fail_json(msg=msg, certificates=certificates)
        elif len(certificates) == 1:
            # update the existing certificate
            module.debug("Existing certificate found in ACM")
            old_cert = certificates[0]  # existing cert in ACM
            if ('tags'
                    not in old_cert) or ('Name' not in old_cert['tags']) or (
                        old_cert['tags']['Name'] != module.params['name_tag']):
                # shouldn't happen
                module.fail_json(
                    msg="Internal error, unsure which certificate to update",
                    certificate=old_cert)

            if 'certificate' not in old_cert:
                # shouldn't happen
                module.fail_json(
                    msg=
                    "Internal error, unsure what the existing cert in ACM is",
                    certificate=old_cert)

            # Are the existing certificate in ACM and the local certificate the same?
            same = True
            same &= chain_compare(module, old_cert['certificate'],
                                  module.params['certificate'])
            if module.params['certificate_chain']:
                # Need to test this
                # not sure if Amazon appends the cert itself to the chain when self-signed
                same &= chain_compare(module, old_cert['certificate_chain'],
                                      module.params['certificate_chain'])
            else:
                # When there is no chain with a cert
                # it seems Amazon returns the cert itself as the chain
                same &= chain_compare(module, old_cert['certificate_chain'],
                                      module.params['certificate'])

            if same:
                module.debug(
                    "Existing certificate in ACM is the same, doing nothing")
                domain = acm.get_domain_of_cert(
                    client=client,
                    module=module,
                    arn=old_cert['certificate_arn'])
                module.exit_json(certificate=dict(
                    domain_name=domain, arn=old_cert['certificate_arn']),
                                 changed=False)
            else:
                module.debug(
                    "Existing certificate in ACM is different, overwriting")

                # update cert in ACM
                arn = acm.import_certificate(
                    client,
                    module,
                    certificate=module.params['certificate'],
                    private_key=module.params['private_key'],
                    certificate_chain=module.params['certificate_chain'],
                    arn=old_cert['certificate_arn'],
                    tags=tags)
                domain = acm.get_domain_of_cert(client=client,
                                                module=module,
                                                arn=arn)
                module.exit_json(certificate=dict(domain_name=domain, arn=arn),
                                 changed=True)
        else:  # len(certificates) == 0
            module.debug("No certificate in ACM. Creating new one.")
            arn = acm.import_certificate(
                client=client,
                module=module,
                certificate=module.params['certificate'],
                private_key=module.params['private_key'],
                certificate_chain=module.params['certificate_chain'],
                tags=tags)
            domain = acm.get_domain_of_cert(client=client,
                                            module=module,
                                            arn=arn)

            module.exit_json(certificate=dict(domain_name=domain, arn=arn),
                             changed=True)

    else:  # state == absent
        for cert in certificates:
            acm.delete_certificate(client, module, cert['certificate_arn'])
        module.exit_json(
            arns=[cert['certificate_arn'] for cert in certificates],
            changed=(len(certificates) > 0))
Exemplo n.º 6
0
def main():
    argument_spec = dict(
        operation=dict(required=True, choices=['run', 'start', 'stop']),
        cluster=dict(required=False, type='str'),  # R S P
        task_definition=dict(required=False, type='str'),  # R* S*
        overrides=dict(required=False, type='dict'),  # R S
        count=dict(required=False, type='int'),  # R
        task=dict(required=False, type='str'),  # P*
        container_instances=dict(required=False, type='list'),  # S*
        started_by=dict(required=False, type='str'),  # R S
        network_configuration=dict(required=False, type='dict'),
        launch_type=dict(required=False, choices=['EC2', 'FARGATE']),
        tags=dict(required=False, type='dict'))

    module = AnsibleAWSModule(argument_spec=argument_spec,
                              supports_check_mode=True,
                              required_if=[('launch_type', 'FARGATE',
                                            ['network_configuration'])])

    # Validate Inputs
    if module.params['operation'] == 'run':
        if 'task_definition' not in module.params and module.params[
                'task_definition'] is None:
            module.fail_json(
                msg="To run a task, a task_definition must be specified")
        task_to_list = module.params['task_definition']
        status_type = "RUNNING"

    if module.params['operation'] == 'start':
        if 'task_definition' not in module.params and module.params[
                'task_definition'] is None:
            module.fail_json(
                msg="To start a task, a task_definition must be specified")
        if 'container_instances' not in module.params and module.params[
                'container_instances'] is None:
            module.fail_json(
                msg="To start a task, container instances must be specified")
        task_to_list = module.params['task']
        status_type = "RUNNING"

    if module.params['operation'] == 'stop':
        if 'task' not in module.params and module.params['task'] is None:
            module.fail_json(msg="To stop a task, a task must be specified")
        if 'task_definition' not in module.params and module.params[
                'task_definition'] is None:
            module.fail_json(
                msg="To stop a task, a task definition must be specified")
        task_to_list = module.params['task_definition']
        status_type = "STOPPED"

    service_mgr = EcsExecManager(module)

    if module.params[
            'network_configuration'] and not service_mgr.ecs_api_handles_network_configuration(
            ):
        module.fail_json(
            msg=
            'botocore needs to be version 1.7.44 or higher to use network configuration'
        )

    if module.params[
            'launch_type'] and not service_mgr.ecs_api_handles_launch_type():
        module.fail_json(
            msg=
            'botocore needs to be version 1.8.4 or higher to use launch type')

    if module.params['tags']:
        if not service_mgr.ecs_api_handles_tags():
            module.fail_json(msg=missing_required_lib("botocore >= 1.12.46",
                                                      reason="to use tags"))
        if not service_mgr.ecs_task_long_format_enabled():
            module.fail_json(
                msg=
                "Cannot set task tags: long format task arns are required to set tags"
            )

    existing = service_mgr.list_tasks(module.params['cluster'], task_to_list,
                                      status_type)

    results = dict(changed=False)
    if module.params['operation'] == 'run':
        if existing:
            # TBD - validate the rest of the details
            results['task'] = existing
        else:
            if not module.check_mode:
                results['task'] = service_mgr.run_task(
                    module.params['cluster'],
                    module.params['task_definition'],
                    module.params['overrides'],
                    module.params['count'],
                    module.params['started_by'],
                    module.params['launch_type'],
                    module.params['tags'],
                )
            results['changed'] = True

    elif module.params['operation'] == 'start':
        if existing:
            # TBD - validate the rest of the details
            results['task'] = existing
        else:
            if not module.check_mode:
                results['task'] = service_mgr.start_task(
                    module.params['cluster'],
                    module.params['task_definition'],
                    module.params['overrides'],
                    module.params['container_instances'],
                    module.params['started_by'],
                    module.params['tags'],
                )
            results['changed'] = True

    elif module.params['operation'] == 'stop':
        if existing:
            results['task'] = existing
        else:
            if not module.check_mode:
                # it exists, so we should delete it and mark changed.
                # return info about the cluster deleted
                results['task'] = service_mgr.stop_task(
                    module.params['cluster'], module.params['task'])
            results['changed'] = True

    module.exit_json(**results)
Exemplo n.º 7
0
def main():
    module = AnsibleAWSModule(
        argument_spec={
            'name': dict(type='str', required=True),
            'state': dict(type='str', choices=['present', 'absent'], default='present'),
            'description': dict(type='str'),
            'scope': dict(type='dict'),
            'source': dict(type='dict', required=True),
            'input_parameters': dict(type='str'),
            'execution_frequency': dict(
                type='str',
                choices=[
                    'One_Hour',
                    'Three_Hours',
                    'Six_Hours',
                    'Twelve_Hours',
                    'TwentyFour_Hours'
                ]
            ),
        },
        supports_check_mode=False,
    )

    result = {
        'changed': False
    }

    name = module.params.get('name')
    resource_type = module.params.get('resource_type')
    state = module.params.get('state')

    params = {}
    if name:
        params['ConfigRuleName'] = name
    if module.params.get('description'):
        params['Description'] = module.params.get('description')
    if module.params.get('scope'):
        params['Scope'] = {}
        if module.params.get('scope').get('compliance_types'):
            params['Scope'].update({
                'ComplianceResourceTypes': module.params.get('scope').get('compliance_types')
            })
        if module.params.get('scope').get('tag_key'):
            params['Scope'].update({
                'TagKey': module.params.get('scope').get('tag_key')
            })
        if module.params.get('scope').get('tag_value'):
            params['Scope'].update({
                'TagValue': module.params.get('scope').get('tag_value')
            })
        if module.params.get('scope').get('compliance_id'):
            params['Scope'].update({
                'ComplianceResourceId': module.params.get('scope').get('compliance_id')
            })
    if module.params.get('source'):
        params['Source'] = {}
        if module.params.get('source').get('owner'):
            params['Source'].update({
                'Owner': module.params.get('source').get('owner')
            })
        if module.params.get('source').get('identifier'):
            params['Source'].update({
                'SourceIdentifier': module.params.get('source').get('identifier')
            })
        if module.params.get('source').get('details'):
            params['Source'].update({
                'SourceDetails': module.params.get('source').get('details')
            })
    if module.params.get('input_parameters'):
        params['InputParameters'] = module.params.get('input_parameters')
    if module.params.get('execution_frequency'):
        params['MaximumExecutionFrequency'] = module.params.get('execution_frequency')
    params['ConfigRuleState'] = 'ACTIVE'

    client = module.client('config', retry_decorator=AWSRetry.jittered_backoff())

    existing_rule = rule_exists(client, module, params)

    if state == 'present':
        if not existing_rule:
            create_resource(client, module, params, result)
        else:
            update_resource(client, module, params, result)

    if state == 'absent':
        if existing_rule:
            delete_resource(client, module, params, result)

    module.exit_json(**result)
Exemplo n.º 8
0
def main():
    argument_spec = dict(
        state=dict(default='present', choices=['present', 'absent', 'enabled', 'disabled']),
        name=dict(default='default'),
        enable_logging=dict(default=True, type='bool'),
        s3_bucket_name=dict(),
        s3_key_prefix=dict(),
        sns_topic_name=dict(),
        is_multi_region_trail=dict(default=False, type='bool'),
        enable_log_file_validation=dict(type='bool', aliases=['log_file_validation_enabled']),
        include_global_events=dict(default=True, type='bool', aliases=['include_global_service_events']),
        cloudwatch_logs_role_arn=dict(),
        cloudwatch_logs_log_group_arn=dict(),
        kms_key_id=dict(),
        tags=dict(default={}, type='dict'),
    )

    required_if = [('state', 'present', ['s3_bucket_name']), ('state', 'enabled', ['s3_bucket_name'])]
    required_together = [('cloudwatch_logs_role_arn', 'cloudwatch_logs_log_group_arn')]

    module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True, required_together=required_together, required_if=required_if)

    # collect parameters
    if module.params['state'] in ('present', 'enabled'):
        state = 'present'
    elif module.params['state'] in ('absent', 'disabled'):
        state = 'absent'
    tags = module.params['tags']
    enable_logging = module.params['enable_logging']
    ct_params = dict(
        Name=module.params['name'],
        S3BucketName=module.params['s3_bucket_name'],
        IncludeGlobalServiceEvents=module.params['include_global_events'],
        IsMultiRegionTrail=module.params['is_multi_region_trail'],
    )

    if module.params['s3_key_prefix']:
        ct_params['S3KeyPrefix'] = module.params['s3_key_prefix'].rstrip('/')

    if module.params['sns_topic_name']:
        ct_params['SnsTopicName'] = module.params['sns_topic_name']

    if module.params['cloudwatch_logs_role_arn']:
        ct_params['CloudWatchLogsRoleArn'] = module.params['cloudwatch_logs_role_arn']

    if module.params['cloudwatch_logs_log_group_arn']:
        ct_params['CloudWatchLogsLogGroupArn'] = module.params['cloudwatch_logs_log_group_arn']

    if module.params['enable_log_file_validation'] is not None:
        ct_params['EnableLogFileValidation'] = module.params['enable_log_file_validation']

    if module.params['kms_key_id']:
        ct_params['KmsKeyId'] = module.params['kms_key_id']

    client = module.client('cloudtrail')
    region = module.region

    results = dict(
        changed=False,
        exists=False
    )

    # Get existing trail facts
    trail = get_trail_facts(module, client, ct_params['Name'])

    # If the trail exists set the result exists variable
    if trail is not None:
        results['exists'] = True

    if state == 'absent' and results['exists']:
        # If Trail exists go ahead and delete
        results['changed'] = True
        results['exists'] = False
        results['trail'] = dict()
        if not module.check_mode:
            delete_trail(module, client, trail['TrailARN'])

    elif state == 'present' and results['exists']:
        # If Trail exists see if we need to update it
        do_update = False
        for key in ct_params:
            tkey = str(key)
            # boto3 has inconsistent parameter naming so we handle it here
            if key == 'EnableLogFileValidation':
                tkey = 'LogFileValidationEnabled'
            # We need to make an empty string equal None
            if ct_params.get(key) == '':
                val = None
            else:
                val = ct_params.get(key)
            if val != trail.get(tkey):
                do_update = True
                results['changed'] = True
                # If we are in check mode copy the changed values to the trail facts in result output to show what would change.
                if module.check_mode:
                    trail.update({tkey: ct_params.get(key)})

        if not module.check_mode and do_update:
            update_trail(module, client, ct_params)
            trail = get_trail_facts(module, client, ct_params['Name'])

        # Check if we need to start/stop logging
        if enable_logging and not trail['IsLogging']:
            results['changed'] = True
            trail['IsLogging'] = True
            if not module.check_mode:
                set_logging(module, client, name=ct_params['Name'], action='start')
        if not enable_logging and trail['IsLogging']:
            results['changed'] = True
            trail['IsLogging'] = False
            if not module.check_mode:
                set_logging(module, client, name=ct_params['Name'], action='stop')

        # Check if we need to update tags on resource
        tag_dry_run = False
        if module.check_mode:
            tag_dry_run = True
        tags_changed = tag_trail(module, client, tags=tags, trail_arn=trail['TrailARN'], curr_tags=trail['tags'], dry_run=tag_dry_run)
        if tags_changed:
            results['changed'] = True
            trail['tags'] = tags
        # Populate trail facts in output
        results['trail'] = camel_dict_to_snake_dict(trail)

    elif state == 'present' and not results['exists']:
        # Trail doesn't exist just go create it
        results['changed'] = True
        if not module.check_mode:
            # If we aren't in check_mode then actually create it
            created_trail = create_trail(module, client, ct_params)
            # Apply tags
            tag_trail(module, client, tags=tags, trail_arn=created_trail['TrailARN'])
            # Get the trail status
            try:
                status_resp = client.get_trail_status(Name=created_trail['Name'])
            except (BotoCoreError, ClientError) as err:
                module.fail_json_aws(err, msg="Failed to fetch Trail statuc")
            # Set the logging state for the trail to desired value
            if enable_logging and not status_resp['IsLogging']:
                set_logging(module, client, name=ct_params['Name'], action='start')
            if not enable_logging and status_resp['IsLogging']:
                set_logging(module, client, name=ct_params['Name'], action='stop')
            # Get facts for newly created Trail
            trail = get_trail_facts(module, client, ct_params['Name'])

        # If we are in check mode create a fake return structure for the newly minted trail
        if module.check_mode:
            acct_id = '123456789012'
            try:
                sts_client = module.client('sts')
                acct_id = sts_client.get_caller_identity()['Account']
            except (BotoCoreError, ClientError):
                pass
            trail = dict()
            trail.update(ct_params)
            if 'EnableLogFileValidation' not in ct_params:
                ct_params['EnableLogFileValidation'] = False
            trail['EnableLogFileValidation'] = ct_params['EnableLogFileValidation']
            trail.pop('EnableLogFileValidation')
            fake_arn = 'arn:aws:cloudtrail:' + region + ':' + acct_id + ':trail/' + ct_params['Name']
            trail['HasCustomEventSelectors'] = False
            trail['HomeRegion'] = region
            trail['TrailARN'] = fake_arn
            trail['IsLogging'] = enable_logging
            trail['tags'] = tags
        # Populate trail facts in output
        results['trail'] = camel_dict_to_snake_dict(trail)

    module.exit_json(**results)
def main():
    argument_spec = dict(
        name=dict(required=True),
        state=dict(choices=['absent', 'present'], default='present'),
        tags=dict(type='dict'),
    )

    required_if = [['state', 'present', ['tags']]]

    module = AnsibleAWSModule(
        argument_spec=argument_spec,
        supports_check_mode=False,
        required_if=required_if,
    )

    name = module.params.get('name')
    state = module.params.get('state').lower()
    tags = module.params.get('tags')
    if tags:
        tags = ansible_dict_to_boto3_tag_list(tags, 'key', 'value')

    client = module.client('inspector')

    try:
        existing_target_arn = client.list_assessment_targets(filter={
            'assessmentTargetNamePattern':
            name
        }, ).get('assessmentTargetArns')[0]

        existing_target = camel_dict_to_snake_dict(
            client.describe_assessment_targets(assessmentTargetArns=[
                existing_target_arn
            ], ).get('assessmentTargets')[0])

        existing_resource_group_arn = existing_target.get('resource_group_arn')
        existing_resource_group_tags = client.describe_resource_groups(
            resourceGroupArns=[existing_resource_group_arn
                               ], ).get('resourceGroups')[0].get('tags')

        target_exists = True
    except (
            botocore.exceptions.BotoCoreError,
            botocore.exceptions.ClientError,
    ) as e:
        module.fail_json_aws(e, msg="trying to retrieve targets")
    except IndexError:
        target_exists = False

    if state == 'present' and target_exists:
        ansible_dict_tags = boto3_tag_list_to_ansible_dict(tags)
        ansible_dict_existing_tags = boto3_tag_list_to_ansible_dict(
            existing_resource_group_tags)
        tags_to_add, tags_to_remove = compare_aws_tags(
            ansible_dict_tags, ansible_dict_existing_tags)
        if not (tags_to_add or tags_to_remove):
            existing_target.update({'tags': ansible_dict_existing_tags})
            module.exit_json(changed=False, **existing_target)
        else:
            try:
                updated_resource_group_arn = client.create_resource_group(
                    resourceGroupTags=tags, ).get('resourceGroupArn')

                client.update_assessment_target(
                    assessmentTargetArn=existing_target_arn,
                    assessmentTargetName=name,
                    resourceGroupArn=updated_resource_group_arn,
                )

                updated_target = camel_dict_to_snake_dict(
                    client.describe_assessment_targets(assessmentTargetArns=[
                        existing_target_arn
                    ], ).get('assessmentTargets')[0])

                updated_target.update({'tags': ansible_dict_tags})
                module.exit_json(changed=True, **updated_target),
            except (
                    botocore.exceptions.BotoCoreError,
                    botocore.exceptions.ClientError,
            ) as e:
                module.fail_json_aws(e, msg="trying to update target")

    elif state == 'present' and not target_exists:
        try:
            new_resource_group_arn = client.create_resource_group(
                resourceGroupTags=tags, ).get('resourceGroupArn')

            new_target_arn = client.create_assessment_target(
                assessmentTargetName=name,
                resourceGroupArn=new_resource_group_arn,
            ).get('assessmentTargetArn')

            new_target = camel_dict_to_snake_dict(
                client.describe_assessment_targets(assessmentTargetArns=[
                    new_target_arn
                ], ).get('assessmentTargets')[0])

            new_target.update({'tags': boto3_tag_list_to_ansible_dict(tags)})
            module.exit_json(changed=True, **new_target)
        except (
                botocore.exceptions.BotoCoreError,
                botocore.exceptions.ClientError,
        ) as e:
            module.fail_json_aws(e, msg="trying to create target")

    elif state == 'absent' and target_exists:
        try:
            client.delete_assessment_target(
                assessmentTargetArn=existing_target_arn, )
            module.exit_json(changed=True)
        except (
                botocore.exceptions.BotoCoreError,
                botocore.exceptions.ClientError,
        ) as e:
            module.fail_json_aws(e, msg="trying to delete target")

    elif state == 'absent' and not target_exists:
        module.exit_json(changed=False)
Exemplo n.º 10
0
def main():
    argument_spec = dict(alias=dict(aliases=['key_alias']),
                         policy_mode=dict(aliases=['mode'],
                                          choices=['grant', 'deny'],
                                          default='grant'),
                         policy_role_name=dict(aliases=['role_name']),
                         policy_role_arn=dict(aliases=['role_arn']),
                         policy_grant_types=dict(aliases=['grant_types'],
                                                 type='list'),
                         policy_clean_invalid_entries=dict(
                             aliases=['clean_invalid_entries'],
                             type='bool',
                             default=True),
                         key_id=dict(aliases=['key_arn']),
                         description=dict(),
                         enabled=dict(type='bool', default=True),
                         tags=dict(type='dict', default={}),
                         purge_tags=dict(type='bool', default=False),
                         grants=dict(type='list', default=[]),
                         policy=dict(type='json'),
                         purge_grants=dict(type='bool', default=False),
                         state=dict(default='present',
                                    choices=['present', 'absent']),
                         enable_key_rotation=(dict(type='bool')))

    module = AnsibleAWSModule(
        supports_check_mode=True,
        argument_spec=argument_spec,
        required_one_of=[['alias', 'key_id']],
    )

    mode = module.params['policy_mode']

    kms = module.client('kms')

    key_metadata = fetch_key_metadata(kms, module, module.params.get('key_id'),
                                      module.params.get('alias'))
    # We can't create keys with a specific ID, if we can't access the key we'll have to fail
    if module.params.get('state') == 'present' and module.params.get(
            'key_id') and not key_metadata:
        module.fail_json(msg="Could not find key with id %s to update")

    if module.params.get('policy_grant_types') or mode == 'deny':
        module.deprecate(
            'Managing the KMS IAM Policy via policy_mode and policy_grant_types is fragile'
            ' and has been deprecated in favour of the policy option.',
            version='2.13')
        result = update_policy_grants(kms, module, key_metadata, mode)
        module.exit_json(**result)

    if module.params.get('state') == 'absent':
        if key_metadata is None:
            module.exit_json(changed=False)
        result = delete_key(kms, module, key_metadata)
        module.exit_json(**result)

    if key_metadata:
        key_details = get_key_details(kms, module, key_metadata['Arn'])
        result = update_key(kms, module, key_details)
        module.exit_json(**result)

    result = create_key(kms, module)
    module.exit_json(**result)
Exemplo n.º 11
0
def main():
    argument_spec = dict(
        resource=dict(required=True),
        tags=dict(type='dict'),
        purge_tags=dict(type='bool', default=False),
        state=dict(default='present', choices=['present', 'absent', 'list']),
    )
    required_if = [('state', 'present', ['tags']),
                   ('state', 'absent', ['tags'])]

    module = AnsibleAWSModule(argument_spec=argument_spec,
                              required_if=required_if,
                              supports_check_mode=True)

    resource = module.params['resource']
    tags = module.params['tags']
    state = module.params['state']
    purge_tags = module.params['purge_tags']

    result = {'changed': False}

    ec2 = module.client('ec2')

    current_tags = get_tags(ec2, module, resource)

    if state == 'list':
        module.deprecate(
            'Using the "list" state has been deprecated.  Please use the ec2_tag_info module instead',
            version='2.14')
        module.exit_json(changed=False, tags=current_tags)

    add_tags, remove = compare_aws_tags(current_tags,
                                        tags,
                                        purge_tags=purge_tags)

    remove_tags = {}
    if state == 'absent':
        for key in tags:
            if key in current_tags and (tags[key] is None
                                        or current_tags[key] == tags[key]):
                remove_tags[key] = current_tags[key]

    for key in remove:
        remove_tags[key] = current_tags[key]

    if remove_tags:
        result['changed'] = True
        result['removed_tags'] = remove_tags
        if not module.check_mode:
            try:
                AWSRetry.jittered_backoff()(ec2.delete_tags)(
                    Resources=[resource],
                    Tags=ansible_dict_to_boto3_tag_list(remove_tags))
            except (BotoCoreError, ClientError) as e:
                module.fail_json_aws(
                    e,
                    msg='Failed to remove tags {0} from resource {1}'.format(
                        remove_tags, resource))

    if state == 'present' and add_tags:
        result['changed'] = True
        result['added_tags'] = add_tags
        current_tags.update(add_tags)
        if not module.check_mode:
            try:
                AWSRetry.jittered_backoff()(ec2.create_tags)(
                    Resources=[resource],
                    Tags=ansible_dict_to_boto3_tag_list(add_tags))
            except (BotoCoreError, ClientError) as e:
                module.fail_json_aws(
                    e,
                    msg='Failed to set tags {0} on resource {1}'.format(
                        add_tags, resource))

    result['tags'] = get_tags(ec2, module, resource)
    module.exit_json(**result)
Exemplo n.º 12
0
def main():
    argument_spec = dict(
        stack_name=dict(required=True),
        template_parameters=dict(required=False, type='dict', default={}),
        state=dict(default='present', choices=['present', 'absent']),
        template=dict(default=None, required=False, type='path'),
        notification_arns=dict(default=None, required=False),
        stack_policy=dict(default=None, required=False),
        disable_rollback=dict(default=False, type='bool'),
        on_create_failure=dict(default=None,
                               required=False,
                               choices=['DO_NOTHING', 'ROLLBACK', 'DELETE']),
        create_timeout=dict(default=None, type='int'),
        template_url=dict(default=None, required=False),
        template_body=dict(default=None, required=False),
        template_format=dict(removed_in_version='2.14'),
        create_changeset=dict(default=False, type='bool'),
        changeset_name=dict(default=None, required=False),
        role_arn=dict(default=None, required=False),
        tags=dict(default=None, type='dict'),
        termination_protection=dict(default=None, type='bool'),
        events_limit=dict(default=200, type='int'),
        backoff_retries=dict(type='int', default=10, required=False),
        backoff_delay=dict(type='int', default=3, required=False),
        backoff_max_delay=dict(type='int', default=30, required=False),
        capabilities=dict(type='list',
                          default=['CAPABILITY_IAM', 'CAPABILITY_NAMED_IAM']))

    module = AnsibleAWSModule(
        argument_spec=argument_spec,
        mutually_exclusive=[['template_url', 'template', 'template_body'],
                            ['disable_rollback', 'on_create_failure']],
        supports_check_mode=True)

    invalid_capabilities = []
    user_capabilities = module.params.get('capabilities')
    for user_cap in user_capabilities:
        if user_cap not in [
                'CAPABILITY_IAM', 'CAPABILITY_NAMED_IAM',
                'CAPABILITY_AUTO_EXPAND'
        ]:
            invalid_capabilities.append(user_cap)

    if invalid_capabilities:
        module.fail_json(msg="Specified capabilities are invalid : %r,"
                         " please check documentation for valid capabilities" %
                         invalid_capabilities)

    # collect the parameters that are passed to boto3. Keeps us from having so many scalars floating around.
    stack_params = {
        'Capabilities': user_capabilities,
        'ClientRequestToken': to_native(uuid.uuid4()),
    }
    state = module.params['state']
    stack_params['StackName'] = module.params['stack_name']

    if module.params['template'] is not None:
        with open(module.params['template'], 'r') as template_fh:
            stack_params['TemplateBody'] = template_fh.read()
    elif module.params['template_body'] is not None:
        stack_params['TemplateBody'] = module.params['template_body']
    elif module.params['template_url'] is not None:
        stack_params['TemplateURL'] = module.params['template_url']

    if module.params.get('notification_arns'):
        stack_params['NotificationARNs'] = module.params[
            'notification_arns'].split(',')
    else:
        stack_params['NotificationARNs'] = []

    # can't check the policy when verifying.
    if module.params[
            'stack_policy'] is not None and not module.check_mode and not module.params[
                'create_changeset']:
        with open(module.params['stack_policy'], 'r') as stack_policy_fh:
            stack_params['StackPolicyBody'] = stack_policy_fh.read()

    template_parameters = module.params['template_parameters']

    stack_params['Parameters'] = []
    for k, v in template_parameters.items():
        if isinstance(v, dict):
            # set parameter based on a dict to allow additional CFN Parameter Attributes
            param = dict(ParameterKey=k)

            if 'value' in v:
                param['ParameterValue'] = str(v['value'])

            if 'use_previous_value' in v and bool(v['use_previous_value']):
                param['UsePreviousValue'] = True
                param.pop('ParameterValue', None)

            stack_params['Parameters'].append(param)
        else:
            # allow default k/v configuration to set a template parameter
            stack_params['Parameters'].append({
                'ParameterKey': k,
                'ParameterValue': str(v)
            })

    if isinstance(module.params.get('tags'), dict):
        stack_params['Tags'] = ansible_dict_to_boto3_tag_list(
            module.params['tags'])

    if module.params.get('role_arn'):
        stack_params['RoleARN'] = module.params['role_arn']

    result = {}

    cfn = module.client('cloudformation')

    # Wrap the cloudformation client methods that this module uses with
    # automatic backoff / retry for throttling error codes
    backoff_wrapper = AWSRetry.jittered_backoff(
        retries=module.params.get('backoff_retries'),
        delay=module.params.get('backoff_delay'),
        max_delay=module.params.get('backoff_max_delay'))
    cfn.describe_stack_events = backoff_wrapper(cfn.describe_stack_events)
    cfn.create_stack = backoff_wrapper(cfn.create_stack)
    cfn.list_change_sets = backoff_wrapper(cfn.list_change_sets)
    cfn.create_change_set = backoff_wrapper(cfn.create_change_set)
    cfn.update_stack = backoff_wrapper(cfn.update_stack)
    cfn.describe_stacks = backoff_wrapper(cfn.describe_stacks)
    cfn.list_stack_resources = backoff_wrapper(cfn.list_stack_resources)
    cfn.delete_stack = backoff_wrapper(cfn.delete_stack)
    if boto_supports_termination_protection(cfn):
        cfn.update_termination_protection = backoff_wrapper(
            cfn.update_termination_protection)

    stack_info = get_stack_facts(cfn, stack_params['StackName'])

    if module.check_mode:
        if state == 'absent' and stack_info:
            module.exit_json(changed=True,
                             msg='Stack would be deleted',
                             meta=[])
        elif state == 'absent' and not stack_info:
            module.exit_json(changed=False,
                             msg='Stack doesn\'t exist',
                             meta=[])
        elif state == 'present' and not stack_info:
            module.exit_json(changed=True,
                             msg='New stack would be created',
                             meta=[])
        else:
            module.exit_json(**check_mode_changeset(module, stack_params, cfn))

    if state == 'present':
        if not stack_info:
            result = create_stack(module, stack_params, cfn,
                                  module.params.get('events_limit'))
        elif module.params.get('create_changeset'):
            result = create_changeset(module, stack_params, cfn,
                                      module.params.get('events_limit'))
        else:
            if module.params.get('termination_protection') is not None:
                update_termination_protection(
                    module, cfn, stack_params['StackName'],
                    bool(module.params.get('termination_protection')))
            result = update_stack(module, stack_params, cfn,
                                  module.params.get('events_limit'))

        # format the stack output

        stack = get_stack_facts(cfn, stack_params['StackName'])
        if stack is not None:
            if result.get('stack_outputs') is None:
                # always define stack_outputs, but it may be empty
                result['stack_outputs'] = {}
            for output in stack.get('Outputs', []):
                result['stack_outputs'][
                    output['OutputKey']] = output['OutputValue']
            stack_resources = []
            reslist = cfn.list_stack_resources(
                StackName=stack_params['StackName'])
            for res in reslist.get('StackResourceSummaries', []):
                stack_resources.append({
                    "logical_resource_id":
                    res['LogicalResourceId'],
                    "physical_resource_id":
                    res.get('PhysicalResourceId', ''),
                    "resource_type":
                    res['ResourceType'],
                    "last_updated_time":
                    res['LastUpdatedTimestamp'],
                    "status":
                    res['ResourceStatus'],
                    "status_reason":
                    res.get('ResourceStatusReason')  # can be blank, apparently
                })
            result['stack_resources'] = stack_resources

    elif state == 'absent':
        # absent state is different because of the way delete_stack works.
        # problem is it it doesn't give an error if stack isn't found
        # so must describe the stack first

        try:
            stack = get_stack_facts(cfn, stack_params['StackName'])
            if not stack:
                result = {'changed': False, 'output': 'Stack not found.'}
            else:
                if stack_params.get('RoleARN') is None:
                    cfn.delete_stack(StackName=stack_params['StackName'])
                else:
                    cfn.delete_stack(StackName=stack_params['StackName'],
                                     RoleARN=stack_params['RoleARN'])
                result = stack_operation(
                    cfn, stack_params['StackName'], 'DELETE',
                    module.params.get('events_limit'),
                    stack_params.get('ClientRequestToken', None))
        except Exception as err:
            module.fail_json_aws(err)

    module.exit_json(**result)
Exemplo n.º 13
0
def main():
    argument_spec = dict(
        name=dict(required=True),
        state=dict(default='present', choices=['present', 'absent']),
        runtime=dict(),
        role=dict(),
        handler=dict(),
        zip_file=dict(aliases=['src']),
        s3_bucket=dict(),
        s3_key=dict(),
        s3_object_version=dict(),
        description=dict(default=''),
        timeout=dict(type='int', default=3),
        memory_size=dict(type='int', default=128),
        vpc_subnet_ids=dict(type='list'),
        vpc_security_group_ids=dict(type='list'),
        environment_variables=dict(type='dict'),
        dead_letter_arn=dict(),
        tracing_mode=dict(choices=['Active', 'PassThrough']),
        tags=dict(type='dict'),
    )

    mutually_exclusive = [['zip_file', 's3_key'],
                          ['zip_file', 's3_bucket'],
                          ['zip_file', 's3_object_version']]

    required_together = [['s3_key', 's3_bucket'],
                         ['vpc_subnet_ids', 'vpc_security_group_ids']]

    required_if = [['state', 'present', ['runtime', 'handler', 'role']]]

    module = AnsibleAWSModule(argument_spec=argument_spec,
                              supports_check_mode=True,
                              mutually_exclusive=mutually_exclusive,
                              required_together=required_together,
                              required_if=required_if)

    name = module.params.get('name')
    state = module.params.get('state').lower()
    runtime = module.params.get('runtime')
    role = module.params.get('role')
    handler = module.params.get('handler')
    s3_bucket = module.params.get('s3_bucket')
    s3_key = module.params.get('s3_key')
    s3_object_version = module.params.get('s3_object_version')
    zip_file = module.params.get('zip_file')
    description = module.params.get('description')
    timeout = module.params.get('timeout')
    memory_size = module.params.get('memory_size')
    vpc_subnet_ids = module.params.get('vpc_subnet_ids')
    vpc_security_group_ids = module.params.get('vpc_security_group_ids')
    environment_variables = module.params.get('environment_variables')
    dead_letter_arn = module.params.get('dead_letter_arn')
    tracing_mode = module.params.get('tracing_mode')
    tags = module.params.get('tags')

    check_mode = module.check_mode
    changed = False

    region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module, boto3=True)
    if not region:
        module.fail_json(msg='region must be specified')

    try:
        client = boto3_conn(module, conn_type='client', resource='lambda',
                            region=region, endpoint=ec2_url, **aws_connect_kwargs)
    except (ClientError, ValidationError) as e:
        module.fail_json_aws(e, msg="Trying to connect to AWS")

    if state == 'present':
        if re.match(r'^arn:aws(-([a-z\-]+))?:iam', role):
            role_arn = role
        else:
            # get account ID and assemble ARN
            account_id, partition = get_account_info(module, region=region, endpoint=ec2_url, **aws_connect_kwargs)
            role_arn = 'arn:{0}:iam::{1}:role/{2}'.format(partition, account_id, role)

    # Get function configuration if present, False otherwise
    current_function = get_current_function(client, name)

    # Update existing Lambda function
    if state == 'present' and current_function:

        # Get current state
        current_config = current_function['Configuration']
        current_version = None

        # Update function configuration
        func_kwargs = {'FunctionName': name}

        # Update configuration if needed
        if role_arn and current_config['Role'] != role_arn:
            func_kwargs.update({'Role': role_arn})
        if handler and current_config['Handler'] != handler:
            func_kwargs.update({'Handler': handler})
        if description and current_config['Description'] != description:
            func_kwargs.update({'Description': description})
        if timeout and current_config['Timeout'] != timeout:
            func_kwargs.update({'Timeout': timeout})
        if memory_size and current_config['MemorySize'] != memory_size:
            func_kwargs.update({'MemorySize': memory_size})
        if runtime and current_config['Runtime'] != runtime:
            func_kwargs.update({'Runtime': runtime})
        if (environment_variables is not None) and (current_config.get(
                'Environment', {}).get('Variables', {}) != environment_variables):
            func_kwargs.update({'Environment': {'Variables': environment_variables}})
        if dead_letter_arn is not None:
            if current_config.get('DeadLetterConfig'):
                if current_config['DeadLetterConfig']['TargetArn'] != dead_letter_arn:
                    func_kwargs.update({'DeadLetterConfig': {'TargetArn': dead_letter_arn}})
            else:
                if dead_letter_arn != "":
                    func_kwargs.update({'DeadLetterConfig': {'TargetArn': dead_letter_arn}})
        if tracing_mode and (current_config.get('TracingConfig', {}).get('Mode', 'PassThrough') != tracing_mode):
            func_kwargs.update({'TracingConfig': {'Mode': tracing_mode}})

        # If VPC configuration is desired
        if vpc_subnet_ids or vpc_security_group_ids:
            if not vpc_subnet_ids or not vpc_security_group_ids:
                module.fail_json(msg='vpc connectivity requires at least one security group and one subnet')

            if 'VpcConfig' in current_config:
                # Compare VPC config with current config
                current_vpc_subnet_ids = current_config['VpcConfig']['SubnetIds']
                current_vpc_security_group_ids = current_config['VpcConfig']['SecurityGroupIds']

                subnet_net_id_changed = sorted(vpc_subnet_ids) != sorted(current_vpc_subnet_ids)
                vpc_security_group_ids_changed = sorted(vpc_security_group_ids) != sorted(current_vpc_security_group_ids)

            if 'VpcConfig' not in current_config or subnet_net_id_changed or vpc_security_group_ids_changed:
                new_vpc_config = {'SubnetIds': vpc_subnet_ids,
                                  'SecurityGroupIds': vpc_security_group_ids}
                func_kwargs.update({'VpcConfig': new_vpc_config})
        else:
            # No VPC configuration is desired, assure VPC config is empty when present in current config
            if 'VpcConfig' in current_config and current_config['VpcConfig'].get('VpcId'):
                func_kwargs.update({'VpcConfig': {'SubnetIds': [], 'SecurityGroupIds': []}})

        # Upload new configuration if configuration has changed
        if len(func_kwargs) > 1:
            try:
                if not check_mode:
                    response = client.update_function_configuration(**func_kwargs)
                    current_version = response['Version']
                changed = True
            except (ParamValidationError, ClientError) as e:
                module.fail_json_aws(e, msg="Trying to update lambda configuration")

        # Update code configuration
        code_kwargs = {'FunctionName': name, 'Publish': True}

        # Update S3 location
        if s3_bucket and s3_key:
            # If function is stored on S3 always update
            code_kwargs.update({'S3Bucket': s3_bucket, 'S3Key': s3_key})

            # If S3 Object Version is given
            if s3_object_version:
                code_kwargs.update({'S3ObjectVersion': s3_object_version})

        # Compare local checksum, update remote code when different
        elif zip_file:
            local_checksum = sha256sum(zip_file)
            remote_checksum = current_config['CodeSha256']

            # Only upload new code when local code is different compared to the remote code
            if local_checksum != remote_checksum:
                try:
                    with open(zip_file, 'rb') as f:
                        encoded_zip = f.read()
                    code_kwargs.update({'ZipFile': encoded_zip})
                except IOError as e:
                    module.fail_json(msg=str(e), exception=traceback.format_exc())

        # Tag Function
        if tags is not None:
            if set_tag(client, module, tags, current_function):
                changed = True

        # Upload new code if needed (e.g. code checksum has changed)
        if len(code_kwargs) > 2:
            try:
                if not check_mode:
                    response = client.update_function_code(**code_kwargs)
                    current_version = response['Version']
                changed = True
            except (ParamValidationError, ClientError) as e:
                module.fail_json_aws(e, msg="Trying to upload new code")

        # Describe function code and configuration
        response = get_current_function(client, name, qualifier=current_version)
        if not response:
            module.fail_json(msg='Unable to get function information after updating')

        # We're done
        module.exit_json(changed=changed, **camel_dict_to_snake_dict(response))

    # Function doesn't exists, create new Lambda function
    elif state == 'present':
        if s3_bucket and s3_key:
            # If function is stored on S3
            code = {'S3Bucket': s3_bucket,
                    'S3Key': s3_key}
            if s3_object_version:
                code.update({'S3ObjectVersion': s3_object_version})
        elif zip_file:
            # If function is stored in local zipfile
            try:
                with open(zip_file, 'rb') as f:
                    zip_content = f.read()

                code = {'ZipFile': zip_content}
            except IOError as e:
                module.fail_json(msg=str(e), exception=traceback.format_exc())

        else:
            module.fail_json(msg='Either S3 object or path to zipfile required')

        func_kwargs = {'FunctionName': name,
                       'Publish': True,
                       'Runtime': runtime,
                       'Role': role_arn,
                       'Code': code,
                       'Timeout': timeout,
                       'MemorySize': memory_size,
                       }

        if description is not None:
            func_kwargs.update({'Description': description})

        if handler is not None:
            func_kwargs.update({'Handler': handler})

        if environment_variables:
            func_kwargs.update({'Environment': {'Variables': environment_variables}})

        if dead_letter_arn:
            func_kwargs.update({'DeadLetterConfig': {'TargetArn': dead_letter_arn}})

        if tracing_mode:
            func_kwargs.update({'TracingConfig': {'Mode': tracing_mode}})

        # If VPC configuration is given
        if vpc_subnet_ids or vpc_security_group_ids:
            if not vpc_subnet_ids or not vpc_security_group_ids:
                module.fail_json(msg='vpc connectivity requires at least one security group and one subnet')

            func_kwargs.update({'VpcConfig': {'SubnetIds': vpc_subnet_ids,
                                              'SecurityGroupIds': vpc_security_group_ids}})

        # Finally try to create function
        current_version = None
        try:
            if not check_mode:
                response = client.create_function(**func_kwargs)
                current_version = response['Version']
            changed = True
        except (ParamValidationError, ClientError) as e:
            module.fail_json_aws(e, msg="Trying to create function")

        # Tag Function
        if tags is not None:
            if set_tag(client, module, tags, get_current_function(client, name)):
                changed = True

        response = get_current_function(client, name, qualifier=current_version)
        if not response:
            module.fail_json(msg='Unable to get function information after creating')
        module.exit_json(changed=changed, **camel_dict_to_snake_dict(response))

    # Delete existing Lambda function
    if state == 'absent' and current_function:
        try:
            if not check_mode:
                client.delete_function(FunctionName=name)
            changed = True
        except (ParamValidationError, ClientError) as e:
            module.fail_json_aws(e, msg="Trying to delete Lambda function")

        module.exit_json(changed=changed)

    # Function already absent, do nothing
    elif state == 'absent':
        module.exit_json(changed=changed)
Exemplo n.º 14
0
def main():
    protocols = [
        'http',
        'https',
        'email',
        'email_json',
        'sms',
        'sqs',
        'application',
        'lambda',
    ]

    argument_spec = dict(
        msg=dict(required=True, aliases=['default']),
        subject=dict(),
        topic=dict(required=True),
        message_attributes=dict(type='dict'),
        message_structure=dict(choices=['json', 'string'], default='json'),
    )

    for p in protocols:
        argument_spec[p] = dict()

    module = AnsibleAWSModule(argument_spec=argument_spec)

    sns_kwargs = dict(
        Message=module.params['msg'],
        Subject=module.params['subject'],
        MessageStructure=module.params['message_structure'],
    )

    if module.params['message_attributes']:
        if module.params['message_structure'] != 'string':
            module.fail_json(msg='message_attributes is only supported when the message_structure is "string".')
        sns_kwargs['MessageAttributes'] = module.params['message_attributes']

    dict_msg = {
        'default': sns_kwargs['Message']
    }

    for p in protocols:
        if module.params[p]:
            if sns_kwargs['MessageStructure'] != 'json':
                module.fail_json(msg='Protocol-specific messages are only supported when message_structure is "json".')
            dict_msg[p.replace('_', '-')] = module.params[p]

    client = module.client('sns')

    topic = module.params['topic']
    if ':' in topic:
        # Short names can't contain ':' so we'll assume this is the full ARN
        sns_kwargs['TopicArn'] = topic
    else:
        sns_kwargs['TopicArn'] = arn_topic_lookup(module, client, topic)

    if not sns_kwargs['TopicArn']:
        module.fail_json(msg='Could not find topic: {0}'.format(topic))

    if sns_kwargs['MessageStructure'] == 'json':
        sns_kwargs['Message'] = json.dumps(dict_msg)

    try:
        result = client.publish(**sns_kwargs)
    except (BotoCoreError, ClientError) as e:
        module.fail_json_aws(e, msg='Failed to publish message')

    module.exit_json(msg='OK', message_id=result['MessageId'])
Exemplo n.º 15
0
def main():

    arg_spec = dict(
        state=dict(type='str', required=True, choices=['present', 'absent']),
        log_group_name=dict(type='str', required=True),
        filter_name=dict(type='str', required=True),
        filter_pattern=dict(type='str'),
        metric_transformation=dict(type='dict',
                                   options=dict(
                                       metric_name=dict(type='str'),
                                       metric_namespace=dict(type='str'),
                                       metric_value=dict(type='str'),
                                       default_value=dict(type='float'))),
    )

    module = AnsibleAWSModule(argument_spec=arg_spec,
                              supports_check_mode=True,
                              required_if=[
                                  ('state', 'present',
                                   ['metric_transformation', 'filter_pattern'])
                              ])

    log_group_name = module.params.get("log_group_name")
    filter_name = module.params.get("filter_name")
    filter_pattern = module.params.get("filter_pattern")
    metric_transformation = module.params.get("metric_transformation")
    state = module.params.get("state")

    cwl = module.client('logs')

    # check if metric filter exists
    response = cwl.describe_metric_filters(logGroupName=log_group_name,
                                           filterNamePrefix=filter_name)

    if len(response.get("metricFilters")) == 1:
        originMetricTransformations = response.get("metricFilters")[0].get(
            "metricTransformations")[0]
        originFilterPattern = response.get("metricFilters")[0].get(
            "filterPattern")
    else:
        originMetricTransformations = None
        originFilterPattern = None
    change = False
    metricTransformation = None

    if state == "absent" and originMetricTransformations:
        if not module.check_mode:
            response = cwl.delete_metric_filter(logGroupName=log_group_name,
                                                filterName=filter_name)
        change = True
        metricTransformation = [
            camel_dict_to_snake_dict(item)
            for item in [originMetricTransformations]
        ]

    elif state == "present":
        metricTransformation, change = metricTransformationHandler(
            metricTransformations=metric_transformation,
            originMetricTransformations=originMetricTransformations)

        change = change or filter_pattern != originFilterPattern

        if change:
            if not module.check_mode:
                response = cwl.put_metric_filter(
                    logGroupName=log_group_name,
                    filterName=filter_name,
                    filterPattern=filter_pattern,
                    metricTransformations=metricTransformation)

        metricTransformation = [
            camel_dict_to_snake_dict(item) for item in metricTransformation
        ]

    module.exit_json(changed=change, metric_filters=metricTransformation)
Exemplo n.º 16
0
def main():
    argument_spec = dict(cluster_name=dict(required=True),
                         resource=dict(required=False),
                         tags=dict(type='dict'),
                         purge_tags=dict(type='bool', default=False),
                         state=dict(default='present',
                                    choices=['present', 'absent']),
                         resource_type=dict(default='cluster',
                                            choices=[
                                                'cluster', 'task', 'service',
                                                'task_definition', 'container'
                                            ]))
    required_if = [('state', 'present', ['tags']),
                   ('state', 'absent', ['tags'])]

    module = AnsibleAWSModule(argument_spec=argument_spec,
                              required_if=required_if,
                              supports_check_mode=True)

    resource_type = module.params['resource_type']
    cluster_name = module.params['cluster_name']
    if resource_type == 'cluster':
        resource = cluster_name
    else:
        resource = module.params['resource']
    tags = module.params['tags']
    state = module.params['state']
    purge_tags = module.params['purge_tags']

    result = {'changed': False}

    ecs = module.client('ecs')

    resource_arn = get_arn(ecs, module, cluster_name, resource_type, resource)

    current_tags = get_tags(ecs, module, resource_arn)

    add_tags, remove = compare_aws_tags(current_tags,
                                        tags,
                                        purge_tags=purge_tags)

    remove_tags = {}
    if state == 'absent':
        for key in tags:
            if key in current_tags and (tags[key] is None
                                        or current_tags[key] == tags[key]):
                remove_tags[key] = current_tags[key]

    for key in remove:
        remove_tags[key] = current_tags[key]

    if remove_tags:
        result['changed'] = True
        result['removed_tags'] = remove_tags
        if not module.check_mode:
            try:
                ecs.untag_resource(resourceArn=resource_arn,
                                   tagKeys=list(remove_tags.keys()))
            except (BotoCoreError, ClientError) as e:
                module.fail_json_aws(
                    e,
                    msg='Failed to remove tags {0} from resource {1}'.format(
                        remove_tags, resource))

    if state == 'present' and add_tags:
        result['changed'] = True
        result['added_tags'] = add_tags
        current_tags.update(add_tags)
        if not module.check_mode:
            try:
                tags = ansible_dict_to_boto3_tag_list(
                    add_tags,
                    tag_name_key_name='key',
                    tag_value_key_name='value')
                ecs.tag_resource(resourceArn=resource_arn, tags=tags)
            except (BotoCoreError, ClientError) as e:
                module.fail_json_aws(
                    e,
                    msg='Failed to set tags {0} on resource {1}'.format(
                        add_tags, resource))

    result['tags'] = get_tags(ecs, module, resource_arn)
    module.exit_json(**result)
Exemplo n.º 17
0
def main():
    module = AnsibleAWSModule(
        argument_spec={
            'name': dict(required=True),
            'state': dict(choices=['present', 'absent'], default='present'),
            'description': dict(default=""),
            'kms_key_id': dict(),
            'secret_type': dict(choices=['binary', 'string'],
                                default="string"),
            'secret': dict(default=""),
            'tags': dict(type='dict', default={}),
            'rotation_lambda': dict(),
            'rotation_interval': dict(type='int', default=30),
            'recovery_window': dict(type='int', default=30),
        },
        supports_check_mode=True,
    )

    changed = False
    state = module.params.get('state')
    secrets_mgr = SecretsManagerInterface(module)
    recovery_window = module.params.get('recovery_window')
    secret = Secret(module.params.get('name'),
                    module.params.get('secret_type'),
                    module.params.get('secret'),
                    description=module.params.get('description'),
                    kms_key_id=module.params.get('kms_key_id'),
                    tags=module.params.get('tags'),
                    lambda_arn=module.params.get('rotation_lambda'),
                    rotation_interval=module.params.get('rotation_interval'))

    current_secret = secrets_mgr.get_secret(secret.name)

    if state == 'absent':
        if current_secret:
            if not current_secret.get("DeletedDate"):
                result = camel_dict_to_snake_dict(
                    secrets_mgr.delete_secret(secret.name,
                                              recovery_window=recovery_window))
                changed = True
            elif current_secret.get("DeletedDate") and recovery_window == 0:
                result = camel_dict_to_snake_dict(
                    secrets_mgr.delete_secret(secret.name,
                                              recovery_window=recovery_window))
                changed = True
        else:
            result = "secret does not exist"
    if state == 'present':
        if current_secret is None:
            result = secrets_mgr.create_secret(secret)
            changed = True
        else:
            if current_secret.get("DeletedDate"):
                secrets_mgr.restore_secret(secret.name)
                changed = True
            if not secrets_mgr.secrets_match(secret, current_secret):
                result = secrets_mgr.update_secret(secret)
                changed = True
            if not rotation_match(secret, current_secret):
                result = secrets_mgr.update_rotation(secret)
                changed = True
            current_tags = boto3_tag_list_to_ansible_dict(
                current_secret.get('Tags', []))
            tags_to_add, tags_to_remove = compare_aws_tags(
                current_tags, secret.tags)
            if tags_to_add:
                secrets_mgr.tag_secret(
                    secret.name, ansible_dict_to_boto3_tag_list(tags_to_add))
                changed = True
            if tags_to_remove:
                secrets_mgr.untag_secret(secret.name, tags_to_remove)
                changed = True
        result = camel_dict_to_snake_dict(secrets_mgr.get_secret(secret.name))
        result.pop("response_metadata")
    module.exit_json(changed=changed, secret=result)
Exemplo n.º 18
0
def main():
    argument_spec = dict(
        name=dict(required=True),
        cidr_block=dict(type='list', required=True),
        ipv6_cidr=dict(type='bool', default=False),
        tenancy=dict(choices=['default', 'dedicated'], default='default'),
        dns_support=dict(type='bool', default=True),
        dns_hostnames=dict(type='bool', default=True),
        dhcp_opts_id=dict(),
        tags=dict(type='dict', aliases=['resource_tags']),
        state=dict(choices=['present', 'absent'], default='present'),
        multi_ok=dict(type='bool', default=False),
        purge_cidrs=dict(type='bool', default=False),
    )

    module = AnsibleAWSModule(argument_spec=argument_spec,
                              supports_check_mode=True)

    name = module.params.get('name')
    cidr_block = get_cidr_network_bits(module, module.params.get('cidr_block'))
    ipv6_cidr = module.params.get('ipv6_cidr')
    purge_cidrs = module.params.get('purge_cidrs')
    tenancy = module.params.get('tenancy')
    dns_support = module.params.get('dns_support')
    dns_hostnames = module.params.get('dns_hostnames')
    dhcp_id = module.params.get('dhcp_opts_id')
    tags = module.params.get('tags')
    state = module.params.get('state')
    multi = module.params.get('multi_ok')

    changed = False

    connection = module.client(
        'ec2',
        retry_decorator=AWSRetry.jittered_backoff(
            retries=8,
            delay=3,
            catch_extra_error_codes=['InvalidVpcID.NotFound']))

    if dns_hostnames and not dns_support:
        module.fail_json(
            msg=
            'In order to enable DNS Hostnames you must also enable DNS support'
        )

    if state == 'present':

        # Check if VPC exists
        vpc_id = vpc_exists(module, connection, name, cidr_block, multi)

        if vpc_id is None:
            vpc_id = create_vpc(connection, module, cidr_block[0], tenancy)
            changed = True

        vpc_obj = get_vpc(module, connection, vpc_id)

        associated_cidrs = dict(
            (cidr['CidrBlock'], cidr['AssociationId'])
            for cidr in vpc_obj.get('CidrBlockAssociationSet', [])
            if cidr['CidrBlockState']['State'] != 'disassociated')
        to_add = [cidr for cidr in cidr_block if cidr not in associated_cidrs]
        to_remove = [
            associated_cidrs[cidr] for cidr in associated_cidrs
            if cidr not in cidr_block
        ]
        expected_cidrs = [
            cidr for cidr in associated_cidrs
            if associated_cidrs[cidr] not in to_remove
        ] + to_add

        if len(cidr_block) > 1:
            for cidr in to_add:
                changed = True
                try:
                    connection.associate_vpc_cidr_block(CidrBlock=cidr,
                                                        VpcId=vpc_id)
                except (botocore.exceptions.ClientError,
                        botocore.exceptions.BotoCoreError) as e:
                    module.fail_json_aws(
                        e, "Unable to associate CIDR {0}.".format(ipv6_cidr))
        if ipv6_cidr:
            if 'Ipv6CidrBlockAssociationSet' in vpc_obj.keys():
                module.warn(
                    "Only one IPv6 CIDR is permitted per VPC, {0} already has CIDR {1}"
                    .format(
                        vpc_id, vpc_obj['Ipv6CidrBlockAssociationSet'][0]
                        ['Ipv6CidrBlock']))
            else:
                try:
                    connection.associate_vpc_cidr_block(
                        AmazonProvidedIpv6CidrBlock=ipv6_cidr, VpcId=vpc_id)
                    changed = True
                except (botocore.exceptions.ClientError,
                        botocore.exceptions.BotoCoreError) as e:
                    module.fail_json_aws(
                        e, "Unable to associate CIDR {0}.".format(ipv6_cidr))

        if purge_cidrs:
            for association_id in to_remove:
                changed = True
                try:
                    connection.disassociate_vpc_cidr_block(
                        AssociationId=association_id)
                except (botocore.exceptions.ClientError,
                        botocore.exceptions.BotoCoreError) as e:
                    module.fail_json_aws(
                        e,
                        "Unable to disassociate {0}. You must detach or delete all gateways and resources that "
                        "are associated with the CIDR block before you can disassociate it."
                        .format(association_id))

        if dhcp_id is not None:
            try:
                if update_dhcp_opts(connection, module, vpc_obj, dhcp_id):
                    changed = True
            except (botocore.exceptions.ClientError,
                    botocore.exceptions.BotoCoreError) as e:
                module.fail_json_aws(e, "Failed to update DHCP options")

        if tags is not None or name is not None:
            try:
                if update_vpc_tags(connection, module, vpc_id, tags, name):
                    changed = True
            except (botocore.exceptions.ClientError,
                    botocore.exceptions.BotoCoreError) as e:
                module.fail_json_aws(e, msg="Failed to update tags")

        current_dns_enabled = connection.describe_vpc_attribute(
            Attribute='enableDnsSupport', VpcId=vpc_id,
            aws_retry=True)['EnableDnsSupport']['Value']
        current_dns_hostnames = connection.describe_vpc_attribute(
            Attribute='enableDnsHostnames', VpcId=vpc_id,
            aws_retry=True)['EnableDnsHostnames']['Value']
        if current_dns_enabled != dns_support:
            changed = True
            if not module.check_mode:
                try:
                    connection.modify_vpc_attribute(
                        VpcId=vpc_id, EnableDnsSupport={'Value': dns_support})
                except (botocore.exceptions.ClientError,
                        botocore.exceptions.BotoCoreError) as e:
                    module.fail_json_aws(
                        e, "Failed to update enabled dns support attribute")
        if current_dns_hostnames != dns_hostnames:
            changed = True
            if not module.check_mode:
                try:
                    connection.modify_vpc_attribute(
                        VpcId=vpc_id,
                        EnableDnsHostnames={'Value': dns_hostnames})
                except (botocore.exceptions.ClientError,
                        botocore.exceptions.BotoCoreError) as e:
                    module.fail_json_aws(
                        e, "Failed to update enabled dns hostnames attribute")

        # wait for associated cidrs to match
        if to_add or to_remove:
            try:
                connection.get_waiter('vpc_available').wait(
                    VpcIds=[vpc_id],
                    Filters=[{
                        'Name': 'cidr-block-association.cidr-block',
                        'Values': expected_cidrs
                    }])
            except (botocore.exceptions.ClientError,
                    botocore.exceptions.BotoCoreError) as e:
                module.fail_json_aws(e, "Failed to wait for CIDRs to update")

        # try to wait for enableDnsSupport and enableDnsHostnames to match
        wait_for_vpc_attribute(connection, module, vpc_id, 'enableDnsSupport',
                               dns_support)
        wait_for_vpc_attribute(connection, module, vpc_id,
                               'enableDnsHostnames', dns_hostnames)

        final_state = camel_dict_to_snake_dict(
            get_vpc(module, connection, vpc_id))
        final_state['tags'] = boto3_tag_list_to_ansible_dict(
            final_state.get('tags', []))
        final_state['id'] = final_state.pop('vpc_id')

        module.exit_json(changed=changed, vpc=final_state)

    elif state == 'absent':

        # Check if VPC exists
        vpc_id = vpc_exists(module, connection, name, cidr_block, multi)

        if vpc_id is not None:
            try:
                if not module.check_mode:
                    connection.delete_vpc(VpcId=vpc_id)
                changed = True
            except (botocore.exceptions.ClientError,
                    botocore.exceptions.BotoCoreError) as e:
                module.fail_json_aws(
                    e,
                    msg=
                    "Failed to delete VPC {0} You may want to use the ec2_vpc_subnet, ec2_vpc_igw, "
                    "and/or ec2_vpc_route_table modules to ensure the other components are absent."
                    .format(vpc_id))

        module.exit_json(changed=changed, vpc={})
Exemplo n.º 19
0
def main():
    argument_spec = dict(
        stack_name=dict(),
        all_facts=dict(required=False, default=False, type='bool'),
        stack_policy=dict(required=False, default=False, type='bool'),
        stack_events=dict(required=False, default=False, type='bool'),
        stack_resources=dict(required=False, default=False, type='bool'),
        stack_template=dict(required=False, default=False, type='bool'),
        stack_change_sets=dict(required=False, default=False, type='bool'),
    )
    module = AnsibleAWSModule(argument_spec=argument_spec,
                              supports_check_mode=True)

    is_old_facts = module._name == 'cloudformation_facts'
    if is_old_facts:
        module.deprecate(
            "The 'cloudformation_facts' module has been renamed to 'cloudformation_info', "
            "and the renamed one no longer returns ansible_facts",
            version='2.13')

    service_mgr = CloudFormationServiceManager(module)

    if is_old_facts:
        result = {'ansible_facts': {'cloudformation': {}}}
    else:
        result = {'cloudformation': {}}

    for stack_description in service_mgr.describe_stacks(
            module.params.get('stack_name')):
        facts = {'stack_description': stack_description}
        stack_name = stack_description.get('StackName')

        # Create stack output and stack parameter dictionaries
        if facts['stack_description']:
            facts['stack_outputs'] = to_dict(
                facts['stack_description'].get('Outputs'), 'OutputKey',
                'OutputValue')
            facts['stack_parameters'] = to_dict(
                facts['stack_description'].get('Parameters'), 'ParameterKey',
                'ParameterValue')
            facts['stack_tags'] = boto3_tag_list_to_ansible_dict(
                facts['stack_description'].get('Tags'))

        # Create optional stack outputs
        all_facts = module.params.get('all_facts')
        if all_facts or module.params.get('stack_resources'):
            facts['stack_resource_list'] = service_mgr.list_stack_resources(
                stack_name)
            facts['stack_resources'] = to_dict(
                facts.get('stack_resource_list'), 'LogicalResourceId',
                'PhysicalResourceId')
        if all_facts or module.params.get('stack_template'):
            facts['stack_template'] = service_mgr.get_template(stack_name)
        if all_facts or module.params.get('stack_policy'):
            facts['stack_policy'] = service_mgr.get_stack_policy(stack_name)
        if all_facts or module.params.get('stack_events'):
            facts['stack_events'] = service_mgr.describe_stack_events(
                stack_name)
        if all_facts or module.params.get('stack_change_sets'):
            facts[
                'stack_change_sets'] = service_mgr.describe_stack_change_sets(
                    stack_name)

        if is_old_facts:
            result['ansible_facts']['cloudformation'][stack_name] = facts
        else:
            result['cloudformation'][stack_name] = camel_dict_to_snake_dict(
                facts,
                ignore_list=('stack_outputs', 'stack_parameters',
                             'stack_policy', 'stack_resources', 'stack_tags',
                             'stack_template'))

    module.exit_json(changed=False, **result)
Exemplo n.º 20
0
def main():
    argument_spec = dict(
        api_id=dict(type='str', required=False),
        state=dict(type='str',
                   default='present',
                   choices=['present', 'absent']),
        swagger_file=dict(type='path',
                          default=None,
                          aliases=['src', 'api_file']),
        swagger_dict=dict(type='json', default=None),
        swagger_text=dict(type='str', default=None),
        stage=dict(type='str', default=None),
        deploy_desc=dict(type='str',
                         default="Automatic deployment by Ansible."),
        cache_enabled=dict(type='bool', default=False),
        cache_size=dict(type='str',
                        default='0.5',
                        choices=[
                            '0.5', '1.6', '6.1', '13.5', '28.4', '58.2', '118',
                            '237'
                        ]),
        stage_variables=dict(type='dict', default={}),
        stage_canary_settings=dict(type='dict', default={}),
        tracing_enabled=dict(type='bool', default=False),
        endpoint_type=dict(type='str',
                           default='EDGE',
                           choices=['EDGE', 'REGIONAL', 'PRIVATE']))

    mutually_exclusive = [['swagger_file', 'swagger_dict',
                           'swagger_text']]  # noqa: F841

    module = AnsibleAWSModule(
        argument_spec=argument_spec,
        supports_check_mode=False,
        mutually_exclusive=mutually_exclusive,
    )

    api_id = module.params.get('api_id')
    state = module.params.get('state')  # noqa: F841
    swagger_file = module.params.get('swagger_file')
    swagger_dict = module.params.get('swagger_dict')
    swagger_text = module.params.get('swagger_text')
    endpoint_type = module.params.get('endpoint_type')

    client = module.client('apigateway')

    changed = True  # for now it will stay that way until we can sometimes avoid change
    conf_res = None
    dep_res = None
    del_res = None

    if state == "present":
        if api_id is None:
            api_id = create_empty_api(module, client, endpoint_type)
        api_data = get_api_definitions(module,
                                       swagger_file=swagger_file,
                                       swagger_dict=swagger_dict,
                                       swagger_text=swagger_text)
        conf_res, dep_res = ensure_api_in_correct_state(
            module, client, api_id, api_data)
    if state == "absent":
        del_res = delete_rest_api(module, client, api_id)

    exit_args = {"changed": changed, "api_id": api_id}

    if conf_res is not None:
        exit_args['configure_response'] = camel_dict_to_snake_dict(conf_res)
    if dep_res is not None:
        exit_args['deploy_response'] = camel_dict_to_snake_dict(dep_res)
    if del_res is not None:
        exit_args['delete_response'] = camel_dict_to_snake_dict(del_res)

    module.exit_json(**exit_args)
Exemplo n.º 21
0
def main():
    argument_spec = dict(app_name=dict(aliases=['name'],
                                       type='str',
                                       required=False),
                         description=dict(),
                         state=dict(choices=['present', 'absent'],
                                    default='present'),
                         terminate_by_force=dict(type='bool',
                                                 default=False,
                                                 required=False))

    module = AnsibleAWSModule(argument_spec=argument_spec,
                              supports_check_mode=True)

    app_name = module.params['app_name']
    description = module.params['description']
    state = module.params['state']
    terminate_by_force = module.params['terminate_by_force']

    if app_name is None:
        module.fail_json(msg='Module parameter "app_name" is required')

    result = {}

    ebs = module.client('elasticbeanstalk')

    app = describe_app(ebs, app_name, module)

    if module.check_mode:
        check_app(ebs, app, module)
        module.fail_json(
            msg='ASSERTION FAILURE: check_app() should not return control.')

    if state == 'present':
        if app is None:
            try:
                create_app = ebs.create_application(**filter_empty(
                    ApplicationName=app_name, Description=description))
            except (BotoCoreError, ClientError) as e:
                module.fail_json_aws(e, msg="Could not create application")

            app = describe_app(ebs, app_name, module)

            result = dict(changed=True, app=app)
        else:
            if app.get("Description", None) != description:
                try:
                    if not description:
                        ebs.update_application(ApplicationName=app_name)
                    else:
                        ebs.update_application(ApplicationName=app_name,
                                               Description=description)
                except (BotoCoreError, ClientError) as e:
                    module.fail_json_aws(e, msg="Could not update application")

                app = describe_app(ebs, app_name, module)

                result = dict(changed=True, app=app)
            else:
                result = dict(changed=False, app=app)

    else:
        if app is None:
            result = dict(changed=False,
                          output='Application not found',
                          app={})
        else:
            try:
                if terminate_by_force:
                    # Running environments will be terminated before deleting the application
                    ebs.delete_application(
                        ApplicationName=app_name,
                        TerminateEnvByForce=terminate_by_force)
                else:
                    ebs.delete_application(ApplicationName=app_name)
                changed = True
            except BotoCoreError as e:
                module.fail_json_aws(e, msg="Cannot terminate app")
            except ClientError as e:
                if 'It is currently pending deletion.' not in e.response[
                        'Error']['Message']:
                    module.fail_json_aws(e, msg="Cannot terminate app")
                else:
                    changed = False

            result = dict(changed=changed, app=app)

    module.exit_json(**result)
Exemplo n.º 22
0
def main():
    argument_spec = dict(
        state=dict(choices=['present', 'absent'], default='present'),
        endpointidentifier=dict(required=True),
        endpointtype=dict(choices=['source', 'target'], required=True),
        enginename=dict(choices=[
            'mysql', 'oracle', 'postgres', 'mariadb', 'aurora', 'redshift',
            's3', 'db2', 'azuredb', 'sybase', 'dynamodb', 'mongodb',
            'sqlserver'
        ],
                        required=True),
        username=dict(),
        password=dict(no_log=True),
        servername=dict(),
        port=dict(type='int'),
        databasename=dict(),
        extraconnectionattributes=dict(),
        kmskeyid=dict(),
        tags=dict(type='dict'),
        certificatearn=dict(),
        sslmode=dict(choices=['none', 'require', 'verify-ca', 'verify-full'],
                     default='none'),
        serviceaccessrolearn=dict(),
        externaltabledefinition=dict(),
        dynamodbsettings=dict(type='dict'),
        s3settings=dict(type='dict'),
        dmstransfersettings=dict(type='dict'),
        mongodbsettings=dict(type='dict'),
        kinesissettings=dict(type='dict'),
        elasticsearchsettings=dict(type='dict'),
        wait=dict(type='bool', default=False),
        timeout=dict(type='int'),
        retries=dict(type='int'))
    global module
    module = AnsibleAWSModule(argument_spec=argument_spec,
                              required_if=[
                                  ["state", "absent", ["wait"]],
                                  ["wait", "True", ["timeout"]],
                                  ["wait", "True", ["retries"]],
                              ],
                              supports_check_mode=False)
    exit_message = None
    changed = False

    state = module.params.get('state')

    dmsclient = module.client('dms')
    endpoint = describe_endpoints(dmsclient,
                                  module.params.get('endpointidentifier'))
    if state == 'present':
        if endpoint_exists(endpoint):
            module.params['EndpointArn'] = \
                endpoint['Endpoints'][0].get('EndpointArn')
            params_changed = compare_params(endpoint["Endpoints"][0])
            if params_changed:
                updated_dms = modify_dms_endpoint(dmsclient)
                exit_message = updated_dms
                changed = True
            else:
                module.exit_json(changed=False, msg="Endpoint Already Exists")
        else:
            dms_properties = create_dms_endpoint(dmsclient)
            exit_message = dms_properties
            changed = True
    elif state == 'absent':
        if endpoint_exists(endpoint):
            delete_results = delete_dms_endpoint(dmsclient)
            exit_message = delete_results
            changed = True
        else:
            changed = False
            exit_message = 'DMS Endpoint does not exist'

    module.exit_json(changed=changed, msg=exit_message)
Exemplo n.º 23
0
def main():
    argument_spec = dict(device_id=dict(required=False,
                                        aliases=['instance_id']),
                         public_ip=dict(required=False, aliases=['ip']),
                         state=dict(required=False,
                                    default='present',
                                    choices=['present', 'absent']),
                         in_vpc=dict(required=False,
                                     type='bool',
                                     default=False),
                         reuse_existing_ip_allowed=dict(required=False,
                                                        type='bool',
                                                        default=False),
                         release_on_disassociation=dict(required=False,
                                                        type='bool',
                                                        default=False),
                         allow_reassociation=dict(type='bool', default=False),
                         wait_timeout=dict(type='int',
                                           removed_in_version='2.14'),
                         private_ip_address=dict(),
                         tag_name=dict(),
                         tag_value=dict(),
                         public_ipv4_pool=dict())

    module = AnsibleAWSModule(
        argument_spec=argument_spec,
        supports_check_mode=True,
        required_by={
            'private_ip_address': ['device_id'],
        },
    )

    ec2 = module.client('ec2', retry_decorator=AWSRetry.jittered_backoff())

    device_id = module.params.get('device_id')
    instance_id = module.params.get('instance_id')
    public_ip = module.params.get('public_ip')
    private_ip_address = module.params.get('private_ip_address')
    state = module.params.get('state')
    in_vpc = module.params.get('in_vpc')
    domain = 'vpc' if in_vpc else None
    reuse_existing_ip_allowed = module.params.get('reuse_existing_ip_allowed')
    release_on_disassociation = module.params.get('release_on_disassociation')
    allow_reassociation = module.params.get('allow_reassociation')
    tag_name = module.params.get('tag_name')
    tag_value = module.params.get('tag_value')
    public_ipv4_pool = module.params.get('public_ipv4_pool')

    if instance_id:
        warnings = [
            "instance_id is no longer used, please use device_id going forward"
        ]
        is_instance = True
        device_id = instance_id
    else:
        if device_id and device_id.startswith('i-'):
            is_instance = True
        elif device_id:
            if device_id.startswith('eni-') and not in_vpc:
                module.fail_json(
                    msg="If you are specifying an ENI, in_vpc must be true")
            is_instance = False

    tag_dict = generate_tag_dict(module, tag_name, tag_value)

    try:
        if device_id:
            address = find_address(ec2,
                                   module,
                                   public_ip,
                                   device_id,
                                   is_instance=is_instance)
        else:
            address = find_address(ec2, module, public_ip, None)

        if state == 'present':
            if device_id:
                result = ensure_present(ec2,
                                        module,
                                        domain,
                                        address,
                                        private_ip_address,
                                        device_id,
                                        reuse_existing_ip_allowed,
                                        allow_reassociation,
                                        module.check_mode,
                                        is_instance=is_instance)
            else:
                if address:
                    changed = False
                else:
                    address, changed = allocate_address(
                        ec2, module, domain, reuse_existing_ip_allowed,
                        module.check_mode, tag_dict, public_ipv4_pool)
                result = {
                    'changed': changed,
                    'public_ip': address['PublicIp'],
                    'allocation_id': address['AllocationId']
                }
        else:
            if device_id:
                disassociated = ensure_absent(ec2,
                                              module,
                                              address,
                                              device_id,
                                              module.check_mode,
                                              is_instance=is_instance)

                if release_on_disassociation and disassociated['changed']:
                    released = release_address(ec2, module, address,
                                               module.check_mode)
                    result = {
                        'changed': True,
                        'disassociated': disassociated,
                        'released': released
                    }
                else:
                    result = {
                        'changed': disassociated['changed'],
                        'disassociated': disassociated,
                        'released': {
                            'changed': False
                        }
                    }
            else:
                released = release_address(ec2, module, address,
                                           module.check_mode)
                result = {
                    'changed': released['changed'],
                    'disassociated': {
                        'changed': False
                    },
                    'released': released
                }

    except (botocore.exceptions.BotoCoreError,
            botocore.exceptions.ClientError) as e:
        module.fail_json_aws(str(e))

    if instance_id:
        result['warnings'] = warnings
    module.exit_json(**result)