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 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
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)
def find_clusters(conn, module, identifier=None, tags=None): try: cluster_paginator = conn.get_paginator('describe_clusters') clusters = cluster_paginator.paginate().build_full_result() except ClientError as e: module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) matched_clusters = [] if identifier is not None: identifier_prog = re.compile('^' + identifier) for cluster in clusters['Clusters']: matched_identifier = True if identifier: matched_identifier = identifier_prog.search( cluster['ClusterIdentifier']) matched_tags = True if tags: matched_tags = match_tags(tags, cluster) if matched_identifier and matched_tags: matched_clusters.append(camel_dict_to_snake_dict(cluster)) return matched_clusters
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)
def list_load_balancers(connection, module): load_balancer_arns = module.params.get("load_balancer_arns") names = module.params.get("names") try: load_balancer_paginator = connection.get_paginator( 'describe_load_balancers') if not load_balancer_arns and not names: load_balancers = load_balancer_paginator.paginate( ).build_full_result() if load_balancer_arns: load_balancers = load_balancer_paginator.paginate( LoadBalancerArns=load_balancer_arns).build_full_result() if names: load_balancers = load_balancer_paginator.paginate( Names=names).build_full_result() except ClientError as e: if e.response['Error']['Code'] == 'LoadBalancerNotFound': module.exit_json(load_balancers=[]) else: 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()) for load_balancer in load_balancers['LoadBalancers']: # Get the attributes for each elb load_balancer.update( get_load_balancer_attributes(connection, module, load_balancer['LoadBalancerArn'])) # Get the listeners for each elb load_balancer['listeners'] = get_elb_listeners( connection, module, load_balancer['LoadBalancerArn']) # For each listener, get listener rules for listener in load_balancer['listeners']: listener['rules'] = get_listener_rules(connection, module, listener['ListenerArn']) # Turn the boto3 result in to ansible_friendly_snaked_names snaked_load_balancers = [ camel_dict_to_snake_dict(load_balancer) for load_balancer in load_balancers['LoadBalancers'] ] # Get tags for each load balancer for snaked_load_balancer in snaked_load_balancers: snaked_load_balancer['tags'] = get_load_balancer_tags( connection, module, snaked_load_balancer['load_balancer_arn']) module.exit_json(load_balancers=snaked_load_balancers)
def list_mfa_devices(connection, module): user_name = module.params.get('user_name') changed = False args = {} if user_name is not None: args['UserName'] = user_name try: response = connection.list_mfa_devices(**args) except ClientError as e: module.fail_json(msg=e.message, **camel_dict_to_snake_dict(e.response)) module.exit_json(changed=changed, **camel_dict_to_snake_dict(response))
def get_bucket_list(module, connection): """ Return result of list_buckets json encoded :param module: :param connection: :return: """ try: buckets = camel_dict_to_snake_dict(connection.list_buckets())['buckets'] except botocore.exceptions.ClientError as e: module.fail_json(msg=to_native(e), exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) return buckets
def ensure_ipv6_cidr_block(conn, module, subnet, ipv6_cidr, check_mode, start_time): wait = module.params['wait'] changed = False if subnet['ipv6_association_id'] and not ipv6_cidr: if not check_mode: disassociate_ipv6_cidr(conn, module, subnet, start_time) changed = True if ipv6_cidr: filters = ansible_dict_to_boto3_filter_list({'ipv6-cidr-block-association.ipv6-cidr-block': ipv6_cidr, 'vpc-id': subnet['vpc_id']}) try: check_subnets = get_subnet_info(describe_subnets_with_backoff(conn, Filters=filters)) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't get subnet info") if check_subnets and check_subnets[0]['ipv6_cidr_block']: module.fail_json(msg="The IPv6 CIDR '{0}' conflicts with another subnet".format(ipv6_cidr)) if subnet['ipv6_association_id']: if not check_mode: disassociate_ipv6_cidr(conn, module, subnet, start_time) changed = True try: if not check_mode: associate_resp = conn.associate_subnet_cidr_block(SubnetId=subnet['id'], Ipv6CidrBlock=ipv6_cidr) changed = True except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't associate ipv6 cidr {0} to {1}".format(ipv6_cidr, subnet['id'])) else: if not check_mode and wait: filters = ansible_dict_to_boto3_filter_list( {'ipv6-cidr-block-association.state': ['associated'], 'vpc-id': subnet['vpc_id']} ) handle_waiter(conn, module, 'subnet_exists', {'SubnetIds': [subnet['id']], 'Filters': filters}, start_time) if associate_resp.get('Ipv6CidrBlockAssociation', {}).get('AssociationId'): subnet['ipv6_association_id'] = associate_resp['Ipv6CidrBlockAssociation']['AssociationId'] subnet['ipv6_cidr_block'] = associate_resp['Ipv6CidrBlockAssociation']['Ipv6CidrBlock'] if subnet['ipv6_cidr_block_association_set']: subnet['ipv6_cidr_block_association_set'][0] = camel_dict_to_snake_dict(associate_resp['Ipv6CidrBlockAssociation']) else: subnet['ipv6_cidr_block_association_set'].append(camel_dict_to_snake_dict(associate_resp['Ipv6CidrBlockAssociation'])) return changed
def get_or_create_policy_version(module, iam, policy, policy_document): try: versions = iam.list_policy_versions( PolicyArn=policy['Arn'])['Versions'] except botocore.exceptions.ClientError as e: module.fail_json(msg="Couldn't list policy versions: %s" % str(e), exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) for v in versions: try: document = iam.get_policy_version( PolicyArn=policy['Arn'], VersionId=v['VersionId'])['PolicyVersion']['Document'] except botocore.exceptions.ClientError as e: module.fail_json(msg="Couldn't get policy version %s: %s" % (v['VersionId'], str(e)), exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) # If the current policy matches the existing one if not compare_policies(document, json.loads( to_native(policy_document))): return v, False # No existing version so create one # There is a service limit (typically 5) of policy versions. # # Rather than assume that it is 5, we'll try to create the policy # and if that doesn't work, delete the oldest non default policy version # and try again. try: version = iam.create_policy_version( PolicyArn=policy['Arn'], PolicyDocument=policy_document)['PolicyVersion'] return version, True except botocore.exceptions.ClientError as e: if e.response['Error']['Code'] == 'LimitExceeded': delete_oldest_non_default_version(module, iam, policy) try: version = iam.create_policy_version( PolicyArn=policy['Arn'], PolicyDocument=policy_document)['PolicyVersion'] return version, True except botocore.exceptions.ClientError as second_e: e = second_e # Handle both when the exception isn't LimitExceeded or # the second attempt still failed module.fail_json(msg="Couldn't create policy version: %s" % str(e), exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
def update_resource(client, module, params, result): current_params = client.describe_delivery_channels( DeliveryChannelNames=[params['name']], aws_retry=True, ) if params != current_params['DeliveryChannels'][0]: try: retry_unavailable_iam_on_put_delivery( client.put_delivery_channel, )(DeliveryChannel=params, ) result['changed'] = True result['channel'] = camel_dict_to_snake_dict( resource_exists(client, module, params)) return result except is_boto3_error_code('InvalidS3KeyPrefixException') as e: module.fail_json_aws( e, msg= "The `s3_prefix` parameter was invalid. Try '/' for no prefix") except is_boto3_error_code('InsufficientDeliveryPolicyException') as e: # pylint: disable=duplicate-except module.fail_json_aws( e, msg="The `s3_prefix` or `s3_bucket` parameter is invalid. " "Make sure the bucket exists and is available") except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws( e, msg="Couldn't create AWS Config delivery channel")
def copy_image(module, ec2): """ Copies an AMI module : AnsibleModule object ec2: ec2 connection object """ image = None changed = False tags = module.params.get('tags') params = { 'SourceRegion': module.params.get('source_region'), 'SourceImageId': module.params.get('source_image_id'), 'Name': module.params.get('name'), 'Description': module.params.get('description'), 'Encrypted': module.params.get('encrypted'), } if module.params.get('kms_key_id'): params['KmsKeyId'] = module.params.get('kms_key_id') try: if module.params.get('tag_equality'): filters = [{ 'Name': 'tag:%s' % k, 'Values': [v] } for (k, v) in module.params.get('tags').items()] filters.append(dict(Name='state', Values=['available', 'pending'])) images = ec2.describe_images(Filters=filters) if len(images['Images']) > 0: image = images['Images'][0] if not image: image = ec2.copy_image(**params) image_id = image['ImageId'] if tags: ec2.create_tags(Resources=[image_id], Tags=ansible_dict_to_boto3_tag_list(tags)) changed = True if module.params.get('wait'): delay = 15 max_attempts = module.params.get('wait_timeout') // delay image_id = image.get('ImageId') ec2.get_waiter('image_available').wait(ImageIds=[image_id], WaiterConfig={ 'Delay': delay, 'MaxAttempts': max_attempts }) module.exit_json(changed=changed, **camel_dict_to_snake_dict(image)) except WaiterError as e: module.fail_json_aws( e, msg='An error occurred waiting for the image to become available') except (ClientError, BotoCoreError) as e: module.fail_json_aws(e, msg="Could not copy AMI") except Exception as e: module.fail_json(msg='Unhandled exception. (%s)' % to_native(e))
def remove_rule_set(client, module): name = module.params.get('name') check_mode = module.check_mode changed = False rule_sets = list_rule_sets(client, module) if rule_set_in(name, rule_sets): active = ruleset_active(client, module, name) if active and not module.params.get('force'): module.fail_json( msg= "Couldn't delete rule set {0} because it is currently active. Set force=true to delete an active ruleset." .format(name), error={ "code": "CannotDelete", "message": "Cannot delete active rule set: {0}".format(name), }) if not check_mode: if active and module.params.get('force'): deactivate_rule_set(client, module) try: client.delete_receipt_rule_set(RuleSetName=name, aws_retry=True) except (BotoCoreError, ClientError) as e: module.fail_json_aws( e, msg="Couldn't delete rule set {0}.".format(name)) changed = True rule_sets = [x for x in rule_sets if x['Name'] != name] module.exit_json( changed=changed, rule_sets=[camel_dict_to_snake_dict(x) for x in rule_sets], )
def create_or_update_rule_set(client, module): name = module.params.get('name') check_mode = module.check_mode changed = False rule_sets = list_rule_sets(client, module) if not rule_set_in(name, rule_sets): if not check_mode: try: client.create_receipt_rule_set(RuleSetName=name, aws_retry=True) except (BotoCoreError, ClientError) as e: module.fail_json_aws( e, msg="Couldn't create rule set {0}.".format(name)) changed = True rule_sets = list(rule_sets) rule_sets.append({ 'Name': name, }) (active_changed, active) = update_active_rule_set(client, module, name, module.params.get('active')) changed |= active_changed module.exit_json( changed=changed, active=active, rule_sets=[camel_dict_to_snake_dict(x) for x in rule_sets], )
def main(): module = AnsibleAWSModule( argument_spec={}, supports_check_mode=True, ) if module._name == 'aws_caller_facts': module.deprecate( "The 'aws_caller_facts' module has been renamed to 'aws_caller_info'", version='2.13') client = module.client('sts') try: caller_info = client.get_caller_identity() caller_info.pop('ResponseMetadata', None) except (BotoCoreError, ClientError) as e: module.fail_json_aws(e, msg='Failed to retrieve caller identity') iam_client = module.client('iam') try: # Although a list is returned by list_account_aliases AWS supports maximum one alias per account. # If an alias is defined it will be returned otherwise a blank string is filled in as account_alias. # see https://docs.aws.amazon.com/cli/latest/reference/iam/list-account-aliases.html#output response = iam_client.list_account_aliases() if response and response['AccountAliases']: caller_info['account_alias'] = response['AccountAliases'][0] else: caller_info['account_alias'] = '' except (BotoCoreError, ClientError) as e: # The iam:ListAccountAliases permission is required for this operation to succeed. # Lacking this permission is handled gracefully by not returning the account_alias. pass module.exit_json(changed=False, **camel_dict_to_snake_dict(caller_info))