Пример #1
0
def check_for_update(connection, module_params, vpn_connection_id):
    """ Determines if there are any tags or routes that need to be updated. Ensures non-modifiable attributes aren't expected to change. """
    tags = module_params.get('tags')
    routes = module_params.get('routes')
    purge_tags = module_params.get('purge_tags')
    purge_routes = module_params.get('purge_routes')

    vpn_connection = find_connection(connection, module_params, vpn_connection_id=vpn_connection_id)
    current_attrs = ec2_utils.camel_dict_to_snake_dict(vpn_connection)

    # Initialize changes dict
    changes = {'tags_to_add': [],
               'tags_to_remove': [],
               'routes_to_add': [],
               'routes_to_remove': []}

    # Get changes to tags
    if 'tags' in current_attrs:
        current_tags = ec2_utils.boto3_tag_list_to_ansible_dict(current_attrs['tags'], u'key', u'value')
        tags_to_add, changes['tags_to_remove'] = ec2_utils.compare_aws_tags(current_tags, tags, purge_tags)
        changes['tags_to_add'] = ec2_utils.ansible_dict_to_boto3_tag_list(tags_to_add)
    elif tags:
        current_tags = {}
        tags_to_add, changes['tags_to_remove'] = ec2_utils.compare_aws_tags(current_tags, tags, purge_tags)
        changes['tags_to_add'] = ec2_utils.ansible_dict_to_boto3_tag_list(tags_to_add)
    # Get changes to routes
    if 'Routes' in vpn_connection:
        current_routes = [route['DestinationCidrBlock'] for route in vpn_connection['Routes']]
        if purge_routes:
            changes['routes_to_remove'] = [old_route for old_route in current_routes if old_route not in routes]
        changes['routes_to_add'] = [new_route for new_route in routes if new_route not in current_routes]

    # Check if nonmodifiable attributes are attempted to be modified
    for attribute in current_attrs:
        if attribute in ("tags", "routes", "state"):
            continue
        elif attribute == 'options':
            will_be = module_params.get('static_only', None)
            is_now = bool(current_attrs[attribute]['static_routes_only'])
            attribute = 'static_only'
        elif attribute == 'type':
            will_be = module_params.get("connection_type", None)
            is_now = current_attrs[attribute]
        else:
            is_now = current_attrs[attribute]
            will_be = module_params.get(attribute, None)

        if will_be is not None and to_text(will_be) != to_text(is_now):
            raise VPNConnectionException(msg="You cannot modify {0}, the current value of which is {1}. Modifiable VPN "
                                         "connection attributes are tags and routes. The value you tried to change it to "
                                         "is {2}.".format(attribute, is_now, will_be))

    return changes
Пример #2
0
def ensure_tags(connection=None, module=None, resource_id=None, tags=None, purge_tags=None, check_mode=None):
    try:
        cur_tags = describe_tags_with_backoff(connection, resource_id)
    except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
        module.fail_json_aws(e, msg='Unable to list tags for VPC')

    to_add, to_delete = compare_aws_tags(cur_tags, tags, purge_tags)

    if not to_add and not to_delete:
        return {'changed': False, 'tags': cur_tags}
    if check_mode:
        if not purge_tags:
            tags = cur_tags.update(tags)
        return {'changed': True, 'tags': tags}

    if to_delete:
        try:
            connection.delete_tags(Resources=[resource_id], Tags=[{'Key': k} for k in to_delete])
        except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
            module.fail_json_aws(e, msg="Couldn't delete tags")
    if to_add:
        try:
            connection.create_tags(Resources=[resource_id], Tags=ansible_dict_to_boto3_tag_list(to_add))
        except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
            module.fail_json_aws(e, msg="Couldn't create tags")

    try:
        latest_tags = describe_tags_with_backoff(connection, resource_id)
    except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
        module.fail_json_aws(e, msg='Unable to list tags for VPC')
    return {'changed': True, 'tags': latest_tags}
Пример #3
0
def update_tags(module, connection, group, tags):
    changed = False
    existing_tags = connection.list_tags_for_resource(ResourceName=group['DBParameterGroupArn'])['TagList']
    to_update, to_delete = compare_aws_tags(boto3_tag_list_to_ansible_dict(existing_tags),
                                            tags, module.params['purge_tags'])
    if to_update:
        try:
            connection.add_tags_to_resource(ResourceName=group['DBParameterGroupArn'],
                                            Tags=ansible_dict_to_boto3_tag_list(to_update))
            changed = True
        except botocore.exceptions.ClientError as e:
            module.fail_json(msg="Couldn't add tags to parameter group: %s" % str(e),
                             exception=traceback.format_exc(),
                             **camel_dict_to_snake_dict(e.response))
        except botocore.exceptions.ParamValidationError as e:
            # Usually a tag value has been passed as an int or bool, needs to be a string
            # The AWS exception message is reasonably ok for this purpose
            module.fail_json(msg="Couldn't add tags to parameter group: %s." % str(e),
                             exception=traceback.format_exc())
    if to_delete:
        try:
            connection.remove_tags_from_resource(ResourceName=group['DBParameterGroupArn'],
                                                 TagKeys=to_delete)
            changed = True
        except botocore.exceptions.ClientError as e:
            module.fail_json(msg="Couldn't remove tags from parameter group: %s" % str(e),
                             exception=traceback.format_exc(),
                             **camel_dict_to_snake_dict(e.response))
    return changed
Пример #4
0
def ensure_tags(conn, module, subnet, tags, purge_tags, start_time):
    changed = False

    filters = ansible_dict_to_boto3_filter_list({'resource-id': subnet['id'], 'resource-type': 'subnet'})
    try:
        cur_tags = conn.describe_tags(Filters=filters)
    except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
        module.fail_json_aws(e, msg="Couldn't describe tags")

    to_update, to_delete = compare_aws_tags(boto3_tag_list_to_ansible_dict(cur_tags.get('Tags')), tags, purge_tags)

    if to_update:
        try:
            if not module.check_mode:
                AWSRetry.exponential_backoff(
                    catch_extra_error_codes=['InvalidSubnetID.NotFound']
                )(conn.create_tags)(
                    Resources=[subnet['id']],
                    Tags=ansible_dict_to_boto3_tag_list(to_update)
                )

            changed = True
        except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
            module.fail_json_aws(e, msg="Couldn't create tags")

    if to_delete:
        try:
            if not module.check_mode:
                tags_list = []
                for key in to_delete:
                    tags_list.append({'Key': key})

                AWSRetry.exponential_backoff(
                    catch_extra_error_codes=['InvalidSubnetID.NotFound']
                )(conn.delete_tags)(Resources=[subnet['id']], Tags=tags_list)

            changed = True
        except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
            module.fail_json_aws(e, msg="Couldn't delete tags")

    if module.params['wait'] and not module.check_mode:
        # Wait for tags to be updated
        filters = [{'Name': 'tag:{0}'.format(k), 'Values': [v]} for k, v in tags.items()]
        handle_waiter(conn, module, 'subnet_exists',
                      {'SubnetIds': [subnet['id']], 'Filters': filters}, start_time)

    return changed
Пример #5
0
def set_tag(client, module, tags, function):
    if not hasattr(client, "list_tags"):
        module.fail_json(msg="Using tags requires botocore 1.5.40 or above")

    changed = False
    arn = function['Configuration']['FunctionArn']

    try:
        current_tags = client.list_tags(Resource=arn).get('Tags', {})
    except ClientError as e:
        module.fail_json(msg="Unable to list tags: {0}".format(to_native(e),
                         exception=traceback.format_exc()))

    tags_to_add, tags_to_remove = compare_aws_tags(current_tags, tags, purge_tags=True)

    try:
        if tags_to_remove:
            client.untag_resource(
                Resource=arn,
                TagKeys=tags_to_remove
            )
            changed = True

        if tags_to_add:
            client.tag_resource(
                Resource=arn,
                Tags=tags_to_add
            )
            changed = True

    except ClientError as e:
        module.fail_json(msg="Unable to tag resource {0}: {1}".format(arn,
                         to_native(e)), exception=traceback.format_exc(),
                         **camel_dict_to_snake_dict(e.response))
    except BotoCoreError as e:
        module.fail_json(msg="Unable to tag resource {0}: {1}".format(arn,
                         to_native(e)), exception=traceback.format_exc())

    return changed
Пример #6
0
    def converge_file_system(self, name, tags, purge_tags, targets,
                             throughput_mode, provisioned_throughput_in_mibps):
        """
         Change attributes (mount targets and tags) of filesystem by name
        """
        result = False
        fs_id = self.get_file_system_id(name)

        if tags is not None:
            tags_need_modify, tags_to_delete = compare_aws_tags(
                boto3_tag_list_to_ansible_dict(
                    self.get_tags(FileSystemId=fs_id)), tags, purge_tags)

            if tags_to_delete:
                try:
                    self.connection.delete_tags(FileSystemId=fs_id,
                                                TagKeys=tags_to_delete)
                except ClientError as e:
                    self.module.fail_json(
                        msg="Unable to delete tags: {0}".format(to_native(e)),
                        exception=traceback.format_exc(),
                        **camel_dict_to_snake_dict(e.response))
                except BotoCoreError as e:
                    self.module.fail_json(
                        msg="Unable to delete tags: {0}".format(to_native(e)),
                        exception=traceback.format_exc())

                result = True

            if tags_need_modify:
                try:
                    self.connection.create_tags(
                        FileSystemId=fs_id,
                        Tags=ansible_dict_to_boto3_tag_list(tags_need_modify))
                except ClientError as e:
                    self.module.fail_json(
                        msg="Unable to create tags: {0}".format(to_native(e)),
                        exception=traceback.format_exc(),
                        **camel_dict_to_snake_dict(e.response))
                except BotoCoreError as e:
                    self.module.fail_json(
                        msg="Unable to create tags: {0}".format(to_native(e)),
                        exception=traceback.format_exc())

                result = True

        if targets is not None:
            incomplete_states = [self.STATE_CREATING, self.STATE_DELETING]
            wait_for(
                lambda: len(
                    self.get_mount_targets_in_state(fs_id, incomplete_states)),
                0)
            current_targets = _index_by_key(
                'SubnetId', self.get_mount_targets(FileSystemId=fs_id))
            targets = _index_by_key('SubnetId', targets)

            targets_to_create, intersection, targets_to_delete = dict_diff(
                current_targets, targets, True)

            # To modify mount target it should be deleted and created again
            changed = [
                sid for sid in intersection if not targets_equal(
                    ['SubnetId', 'IpAddress', 'NetworkInterfaceId'],
                    current_targets[sid], targets[sid])
            ]
            targets_to_delete = list(targets_to_delete) + changed
            targets_to_create = list(targets_to_create) + changed

            if targets_to_delete:
                for sid in targets_to_delete:
                    self.connection.delete_mount_target(
                        MountTargetId=current_targets[sid]['MountTargetId'])
                wait_for(
                    lambda: len(
                        self.get_mount_targets_in_state(
                            fs_id, incomplete_states)), 0)
                result = True

            if targets_to_create:
                for sid in targets_to_create:
                    self.connection.create_mount_target(FileSystemId=fs_id,
                                                        **targets[sid])
                wait_for(
                    lambda: len(
                        self.get_mount_targets_in_state(
                            fs_id, incomplete_states)), 0, self.wait_timeout)
                result = True

            # If no security groups were passed into the module, then do not change it.
            security_groups_to_update = [
                sid for sid in intersection
                if 'SecurityGroups' in targets[sid] and current_targets[sid]
                ['SecurityGroups'] != targets[sid]['SecurityGroups']
            ]

            if security_groups_to_update:
                for sid in security_groups_to_update:
                    self.connection.modify_mount_target_security_groups(
                        MountTargetId=current_targets[sid]['MountTargetId'],
                        SecurityGroups=targets[sid].get(
                            'SecurityGroups', None))
                result = True

        return result
Пример #7
0
def create_or_update_elb(connection, connection_ec2, module):
    """Create ELB or modify main attributes. json_exit here"""

    changed = False
    new_load_balancer = False
    params = dict()
    params['Name'] = module.params.get("name")
    params['Subnets'] = module.params.get("subnets")
    try:
        params['SecurityGroups'] = get_ec2_security_group_ids_from_names(module.params.get('security_groups'), connection_ec2, boto3=True)
    except ValueError as e:
        module.fail_json(msg=str(e), exception=traceback.format_exc())
    except ClientError as e:
        module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
    except NoCredentialsError as e:
        module.fail_json(msg="AWS authentication problem. " + e.message, exception=traceback.format_exc())

    params['Scheme'] = module.params.get("scheme")
    if module.params.get("tags"):
        params['Tags'] = ansible_dict_to_boto3_tag_list(module.params.get("tags"))
    purge_tags = module.params.get("purge_tags")
    access_logs_enabled = module.params.get("access_logs_enabled")
    access_logs_s3_bucket = module.params.get("access_logs_s3_bucket")
    access_logs_s3_prefix = module.params.get("access_logs_s3_prefix")
    deletion_protection = module.params.get("deletion_protection")
    idle_timeout = module.params.get("idle_timeout")

    # Does the ELB currently exist?
    elb = get_elb(connection, module)

    if elb:
        # ELB exists so check subnets, security groups and tags match what has been passed

        # Subnets
        if set(_get_subnet_ids_from_subnet_list(elb['AvailabilityZones'])) != set(params['Subnets']):
            try:
                connection.set_subnets(LoadBalancerArn=elb['LoadBalancerArn'], Subnets=params['Subnets'])
            except ClientError as e:
                module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
            changed = True

        # Security Groups
        if set(elb['SecurityGroups']) != set(params['SecurityGroups']):
            try:
                connection.set_security_groups(LoadBalancerArn=elb['LoadBalancerArn'], SecurityGroups=params['SecurityGroups'])
            except ClientError as e:
                module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
            changed = True

        # Tags - only need to play with tags if tags parameter has been set to something
        if module.params.get("tags"):
            try:
                elb_tags = connection.describe_tags(ResourceArns=[elb['LoadBalancerArn']])['TagDescriptions'][0]['Tags']
            except ClientError as e:
                module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))

            # Delete necessary tags
            tags_need_modify, tags_to_delete = compare_aws_tags(boto3_tag_list_to_ansible_dict(elb_tags), boto3_tag_list_to_ansible_dict(params['Tags']),
                                                                purge_tags)
            if tags_to_delete:
                try:
                    connection.remove_tags(ResourceArns=[elb['LoadBalancerArn']], TagKeys=tags_to_delete)
                except ClientError as e:
                    module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
                changed = True

            # Add/update tags
            if tags_need_modify:
                try:
                    connection.add_tags(ResourceArns=[elb['LoadBalancerArn']], Tags=params['Tags'])
                except ClientError as e:
                    module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
                changed = True

    else:
        try:
            elb = connection.create_load_balancer(**params)['LoadBalancers'][0]
            changed = True
            new_load_balancer = True
        except ClientError as e:
            module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))

        if module.params.get("wait"):
            status_achieved, new_elb = wait_for_status(connection, module, elb['LoadBalancerArn'], 'active')

    # Now set ELB attributes. Use try statement here so we can remove the ELB if this stage fails
    update_attributes = []

    # Get current attributes
    current_elb_attributes = get_elb_attributes(connection, module, elb['LoadBalancerArn'])

    if access_logs_enabled and current_elb_attributes['access_logs_s3_enabled'] != "true":
        update_attributes.append({'Key': 'access_logs.s3.enabled', 'Value': "true"})
    if not access_logs_enabled and current_elb_attributes['access_logs_s3_enabled'] != "false":
        update_attributes.append({'Key': 'access_logs.s3.enabled', 'Value': 'false'})
    if access_logs_s3_bucket is not None and access_logs_s3_bucket != current_elb_attributes['access_logs_s3_bucket']:
        update_attributes.append({'Key': 'access_logs.s3.bucket', 'Value': access_logs_s3_bucket})
    if access_logs_s3_prefix is not None and access_logs_s3_prefix != current_elb_attributes['access_logs_s3_prefix']:
        update_attributes.append({'Key': 'access_logs.s3.prefix', 'Value': access_logs_s3_prefix})
    if deletion_protection and current_elb_attributes['deletion_protection_enabled'] != "true":
        update_attributes.append({'Key': 'deletion_protection.enabled', 'Value': "true"})
    if not deletion_protection and current_elb_attributes['deletion_protection_enabled'] != "false":
        update_attributes.append({'Key': 'deletion_protection.enabled', 'Value': "false"})
    if idle_timeout is not None and str(idle_timeout) != current_elb_attributes['idle_timeout_timeout_seconds']:
        update_attributes.append({'Key': 'idle_timeout.timeout_seconds', 'Value': str(idle_timeout)})

    if update_attributes:
        try:
            connection.modify_load_balancer_attributes(LoadBalancerArn=elb['LoadBalancerArn'], Attributes=update_attributes)
            changed = True
        except ClientError as e:
            # Something went wrong setting attributes. If this ELB was created during this task, delete it to leave a consistent state
            if new_load_balancer:
                connection.delete_load_balancer(LoadBalancerArn=elb['LoadBalancerArn'])
            module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))

    # Now, if required, set ELB listeners. Use try statement here so we can remove the ELB if this stage fails
    try:
        listener_changed = create_or_update_elb_listeners(connection, module, elb)
        if listener_changed:
            changed = True
    except ClientError as e:
        # Something went wrong setting listeners. If this ELB was created during this task, delete it to leave a consistent state
        if new_load_balancer:
            connection.delete_load_balancer(LoadBalancerArn=elb['LoadBalancerArn'])
        module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))

    # Get the ELB again
    elb = get_elb(connection, module)

    # Get the ELB listeners again
    elb['listeners'] = get_elb_listeners(connection, module, elb['LoadBalancerArn'])

    # For each listener, get listener rules
    for listener in elb['listeners']:
        listener['rules'] = get_listener_rules(connection, module, listener['ListenerArn'])

    # Get the ELB attributes again
    elb.update(get_elb_attributes(connection, module, elb['LoadBalancerArn']))

    # Convert to snake_case
    snaked_elb = camel_dict_to_snake_dict(elb)

    # Get the tags of the ELB
    elb_tags = connection.describe_tags(ResourceArns=[elb['LoadBalancerArn']])['TagDescriptions'][0]['Tags']
    snaked_elb['tags'] = boto3_tag_list_to_ansible_dict(elb_tags)

    module.exit_json(changed=changed, **snaked_elb)
Пример #8
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)
Пример #9
0
def update_key(connection, module, key):
    changed = False
    alias = module.params['alias']
    if not alias.startswith('alias/'):
        alias = 'alias/' + alias
    aliases = get_kms_aliases_with_backoff(connection)['Aliases']
    key_id = module.params.get('key_id')
    if key_id:
        # We will only add new aliases, not rename existing ones
        if alias not in [_alias['AliasName'] for _alias in aliases]:
            try:
                connection.create_alias(KeyId=key_id, AliasName=alias)
                changed = True
            except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
                module.fail_json_aws(e, msg="Failed create key alias")

    if key['key_state'] == 'PendingDeletion':
        try:
            connection.cancel_key_deletion(KeyId=key['key_id'])
            # key is disabled after deletion cancellation
            # set this so that ensure_enabled_disabled works correctly
            key['key_state'] = 'Disabled'
            changed = True
        except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
            module.fail_json_aws(e, msg="Failed to cancel key deletion")

    changed = ensure_enabled_disabled(connection, module, key) or changed

    description = module.params.get('description')
    # don't update description if description is not set
    # (means you can't remove a description completely)
    if description and key['description'] != description:
        try:
            connection.update_key_description(KeyId=key['key_id'], Description=description)
            changed = True
        except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
            module.fail_json_aws(e, msg="Failed to update key description")

    desired_tags = module.params.get('tags')
    to_add, to_remove = compare_aws_tags(key['tags'], desired_tags,
                                         module.params.get('purge_tags'))
    if to_remove:
        try:
            connection.untag_resource(KeyId=key['key_id'], TagKeys=to_remove)
            changed = True
        except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
            module.fail_json_aws(e, msg="Unable to remove or update tag")
    if to_add:
        try:
            connection.tag_resource(KeyId=key['key_id'],
                                    Tags=[{'TagKey': tag_key, 'TagValue': desired_tags[tag_key]}
                                          for tag_key in to_add])
            changed = True
        except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
            module.fail_json_aws(e, msg="Unable to add tag to key")

    desired_grants = module.params.get('grants')
    existing_grants = key['grants']

    to_add, to_remove = compare_grants(existing_grants, desired_grants,
                                       module.params.get('purge_grants'))
    if to_remove:
        for grant in to_remove:
            try:
                connection.retire_grant(KeyId=key['key_arn'], GrantId=grant['grant_id'])
                changed = True
            except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
                module.fail_json_aws(e, msg="Unable to retire grant")

    if to_add:
        for grant in to_add:
            grant_params = convert_grant_params(grant, key)
            try:
                connection.create_grant(**grant_params)
                changed = True
            except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
                module.fail_json_aws(e, msg="Unable to create grant")

    # make results consistent with kms_facts
    result = get_key_details(connection, module, key['key_id'])
    module.exit_json(changed=changed, **result)
Пример #10
0
def update_key(connection, module, key):
    changed = False
    alias = module.params.get('alias')
    key_id = key['key_arn']

    if alias:
        changed = update_alias(connection, module, key_id, alias) or changed

    if key['key_state'] == 'PendingDeletion':
        try:
            connection.cancel_key_deletion(KeyId=key_id)
            # key is disabled after deletion cancellation
            # set this so that ensure_enabled_disabled works correctly
            key['key_state'] = 'Disabled'
            changed = True
        except (botocore.exceptions.ClientError,
                botocore.exceptions.BotoCoreError) as e:
            module.fail_json_aws(e, msg="Failed to cancel key deletion")

    changed = ensure_enabled_disabled(connection, module, key) or changed

    description = module.params.get('description')
    # don't update description if description is not set
    # (means you can't remove a description completely)
    if description and key['description'] != description:
        try:
            connection.update_key_description(KeyId=key_id,
                                              Description=description)
            changed = True
        except (botocore.exceptions.ClientError,
                botocore.exceptions.BotoCoreError) as e:
            module.fail_json_aws(e, msg="Failed to update key description")

    desired_tags = module.params.get('tags')
    to_add, to_remove = compare_aws_tags(key['tags'], desired_tags,
                                         module.params.get('purge_tags'))
    if to_remove:
        try:
            connection.untag_resource(KeyId=key_id, TagKeys=to_remove)
            changed = True
        except (botocore.exceptions.ClientError,
                botocore.exceptions.BotoCoreError) as e:
            module.fail_json_aws(e, msg="Unable to remove or update tag")
    if to_add:
        try:
            connection.tag_resource(KeyId=key_id,
                                    Tags=[{
                                        'TagKey': tag_key,
                                        'TagValue': desired_tags[tag_key]
                                    } for tag_key in to_add])
            changed = True
        except (botocore.exceptions.ClientError,
                botocore.exceptions.BotoCoreError) as e:
            module.fail_json_aws(e, msg="Unable to add tag to key")

    # Update existing policy before trying to tweak grants
    if module.params.get('policy'):
        policy = module.params.get('policy')
        try:
            keyret = connection.get_key_policy(KeyId=key_id,
                                               PolicyName='default')
        except (botocore.exceptions.ClientError,
                botocore.exceptions.BotoCoreError) as e:
            # If we can't fetch the current policy assume we're making a change
            # Could occur if we have PutKeyPolicy without GetKeyPolicy
            original_policy = {}
        original_policy = json.loads(keyret['Policy'])
        try:
            new_policy = json.loads(policy)
        except ValueError as e:
            module.fail_json_aws(e, msg="Unable to parse new policy as JSON")
        if compare_policies(original_policy, new_policy):
            changed = True
            if not module.check_mode:
                try:
                    connection.put_key_policy(KeyId=key_id,
                                              PolicyName='default',
                                              Policy=policy)
                except (botocore.exceptions.ClientError,
                        botocore.exceptions.BotoCoreError) as e:
                    module.fail_json_aws(e, msg="Unable to update key policy")

    desired_grants = module.params.get('grants')
    existing_grants = key['grants']

    to_add, to_remove = compare_grants(existing_grants, desired_grants,
                                       module.params.get('purge_grants'))
    if to_remove:
        for grant in to_remove:
            try:
                connection.retire_grant(KeyId=key_id,
                                        GrantId=grant['grant_id'])
                changed = True
            except (botocore.exceptions.ClientError,
                    botocore.exceptions.BotoCoreError) as e:
                module.fail_json_aws(e, msg="Unable to retire grant")

    if to_add:
        for grant in to_add:
            grant_params = convert_grant_params(grant, key)
            try:
                connection.create_grant(**grant_params)
                changed = True
            except (botocore.exceptions.ClientError,
                    botocore.exceptions.BotoCoreError) as e:
                module.fail_json_aws(e, msg="Unable to create grant")

    # make results consistent with kms_facts before returning
    result = get_key_details(connection, module, key_id)
    module.exit_json(changed=changed, **result)
Пример #11
0
def create_or_update_target_group(connection, module):

    changed = False
    params = dict()
    params['Name'] = module.params.get("name")
    params['Protocol'] = module.params.get("protocol").upper()
    params['Port'] = module.params.get("port")
    params['VpcId'] = module.params.get("vpc_id")
    tags = module.params.get("tags")
    purge_tags = module.params.get("purge_tags")
    deregistration_delay_timeout = module.params.get("deregistration_delay_timeout")
    stickiness_enabled = module.params.get("stickiness_enabled")
    stickiness_lb_cookie_duration = module.params.get("stickiness_lb_cookie_duration")
    stickiness_type = module.params.get("stickiness_type")

    # If health check path not None, set health check attributes
    if module.params.get("health_check_path") is not None:
        params['HealthCheckPath'] = module.params.get("health_check_path")

        if module.params.get("health_check_protocol") is not None:
            params['HealthCheckProtocol'] = module.params.get("health_check_protocol").upper()

        if module.params.get("health_check_port") is not None:
            params['HealthCheckPort'] = str(module.params.get("health_check_port"))

        if module.params.get("health_check_interval") is not None:
            params['HealthCheckIntervalSeconds'] = module.params.get("health_check_interval")

        if module.params.get("health_check_timeout") is not None:
            params['HealthCheckTimeoutSeconds'] = module.params.get("health_check_timeout")

        if module.params.get("healthy_threshold_count") is not None:
            params['HealthyThresholdCount'] = module.params.get("healthy_threshold_count")

        if module.params.get("unhealthy_threshold_count") is not None:
            params['UnhealthyThresholdCount'] = module.params.get("unhealthy_threshold_count")

        if module.params.get("successful_response_codes") is not None:
            params['Matcher'] = {}
            params['Matcher']['HttpCode'] = module.params.get("successful_response_codes")

    # Get target group
    tg = get_target_group(connection, module)

    if tg:
        # Target group exists so check health check parameters match what has been passed
        health_check_params = dict()

        # If we have no health check path then we have nothing to modify
        if module.params.get("health_check_path") is not None:
            # Health check protocol
            if 'HealthCheckProtocol' in params and tg['HealthCheckProtocol'] != params['HealthCheckProtocol']:
                health_check_params['HealthCheckProtocol'] = params['HealthCheckProtocol']

            # Health check port
            if 'HealthCheckPort' in params and tg['HealthCheckPort'] != params['HealthCheckPort']:
                health_check_params['HealthCheckPort'] = params['HealthCheckPort']

            # Health check path
            if 'HealthCheckPath'in params and tg['HealthCheckPath'] != params['HealthCheckPath']:
                health_check_params['HealthCheckPath'] = params['HealthCheckPath']

            # Health check interval
            if 'HealthCheckIntervalSeconds' in params and tg['HealthCheckIntervalSeconds'] != params['HealthCheckIntervalSeconds']:
                health_check_params['HealthCheckIntervalSeconds'] = params['HealthCheckIntervalSeconds']

            # Health check timeout
            if 'HealthCheckTimeoutSeconds' in params and tg['HealthCheckTimeoutSeconds'] != params['HealthCheckTimeoutSeconds']:
                health_check_params['HealthCheckTimeoutSeconds'] = params['HealthCheckTimeoutSeconds']

            # Healthy threshold
            if 'HealthyThresholdCount' in params and tg['HealthyThresholdCount'] != params['HealthyThresholdCount']:
                health_check_params['HealthyThresholdCount'] = params['HealthyThresholdCount']

            # Unhealthy threshold
            if 'UnhealthyThresholdCount' in params and tg['UnhealthyThresholdCount'] != params['UnhealthyThresholdCount']:
                health_check_params['UnhealthyThresholdCount'] = params['UnhealthyThresholdCount']

            # Matcher (successful response codes)
            # TODO: required and here?
            if 'Matcher' in params:
                current_matcher_list = tg['Matcher']['HttpCode'].split(',')
                requested_matcher_list = params['Matcher']['HttpCode'].split(',')
                if set(current_matcher_list) != set(requested_matcher_list):
                    health_check_params['Matcher'] = {}
                    health_check_params['Matcher']['HttpCode'] = ','.join(requested_matcher_list)

            try:
                if health_check_params:
                    connection.modify_target_group(TargetGroupArn=tg['TargetGroupArn'], **health_check_params)
                    changed = True
            except ClientError as e:
                module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))

        # Do we need to modify targets?
        if module.params.get("modify_targets"):
            if module.params.get("targets"):
                params['Targets'] = module.params.get("targets")

                # get list of current target instances. I can't see anything like a describe targets in the doco so
                # describe_target_health seems to be the only way to get them

                try:
                    current_targets = connection.describe_target_health(TargetGroupArn=tg['TargetGroupArn'])
                except ClientError as e:
                    module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))

                current_instance_ids = []

                for instance in current_targets['TargetHealthDescriptions']:
                    current_instance_ids.append(instance['Target']['Id'])

                new_instance_ids = []
                for instance in params['Targets']:
                    new_instance_ids.append(instance['Id'])

                add_instances = set(new_instance_ids) - set(current_instance_ids)

                if add_instances:
                    instances_to_add = []
                    for target in params['Targets']:
                        if target['Id'] in add_instances:
                            instances_to_add.append(target)

                    changed = True
                    try:
                        connection.register_targets(TargetGroupArn=tg['TargetGroupArn'], Targets=instances_to_add)
                    except ClientError as e:
                        module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))

                    if module.params.get("wait"):
                        status_achieved, registered_instances = wait_for_status(connection, module, tg['TargetGroupArn'], instances_to_add, 'healthy')
                        if not status_achieved:
                            module.fail_json(msg='Error waiting for target registration - please check the AWS console')

                remove_instances = set(current_instance_ids) - set(new_instance_ids)

                if remove_instances:
                    instances_to_remove = []
                    for target in current_targets['TargetHealthDescriptions']:
                        if target['Target']['Id'] in remove_instances:
                            instances_to_remove.append({'Id': target['Target']['Id'], 'Port': target['Target']['Port']})

                    changed = True
                    try:
                        connection.deregister_targets(TargetGroupArn=tg['TargetGroupArn'], Targets=instances_to_remove)
                    except ClientError as e:
                        module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))

                    if module.params.get("wait"):
                        status_achieved, registered_instances = wait_for_status(connection, module, tg['TargetGroupArn'], instances_to_remove, 'unused')
                        if not status_achieved:
                            module.fail_json(msg='Error waiting for target deregistration - please check the AWS console')
            else:
                try:
                    current_targets = connection.describe_target_health(TargetGroupArn=tg['TargetGroupArn'])
                except ClientError as e:
                    module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))

                current_instances = current_targets['TargetHealthDescriptions']

                if current_instances:
                    instances_to_remove = []
                    for target in current_targets['TargetHealthDescriptions']:
                        instances_to_remove.append({'Id': target['Target']['Id'], 'Port': target['Target']['Port']})

                    changed = True
                    try:
                        connection.deregister_targets(TargetGroupArn=tg['TargetGroupArn'], Targets=instances_to_remove)
                    except ClientError as e:
                        module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))

                    if module.params.get("wait"):
                        status_achieved, registered_instances = wait_for_status(connection, module, tg['TargetGroupArn'], instances_to_remove, 'unused')
                        if not status_achieved:
                            module.fail_json(msg='Error waiting for target deregistration - please check the AWS console')
    else:
        try:
            connection.create_target_group(**params)
            changed = True
            new_target_group = True
        except ClientError as e:
            module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))

        tg = get_target_group(connection, module)

        if module.params.get("targets"):
            params['Targets'] = module.params.get("targets")
            try:
                connection.register_targets(TargetGroupArn=tg['TargetGroupArn'], Targets=params['Targets'])
            except ClientError as e:
                module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))

            if module.params.get("wait"):
                status_achieved, registered_instances = wait_for_status(connection, module, tg['TargetGroupArn'], params['Targets'], 'healthy')
                if not status_achieved:
                    module.fail_json(msg='Error waiting for target registration - please check the AWS console')

    # Now set target group attributes
    update_attributes = []

    # Get current attributes
    current_tg_attributes = get_tg_attributes(connection, module, tg['TargetGroupArn'])

    if deregistration_delay_timeout is not None:
        if str(deregistration_delay_timeout) != current_tg_attributes['deregistration_delay_timeout_seconds']:
            update_attributes.append({'Key': 'deregistration_delay.timeout_seconds', 'Value': str(deregistration_delay_timeout)})
    if stickiness_enabled is not None:
        if stickiness_enabled and current_tg_attributes['stickiness_enabled'] != "true":
            update_attributes.append({'Key': 'stickiness.enabled', 'Value': 'true'})
    if stickiness_lb_cookie_duration is not None:
        if str(stickiness_lb_cookie_duration) != current_tg_attributes['stickiness_lb_cookie_duration_seconds']:
            update_attributes.append({'Key': 'stickiness.lb_cookie.duration_seconds', 'Value': str(stickiness_lb_cookie_duration)})
    if stickiness_type is not None:
        if stickiness_type != current_tg_attributes['stickiness_type']:
            update_attributes.append({'Key': 'stickiness.type', 'Value': stickiness_type})

    if update_attributes:
        try:
            connection.modify_target_group_attributes(TargetGroupArn=tg['TargetGroupArn'], Attributes=update_attributes)
            changed = True
        except ClientError as e:
            # Something went wrong setting attributes. If this target group was created during this task, delete it to leave a consistent state
            if new_target_group:
                connection.delete_target_group(TargetGroupArn=tg['TargetGroupArn'])
            module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))

    # Tags - only need to play with tags if tags parameter has been set to something
    if tags:
        # Get tags
        current_tags = get_target_group_tags(connection, module, tg['TargetGroupArn'])

        # Delete necessary tags
        tags_need_modify, tags_to_delete = compare_aws_tags(boto3_tag_list_to_ansible_dict(current_tags), tags, purge_tags)
        if tags_to_delete:
            try:
                connection.remove_tags(ResourceArns=[tg['TargetGroupArn']], TagKeys=tags_to_delete)
            except ClientError as e:
                module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
            changed = True

        # Add/update tags
        if tags_need_modify:
            try:
                connection.add_tags(ResourceArns=[tg['TargetGroupArn']], Tags=ansible_dict_to_boto3_tag_list(tags_need_modify))
            except ClientError as e:
                module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
            changed = True

    # Get the target group again
    tg = get_target_group(connection, module)

    # Get the target group attributes again
    tg.update(get_tg_attributes(connection, module, tg['TargetGroupArn']))

    # Convert tg to snake_case
    snaked_tg = camel_dict_to_snake_dict(tg)

    snaked_tg['tags'] = boto3_tag_list_to_ansible_dict(get_target_group_tags(connection, module, tg['TargetGroupArn']))

    module.exit_json(changed=changed, **snaked_tg)
Пример #12
0
def create_or_update_elb(elb_obj):
    """Create ELB or modify main attributes. json_exit here"""

    if elb_obj.elb:
        # ELB exists so check subnets, security groups and tags match what has been passed

        # Subnets
        if not elb_obj.compare_subnets():
            elb_obj.modify_subnets()

        # Security Groups
        if not elb_obj.compare_security_groups():
            elb_obj.modify_security_groups()

        # Tags - only need to play with tags if tags parameter has been set to something
        if elb_obj.tags is not None:

            # Delete necessary tags
            tags_need_modify, tags_to_delete = compare_aws_tags(
                boto3_tag_list_to_ansible_dict(elb_obj.elb['tags']),
                boto3_tag_list_to_ansible_dict(elb_obj.tags),
                elb_obj.purge_tags)
            if tags_to_delete:
                elb_obj.delete_tags(tags_to_delete)

            # Add/update tags
            if tags_need_modify:
                elb_obj.modify_tags()

    else:
        # Create load balancer
        elb_obj.create_elb()

    # ELB attributes
    elb_obj.update_elb_attributes()
    elb_obj.modify_elb_attributes()

    # Listeners
    listeners_obj = ELBListeners(elb_obj.connection, elb_obj.module,
                                 elb_obj.elb['LoadBalancerArn'])

    listeners_to_add, listeners_to_modify, listeners_to_delete = listeners_obj.compare_listeners(
    )

    # Delete listeners
    for listener_to_delete in listeners_to_delete:
        listener_obj = ELBListener(elb_obj.connection, elb_obj.module,
                                   listener_to_delete,
                                   elb_obj.elb['LoadBalancerArn'])
        listener_obj.delete()
        listeners_obj.changed = True

    # Add listeners
    for listener_to_add in listeners_to_add:
        listener_obj = ELBListener(elb_obj.connection, elb_obj.module,
                                   listener_to_add,
                                   elb_obj.elb['LoadBalancerArn'])
        listener_obj.add()
        listeners_obj.changed = True

    # Modify listeners
    for listener_to_modify in listeners_to_modify:
        listener_obj = ELBListener(elb_obj.connection, elb_obj.module,
                                   listener_to_modify,
                                   elb_obj.elb['LoadBalancerArn'])
        listener_obj.modify()
        listeners_obj.changed = True

    # If listeners changed, mark ELB as changed
    if listeners_obj.changed:
        elb_obj.changed = True

    # Rules of each listener
    for listener in listeners_obj.listeners:
        if 'Rules' in listener:
            rules_obj = ELBListenerRules(elb_obj.connection, elb_obj.module,
                                         elb_obj.elb['LoadBalancerArn'],
                                         listener['Rules'], listener['Port'])

            rules_to_add, rules_to_modify, rules_to_delete = rules_obj.compare_rules(
            )

            # Delete rules
            if elb_obj.module.params['purge_rules']:
                for rule in rules_to_delete:
                    rule_obj = ELBListenerRule(elb_obj.connection,
                                               elb_obj.module,
                                               {'RuleArn': rule},
                                               rules_obj.listener_arn)
                    rule_obj.delete()
                    elb_obj.changed = True

            # Add rules
            for rule in rules_to_add:
                rule_obj = ELBListenerRule(elb_obj.connection, elb_obj.module,
                                           rule, rules_obj.listener_arn)
                rule_obj.create()
                elb_obj.changed = True

            # Modify rules
            for rule in rules_to_modify:
                rule_obj = ELBListenerRule(elb_obj.connection, elb_obj.module,
                                           rule, rules_obj.listener_arn)
                rule_obj.modify()
                elb_obj.changed = True

    # Get the ELB again
    elb_obj.update()

    # Get the ELB listeners again
    listeners_obj.update()

    # Update the ELB attributes
    elb_obj.update_elb_attributes()

    # Convert to snake_case and merge in everything we want to return to the user
    snaked_elb = camel_dict_to_snake_dict(elb_obj.elb)
    snaked_elb.update(camel_dict_to_snake_dict(elb_obj.elb_attributes))
    snaked_elb['listeners'] = []
    for listener in listeners_obj.current_listeners:
        # For each listener, get listener rules
        listener['rules'] = get_elb_listener_rules(elb_obj.connection,
                                                   elb_obj.module,
                                                   listener['ListenerArn'])
        snaked_elb['listeners'].append(camel_dict_to_snake_dict(listener))

    # Change tags to ansible friendly dict
    snaked_elb['tags'] = boto3_tag_list_to_ansible_dict(snaked_elb['tags'])

    elb_obj.module.exit_json(changed=elb_obj.changed, **snaked_elb)
def ensure_domain_name_present(module, client):
    name = module.params.get('name')
    cert_arn = module.params.get('cert_arn')
    cert_name = module.params.get('cert_name')
    security_policy = module.params.get('security_policy')
    tags = module.params.get('tags')
    purge_tags = module.params.get('purge_tags')

    domain = retrieve_domain_name(module, client, name)
    changed = False

    if cert_arn is None and cert_name is None:
        module.fail_json(
            msg="Certificate ARN or name is required to create a domain name")
        return {'changed': False}

    if domain is None:
        args = dict(domainName=name)
        if cert_arn is None:
            args['certificateName'] = cert_name
        else:
            args['certificateArn'] = cert_arn
        if security_policy not in ['', None]:
            args['securityPolicy'] = security_policy
        if tags != None:
            args['tags'] = tags

        if not module.check_mode:
            domain = backoff_create_domain_name(client, args)
        changed = True
        # Domain will be None when check_mode is true
        if domain is None:
            return {'changed': changed, 'domain_name': {}}

    if domain and tags is not None:
        region, ec2_url, aws_connect_kwargs = get_aws_connection_info(
            module, boto3=True)
        arn = 'arn:aws:apigateway:{0}::/domainnames/{1}'.format(region, name)
        old_tags = domain.get('tags') or {}
        to_tag, to_untag = compare_aws_tags(old_tags,
                                            tags,
                                            purge_tags=purge_tags)
        if to_tag:
            changed |= True
            if not module.check_mode:
                backoff_tag_resource(client, arn, to_tag)
        if to_untag:
            changed |= True
            if not module.check_mode:
                backoff_untag_resource(client, arn, to_untag)

        # need to get new tags
        if changed:
            domain = retrieve_domain_name(module, client, name)

    try:
        patches = []
        if cert_arn not in ['', None] and cert_arn != domain['certificateArn']:
            patches.append({
                'op': 'replace',
                'path': '/certificateArn',
                'value': cert_arn
            })
        if cert_name not in ['', None
                             ] and cert_name != domain['certificateName']:
            patches.append({
                'op': 'replace',
                'path': '/certificateName',
                'value': cert_name
            })
        if security_policy not in [
                '', None
        ] and security_policy != domain.get('securityPolicy'):
            patches.append({
                'op': 'replace',
                'path': '/securityPolicy',
                'value': security_policy
            })

        if patches:
            changed = True

            if not module.check_mode:
                domain = backoff_update_domain_name(client, name, patches)
    except botocore.exceptions.BotoCoreError as e:
        module.fail_json(
            msg="Error when updating domain_name via boto3: {}".format(e))

    # Don't want response metadata. It's not documented as part of return, so not sure why it's here
    domain.pop('ResponseMetadata', None)

    return {
        'changed': changed,
        'domain_name': camel_dict_to_snake_dict(domain),
    }
Пример #14
0
def main():
    argument_spec = ec2_argument_spec()
    argument_spec.update(dict(
        name=dict(),
        group_id=dict(),
        description=dict(),
        vpc_id=dict(),
        rules=dict(type='list'),
        rules_egress=dict(type='list'),
        state=dict(default='present', type='str', choices=['present', 'absent']),
        purge_rules=dict(default=True, required=False, type='bool'),
        purge_rules_egress=dict(default=True, required=False, type='bool'),
        tags=dict(required=False, type='dict', aliases=['resource_tags']),
        purge_tags=dict(default=True, required=False, type='bool')
    )
    )
    module = AnsibleModule(
        argument_spec=argument_spec,
        supports_check_mode=True,
        required_one_of=[['name', 'group_id']],
        required_if=[['state', 'present', ['name']]],
    )

    if not HAS_BOTO3:
        module.fail_json(msg='boto3 required for this module')

    name = module.params['name']
    group_id = module.params['group_id']
    description = module.params['description']
    vpc_id = module.params['vpc_id']
    rules = deduplicate_rules_args(rules_expand_sources(rules_expand_ports(module.params['rules'])))
    rules_egress = deduplicate_rules_args(rules_expand_sources(rules_expand_ports(module.params['rules_egress'])))
    state = module.params.get('state')
    purge_rules = module.params['purge_rules']
    purge_rules_egress = module.params['purge_rules_egress']
    tags = module.params['tags']
    purge_tags = module.params['purge_tags']

    if state == 'present' and not description:
        module.fail_json(msg='Must provide description when state is present.')

    changed = False
    region, ec2_url, aws_connect_params = get_aws_connection_info(module, boto3=True)
    if not region:
        module.fail_json(msg="The AWS region must be specified as an "
                             "environment variable or in the AWS credentials "
                             "profile.")
    client = boto3_conn(module, conn_type='client', resource='ec2', endpoint=ec2_url, region=region, **aws_connect_params)
    group = None
    groups = dict()
    security_groups = []
    # do get all security groups
    # find if the group is present
    try:
        response = get_security_groups_with_backoff(client)
        security_groups = response.get('SecurityGroups', [])
    except botocore.exceptions.NoCredentialsError as e:
        module.fail_json(msg="Error in describe_security_groups: %s" % "Unable to locate credentials", exception=traceback.format_exc())
    except botocore.exceptions.ClientError as e:
        module.fail_json(msg="Error in describe_security_groups: %s" % e, exception=traceback.format_exc(),
                         **camel_dict_to_snake_dict(e.response))

    for sg in security_groups:
        groups[sg['GroupId']] = sg
        groupName = sg['GroupName']
        if groupName in groups:
            # Prioritise groups from the current VPC
            # even if current VPC is EC2-Classic
            if groups[groupName].get('VpcId') == vpc_id:
                # Group saved already matches current VPC, change nothing
                pass
            elif vpc_id is None and groups[groupName].get('VpcId') is None:
                # We're in EC2 classic, and the group already saved is as well
                # No VPC groups can be used alongside EC2 classic groups
                pass
            else:
                # the current SG stored has no direct match, so we can replace it
                groups[groupName] = sg
        else:
            groups[groupName] = sg

        if group_id and sg['GroupId'] == group_id:
            group = sg
        elif groupName == name and (vpc_id is None or sg.get('VpcId') == vpc_id):
            group = sg

    # Ensure requested group is absent
    if state == 'absent':
        if group:
            # found a match, delete it
            try:
                if not module.check_mode:
                    client.delete_security_group(GroupId=group['GroupId'])
            except botocore.exceptions.ClientError as e:
                module.fail_json(msg="Unable to delete security group '%s' - %s" % (group, e),
                                 exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
            else:
                group = None
                changed = True
        else:
            # no match found, no changes required
            pass

    # Ensure requested group is present
    elif state == 'present':
        if group:
            # existing group
            if group['Description'] != description:
                module.warn("Group description does not match existing group. Descriptions cannot be changed without deleting "
                            "and re-creating the security group. Try using state=absent to delete, then rerunning this task.")

        # if the group doesn't exist, create it now
        else:
            # no match found, create it
            if not module.check_mode:
                params = dict(GroupName=name, Description=description)
                if vpc_id:
                    params['VpcId'] = vpc_id
                group = client.create_security_group(**params)
                # When a group is created, an egress_rule ALLOW ALL
                # to 0.0.0.0/0 is added automatically but it's not
                # reflected in the object returned by the AWS API
                # call. We re-read the group for getting an updated object
                # amazon sometimes takes a couple seconds to update the security group so wait till it exists
                while True:
                    group = get_security_groups_with_backoff(client, GroupIds=[group['GroupId']])['SecurityGroups'][0]
                    if group.get('VpcId') and not group.get('IpPermissionsEgress'):
                        pass
                    else:
                        break

            changed = True

        if tags is not None:
            current_tags = boto3_tag_list_to_ansible_dict(group.get('Tags', []))
            tags_need_modify, tags_to_delete = compare_aws_tags(current_tags, tags, purge_tags)
            if tags_to_delete:
                try:
                    client.delete_tags(Resources=[group['GroupId']], Tags=[{'Key': tag} for tag in tags_to_delete])
                except botocore.exceptions.ClientError as e:
                    module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
                changed = True

            # Add/update tags
            if tags_need_modify:
                try:
                    client.create_tags(Resources=[group['GroupId']], Tags=ansible_dict_to_boto3_tag_list(tags_need_modify))
                except botocore.exceptions.ClientError as e:
                    module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
                changed = True

    else:
        module.fail_json(msg="Unsupported state requested: %s" % state)

    # create a lookup for all existing rules on the group
    ip_permission = []
    if group:
        # Manage ingress rules
        groupRules = {}
        add_rules_to_lookup(group['IpPermissions'], group['GroupId'], 'in', groupRules)
        # Now, go through all provided rules and ensure they are there.
        if rules is not None:
            for rule in rules:
                validate_rule(module, rule)
                group_id, ip, ipv6, target_group_created = get_target_from_rule(module, client, rule, name,
                                                                                group, groups, vpc_id)
                if target_group_created:
                    changed = True

                if rule['proto'] in ('all', '-1', -1):
                    rule['proto'] = -1
                    rule['from_port'] = None
                    rule['to_port'] = None

                if group_id:
                    rule_id = make_rule_key('in', rule, group['GroupId'], group_id)
                    if rule_id in groupRules:
                        del groupRules[rule_id]
                    else:
                        if not module.check_mode:
                            ip_permission = serialize_group_grant(group_id, rule)
                            if ip_permission:
                                ips = ip_permission
                                if vpc_id:
                                    [useridpair.update({'VpcId': vpc_id}) for useridpair in
                                     ip_permission.get('UserIdGroupPairs', [])]
                                try:
                                    client.authorize_security_group_ingress(GroupId=group['GroupId'], IpPermissions=[ips])
                                except botocore.exceptions.ClientError as e:
                                    module.fail_json(
                                        msg="Unable to authorize ingress for group %s security group '%s' - %s" %
                                            (group_id, group['GroupName'], e),
                                        exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
                        changed = True
                elif ip:
                    # Convert ip to list we can iterate over
                    if ip and not isinstance(ip, list):
                        ip = [ip]

                    changed, ip_permission = authorize_ip("in", changed, client, group, groupRules, ip, ip_permission,
                                                          module, rule, "ipv4")
                elif ipv6:
                    # Convert ip to list we can iterate over
                    if not isinstance(ipv6, list):
                        ipv6 = [ipv6]
                    # If rule already exists, don't later delete it
                    changed, ip_permission = authorize_ip("in", changed, client, group, groupRules, ipv6, ip_permission,
                                                          module, rule, "ipv6")
        # Finally, remove anything left in the groupRules -- these will be defunct rules
        if purge_rules:
            for (rule, grant) in groupRules.values():
                ip_permission = serialize_revoke(grant, rule)
                if not module.check_mode:
                    try:
                        client.revoke_security_group_ingress(GroupId=group['GroupId'], IpPermissions=[ip_permission])
                    except botocore.exceptions.ClientError as e:
                        module.fail_json(
                            msg="Unable to revoke ingress for security group '%s' - %s" %
                                (group['GroupName'], e),
                            exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
                changed = True

        # Manage egress rules
        groupRules = {}
        add_rules_to_lookup(group['IpPermissionsEgress'], group['GroupId'], 'out', groupRules)
        # Now, go through all provided rules and ensure they are there.
        if rules_egress is not None:
            for rule in rules_egress:
                validate_rule(module, rule)
                group_id, ip, ipv6, target_group_created = get_target_from_rule(module, client, rule, name,
                                                                                group, groups, vpc_id)
                if target_group_created:
                    changed = True

                if rule['proto'] in ('all', '-1', -1):
                    rule['proto'] = -1
                    rule['from_port'] = None
                    rule['to_port'] = None

                if group_id:
                    rule_id = make_rule_key('out', rule, group['GroupId'], group_id)
                    if rule_id in groupRules:
                        del groupRules[rule_id]
                    else:
                        if not module.check_mode:
                            ip_permission = serialize_group_grant(group_id, rule)
                            if ip_permission:
                                ips = ip_permission
                                if vpc_id:
                                    [useridpair.update({'VpcId': vpc_id}) for useridpair in
                                     ip_permission.get('UserIdGroupPairs', [])]
                                try:
                                    client.authorize_security_group_egress(GroupId=group['GroupId'], IpPermissions=[ips])
                                except botocore.exceptions.ClientError as e:
                                    module.fail_json(
                                        msg="Unable to authorize egress for group %s security group '%s' - %s" %
                                            (group_id, group['GroupName'], e),
                                        exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
                        changed = True
                elif ip:
                    # Convert ip to list we can iterate over
                    if not isinstance(ip, list):
                        ip = [ip]
                    changed, ip_permission = authorize_ip("out", changed, client, group, groupRules, ip,
                                                          ip_permission, module, rule, "ipv4")
                elif ipv6:
                    # Convert ip to list we can iterate over
                    if not isinstance(ipv6, list):
                        ipv6 = [ipv6]
                    # If rule already exists, don't later delete it
                    changed, ip_permission = authorize_ip("out", changed, client, group, groupRules, ipv6,
                                                          ip_permission, module, rule, "ipv6")
        elif vpc_id is not None:
            # when no egress rules are specified and we're in a VPC,
            # we add in a default allow all out rule, which was the
            # default behavior before egress rules were added
            default_egress_rule = 'out--1-None-None-' + group['GroupId'] + '-0.0.0.0/0'
            if default_egress_rule not in groupRules:
                if not module.check_mode:
                    ip_permission = [{'IpProtocol': '-1',
                                      'IpRanges': [{'CidrIp': '0.0.0.0/0'}]
                                      }
                                     ]
                    try:
                        client.authorize_security_group_egress(GroupId=group['GroupId'], IpPermissions=ip_permission)
                    except botocore.exceptions.ClientError as e:
                        module.fail_json(msg="Unable to authorize egress for ip %s security group '%s' - %s" %
                                             ('0.0.0.0/0',
                                              group['GroupName'],
                                              e),
                                         exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
                changed = True
            else:
                # make sure the default egress rule is not removed
                del groupRules[default_egress_rule]

        # Finally, remove anything left in the groupRules -- these will be defunct rules
        if purge_rules_egress and vpc_id is not None:
            for (rule, grant) in groupRules.values():
                # we shouldn't be revoking 0.0.0.0 egress
                if grant != '0.0.0.0/0':
                    ip_permission = serialize_revoke(grant, rule)
                    if not module.check_mode:
                        try:
                            client.revoke_security_group_egress(GroupId=group['GroupId'], IpPermissions=[ip_permission])
                        except botocore.exceptions.ClientError as e:
                            module.fail_json(msg="Unable to revoke egress for ip %s security group '%s' - %s" %
                                                 (grant, group['GroupName'], e),
                                             exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
                    changed = True

    if group:
        security_group = get_security_groups_with_backoff(client, GroupIds=[group['GroupId']])['SecurityGroups'][0]
        security_group = camel_dict_to_snake_dict(security_group)
        security_group['tags'] = boto3_tag_list_to_ansible_dict(security_group.get('tags', []),
                                                                tag_name_key_name='key', tag_value_key_name='value')
        module.exit_json(changed=changed, **security_group)
    else:
        module.exit_json(changed=changed, group_id=None)
Пример #15
0
def create_or_update_elb(connection, connection_ec2, module):
    """Create ELB or modify main attributes. json_exit here"""

    changed = False
    new_load_balancer = False
    params = dict()
    params['Name'] = module.params.get("name")
    params['Subnets'] = module.params.get("subnets")
    try:
        params['SecurityGroups'] = get_ec2_security_group_ids_from_names(
            module.params.get('security_groups'), connection_ec2, boto3=True)
    except ValueError as e:
        module.fail_json(msg=str(e), exception=traceback.format_exc())
    except ClientError as e:
        module.fail_json(msg=e.message,
                         exception=traceback.format_exc(),
                         **camel_dict_to_snake_dict(e.response))
    except NoCredentialsError as e:
        module.fail_json(msg="AWS authentication problem. " + e.message,
                         exception=traceback.format_exc())

    params['Scheme'] = module.params.get("scheme")
    if module.params.get("tags"):
        params['Tags'] = ansible_dict_to_boto3_tag_list(
            module.params.get("tags"))
    purge_tags = module.params.get("purge_tags")
    access_logs_enabled = module.params.get("access_logs_enabled")
    access_logs_s3_bucket = module.params.get("access_logs_s3_bucket")
    access_logs_s3_prefix = module.params.get("access_logs_s3_prefix")
    deletion_protection = module.params.get("deletion_protection")
    idle_timeout = module.params.get("idle_timeout")

    # Does the ELB currently exist?
    elb = get_elb(connection, module)

    if elb:
        # ELB exists so check subnets, security groups and tags match what has been passed

        # Subnets
        if set(_get_subnet_ids_from_subnet_list(
                elb['AvailabilityZones'])) != set(params['Subnets']):
            try:
                connection.set_subnets(LoadBalancerArn=elb['LoadBalancerArn'],
                                       Subnets=params['Subnets'])
            except ClientError as e:
                module.fail_json(msg=e.message,
                                 exception=traceback.format_exc(),
                                 **camel_dict_to_snake_dict(e.response))
            changed = True

        # Security Groups
        if set(elb['SecurityGroups']) != set(params['SecurityGroups']):
            try:
                connection.set_security_groups(
                    LoadBalancerArn=elb['LoadBalancerArn'],
                    SecurityGroups=params['SecurityGroups'])
            except ClientError as e:
                module.fail_json(msg=e.message,
                                 exception=traceback.format_exc(),
                                 **camel_dict_to_snake_dict(e.response))
            changed = True

        # Tags - only need to play with tags if tags parameter has been set to something
        if module.params.get("tags"):
            try:
                elb_tags = connection.describe_tags(
                    ResourceArns=[elb['LoadBalancerArn']
                                  ])['TagDescriptions'][0]['Tags']
            except ClientError as e:
                module.fail_json(msg=e.message,
                                 exception=traceback.format_exc(),
                                 **camel_dict_to_snake_dict(e.response))

            # Delete necessary tags
            tags_need_modify, tags_to_delete = compare_aws_tags(
                boto3_tag_list_to_ansible_dict(elb_tags),
                boto3_tag_list_to_ansible_dict(params['Tags']), purge_tags)
            if tags_to_delete:
                try:
                    connection.remove_tags(
                        ResourceArns=[elb['LoadBalancerArn']],
                        TagKeys=tags_to_delete)
                except ClientError as e:
                    module.fail_json(msg=e.message,
                                     exception=traceback.format_exc(),
                                     **camel_dict_to_snake_dict(e.response))
                changed = True

            # Add/update tags
            if tags_need_modify:
                try:
                    connection.add_tags(ResourceArns=[elb['LoadBalancerArn']],
                                        Tags=params['Tags'])
                except ClientError as e:
                    module.fail_json(msg=e.message,
                                     exception=traceback.format_exc(),
                                     **camel_dict_to_snake_dict(e.response))
                changed = True

    else:
        try:
            elb = connection.create_load_balancer(**params)['LoadBalancers'][0]
            changed = True
            new_load_balancer = True
        except ClientError as e:
            module.fail_json(msg=e.message,
                             exception=traceback.format_exc(),
                             **camel_dict_to_snake_dict(e.response))

        if module.params.get("wait"):
            status_achieved, new_elb = wait_for_status(connection, module,
                                                       elb['LoadBalancerArn'],
                                                       'active')

    # Now set ELB attributes. Use try statement here so we can remove the ELB if this stage fails
    update_attributes = []

    # Get current attributes
    current_elb_attributes = get_elb_attributes(connection, module,
                                                elb['LoadBalancerArn'])

    if access_logs_enabled and current_elb_attributes[
            'access_logs_s3_enabled'] != "true":
        update_attributes.append({
            'Key': 'access_logs.s3.enabled',
            'Value': "true"
        })
    if not access_logs_enabled and current_elb_attributes[
            'access_logs_s3_enabled'] != "false":
        update_attributes.append({
            'Key': 'access_logs.s3.enabled',
            'Value': 'false'
        })
    if access_logs_s3_bucket is not None and access_logs_s3_bucket != current_elb_attributes[
            'access_logs_s3_bucket']:
        update_attributes.append({
            'Key': 'access_logs.s3.bucket',
            'Value': access_logs_s3_bucket
        })
    if access_logs_s3_prefix is not None and access_logs_s3_prefix != current_elb_attributes[
            'access_logs_s3_prefix']:
        update_attributes.append({
            'Key': 'access_logs.s3.prefix',
            'Value': access_logs_s3_prefix
        })
    if deletion_protection and current_elb_attributes[
            'deletion_protection_enabled'] != "true":
        update_attributes.append({
            'Key': 'deletion_protection.enabled',
            'Value': "true"
        })
    if not deletion_protection and current_elb_attributes[
            'deletion_protection_enabled'] != "false":
        update_attributes.append({
            'Key': 'deletion_protection.enabled',
            'Value': "false"
        })
    if idle_timeout is not None and str(
            idle_timeout
    ) != current_elb_attributes['idle_timeout_timeout_seconds']:
        update_attributes.append({
            'Key': 'idle_timeout.timeout_seconds',
            'Value': str(idle_timeout)
        })

    if update_attributes:
        try:
            connection.modify_load_balancer_attributes(
                LoadBalancerArn=elb['LoadBalancerArn'],
                Attributes=update_attributes)
            changed = True
        except ClientError as e:
            # Something went wrong setting attributes. If this ELB was created during this task, delete it to leave a consistent state
            if new_load_balancer:
                connection.delete_load_balancer(
                    LoadBalancerArn=elb['LoadBalancerArn'])
            module.fail_json(msg=e.message,
                             exception=traceback.format_exc(),
                             **camel_dict_to_snake_dict(e.response))

    # Now, if required, set ELB listeners. Use try statement here so we can remove the ELB if this stage fails
    try:
        listener_changed = create_or_update_elb_listeners(
            connection, module, elb)
        if listener_changed:
            changed = True
    except ClientError as e:
        # Something went wrong setting listeners. If this ELB was created during this task, delete it to leave a consistent state
        if new_load_balancer:
            connection.delete_load_balancer(
                LoadBalancerArn=elb['LoadBalancerArn'])
        module.fail_json(msg=e.message,
                         exception=traceback.format_exc(),
                         **camel_dict_to_snake_dict(e.response))

    # Get the ELB again
    elb = get_elb(connection, module)

    # Get the ELB listeners again
    elb['listeners'] = get_elb_listeners(connection, module,
                                         elb['LoadBalancerArn'])

    # For each listener, get listener rules
    for listener in elb['listeners']:
        listener['rules'] = get_listener_rules(connection, module,
                                               listener['ListenerArn'])

    # Get the ELB attributes again
    elb.update(get_elb_attributes(connection, module, elb['LoadBalancerArn']))

    # Convert to snake_case
    snaked_elb = camel_dict_to_snake_dict(elb)

    # Get the tags of the ELB
    elb_tags = connection.describe_tags(
        ResourceArns=[elb['LoadBalancerArn']])['TagDescriptions'][0]['Tags']
    snaked_elb['tags'] = boto3_tag_list_to_ansible_dict(elb_tags)

    module.exit_json(changed=changed, **snaked_elb)
Пример #16
0
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)
Пример #17
0
def create_or_update_elb(elb_obj):
    """Create ELB or modify main attributes. json_exit here"""

    if elb_obj.elb:
        # ELB exists so check subnets, security groups and tags match what has been passed

        # Subnets
        if not elb_obj.compare_subnets():
            elb_obj.modify_subnets()

        # Security Groups
        if not elb_obj.compare_security_groups():
            elb_obj.modify_security_groups()

        # Tags - only need to play with tags if tags parameter has been set to something
        if elb_obj.tags is not None:

            # Delete necessary tags
            tags_need_modify, tags_to_delete = compare_aws_tags(boto3_tag_list_to_ansible_dict(elb_obj.elb['tags']),
                                                                boto3_tag_list_to_ansible_dict(elb_obj.tags), elb_obj.purge_tags)
            if tags_to_delete:
                elb_obj.delete_tags(tags_to_delete)

            # Add/update tags
            if tags_need_modify:
                elb_obj.modify_tags()

    else:
        # Create load balancer
        elb_obj.create_elb()

    # ELB attributes
    elb_obj.update_elb_attributes()
    elb_obj.modify_elb_attributes()

    # Listeners
    listeners_obj = ELBListeners(elb_obj.connection, elb_obj.module, elb_obj.elb['LoadBalancerArn'])

    listeners_to_add, listeners_to_modify, listeners_to_delete = listeners_obj.compare_listeners()

    # Delete listeners
    for listener_to_delete in listeners_to_delete:
        listener_obj = ELBListener(elb_obj.connection, elb_obj.module, listener_to_delete, elb_obj.elb['LoadBalancerArn'])
        listener_obj.delete()
        listeners_obj.changed = True

    # Add listeners
    for listener_to_add in listeners_to_add:
        listener_obj = ELBListener(elb_obj.connection, elb_obj.module, listener_to_add, elb_obj.elb['LoadBalancerArn'])
        listener_obj.add()
        listeners_obj.changed = True

    # Modify listeners
    for listener_to_modify in listeners_to_modify:
        listener_obj = ELBListener(elb_obj.connection, elb_obj.module, listener_to_modify, elb_obj.elb['LoadBalancerArn'])
        listener_obj.modify()
        listeners_obj.changed = True

    # If listeners changed, mark ELB as changed
    if listeners_obj.changed:
        elb_obj.changed = True

    # Rules of each listener
    for listener in listeners_obj.listeners:
        if 'Rules' in listener:
            rules_obj = ELBListenerRules(elb_obj.connection, elb_obj.module, elb_obj.elb['LoadBalancerArn'], listener['Rules'], listener['Port'])

            rules_to_add, rules_to_modify, rules_to_delete = rules_obj.compare_rules()

            # Delete rules
            for rule in rules_to_delete:
                rule_obj = ELBListenerRule(elb_obj.connection, elb_obj.module, {'RuleArn': rule}, rules_obj.listener_arn)
                rule_obj.delete()
                elb_obj.changed = True

            # Add rules
            for rule in rules_to_add:
                rule_obj = ELBListenerRule(elb_obj.connection, elb_obj.module, rule, rules_obj.listener_arn)
                rule_obj.create()
                elb_obj.changed = True

            # Modify rules
            for rule in rules_to_modify:
                rule_obj = ELBListenerRule(elb_obj.connection, elb_obj.module, rule, rules_obj.listener_arn)
                rule_obj.modify()
                elb_obj.changed = True

    # Get the ELB again
    elb_obj.update()

    # Get the ELB listeners again
    listeners_obj.update()

    # Update the ELB attributes
    elb_obj.update_elb_attributes()

    # Convert to snake_case and merge in everything we want to return to the user
    snaked_elb = camel_dict_to_snake_dict(elb_obj.elb)
    snaked_elb.update(camel_dict_to_snake_dict(elb_obj.elb_attributes))
    snaked_elb['listeners'] = []
    for listener in listeners_obj.current_listeners:
        # For each listener, get listener rules
        listener['rules'] = get_elb_listener_rules(elb_obj.connection, elb_obj.module, listener['ListenerArn'])
        snaked_elb['listeners'].append(camel_dict_to_snake_dict(listener))

    # Change tags to ansible friendly dict
    snaked_elb['tags'] = boto3_tag_list_to_ansible_dict(snaked_elb['tags'])

    elb_obj.module.exit_json(changed=elb_obj.changed, **snaked_elb)
Пример #18
0
    def ensure_tags(self, igw_id, tags, add_only):
        final_tags = []

        filters = ansible_dict_to_boto3_filter_list({
            'resource-id':
            igw_id,
            'resource-type':
            'internet-gateway'
        })
        cur_tags = None
        try:
            cur_tags = self._connection.describe_tags(Filters=filters)
        except (botocore.exceptions.ClientError,
                botocore.exceptions.BotoCoreError) as e:
            self._module.fail_json_aws(e, msg="Couldn't describe tags")

        purge_tags = bool(not add_only)
        to_update, to_delete = compare_aws_tags(
            boto3_tag_list_to_ansible_dict(cur_tags.get('Tags')), tags,
            purge_tags)
        final_tags = boto3_tag_list_to_ansible_dict(cur_tags.get('Tags'))

        if to_update:
            try:
                if self._check_mode:
                    # update tags
                    final_tags.update(to_update)
                else:
                    AWSRetry.exponential_backoff()(
                        self._connection.create_tags)(
                            Resources=[igw_id],
                            Tags=ansible_dict_to_boto3_tag_list(to_update))

                self._results['changed'] = True
            except (botocore.exceptions.ClientError,
                    botocore.exceptions.BotoCoreError) as e:
                self._module.fail_json_aws(e, msg="Couldn't create tags")

        if to_delete:
            try:
                if self._check_mode:
                    # update tags
                    for key in to_delete:
                        del final_tags[key]
                else:
                    tags_list = []
                    for key in to_delete:
                        tags_list.append({'Key': key})

                    AWSRetry.exponential_backoff()(
                        self._connection.delete_tags)(Resources=[igw_id],
                                                      Tags=tags_list)

                self._results['changed'] = True
            except (botocore.exceptions.ClientError,
                    botocore.exceptions.BotoCoreError) as e:
                self._module.fail_json_aws(e, msg="Couldn't delete tags")

        if not self._check_mode and (to_update or to_delete):
            try:
                response = self._connection.describe_tags(Filters=filters)
                final_tags = boto3_tag_list_to_ansible_dict(
                    response.get('Tags'))
            except (botocore.exceptions.ClientError,
                    botocore.exceptions.BotoCoreError) as e:
                self._module.fail_json_aws(e, msg="Couldn't describe tags")

        return final_tags
Пример #19
0
def main():
    argument_spec = ec2_argument_spec()
    argument_spec.update(
        dict(name=dict(),
             group_id=dict(),
             description=dict(),
             vpc_id=dict(),
             rules=dict(type='list'),
             rules_egress=dict(type='list'),
             state=dict(default='present',
                        type='str',
                        choices=['present', 'absent']),
             purge_rules=dict(default=True, required=False, type='bool'),
             purge_rules_egress=dict(default=True, required=False,
                                     type='bool'),
             tags=dict(required=False, type='dict', aliases=['resource_tags']),
             purge_tags=dict(default=True, required=False, type='bool')))
    module = AnsibleModule(
        argument_spec=argument_spec,
        supports_check_mode=True,
        required_one_of=[['name', 'group_id']],
        required_if=[['state', 'present', ['name']]],
    )

    if not HAS_BOTO3:
        module.fail_json(msg='boto3 required for this module')

    name = module.params['name']
    group_id = module.params['group_id']
    description = module.params['description']
    vpc_id = module.params['vpc_id']
    rules = deduplicate_rules_args(
        rules_expand_sources(rules_expand_ports(module.params['rules'])))
    rules_egress = deduplicate_rules_args(
        rules_expand_sources(rules_expand_ports(
            module.params['rules_egress'])))
    state = module.params.get('state')
    purge_rules = module.params['purge_rules']
    purge_rules_egress = module.params['purge_rules_egress']
    tags = module.params['tags']
    purge_tags = module.params['purge_tags']

    if state == 'present' and not description:
        module.fail_json(msg='Must provide description when state is present.')

    changed = False
    region, ec2_url, aws_connect_params = get_aws_connection_info(module,
                                                                  boto3=True)
    if not region:
        module.fail_json(msg="The AWS region must be specified as an "
                         "environment variable or in the AWS credentials "
                         "profile.")
    client = boto3_conn(module,
                        conn_type='client',
                        resource='ec2',
                        endpoint=ec2_url,
                        region=region,
                        **aws_connect_params)
    group = None
    groups = dict()
    security_groups = []
    # do get all security groups
    # find if the group is present
    try:
        response = get_security_groups_with_backoff(client)
        security_groups = response.get('SecurityGroups', [])
    except botocore.exceptions.NoCredentialsError as e:
        module.fail_json(msg="Error in describe_security_groups: %s" %
                         "Unable to locate credentials",
                         exception=traceback.format_exc())
    except botocore.exceptions.ClientError as e:
        module.fail_json(msg="Error in describe_security_groups: %s" % e,
                         exception=traceback.format_exc(),
                         **camel_dict_to_snake_dict(e.response))

    for sg in security_groups:
        groups[sg['GroupId']] = sg
        groupName = sg['GroupName']
        if groupName in groups:
            # Prioritise groups from the current VPC
            # even if current VPC is EC2-Classic
            if groups[groupName].get('VpcId') == vpc_id:
                # Group saved already matches current VPC, change nothing
                pass
            elif vpc_id is None and groups[groupName].get('VpcId') is None:
                # We're in EC2 classic, and the group already saved is as well
                # No VPC groups can be used alongside EC2 classic groups
                pass
            else:
                # the current SG stored has no direct match, so we can replace it
                groups[groupName] = sg
        else:
            groups[groupName] = sg

        if group_id and sg['GroupId'] == group_id:
            group = sg
        elif groupName == name and (vpc_id is None or sg['VpcId'] == vpc_id):
            group = sg

    # Ensure requested group is absent
    if state == 'absent':
        if group:
            # found a match, delete it
            try:
                if not module.check_mode:
                    client.delete_security_group(GroupId=group['GroupId'])
            except botocore.exceptions.ClientError as e:
                module.fail_json(
                    msg="Unable to delete security group '%s' - %s" %
                    (group, e),
                    exception=traceback.format_exc(),
                    **camel_dict_to_snake_dict(e.response))
            else:
                group = None
                changed = True
        else:
            # no match found, no changes required
            pass

    # Ensure requested group is present
    elif state == 'present':
        if group:
            # existing group
            if group['Description'] != description:
                module.fail_json(
                    msg=
                    "Group description does not match existing group. ec2_group does not support this case."
                )

        # if the group doesn't exist, create it now
        else:
            # no match found, create it
            if not module.check_mode:
                params = dict(GroupName=name, Description=description)
                if vpc_id:
                    params['VpcId'] = vpc_id
                group = client.create_security_group(**params)
                # When a group is created, an egress_rule ALLOW ALL
                # to 0.0.0.0/0 is added automatically but it's not
                # reflected in the object returned by the AWS API
                # call. We re-read the group for getting an updated object
                # amazon sometimes takes a couple seconds to update the security group so wait till it exists
                while True:
                    group = get_security_groups_with_backoff(
                        client,
                        GroupIds=[group['GroupId']])['SecurityGroups'][0]
                    if group.get(
                            'VpcId') and not group.get('IpPermissionsEgress'):
                        pass
                    else:
                        break

            changed = True

        if tags is not None:
            current_tags = boto3_tag_list_to_ansible_dict(group.get(
                'Tags', []))
            tags_need_modify, tags_to_delete = compare_aws_tags(
                current_tags, tags, purge_tags)
            if tags_to_delete:
                try:
                    client.delete_tags(Resources=[group['GroupId']],
                                       Tags=[{
                                           'Key': tag
                                       } for tag in tags_to_delete])
                except botocore.exceptions.ClientError as e:
                    module.fail_json(msg=e.message,
                                     exception=traceback.format_exc(),
                                     **camel_dict_to_snake_dict(e.response))
                changed = True

            # Add/update tags
            if tags_need_modify:
                try:
                    client.create_tags(
                        Resources=[group['GroupId']],
                        Tags=ansible_dict_to_boto3_tag_list(tags_need_modify))
                except botocore.exceptions.ClientError as e:
                    module.fail_json(msg=e.message,
                                     exception=traceback.format_exc(),
                                     **camel_dict_to_snake_dict(e.response))
                changed = True

    else:
        module.fail_json(msg="Unsupported state requested: %s" % state)

    # create a lookup for all existing rules on the group
    ip_permission = []
    if group:
        # Manage ingress rules
        groupRules = {}
        add_rules_to_lookup(group['IpPermissions'], group['GroupId'], 'in',
                            groupRules)
        # Now, go through all provided rules and ensure they are there.
        if rules is not None:
            for rule in rules:
                validate_rule(module, rule)
                group_id, ip, ipv6, target_group_created = get_target_from_rule(
                    module, client, rule, name, group, groups, vpc_id)
                if target_group_created:
                    changed = True

                if rule['proto'] in ('all', '-1', -1):
                    rule['proto'] = -1
                    rule['from_port'] = None
                    rule['to_port'] = None

                if group_id:
                    rule_id = make_rule_key('in', rule, group['GroupId'],
                                            group_id)
                    if rule_id in groupRules:
                        del groupRules[rule_id]
                    else:
                        if not module.check_mode:
                            ip_permission = serialize_group_grant(
                                group_id, rule)
                            if ip_permission:
                                ips = ip_permission
                                if vpc_id:
                                    [
                                        useridpair.update({'VpcId': vpc_id})
                                        for useridpair in ip_permission.get(
                                            'UserIdGroupPairs', [])
                                    ]
                                try:
                                    client.authorize_security_group_ingress(
                                        GroupId=group['GroupId'],
                                        IpPermissions=[ips])
                                except botocore.exceptions.ClientError as e:
                                    module.fail_json(
                                        msg=
                                        "Unable to authorize ingress for group %s security group '%s' - %s"
                                        % (group_id, group['GroupName'], e),
                                        exception=traceback.format_exc(),
                                        **camel_dict_to_snake_dict(e.response))
                        changed = True
                elif ip:
                    # Convert ip to list we can iterate over
                    if ip and not isinstance(ip, list):
                        ip = [ip]

                    changed, ip_permission = authorize_ip(
                        "in", changed, client, group, groupRules, ip,
                        ip_permission, module, rule, "ipv4")
                elif ipv6:
                    # Convert ip to list we can iterate over
                    if not isinstance(ipv6, list):
                        ipv6 = [ipv6]
                    # If rule already exists, don't later delete it
                    changed, ip_permission = authorize_ip(
                        "in", changed, client, group, groupRules, ipv6,
                        ip_permission, module, rule, "ipv6")
        # Finally, remove anything left in the groupRules -- these will be defunct rules
        if purge_rules:
            for (rule, grant) in groupRules.values():
                ip_permission = serialize_revoke(grant, rule)
                if not module.check_mode:
                    try:
                        client.revoke_security_group_ingress(
                            GroupId=group['GroupId'],
                            IpPermissions=[ip_permission])
                    except botocore.exceptions.ClientError as e:
                        module.fail_json(
                            msg=
                            "Unable to revoke ingress for security group '%s' - %s"
                            % (group['GroupName'], e),
                            exception=traceback.format_exc(),
                            **camel_dict_to_snake_dict(e.response))
                changed = True

        # Manage egress rules
        groupRules = {}
        add_rules_to_lookup(group['IpPermissionsEgress'], group['GroupId'],
                            'out', groupRules)
        # Now, go through all provided rules and ensure they are there.
        if rules_egress is not None:
            for rule in rules_egress:
                validate_rule(module, rule)
                group_id, ip, ipv6, target_group_created = get_target_from_rule(
                    module, client, rule, name, group, groups, vpc_id)
                if target_group_created:
                    changed = True

                if rule['proto'] in ('all', '-1', -1):
                    rule['proto'] = -1
                    rule['from_port'] = None
                    rule['to_port'] = None

                if group_id:
                    rule_id = make_rule_key('out', rule, group['GroupId'],
                                            group_id)
                    if rule_id in groupRules:
                        del groupRules[rule_id]
                    else:
                        if not module.check_mode:
                            ip_permission = serialize_group_grant(
                                group_id, rule)
                            if ip_permission:
                                ips = ip_permission
                                if vpc_id:
                                    [
                                        useridpair.update({'VpcId': vpc_id})
                                        for useridpair in ip_permission.get(
                                            'UserIdGroupPairs', [])
                                    ]
                                try:
                                    client.authorize_security_group_egress(
                                        GroupId=group['GroupId'],
                                        IpPermissions=[ips])
                                except botocore.exceptions.ClientError as e:
                                    module.fail_json(
                                        msg=
                                        "Unable to authorize egress for group %s security group '%s' - %s"
                                        % (group_id, group['GroupName'], e),
                                        exception=traceback.format_exc(),
                                        **camel_dict_to_snake_dict(e.response))
                        changed = True
                elif ip:
                    # Convert ip to list we can iterate over
                    if not isinstance(ip, list):
                        ip = [ip]
                    changed, ip_permission = authorize_ip(
                        "out", changed, client, group, groupRules, ip,
                        ip_permission, module, rule, "ipv4")
                elif ipv6:
                    # Convert ip to list we can iterate over
                    if not isinstance(ipv6, list):
                        ipv6 = [ipv6]
                    # If rule already exists, don't later delete it
                    changed, ip_permission = authorize_ip(
                        "out", changed, client, group, groupRules, ipv6,
                        ip_permission, module, rule, "ipv6")
        elif vpc_id is not None:
            # when no egress rules are specified and we're in a VPC,
            # we add in a default allow all out rule, which was the
            # default behavior before egress rules were added
            default_egress_rule = 'out--1-None-None-' + group[
                'GroupId'] + '-0.0.0.0/0'
            if default_egress_rule not in groupRules:
                if not module.check_mode:
                    ip_permission = [{
                        'IpProtocol': '-1',
                        'IpRanges': [{
                            'CidrIp': '0.0.0.0/0'
                        }]
                    }]
                    try:
                        client.authorize_security_group_egress(
                            GroupId=group['GroupId'],
                            IpPermissions=ip_permission)
                    except botocore.exceptions.ClientError as e:
                        module.fail_json(
                            msg=
                            "Unable to authorize egress for ip %s security group '%s' - %s"
                            % ('0.0.0.0/0', group['GroupName'], e),
                            exception=traceback.format_exc(),
                            **camel_dict_to_snake_dict(e.response))
                changed = True
            else:
                # make sure the default egress rule is not removed
                del groupRules[default_egress_rule]

        # Finally, remove anything left in the groupRules -- these will be defunct rules
        if purge_rules_egress and vpc_id is not None:
            for (rule, grant) in groupRules.values():
                # we shouldn't be revoking 0.0.0.0 egress
                if grant != '0.0.0.0/0':
                    ip_permission = serialize_revoke(grant, rule)
                    if not module.check_mode:
                        try:
                            client.revoke_security_group_egress(
                                GroupId=group['GroupId'],
                                IpPermissions=[ip_permission])
                        except botocore.exceptions.ClientError as e:
                            module.fail_json(
                                msg=
                                "Unable to revoke egress for ip %s security group '%s' - %s"
                                % (grant, group['GroupName'], e),
                                exception=traceback.format_exc(),
                                **camel_dict_to_snake_dict(e.response))
                    changed = True

    if group:
        security_group = get_security_groups_with_backoff(
            client, GroupIds=[group['GroupId']])['SecurityGroups'][0]
        security_group = camel_dict_to_snake_dict(security_group)
        security_group['tags'] = boto3_tag_list_to_ansible_dict(
            security_group.get('tags', []),
            tag_name_key_name='key',
            tag_value_key_name='value')
        module.exit_json(changed=changed, **security_group)
    else:
        module.exit_json(changed=changed, group_id=None)
Пример #20
0
def create_or_update_target_group(connection, module):

    changed = False
    new_target_group = False
    params = dict()
    params['Name'] = module.params.get("name")
    params['Protocol'] = module.params.get("protocol").upper()
    params['Port'] = module.params.get("port")
    params['VpcId'] = module.params.get("vpc_id")
    tags = module.params.get("tags")
    purge_tags = module.params.get("purge_tags")
    deregistration_delay_timeout = module.params.get(
        "deregistration_delay_timeout")
    stickiness_enabled = module.params.get("stickiness_enabled")
    stickiness_lb_cookie_duration = module.params.get(
        "stickiness_lb_cookie_duration")
    stickiness_type = module.params.get("stickiness_type")

    # If health check path not None, set health check attributes
    if module.params.get("health_check_path") is not None:
        params['HealthCheckPath'] = module.params.get("health_check_path")

        if module.params.get("health_check_protocol") is not None:
            params['HealthCheckProtocol'] = module.params.get(
                "health_check_protocol").upper()

        if module.params.get("health_check_port") is not None:
            params['HealthCheckPort'] = module.params.get("health_check_port")

        if module.params.get("health_check_interval") is not None:
            params['HealthCheckIntervalSeconds'] = module.params.get(
                "health_check_interval")

        if module.params.get("health_check_timeout") is not None:
            params['HealthCheckTimeoutSeconds'] = module.params.get(
                "health_check_timeout")

        if module.params.get("healthy_threshold_count") is not None:
            params['HealthyThresholdCount'] = module.params.get(
                "healthy_threshold_count")

        if module.params.get("unhealthy_threshold_count") is not None:
            params['UnhealthyThresholdCount'] = module.params.get(
                "unhealthy_threshold_count")

        if module.params.get("successful_response_codes") is not None:
            params['Matcher'] = {}
            params['Matcher']['HttpCode'] = module.params.get(
                "successful_response_codes")

    # Get target type
    if module.params.get("target_type") is not None:
        params['TargetType'] = module.params.get("target_type")
        if params['TargetType'] == 'ip':
            fail_if_ip_target_type_not_supported(module)

    # Get target group
    tg = get_target_group(connection, module)

    if tg:
        diffs = [
            param for param in ('Port', 'Protocol', 'VpcId')
            if tg.get(param) != params.get(param)
        ]
        if diffs:
            module.fail_json(
                msg="Cannot modify %s parameter(s) for a target group" %
                ", ".join(diffs))
        # Target group exists so check health check parameters match what has been passed
        health_check_params = dict()

        # If we have no health check path then we have nothing to modify
        if module.params.get("health_check_path") is not None:
            # Health check protocol
            if 'HealthCheckProtocol' in params and tg[
                    'HealthCheckProtocol'] != params['HealthCheckProtocol']:
                health_check_params['HealthCheckProtocol'] = params[
                    'HealthCheckProtocol']

            # Health check port
            if 'HealthCheckPort' in params and tg['HealthCheckPort'] != params[
                    'HealthCheckPort']:
                health_check_params['HealthCheckPort'] = params[
                    'HealthCheckPort']

            # Health check path
            if 'HealthCheckPath' in params and tg['HealthCheckPath'] != params[
                    'HealthCheckPath']:
                health_check_params['HealthCheckPath'] = params[
                    'HealthCheckPath']

            # Health check interval
            if 'HealthCheckIntervalSeconds' in params and tg[
                    'HealthCheckIntervalSeconds'] != params[
                        'HealthCheckIntervalSeconds']:
                health_check_params['HealthCheckIntervalSeconds'] = params[
                    'HealthCheckIntervalSeconds']

            # Health check timeout
            if 'HealthCheckTimeoutSeconds' in params and tg[
                    'HealthCheckTimeoutSeconds'] != params[
                        'HealthCheckTimeoutSeconds']:
                health_check_params['HealthCheckTimeoutSeconds'] = params[
                    'HealthCheckTimeoutSeconds']

            # Healthy threshold
            if 'HealthyThresholdCount' in params and tg[
                    'HealthyThresholdCount'] != params['HealthyThresholdCount']:
                health_check_params['HealthyThresholdCount'] = params[
                    'HealthyThresholdCount']

            # Unhealthy threshold
            if 'UnhealthyThresholdCount' in params and tg[
                    'UnhealthyThresholdCount'] != params[
                        'UnhealthyThresholdCount']:
                health_check_params['UnhealthyThresholdCount'] = params[
                    'UnhealthyThresholdCount']

            # Matcher (successful response codes)
            # TODO: required and here?
            if 'Matcher' in params:
                current_matcher_list = tg['Matcher']['HttpCode'].split(',')
                requested_matcher_list = params['Matcher']['HttpCode'].split(
                    ',')
                if set(current_matcher_list) != set(requested_matcher_list):
                    health_check_params['Matcher'] = {}
                    health_check_params['Matcher']['HttpCode'] = ','.join(
                        requested_matcher_list)

            try:
                if health_check_params:
                    connection.modify_target_group(
                        TargetGroupArn=tg['TargetGroupArn'],
                        **health_check_params)
                    changed = True
            except (botocore.exceptions.ClientError,
                    botocore.exceptions.BotoCoreError) as e:
                module.fail_json_aws(e, msg="Couldn't update target group")

        # Do we need to modify targets?
        if module.params.get("modify_targets"):
            if module.params.get("targets"):
                params['Targets'] = module.params.get("targets")

                # get list of current target instances. I can't see anything like a describe targets in the doco so
                # describe_target_health seems to be the only way to get them

                try:
                    current_targets = connection.describe_target_health(
                        TargetGroupArn=tg['TargetGroupArn'])
                except (botocore.exceptions.ClientError,
                        botocore.exceptions.BotoCoreError) as e:
                    module.fail_json_aws(
                        e, msg="Couldn't get target group health")

                current_instance_ids = []

                for instance in current_targets['TargetHealthDescriptions']:
                    current_instance_ids.append(instance['Target']['Id'])

                new_instance_ids = []
                for instance in params['Targets']:
                    new_instance_ids.append(instance['Id'])

                add_instances = set(new_instance_ids) - set(
                    current_instance_ids)

                if add_instances:
                    instances_to_add = []
                    for target in params['Targets']:
                        if target['Id'] in add_instances:
                            instances_to_add.append({
                                'Id':
                                target['Id'],
                                'Port':
                                int(
                                    target.get('Port',
                                               module.params.get('port')))
                            })

                    changed = True
                    try:
                        connection.register_targets(
                            TargetGroupArn=tg['TargetGroupArn'],
                            Targets=instances_to_add)
                    except (botocore.exceptions.ClientError,
                            botocore.exceptions.BotoCoreError) as e:
                        module.fail_json_aws(e,
                                             msg="Couldn't register targets")

                    if module.params.get("wait"):
                        status_achieved, registered_instances = wait_for_status(
                            connection, module, tg['TargetGroupArn'],
                            instances_to_add, 'healthy')
                        if not status_achieved:
                            module.fail_json(
                                msg=
                                'Error waiting for target registration to be healthy - please check the AWS console'
                            )

                remove_instances = set(current_instance_ids) - set(
                    new_instance_ids)

                if remove_instances:
                    instances_to_remove = []
                    for target in current_targets['TargetHealthDescriptions']:
                        if target['Target']['Id'] in remove_instances:
                            instances_to_remove.append({
                                'Id':
                                target['Target']['Id'],
                                'Port':
                                target['Target']['Port']
                            })

                    changed = True
                    try:
                        connection.deregister_targets(
                            TargetGroupArn=tg['TargetGroupArn'],
                            Targets=instances_to_remove)
                    except (botocore.exceptions.ClientError,
                            botocore.exceptions.BotoCoreError) as e:
                        module.fail_json_aws(e, msg="Couldn't remove targets")

                    if module.params.get("wait"):
                        status_achieved, registered_instances = wait_for_status(
                            connection, module, tg['TargetGroupArn'],
                            instances_to_remove, 'unused')
                        if not status_achieved:
                            module.fail_json(
                                msg=
                                'Error waiting for target deregistration - please check the AWS console'
                            )
            else:
                try:
                    current_targets = connection.describe_target_health(
                        TargetGroupArn=tg['TargetGroupArn'])
                except (botocore.exceptions.ClientError,
                        botocore.exceptions.BotoCoreError) as e:
                    module.fail_json_aws(e, msg="Couldn't get target health")

                current_instances = current_targets['TargetHealthDescriptions']

                if current_instances:
                    instances_to_remove = []
                    for target in current_targets['TargetHealthDescriptions']:
                        instances_to_remove.append({
                            'Id':
                            target['Target']['Id'],
                            'Port':
                            target['Target']['Port']
                        })

                    changed = True
                    try:
                        connection.deregister_targets(
                            TargetGroupArn=tg['TargetGroupArn'],
                            Targets=instances_to_remove)
                    except (botocore.exceptions.ClientError,
                            botocore.exceptions.BotoCoreError) as e:
                        module.fail_json_aws(e, msg="Couldn't remove targets")

                    if module.params.get("wait"):
                        status_achieved, registered_instances = wait_for_status(
                            connection, module, tg['TargetGroupArn'],
                            instances_to_remove, 'unused')
                        if not status_achieved:
                            module.fail_json(
                                msg=
                                'Error waiting for target deregistration - please check the AWS console'
                            )
    else:
        try:
            connection.create_target_group(**params)
            changed = True
            new_target_group = True
        except (botocore.exceptions.ClientError,
                botocore.exceptions.BotoCoreError) as e:
            module.fail_json_aws(e, msg="Couldn't create target group")

        tg = get_target_group(connection, module)

        if module.params.get("targets"):
            params['Targets'] = module.params.get("targets")
            try:
                connection.register_targets(
                    TargetGroupArn=tg['TargetGroupArn'],
                    Targets=params['Targets'])
            except (botocore.exceptions.ClientError,
                    botocore.exceptions.BotoCoreError) as e:
                module.fail_json_aws(e, msg="Couldn't register targets")

            if module.params.get("wait"):
                status_achieved, registered_instances = wait_for_status(
                    connection, module, tg['TargetGroupArn'],
                    params['Targets'], 'healthy')
                if not status_achieved:
                    module.fail_json(
                        msg=
                        'Error waiting for target registration to be healthy - please check the AWS console'
                    )

    # Now set target group attributes
    update_attributes = []

    # Get current attributes
    current_tg_attributes = get_tg_attributes(connection, module,
                                              tg['TargetGroupArn'])

    if deregistration_delay_timeout is not None:
        if str(deregistration_delay_timeout) != current_tg_attributes[
                'deregistration_delay_timeout_seconds']:
            update_attributes.append({
                'Key': 'deregistration_delay.timeout_seconds',
                'Value': str(deregistration_delay_timeout)
            })
    if stickiness_enabled is not None:
        if stickiness_enabled and current_tg_attributes[
                'stickiness_enabled'] != "true":
            update_attributes.append({
                'Key': 'stickiness.enabled',
                'Value': 'true'
            })
    if stickiness_lb_cookie_duration is not None:
        if str(stickiness_lb_cookie_duration) != current_tg_attributes[
                'stickiness_lb_cookie_duration_seconds']:
            update_attributes.append({
                'Key':
                'stickiness.lb_cookie.duration_seconds',
                'Value':
                str(stickiness_lb_cookie_duration)
            })
    if stickiness_type is not None and "stickiness_type" in current_tg_attributes:
        if stickiness_type != current_tg_attributes['stickiness_type']:
            update_attributes.append({
                'Key': 'stickiness.type',
                'Value': stickiness_type
            })

    if update_attributes:
        try:
            connection.modify_target_group_attributes(
                TargetGroupArn=tg['TargetGroupArn'],
                Attributes=update_attributes)
            changed = True
        except (botocore.exceptions.ClientError,
                botocore.exceptions.BotoCoreError) as e:
            # Something went wrong setting attributes. If this target group was created during this task, delete it to leave a consistent state
            if new_target_group:
                connection.delete_target_group(
                    TargetGroupArn=tg['TargetGroupArn'])
            module.fail_json_aws(e, msg="Couldn't delete target group")

    # Tags - only need to play with tags if tags parameter has been set to something
    if tags:
        # Get tags
        current_tags = get_target_group_tags(connection, module,
                                             tg['TargetGroupArn'])

        # Delete necessary tags
        tags_need_modify, tags_to_delete = compare_aws_tags(
            boto3_tag_list_to_ansible_dict(current_tags), tags, purge_tags)
        if tags_to_delete:
            try:
                connection.remove_tags(ResourceArns=[tg['TargetGroupArn']],
                                       TagKeys=tags_to_delete)
            except (botocore.exceptions.ClientError,
                    botocore.exceptions.BotoCoreError) as e:
                module.fail_json_aws(
                    e, msg="Couldn't delete tags from target group")
            changed = True

        # Add/update tags
        if tags_need_modify:
            try:
                connection.add_tags(
                    ResourceArns=[tg['TargetGroupArn']],
                    Tags=ansible_dict_to_boto3_tag_list(tags_need_modify))
            except (botocore.exceptions.ClientError,
                    botocore.exceptions.BotoCoreError) as e:
                module.fail_json_aws(e,
                                     msg="Couldn't add tags to target group")
            changed = True

    # Get the target group again
    tg = get_target_group(connection, module)

    # Get the target group attributes again
    tg.update(get_tg_attributes(connection, module, tg['TargetGroupArn']))

    # Convert tg to snake_case
    snaked_tg = camel_dict_to_snake_dict(tg)

    snaked_tg['tags'] = boto3_tag_list_to_ansible_dict(
        get_target_group_tags(connection, module, tg['TargetGroupArn']))

    module.exit_json(changed=changed, **snaked_tg)
Пример #21
0
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,
    )

    if not HAS_BOTO3:
        module.fail_json(msg='boto3 and botocore are required for this module')

    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)
Пример #22
0
def update_image(module, connection, image_id):
    launch_permissions = module.params.get('launch_permissions')
    image = get_image_by_id(module, connection, image_id)
    if image is None:
        module.fail_json(msg="Image %s does not exist" % image_id,
                         changed=False)
    changed = False

    if launch_permissions is not None:
        current_permissions = image['LaunchPermissions']

        current_users = set(permission['UserId']
                            for permission in current_permissions
                            if 'UserId' in permission)
        desired_users = set(
            str(user_id) for user_id in launch_permissions.get('user_ids', []))
        current_groups = set(permission['Group']
                             for permission in current_permissions
                             if 'Group' in permission)
        desired_groups = set(launch_permissions.get('group_names', []))

        to_add_users = desired_users - current_users
        to_remove_users = current_users - desired_users
        to_add_groups = desired_groups - current_groups
        to_remove_groups = current_groups - desired_groups

        to_add = [dict(Group=group) for group in to_add_groups
                  ] + [dict(UserId=user_id) for user_id in to_add_users]
        to_remove = [dict(Group=group) for group in to_remove_groups
                     ] + [dict(UserId=user_id) for user_id in to_remove_users]

        if to_add or to_remove:
            try:
                connection.modify_image_attribute(ImageId=image_id,
                                                  Attribute='launchPermission',
                                                  LaunchPermission=dict(
                                                      Add=to_add,
                                                      Remove=to_remove))
                changed = True
            except (botocore.exceptions.BotoCoreError,
                    botocore.exceptions.ClientError) as e:
                module.fail_json_aws(
                    e,
                    msg="Error updating launch permissions of image %s" %
                    image_id)

    desired_tags = module.params.get('tags')
    if desired_tags is not None:
        current_tags = boto3_tag_list_to_ansible_dict(image.get('Tags'))
        tags_to_add, tags_to_remove = compare_aws_tags(
            current_tags,
            desired_tags,
            purge_tags=module.params.get('purge_tags'))

        if tags_to_remove:
            try:
                connection.delete_tags(
                    Resources=[image_id],
                    Tags=[dict(Key=tagkey) for tagkey in tags_to_remove])
                changed = True
            except (botocore.exceptions.BotoCoreError,
                    botocore.exceptions.ClientError) as e:
                module.fail_json_aws(e, msg="Error updating tags")

        if tags_to_add:
            try:
                connection.create_tags(
                    Resources=[image_id],
                    Tags=ansible_dict_to_boto3_tag_list(tags_to_add))
                changed = True
            except (botocore.exceptions.BotoCoreError,
                    botocore.exceptions.ClientError) as e:
                module.fail_json_aws(e, msg="Error updating tags")

    description = module.params.get('description')
    if description and description != image['Description']:
        try:
            connection.modify_image_attribute(
                Attribute='Description ',
                ImageId=image_id,
                Description=dict(Value=description))
            changed = True
        except (botocore.exceptions.BotoCoreError,
                botocore.exceptions.ClientError) as e:
            module.fail_json_aws(e,
                                 msg="Error setting description for image %s" %
                                 image_id)

    if changed:
        module.exit_json(msg="AMI updated.",
                         changed=True,
                         **get_ami_info(
                             get_image_by_id(module, connection, image_id)))
    else:
        module.exit_json(msg="AMI not updated.",
                         changed=False,
                         **get_ami_info(
                             get_image_by_id(module, connection, image_id)))
Пример #23
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', 'list']),
                         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)

    if state == 'list':
        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:
                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)
Пример #24
0
def check_for_update(connection, module_params, vpn_connection_id):
    """ Determines if there are any tags or routes that need to be updated. Ensures non-modifiable attributes aren't expected to change. """
    tags = module_params.get('tags')
    routes = module_params.get('routes')
    purge_tags = module_params.get('purge_tags')
    purge_routes = module_params.get('purge_routes')

    vpn_connection = find_connection(connection,
                                     module_params,
                                     vpn_connection_id=vpn_connection_id)
    current_attrs = camel_dict_to_snake_dict(vpn_connection)

    # Initialize changes dict
    changes = {
        'tags_to_add': [],
        'tags_to_remove': [],
        'routes_to_add': [],
        'routes_to_remove': []
    }

    # Get changes to tags
    current_tags = boto3_tag_list_to_ansible_dict(
        current_attrs.get('tags', []), u'key', u'value')
    tags_to_add, changes['tags_to_remove'] = compare_aws_tags(
        current_tags, tags, purge_tags)
    changes['tags_to_add'] = ansible_dict_to_boto3_tag_list(tags_to_add)
    # Get changes to routes
    if 'Routes' in vpn_connection:
        current_routes = [
            route['DestinationCidrBlock'] for route in vpn_connection['Routes']
        ]
        if purge_routes:
            changes['routes_to_remove'] = [
                old_route for old_route in current_routes
                if old_route not in routes
            ]
        changes['routes_to_add'] = [
            new_route for new_route in routes
            if new_route not in current_routes
        ]

    # Check if nonmodifiable attributes are attempted to be modified
    for attribute in current_attrs:
        if attribute in ("tags", "routes", "state"):
            continue
        elif attribute == 'options':
            will_be = module_params.get('static_only', None)
            is_now = bool(current_attrs[attribute]['static_routes_only'])
            attribute = 'static_only'
        elif attribute == 'type':
            will_be = module_params.get("connection_type", None)
            is_now = current_attrs[attribute]
        else:
            is_now = current_attrs[attribute]
            will_be = module_params.get(attribute, None)

        if will_be is not None and to_text(will_be) != to_text(is_now):
            raise VPNConnectionException(
                msg=
                "You cannot modify {0}, the current value of which is {1}. Modifiable VPN "
                "connection attributes are tags and routes. The value you tried to change it to "
                "is {2}.".format(attribute, is_now, will_be))

    return changes
Пример #25
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.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 current_tags[key] == tags[key]:
                remove_tags[key] = 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:
                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:
                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)
Пример #26
0
    def converge_file_system(self, name, tags, purge_tags, targets):
        """
         Change attributes (mount targets and tags) of filesystem by name
        """
        result = False
        fs_id = self.get_file_system_id(name)

        if tags is not None:
            tags_need_modify, tags_to_delete = compare_aws_tags(boto3_tag_list_to_ansible_dict(self.get_tags(FileSystemId=fs_id)), tags, purge_tags)

            if tags_to_delete:
                try:
                    self.connection.delete_tags(
                        FileSystemId=fs_id,
                        TagKeys=tags_to_delete
                    )
                except ClientError as e:
                    self.module.fail_json(msg="Unable to delete tags: {0}".format(to_native(e)),
                                          exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
                except BotoCoreError as e:
                    self.module.fail_json(msg="Unable to delete tags: {0}".format(to_native(e)),
                                          exception=traceback.format_exc())

                result = True

            if tags_need_modify:
                try:
                    self.connection.create_tags(
                        FileSystemId=fs_id,
                        Tags=ansible_dict_to_boto3_tag_list(tags_need_modify)
                    )
                except ClientError as e:
                    self.module.fail_json(msg="Unable to create tags: {0}".format(to_native(e)),
                                          exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
                except BotoCoreError as e:
                    self.module.fail_json(msg="Unable to create tags: {0}".format(to_native(e)),
                                          exception=traceback.format_exc())

                result = True

        if targets is not None:
            incomplete_states = [self.STATE_CREATING, self.STATE_DELETING]
            wait_for(
                lambda: len(self.get_mount_targets_in_state(fs_id, incomplete_states)),
                0
            )
            current_targets = _index_by_key('SubnetId', self.get_mount_targets(FileSystemId=fs_id))
            targets = _index_by_key('SubnetId', targets)

            targets_to_create, intersection, targets_to_delete = dict_diff(current_targets,
                                                                           targets, True)

            # To modify mount target it should be deleted and created again
            changed = [sid for sid in intersection if not targets_equal(['SubnetId', 'IpAddress', 'NetworkInterfaceId'],
                                                                        current_targets[sid], targets[sid])]
            targets_to_delete = list(targets_to_delete) + changed
            targets_to_create = list(targets_to_create) + changed

            if targets_to_delete:
                for sid in targets_to_delete:
                    self.connection.delete_mount_target(
                        MountTargetId=current_targets[sid]['MountTargetId']
                    )
                wait_for(
                    lambda: len(self.get_mount_targets_in_state(fs_id, incomplete_states)),
                    0
                )
                result = True

            if targets_to_create:
                for sid in targets_to_create:
                    self.connection.create_mount_target(
                        FileSystemId=fs_id,
                        **targets[sid]
                    )
                wait_for(
                    lambda: len(self.get_mount_targets_in_state(fs_id, incomplete_states)),
                    0,
                    self.wait_timeout
                )
                result = True

            # If no security groups were passed into the module, then do not change it.
            security_groups_to_update = [sid for sid in intersection if
                                         'SecurityGroups' in targets[sid] and
                                         current_targets[sid]['SecurityGroups'] != targets[sid]['SecurityGroups']]

            if security_groups_to_update:
                for sid in security_groups_to_update:
                    self.connection.modify_mount_target_security_groups(
                        MountTargetId=current_targets[sid]['MountTargetId'],
                        SecurityGroups=targets[sid].get('SecurityGroups', None)
                    )
                result = True

        return result
Пример #27
0
def update_image(module, connection, image_id):
    launch_permissions = module.params.get('launch_permissions')
    image = get_image_by_id(module, connection, image_id)
    if image is None:
        module.fail_json(msg="Image %s does not exist" % image_id, changed=False)
    changed = False

    if launch_permissions is not None:
        current_permissions = image['LaunchPermissions']

        current_users = set(permission['UserId'] for permission in current_permissions if 'UserId' in permission)
        desired_users = set(str(user_id) for user_id in launch_permissions.get('user_ids', []))
        current_groups = set(permission['Group'] for permission in current_permissions if 'Group' in permission)
        desired_groups = set(launch_permissions.get('group_names', []))

        to_add_users = desired_users - current_users
        to_remove_users = current_users - desired_users
        to_add_groups = desired_groups - current_groups
        to_remove_groups = current_groups - desired_groups

        to_add = [dict(Group=group) for group in to_add_groups] + [dict(UserId=user_id) for user_id in to_add_users]
        to_remove = [dict(Group=group) for group in to_remove_groups] + [dict(UserId=user_id) for user_id in to_remove_users]

        if to_add or to_remove:
            try:
                connection.modify_image_attribute(ImageId=image_id, Attribute='launchPermission',
                                                  LaunchPermission=dict(Add=to_add, Remove=to_remove))
                changed = True
            except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e:
                module.fail_json_aws(e, msg="Error updating launch permissions of image %s" % image_id)

    desired_tags = module.params.get('tags')
    if desired_tags is not None:
        current_tags = boto3_tag_list_to_ansible_dict(image.get('Tags'))
        tags_to_add, tags_to_remove = compare_aws_tags(current_tags, desired_tags, purge_tags=module.params.get('purge_tags'))

        if tags_to_remove:
            try:
                connection.delete_tags(Resources=[image_id], Tags=[dict(Key=tagkey) for tagkey in tags_to_remove])
                changed = True
            except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e:
                module.fail_json_aws(e, msg="Error updating tags")

        if tags_to_add:
            try:
                connection.create_tags(Resources=[image_id], Tags=ansible_dict_to_boto3_tag_list(tags_to_add))
                changed = True
            except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e:
                module.fail_json_aws(e, msg="Error updating tags")

    description = module.params.get('description')
    if description and description != image['Description']:
        try:
            connection.modify_image_attribute(Attribute='Description ', ImageId=image_id, Description=dict(Value=description))
            changed = True
        except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e:
            module.fail_json_aws(e, msg="Error setting description for image %s" % image_id)

    if changed:
        module.exit_json(msg="AMI updated.", changed=True,
                         **get_ami_info(get_image_by_id(module, connection, image_id)))
    else:
        module.exit_json(msg="AMI not updated.", changed=False,
                         **get_ami_info(get_image_by_id(module, connection, image_id)))