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 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 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 create_vpc_endpoint(client, module): params = dict() changed = False token_provided = False params['VpcId'] = module.params.get('vpc_id') params['ServiceName'] = module.params.get('service') params['DryRun'] = module.check_mode if module.params.get('route_table_ids'): params['RouteTableIds'] = module.params.get('route_table_ids') if module.params.get('client_token'): token_provided = True request_time = datetime.datetime.utcnow() params['ClientToken'] = module.params.get('client_token') policy = None if module.params.get('policy'): try: policy = json.loads(module.params.get('policy')) except ValueError as e: module.fail_json(msg=str(e), exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) elif module.params.get('policy_file'): try: with open(module.params.get('policy'), 'r') as json_data: policy = json.load(json_data) except Exception as e: module.fail_json(msg=str(e), exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) if policy: params['PolicyDocument'] = json.dumps(policy) try: changed = True result = camel_dict_to_snake_dict(client.create_vpc_endpoint(**params)['VpcEndpoint']) if token_provided and (request_time > result['creation_timestamp'].replace(tzinfo=None)): changed = False elif module.params.get('wait') and not module.check_mode: status_achieved, result = wait_for_status(client, module, result['vpc_endpoint_id'], 'available') if not status_achieved: module.fail_json(msg='Error waiting for vpc endpoint to become available - please check the AWS console') except botocore.exceptions.ClientError as e: if "DryRunOperation" in e.message: changed = True result = 'Would have created VPC Endpoint if not in check mode' elif "IdempotentParameterMismatch" in e.message: module.fail_json(msg="IdempotentParameterMismatch - updates of endpoints are not allowed by the API") elif "RouteAlreadyExists" in e.message: module.fail_json(msg="RouteAlreadyExists for one of the route tables - update is not allowed by the API") else: module.fail_json(msg=str(e), exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) except Exception as e: module.fail_json(msg=str(e), exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) return changed, result
def detach_all_entities(module, iam, policy, **kwargs): try: entities = iam.list_entities_for_policy(PolicyArn=policy['Arn'], **kwargs) except botocore.exceptions.ClientError as e: module.fail_json(msg="Couldn't detach list entities for policy %s: %s" % (policy['PolicyName'], str(e)), exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) for g in entities['PolicyGroups']: try: iam.detach_group_policy(PolicyArn=policy['Arn'], GroupName=g['GroupName']) except botocore.exceptions.ClientError as e: module.fail_json(msg="Couldn't detach group policy %s: %s" % (g['GroupName'], str(e)), exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) for u in entities['PolicyUsers']: try: iam.detach_user_policy(PolicyArn=policy['Arn'], UserName=u['UserName']) except botocore.exceptions.ClientError as e: module.fail_json(msg="Couldn't detach user policy %s: %s" % (u['UserName'], str(e)), exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) for r in entities['PolicyRoles']: try: iam.detach_role_policy(PolicyArn=policy['Arn'], RoleName=r['RoleName']) except botocore.exceptions.ClientError as e: module.fail_json(msg="Couldn't detach role policy %s: %s" % (r['RoleName'], str(e)), exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) if entities['IsTruncated']: detach_all_entities(module, iam, policy, marker=entities['Marker'])
def main(): argument_spec = ec2_argument_spec() argument_spec.update( dict( vpc_id=dict(), service=dict(), policy=dict(type='json'), policy_file=dict(type='path'), state=dict(default='present', choices=['present', 'absent']), wait=dict(type='bool', default=False), wait_timeout=dict(type='int', default=320, required=False), route_table_ids=dict(type='list'), vpc_endpoint_id=dict(), client_token=dict(), ) ) module = AnsibleModule( argument_spec=argument_spec, supports_check_mode=True, mutually_exclusive=[['policy', 'policy_file']], required_if=[ ['state', 'present', ['vpc_id', 'service']], ['state', 'absent', ['vpc_endpoint_id']], ] ) # Validate Requirements if not HAS_BOTO3: module.fail_json(msg='botocore and boto3 are required for this module') state = module.params.get('state') try: region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module, boto3=True) except NameError as e: # Getting around the get_aws_connection_info boto reliance for region if "global name 'boto' is not defined" in e.message: module.params['region'] = botocore.session.get_session().get_config_variable('region') if not module.params['region']: module.fail_json(msg="Error - no region provided") else: module.fail_json(msg="Can't retrieve connection information - " + str(e), exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) try: region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module, boto3=True) ec2 = boto3_conn(module, conn_type='client', resource='ec2', region=region, endpoint=ec2_url, **aws_connect_kwargs) except botocore.exceptions.NoCredentialsError as e: module.fail_json(msg="Failed to connect to AWS due to wrong or missing credentials: %s" % str(e), exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) # Ensure resource is present if state == 'present': (changed, results) = setup_creation(ec2, module) else: (changed, results) = setup_removal(ec2, module) module.exit_json(changed=changed, result=results)
def create_or_update_identity(connection, module, region, account_id): identity = module.params.get('identity') changed = False verification_attributes = get_verification_attributes(connection, module, identity) if verification_attributes is None: if '@' in identity: call_and_handle_errors(module, connection.verify_email_identity, EmailAddress=identity) else: call_and_handle_errors(module, connection.verify_domain_identity, Domain=identity) verification_attributes = get_verification_attributes(connection, module, identity, retries=4) changed = True elif verification_attributes['VerificationStatus'] not in ('Pending', 'Success'): module.fail_json(msg="Identity " + identity + " in bad status " + verification_attributes['VerificationStatus'], verification_attributes=camel_dict_to_snake_dict(verification_attributes)) if verification_attributes is None: module.fail_json(msg='Unable to load identity verification attributes after registering identity.') notifications_changed, notification_attributes = update_identity_notifications(connection, module) changed |= notifications_changed if notification_attributes is None: module.fail_json(msg='Unable to load identity notification attributes.') identity_arn = 'arn:aws:ses:' + region + ':' + account_id + ':identity/' + identity module.exit_json( changed=changed, identity=identity, identity_arn=identity_arn, verification_attributes=camel_dict_to_snake_dict(verification_attributes), notification_attributes=camel_dict_to_snake_dict(notification_attributes), )
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 find_clusters(conn, module, identifier=None, tags=None): try: cluster_paginator = conn.get_paginator('describe_clusters') clusters = cluster_paginator.paginate().build_full_result() except ClientError as e: module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) matched_clusters = [] if identifier is not None: identifier_prog = re.compile('^' + identifier) for cluster in clusters['Clusters']: matched_identifier = True if identifier: matched_identifier = identifier_prog.search(cluster['ClusterIdentifier']) matched_tags = True if tags: matched_tags = match_tags(tags, cluster) if matched_identifier and matched_tags: matched_clusters.append(camel_dict_to_snake_dict(cluster)) return matched_clusters
def 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 destroy_role(connection, module): params = dict() params['RoleName'] = module.params.get('name') if get_role(connection, module, params['RoleName']): # We need to remove any instance profiles from the role before we delete it try: instance_profiles = connection.list_instance_profiles_for_role(RoleName=params['RoleName'])['InstanceProfiles'] except ClientError as e: module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) # Now remove the role from the instance profile(s) for profile in instance_profiles: try: connection.remove_role_from_instance_profile(InstanceProfileName=profile['InstanceProfileName'], RoleName=params['RoleName']) except ClientError as e: module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) # Now remove any attached policies otherwise deletion fails try: for policy in get_attached_policy_list(connection, module, params['RoleName']): connection.detach_role_policy(RoleName=params['RoleName'], PolicyArn=policy['PolicyArn']) except ClientError as e: module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) try: connection.delete_role(**params) except ClientError as e: module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) else: module.exit_json(changed=False) module.exit_json(changed=True)
def destroy_group(connection, module): params = dict() params['GroupName'] = module.params.get('name') try: group = get_group(connection, module, params['GroupName']) except ClientError as e: module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) if group: # Check mode means we would remove this group if module.check_mode: module.exit_json(changed=True) # Remove any attached policies otherwise deletion fails try: for policy in get_attached_policy_list(connection, module, params['GroupName']): connection.detach_group_policy(GroupName=params['GroupName'], PolicyArn=policy['PolicyArn']) except ClientError as e: module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) except ParamValidationError as e: module.fail_json(msg=e.message, exception=traceback.format_exc()) # Remove any users in the group otherwise deletion fails current_group_members_list = [] try: current_group_members = get_group(connection, module, params['GroupName'])['Users'] except ClientError as e: module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) for member in current_group_members: current_group_members_list.append(member['UserName']) for user in current_group_members_list: try: connection.remove_user_from_group(GroupName=params['GroupName'], UserName=user) except ClientError as e: module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) except ParamValidationError as e: module.fail_json(msg=e.message, exception=traceback.format_exc()) try: connection.delete_group(**params) except ClientError as e: module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) except ParamValidationError as e: module.fail_json(msg=e.message, exception=traceback.format_exc()) else: module.exit_json(changed=False) module.exit_json(changed=True)
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_utils.ec2_argument_spec() argument_spec.update( dict( state=dict(type='str', default='present', choices=['present', 'absent']), filters=dict(type='dict', default={}), vpn_gateway_id=dict(type='str'), tags=dict(default={}, type='dict'), connection_type=dict(default='ipsec.1', type='str'), static_only=dict(default=False, type='bool'), customer_gateway_id=dict(type='str'), vpn_connection_id=dict(type='str'), purge_tags=dict(type='bool', default=False), routes=dict(type='list', default=[]), purge_routes=dict(type='bool', default=False), ) ) module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) if not ec2_utils.HAS_BOTO3: module.fail_json(msg='boto3 required for this module') # Retrieve any AWS settings from the environment. region, ec2_url, aws_connect_kwargs = ec2_utils.get_aws_connection_info(module, boto3=True) if not region: module.fail_json(msg="Either region or AWS_REGION or EC2_REGION environment variable or boto config aws_region or ec2_region must be set.") connection = ec2_utils.boto3_conn(module, conn_type='client', resource='ec2', region=region, endpoint=ec2_url, **aws_connect_kwargs) state = module.params.get('state') parameters = dict(module.params) try: if state == 'present': changed, response = ensure_present(connection, parameters, module.check_mode) elif state == 'absent': changed, response = ensure_absent(connection, parameters, module.check_mode) except VPNConnectionException as e: if e.response and e.error_traceback: module.fail_json(msg=e.msg, exception=e.error_traceback, **ec2_utils.camel_dict_to_snake_dict(e.response)) elif e.error_traceback: module.fail_json(msg=e.msg, exception=e.error_traceback) else: module.fail_json(msg=e.msg) facts_result = dict(changed=changed, **ec2_utils.camel_dict_to_snake_dict(response)) module.exit_json(**facts_result)
def list_mfa_devices(connection, module): user_name = module.params.get('user_name') changed = False args = {} if user_name is not None: args['UserName'] = user_name try: response = connection.list_mfa_devices(**args) except ClientError as e: module.fail_json(msg=e.message, **camel_dict_to_snake_dict(e.response)) module.exit_json(changed=changed, **camel_dict_to_snake_dict(response))
def get_bucket_list(module, connection): """ Return result of list_buckets json encoded :param module: :param connection: :return: """ try: buckets = camel_dict_to_snake_dict(connection.list_buckets())['buckets'] except botocore.exceptions.ClientError as e: module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) return buckets
def ensure_ipv6_cidr_block(conn, module, subnet, ipv6_cidr, check_mode, start_time): wait = module.params['wait'] changed = False if subnet['ipv6_association_id'] and not ipv6_cidr: if not check_mode: disassociate_ipv6_cidr(conn, module, subnet, start_time) changed = True if ipv6_cidr: filters = ansible_dict_to_boto3_filter_list({'ipv6-cidr-block-association.ipv6-cidr-block': ipv6_cidr, 'vpc-id': subnet['vpc_id']}) try: check_subnets = get_subnet_info(describe_subnets_with_backoff(conn, Filters=filters)) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't get subnet info") if check_subnets and check_subnets[0]['ipv6_cidr_block']: module.fail_json(msg="The IPv6 CIDR '{0}' conflicts with another subnet".format(ipv6_cidr)) if subnet['ipv6_association_id']: if not check_mode: disassociate_ipv6_cidr(conn, module, subnet, start_time) changed = True try: if not check_mode: associate_resp = conn.associate_subnet_cidr_block(SubnetId=subnet['id'], Ipv6CidrBlock=ipv6_cidr) changed = True except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't associate ipv6 cidr {0} to {1}".format(ipv6_cidr, subnet['id'])) else: if not check_mode and wait: filters = ansible_dict_to_boto3_filter_list( {'ipv6-cidr-block-association.state': ['associated'], 'vpc-id': subnet['vpc_id']} ) handle_waiter(conn, module, 'subnet_exists', {'SubnetIds': [subnet['id']], 'Filters': filters}, start_time) if associate_resp.get('Ipv6CidrBlockAssociation', {}).get('AssociationId'): subnet['ipv6_association_id'] = associate_resp['Ipv6CidrBlockAssociation']['AssociationId'] subnet['ipv6_cidr_block'] = associate_resp['Ipv6CidrBlockAssociation']['Ipv6CidrBlock'] if subnet['ipv6_cidr_block_association_set']: subnet['ipv6_cidr_block_association_set'][0] = camel_dict_to_snake_dict(associate_resp['Ipv6CidrBlockAssociation']) else: subnet['ipv6_cidr_block_association_set'].append(camel_dict_to_snake_dict(associate_resp['Ipv6CidrBlockAssociation'])) return changed
def main(): argument_spec = ec2_argument_spec() argument_spec.update(dict( state=dict(required=True, choices=['present', 'absent']), name=dict(), location=dict(), bandwidth=dict(choices=['1Gbps', '10Gbps']), link_aggregation_group=dict(), connection_id=dict(), forced_update=dict(type='bool', default=False) )) module = AnsibleAWSModule( argument_spec=argument_spec, required_one_of=[('connection_id', 'name')], required_if=[('state', 'present', ('location', 'bandwidth'))] ) connection = module.client('directconnect') state = module.params.get('state') try: connection_id = connection_exists( connection, connection_id=module.params.get('connection_id'), connection_name=module.params.get('name') ) if not connection_id and module.params.get('connection_id'): module.fail_json(msg="The Direct Connect connection {0} does not exist.".format(module.params.get('connection_id'))) if state == 'present': changed, connection_id = ensure_present(connection, connection_id=connection_id, connection_name=module.params.get('name'), location=module.params.get('location'), bandwidth=module.params.get('bandwidth'), lag_id=module.params.get('link_aggregation_group'), forced_update=module.params.get('forced_update')) response = connection_status(connection, connection_id) elif state == 'absent': changed = ensure_absent(connection, connection_id) response = {} except DirectConnectError as e: if e.last_traceback: module.fail_json(msg=e.msg, exception=e.last_traceback, **camel_dict_to_snake_dict(e.exception.response)) else: module.fail_json(msg=e.msg) module.exit_json(changed=changed, **camel_dict_to_snake_dict(response))
def fail_json_aws(self, exception, msg=None): """call fail_json with processed exception function for converting exceptions thrown by AWS SDK modules, botocore, boto3 and boto, into nice error messages. """ last_traceback = traceback.format_exc() # to_native is trusted to handle exceptions that str() could # convert to text. try: except_msg = to_native(exception.message) except AttributeError: except_msg = to_native(exception) if msg is not None: message = '{0}: {1}'.format(msg, except_msg) else: message = except_msg try: response = exception.response except AttributeError: response = None if response is None: self._module.fail_json(msg=message, exception=last_traceback) else: self._module.fail_json(msg=message, exception=last_traceback, **camel_dict_to_snake_dict(response))
def policy_details(client, module): """ Returns policy attached to a lambda function. :param client: AWS API client reference (boto3) :param module: Ansible module reference :return dict: """ if module.params.get('max_items') or module.params.get('next_marker'): module.fail_json(msg='Cannot specify max_items nor next_marker for query=policy.') lambda_facts = dict() function_name = module.params.get('function_name') if function_name: try: # get_policy returns a JSON string so must convert to dict before reassigning to its key lambda_facts.update(policy=json.loads(client.get_policy(FunctionName=function_name)['Policy'])) except ClientError as e: if e.response['Error']['Code'] == 'ResourceNotFoundException': lambda_facts.update(policy={}) else: module.fail_json_aws(e, msg="Trying to get {0} policy".format(function_name)) else: module.fail_json(msg='Parameter function_name required for query=policy.') return {function_name: camel_dict_to_snake_dict(lambda_facts)}
def main(): argument_spec = ec2_argument_spec() argument_spec.update(dict(state=dict(default='present', choices=['present', 'absent']), name=dict(), amazon_asn=dict(), virtual_gateway_id=dict(), direct_connect_gateway_id=dict(), wait_timeout=dict(type='int', default=320))) required_if = [('state', 'present', ['name', 'amazon_asn']), ('state', 'absent', ['direct_connect_gateway_id'])] module = AnsibleModule(argument_spec=argument_spec, required_if=required_if) if not HAS_BOTO3: module.fail_json(msg='boto3 is required for this module') state = module.params.get('state') region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module, boto3=True) client = boto3_conn(module, conn_type='client', resource='directconnect', region=region, endpoint=ec2_url, **aws_connect_kwargs) if state == 'present': (changed, results) = ensure_present(client, module) elif state == 'absent': changed = ensure_absent(client, module) results = {} module.exit_json(changed=changed, **camel_dict_to_snake_dict(results))
def version_details(client, module): """ Returns all lambda function versions. :param client: AWS API client reference (boto3) :param module: Ansible module reference :return dict: """ lambda_facts = dict() function_name = module.params.get('function_name') if function_name: params = dict() if module.params.get('max_items'): params['MaxItems'] = module.params.get('max_items') if module.params.get('next_marker'): params['Marker'] = module.params.get('next_marker') try: lambda_facts.update(versions=client.list_versions_by_function(FunctionName=function_name, **params)['Versions']) except ClientError as e: if e.response['Error']['Code'] == 'ResourceNotFoundException': lambda_facts.update(versions=[]) else: module.fail_json_aws(e, msg="Trying to get {0} versions".format(function_name)) else: module.fail_json(msg='Parameter function_name required for query=versions.') return {function_name: camel_dict_to_snake_dict(lambda_facts)}
def main(): argument_spec = dict( state=dict(type='str', default='present', choices=['present', 'absent']), filters=dict(type='dict', default={}), vpn_gateway_id=dict(type='str'), tags=dict(default={}, type='dict'), connection_type=dict(default='ipsec.1', type='str'), tunnel_options=dict(type='list', default=[]), static_only=dict(default=False, type='bool'), customer_gateway_id=dict(type='str'), vpn_connection_id=dict(type='str'), purge_tags=dict(type='bool', default=False), routes=dict(type='list', default=[]), purge_routes=dict(type='bool', default=False), ) module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True) connection = module.client('ec2') state = module.params.get('state') parameters = dict(module.params) try: if state == 'present': changed, response = ensure_present(connection, parameters, module.check_mode) elif state == 'absent': changed, response = ensure_absent(connection, parameters, module.check_mode) except VPNConnectionException as e: if e.exception: module.fail_json_aws(e.exception, msg=e.msg) else: module.fail_json(msg=e.msg) module.exit_json(changed=changed, **camel_dict_to_snake_dict(response))
def alias_details(client, module): """ Returns list of aliases for a specified function. :param client: AWS API client reference (boto3) :param module: Ansible module reference :return dict: """ lambda_facts = dict() function_name = module.params.get('function_name') if function_name: params = dict() if module.params.get('max_items'): params['MaxItems'] = module.params.get('max_items') if module.params.get('next_marker'): params['Marker'] = module.params.get('next_marker') try: lambda_facts.update(aliases=client.list_aliases(FunctionName=function_name, **params)['Aliases']) except ClientError as e: if e.response['Error']['Code'] == 'ResourceNotFoundException': lambda_facts.update(aliases=[]) else: module.fail_json_aws(e, msg="Trying to get aliases") else: module.fail_json(msg='Parameter function_name required for query=aliases.') return {function_name: camel_dict_to_snake_dict(lambda_facts)}
def list_web_acls(client, module): try: return list_web_acls_with_backoff(client) except botocore.exceptions.ClientError as e: module.fail_json(msg="Couldn't obtain web acls", exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
def describe_iam_roles(module, client): name = module.params['name'] path_prefix = module.params['path_prefix'] if name: try: roles = [client.get_role(RoleName=name)['Role']] except botocore.exceptions.ClientError as e: if e.response['Error']['Code'] == 'NoSuchEntity': return [] else: module.fail_json_aws(e, msg="Couldn't get IAM role %s" % name) except botocore.exceptions.BotoCoreError as e: module.fail_json_aws(e, msg="Couldn't get IAM role %s" % name) else: params = dict() if path_prefix: if not path_prefix.startswith('/'): path_prefix = '/' + path_prefix if not path_prefix.endswith('/'): path_prefix = path_prefix + '/' params['PathPrefix'] = path_prefix try: roles = list_iam_roles_with_backoff(client, **params)['Roles'] except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't list IAM roles") return [camel_dict_to_snake_dict(describe_iam_role(module, client, role)) for role in roles]
def fail_json_aws(module, exception, msg=None): """call fail_json with processed exception function for converting exceptions thrown by AWS SDK modules, botocore, boto3 and boto, into nice error messages. """ last_traceback = traceback.format_exc() try: except_msg = exception.message except AttributeError: except_msg = str(exception) if msg is not None: message = '{}: {}'.format(msg, except_msg) else: message = except_msg try: response = exception.response except AttributeError: response = None if response is None: module.fail_json(msg=message, traceback=last_traceback) else: module.fail_json(msg=message, traceback=last_traceback, **camel_dict_to_snake_dict(response))
def create(self): params = dict() check_mode = self.params['check_mode'] description = self.params['description'] name = self.params['name'] untagged_interfaces = self.params['untagged_interfaces'] tagged_interfaces = self.params['tagged_interfaces'] partition = self.params['partition'] tag = self.params['tag'] if tag is not None: params['tag'] = tag if untagged_interfaces is not None or tagged_interfaces is not None: tmp = [] ifcs = self.api.tm.net.interfaces.get_collection() ifcs = [str(x.name) for x in ifcs] if len(ifcs) is 0: raise F5ModuleError( 'No interfaces were found' ) pinterfaces = [] if untagged_interfaces: interfaces = untagged_interfaces elif tagged_interfaces: interfaces = tagged_interfaces for ifc in interfaces: ifc = str(ifc) if ifc in ifcs: pinterfaces.append(ifc) if tagged_interfaces: tmp = [dict(name=x, tagged=True) for x in pinterfaces] elif untagged_interfaces: tmp = [dict(name=x, untagged=True) for x in pinterfaces] if tmp: params['interfaces'] = tmp if description is not None: params['description'] = self.params['description'] params['name'] = name params['partition'] = partition self.cparams = camel_dict_to_snake_dict(params) if check_mode: return True d = self.api.tm.net.vlans.vlan d.create(**params) if self.exists(): return True else: raise F5ModuleError("Failed to create the VLAN")
def format_facts(self, server, collection_type=None): result = dict() server_dict = server.to_dict() result.update(self.format_string_facts(server_dict)) result.update(self.format_address_facts(server)) result.update(self.format_virtual_server_facts(server)) return camel_dict_to_snake_dict(result)
def create_or_update_bucket_cors(connection, module): name = module.params.get("name") rules = module.params.get("rules", []) changed = False try: current_camel_rules = connection.get_bucket_cors(Bucket=name)['CORSRules'] except ClientError: current_camel_rules = [] new_camel_rules = snake_dict_to_camel_dict(rules, capitalize_first=True) # compare_policies() takes two dicts and makes them hashable for comparison if compare_policies(new_camel_rules, current_camel_rules): changed = True if changed: try: cors = connection.put_bucket_cors(Bucket=name, CORSConfiguration={'CORSRules': new_camel_rules}) except ClientError as e: module.fail_json( msg="Unable to update CORS for bucket {0}: {1}".format(name, to_native(e)), exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response) ) except BotoCoreError as e: module.fail_json( msg=to_native(e), exception=traceback.format_exc() ) module.exit_json(changed=changed, name=name, rules=rules)
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 _snakify(self, dict): """Converts cammel case to snake case""" return camel_dict_to_snake_dict(dict)
def deregister_target(connection, module): """ Deregisters a target to a target group :param module: ansible module object :param connection: boto3 connection :return: """ deregister_unused = module.params.get("deregister_unused") target_group_arn = module.params.get("target_group_arn") target_id = module.params.get("target_id") target_port = module.params.get("target_port") target_status = module.params.get("target_status") target_status_timeout = module.params.get("target_status_timeout") changed = False if not target_group_arn: target_group_arn = convert_tg_name_to_arn( connection, module, module.params.get("target_group_name")) target = dict(Id=target_id) if target_port: target['Port'] = target_port target_description = describe_targets(connection, module, target_group_arn, [target]) current_target_state = target_description['TargetHealth']['State'] current_target_reason = target_description['TargetHealth'].get('Reason') needs_deregister = False if deregister_unused and current_target_state == 'unused': if current_target_reason != 'Target.NotRegistered': needs_deregister = True elif current_target_state not in ['unused', 'draining']: needs_deregister = True if needs_deregister: try: connection.deregister_targets(TargetGroupArn=target_group_arn, Targets=[target]) changed = True except ClientError as e: module.fail_json(msg="Unable to deregister target {0}: {1}".format( target, to_native(e)), exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) except BotoCoreError as e: module.fail_json(msg="Unable to deregister target {0}: {1}".format( target, to_native(e)), exception=traceback.format_exc()) else: if current_target_reason != 'Target.NotRegistered' and current_target_state != 'draining': module.warn( warning= "Your specified target has an 'unused' state but is still registered to the target group. " + "To force deregistration use the 'deregister_unused' option.") if target_status: target_status_check(connection, module, target_group_arn, target, target_status, target_status_timeout) # Get all targets for the target group target_descriptions = describe_targets(connection, module, target_group_arn, []) module.exit_json(changed=changed, target_health_descriptions=camel_dict_to_snake_dict( target_descriptions), target_group_arn=target_group_arn)
def create_launch_config(connection, module): name = module.params.get('name') vpc_id = module.params.get('vpc_id') try: region, ec2_url, aws_connect_kwargs = get_aws_connection_info( module, boto3=True) ec2_connection = boto3_conn(module, 'client', 'ec2', region, ec2_url, **aws_connect_kwargs) security_groups = get_ec2_security_group_ids_from_names( module.params.get('security_groups'), ec2_connection, vpc_id=vpc_id, boto3=True) except botocore.exceptions.ClientError as e: module.fail_json(msg="Failed to get Security Group IDs", exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) except ValueError as e: module.fail_json(msg="Failed to get Security Group IDs", exception=traceback.format_exc()) user_data = module.params.get('user_data') user_data_path = module.params.get('user_data_path') volumes = module.params['volumes'] instance_monitoring = module.params.get('instance_monitoring') assign_public_ip = module.params.get('assign_public_ip') instance_profile_name = module.params.get('instance_profile_name') ebs_optimized = module.params.get('ebs_optimized') classic_link_vpc_id = module.params.get('classic_link_vpc_id') classic_link_vpc_security_groups = module.params.get( 'classic_link_vpc_security_groups') block_device_mapping = [] convert_list = [ 'image_id', 'instance_type', 'instance_type', 'instance_id', 'placement_tenancy', 'key_name', 'kernel_id', 'ramdisk_id', 'spot_price' ] launch_config = (snake_dict_to_camel_dict( dict((k.capitalize(), str(v)) for k, v in module.params.items() if v is not None and k in convert_list))) if user_data_path: try: with open(user_data_path, 'r') as user_data_file: user_data = user_data_file.read() except IOError as e: module.fail_json(msg="Failed to open file for reading", exception=traceback.format_exc()) if volumes: for volume in volumes: if 'device_name' not in volume: module.fail_json(msg='Device name must be set for volume') # Minimum volume size is 1GB. We'll use volume size explicitly set to 0 to be a signal not to create this volume if 'volume_size' not in volume or int(volume['volume_size']) > 0: block_device_mapping.append( create_block_device_meta(module, volume)) try: launch_configs = connection.describe_launch_configurations( LaunchConfigurationNames=[name]).get('LaunchConfigurations') except botocore.exceptions.ClientError as e: module.fail_json(msg="Failed to describe launch configuration by name", exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) changed = False result = {} launch_config['LaunchConfigurationName'] = name if security_groups is not None: launch_config['SecurityGroups'] = security_groups if classic_link_vpc_id is not None: launch_config['ClassicLinkVPCId'] = classic_link_vpc_id if instance_monitoring is not None: launch_config['InstanceMonitoring'] = {'Enabled': instance_monitoring} if classic_link_vpc_security_groups is not None: launch_config[ 'ClassicLinkVPCSecurityGroups'] = classic_link_vpc_security_groups if block_device_mapping: launch_config['BlockDeviceMappings'] = block_device_mapping if instance_profile_name is not None: launch_config['IamInstanceProfile'] = instance_profile_name if assign_public_ip is not None: launch_config['AssociatePublicIpAddress'] = assign_public_ip if user_data is not None: launch_config['UserData'] = user_data if ebs_optimized is not None: launch_config['EbsOptimized'] = ebs_optimized if len(launch_configs) == 0: try: connection.create_launch_configuration(**launch_config) launch_configs = connection.describe_launch_configurations( LaunchConfigurationNames=[name]).get('LaunchConfigurations') changed = True if launch_configs: launch_config = launch_configs[0] except botocore.exceptions.ClientError as e: module.fail_json(msg="Failed to create launch configuration", exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) result = (dict((k, v) for k, v in launch_config.items() if k not in [ 'Connection', 'CreatedTime', 'InstanceMonitoring', 'BlockDeviceMappings' ])) result['CreatedTime'] = to_text(launch_config.get('CreatedTime')) try: result['InstanceMonitoring'] = module.boolean( launch_config.get('InstanceMonitoring').get('Enabled')) except AttributeError: result['InstanceMonitoring'] = False result['BlockDeviceMappings'] = [] for block_device_mapping in launch_config.get('BlockDeviceMappings', []): result['BlockDeviceMappings'].append( dict(device_name=block_device_mapping.get('DeviceName'), virtual_name=block_device_mapping.get('VirtualName'))) if block_device_mapping.get('Ebs') is not None: result['BlockDeviceMappings'][-1]['ebs'] = dict( snapshot_id=block_device_mapping.get('Ebs').get('SnapshotId'), volume_size=block_device_mapping.get('Ebs').get('VolumeSize')) if user_data_path: result[ 'UserData'] = "hidden" # Otherwise, we dump binary to the user's terminal return_object = { 'Name': result.get('LaunchConfigurationName'), 'CreatedTime': result.get('CreatedTime'), 'ImageId': result.get('ImageId'), 'Arn': result.get('LaunchConfigurationARN'), 'SecurityGroups': result.get('SecurityGroups'), 'InstanceType': result.get('InstanceType'), 'Result': result } module.exit_json(changed=changed, **camel_dict_to_snake_dict(return_object))
def main(): argument_spec = ec2_argument_spec() argument_spec.update( dict( bgp_asn=dict(required=False, type='int'), ip_address=dict(required=True), name=dict(required=True), state=dict(default='present', choices=['present', 'absent']), )) module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True, required_if=[('state', 'present', ['bgp_arn'])]) if not HAS_BOTOCORE: module.fail_json(msg='botocore is required.') if not HAS_BOTO3: module.fail_json(msg='boto3 is required.') gw_mgr = Ec2CustomerGatewayManager(module) name = module.params.get('name') existing = gw_mgr.describe_gateways(module.params['ip_address']) # describe_gateways returns a key of CustomerGateways where as create_gateway returns a # key of CustomerGateway. For consistency, change it here existing['CustomerGateway'] = existing['CustomerGateways'] results = dict(changed=False) if module.params['state'] == 'present': if existing['CustomerGateway']: results['gateway'] = existing if existing['CustomerGateway'][0]['Tags']: tag_array = existing['CustomerGateway'][0]['Tags'] for key, value in enumerate(tag_array): if value['Key'] == 'Name': current_name = value['Value'] if current_name != name: results['name'] = gw_mgr.tag_cgw_name( results['gateway']['CustomerGateway'][0] ['CustomerGatewayId'], module.params['name'], ) results['changed'] = True else: if not module.check_mode: results['gateway'] = gw_mgr.ensure_cgw_present( module.params['bgp_asn'], module.params['ip_address'], ) results['name'] = gw_mgr.tag_cgw_name( results['gateway']['CustomerGateway']['CustomerGatewayId'], module.params['name'], ) results['changed'] = True elif module.params['state'] == 'absent': if existing['CustomerGateway']: results['gateway'] = existing if not module.check_mode: results['gateway'] = gw_mgr.ensure_cgw_absent( existing['CustomerGateway'][0]['CustomerGatewayId']) results['changed'] = True pretty_results = camel_dict_to_snake_dict(results) module.exit_json(**pretty_results)
def create_dirkey(module, s3, bucket, obj, encrypt): if module.check_mode: module.exit_json(msg="PUT operation skipped - running in check mode", changed=True) try: params = {'Bucket': bucket, 'Key': obj, 'Body': b''} if encrypt: params['ServerSideEncryption'] = 'AES256' s3.put_object(**params) for acl in module.params.get('permission'): s3.put_object_acl(ACL=acl, Bucket=bucket, Key=obj) module.exit_json(msg="Virtual directory %s created in bucket %s" % (obj, bucket), changed=True) except botocore.exceptions.ClientError as e: module.fail_json(msg="Failed while creating object %s." % obj, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
def upload_s3file(module, s3, bucket, obj, src, expiry, metadata, encrypt, headers): if module.check_mode: module.exit_json(msg="PUT operation skipped - running in check mode", changed=True) try: extra = {} if encrypt: extra['ServerSideEncryption'] = 'AES256' if metadata: extra['Metadata'] = {} # determine object metadata and extra arguments for option in metadata: extra_args_option = option_in_extra_args(option) if extra_args_option is not None: extra[extra_args_option] = metadata[option] else: extra['Metadata'][option] = metadata[option] if 'ContentType' not in extra: content_type = mimetypes.guess_type(src)[0] if content_type is None: # s3 default content type content_type = 'binary/octet-stream' extra['ContentType'] = content_type s3.upload_file(Filename=src, Bucket=bucket, Key=obj, ExtraArgs=extra) for acl in module.params.get('permission'): s3.put_object_acl(ACL=acl, Bucket=bucket, Key=obj) url = s3.generate_presigned_url(ClientMethod='put_object', Params={'Bucket': bucket, 'Key': obj}, ExpiresIn=expiry) module.exit_json(msg="PUT operation complete", url=url, changed=True) except botocore.exceptions.ClientError as e: module.fail_json(msg="Unable to complete PUT operation.", exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
def download_s3file(module, s3, bucket, obj, dest, retries, version=None): if module.check_mode: module.exit_json(msg="GET operation skipped - running in check mode", changed=True) # retries is the number of loops; range/xrange needs to be one # more to get that count of loops. try: if version: key = s3.get_object(Bucket=bucket, Key=obj, VersionId=version) else: key = s3.get_object(Bucket=bucket, Key=obj) except botocore.exceptions.ClientError as e: if e.response['Error']['Code'] != "404": module.fail_json(msg="Could not find the key %s." % obj, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) for x in range(0, retries + 1): try: s3.download_file(bucket, obj, dest) module.exit_json(msg="GET operation complete", changed=True) except botocore.exceptions.ClientError as e: # actually fail on last pass through the loop. if x >= retries: module.fail_json(msg="Failed while downloading %s." % obj, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) # otherwise, try again, this may be a transient timeout. except SSLError as e: # will ClientError catch SSLError? # actually fail on last pass through the loop. if x >= retries: module.fail_json(msg="s3 download failed: %s." % e, exception=traceback.format_exc())
def main(): argument_spec = ec2_argument_spec() argument_spec.update( dict( mode=dict(choices=['push'], default='push'), file_change_strategy=dict( choices=['force', 'date_size', 'checksum'], default='date_size'), bucket=dict(required=True), key_prefix=dict(required=False, default=''), file_root=dict(required=True, type='path'), permission=dict(required=False, choices=[ 'private', 'public-read', 'public-read-write', 'authenticated-read', 'aws-exec-read', 'bucket-owner-read', 'bucket-owner-full-control' ]), retries=dict(required=False), mime_map=dict(required=False, type='dict'), exclude=dict(required=False, default=".*"), include=dict(required=False, default="*"), # future options: cache_control (string or map, perhaps), encoding, metadata, storage_class, retries )) module = AnsibleModule(argument_spec=argument_spec, ) if not HAS_BOTO3: module.fail_json(msg='boto3 required for this module') result = {} mode = module.params['mode'] region, ec2_url, aws_connect_kwargs = ansible.module_utils.ec2.get_aws_connection_info( module, boto3=True) if not region: module.fail_json(msg="Region must be specified") s3 = ansible.module_utils.ec2.boto3_conn(module, conn_type='client', resource='s3', region=region, endpoint=ec2_url, **aws_connect_kwargs) if mode == 'push': try: result['filelist_initial'] = gather_files( module.params['file_root'], exclude=module.params['exclude'], include=module.params['include']) result['filelist_typed'] = determine_mimetypes( result['filelist_initial'], module.params.get('mime_map')) result['filelist_s3'] = calculate_s3_path( result['filelist_typed'], module.params['key_prefix']) result['filelist_local_etag'] = calculate_local_etag( result['filelist_s3']) result['filelist_actionable'] = filter_list( s3, module.params['bucket'], result['filelist_local_etag'], module.params['file_change_strategy']) result['uploads'] = upload_files(s3, module.params['bucket'], result['filelist_actionable'], module.params) # mark changed if we actually upload something. if result.get('uploads') and len(result.get('uploads')): result['changed'] = True # result.update(filelist=actionable_filelist) except botocore.exceptions.ClientError as err: error_msg = boto_exception(err) module.fail_json(msg=error_msg, exception=traceback.format_exc(), **camel_dict_to_snake_dict(err.response)) module.exit_json(**result)
def get_download_url(module, s3, bucket, obj, expiry, changed=True): try: url = s3.generate_presigned_url(ClientMethod='get_object', Params={'Bucket': bucket, 'Key': obj}, ExpiresIn=expiry) module.exit_json(msg="Download url:", url=url, expiry=expiry, changed=changed) except botocore.exceptions.ClientError as e: module.fail_json(msg="Failed while getting download url.", exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
def main(): argument_spec = ec2_argument_spec() argument_spec.update( dict( api_id=dict(type='str', required=False), state=dict(type='str', default='present', choices=['present', 'absent']), swagger_file=dict(type='path', default=None, aliases=['src', 'api_file']), swagger_dict=dict(type='json', default=None), swagger_text=dict(type='str', default=None), stage=dict(type='str', default=None), deploy_desc=dict(type='str', default="Automatic deployment by Ansible."), ) ) mutually_exclusive = [['swagger_file', 'swagger_dict', 'swagger_text']] # noqa: F841 module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False, mutually_exclusive=mutually_exclusive) api_id = module.params.get('api_id') state = module.params.get('state') # noqa: F841 swagger_file = module.params.get('swagger_file') swagger_dict = module.params.get('swagger_dict') swagger_text = module.params.get('swagger_text') stage = module.params.get('stage') deploy_desc = module.params.get('deploy_desc') # check_mode = module.check_mode changed = False if not HAS_BOTO3: module.fail_json(msg='Python module "boto3" is missing, please install boto3') if not HAS_BOTOCORE: module.fail_json(msg='Python module "botocore" is missing, please install it') region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module, boto3=True) try: client = boto3_conn(module, conn_type='client', resource='apigateway', region=region, endpoint=ec2_url, **aws_connect_kwargs) except botocore.exceptions.NoRegionError: module.fail_json(msg="Region must be specified as a parameter, in " "AWS_DEFAULT_REGION environment variable or in boto configuration file") except (botocore.exceptions.ValidationError, botocore.exceptions.ClientError) as e: fail_json_aws(module, e, msg="connecting to AWS") changed = True # for now it will stay that way until we can sometimes avoid change conf_res = None dep_res = None del_res = None if state == "present": if api_id is None: api_id = create_empty_api(module, client) api_data = get_api_definitions(module, swagger_file=swagger_file, swagger_dict=swagger_dict, swagger_text=swagger_text) conf_res, dep_res = ensure_api_in_correct_state(module, client, api_id=api_id, api_data=api_data, stage=stage, deploy_desc=deploy_desc) if state == "absent": del_res = delete_rest_api(module, client, api_id) exit_args = {"changed": changed, "api_id": api_id} if conf_res is not None: exit_args['configure_response'] = camel_dict_to_snake_dict(conf_res) if dep_res is not None: exit_args['deploy_response'] = camel_dict_to_snake_dict(dep_res) if del_res is not None: exit_args['delete_response'] = camel_dict_to_snake_dict(del_res) module.exit_json(**exit_args)
def main(): argument_spec = ec2_argument_spec() argument_spec.update( dict(log_group_name=dict(required=True, type='str'), state=dict(choices=['present', 'absent'], default='present'), kms_key_id=dict(required=False, type='str'), tags=dict(required=False, type='dict'), retention=dict(required=False, type='int'), overwrite=dict(required=False, type='bool', default=False))) module = AnsibleModule(argument_spec=argument_spec) if not HAS_BOTO3: module.fail_json(msg='boto3 is required.') region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module, boto3=True) logs = boto3_conn(module, conn_type='client', resource='logs', region=region, endpoint=ec2_url, **aws_connect_kwargs) state = module.params.get('state') changed = False # Determine if the log group exists desc_log_group = describe_log_group( client=logs, log_group_name=module.params['log_group_name'], module=module) found_log_group = {} for i in desc_log_group.get('logGroups', []): if module.params['log_group_name'] == i['logGroupName']: found_log_group = i break if state == 'present': if found_log_group and module.params['overwrite'] is True: changed = True delete_log_group(client=logs, log_group_name=module.params['log_group_name'], module=module) found_log_group = create_log_group( client=logs, log_group_name=module.params['log_group_name'], kms_key_id=module.params['kms_key_id'], tags=module.params['tags'], retention=module.params['retention'], module=module) elif not found_log_group: changed = True found_log_group = create_log_group( client=logs, log_group_name=module.params['log_group_name'], kms_key_id=module.params['kms_key_id'], tags=module.params['tags'], retention=module.params['retention'], module=module) elif found_log_group: if module.params['retention'] != found_log_group['retentionInDays']: changed = True input_retention_policy( client=logs, log_group_name=module.params['log_group_name'], retention=module.params['retention'], module=module) found_log_group['retentionInDays'] = module.params['retention'] module.exit_json(changed=changed, **camel_dict_to_snake_dict(found_log_group)) elif state == 'absent': if found_log_group: changed = True delete_log_group(client=logs, log_group_name=module.params['log_group_name'], module=module) module.exit_json(changed=changed)
def main(): argument_spec = dict( state=dict(default='present', choices=['present', 'absent', 'enabled', 'disabled']), name=dict(default='default'), enable_logging=dict(default=True, type='bool'), s3_bucket_name=dict(), s3_key_prefix=dict(), sns_topic_name=dict(), is_multi_region_trail=dict(default=False, type='bool'), enable_log_file_validation=dict( type='bool', aliases=['log_file_validation_enabled']), include_global_events=dict(default=True, type='bool', aliases=['include_global_service_events']), cloudwatch_logs_role_arn=dict(), cloudwatch_logs_log_group_arn=dict(), kms_key_id=dict(), tags=dict(default={}, type='dict'), ) required_if = [('state', 'present', ['s3_bucket_name']), ('state', 'enabled', ['s3_bucket_name'])] required_together = [('cloudwatch_logs_role_arn', 'cloudwatch_logs_log_group_arn')] module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True, required_together=required_together, required_if=required_if) # collect parameters if module.params['state'] in ('present', 'enabled'): state = 'present' elif module.params['state'] in ('absent', 'disabled'): state = 'absent' tags = module.params['tags'] enable_logging = module.params['enable_logging'] ct_params = dict( Name=module.params['name'], S3BucketName=module.params['s3_bucket_name'], IncludeGlobalServiceEvents=module.params['include_global_events'], IsMultiRegionTrail=module.params['is_multi_region_trail'], ) if module.params['s3_key_prefix']: ct_params['S3KeyPrefix'] = module.params['s3_key_prefix'].rstrip('/') if module.params['sns_topic_name']: ct_params['SnsTopicName'] = module.params['sns_topic_name'] if module.params['cloudwatch_logs_role_arn']: ct_params['CloudWatchLogsRoleArn'] = module.params[ 'cloudwatch_logs_role_arn'] if module.params['cloudwatch_logs_log_group_arn']: ct_params['CloudWatchLogsLogGroupArn'] = module.params[ 'cloudwatch_logs_log_group_arn'] if module.params['enable_log_file_validation'] is not None: ct_params['EnableLogFileValidation'] = module.params[ 'enable_log_file_validation'] if module.params['kms_key_id']: ct_params['KmsKeyId'] = module.params['kms_key_id'] client = module.client('cloudtrail') region = module.region results = dict(changed=False, exists=False) # Get existing trail facts trail = get_trail_facts(module, client, ct_params['Name']) # If the trail exists set the result exists variable if trail is not None: results['exists'] = True if state == 'absent' and results['exists']: # If Trail exists go ahead and delete results['changed'] = True results['exists'] = False results['trail'] = dict() if not module.check_mode: delete_trail(module, client, trail['TrailARN']) elif state == 'present' and results['exists']: # If Trail exists see if we need to update it do_update = False for key in ct_params: tkey = str(key) # boto3 has inconsistent parameter naming so we handle it here if key == 'EnableLogFileValidation': tkey = 'LogFileValidationEnabled' # We need to make an empty string equal None if ct_params.get(key) == '': val = None else: val = ct_params.get(key) if val != trail.get(tkey): do_update = True results['changed'] = True # If we are in check mode copy the changed values to the trail facts in result output to show what would change. if module.check_mode: trail.update({tkey: ct_params.get(key)}) if not module.check_mode and do_update: update_trail(module, client, ct_params) trail = get_trail_facts(module, client, ct_params['Name']) # Check if we need to start/stop logging if enable_logging and not trail['IsLogging']: results['changed'] = True trail['IsLogging'] = True if not module.check_mode: set_logging(module, client, name=ct_params['Name'], action='start') if not enable_logging and trail['IsLogging']: results['changed'] = True trail['IsLogging'] = False if not module.check_mode: set_logging(module, client, name=ct_params['Name'], action='stop') # Check if we need to update tags on resource tag_dry_run = False if module.check_mode: tag_dry_run = True tags_changed = tag_trail(module, client, tags=tags, trail_arn=trail['TrailARN'], curr_tags=trail['tags'], dry_run=tag_dry_run) if tags_changed: results['changed'] = True trail['tags'] = tags # Populate trail facts in output results['trail'] = camel_dict_to_snake_dict(trail) elif state == 'present' and not results['exists']: # Trail doesn't exist just go create it results['changed'] = True if not module.check_mode: # If we aren't in check_mode then actually create it created_trail = create_trail(module, client, ct_params) # Apply tags tag_trail(module, client, tags=tags, trail_arn=created_trail['TrailARN']) # Get the trail status try: status_resp = client.get_trail_status( Name=created_trail['Name']) except (BotoCoreError, ClientError) as err: module.fail_json_aws(err, msg="Failed to fetch Trail statuc") # Set the logging state for the trail to desired value if enable_logging and not status_resp['IsLogging']: set_logging(module, client, name=ct_params['Name'], action='start') if not enable_logging and status_resp['IsLogging']: set_logging(module, client, name=ct_params['Name'], action='stop') # Get facts for newly created Trail trail = get_trail_facts(module, client, ct_params['Name']) # If we are in check mode create a fake return structure for the newly minted trail if module.check_mode: acct_id = '123456789012' try: sts_client = module.client('sts') acct_id = sts_client.get_caller_identity()['Account'] except (BotoCoreError, ClientError): pass trail = dict() trail.update(ct_params) if 'EnableLogFileValidation' not in ct_params: ct_params['EnableLogFileValidation'] = False trail['EnableLogFileValidation'] = ct_params[ 'EnableLogFileValidation'] trail.pop('EnableLogFileValidation') fake_arn = 'arn:aws:cloudtrail:' + region + ':' + acct_id + ':trail/' + ct_params[ 'Name'] trail['HasCustomEventSelectors'] = False trail['HomeRegion'] = region trail['TrailARN'] = fake_arn trail['IsLogging'] = enable_logging trail['tags'] = tags # Populate trail facts in output results['trail'] = camel_dict_to_snake_dict(trail) module.exit_json(**results)
def delete_key(module, s3, bucket, obj): if module.check_mode: module.exit_json(msg="DELETE operation skipped - running in check mode", changed=True) try: s3.delete_object(Bucket=bucket, Key=obj) module.exit_json(msg="Object deleted from bucket %s." % (bucket), changed=True) except botocore.exceptions.ClientError as e: module.fail_json(msg="Failed while trying to delete %s." % obj, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
def update(self): changed = False params = dict() current = self.read() check_mode = self.params['check_mode'] description = self.params['description'] name = self.params['name'] tag = self.params['tag'] partition = self.params['partition'] tagged_interfaces = self.params['tagged_interfaces'] untagged_interfaces = self.params['untagged_interfaces'] if untagged_interfaces is not None or tagged_interfaces is not None: ifcs = self.api.tm.net.interfaces.get_collection() ifcs = [str(x.name) for x in ifcs] if len(ifcs) is 0: raise F5ModuleError( 'No interfaces were found' ) pinterfaces = [] if untagged_interfaces: interfaces = untagged_interfaces elif tagged_interfaces: interfaces = tagged_interfaces for ifc in interfaces: ifc = str(ifc) if ifc in ifcs: pinterfaces.append(ifc) else: raise F5ModuleError( 'The specified interface "%s" was not found' % (ifc) ) if tagged_interfaces: tmp = [dict(name=x, tagged=True) for x in pinterfaces] if 'tagged_interfaces' in current: if pinterfaces != current['tagged_interfaces']: params['interfaces'] = tmp else: params['interfaces'] = tmp elif untagged_interfaces: tmp = [dict(name=x, untagged=True) for x in pinterfaces] if 'untagged_interfaces' in current: if pinterfaces != current['untagged_interfaces']: params['interfaces'] = tmp else: params['interfaces'] = tmp if description is not None: if 'description' in current: if description != current['description']: params['description'] = description else: params['description'] = description if tag is not None: if 'tag' in current: if tag != current['tag']: params['tag'] = tag else: params['tag'] = tag if params: changed = True params['name'] = name params['partition'] = partition if check_mode: return changed self.cparams = camel_dict_to_snake_dict(params) else: return changed r = self.api.tm.net.vlans.vlan.load( name=name, partition=partition ) r.update(**params) r.refresh() return True
def converge_file_system(self, name, tags, purge_tags, targets, throughput_mode, provisioned_throughput_in_mibps): """ Change attributes (mount targets and tags) of filesystem by name """ result = False fs_id = self.get_file_system_id(name) if tags is not None: tags_need_modify, tags_to_delete = compare_aws_tags( boto3_tag_list_to_ansible_dict( self.get_tags(FileSystemId=fs_id)), tags, purge_tags) if tags_to_delete: try: self.connection.delete_tags(FileSystemId=fs_id, TagKeys=tags_to_delete) except ClientError as e: self.module.fail_json( msg="Unable to delete tags: {0}".format(to_native(e)), exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) except BotoCoreError as e: self.module.fail_json( msg="Unable to delete tags: {0}".format(to_native(e)), exception=traceback.format_exc()) result = True if tags_need_modify: try: self.connection.create_tags( FileSystemId=fs_id, Tags=ansible_dict_to_boto3_tag_list(tags_need_modify)) except ClientError as e: self.module.fail_json( msg="Unable to create tags: {0}".format(to_native(e)), exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) except BotoCoreError as e: self.module.fail_json( msg="Unable to create tags: {0}".format(to_native(e)), exception=traceback.format_exc()) result = True if targets is not None: incomplete_states = [self.STATE_CREATING, self.STATE_DELETING] wait_for( lambda: len( self.get_mount_targets_in_state(fs_id, incomplete_states)), 0) current_targets = _index_by_key( 'SubnetId', self.get_mount_targets(FileSystemId=fs_id)) targets = _index_by_key('SubnetId', targets) targets_to_create, intersection, targets_to_delete = dict_diff( current_targets, targets, True) # To modify mount target it should be deleted and created again changed = [ sid for sid in intersection if not targets_equal( ['SubnetId', 'IpAddress', 'NetworkInterfaceId'], current_targets[sid], targets[sid]) ] targets_to_delete = list(targets_to_delete) + changed targets_to_create = list(targets_to_create) + changed if targets_to_delete: for sid in targets_to_delete: self.connection.delete_mount_target( MountTargetId=current_targets[sid]['MountTargetId']) wait_for( lambda: len( self.get_mount_targets_in_state( fs_id, incomplete_states)), 0) result = True if targets_to_create: for sid in targets_to_create: self.connection.create_mount_target(FileSystemId=fs_id, **targets[sid]) wait_for( lambda: len( self.get_mount_targets_in_state( fs_id, incomplete_states)), 0, self.wait_timeout) result = True # If no security groups were passed into the module, then do not change it. security_groups_to_update = [ sid for sid in intersection if 'SecurityGroups' in targets[sid] and current_targets[sid] ['SecurityGroups'] != targets[sid]['SecurityGroups'] ] if security_groups_to_update: for sid in security_groups_to_update: self.connection.modify_mount_target_security_groups( MountTargetId=current_targets[sid]['MountTargetId'], SecurityGroups=targets[sid].get( 'SecurityGroups', None)) result = True return result
def instance_to_facts(instance): assert 'DBInstanceIdentifier' in instance, "instance argument was not a valid instance" d = camel_dict_to_snake_dict(instance) return d
def main(): """ Module action handler """ argument_spec = ec2_argument_spec() argument_spec.update( dict(encrypt=dict(required=False, type="bool", default=False), state=dict(required=False, type='str', choices=["present", "absent"], default="present"), kms_key_id=dict(required=False, type='str', default=None), purge_tags=dict(default=True, type='bool'), id=dict(required=False, type='str', default=None), name=dict(required=False, type='str', default=None), tags=dict(required=False, type="dict", default={}), targets=dict(required=False, type="list", default=[]), performance_mode=dict(required=False, type='str', choices=["general_purpose", "max_io"], default="general_purpose"), throughput_mode=dict(required=False, type='str', choices=["bursting", "provisioned"], default=None), provisioned_throughput_in_mibps=dict(required=False, type='float'), wait=dict(required=False, type="bool", default=False), wait_timeout=dict(required=False, type="int", default=0))) module = AnsibleModule(argument_spec=argument_spec) 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) connection = EFSConnection(module, region, **aws_connect_params) name = module.params.get('name') fs_id = module.params.get('id') tags = module.params.get('tags') target_translations = { 'ip_address': 'IpAddress', 'security_groups': 'SecurityGroups', 'subnet_id': 'SubnetId' } targets = [ dict((target_translations[key], value) for (key, value) in x.items()) for x in module.params.get('targets') ] performance_mode_translations = { 'general_purpose': 'generalPurpose', 'max_io': 'maxIO' } encrypt = module.params.get('encrypt') kms_key_id = module.params.get('kms_key_id') performance_mode = performance_mode_translations[module.params.get( 'performance_mode')] purge_tags = module.params.get('purge_tags') throughput_mode = module.params.get('throughput_mode') provisioned_throughput_in_mibps = module.params.get( 'provisioned_throughput_in_mibps') state = str(module.params.get('state')).lower() changed = False if state == 'present': if not name: module.fail_json(msg='Name parameter is required for create') changed = connection.create_file_system( name, performance_mode, encrypt, kms_key_id, throughput_mode, provisioned_throughput_in_mibps) if connection.supports_provisioned_mode(): changed = connection.update_file_system( name, throughput_mode, provisioned_throughput_in_mibps) or changed changed = connection.converge_file_system( name=name, tags=tags, purge_tags=purge_tags, targets=targets, throughput_mode=throughput_mode, provisioned_throughput_in_mibps=provisioned_throughput_in_mibps ) or changed result = first_or_default( connection.get_file_systems(CreationToken=name)) elif state == 'absent': if not name and not fs_id: module.fail_json( msg='Either name or id parameter is required for delete') changed = connection.delete_file_system(name, fs_id) result = None if result: result = camel_dict_to_snake_dict(result) module.exit_json(changed=changed, efs=result)
def create_or_update_elb(elb_obj): """Create ELB or modify main attributes. json_exit here""" if elb_obj.elb: # ELB exists so check subnets, security groups and tags match what has been passed # Subnets if not elb_obj.compare_subnets(): elb_obj.modify_subnets() # Security Groups if not elb_obj.compare_security_groups(): elb_obj.modify_security_groups() # Tags - only need to play with tags if tags parameter has been set to something if elb_obj.tags is not None: # Delete necessary tags tags_need_modify, tags_to_delete = compare_aws_tags(boto3_tag_list_to_ansible_dict(elb_obj.elb['tags']), boto3_tag_list_to_ansible_dict(elb_obj.tags), elb_obj.purge_tags) if tags_to_delete: elb_obj.delete_tags(tags_to_delete) # Add/update tags if tags_need_modify: elb_obj.modify_tags() else: # Create load balancer elb_obj.create_elb() # ELB attributes elb_obj.update_elb_attributes() elb_obj.modify_elb_attributes() # Listeners listeners_obj = ELBListeners(elb_obj.connection, elb_obj.module, elb_obj.elb['LoadBalancerArn']) listeners_to_add, listeners_to_modify, listeners_to_delete = listeners_obj.compare_listeners() # Delete listeners for listener_to_delete in listeners_to_delete: listener_obj = ELBListener(elb_obj.connection, elb_obj.module, listener_to_delete, elb_obj.elb['LoadBalancerArn']) listener_obj.delete() listeners_obj.changed = True # Add listeners for listener_to_add in listeners_to_add: listener_obj = ELBListener(elb_obj.connection, elb_obj.module, listener_to_add, elb_obj.elb['LoadBalancerArn']) listener_obj.add() listeners_obj.changed = True # Modify listeners for listener_to_modify in listeners_to_modify: listener_obj = ELBListener(elb_obj.connection, elb_obj.module, listener_to_modify, elb_obj.elb['LoadBalancerArn']) listener_obj.modify() listeners_obj.changed = True # If listeners changed, mark ELB as changed if listeners_obj.changed: elb_obj.changed = True # Rules of each listener for listener in listeners_obj.listeners: if 'Rules' in listener: rules_obj = ELBListenerRules(elb_obj.connection, elb_obj.module, elb_obj.elb['LoadBalancerArn'], listener['Rules'], listener['Port']) rules_to_add, rules_to_modify, rules_to_delete = rules_obj.compare_rules() # Delete rules for rule in rules_to_delete: rule_obj = ELBListenerRule(elb_obj.connection, elb_obj.module, {'RuleArn': rule}, rules_obj.listener_arn) rule_obj.delete() elb_obj.changed = True # Add rules for rule in rules_to_add: rule_obj = ELBListenerRule(elb_obj.connection, elb_obj.module, rule, rules_obj.listener_arn) rule_obj.create() elb_obj.changed = True # Modify rules for rule in rules_to_modify: rule_obj = ELBListenerRule(elb_obj.connection, elb_obj.module, rule, rules_obj.listener_arn) rule_obj.modify() elb_obj.changed = True # Get the ELB again elb_obj.update() # Get the ELB listeners again listeners_obj.update() # Update the ELB attributes elb_obj.update_elb_attributes() # Convert to snake_case and merge in everything we want to return to the user snaked_elb = camel_dict_to_snake_dict(elb_obj.elb) snaked_elb.update(camel_dict_to_snake_dict(elb_obj.elb_attributes)) snaked_elb['listeners'] = [] for listener in listeners_obj.current_listeners: # For each listener, get listener rules listener['rules'] = get_elb_listener_rules(elb_obj.connection, elb_obj.module, listener['ListenerArn']) snaked_elb['listeners'].append(camel_dict_to_snake_dict(listener)) # Change tags to ansible friendly dict snaked_elb['tags'] = boto3_tag_list_to_ansible_dict(snaked_elb['tags']) elb_obj.module.exit_json(changed=elb_obj.changed, **snaked_elb)
def snapshot_to_facts(snapshot): assert 'DBSnapshotIdentifier' in snapshot, "snapshot argument was not a valid snapshot" d = camel_dict_to_snake_dict(snapshot) return d
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 find_asgs(conn, module, name=None, tags=None): """ Args: conn (boto3.AutoScaling.Client): Valid Boto3 ASG client. name (str): Optional name of the ASG you are looking for. tags (dict): Optional dictionary of tags and values to search for. Basic Usage: >>> name = 'public-webapp-production' >>> tags = { 'env': 'production' } >>> conn = boto3.client('autoscaling', region_name='us-west-2') >>> results = find_asgs(name, conn) Returns: List [ { "auto_scaling_group_arn": ( "arn:aws:autoscaling:us-west-2:275977225706:autoScalingGroup:58abc686-9783-4528-b338-3ad6f1cbbbaf:" "autoScalingGroupName/public-webapp-production" ), "auto_scaling_group_name": "public-webapp-production", "availability_zones": ["us-west-2c", "us-west-2b", "us-west-2a"], "created_time": "2016-02-02T23:28:42.481000+00:00", "default_cooldown": 300, "desired_capacity": 2, "enabled_metrics": [], "health_check_grace_period": 300, "health_check_type": "ELB", "instances": [ { "availability_zone": "us-west-2c", "health_status": "Healthy", "instance_id": "i-047a12cb", "launch_configuration_name": "public-webapp-production-1", "lifecycle_state": "InService", "protected_from_scale_in": false }, { "availability_zone": "us-west-2a", "health_status": "Healthy", "instance_id": "i-7a29df2c", "launch_configuration_name": "public-webapp-production-1", "lifecycle_state": "InService", "protected_from_scale_in": false } ], "launch_config_name": "public-webapp-production-1", "launch_configuration_name": "public-webapp-production-1", "load_balancer_names": ["public-webapp-production-lb"], "max_size": 4, "min_size": 2, "new_instances_protected_from_scale_in": false, "placement_group": None, "status": None, "suspended_processes": [], "tags": [ { "key": "Name", "propagate_at_launch": true, "resource_id": "public-webapp-production", "resource_type": "auto-scaling-group", "value": "public-webapp-production" }, { "key": "env", "propagate_at_launch": true, "resource_id": "public-webapp-production", "resource_type": "auto-scaling-group", "value": "production" } ], "target_group_names": [], "target_group_arns": [], "termination_policies": [ "Default" ], "vpc_zone_identifier": [ "subnet-a1b1c1d1", "subnet-a2b2c2d2", "subnet-a3b3c3d3" ] } ] """ try: asgs_paginator = conn.get_paginator('describe_auto_scaling_groups') asgs = asgs_paginator.paginate().build_full_result() except (BotoCoreError, ClientError) as e: module.fail_json_aws(e, msg='Failed to describe AutoScalingGroups') if not asgs: return asgs try: elbv2 = module.client('elbv2') except ClientError as e: # This is nice to have, not essential elbv2 = None matched_asgs = [] if name is not None: # if the user didn't specify a name name_prog = re.compile(r'^' + name) for asg in asgs['AutoScalingGroups']: if name: matched_name = name_prog.search(asg['AutoScalingGroupName']) else: matched_name = True if tags: matched_tags = match_asg_tags(tags, asg) else: matched_tags = True if matched_name and matched_tags: asg = camel_dict_to_snake_dict(asg) # compatibility with ec2_asg module if 'launch_configuration_name' in asg: asg['launch_config_name'] = asg['launch_configuration_name'] # workaround for https://github.com/ansible/ansible/pull/25015 if 'target_group_ar_ns' in asg: asg['target_group_arns'] = asg['target_group_ar_ns'] del (asg['target_group_ar_ns']) if asg.get('target_group_arns'): if elbv2: try: tg_paginator = elbv2.get_paginator( 'describe_target_groups') tg_result = tg_paginator.paginate( TargetGroupArns=asg['target_group_arns'] ).build_full_result() asg['target_group_names'] = [ tg['TargetGroupName'] for tg in tg_result['TargetGroups'] ] except ClientError as e: if e.response['Error'][ 'Code'] == 'TargetGroupNotFound': asg['target_group_names'] = [] else: module.fail_json_aws( e, msg="Failed to describe Target Groups") except BotoCoreError as e: module.fail_json_aws( e, msg="Failed to describe Target Groups") else: asg['target_group_names'] = [] matched_asgs.append(asg) return matched_asgs
def delete_bucket(module, s3, bucket): if module.check_mode: module.exit_json(msg="DELETE operation skipped - running in check mode", changed=True) try: exists = bucket_check(module, s3, bucket) if exists is False: return False # if there are contents then we need to delete them before we can delete the bucket for keys in paginated_list(s3, Bucket=bucket): formatted_keys = [{'Key': key} for key in keys] if formatted_keys: s3.delete_objects(Bucket=bucket, Delete={'Objects': formatted_keys}) s3.delete_bucket(Bucket=bucket) return True except botocore.exceptions.ClientError as e: module.fail_json(msg="Failed while deleting bucket %s.", exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
def lambda_event_stream(module, aws): """ Adds, updates or deletes lambda stream (DynamoDb, Kinesis) event notifications. :param module: :param aws: :return: """ client = aws.client('lambda') facts = dict() changed = False current_state = 'absent' state = module.params['state'] api_params = dict(FunctionName=module.params['lambda_function_arn']) # check if required sub-parameters are present and valid source_params = module.params['source_params'] source_arn = source_params.get('source_arn') if source_arn: api_params.update(EventSourceArn=source_arn) else: module.fail_json( msg= "Source parameter 'source_arn' is required for stream event notification." ) # check if optional sub-parameters are valid, if present batch_size = source_params.get('batch_size') if batch_size: try: source_params['batch_size'] = int(batch_size) except ValueError: module.fail_json( msg= "Source parameter 'batch_size' must be an integer, found: {0}". format(source_params['batch_size'])) # optional boolean value needs special treatment as not present does not imply False source_param_enabled = module.boolean(source_params.get('enabled', 'True')) # check if event mapping exist try: facts = client.list_event_source_mappings( **api_params)['EventSourceMappings'] if facts: current_state = 'present' except ClientError as e: module.fail_json( msg='Error retrieving stream event notification configuration: {0}' .format(e)) if state == 'present': if current_state == 'absent': starting_position = source_params.get('starting_position') if starting_position: api_params.update(StartingPosition=starting_position) else: module.fail_json( msg= "Source parameter 'starting_position' is required for stream event notification." ) if source_arn: api_params.update(Enabled=source_param_enabled) if source_params.get('batch_size'): api_params.update(BatchSize=source_params.get('batch_size')) try: if not module.check_mode: facts = client.create_event_source_mapping(**api_params) changed = True except (ClientError, ParamValidationError, MissingParametersError) as e: module.fail_json( msg='Error creating stream source event mapping: {0}'. format(e)) else: # current_state is 'present' api_params = dict( FunctionName=module.params['lambda_function_arn']) current_mapping = facts[0] api_params.update(UUID=current_mapping['UUID']) mapping_changed = False # check if anything changed if source_params.get('batch_size') and source_params[ 'batch_size'] != current_mapping['BatchSize']: api_params.update(BatchSize=source_params['batch_size']) mapping_changed = True if source_param_enabled is not None: if source_param_enabled: if current_mapping['State'] not in ('Enabled', 'Enabling'): api_params.update(Enabled=True) mapping_changed = True else: if current_mapping['State'] not in ('Disabled', 'Disabling'): api_params.update(Enabled=False) mapping_changed = True if mapping_changed: try: if not module.check_mode: facts = client.update_event_source_mapping( **api_params) changed = True except (ClientError, ParamValidationError, MissingParametersError) as e: module.fail_json( msg='Error updating stream source event mapping: {0}'. format(e)) else: if current_state == 'present': # remove the stream event mapping api_params = dict(UUID=facts[0]['UUID']) try: if not module.check_mode: facts = client.delete_event_source_mapping(**api_params) changed = True except (ClientError, ParamValidationError, MissingParametersError) as e: module.fail_json( msg='Error removing stream source event mapping: {0}'. format(e)) return camel_dict_to_snake_dict(dict(changed=changed, events=facts))
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 main(): argument_spec = ec2_utils.ec2_argument_spec() argument_spec.update( dict( state=dict(type='str', default='present', choices=['present', 'absent']), filters=dict(type='dict', default={}), vpn_gateway_id=dict(type='str'), tags=dict(default={}, type='dict'), connection_type=dict(default='ipsec.1', type='str'), static_only=dict(default=False, type='bool'), customer_gateway_id=dict(type='str'), vpn_connection_id=dict(type='str'), purge_tags=dict(type='bool', default=False), routes=dict(type='list', default=[]), purge_routes=dict(type='bool', default=False), )) module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) if not ec2_utils.HAS_BOTO3: module.fail_json(msg='boto3 required for this module') # Retrieve any AWS settings from the environment. region, ec2_url, aws_connect_kwargs = ec2_utils.get_aws_connection_info( module, boto3=True) if not region: module.fail_json( msg= "Either region or AWS_REGION or EC2_REGION environment variable or boto config aws_region or ec2_region must be set." ) connection = ec2_utils.boto3_conn(module, conn_type='client', resource='ec2', region=region, endpoint=ec2_url, **aws_connect_kwargs) state = module.params.get('state') parameters = dict(module.params) try: if state == 'present': changed, response = ensure_present(connection, parameters, module.check_mode) elif state == 'absent': changed, response = ensure_absent(connection, parameters, module.check_mode) except VPNConnectionException as e: if e.response and e.error_traceback: module.fail_json(msg=e.msg, exception=e.error_traceback, **ec2_utils.camel_dict_to_snake_dict(e.response)) elif e.error_traceback: module.fail_json(msg=e.msg, exception=e.error_traceback) else: module.fail_json(msg=e.msg) facts_result = dict(changed=changed, **ec2_utils.camel_dict_to_snake_dict(response)) module.exit_json(**facts_result)
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 create_or_update_glue_job(connection, module, glue_job): """ Create or update an AWS Glue job :param connection: AWS boto3 glue connection :param module: Ansible module :param glue_job: a dict of AWS Glue job parameters or None :return: """ changed = False params = dict() params['Name'] = module.params.get("name") params['Role'] = module.params.get("role") if module.params.get("allocated_capacity") is not None: params['AllocatedCapacity'] = module.params.get("allocated_capacity") if module.params.get("command_script_location") is not None: params['Command'] = { 'Name': module.params.get("command_name"), 'ScriptLocation': module.params.get("command_script_location") } if module.params.get("connections") is not None: params['Connections'] = { 'Connections': module.params.get("connections") } if module.params.get("default_arguments") is not None: params['DefaultArguments'] = module.params.get("default_arguments") if module.params.get("description") is not None: params['Description'] = module.params.get("description") if module.params.get("max_concurrent_runs") is not None: params['ExecutionProperty'] = { 'MaxConcurrentRuns': module.params.get("max_concurrent_runs") } if module.params.get("max_retries") is not None: params['MaxRetries'] = module.params.get("max_retries") if module.params.get("timeout") is not None: params['Timeout'] = module.params.get("timeout") # If glue_job is not None then check if it needs to be modified, else create it if glue_job: if _compare_glue_job_params(params, glue_job): try: # Update job needs slightly modified params update_params = { 'JobName': params['Name'], 'JobUpdate': copy.deepcopy(params) } del update_params['JobUpdate']['Name'] connection.update_job(**update_params) changed = True except (BotoCoreError, ClientError) as e: module.fail_json_aws(e) else: try: connection.create_job(**params) changed = True except (BotoCoreError, ClientError) as e: module.fail_json_aws(e) # If changed, get the Glue job again if changed: glue_job = _get_glue_job(connection, module, params['Name']) module.exit_json(changed=changed, **camel_dict_to_snake_dict(glue_job))
def main(): argument_spec = ec2_argument_spec() argument_spec.update( dict(bucket=dict(required=True), dest=dict(default=None), encrypt=dict(default=True, type='bool'), expiry=dict(default=600, type='int', aliases=['expiration']), headers=dict(type='dict'), marker=dict(default=""), max_keys=dict(default=1000, type='int'), metadata=dict(type='dict'), mode=dict(choices=[ 'get', 'put', 'delete', 'create', 'geturl', 'getstr', 'delobj', 'list' ], required=True), object=dict(), permission=dict(type='list', default=['private']), version=dict(default=None), overwrite=dict(aliases=['force'], default='always'), prefix=dict(default=""), retries=dict(aliases=['retry'], type='int', default=0), s3_url=dict(aliases=['S3_URL']), rgw=dict(default='no', type='bool'), src=dict(), ignore_nonexistent_bucket=dict(default=False, type='bool')), ) module = AnsibleModule( argument_spec=argument_spec, supports_check_mode=True, ) if module._name == 's3': module.deprecate("The 's3' module is being renamed 'aws_s3'", version=2.7) if not HAS_BOTO3: module.fail_json(msg='boto3 and botocore required for this module') bucket = module.params.get('bucket') encrypt = module.params.get('encrypt') expiry = module.params.get('expiry') dest = module.params.get('dest', '') headers = module.params.get('headers') marker = module.params.get('marker') max_keys = module.params.get('max_keys') metadata = module.params.get('metadata') mode = module.params.get('mode') obj = module.params.get('object') version = module.params.get('version') overwrite = module.params.get('overwrite') prefix = module.params.get('prefix') retries = module.params.get('retries') s3_url = module.params.get('s3_url') rgw = module.params.get('rgw') src = module.params.get('src') ignore_nonexistent_bucket = module.params.get('ignore_nonexistent_bucket') if dest: dest = os.path.expanduser(dest) object_canned_acl = [ "private", "public-read", "public-read-write", "aws-exec-read", "authenticated-read", "bucket-owner-read", "bucket-owner-full-control" ] bucket_canned_acl = [ "private", "public-read", "public-read-write", "authenticated-read" ] if overwrite not in ['always', 'never', 'different']: if module.boolean(overwrite): overwrite = 'always' else: overwrite = 'never' region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module, boto3=True) if region in ('us-east-1', '', None): # default to US Standard region location = 'us-east-1' else: # Boto uses symbolic names for locations but region strings will # actually work fine for everything except us-east-1 (US Standard) location = region if module.params.get('object'): obj = module.params['object'] # If there is a top level object, do nothing - if the object starts with / # remove the leading character to maintain compatibility with Ansible versions < 2.4 if obj.startswith('/'): obj = obj[1:] # Bucket deletion does not require obj. Prevents ambiguity with delobj. if obj and mode == "delete": module.fail_json(msg='Parameter obj cannot be used with mode=delete') # allow eucarc environment variables to be used if ansible vars aren't set if not s3_url and 'S3_URL' in os.environ: s3_url = os.environ['S3_URL'] # rgw requires an explicit url if rgw and not s3_url: module.fail_json(msg='rgw flavour requires s3_url') # Look at s3_url and tweak connection settings # if connecting to RGW, Walrus or fakes3 for key in ['validate_certs', 'security_token', 'profile_name']: aws_connect_kwargs.pop(key, None) try: s3 = get_s3_connection(module, aws_connect_kwargs, location, rgw, s3_url) except (botocore.exceptions.NoCredentialsError, botocore.exceptions.ProfileNotFound) as e: module.fail_json( msg= "Can't authorize connection. Check your credentials and profile.", exceptions=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) validate = not ignore_nonexistent_bucket # separate types of ACLs bucket_acl = [ acl for acl in module.params.get('permission') if acl in bucket_canned_acl ] object_acl = [ acl for acl in module.params.get('permission') if acl in object_canned_acl ] error_acl = [ acl for acl in module.params.get('permission') if acl not in bucket_canned_acl and acl not in object_canned_acl ] if error_acl: module.fail_json(msg='Unknown permission specified: %s' % error_acl) # First, we check to see if the bucket exists, we get "bucket" returned. bucketrtn = bucket_check(module, s3, bucket, validate=validate) if validate and mode not in ('create', 'put', 'delete') and not bucketrtn: module.fail_json(msg="Source bucket cannot be found.") # If our mode is a GET operation (download), go through the procedure as appropriate ... if mode == 'get': # Next, we check to see if the key in the bucket exists. If it exists, it also returns key_matches md5sum check. keyrtn = key_check(module, s3, bucket, obj, version=version, validate=validate) if keyrtn is False: module.fail_json(msg="Key %s with version id %s does not exist." % (obj, version)) # If the destination path doesn't exist or overwrite is True, no need to do the md5um etag check, so just download. # Compare the remote MD5 sum of the object with the local dest md5sum, if it already exists. if path_check(dest): # Determine if the remote and local object are identical if keysum(module, s3, bucket, obj, version=version) == module.md5(dest): sum_matches = True if overwrite == 'always': download_s3file(module, s3, bucket, obj, dest, retries, version=version) else: module.exit_json( msg= "Local and remote object are identical, ignoring. Use overwrite=always parameter to force.", changed=False) else: sum_matches = False if overwrite in ('always', 'different'): download_s3file(module, s3, bucket, obj, dest, retries, version=version) else: module.exit_json( msg= "WARNING: Checksums do not match. Use overwrite parameter to force download." ) else: download_s3file(module, s3, bucket, obj, dest, retries, version=version) # if our mode is a PUT operation (upload), go through the procedure as appropriate ... if mode == 'put': # if putting an object in a bucket yet to be created, acls for the bucket and/or the object may be specified # these were separated into the variables bucket_acl and object_acl above # Lets check the src path. if not path_check(src): module.fail_json(msg="Local object for PUT does not exist") # Lets check to see if bucket exists to get ground truth. if bucketrtn: keyrtn = key_check(module, s3, bucket, obj, version=version, validate=validate) # Lets check key state. Does it exist and if it does, compute the etag md5sum. if bucketrtn and keyrtn: # Compare the local and remote object if module.md5(src) == keysum(module, s3, bucket, obj): sum_matches = True if overwrite == 'always': # only use valid object acls for the upload_s3file function module.params['permission'] = object_acl upload_s3file(module, s3, bucket, obj, src, expiry, metadata, encrypt, headers) else: get_download_url(module, s3, bucket, obj, expiry, changed=False) else: sum_matches = False if overwrite in ('always', 'different'): # only use valid object acls for the upload_s3file function module.params['permission'] = object_acl upload_s3file(module, s3, bucket, obj, src, expiry, metadata, encrypt, headers) else: module.exit_json( msg= "WARNING: Checksums do not match. Use overwrite parameter to force upload." ) # If neither exist (based on bucket existence), we can create both. if not bucketrtn: # only use valid bucket acls for create_bucket function module.params['permission'] = bucket_acl create_bucket(module, s3, bucket, location) # only use valid object acls for the upload_s3file function module.params['permission'] = object_acl upload_s3file(module, s3, bucket, obj, src, expiry, metadata, encrypt, headers) # If bucket exists but key doesn't, just upload. if bucketrtn and not keyrtn: # only use valid object acls for the upload_s3file function module.params['permission'] = object_acl upload_s3file(module, s3, bucket, obj, src, expiry, metadata, encrypt, headers) # Delete an object from a bucket, not the entire bucket if mode == 'delobj': if obj is None: module.fail_json(msg="object parameter is required") if bucket: deletertn = delete_key(module, s3, bucket, obj) if deletertn is True: module.exit_json(msg="Object deleted from bucket %s." % bucket, changed=True) else: module.fail_json(msg="Bucket parameter is required.") # Delete an entire bucket, including all objects in the bucket if mode == 'delete': if bucket: deletertn = delete_bucket(module, s3, bucket) if deletertn is True: module.exit_json( msg="Bucket %s and all keys have been deleted." % bucket, changed=True) else: module.fail_json(msg="Bucket parameter is required.") # Support for listing a set of keys if mode == 'list': exists = bucket_check(module, s3, bucket) # If the bucket does not exist then bail out if not exists: module.fail_json(msg="Target bucket (%s) cannot be found" % bucket) list_keys(module, s3, bucket, prefix, marker, max_keys) # Need to research how to create directories without "populating" a key, so this should just do bucket creation for now. # WE SHOULD ENABLE SOME WAY OF CREATING AN EMPTY KEY TO CREATE "DIRECTORY" STRUCTURE, AWS CONSOLE DOES THIS. if mode == 'create': # if both creating a bucket and putting an object in it, acls for the bucket and/or the object may be specified # these were separated above into the variables bucket_acl and object_acl if bucket and not obj: if bucketrtn: module.exit_json(msg="Bucket already exists.", changed=False) else: # only use valid bucket acls when creating the bucket module.params['permission'] = bucket_acl module.exit_json(msg="Bucket created successfully", changed=create_bucket(module, s3, bucket, location)) if bucket and obj: if obj.endswith('/'): dirobj = obj else: dirobj = obj + "/" if bucketrtn: if key_check(module, s3, bucket, dirobj): module.exit_json( msg="Bucket %s and key %s already exists." % (bucket, obj), changed=False) else: # setting valid object acls for the create_dirkey function module.params['permission'] = object_acl create_dirkey(module, s3, bucket, dirobj) else: # only use valid bucket acls for the create_bucket function module.params['permission'] = bucket_acl created = create_bucket(module, s3, bucket, location) # only use valid object acls for the create_dirkey function module.params['permission'] = object_acl create_dirkey(module, s3, bucket, dirobj) # Support for grabbing the time-expired URL for an object in S3/Walrus. if mode == 'geturl': if not bucket and not obj: module.fail_json(msg="Bucket and Object parameters must be set") keyrtn = key_check(module, s3, bucket, obj, version=version, validate=validate) if keyrtn: get_download_url(module, s3, bucket, obj, expiry) else: module.fail_json(msg="Key %s does not exist." % obj) if mode == 'getstr': if bucket and obj: keyrtn = key_check(module, s3, bucket, obj, version=version, validate=validate) if keyrtn: download_s3str(module, s3, bucket, obj, version=version) elif version is not None: module.fail_json( msg="Key %s with version id %s does not exist." % (obj, version)) else: module.fail_json(msg="Key %s does not exist." % obj) module.exit_json(failed=False)
def register_target(connection, module): """ Registers a target to a target group :param module: ansible module object :param connection: boto3 connection :return: """ target_az = module.params.get("target_az") target_group_arn = module.params.get("target_group_arn") target_id = module.params.get("target_id") target_port = module.params.get("target_port") target_status = module.params.get("target_status") target_status_timeout = module.params.get("target_status_timeout") changed = False if not target_group_arn: target_group_arn = convert_tg_name_to_arn( connection, module, module.params.get("target_group_name")) target = dict(Id=target_id) if target_az: target['AvailabilityZone'] = target_az if target_port: target['Port'] = target_port target_description = describe_targets(connection, module, target_group_arn, [target]) if 'Reason' in target_description['TargetHealth']: if target_description['TargetHealth'][ 'Reason'] == "Target.NotRegistered": try: connection.register_targets(TargetGroupArn=target_group_arn, Targets=[target]) changed = True if target_status: target_status_check(connection, module, target_group_arn, target, target_status, target_status_timeout) except ClientError as e: module.fail_json( msg="Unable to deregister target {0}: {1}".format( target, to_native(e)), exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) except BotoCoreError as e: module.fail_json( msg="Unable to deregister target {0}: {1}".format( target, to_native(e)), exception=traceback.format_exc()) # Get all targets for the target group target_descriptions = describe_targets(connection, module, target_group_arn, []) module.exit_json(changed=changed, target_health_descriptions=camel_dict_to_snake_dict( target_descriptions), target_group_arn=target_group_arn)