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")
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') )
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
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())
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 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)
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
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
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())
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)
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'])
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())
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
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 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)
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
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
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
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())
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)
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)
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)
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
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
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)
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)
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))
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
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={})
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'])
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)
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)))
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)
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)
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))
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)
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
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)
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)
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
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={})
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))
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
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', ))