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 find_address(ec2, module, public_ip, device_id, is_instance=True): """ Find an existing Elastic IP address """ filters = [] kwargs = {} if public_ip: kwargs["PublicIps"] = [public_ip] elif device_id: if is_instance: filters.append({"Name": 'instance-id', "Values": [device_id]}) else: filters.append({'Name': 'network-interface-id', "Values": [device_id]}) if len(filters) > 0: kwargs["Filters"] = filters elif len(filters) == 0 and public_ip is None: return None try: addresses = ec2.describe_addresses(**kwargs) except is_boto3_error_code('InvalidAddress.NotFound') as e: # If we're releasing and we can't find it, it's already gone... if module.params.get('state') == 'absent': module.exit_json(changed=False) module.fail_json_aws(e, msg="Couldn't obtain list of existing Elastic IP addresses") addresses = addresses["Addresses"] if len(addresses) == 1: return addresses[0] elif len(addresses) > 1: msg = "Found more than one address using args {0}".format(kwargs) msg += "Addresses found: {0}".format(addresses) module.fail_json_aws(botocore.exceptions.ClientError, msg=msg)
def existing_templates(module): ec2 = module.client('ec2', retry_decorator=AWSRetry.jittered_backoff()) matches = None try: if module.params.get('template_id'): matches = ec2.describe_launch_templates( LaunchTemplateIds=[module.params.get('template_id')]) elif module.params.get('template_name'): matches = ec2.describe_launch_templates( LaunchTemplateNames=[module.params.get('template_name')]) except is_boto3_error_code( 'InvalidLaunchTemplateName.NotFoundException') as e: # no named template was found, return nothing/empty versions return None, [] except is_boto3_error_code('InvalidLaunchTemplateId.Malformed') as e: # pylint: disable=duplicate-except module.fail_json_aws( e, msg= 'Launch template with ID {0} is not a valid ID. It should start with `lt-....`' .format(module.params.get('launch_template_id'))) except is_boto3_error_code( 'InvalidLaunchTemplateId.NotFoundException') as e: # pylint: disable=duplicate-except module.fail_json_aws( e, msg= 'Launch template with ID {0} could not be found, please supply a name ' 'instead so that a new template can be created'.format( module.params.get('launch_template_id'))) except (ClientError, BotoCoreError, WaiterError) as e: # pylint: disable=duplicate-except module.fail_json_aws( e, msg= 'Could not check existing launch templates. This may be an IAM permission problem.' ) else: template = matches['LaunchTemplates'][0] template_id, template_version, template_default = template[ 'LaunchTemplateId'], template['LatestVersionNumber'], template[ 'DefaultVersionNumber'] try: return template, ec2.describe_launch_template_versions( LaunchTemplateId=template_id)['LaunchTemplateVersions'] except (ClientError, BotoCoreError, WaiterError) as e: module.fail_json_aws( e, msg='Could not find launch template versions for {0} (ID: {1}).' .format(template['LaunchTemplateName'], template_id))
def paginated_versions_list(s3_client, **pagination_params): try: pg = s3_client.get_paginator('list_object_versions') for page in pg.paginate(**pagination_params): # We have to merge the Versions and DeleteMarker lists here, as DeleteMarkers can still prevent a bucket deletion yield [(data['Key'], data['VersionId']) for data in (page.get('Versions', []) + page.get('DeleteMarkers', []))] except is_boto3_error_code('NoSuchBucket'): yield []
def resource_exists(client, module, params): try: aggregator = client.describe_configuration_aggregators( ConfigurationAggregatorNames=[params['name']]) return aggregator['ConfigurationAggregators'][0] except is_boto3_error_code('NoSuchConfigurationAggregatorException'): return except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e)
def resource_exists(client, module, params): try: channel = client.describe_delivery_channels( DeliveryChannelNames=[params['name']], aws_retry=True, ) return channel['DeliveryChannels'][0] except is_boto3_error_code('NoSuchDeliveryChannelException'): return except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e)
def rule_exists(client, module, params): try: rule = client.describe_config_rules( ConfigRuleNames=[params['ConfigRuleName']], aws_retry=True, ) return rule['ConfigRules'][0] except is_boto3_error_code('NoSuchConfigRuleException'): return except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e)
def get_kms_policies(connection, module, key_id): try: policies = list_key_policies_with_backoff(connection, key_id)['PolicyNames'] return [ get_key_policy_with_backoff(connection, key_id, policy)['Policy'] for policy in policies ] except is_boto3_error_code('AccessDeniedException'): return [] except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e, msg="Failed to obtain key policies")
def describe_pipeline(client, name, version, module): pipeline = {} try: if version is not None: pipeline = client.get_pipeline(name=name, version=version) return pipeline else: pipeline = client.get_pipeline(name=name) return pipeline except is_boto3_error_code('PipelineNotFoundException'): return pipeline except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e)
def _add_tags_for_hosts(connection, hosts, strict): for host in hosts: if 'DBInstanceArn' in host: resource_arn = host['DBInstanceArn'] else: resource_arn = host['DBClusterArn'] try: tags = connection.list_tags_for_resource(ResourceName=resource_arn)['TagList'] except is_boto3_error_code('AccessDenied') as e: if not strict: tags = [] else: raise e host['Tags'] = tags
def peer_status(client, module): params = dict() params['VpcPeeringConnectionIds'] = [module.params.get('peering_id')] try: vpc_peering_connection = client.describe_vpc_peering_connections( **params) return vpc_peering_connection['VpcPeeringConnections'][0]['Status'][ 'Code'] except is_boto3_error_code('InvalidVpcPeeringConnectionId.Malformed') as e: # pylint: disable=duplicate-except module.fail_json(msg='Malformed connection ID: {0}'.format(e), traceback=traceback.format_exc()) except botocore.exceptions.ClientError as e: # pylint: disable=duplicate-except module.fail_json( msg='Error while describing peering connection by peering_id: {0}'. format(e), traceback=traceback.format_exc())
def wrapper(f, *args, **kwargs): try: results = f(*args, **kwargs) if 'DBInstances' in results: results = results['DBInstances'] else: results = results['DBClusters'] _add_tags_for_hosts(connection, results, strict) except is_boto3_error_code('AccessDenied') as e: # pylint: disable=duplicate-except if not strict: results = [] else: raise AnsibleError("Failed to query RDS: {0}".format(to_native(e))) except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: # pylint: disable=duplicate-except raise AnsibleError("Failed to query RDS: {0}".format(to_native(e))) return results
def common_snapshot_info(module, conn, method, prefix, params): paginator = conn.get_paginator(method) try: results = paginator.paginate(**params).build_full_result()['%ss' % prefix] except is_boto3_error_code('%sNotFound' % prefix): results = [] except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e, "trying to get snapshot information") for snapshot in results: try: snapshot['Tags'] = boto3_tag_list_to_ansible_dict(conn.list_tags_for_resource(ResourceName=snapshot['%sArn' % prefix], aws_retry=True)['TagList']) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, "Couldn't get tags for snapshot %s" % snapshot['%sIdentifier' % prefix]) return [camel_dict_to_snake_dict(snapshot, ignore_list=['Tags']) for snapshot in results]
def determine_iam_role(module, name_or_arn): if re.match(r'^arn:aws:iam::\d+:instance-profile/[\w+=/,.@-]+$', name_or_arn): return name_or_arn iam = module.client('iam', retry_decorator=AWSRetry.jittered_backoff()) try: role = iam.get_instance_profile(InstanceProfileName=name_or_arn, aws_retry=True) return {'arn': role['InstanceProfile']['Arn']} except is_boto3_error_code('NoSuchEntity') as e: module.fail_json_aws( e, msg="Could not find instance_role {0}".format(name_or_arn)) except (BotoCoreError, ClientError) as e: # pylint: disable=duplicate-except module.fail_json_aws( e, msg= "An error occurred while searching for instance_role {0}. Please try supplying the full ARN." .format(name_or_arn))
def delete_cluster(module, redshift): """ Delete a cluster. module: Ansible module object redshift: authenticated redshift connection object """ identifier = module.params.get('identifier') wait = module.params.get('wait') wait_timeout = module.params.get('wait_timeout') params = {} for p in ('skip_final_cluster_snapshot', 'final_cluster_snapshot_identifier'): if p in module.params: # https://github.com/boto/boto3/issues/400 if module.params.get(p) is not None: params[p] = module.params.get(p) try: redshift.delete_cluster(ClusterIdentifier=identifier, **snake_dict_to_camel_dict( params, capitalize_first=True)) except is_boto3_error_code('ClusterNotFound'): return (False, {}) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e, msg="Failed to delete cluster") if wait: attempts = wait_timeout // 60 waiter = redshift.get_waiter('cluster_deleted') try: waiter.wait(ClusterIdentifier=identifier, WaiterConfig=dict(MaxAttempts=attempts)) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Timeout deleting the cluster") return (True, {})
def get_kms_tags(connection, module, key_id): # Handle pagination here as list_resource_tags does not have # a paginator kwargs = {} tags = [] more = True while more: try: tag_response = get_kms_tags_with_backoff(connection, key_id, **kwargs) tags.extend(tag_response['Tags']) except is_boto3_error_code('AccessDeniedException'): tag_response = {} except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e, msg="Failed to obtain key tags") if tag_response.get('NextMarker'): kwargs['Marker'] = tag_response['NextMarker'] else: more = False return tags
def _list_topic_subscriptions(self): try: return self._list_topic_subscriptions_with_backoff() except is_boto3_error_code('AuthorizationError'): try: # potentially AuthorizationError when listing subscriptions for third party topic return [ sub for sub in self._list_subscriptions_with_backoff() if sub['TopicArn'] == self.topic_arn ] except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: self.module.fail_json_aws( e, msg="Couldn't get subscriptions list for topic %s" % self.topic_arn) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except self.module.fail_json_aws( e, msg="Couldn't get subscriptions list for topic %s" % self.topic_arn)
def create_vgw(client, module): params = dict() params['Type'] = module.params.get('type') if module.params.get('asn'): params['AmazonSideAsn'] = module.params.get('asn') try: response = client.create_vpn_gateway(**params) get_waiter( client, 'vpn_gateway_exists' ).wait( VpnGatewayIds=[response['VpnGateway']['VpnGatewayId']] ) except botocore.exceptions.WaiterError as e: module.fail_json(msg="Failed to wait for Vpn Gateway {0} to be available".format(response['VpnGateway']['VpnGatewayId']), exception=traceback.format_exc()) except is_boto3_error_code('VpnGatewayLimitExceeded'): module.fail_json(msg="Too many VPN gateways exist in this account.", exception=traceback.format_exc()) except botocore.exceptions.ClientError as e: # pylint: disable=duplicate-except module.fail_json(msg=to_native(e), exception=traceback.format_exc()) result = response return result
def instance_info(module, conn): instance_name = module.params.get('db_instance_identifier') filters = module.params.get('filters') params = dict() if instance_name: params['DBInstanceIdentifier'] = instance_name if filters: params['Filters'] = ansible_dict_to_boto3_filter_list(filters) paginator = conn.get_paginator('describe_db_instances') try: results = paginator.paginate( **params).build_full_result()['DBInstances'] except is_boto3_error_code('DBInstanceNotFound'): results = [] except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e, "Couldn't get instance information") for instance in results: try: instance['Tags'] = boto3_tag_list_to_ansible_dict( conn.list_tags_for_resource( ResourceName=instance['DBInstanceArn'], aws_retry=True)['TagList']) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws( e, "Couldn't get tags for instance %s" % instance['DBInstanceIdentifier']) return dict(changed=False, instances=[ camel_dict_to_snake_dict(instance, ignore_list=['Tags']) for instance in results ])
def describe_eni_with_backoff(ec2, module, device_id): try: return ec2.describe_network_interfaces(NetworkInterfaceIds=[device_id]) except is_boto3_error_code('InvalidNetworkInterfaceID.NotFound') as e: module.fail_json_aws(e, msg="Couldn't get list of network interfaces.")
def create_cluster(module, redshift): """ Create a new cluster module: AnsibleModule object redshift: authenticated redshift connection object Returns: """ identifier = module.params.get('identifier') node_type = module.params.get('node_type') username = module.params.get('username') password = module.params.get('password') d_b_name = module.params.get('db_name') wait = module.params.get('wait') wait_timeout = module.params.get('wait_timeout') changed = True # Package up the optional parameters params = {} for p in ('cluster_type', 'cluster_security_groups', 'vpc_security_group_ids', 'cluster_subnet_group_name', 'availability_zone', 'preferred_maintenance_window', 'cluster_parameter_group_name', 'automated_snapshot_retention_period', 'port', 'cluster_version', 'allow_version_upgrade', 'number_of_nodes', 'publicly_accessible', 'encrypted', 'elastic_ip', 'enhanced_vpc_routing'): # https://github.com/boto/boto3/issues/400 if module.params.get(p) is not None: params[p] = module.params.get(p) if d_b_name: params['d_b_name'] = d_b_name try: redshift.describe_clusters(ClusterIdentifier=identifier)['Clusters'][0] changed = False except is_boto3_error_code('ClusterNotFound'): try: redshift.create_cluster(ClusterIdentifier=identifier, NodeType=node_type, MasterUsername=username, MasterUserPassword=password, **snake_dict_to_camel_dict( params, capitalize_first=True)) except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: module.fail_json_aws(e, msg="Failed to create cluster") except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e, msg="Failed to describe cluster") if wait: attempts = wait_timeout // 60 waiter = redshift.get_waiter('cluster_available') try: waiter.wait(ClusterIdentifier=identifier, WaiterConfig=dict(MaxAttempts=attempts)) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws( e, msg="Timeout waiting for the cluster creation") try: resource = redshift.describe_clusters( ClusterIdentifier=identifier)['Clusters'][0] except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: module.fail_json_aws(e, msg="Failed to describe cluster") return (changed, _collect_facts(resource))