Example #1
0
def update_vpc_tags(connection, module, vpc_id, tags, name):

    if tags is None:
        tags = dict()

    tags.update({'Name': name})
    try:
        current_tags = dict((t['Key'], t['Value']) for t in connection.describe_tags(Filters=[{'Name': 'resource-id', 'Values': [vpc_id]}])['Tags'])
        if tags != current_tags:
            if not module.check_mode:
                tags = ansible_dict_to_boto3_tag_list(tags)
                vpc_obj = AWSRetry.backoff(
                    delay=1, tries=5,
                    catch_extra_error_codes=['InvalidVpcID.NotFound'],
                )(connection.create_tags)(Resources=[vpc_id], Tags=tags)

                # Wait for tags to be updated
                expected_tags = boto3_tag_list_to_ansible_dict(tags)
                filters = [{'Name': 'tag:{0}'.format(key), 'Values': [value]} for key, value in expected_tags.items()]
                connection.get_waiter('vpc_available').wait(VpcIds=[vpc_id], Filters=filters)

            return True
        else:
            return False
    except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
        module.fail_json_aws(e, msg="Failed to update tags")
Example #2
0
def get_ami_info(camel_image):
    image = camel_dict_to_snake_dict(camel_image)
    return dict(
        image_id=image.get("image_id"),
        state=image.get("state"),
        architecture=image.get("architecture"),
        block_device_mapping=get_block_device_mapping(image),
        creationDate=image.get("creation_date"),
        description=image.get("description"),
        hypervisor=image.get("hypervisor"),
        is_public=image.get("public"),
        location=image.get("image_location"),
        ownerId=image.get("owner_id"),
        root_device_name=image.get("root_device_name"),
        root_device_type=image.get("root_device_type"),
        virtualization_type=image.get("virtualization_type"),
        name=image.get("name"),
        tags=boto3_tag_list_to_ansible_dict(image.get('tags')),
        platform=image.get("platform"),
        enhanced_networking=image.get("ena_support"),
        image_owner_alias=image.get("image_owner_alias"),
        image_type=image.get("image_type"),
        kernel_id=image.get("kernel_id"),
        product_codes=image.get("product_codes"),
        ramdisk_id=image.get("ramdisk_id"),
        sriov_net_support=image.get("sriov_net_support"),
        state_reason=image.get("state_reason"),
        launch_permissions=image.get('launch_permissions')
    )
Example #3
0
def get_trail_facts(module, client, name):
    """
    Describes existing trail in an account

    module : AnsibleModule object
    client : boto3 client connection object
    name : Name of the trail
    """
    # get Trail info
    try:
        trail_resp = client.describe_trails(trailNameList=[name])
    except ClientError as err:
        module.fail_json(msg=err.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(err.response))

    # Now check to see if our trail exists and get status and tags
    if len(trail_resp['trailList']):
        trail = trail_resp['trailList'][0]
        try:
            status_resp = client.get_trail_status(Name=trail['Name'])
            tags_list = client.list_tags(ResourceIdList=[trail['TrailARN']])
        except ClientError as err:
            module.fail_json(msg=err.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(err.response))

        trail['IsLogging'] = status_resp['IsLogging']
        trail['tags'] = boto3_tag_list_to_ansible_dict(tags_list['ResourceTagList'][0]['TagsList'])
        # Check for non-existent values and populate with None
        optional_vals = set(['S3KeyPrefix', 'SnsTopicName', 'SnsTopicARN', 'CloudWatchLogsLogGroupArn', 'CloudWatchLogsRoleArn', 'KmsKeyId'])
        for v in optional_vals - set(trail.keys()):
            trail[v] = None
        return trail

    else:
        # trail doesn't exist return None
        return None
Example #4
0
 def summary_get_distribution_list(self, streaming=False):
     try:
         list_name = 'streaming_distributions' if streaming else 'distributions'
         key_list = ['Id', 'ARN', 'Status', 'LastModifiedTime', 'DomainName', 'Comment', 'PriceClass', 'Enabled' ]
         distribution_list = { list_name: [] }
         distributions = self.list_streaming_distributions(False) if streaming else self.list_distributions(False)
         for dist in distributions:
             temp_distribution = {}
             for key_name in key_list:
                 temp_distribution[key_name] = dist[key_name]
             temp_distribution['Aliases'] = [alias for alias in dist['Aliases'].get('Items', [])]
             temp_distribution['ETag'] = self.get_etag_from_distribution_id(dist['Id'], streaming)
             if not streaming:
                 temp_distribution['WebACLId'] = dist['WebACLId']
                 invalidation_ids = self.get_list_of_invalidation_ids_from_distribution_id(dist['Id'])
                 if invalidation_ids:
                     temp_distribution['Invalidations'] = invalidation_ids
             resource_tags = self.client.list_tags_for_resource(Resource=dist['ARN'])
             temp_distribution['Tags'] = boto3_tag_list_to_ansible_dict(resource_tags['Tags'].get('Items', []))
             distribution_list[list_name].append(temp_distribution)
         return distribution_list
     except botocore.exceptions.ClientError as e:
         self.module.fail_json(msg="Error generating summary of distributions - " + str(e),
                               exception=traceback.format_exc(),
                               **camel_dict_to_snake_dict(e.response))
     except Exception as e:
         self.module.fail_json(msg="Error generating summary of distributions - " + str(e),
                               exception=traceback.format_exc())
Example #5
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
Example #6
0
def get_subnet_info(subnet):
    if 'Subnets' in subnet:
        return [get_subnet_info(s) for s in subnet['Subnets']]
    elif 'Subnet' in subnet:
        subnet = camel_dict_to_snake_dict(subnet['Subnet'])
    else:
        subnet = camel_dict_to_snake_dict(subnet)

    if 'tags' in subnet:
        subnet['tags'] = boto3_tag_list_to_ansible_dict(subnet['tags'])
    else:
        subnet['tags'] = dict()

    if 'subnet_id' in subnet:
        subnet['id'] = subnet['subnet_id']
        del subnet['subnet_id']

    subnet['ipv6_cidr_block'] = ''
    subnet['ipv6_association_id'] = ''
    ipv6set = subnet.get('ipv6_cidr_block_association_set')
    if ipv6set:
        for item in ipv6set:
            if item.get('ipv6_cidr_block_state', {}).get('state') in ('associated', 'associating'):
                subnet['ipv6_cidr_block'] = item['ipv6_cidr_block']
                subnet['ipv6_association_id'] = item['association_id']

    return subnet
def describe_subnets(connection, module):
    """
    Describe Subnets.

    module  : AnsibleModule object
    connection  : boto3 client connection object
    """
    # collect parameters
    filters = ansible_dict_to_boto3_filter_list(module.params.get('filters'))
    subnet_ids = module.params.get('subnet_ids')

    if subnet_ids is None:
        # Set subnet_ids to empty list if it is None
        subnet_ids = []

    # init empty list for return vars
    subnet_info = list()

    # Get the basic VPC info
    try:
        response = describe_subnets_with_backoff(connection, subnet_ids, filters)
    except botocore.exceptions.ClientError as e:
        module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))

    for subnet in response['Subnets']:
        # for backwards compatibility
        subnet['id'] = subnet['SubnetId']
        subnet_info.append(camel_dict_to_snake_dict(subnet))
        # convert tag list to ansible dict
        subnet_info[-1]['tags'] = boto3_tag_list_to_ansible_dict(subnet.get('Tags', []))

    module.exit_json(subnets=subnet_info)
Example #8
0
def get_key_details(connection, module, key_id, tokens=None):
    if not tokens:
        tokens = []
    try:
        result = get_kms_metadata_with_backoff(connection, key_id)['KeyMetadata']
    except botocore.exceptions.ClientError as e:
        module.fail_json(msg="Failed to obtain key metadata",
                         exception=traceback.format_exc(),
                         **camel_dict_to_snake_dict(e.response))
    result['KeyArn'] = result.pop('Arn')

    try:
        aliases = get_kms_aliases_lookup(connection)
    except botocore.exceptions.ClientError as e:
        module.fail_json(msg="Failed to obtain aliases",
                         exception=traceback.format_exc(),
                         **camel_dict_to_snake_dict(e.response))
    result['aliases'] = aliases.get(result['KeyId'], [])

    if module.params.get('pending_deletion'):
        return camel_dict_to_snake_dict(result)

    try:
        result['grants'] = get_kms_grants_with_backoff(connection, key_id, tokens=tokens)['Grants']
    except botocore.exceptions.ClientError as e:
        module.fail_json(msg="Failed to obtain key grants",
                         exception=traceback.format_exc(),
                         **camel_dict_to_snake_dict(e.response))
    tags = get_kms_tags(connection, module, key_id)

    result = camel_dict_to_snake_dict(result)
    result['tags'] = boto3_tag_list_to_ansible_dict(tags, 'TagKey', 'TagValue')
    result['policies'] = get_kms_policies(connection, module, key_id)
    return result
Example #9
0
def get_check_mode_results(connection, module_params, vpn_connection_id=None, current_state=None):
    """ Returns the changes that would be made to a VPN Connection """
    state = module_params.get('state')
    if state == 'absent':
        if vpn_connection_id:
            return True, {}
        else:
            return False, {}

    changed = False
    results = {'customer_gateway_configuration': '',
               'customer_gateway_id': module_params.get('customer_gateway_id'),
               'vpn_gateway_id': module_params.get('vpn_gateway_id'),
               'options': {'static_routes_only': module_params.get('static_only')},
               'routes': [module_params.get('routes')]}

    # get combined current tags and tags to set
    present_tags = module_params.get('tags')
    if current_state and 'Tags' in current_state:
        current_tags = boto3_tag_list_to_ansible_dict(current_state['Tags'])
        if module_params.get('purge_tags'):
            if current_tags != present_tags:
                changed = True
        elif current_tags != present_tags:
            if not set(present_tags.keys()) < set(current_tags.keys()):
                changed = True
            # add preexisting tags that new tags didn't overwrite
            present_tags.update((tag, current_tags[tag]) for tag in current_tags if tag not in present_tags)
        elif current_tags.keys() == present_tags.keys() and set(present_tags.values()) != set(current_tags.values()):
            changed = True
    elif module_params.get('tags'):
        changed = True
    if present_tags:
        results['tags'] = present_tags

    # get combined current routes and routes to add
    present_routes = module_params.get('routes')
    if current_state and 'Routes' in current_state:
        current_routes = [route['DestinationCidrBlock'] for route in current_state['Routes']]
        if module_params.get('purge_routes'):
            if set(current_routes) != set(present_routes):
                changed = True
        elif set(present_routes) != set(current_routes):
            if not set(present_routes) < set(current_routes):
                changed = True
            present_routes.extend([route for route in current_routes if route not in present_routes])
    elif module_params.get('routes'):
        changed = True
    results['routes'] = [{"destination_cidr_block": cidr, "state": "available"} for cidr in present_routes]

    # return the vpn_connection_id if it's known
    if vpn_connection_id:
        results['vpn_connection_id'] = vpn_connection_id
    else:
        changed = True
        results['vpn_connection_id'] = 'vpn-XXXXXXXX'

    return changed, results
Example #10
0
def get_elb_attributes(connection, module, elb_arn):

    try:
        elb_attributes = boto3_tag_list_to_ansible_dict(connection.describe_load_balancer_attributes(LoadBalancerArn=elb_arn)['Attributes'])
    except ClientError as e:
        module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))

    # Replace '.' with '_' in attribute key names to make it more Ansibley
    return dict((k.replace('.', '_'), v) for k, v in elb_attributes.items())
Example #11
0
def get_current_bucket_tags_dict(s3_client, bucket_name):
    try:
        current_tags = s3_client.get_bucket_tagging(Bucket=bucket_name).get('TagSet')
    except ClientError as e:
        if e.response['Error']['Code'] == 'NoSuchTagSet':
            return {}
        raise e

    return boto3_tag_list_to_ansible_dict(current_tags)
Example #12
0
def test_check_for_update_tags(placeboify, maybe_sleep):
    # setup dependencies for 1 vpn connection
    dependencies = setup_req(placeboify, 1)
    params, vpn, m, conn = dependencies['params'], dependencies['vpn'], dependencies['module'], dependencies['connection']

    # add and remove a number of tags
    m.params['tags'] = {'One': 'one', 'Two': 'two'}
    ec2_vpc_vpn.ensure_present(conn, m.params)
    m.params['tags'] = {'Two': 'two', 'Three': 'three', 'Four': 'four'}
    changes = ec2_vpc_vpn.check_for_update(conn, m.params, vpn['VpnConnectionId'])

    flat_dict_changes = boto3_tag_list_to_ansible_dict(changes['tags_to_add'])
    correct_changes = boto3_tag_list_to_ansible_dict([{'Key': 'Three', 'Value': 'three'}, {'Key': 'Four', 'Value': 'four'}])
    assert flat_dict_changes == correct_changes
    assert changes['tags_to_remove'] == ['One']

    # delete connection
    tear_down_conn(placeboify, conn, vpn['VpnConnectionId'])
Example #13
0
def get_target_group_attributes(connection, module, target_group_arn):

    try:
        target_group_attributes = boto3_tag_list_to_ansible_dict(connection.describe_target_group_attributes(TargetGroupArn=target_group_arn)['Attributes'])
    except ClientError as e:
        module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))

    # Replace '.' with '_' in attribute key names to make it more Ansibley
    return dict((k.replace('.', '_'), v)
                for (k, v) in target_group_attributes.items())
Example #14
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
Example #15
0
def ensure_present(module, connection):
    groupname = module.params['name']
    tags = module.params.get('tags')
    changed = False
    errors = []
    try:
        response = connection.describe_db_parameter_groups(DBParameterGroupName=groupname)
    except botocore.exceptions.ClientError as e:
        if e.response['Error']['Code'] == 'DBParameterGroupNotFound':
            response = None
        else:
            module.fail_json(msg="Couldn't access parameter group information: %s" % str(e),
                             exception=traceback.format_exc(),
                             **camel_dict_to_snake_dict(e.response))
    if not response:
        params = dict(DBParameterGroupName=groupname,
                      DBParameterGroupFamily=module.params['engine'],
                      Description=module.params['description'])
        if tags:
            params['Tags'] = ansible_dict_to_boto3_tag_list(tags)
        try:
            response = connection.create_db_parameter_group(**params)
            changed = True
        except botocore.exceptions.ClientError as e:
            module.fail_json(msg="Couldn't create parameter group: %s" % str(e),
                             exception=traceback.format_exc(),
                             **camel_dict_to_snake_dict(e.response))
    else:
        group = response['DBParameterGroups'][0]
        if tags:
            changed = update_tags(module, connection, group, tags)

    if module.params.get('params'):
        params_changed, errors = update_parameters(module, connection)
        changed = changed or params_changed

    try:
        response = connection.describe_db_parameter_groups(DBParameterGroupName=groupname)
        group = camel_dict_to_snake_dict(response['DBParameterGroups'][0])
    except botocore.exceptions.ClientError as e:
        module.fail_json(msg="Couldn't obtain parameter group information: %s" % str(e),
                         exception=traceback.format_exc(),
                         **camel_dict_to_snake_dict(e.response))
    try:
        tags = connection.list_tags_for_resource(ResourceName=group['db_parameter_group_arn'])['TagList']
    except botocore.exceptions.ClientError as e:
        module.fail_json(msg="Couldn't obtain parameter group tags: %s" % str(e),
                         exception=traceback.format_exc(),
                         **camel_dict_to_snake_dict(e.response))
    group['tags'] = boto3_tag_list_to_ansible_dict(tags)

    module.exit_json(changed=changed, errors=errors, **group)
Example #16
0
def main():
    argument_spec = ec2_argument_spec()
    argument_spec.update(
        dict(
            filters=dict(default={}, type='dict')
        )
    )

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

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

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

    if region:
        connection = boto3_conn(
            module,
            conn_type='client',
            resource='ec2',
            region=region,
            endpoint=ec2_url,
            **aws_connect_params
        )
    else:
        module.fail_json(msg="region must be specified")

    # Replace filter key underscores with dashes, for compatibility, except if we're dealing with tags
    sanitized_filters = module.params.get("filters")
    for key in sanitized_filters:
        if not key.startswith("tag:"):
            sanitized_filters[key.replace("_", "-")] = sanitized_filters.pop(key)

    try:
        security_groups = connection.describe_security_groups(
            Filters=ansible_dict_to_boto3_filter_list(sanitized_filters)
        )
    except ClientError as e:
        module.fail_json(msg=e.message, exception=traceback.format_exc())

    snaked_security_groups = []
    for security_group in security_groups['SecurityGroups']:
        # Modify boto3 tags list to be ansible friendly dict
        # but don't camel case tags
        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')
        snaked_security_groups.append(security_group)

    module.exit_json(security_groups=snaked_security_groups)
def main():
    argument_spec = ec2_argument_spec()
    argument_spec.update(dict(
        stack_name=dict(),
        all_facts=dict(required=False, default=False, type='bool'),
        stack_policy=dict(required=False, default=False, type='bool'),
        stack_events=dict(required=False, default=False, type='bool'),
        stack_resources=dict(required=False, default=False, type='bool'),
        stack_template=dict(required=False, default=False, type='bool'),
    ))

    module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False)

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

    service_mgr = CloudFormationServiceManager(module)

    result = {'ansible_facts': {'cloudformation': {}}}

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

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

        # normalize stack description API output
        facts['stack_description'] = camel_dict_to_snake_dict(facts['stack_description'])

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

        result['ansible_facts']['cloudformation'][stack_name] = facts

    result['changed'] = False
    module.exit_json(**result)
Example #18
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
Example #19
0
def get_eips_details(module):
    connection = module.client('ec2')
    filters = module.params.get("filters")
    try:
        response = connection.describe_addresses(
            Filters=ansible_dict_to_boto3_filter_list(filters)
        )
    except (BotoCoreError, ClientError) as e:
        module.fail_json_aws(
            e,
            msg="Error retrieving EIPs")

    addresses = camel_dict_to_snake_dict(response)['addresses']
    for address in addresses:
        if 'tags' in address:
            address['tags'] = boto3_tag_list_to_ansible_dict(address['tags'])
    return addresses
Example #20
0
def get_subnet_info(subnet):
    if 'Subnets' in subnet:
        return [get_subnet_info(s) for s in subnet['Subnets']]
    elif 'Subnet' in subnet:
        subnet = camel_dict_to_snake_dict(subnet['Subnet'])
    else:
        subnet = camel_dict_to_snake_dict(subnet)

    if 'tags' in subnet:
        subnet['tags'] = boto3_tag_list_to_ansible_dict(subnet['tags'])
    else:
        subnet['tags'] = dict()

    if 'subnet_id' in subnet:
        subnet['id'] = subnet['subnet_id']
        del subnet['subnet_id']

    return subnet
Example #21
0
    def get_elb_attributes(self):
        """
        Get load balancer attributes

        :return:
        """

        try:
            attr_list = AWSRetry.jittered_backoff()(
                self.connection.describe_load_balancer_attributes
            )(LoadBalancerArn=self.elb['LoadBalancerArn'])['Attributes']

            elb_attributes = boto3_tag_list_to_ansible_dict(attr_list)
        except (BotoCoreError, ClientError) as e:
            self.module.fail_json_aws(e)

        # Replace '.' with '_' in attribute key names to make it more Ansibley
        return dict((k.replace('.', '_'), v) for k, v in elb_attributes.items())
Example #22
0
def list_ec2_snapshots_boto3(connection, module):

    if module.params.get("filters") is None:
        filters = []
    else:
        filters = ansible_dict_to_boto3_filter_list(module.params.get("filters"))

    try:
        network_interfaces_result = connection.describe_network_interfaces(Filters=filters)
    except (ClientError, NoCredentialsError) as e:
        module.fail_json(msg=e.message)

    # Turn the boto3 result in to ansible_friendly_snaked_names
    snaked_network_interfaces_result = camel_dict_to_snake_dict(network_interfaces_result)
    for network_interfaces in snaked_network_interfaces_result['network_interfaces']:
        network_interfaces['tag_set'] = boto3_tag_list_to_ansible_dict(network_interfaces['tag_set'])

    module.exit_json(**snaked_network_interfaces_result)
Example #23
0
def list_ec2_images(ec2_client, module):

    image_ids = module.params.get("image_ids")
    owners = module.params.get("owners")
    executable_users = module.params.get("executable_users")
    filters = module.params.get("filters")
    owner_param = []

    # describe_images is *very* slow if you pass the `Owners`
    # param (unless it's self), for some reason.
    # Converting the owners to filters and removing from the
    # owners param greatly speeds things up.
    # Implementation based on aioue's suggestion in #24886
    for owner in owners:
        if owner.isdigit():
            if 'owner-id' not in filters:
                filters['owner-id'] = list()
            filters['owner-id'].append(owner)
        elif owner == 'self':
            # self not a valid owner-alias filter (https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeImages.html)
            owner_param.append(owner)
        else:
            if 'owner-alias' not in filters:
                filters['owner-alias'] = list()
            filters['owner-alias'].append(owner)

    filters = ansible_dict_to_boto3_filter_list(filters)

    try:
        images = ec2_client.describe_images(ImageIds=image_ids, Filters=filters, Owners=owner_param, ExecutableUsers=executable_users)
        images = [camel_dict_to_snake_dict(image) for image in images["Images"]]
    except (ClientError, BotoCoreError) as err:
        module.fail_json_aws(err, msg="error describing images")
    for image in images:
        try:
            image['tags'] = boto3_tag_list_to_ansible_dict(image.get('tags', []))
            if module.params.get("describe_image_attributes"):
                launch_permissions = ec2_client.describe_image_attribute(Attribute='launchPermission', ImageId=image['image_id'])['LaunchPermissions']
                image['launch_permissions'] = [camel_dict_to_snake_dict(perm) for perm in launch_permissions]
        except (ClientError, BotoCoreError) as err:
            # describing launch permissions of images owned by others is not permitted, but shouldn't cause failures
            pass

    module.exit_json(images=images)
Example #24
0
def list_eni(connection, module):

    if module.params.get("filters") is None:
        filters = []
    else:
        filters = ansible_dict_to_boto3_filter_list(module.params.get("filters"))

    try:
        network_interfaces_result = connection.describe_network_interfaces(Filters=filters)['NetworkInterfaces']
    except (ClientError, NoCredentialsError) as e:
        module.fail_json(msg=e.message)

    # Modify boto3 tags list to be ansible friendly dict and then camel_case
    camel_network_interfaces = []
    for network_interface in network_interfaces_result:
        network_interface['TagSet'] = boto3_tag_list_to_ansible_dict(network_interface['TagSet'])
        # Added id to interface info to be compatible with return values of ec2_eni module:
        network_interface['Id'] = network_interface['NetworkInterfaceId']
        camel_network_interfaces.append(camel_dict_to_snake_dict(network_interface))

    module.exit_json(network_interfaces=camel_network_interfaces)
def list_ec2_vpc_nacls(connection, module):

    nacl_ids = module.params.get("nacl_ids")
    filters = ansible_dict_to_boto3_filter_list(module.params.get("filters"))

    if nacl_ids is None:
        nacl_ids = []

    try:
        nacls = connection.describe_network_acls(NetworkAclIds=nacl_ids, Filters=filters)
    except ClientError as e:
        module.fail_json(msg="Unable to describe network ACLs {0}: {1}".format(nacl_ids, to_native(e)),
                         exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
    except BotoCoreError as e:
        module.fail_json(msg="Unable to describe network ACLs {0}: {1}".format(nacl_ids, to_native(e)),
                         exception=traceback.format_exc())

    # Turn the boto3 result in to ansible_friendly_snaked_names
    snaked_nacls = []
    for nacl in nacls['NetworkAcls']:
        snaked_nacls.append(camel_dict_to_snake_dict(nacl))

    # Turn the boto3 result in to ansible friendly tag dictionary
    for nacl in snaked_nacls:
        if 'tags' in nacl:
            nacl['tags'] = boto3_tag_list_to_ansible_dict(nacl['tags'], 'key', 'value')
        if 'entries' in nacl:
            nacl['egress'] = [nacl_entry_to_list(entry) for entry in nacl['entries']
                              if entry['rule_number'] < 32767 and entry['egress']]
            nacl['ingress'] = [nacl_entry_to_list(entry) for entry in nacl['entries']
                               if entry['rule_number'] < 32767 and not entry['egress']]
            del nacl['entries']
        if 'associations' in nacl:
            nacl['subnets'] = [a['subnet_id'] for a in nacl['associations']]
            del nacl['associations']
        if 'network_acl_id' in nacl:
            nacl['nacl_id'] = nacl['network_acl_id']
            del nacl['network_acl_id']

    module.exit_json(nacls=snaked_nacls)
Example #26
0
def ensure_present(connection, module_params, check_mode=False):
    """ Creates and adds tags to a VPN connection. If the connection already exists update tags. """
    vpn_connection = find_connection(connection, module_params)
    changed = False

    # No match but vpn_connection_id was specified.
    if not vpn_connection and module_params.get('vpn_connection_id'):
        raise VPNConnectionException(msg="There is no VPN connection available or pending with that id. Did you delete it?")

    # Unique match was found. Check if attributes provided differ.
    elif vpn_connection:
        vpn_connection_id = vpn_connection['VpnConnectionId']
        # check_for_update returns a dict with the keys tags_to_add, tags_to_remove, routes_to_add, routes_to_remove
        changes = check_for_update(connection, module_params, vpn_connection_id)
        if check_mode:
            return get_check_mode_results(connection, module_params, vpn_connection_id, current_state=vpn_connection)
        changed = make_changes(connection, vpn_connection_id, changes)

    # No match was found. Create and tag a connection and add routes.
    else:
        changed = True
        if check_mode:
            return get_check_mode_results(connection, module_params)
        vpn_connection = create_connection(connection,
                                           customer_gateway_id=module_params.get('customer_gateway_id'),
                                           static_only=module_params.get('static_only'),
                                           vpn_gateway_id=module_params.get('vpn_gateway_id'),
                                           connection_type=module_params.get('connection_type'),
                                           tunnel_options=module_params.get('tunnel_options'))
        changes = check_for_update(connection, module_params, vpn_connection['VpnConnectionId'])
        _ = make_changes(connection, vpn_connection['VpnConnectionId'], changes)

    # get latest version if a change has been made and make tags output nice before returning it
    if vpn_connection:
        vpn_connection = find_connection(connection, module_params, vpn_connection['VpnConnectionId'])
        if 'Tags' in vpn_connection:
            vpn_connection['Tags'] = boto3_tag_list_to_ansible_dict(vpn_connection['Tags'])

    return changed, vpn_connection
Example #27
0
def get_certificates(client, module, domain_name=None, statuses=None):
    try:
        all_certificates = list_certificates_with_backoff(client, statuses)
    except botocore.exceptions.ClientError as e:
        module.fail_json(msg="Couldn't obtain certificates",
                         exception=traceback.format_exc(),
                         **camel_dict_to_snake_dict(e.response))
    if domain_name:
        certificates = [cert for cert in all_certificates
                        if cert['DomainName'] == domain_name]
    else:
        certificates = all_certificates

    results = []
    for certificate in certificates:
        try:
            cert_data = describe_certificate_with_backoff(client, certificate['CertificateArn'])
        except botocore.exceptions.ClientError as e:
            module.fail_json(msg="Couldn't obtain certificate metadata for domain %s" % certificate['DomainName'],
                             exception=traceback.format_exc(),
                             **camel_dict_to_snake_dict(e.response))
        try:
            cert_data.update(get_certificate_with_backoff(client, certificate['CertificateArn']))
        except botocore.exceptions.ClientError as e:
            if e.response['Error']['Code'] != "RequestInProgressException":
                module.fail_json(msg="Couldn't obtain certificate data for domain %s" % certificate['DomainName'],
                                 exception=traceback.format_exc(),
                                 **camel_dict_to_snake_dict(e.response))
        cert_data = camel_dict_to_snake_dict(cert_data)
        try:
            tags = list_certificate_tags_with_backoff(client, certificate['CertificateArn'])
        except botocore.exceptions.ClientError as e:
            module.fail_json(msg="Couldn't obtain tags for domain %s" % certificate['DomainName'],
                             exception=traceback.format_exc(),
                             **camel_dict_to_snake_dict(e.response))
        cert_data['tags'] = boto3_tag_list_to_ansible_dict(tags)
        results.append(cert_data)
    return results
Example #28
0
def list_ec2_snapshots(connection, module):

    snapshot_ids = module.params.get("snapshot_ids")
    owner_ids = map(str, module.params.get("owner_ids"))
    restorable_by_user_ids = module.params.get("restorable_by_user_ids")
    filters = ansible_dict_to_boto3_filter_list(module.params.get("filters"))

    try:
        snapshots = connection.describe_snapshots(SnapshotIds=snapshot_ids, OwnerIds=owner_ids, RestorableByUserIds=restorable_by_user_ids, Filters=filters)
    except ClientError as e:
        module.fail_json(msg=e.message)

    # Turn the boto3 result in to ansible_friendly_snaked_names
    snaked_snapshots = []
    for snapshot in snapshots['Snapshots']:
        snaked_snapshots.append(camel_dict_to_snake_dict(snapshot))

    # Turn the boto3 result in to ansible friendly tag dictionary
    for snapshot in snaked_snapshots:
        if 'tags' in snapshot:
            snapshot['tags'] = boto3_tag_list_to_ansible_dict(snapshot['tags'], 'key', 'value')

    module.exit_json(snapshots=snaked_snapshots)
def list_ec2_instances(connection, module):

    instance_ids = module.params.get("instance_ids")
    filters = ansible_dict_to_boto3_filter_list(module.params.get("filters"))

    try:
        reservations_paginator = connection.get_paginator('describe_instances')
        reservations = reservations_paginator.paginate(InstanceIds=instance_ids, Filters=filters).build_full_result()
    except ClientError as e:
        module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))

    # Get instances from reservations
    instances = []
    for reservation in reservations['Reservations']:
        instances = instances + reservation['Instances']

    # Turn the boto3 result in to ansible_friendly_snaked_names
    snaked_instances = [camel_dict_to_snake_dict(instance) for instance in instances]

    # Turn the boto3 result in to ansible friendly tag dictionary
    for instance in snaked_instances:
        instance['tags'] = boto3_tag_list_to_ansible_dict(instance.get('tags', []), 'key', 'value')

    module.exit_json(instances=snaked_instances)
Example #30
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)
Example #31
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
            if vpc_id is None or sg['VpcId'] == vpc_id:
                groups[groupName] = sg
        else:
            groups[groupName] = sg

        if group_id:
            if sg['GroupId'] == group_id:
                group = sg
        else:
            if 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 not group['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
    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:
            ip_permission = []
            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")
        else:
            # when no egress rules are specified,
            # 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:
            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)
def get_target_group_tags(connection, module, target_group_arn):

    try:
        return boto3_tag_list_to_ansible_dict(connection.describe_tags(ResourceArns=[target_group_arn])['TagDescriptions'][0]['Tags'])
    except ClientError as e:
        module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
Example #33
0
    def run(self, terms, variables=None, boto_profile=None, aws_profile=None,
            aws_secret_key=None, aws_access_key=None, aws_security_token=None, region=None,
            bypath=False, shortnames=False, recursive=False, decrypt=True):
        '''
            :arg terms: a list of lookups to run.
                e.g. ['parameter_name', 'parameter_name_too' ]
            :kwarg variables: ansible variables active at the time of the lookup
            :kwarg aws_secret_key: identity of the AWS key to use
            :kwarg aws_access_key: AWS seret key (matching identity)
            :kwarg aws_security_token: AWS session key if using STS
            :kwarg decrypt: Set to True to get decrypted parameters
            :kwarg region: AWS region in which to do the lookup
            :kwarg bypath: Set to True to do a lookup of variables under a path
            :kwarg recursive: Set to True to recurse below the path (requires bypath=True)
            :returns: A list of parameter values or a list of dictionaries if bypath=True.
        '''

        if not HAS_BOTO3:
            raise AnsibleError('botocore and boto3 are required for aws_ssm lookup.')

        ret = []
        response = {}
        ssm_dict = {}

        credentials = {}
        if aws_profile:
            credentials['boto_profile'] = aws_profile
        else:
            credentials['boto_profile'] = boto_profile
        credentials['aws_secret_access_key'] = aws_secret_key
        credentials['aws_access_key_id'] = aws_access_key
        credentials['aws_session_token'] = aws_security_token

        client = _boto3_conn(region, credentials)

        ssm_dict['WithDecryption'] = decrypt

        # Lookup by path
        if bypath:
            ssm_dict['Recursive'] = recursive
            for term in terms:
                ssm_dict["Path"] = term
                display.vvv("AWS_ssm path lookup term: %s in region: %s" % (term, region))
                try:
                    response = client.get_parameters_by_path(**ssm_dict)
                except ClientError as e:
                    raise AnsibleError("SSM lookup exception: {0}".format(to_native(e)))
                paramlist = list()
                paramlist.extend(response['Parameters'])

                # Manual pagination, since boto doesn't support it yet for get_parameters_by_path
                while 'NextToken' in response:
                    response = client.get_parameters_by_path(NextToken=response['NextToken'], **ssm_dict)
                    paramlist.extend(response['Parameters'])

                # shorten parameter names. yes, this will return duplicate names with different values.
                if shortnames:
                    for x in paramlist:
                        x['Name'] = x['Name'][x['Name'].rfind('/') + 1:]

                display.vvvv("AWS_ssm path lookup returned: %s" % str(paramlist))
                if len(paramlist):
                    ret.append(boto3_tag_list_to_ansible_dict(paramlist,
                                                              tag_name_key_name="Name",
                                                              tag_value_key_name="Value"))
                else:
                    ret.append({})
            # Lookup by parameter name - always returns a list with one or no entry.
        else:
            display.vvv("AWS_ssm name lookup term: %s" % terms)
            ssm_dict["Names"] = terms
            try:
                response = client.get_parameters(**ssm_dict)
            except ClientError as e:
                raise AnsibleError("SSM lookup exception: {0}".format(to_native(e)))
            params = boto3_tag_list_to_ansible_dict(response['Parameters'], tag_name_key_name="Name",
                                                    tag_value_key_name="Value")
            for i in terms:
                if i in params:
                    ret.append(params[i])
                elif i in response['InvalidParameters']:
                    ret.append(None)
                else:
                    raise AnsibleError("Ansible internal error: aws_ssm lookup failed to understand boto3 return value: {0}".format(str(response)))
            return ret

        display.vvvv("AWS_ssm path lookup returning: %s " % str(ret))
        return ret
Example #34
0
def main():
    argument_spec = ec2_argument_spec()
    argument_spec.update(
        dict(
            name=dict(required=True),
            cidr_block=dict(type='list', required=True),
            tenancy=dict(choices=['default', 'dedicated'], default='default'),
            dns_support=dict(type='bool', default=True),
            dns_hostnames=dict(type='bool', default=True),
            dhcp_opts_id=dict(),
            tags=dict(type='dict', aliases=['resource_tags']),
            state=dict(choices=['present', 'absent'], default='present'),
            multi_ok=dict(type='bool', default=False),
            purge_cidrs=dict(type='bool', default=False),
        ))

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

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

    changed = False

    region, ec2_url, aws_connect_params = get_aws_connection_info(module,
                                                                  boto3=True)
    connection = boto3_conn(module,
                            conn_type='client',
                            resource='ec2',
                            region=region,
                            endpoint=ec2_url,
                            **aws_connect_params)

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

    if state == 'present':

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

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

        vpc_obj = get_vpc(module, connection, vpc_id)

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

        if len(cidr_block) > 1:
            for cidr in to_add:
                changed = True
                connection.associate_vpc_cidr_block(CidrBlock=cidr,
                                                    VpcId=vpc_id)

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

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

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

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

        try:
            connection.get_waiter('vpc_available').wait(VpcIds=[vpc_id])
        except (botocore.exceptions.ClientError,
                botocore.exceptions.BotoCoreError) as e:
            module.fail_json_aws(
                e,
                msg="Unable to wait for VPC {0} to be available.".format(
                    vpc_id))

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

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

    elif state == 'absent':

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

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

        module.exit_json(changed=changed, vpc={})
Example #35
0
def main():
    argument_spec = ec2_argument_spec()
    argument_spec.update(
        dict(
            stack_name=dict(),
            all_facts=dict(required=False, default=False, type='bool'),
            stack_policy=dict(required=False, default=False, type='bool'),
            stack_events=dict(required=False, default=False, type='bool'),
            stack_resources=dict(required=False, default=False, type='bool'),
            stack_template=dict(required=False, default=False, type='bool'),
        ))

    module = AnsibleModule(argument_spec=argument_spec,
                           supports_check_mode=False)

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

    service_mgr = CloudFormationServiceManager(module)

    result = {'ansible_facts': {'cloudformation': {}}}

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

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

        # normalize stack description API output
        facts['stack_description'] = camel_dict_to_snake_dict(
            facts['stack_description'])

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

        result['ansible_facts']['cloudformation'][stack_name] = facts

    result['changed'] = False
    module.exit_json(**result)
def get_tags(connection, load_balancer_name):
    tags = connection.describe_tags(LoadBalancerNames=[load_balancer_name])['TagDescriptions']
    if not tags:
        return {}
    return boto3_tag_list_to_ansible_dict(tags[0]['Tags'])
Example #37
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
def main():
    argument_spec = dict(
        stack_name=dict(),
        all_facts=dict(required=False, default=False, type='bool'),
        stack_policy=dict(required=False, default=False, type='bool'),
        stack_events=dict(required=False, default=False, type='bool'),
        stack_resources=dict(required=False, default=False, type='bool'),
        stack_template=dict(required=False, default=False, type='bool'),
        stack_change_sets=dict(required=False, default=False, type='bool'),
    )
    module = AnsibleAWSModule(argument_spec=argument_spec,
                              supports_check_mode=False)

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

    service_mgr = CloudFormationServiceManager(module)

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

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

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

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

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

    module.exit_json(changed=False, **result)
Example #39
0
def main():
    argument_spec = 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 = AnsibleAWSModule(
        argument_spec=argument_spec,
        supports_check_mode=True,
        required_one_of=[['name', 'group_id']],
        required_if=[['state', 'present', ['name']]],
    )

    name = module.params['name']
    group_id = module.params['group_id']
    description = module.params['description']
    vpc_id = module.params['vpc_id']
    rules = flatten_nested_targets(module, deepcopy(module.params['rules']))
    rules_egress = flatten_nested_targets(module, deepcopy(module.params['rules_egress']))
    rules = deduplicate_rules_args(rules_expand_sources(rules_expand_ports(rules)))
    rules_egress = deduplicate_rules_args(rules_expand_sources(rules_expand_ports(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
    client = module.client('ec2')

    verify_rules_with_descriptions_permitted(client, module, rules, rules_egress)
    group, groups = group_exists(client, module, vpc_id, group_id, name)
    group_created_new = not bool(group)

    global current_account_id
    current_account_id = get_aws_account_id(module)

    before = {}
    after = {}

    # Ensure requested group is absent
    if state == 'absent':
        if group:
            # found a match, delete it
            before = camel_dict_to_snake_dict(group, ignore_list=['Tags'])
            before['tags'] = boto3_tag_list_to_ansible_dict(before.get('tags', []))
            try:
                if not module.check_mode:
                    client.delete_security_group(GroupId=group['GroupId'])
            except (BotoCoreError, ClientError) as e:
                module.fail_json_aws(e, msg="Unable to delete security group '%s'" % group)
            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
            before = camel_dict_to_snake_dict(group, ignore_list=['Tags'])
            before['tags'] = boto3_tag_list_to_ansible_dict(before.get('tags', []))
            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.")
        else:
            # no match found, create it
            group = create_security_group(client, module, name, description, vpc_id)
            changed = True

        if tags is not None and group is not None:
            current_tags = boto3_tag_list_to_ansible_dict(group.get('Tags', []))
            changed |= update_tags(client, module, group['GroupId'], current_tags, tags, purge_tags)

    if group:
        named_tuple_ingress_list = []
        named_tuple_egress_list = []
        current_ingress = sum([list(rule_from_group_permission(p)) for p in group['IpPermissions']], [])
        current_egress = sum([list(rule_from_group_permission(p)) for p in group['IpPermissionsEgress']], [])

        for new_rules, rule_type, named_tuple_rule_list in [(rules, 'in', named_tuple_ingress_list),
                                                            (rules_egress, 'out', named_tuple_egress_list)]:
            if new_rules is None:
                continue
            for rule in new_rules:
                target_type, target, target_group_created = get_target_from_rule(
                    module, client, rule, name, group, groups, vpc_id)
                changed |= target_group_created

                if rule.get('proto', 'tcp') in ('all', '-1', -1):
                    rule['proto'] = '-1'
                    rule['from_port'] = None
                    rule['to_port'] = None
                try:
                    int(rule.get('proto', 'tcp'))
                    rule['proto'] = to_text(rule.get('proto', 'tcp'))
                    rule['from_port'] = None
                    rule['to_port'] = None
                except ValueError:
                    # rule does not use numeric protocol spec
                    pass

                named_tuple_rule_list.append(
                    Rule(
                        port_range=(rule['from_port'], rule['to_port']),
                        protocol=to_text(rule.get('proto', 'tcp')),
                        target=target, target_type=target_type,
                        description=rule.get('rule_desc'),
                    )
                )

        # List comprehensions for rules to add, rules to modify, and rule ids to determine purging
        new_ingress_permissions = [to_permission(r) for r in (set(named_tuple_ingress_list) - set(current_ingress))]
        new_egress_permissions = [to_permission(r) for r in (set(named_tuple_egress_list) - set(current_egress))]

        if module.params.get('rules_egress') is None and 'VpcId' in group:
            # 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
            rule = Rule((None, None), '-1', '0.0.0.0/0', 'ipv4', None)
            if rule in current_egress:
                named_tuple_egress_list.append(rule)
            if rule not in current_egress:
                current_egress.append(rule)

        # List comprehensions for rules to add, rules to modify, and rule ids to determine purging
        present_ingress = list(set(named_tuple_ingress_list).union(set(current_ingress)))
        present_egress = list(set(named_tuple_egress_list).union(set(current_egress)))

        if purge_rules:
            revoke_ingress = []
            for p in present_ingress:
                if not any([rule_cmp(p, b) for b in named_tuple_ingress_list]):
                    revoke_ingress.append(to_permission(p))
        else:
            revoke_ingress = []
        if purge_rules_egress and module.params.get('rules_egress') is not None:
            if module.params.get('rules_egress') is []:
                revoke_egress = [
                    to_permission(r) for r in set(present_egress) - set(named_tuple_egress_list)
                    if r != Rule((None, None), '-1', '0.0.0.0/0', 'ipv4', None)
                ]
            else:
                revoke_egress = []
                for p in present_egress:
                    if not any([rule_cmp(p, b) for b in named_tuple_egress_list]):
                        revoke_egress.append(to_permission(p))
        else:
            revoke_egress = []

        changed |= update_rule_descriptions(module, group['GroupId'], present_ingress, named_tuple_ingress_list, present_egress, named_tuple_egress_list)

        # Revoke old rules
        changed |= remove_old_permissions(client, module, revoke_ingress, revoke_egress, group['GroupId'])
        rule_msg = 'Revoking {0}, and egress {1}'.format(revoke_ingress, revoke_egress)

        new_ingress_permissions = [to_permission(r) for r in (set(named_tuple_ingress_list) - set(current_ingress))]
        new_ingress_permissions = rules_to_permissions(set(named_tuple_ingress_list) - set(current_ingress))
        new_egress_permissions = rules_to_permissions(set(named_tuple_egress_list) - set(current_egress))
        # Authorize new rules
        changed |= add_new_permissions(client, module, new_ingress_permissions, new_egress_permissions, group['GroupId'])

        if group_created_new and module.params.get('rules') is None and module.params.get('rules_egress') is None:
            # A new group with no rules provided is already being awaited.
            # When it is created we wait for the default egress rule to be added by AWS
            security_group = get_security_groups_with_backoff(client, GroupIds=[group['GroupId']])['SecurityGroups'][0]
        elif changed and not module.check_mode:
            security_group = wait_for_rule_propagation(module, group, named_tuple_ingress_list, named_tuple_egress_list, purge_rules, purge_rules_egress)
        else:
            security_group = get_security_groups_with_backoff(client, GroupIds=[group['GroupId']])['SecurityGroups'][0]
        security_group = camel_dict_to_snake_dict(security_group, ignore_list=['Tags'])
        security_group['tags'] = boto3_tag_list_to_ansible_dict(security_group.get('tags', []))

    else:
        security_group = {'group_id': None}

    if module._diff:
        if module.params['state'] == 'present':
            after = get_diff_final_resource(client, module, security_group)

        security_group['diff'] = [{'before': before, 'after': after}]

    module.exit_json(changed=changed, **security_group)
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)))
Example #41
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)
Example #42
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
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)
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)
Example #45
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))
    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")

    # 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, NoCredentialsError) 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, NoCredentialsError) 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, NoCredentialsError) as e:
                module.fail_json(msg=e.message,
                                 exception=traceback.format_exc(),
                                 **camel_dict_to_snake_dict(e.response))

            # Delete necessary tags
            tags_to_delete, tags_need_modify = _compare_tags(
                elb_tags, params['Tags'], purge_tags)
            if tags_to_delete:
                try:
                    connection.remove_tags(
                        ResourceArns=[elb['LoadBalancerArn']],
                        TagKeys=tags_to_delete)
                except (ClientError, NoCredentialsError) as e:
                    module.fail_json(msg=e.message,
                                     **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, NoCredentialsError) 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, NoCredentialsError) 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
    try:
        attribute_changed = update_elb_attributes(connection, module, elb)
        if attribute_changed:
            changed = True
    except (ClientError, NoCredentialsError) 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, NoCredentialsError) 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 tags of the ELB
    elb_tags = connection.describe_tags(
        ResourceArns=[elb['LoadBalancerArn']])['TagDescriptions'][0]['Tags']
    elb['Tags'] = boto3_tag_list_to_ansible_dict(elb_tags)

    module.exit_json(changed=changed,
                     load_balancer=camel_dict_to_snake_dict(elb))
Example #46
0
def describe_vpcs(connection, module):
    """
    Describe VPCs.

    connection  : boto3 client connection object
    module  : AnsibleModule object
    """
    # collect parameters
    filters = ansible_dict_to_boto3_filter_list(module.params.get('filters'))
    vpc_ids = module.params.get('vpc_ids')

    # init empty list for return vars
    vpc_info = list()
    vpc_list = list()

    # Get the basic VPC info
    try:
        response = connection.describe_vpcs(VpcIds=vpc_ids, Filters=filters)
    except botocore.exceptions.ClientError as e:
        module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))

    # Loop through results and create a list of VPC IDs
    for vpc in response['Vpcs']:
        vpc_list.append(vpc['VpcId'])

    # We can get these results in bulk but still needs two separate calls to the API
    try:
        cl_enabled = connection.describe_vpc_classic_link(VpcIds=vpc_list)
    except botocore.exceptions.ClientError as e:
        module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))

    try:
        cl_dns_support = connection.describe_vpc_classic_link_dns_support(VpcIds=vpc_list)
    except botocore.exceptions.ClientError as e:
        module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))

    # Loop through the results and add the other VPC attributes we gathered
    for vpc in response['Vpcs']:
        # We have to make two separate calls per VPC to get these attributes.
        try:
            dns_support = describe_vpc_attr_with_backoff(connection, vpc['VpcId'], 'enableDnsSupport')
        except botocore.exceptions.ClientError as e:
            module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))

        try:
            dns_hostnames = describe_vpc_attr_with_backoff(connection, vpc['VpcId'], 'enableDnsHostnames')
        except botocore.exceptions.ClientError as e:
            module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))

        # loop through the ClassicLink Enabled results and add the value for the correct VPC
        for item in cl_enabled['Vpcs']:
            if vpc['VpcId'] == item['VpcId']:
                vpc['ClassicLinkEnabled'] = item['ClassicLinkEnabled']

        # loop through the ClassicLink DNS support results and add the value for the correct VPC
        for item in cl_dns_support['Vpcs']:
            if vpc['VpcId'] == item['VpcId']:
                vpc['ClassicLinkDnsSupported'] = item['ClassicLinkDnsSupported']

        # add the two DNS attributes
        vpc['EnableDnsSupport'] = dns_support['EnableDnsSupport'].get('Value')
        vpc['EnableDnsHostnames'] = dns_hostnames['EnableDnsHostnames'].get('Value')
        # for backwards compatibility
        vpc['id'] = vpc['VpcId']
        vpc_info.append(camel_dict_to_snake_dict(vpc))
        # convert tag list to ansible dict
        vpc_info[-1]['tags'] = boto3_tag_list_to_ansible_dict(vpc.get('Tags', []))

    module.exit_json(vpcs=vpc_info)
Example #47
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()

        # 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

    # 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:
        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 get_dhcp_options_info(dhcp_option):
    dhcp_option_info = {'DhcpOptionsId': dhcp_option['DhcpOptionsId'],
                        'DhcpConfigurations': dhcp_option['DhcpConfigurations'],
                        'Tags': boto3_tag_list_to_ansible_dict(dhcp_option.get('Tags', [{'Value': '', 'Key': 'Name'}]))}
    return dhcp_option_info
Example #49
0
def create_or_update_target_group(connection, module):

    changed = False
    new_target_group = False
    params = dict()
    params['Name'] = module.params.get("name")
    if module.params.get("target_type") != "lambda":
        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")

    health_option_keys = [
        "health_check_path", "health_check_protocol", "health_check_interval", "health_check_timeout",
        "healthy_threshold_count", "unhealthy_threshold_count", "successful_response_codes"
    ]
    health_options = any([module.params[health_option_key] is not None for health_option_key in health_option_keys])

    # Set health check if anything set
    if health_options:

        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")

        # Only need to check response code and path for http(s) health checks
        protocol = module.params.get("health_check_protocol")
        if protocol is not None and protocol.upper() in ['HTTP', 'HTTPS']:

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

            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()

        # Modify health check if anything set
        if health_options:

            # 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 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']

            # Only need to check response code and path for http(s) health checks
            if tg['HealthCheckProtocol'] in ['HTTP', 'HTTPS']:
                # Health check path
                if 'HealthCheckPath'in params and tg['HealthCheckPath'] != params['HealthCheckPath']:
                    health_check_params['HealthCheckPath'] = params['HealthCheckPath']

                # 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"):
            # 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")

            if module.params.get("targets"):

                if module.params.get("target_type") != "lambda":
                    params['Targets'] = module.params.get("targets")

                    # Correct type of target ports
                    for target in params['Targets']:
                        target['Port'] = int(target.get('Port', module.params.get('port')))

                    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': target['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')

                # register lambda target
                else:
                    try:
                        changed = False
                        target = module.params.get("targets")[0]
                        if len(current_targets["TargetHealthDescriptions"]) == 0:
                            changed = True
                        else:
                            for item in current_targets["TargetHealthDescriptions"]:
                                if target["Id"] != item["Target"]["Id"]:
                                    changed = True
                                    break  # only one target is possible with lambda

                        if changed:
                            if target.get("Id"):
                                response = connection.register_targets(
                                    TargetGroupArn=tg['TargetGroupArn'],
                                    Targets=[
                                        {
                                            "Id": target['Id']
                                        }
                                    ]
                                )

                    except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
                        module.fail_json_aws(
                            e, msg="Couldn't register targets")
            else:
                if module.params.get("target_type") != "lambda":

                    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')

                # remove lambda targets
                else:
                    changed = False
                    if current_targets["TargetHealthDescriptions"]:
                        changed = True
                        # only one target is possible with lambda
                        target_to_remove = current_targets["TargetHealthDescriptions"][0]["Target"]["Id"]
                    if changed:
                        connection.deregister_targets(
                            TargetGroupArn=tg['TargetGroupArn'], Targets=[{"Id": target_to_remove}])
    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"):
            if module.params.get("target_type") != "lambda":
                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')

            else:
                try:
                    target = module.params.get("targets")[0]
                    response = connection.register_targets(
                        TargetGroupArn=tg['TargetGroupArn'],
                        Targets=[
                            {
                                "Id": target["Id"]
                            }
                        ]
                    )
                    changed = True
                except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
                    module.fail_json_aws(
                        e, msg="Couldn't register targets")

    # 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)
Example #50
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
def describe_tags_with_backoff(connection, resource_id):
    filters = ansible_dict_to_boto3_filter_list({'resource-id': resource_id})
    paginator = connection.get_paginator('describe_tags')
    tags = paginator.paginate(Filters=filters).build_full_result()['Tags']
    return boto3_tag_list_to_ansible_dict(tags)
Example #52
0
def get_check_mode_results(connection,
                           module_params,
                           vpn_connection_id=None,
                           current_state=None):
    """ Returns the changes that would be made to a VPN Connection """
    state = module_params.get('state')
    if state == 'absent':
        if vpn_connection_id:
            return True, {}
        else:
            return False, {}

    changed = False
    results = {
        'customer_gateway_configuration': '',
        'customer_gateway_id': module_params.get('customer_gateway_id'),
        'vpn_gateway_id': module_params.get('vpn_gateway_id'),
        'options': {
            'static_routes_only': module_params.get('static_only')
        },
        'routes': [module_params.get('routes')]
    }

    # get combined current tags and tags to set
    present_tags = module_params.get('tags')
    if current_state and 'Tags' in current_state:
        current_tags = boto3_tag_list_to_ansible_dict(current_state['Tags'])
        if module_params.get('purge_tags'):
            if current_tags != present_tags:
                changed = True
        elif current_tags != present_tags:
            if not set(present_tags.keys()) < set(current_tags.keys()):
                changed = True
            # add preexisting tags that new tags didn't overwrite
            present_tags.update((tag, current_tags[tag])
                                for tag in current_tags
                                if tag not in present_tags)
        elif current_tags.keys() == present_tags.keys() and set(
                present_tags.values()) != set(current_tags.values()):
            changed = True
    elif module_params.get('tags'):
        changed = True
    if present_tags:
        results['tags'] = present_tags

    # get combined current routes and routes to add
    present_routes = module_params.get('routes')
    if current_state and 'Routes' in current_state:
        current_routes = [
            route['DestinationCidrBlock'] for route in current_state['Routes']
        ]
        if module_params.get('purge_routes'):
            if set(current_routes) != set(present_routes):
                changed = True
        elif set(present_routes) != set(current_routes):
            if not set(present_routes) < set(current_routes):
                changed = True
            present_routes.extend([
                route for route in current_routes
                if route not in present_routes
            ])
    elif module_params.get('routes'):
        changed = True
    results['routes'] = [{
        "destination_cidr_block": cidr,
        "state": "available"
    } for cidr in present_routes]

    # return the vpn_connection_id if it's known
    if vpn_connection_id:
        results['vpn_connection_id'] = vpn_connection_id
    else:
        changed = True
        results['vpn_connection_id'] = 'vpn-XXXXXXXX'

    return changed, results
Example #53
0
def main():
    argument_spec = dict(
        name=dict(required=True),
        cidr_block=dict(type='list', required=True),
        ipv6_cidr=dict(type='bool', default=False),
        tenancy=dict(choices=['default', 'dedicated'], default='default'),
        dns_support=dict(type='bool', default=True),
        dns_hostnames=dict(type='bool', default=True),
        dhcp_opts_id=dict(),
        tags=dict(type='dict', aliases=['resource_tags']),
        state=dict(choices=['present', 'absent'], default='present'),
        multi_ok=dict(type='bool', default=False),
        purge_cidrs=dict(type='bool', default=False),
    )

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

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

    changed = False

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

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

    if state == 'present':

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

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

        vpc_obj = get_vpc(module, connection, vpc_id)

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

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

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

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

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

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

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

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

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

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

    elif state == 'absent':

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

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

        module.exit_json(changed=changed, vpc={})
Example #54
0
 def get_tags(self, file_system_id):
     """
      Returns tag list for selected instance of EFS
     """
     paginator = self.connection.get_paginator('describe_tags')
     return boto3_tag_list_to_ansible_dict(paginator.paginate(FileSystemId=file_system_id).build_full_result()['Tags'])
def get_tags(ec2, module, resource):
    filters = [{'Name': 'resource-id', 'Values': [resource]}]
    try:
        return boto3_tag_list_to_ansible_dict(ec2.describe_tags(Filters=filters)['Tags'])
    except (BotoCoreError, ClientError) as e:
        module.fail_json_aws(e, msg='Failed to fetch tags for resource {0}'.format(resource))
Example #56
0
    def get_certificates(self,
                         client,
                         module,
                         domain_name=None,
                         statuses=None,
                         arn=None,
                         only_tags=None):
        try:
            all_certificates = self.list_certificates_with_backoff(
                client=client, statuses=statuses)
        except (BotoCoreError, ClientError) as e:
            module.fail_json_aws(e, msg="Couldn't obtain certificates")
        if domain_name:
            certificates = [
                cert for cert in all_certificates
                if cert['DomainName'] == domain_name
            ]
        else:
            certificates = all_certificates

        if arn:
            # still return a list, not just one item
            certificates = [
                c for c in certificates if c['CertificateArn'] == arn
            ]

        results = []
        for certificate in certificates:
            try:
                cert_data = self.describe_certificate_with_backoff(
                    client, certificate['CertificateArn'])
            except (BotoCoreError, ClientError) as e:
                module.fail_json_aws(
                    e,
                    msg="Couldn't obtain certificate metadata for domain %s" %
                    certificate['DomainName'])

            # in some states, ACM resources do not have a corresponding cert
            if cert_data['Status'] not in [
                    'PENDING_VALIDATION', 'VALIDATION_TIMED_OUT', 'FAILED'
            ]:
                try:
                    cert_data.update(
                        self.get_certificate_with_backoff(
                            client, certificate['CertificateArn']))
                except (BotoCoreError, ClientError, KeyError) as e:
                    if e.response['Error'][
                            'Code'] != "RequestInProgressException":
                        module.fail_json_aws(
                            e,
                            msg="Couldn't obtain certificate data for domain %s"
                            % certificate['DomainName'])
            cert_data = camel_dict_to_snake_dict(cert_data)
            try:
                tags = self.list_certificate_tags_with_backoff(
                    client, certificate['CertificateArn'])
            except (BotoCoreError, ClientError) as e:
                module.fail_json_aws(e,
                                     msg="Couldn't obtain tags for domain %s" %
                                     certificate['DomainName'])

            cert_data['tags'] = boto3_tag_list_to_ansible_dict(tags)
            results.append(cert_data)

        if only_tags:
            for tag_key in only_tags:
                try:
                    results = [
                        c for c in results
                        if ('tags' in c) and (tag_key in c['tags']) and (
                            c['tags'][tag_key] == only_tags[tag_key])
                    ]
                except (TypeError, AttributeError) as e:
                    for c in results:
                        if 'tags' not in c:
                            module.debug("cert is %s" % str(c))
                    module.fail_json(msg="ACM tag filtering err", exception=e)

        return results
Example #57
0
def get_target_group_tags(connection, module, target_group_arn):

    try:
        return boto3_tag_list_to_ansible_dict(connection.describe_tags(ResourceArns=[target_group_arn])['TagDescriptions'][0]['Tags'])
    except ClientError as e:
        module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
Example #58
0
def await_stack_set_exists(cfn, stack_set_name):
    # AWSRetry will retry on `NotFound` errors for us
    ss = cfn.describe_stack_set(StackSetName=stack_set_name,
                                aws_retry=True)['StackSet']
    ss['Tags'] = boto3_tag_list_to_ansible_dict(ss['Tags'])
    return camel_dict_to_snake_dict(ss, ignore_list=('Tags', ))