def main(): argument_spec = dict(dhcp_options_id=dict(type='str', default=None), domain_name=dict(type='str', default=None), dns_servers=dict(type='list', default=None), ntp_servers=dict(type='list', default=None), netbios_name_servers=dict(type='list', default=None), netbios_node_type=dict(type='int', default=None), vpc_id=dict(type='str', default=None), delete_old=dict(type='bool', default=True), inherit_existing=dict(type='bool', default=False), tags=dict(type='dict', default=None, aliases=['resource_tags']), state=dict(type='str', default='present', choices=['present', 'absent'])) module = AnsibleAWSModule(argument_spec=argument_spec, check_boto3=False, supports_check_mode=True) params = module.params found = False changed = False new_options = collections.defaultdict(lambda: None) if not HAS_BOTO: module.fail_json(msg='boto is required for this module') region, ec2_url, boto_params = get_aws_connection_info(module) connection = connect_to_aws(boto.vpc, region, **boto_params) existing_options = None # First check if we were given a dhcp_options_id if not params['dhcp_options_id']: # No, so create new_options from the parameters if params['dns_servers'] is not None: new_options['domain-name-servers'] = params['dns_servers'] if params['netbios_name_servers'] is not None: new_options['netbios-name-servers'] = params[ 'netbios_name_servers'] if params['ntp_servers'] is not None: new_options['ntp-servers'] = params['ntp_servers'] if params['domain_name'] is not None: # needs to be a list for comparison with boto objects later new_options['domain-name'] = [params['domain_name']] if params['netbios_node_type'] is not None: # needs to be a list for comparison with boto objects later new_options['netbios-node-type'] = [ str(params['netbios_node_type']) ] # If we were given a vpc_id then we need to look at the options on that if params['vpc_id']: existing_options = fetch_dhcp_options_for_vpc( connection, params['vpc_id']) # if we've been asked to inherit existing options, do that now if params['inherit_existing']: if existing_options: for option in [ 'domain-name-servers', 'netbios-name-servers', 'ntp-servers', 'domain-name', 'netbios-node-type' ]: if existing_options.options.get( option) and new_options[option] != [] and ( not new_options[option] or [''] == new_options[option]): new_options[option] = existing_options.options.get( option) # Do the vpc's dhcp options already match what we're asked for? if so we are done if existing_options and new_options == existing_options.options: module.exit_json(changed=changed, new_options=new_options, dhcp_options_id=existing_options.id) # If no vpc_id was given, or the options don't match then look for an existing set using tags found, dhcp_option = match_dhcp_options(connection, params['tags'], new_options) # Now let's cover the case where there are existing options that we were told about by id # If a dhcp_options_id was supplied we don't look at options inside, just set tags (if given) else: supplied_options = connection.get_all_dhcp_options( filters={'dhcp-options-id': params['dhcp_options_id']}) if len(supplied_options) != 1: if params['state'] != 'absent': module.fail_json( msg=" a dhcp_options_id was supplied, but does not exist") else: found = True dhcp_option = supplied_options[0] if params['state'] != 'absent' and params['tags']: ensure_tags(module, connection, dhcp_option.id, params['tags'], False, module.check_mode) # Now we have the dhcp options set, let's do the necessary # if we found options we were asked to remove then try to do so if params['state'] == 'absent': if not module.check_mode: if found: changed = remove_dhcp_options_by_id(connection, dhcp_option.id) module.exit_json(changed=changed, new_options={}) # otherwise if we haven't found the required options we have something to do elif not module.check_mode and not found: # create some dhcp options if we weren't able to use existing ones if not found: # Convert netbios-node-type and domain-name back to strings if new_options['netbios-node-type']: new_options['netbios-node-type'] = new_options[ 'netbios-node-type'][0] if new_options['domain-name']: new_options['domain-name'] = new_options['domain-name'][0] # create the new dhcp options set requested dhcp_option = connection.create_dhcp_options( new_options['domain-name'], new_options['domain-name-servers'], new_options['ntp-servers'], new_options['netbios-name-servers'], new_options['netbios-node-type']) # wait for dhcp option to be accessible found_dhcp_opt = False start_time = time() try: found_dhcp_opt = retry_not_found( connection.get_all_dhcp_options, dhcp_options_ids=[dhcp_option.id]) except EC2ResponseError as e: module.fail_json_aws(e, msg="Failed to describe DHCP options") if not found_dhcp_opt: module.fail_json(msg="Failed to wait for {0} to be available.". format(dhcp_option.id)) changed = True if params['tags']: ensure_tags(module, connection, dhcp_option.id, params['tags'], False, module.check_mode) # If we were given a vpc_id, then attach the options we now have to that before we finish if params['vpc_id'] and not module.check_mode: changed = True connection.associate_dhcp_options(dhcp_option.id, params['vpc_id']) # and remove old ones if that was requested if params['delete_old'] and existing_options: remove_dhcp_options_by_id(connection, existing_options.id) module.exit_json(changed=changed, new_options=new_options, dhcp_options_id=dhcp_option.id)
def main(): argument_spec = dict(bucket=dict(required=True), dest=dict(default=None, type='path'), encrypt=dict(default=True, type='bool'), encryption_mode=dict(choices=['AES256', 'aws:kms'], default='AES256'), 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']), dualstack=dict(default='no', type='bool'), rgw=dict(default='no', type='bool'), src=dict(), ignore_nonexistent_bucket=dict(default=False, type='bool'), encryption_kms_key_id=dict()) module = AnsibleAWSModule( argument_spec=argument_spec, supports_check_mode=True, required_if=[['mode', 'put', ['src', 'object']], ['mode', 'get', ['dest', 'object']], ['mode', 'getstr', ['object']], ['mode', 'geturl', ['object']]], ) 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') dualstack = module.params.get('dualstack') rgw = module.params.get('rgw') src = module.params.get('src') ignore_nonexistent_bucket = module.params.get('ignore_nonexistent_bucket') 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' if overwrite == 'different' and not HAS_MD5: module.fail_json( msg= 'overwrite=different is unavailable: ETag calculation requires MD5 support' ) 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'] if dualstack and s3_url is not None and 'amazonaws.com' not in s3_url: module.fail_json(msg='dualstack only applies to AWS S3') if dualstack and not module.botocore_at_least('1.4.45'): module.fail_json(msg='dualstack requires botocore >= 1.4.45') # 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 if s3_url: for key in ['validate_certs', 'security_token', 'profile_name']: aws_connect_kwargs.pop(key, None) s3 = get_s3_connection(module, aws_connect_kwargs, location, rgw, s3_url) 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 mode == 'get': keyrtn = key_check(module, s3, bucket, obj, version=version, validate=validate) if keyrtn is False: if version: 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) if path_check(dest) and overwrite != 'always': if overwrite == 'never': module.exit_json( msg= "Local object already exists and overwrite is disabled.", changed=False) if etag_compare(module, dest, s3, bucket, obj, version=version): module.exit_json( msg= "Local and remote object are identical, ignoring. Use overwrite=always parameter to force.", changed=False) try: download_s3file(module, s3, bucket, obj, dest, retries, version=version) except Sigv4Required: s3 = get_s3_connection(module, aws_connect_kwargs, location, rgw, s3_url, sig_4=True) download_s3file(module, s3, bucket, obj, dest, retries, version=version) 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 if not path_check(src): module.fail_json(msg="Local object for PUT does not exist") if bucketrtn: keyrtn = key_check(module, s3, bucket, obj, version=version, validate=validate) else: # If the bucket doesn't exist we should create it. # only use valid bucket acls for create_bucket function module.params['permission'] = bucket_acl create_bucket(module, s3, bucket, location) if keyrtn and overwrite != 'always': if overwrite == 'never' or etag_compare(module, src, s3, bucket, obj): # Return the download URL for the existing object get_download_url(module, s3, bucket, obj, expiry, changed=False) # 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, encrypt) 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, encrypt) # 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: try: download_s3str(module, s3, bucket, obj, version=version) except Sigv4Required: s3 = get_s3_connection(module, aws_connect_kwargs, location, rgw, s3_url, sig_4=True) 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 main(): module = AnsibleAWSModule( argument_spec={ 'name': dict(type='str', required=True), 'state': dict(type='str', choices=['present', 'absent'], default='present'), 'account_sources': dict(type='list', required=True), 'organization_source': dict(type='dict', required=True) }, supports_check_mode=False, ) result = { 'changed': False } name = module.params.get('name') state = module.params.get('state') params = {} if name: params['ConfigurationAggregatorName'] = name if module.params.get('account_sources'): params['AccountAggregationSources'] = [] for i in module.params.get('account_sources'): tmp_dict = {} if i.get('account_ids'): tmp_dict['AccountIds'] = i.get('account_ids') if i.get('aws_regions'): tmp_dict['AwsRegions'] = i.get('aws_regions') if i.get('all_aws_regions') is not None: tmp_dict['AllAwsRegions'] = i.get('all_aws_regions') params['AccountAggregationSources'].append(tmp_dict) if module.params.get('organization_source'): params['OrganizationAggregationSource'] = {} if module.params.get('organization_source').get('role_arn'): params['OrganizationAggregationSource'].update({ 'RoleArn': module.params.get('organization_source').get('role_arn') }) if module.params.get('organization_source').get('aws_regions'): params['OrganizationAggregationSource'].update({ 'AwsRegions': module.params.get('organization_source').get('aws_regions') }) if module.params.get('organization_source').get('all_aws_regions') is not None: params['OrganizationAggregationSourcep'].update({ 'AllAwsRegions': module.params.get('organization_source').get('all_aws_regions') }) client = module.client('config', retry_decorator=AWSRetry.jittered_backoff()) resource_status = resource_exists(client, module, params) if state == 'present': if not resource_status: create_resource(client, module, params, result) else: update_resource(client, module, params, result) if state == 'absent': if resource_status: delete_resource(client, module, params, result) module.exit_json(changed=result['changed'])
def main(): argument_spec = dict( state=dict(required=True, choices=['present', 'absent']), arn=dict(required=False, type='str'), family=dict(required=False, type='str'), revision=dict(required=False, type='int'), force_create=dict(required=False, default=False, type='bool'), containers=dict(required=False, type='list'), network_mode=dict(required=False, default='bridge', choices=['default', 'bridge', 'host', 'none', 'awsvpc'], type='str'), task_role_arn=dict(required=False, default='', type='str'), execution_role_arn=dict(required=False, default='', type='str'), volumes=dict(required=False, type='list'), launch_type=dict(required=False, choices=['EC2', 'FARGATE']), cpu=dict(), memory=dict(required=False, type='str') ) module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True, required_if=[('launch_type', 'FARGATE', ['cpu', 'memory'])] ) task_to_describe = None task_mgr = EcsTaskManager(module) results = dict(changed=False) if module.params['launch_type']: if not module.botocore_at_least('1.8.4'): module.fail_json(msg='botocore needs to be version 1.8.4 or higher to use launch_type') if module.params['execution_role_arn']: if not module.botocore_at_least('1.10.44'): module.fail_json(msg='botocore needs to be version 1.10.44 or higher to use execution_role_arn') if module.params['containers']: for container in module.params['containers']: for environment in container.get('environment', []): environment['value'] = to_text(environment['value']) if module.params['state'] == 'present': if 'containers' not in module.params or not module.params['containers']: module.fail_json(msg="To use task definitions, a list of containers must be specified") if 'family' not in module.params or not module.params['family']: module.fail_json(msg="To use task definitions, a family must be specified") network_mode = module.params['network_mode'] launch_type = module.params['launch_type'] if launch_type == 'FARGATE' and network_mode != 'awsvpc': module.fail_json(msg="To use FARGATE launch type, network_mode must be awsvpc") family = module.params['family'] existing_definitions_in_family = task_mgr.describe_task_definitions(module.params['family']) if 'revision' in module.params and module.params['revision']: # The definition specifies revision. We must guarantee that an active revision of that number will result from this. revision = int(module.params['revision']) # A revision has been explicitly specified. Attempt to locate a matching revision tasks_defs_for_revision = [td for td in existing_definitions_in_family if td['revision'] == revision] existing = tasks_defs_for_revision[0] if len(tasks_defs_for_revision) > 0 else None if existing and existing['status'] != "ACTIVE": # We cannot reactivate an inactive revision module.fail_json(msg="A task in family '%s' already exists for revision %d, but it is inactive" % (family, revision)) elif not existing: if not existing_definitions_in_family and revision != 1: module.fail_json(msg="You have specified a revision of %d but a created revision would be 1" % revision) elif existing_definitions_in_family and existing_definitions_in_family[-1]['revision'] + 1 != revision: module.fail_json(msg="You have specified a revision of %d but a created revision would be %d" % (revision, existing_definitions_in_family[-1]['revision'] + 1)) else: existing = None def _right_has_values_of_left(left, right): # Make sure the values are equivalent for everything left has for k, v in left.items(): if not ((not v and (k not in right or not right[k])) or (k in right and v == right[k])): # We don't care about list ordering because ECS can change things if isinstance(v, list) and k in right: left_list = v right_list = right[k] or [] if len(left_list) != len(right_list): return False for list_val in left_list: if list_val not in right_list: return False else: return False # Make sure right doesn't have anything that left doesn't for k, v in right.items(): if v and k not in left: return False return True def _task_definition_matches(requested_volumes, requested_containers, requested_task_role_arn, existing_task_definition): if td['status'] != "ACTIVE": return None if requested_task_role_arn != td.get('taskRoleArn', ""): return None existing_volumes = td.get('volumes', []) or [] if len(requested_volumes) != len(existing_volumes): # Nope. return None if len(requested_volumes) > 0: for requested_vol in requested_volumes: found = False for actual_vol in existing_volumes: if _right_has_values_of_left(requested_vol, actual_vol): found = True break if not found: return None existing_containers = td.get('containerDefinitions', []) or [] if len(requested_containers) != len(existing_containers): # Nope. return None for requested_container in requested_containers: found = False for actual_container in existing_containers: if _right_has_values_of_left(requested_container, actual_container): found = True break if not found: return None return existing_task_definition # No revision explicitly specified. Attempt to find an active, matching revision that has all the properties requested for td in existing_definitions_in_family: requested_volumes = module.params['volumes'] or [] requested_containers = module.params['containers'] or [] requested_task_role_arn = module.params['task_role_arn'] existing = _task_definition_matches(requested_volumes, requested_containers, requested_task_role_arn, td) if existing: break if existing and not module.params.get('force_create'): # Awesome. Have an existing one. Nothing to do. results['taskdefinition'] = existing else: if not module.check_mode: # Doesn't exist. create it. volumes = module.params.get('volumes', []) or [] results['taskdefinition'] = task_mgr.register_task(module.params['family'], module.params['task_role_arn'], module.params['execution_role_arn'], module.params['network_mode'], module.params['containers'], volumes, module.params['launch_type'], module.params['cpu'], module.params['memory']) results['changed'] = True elif module.params['state'] == 'absent': # When de-registering a task definition, we can specify the ARN OR the family and revision. if module.params['state'] == 'absent': if 'arn' in module.params and module.params['arn'] is not None: task_to_describe = module.params['arn'] elif 'family' in module.params and module.params['family'] is not None and 'revision' in module.params and \ module.params['revision'] is not None: task_to_describe = module.params['family'] + ":" + str(module.params['revision']) else: module.fail_json(msg="To use task definitions, an arn or family and revision must be specified") existing = task_mgr.describe_task(task_to_describe) if not existing: pass else: # It exists, so we should delete it and mark changed. Return info about the task definition deleted results['taskdefinition'] = existing if 'status' in existing and existing['status'] == "INACTIVE": results['changed'] = False else: if not module.check_mode: task_mgr.deregister_task(task_to_describe) results['changed'] = True module.exit_json(**results)
def main(): argument_spec = dict(certificate=dict(), certificate_arn=dict(aliases=['arn']), certificate_chain=dict(), domain_name=dict(aliases=['domain']), name_tag=dict(aliases=['name']), private_key=dict(no_log=True), state=dict(default='present', choices=['present', 'absent'])) required_if = [ ['state', 'present', ['certificate', 'name_tag', 'private_key']], ] module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True, required_if=required_if) acm = ACMServiceManager(module) # Check argument requirements if module.params['state'] == 'present': if module.params['certificate_arn']: module.fail_json( msg= "Parameter 'certificate_arn' is only valid if parameter 'state' is specified as 'absent'" ) else: # absent # exactly one of these should be specified absent_args = ['certificate_arn', 'domain_name', 'name_tag'] if sum([(module.params[a] is not None) for a in absent_args]) != 1: for a in absent_args: module.debug("%s is %s" % (a, module.params[a])) module.fail_json( msg= "If 'state' is specified as 'absent' then exactly one of 'name_tag', certificate_arn' or 'domain_name' must be specified" ) if module.params['name_tag']: tags = dict(Name=module.params['name_tag']) else: tags = None client = module.client('acm') # fetch the list of certificates currently in ACM certificates = acm.get_certificates( client=client, module=module, domain_name=module.params['domain_name'], arn=module.params['certificate_arn'], only_tags=tags) module.debug("Found %d corresponding certificates in ACM" % len(certificates)) if module.params['state'] == 'present': if len(certificates) > 1: msg = "More than one certificate with Name=%s exists in ACM in this region" % module.params[ 'name_tag'] module.fail_json(msg=msg, certificates=certificates) elif len(certificates) == 1: # update the existing certificate module.debug("Existing certificate found in ACM") old_cert = certificates[0] # existing cert in ACM if ('tags' not in old_cert) or ('Name' not in old_cert['tags']) or ( old_cert['tags']['Name'] != module.params['name_tag']): # shouldn't happen module.fail_json( msg="Internal error, unsure which certificate to update", certificate=old_cert) if 'certificate' not in old_cert: # shouldn't happen module.fail_json( msg= "Internal error, unsure what the existing cert in ACM is", certificate=old_cert) # Are the existing certificate in ACM and the local certificate the same? same = True same &= chain_compare(module, old_cert['certificate'], module.params['certificate']) if module.params['certificate_chain']: # Need to test this # not sure if Amazon appends the cert itself to the chain when self-signed same &= chain_compare(module, old_cert['certificate_chain'], module.params['certificate_chain']) else: # When there is no chain with a cert # it seems Amazon returns the cert itself as the chain same &= chain_compare(module, old_cert['certificate_chain'], module.params['certificate']) if same: module.debug( "Existing certificate in ACM is the same, doing nothing") domain = acm.get_domain_of_cert( client=client, module=module, arn=old_cert['certificate_arn']) module.exit_json(certificate=dict( domain_name=domain, arn=old_cert['certificate_arn']), changed=False) else: module.debug( "Existing certificate in ACM is different, overwriting") # update cert in ACM arn = acm.import_certificate( client, module, certificate=module.params['certificate'], private_key=module.params['private_key'], certificate_chain=module.params['certificate_chain'], arn=old_cert['certificate_arn'], tags=tags) domain = acm.get_domain_of_cert(client=client, module=module, arn=arn) module.exit_json(certificate=dict(domain_name=domain, arn=arn), changed=True) else: # len(certificates) == 0 module.debug("No certificate in ACM. Creating new one.") arn = acm.import_certificate( client=client, module=module, certificate=module.params['certificate'], private_key=module.params['private_key'], certificate_chain=module.params['certificate_chain'], tags=tags) domain = acm.get_domain_of_cert(client=client, module=module, arn=arn) module.exit_json(certificate=dict(domain_name=domain, arn=arn), changed=True) else: # state == absent for cert in certificates: acm.delete_certificate(client, module, cert['certificate_arn']) module.exit_json( arns=[cert['certificate_arn'] for cert in certificates], changed=(len(certificates) > 0))
def main(): argument_spec = dict( operation=dict(required=True, choices=['run', 'start', 'stop']), cluster=dict(required=False, type='str'), # R S P task_definition=dict(required=False, type='str'), # R* S* overrides=dict(required=False, type='dict'), # R S count=dict(required=False, type='int'), # R task=dict(required=False, type='str'), # P* container_instances=dict(required=False, type='list'), # S* started_by=dict(required=False, type='str'), # R S network_configuration=dict(required=False, type='dict'), launch_type=dict(required=False, choices=['EC2', 'FARGATE']), tags=dict(required=False, type='dict')) module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True, required_if=[('launch_type', 'FARGATE', ['network_configuration'])]) # Validate Inputs if module.params['operation'] == 'run': if 'task_definition' not in module.params and module.params[ 'task_definition'] is None: module.fail_json( msg="To run a task, a task_definition must be specified") task_to_list = module.params['task_definition'] status_type = "RUNNING" if module.params['operation'] == 'start': if 'task_definition' not in module.params and module.params[ 'task_definition'] is None: module.fail_json( msg="To start a task, a task_definition must be specified") if 'container_instances' not in module.params and module.params[ 'container_instances'] is None: module.fail_json( msg="To start a task, container instances must be specified") task_to_list = module.params['task'] status_type = "RUNNING" if module.params['operation'] == 'stop': if 'task' not in module.params and module.params['task'] is None: module.fail_json(msg="To stop a task, a task must be specified") if 'task_definition' not in module.params and module.params[ 'task_definition'] is None: module.fail_json( msg="To stop a task, a task definition must be specified") task_to_list = module.params['task_definition'] status_type = "STOPPED" service_mgr = EcsExecManager(module) if module.params[ 'network_configuration'] and not service_mgr.ecs_api_handles_network_configuration( ): module.fail_json( msg= 'botocore needs to be version 1.7.44 or higher to use network configuration' ) if module.params[ 'launch_type'] and not service_mgr.ecs_api_handles_launch_type(): module.fail_json( msg= 'botocore needs to be version 1.8.4 or higher to use launch type') if module.params['tags']: if not service_mgr.ecs_api_handles_tags(): module.fail_json(msg=missing_required_lib("botocore >= 1.12.46", reason="to use tags")) if not service_mgr.ecs_task_long_format_enabled(): module.fail_json( msg= "Cannot set task tags: long format task arns are required to set tags" ) existing = service_mgr.list_tasks(module.params['cluster'], task_to_list, status_type) results = dict(changed=False) if module.params['operation'] == 'run': if existing: # TBD - validate the rest of the details results['task'] = existing else: if not module.check_mode: results['task'] = service_mgr.run_task( module.params['cluster'], module.params['task_definition'], module.params['overrides'], module.params['count'], module.params['started_by'], module.params['launch_type'], module.params['tags'], ) results['changed'] = True elif module.params['operation'] == 'start': if existing: # TBD - validate the rest of the details results['task'] = existing else: if not module.check_mode: results['task'] = service_mgr.start_task( module.params['cluster'], module.params['task_definition'], module.params['overrides'], module.params['container_instances'], module.params['started_by'], module.params['tags'], ) results['changed'] = True elif module.params['operation'] == 'stop': if existing: results['task'] = existing else: if not module.check_mode: # it exists, so we should delete it and mark changed. # return info about the cluster deleted results['task'] = service_mgr.stop_task( module.params['cluster'], module.params['task']) results['changed'] = True module.exit_json(**results)
def main(): module = AnsibleAWSModule( argument_spec={ 'name': dict(type='str', required=True), 'state': dict(type='str', choices=['present', 'absent'], default='present'), 'description': dict(type='str'), 'scope': dict(type='dict'), 'source': dict(type='dict', required=True), 'input_parameters': dict(type='str'), 'execution_frequency': dict( type='str', choices=[ 'One_Hour', 'Three_Hours', 'Six_Hours', 'Twelve_Hours', 'TwentyFour_Hours' ] ), }, supports_check_mode=False, ) result = { 'changed': False } name = module.params.get('name') resource_type = module.params.get('resource_type') state = module.params.get('state') params = {} if name: params['ConfigRuleName'] = name if module.params.get('description'): params['Description'] = module.params.get('description') if module.params.get('scope'): params['Scope'] = {} if module.params.get('scope').get('compliance_types'): params['Scope'].update({ 'ComplianceResourceTypes': module.params.get('scope').get('compliance_types') }) if module.params.get('scope').get('tag_key'): params['Scope'].update({ 'TagKey': module.params.get('scope').get('tag_key') }) if module.params.get('scope').get('tag_value'): params['Scope'].update({ 'TagValue': module.params.get('scope').get('tag_value') }) if module.params.get('scope').get('compliance_id'): params['Scope'].update({ 'ComplianceResourceId': module.params.get('scope').get('compliance_id') }) if module.params.get('source'): params['Source'] = {} if module.params.get('source').get('owner'): params['Source'].update({ 'Owner': module.params.get('source').get('owner') }) if module.params.get('source').get('identifier'): params['Source'].update({ 'SourceIdentifier': module.params.get('source').get('identifier') }) if module.params.get('source').get('details'): params['Source'].update({ 'SourceDetails': module.params.get('source').get('details') }) if module.params.get('input_parameters'): params['InputParameters'] = module.params.get('input_parameters') if module.params.get('execution_frequency'): params['MaximumExecutionFrequency'] = module.params.get('execution_frequency') params['ConfigRuleState'] = 'ACTIVE' client = module.client('config', retry_decorator=AWSRetry.jittered_backoff()) existing_rule = rule_exists(client, module, params) if state == 'present': if not existing_rule: create_resource(client, module, params, result) else: update_resource(client, module, params, result) if state == 'absent': if existing_rule: delete_resource(client, module, params, result) module.exit_json(**result)
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 main(): argument_spec = dict( name=dict(required=True), state=dict(choices=['absent', 'present'], default='present'), tags=dict(type='dict'), ) required_if = [['state', 'present', ['tags']]] module = AnsibleAWSModule( argument_spec=argument_spec, supports_check_mode=False, required_if=required_if, ) name = module.params.get('name') state = module.params.get('state').lower() tags = module.params.get('tags') if tags: tags = ansible_dict_to_boto3_tag_list(tags, 'key', 'value') client = module.client('inspector') try: existing_target_arn = client.list_assessment_targets(filter={ 'assessmentTargetNamePattern': name }, ).get('assessmentTargetArns')[0] existing_target = camel_dict_to_snake_dict( client.describe_assessment_targets(assessmentTargetArns=[ existing_target_arn ], ).get('assessmentTargets')[0]) existing_resource_group_arn = existing_target.get('resource_group_arn') existing_resource_group_tags = client.describe_resource_groups( resourceGroupArns=[existing_resource_group_arn ], ).get('resourceGroups')[0].get('tags') target_exists = True except ( botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError, ) as e: module.fail_json_aws(e, msg="trying to retrieve targets") except IndexError: target_exists = False if state == 'present' and target_exists: ansible_dict_tags = boto3_tag_list_to_ansible_dict(tags) ansible_dict_existing_tags = boto3_tag_list_to_ansible_dict( existing_resource_group_tags) tags_to_add, tags_to_remove = compare_aws_tags( ansible_dict_tags, ansible_dict_existing_tags) if not (tags_to_add or tags_to_remove): existing_target.update({'tags': ansible_dict_existing_tags}) module.exit_json(changed=False, **existing_target) else: try: updated_resource_group_arn = client.create_resource_group( resourceGroupTags=tags, ).get('resourceGroupArn') client.update_assessment_target( assessmentTargetArn=existing_target_arn, assessmentTargetName=name, resourceGroupArn=updated_resource_group_arn, ) updated_target = camel_dict_to_snake_dict( client.describe_assessment_targets(assessmentTargetArns=[ existing_target_arn ], ).get('assessmentTargets')[0]) updated_target.update({'tags': ansible_dict_tags}) module.exit_json(changed=True, **updated_target), except ( botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError, ) as e: module.fail_json_aws(e, msg="trying to update target") elif state == 'present' and not target_exists: try: new_resource_group_arn = client.create_resource_group( resourceGroupTags=tags, ).get('resourceGroupArn') new_target_arn = client.create_assessment_target( assessmentTargetName=name, resourceGroupArn=new_resource_group_arn, ).get('assessmentTargetArn') new_target = camel_dict_to_snake_dict( client.describe_assessment_targets(assessmentTargetArns=[ new_target_arn ], ).get('assessmentTargets')[0]) new_target.update({'tags': boto3_tag_list_to_ansible_dict(tags)}) module.exit_json(changed=True, **new_target) except ( botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError, ) as e: module.fail_json_aws(e, msg="trying to create target") elif state == 'absent' and target_exists: try: client.delete_assessment_target( assessmentTargetArn=existing_target_arn, ) module.exit_json(changed=True) except ( botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError, ) as e: module.fail_json_aws(e, msg="trying to delete target") elif state == 'absent' and not target_exists: module.exit_json(changed=False)
def main(): argument_spec = dict(alias=dict(aliases=['key_alias']), policy_mode=dict(aliases=['mode'], choices=['grant', 'deny'], default='grant'), policy_role_name=dict(aliases=['role_name']), policy_role_arn=dict(aliases=['role_arn']), policy_grant_types=dict(aliases=['grant_types'], type='list'), policy_clean_invalid_entries=dict( aliases=['clean_invalid_entries'], type='bool', default=True), key_id=dict(aliases=['key_arn']), description=dict(), enabled=dict(type='bool', default=True), tags=dict(type='dict', default={}), purge_tags=dict(type='bool', default=False), grants=dict(type='list', default=[]), policy=dict(type='json'), purge_grants=dict(type='bool', default=False), state=dict(default='present', choices=['present', 'absent']), enable_key_rotation=(dict(type='bool'))) module = AnsibleAWSModule( supports_check_mode=True, argument_spec=argument_spec, required_one_of=[['alias', 'key_id']], ) mode = module.params['policy_mode'] kms = module.client('kms') key_metadata = fetch_key_metadata(kms, module, module.params.get('key_id'), module.params.get('alias')) # We can't create keys with a specific ID, if we can't access the key we'll have to fail if module.params.get('state') == 'present' and module.params.get( 'key_id') and not key_metadata: module.fail_json(msg="Could not find key with id %s to update") if module.params.get('policy_grant_types') or mode == 'deny': module.deprecate( 'Managing the KMS IAM Policy via policy_mode and policy_grant_types is fragile' ' and has been deprecated in favour of the policy option.', version='2.13') result = update_policy_grants(kms, module, key_metadata, mode) module.exit_json(**result) if module.params.get('state') == 'absent': if key_metadata is None: module.exit_json(changed=False) result = delete_key(kms, module, key_metadata) module.exit_json(**result) if key_metadata: key_details = get_key_details(kms, module, key_metadata['Arn']) result = update_key(kms, module, key_details) module.exit_json(**result) result = create_key(kms, module) module.exit_json(**result)
def main(): argument_spec = dict( resource=dict(required=True), tags=dict(type='dict'), purge_tags=dict(type='bool', default=False), state=dict(default='present', choices=['present', 'absent', 'list']), ) required_if = [('state', 'present', ['tags']), ('state', 'absent', ['tags'])] module = AnsibleAWSModule(argument_spec=argument_spec, required_if=required_if, supports_check_mode=True) resource = module.params['resource'] tags = module.params['tags'] state = module.params['state'] purge_tags = module.params['purge_tags'] result = {'changed': False} ec2 = module.client('ec2') current_tags = get_tags(ec2, module, resource) if state == 'list': module.deprecate( 'Using the "list" state has been deprecated. Please use the ec2_tag_info module instead', version='2.14') module.exit_json(changed=False, tags=current_tags) add_tags, remove = compare_aws_tags(current_tags, tags, purge_tags=purge_tags) remove_tags = {} if state == 'absent': for key in tags: if key in current_tags and (tags[key] is None or current_tags[key] == tags[key]): remove_tags[key] = current_tags[key] for key in remove: remove_tags[key] = current_tags[key] if remove_tags: result['changed'] = True result['removed_tags'] = remove_tags if not module.check_mode: try: AWSRetry.jittered_backoff()(ec2.delete_tags)( Resources=[resource], Tags=ansible_dict_to_boto3_tag_list(remove_tags)) except (BotoCoreError, ClientError) as e: module.fail_json_aws( e, msg='Failed to remove tags {0} from resource {1}'.format( remove_tags, resource)) if state == 'present' and add_tags: result['changed'] = True result['added_tags'] = add_tags current_tags.update(add_tags) if not module.check_mode: try: AWSRetry.jittered_backoff()(ec2.create_tags)( Resources=[resource], Tags=ansible_dict_to_boto3_tag_list(add_tags)) except (BotoCoreError, ClientError) as e: module.fail_json_aws( e, msg='Failed to set tags {0} on resource {1}'.format( add_tags, resource)) result['tags'] = get_tags(ec2, module, resource) module.exit_json(**result)
def main(): argument_spec = dict( stack_name=dict(required=True), template_parameters=dict(required=False, type='dict', default={}), state=dict(default='present', choices=['present', 'absent']), template=dict(default=None, required=False, type='path'), notification_arns=dict(default=None, required=False), stack_policy=dict(default=None, required=False), disable_rollback=dict(default=False, type='bool'), on_create_failure=dict(default=None, required=False, choices=['DO_NOTHING', 'ROLLBACK', 'DELETE']), create_timeout=dict(default=None, type='int'), template_url=dict(default=None, required=False), template_body=dict(default=None, required=False), template_format=dict(removed_in_version='2.14'), create_changeset=dict(default=False, type='bool'), changeset_name=dict(default=None, required=False), role_arn=dict(default=None, required=False), tags=dict(default=None, type='dict'), termination_protection=dict(default=None, type='bool'), events_limit=dict(default=200, type='int'), backoff_retries=dict(type='int', default=10, required=False), backoff_delay=dict(type='int', default=3, required=False), backoff_max_delay=dict(type='int', default=30, required=False), capabilities=dict(type='list', default=['CAPABILITY_IAM', 'CAPABILITY_NAMED_IAM'])) module = AnsibleAWSModule( argument_spec=argument_spec, mutually_exclusive=[['template_url', 'template', 'template_body'], ['disable_rollback', 'on_create_failure']], supports_check_mode=True) invalid_capabilities = [] user_capabilities = module.params.get('capabilities') for user_cap in user_capabilities: if user_cap not in [ 'CAPABILITY_IAM', 'CAPABILITY_NAMED_IAM', 'CAPABILITY_AUTO_EXPAND' ]: invalid_capabilities.append(user_cap) if invalid_capabilities: module.fail_json(msg="Specified capabilities are invalid : %r," " please check documentation for valid capabilities" % invalid_capabilities) # collect the parameters that are passed to boto3. Keeps us from having so many scalars floating around. stack_params = { 'Capabilities': user_capabilities, 'ClientRequestToken': to_native(uuid.uuid4()), } state = module.params['state'] stack_params['StackName'] = module.params['stack_name'] if module.params['template'] is not None: with open(module.params['template'], 'r') as template_fh: stack_params['TemplateBody'] = template_fh.read() elif module.params['template_body'] is not None: stack_params['TemplateBody'] = module.params['template_body'] elif module.params['template_url'] is not None: stack_params['TemplateURL'] = module.params['template_url'] if module.params.get('notification_arns'): stack_params['NotificationARNs'] = module.params[ 'notification_arns'].split(',') else: stack_params['NotificationARNs'] = [] # can't check the policy when verifying. if module.params[ 'stack_policy'] is not None and not module.check_mode and not module.params[ 'create_changeset']: with open(module.params['stack_policy'], 'r') as stack_policy_fh: stack_params['StackPolicyBody'] = stack_policy_fh.read() template_parameters = module.params['template_parameters'] stack_params['Parameters'] = [] for k, v in template_parameters.items(): if isinstance(v, dict): # set parameter based on a dict to allow additional CFN Parameter Attributes param = dict(ParameterKey=k) if 'value' in v: param['ParameterValue'] = str(v['value']) if 'use_previous_value' in v and bool(v['use_previous_value']): param['UsePreviousValue'] = True param.pop('ParameterValue', None) stack_params['Parameters'].append(param) else: # allow default k/v configuration to set a template parameter stack_params['Parameters'].append({ 'ParameterKey': k, 'ParameterValue': str(v) }) if isinstance(module.params.get('tags'), dict): stack_params['Tags'] = ansible_dict_to_boto3_tag_list( module.params['tags']) if module.params.get('role_arn'): stack_params['RoleARN'] = module.params['role_arn'] result = {} cfn = module.client('cloudformation') # Wrap the cloudformation client methods that this module uses with # automatic backoff / retry for throttling error codes backoff_wrapper = AWSRetry.jittered_backoff( retries=module.params.get('backoff_retries'), delay=module.params.get('backoff_delay'), max_delay=module.params.get('backoff_max_delay')) cfn.describe_stack_events = backoff_wrapper(cfn.describe_stack_events) cfn.create_stack = backoff_wrapper(cfn.create_stack) cfn.list_change_sets = backoff_wrapper(cfn.list_change_sets) cfn.create_change_set = backoff_wrapper(cfn.create_change_set) cfn.update_stack = backoff_wrapper(cfn.update_stack) cfn.describe_stacks = backoff_wrapper(cfn.describe_stacks) cfn.list_stack_resources = backoff_wrapper(cfn.list_stack_resources) cfn.delete_stack = backoff_wrapper(cfn.delete_stack) if boto_supports_termination_protection(cfn): cfn.update_termination_protection = backoff_wrapper( cfn.update_termination_protection) stack_info = get_stack_facts(cfn, stack_params['StackName']) if module.check_mode: if state == 'absent' and stack_info: module.exit_json(changed=True, msg='Stack would be deleted', meta=[]) elif state == 'absent' and not stack_info: module.exit_json(changed=False, msg='Stack doesn\'t exist', meta=[]) elif state == 'present' and not stack_info: module.exit_json(changed=True, msg='New stack would be created', meta=[]) else: module.exit_json(**check_mode_changeset(module, stack_params, cfn)) if state == 'present': if not stack_info: result = create_stack(module, stack_params, cfn, module.params.get('events_limit')) elif module.params.get('create_changeset'): result = create_changeset(module, stack_params, cfn, module.params.get('events_limit')) else: if module.params.get('termination_protection') is not None: update_termination_protection( module, cfn, stack_params['StackName'], bool(module.params.get('termination_protection'))) result = update_stack(module, stack_params, cfn, module.params.get('events_limit')) # format the stack output stack = get_stack_facts(cfn, stack_params['StackName']) if stack is not None: if result.get('stack_outputs') is None: # always define stack_outputs, but it may be empty result['stack_outputs'] = {} for output in stack.get('Outputs', []): result['stack_outputs'][ output['OutputKey']] = output['OutputValue'] stack_resources = [] reslist = cfn.list_stack_resources( StackName=stack_params['StackName']) for res in reslist.get('StackResourceSummaries', []): stack_resources.append({ "logical_resource_id": res['LogicalResourceId'], "physical_resource_id": res.get('PhysicalResourceId', ''), "resource_type": res['ResourceType'], "last_updated_time": res['LastUpdatedTimestamp'], "status": res['ResourceStatus'], "status_reason": res.get('ResourceStatusReason') # can be blank, apparently }) result['stack_resources'] = stack_resources elif state == 'absent': # absent state is different because of the way delete_stack works. # problem is it it doesn't give an error if stack isn't found # so must describe the stack first try: stack = get_stack_facts(cfn, stack_params['StackName']) if not stack: result = {'changed': False, 'output': 'Stack not found.'} else: if stack_params.get('RoleARN') is None: cfn.delete_stack(StackName=stack_params['StackName']) else: cfn.delete_stack(StackName=stack_params['StackName'], RoleARN=stack_params['RoleARN']) result = stack_operation( cfn, stack_params['StackName'], 'DELETE', module.params.get('events_limit'), stack_params.get('ClientRequestToken', None)) except Exception as err: module.fail_json_aws(err) module.exit_json(**result)
def main(): argument_spec = dict( name=dict(required=True), state=dict(default='present', choices=['present', 'absent']), runtime=dict(), role=dict(), handler=dict(), zip_file=dict(aliases=['src']), s3_bucket=dict(), s3_key=dict(), s3_object_version=dict(), description=dict(default=''), timeout=dict(type='int', default=3), memory_size=dict(type='int', default=128), vpc_subnet_ids=dict(type='list'), vpc_security_group_ids=dict(type='list'), environment_variables=dict(type='dict'), dead_letter_arn=dict(), tracing_mode=dict(choices=['Active', 'PassThrough']), tags=dict(type='dict'), ) mutually_exclusive = [['zip_file', 's3_key'], ['zip_file', 's3_bucket'], ['zip_file', 's3_object_version']] required_together = [['s3_key', 's3_bucket'], ['vpc_subnet_ids', 'vpc_security_group_ids']] required_if = [['state', 'present', ['runtime', 'handler', 'role']]] module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True, mutually_exclusive=mutually_exclusive, required_together=required_together, required_if=required_if) name = module.params.get('name') state = module.params.get('state').lower() runtime = module.params.get('runtime') role = module.params.get('role') handler = module.params.get('handler') s3_bucket = module.params.get('s3_bucket') s3_key = module.params.get('s3_key') s3_object_version = module.params.get('s3_object_version') zip_file = module.params.get('zip_file') description = module.params.get('description') timeout = module.params.get('timeout') memory_size = module.params.get('memory_size') vpc_subnet_ids = module.params.get('vpc_subnet_ids') vpc_security_group_ids = module.params.get('vpc_security_group_ids') environment_variables = module.params.get('environment_variables') dead_letter_arn = module.params.get('dead_letter_arn') tracing_mode = module.params.get('tracing_mode') tags = module.params.get('tags') check_mode = module.check_mode changed = False region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module, boto3=True) if not region: module.fail_json(msg='region must be specified') try: client = boto3_conn(module, conn_type='client', resource='lambda', region=region, endpoint=ec2_url, **aws_connect_kwargs) except (ClientError, ValidationError) as e: module.fail_json_aws(e, msg="Trying to connect to AWS") if state == 'present': if re.match(r'^arn:aws(-([a-z\-]+))?:iam', role): role_arn = role else: # get account ID and assemble ARN account_id, partition = get_account_info(module, region=region, endpoint=ec2_url, **aws_connect_kwargs) role_arn = 'arn:{0}:iam::{1}:role/{2}'.format(partition, account_id, role) # Get function configuration if present, False otherwise current_function = get_current_function(client, name) # Update existing Lambda function if state == 'present' and current_function: # Get current state current_config = current_function['Configuration'] current_version = None # Update function configuration func_kwargs = {'FunctionName': name} # Update configuration if needed if role_arn and current_config['Role'] != role_arn: func_kwargs.update({'Role': role_arn}) if handler and current_config['Handler'] != handler: func_kwargs.update({'Handler': handler}) if description and current_config['Description'] != description: func_kwargs.update({'Description': description}) if timeout and current_config['Timeout'] != timeout: func_kwargs.update({'Timeout': timeout}) if memory_size and current_config['MemorySize'] != memory_size: func_kwargs.update({'MemorySize': memory_size}) if runtime and current_config['Runtime'] != runtime: func_kwargs.update({'Runtime': runtime}) if (environment_variables is not None) and (current_config.get( 'Environment', {}).get('Variables', {}) != environment_variables): func_kwargs.update({'Environment': {'Variables': environment_variables}}) if dead_letter_arn is not None: if current_config.get('DeadLetterConfig'): if current_config['DeadLetterConfig']['TargetArn'] != dead_letter_arn: func_kwargs.update({'DeadLetterConfig': {'TargetArn': dead_letter_arn}}) else: if dead_letter_arn != "": func_kwargs.update({'DeadLetterConfig': {'TargetArn': dead_letter_arn}}) if tracing_mode and (current_config.get('TracingConfig', {}).get('Mode', 'PassThrough') != tracing_mode): func_kwargs.update({'TracingConfig': {'Mode': tracing_mode}}) # If VPC configuration is desired if vpc_subnet_ids or vpc_security_group_ids: if not vpc_subnet_ids or not vpc_security_group_ids: module.fail_json(msg='vpc connectivity requires at least one security group and one subnet') if 'VpcConfig' in current_config: # Compare VPC config with current config current_vpc_subnet_ids = current_config['VpcConfig']['SubnetIds'] current_vpc_security_group_ids = current_config['VpcConfig']['SecurityGroupIds'] subnet_net_id_changed = sorted(vpc_subnet_ids) != sorted(current_vpc_subnet_ids) vpc_security_group_ids_changed = sorted(vpc_security_group_ids) != sorted(current_vpc_security_group_ids) if 'VpcConfig' not in current_config or subnet_net_id_changed or vpc_security_group_ids_changed: new_vpc_config = {'SubnetIds': vpc_subnet_ids, 'SecurityGroupIds': vpc_security_group_ids} func_kwargs.update({'VpcConfig': new_vpc_config}) else: # No VPC configuration is desired, assure VPC config is empty when present in current config if 'VpcConfig' in current_config and current_config['VpcConfig'].get('VpcId'): func_kwargs.update({'VpcConfig': {'SubnetIds': [], 'SecurityGroupIds': []}}) # Upload new configuration if configuration has changed if len(func_kwargs) > 1: try: if not check_mode: response = client.update_function_configuration(**func_kwargs) current_version = response['Version'] changed = True except (ParamValidationError, ClientError) as e: module.fail_json_aws(e, msg="Trying to update lambda configuration") # Update code configuration code_kwargs = {'FunctionName': name, 'Publish': True} # Update S3 location if s3_bucket and s3_key: # If function is stored on S3 always update code_kwargs.update({'S3Bucket': s3_bucket, 'S3Key': s3_key}) # If S3 Object Version is given if s3_object_version: code_kwargs.update({'S3ObjectVersion': s3_object_version}) # Compare local checksum, update remote code when different elif zip_file: local_checksum = sha256sum(zip_file) remote_checksum = current_config['CodeSha256'] # Only upload new code when local code is different compared to the remote code if local_checksum != remote_checksum: try: with open(zip_file, 'rb') as f: encoded_zip = f.read() code_kwargs.update({'ZipFile': encoded_zip}) except IOError as e: module.fail_json(msg=str(e), exception=traceback.format_exc()) # Tag Function if tags is not None: if set_tag(client, module, tags, current_function): changed = True # Upload new code if needed (e.g. code checksum has changed) if len(code_kwargs) > 2: try: if not check_mode: response = client.update_function_code(**code_kwargs) current_version = response['Version'] changed = True except (ParamValidationError, ClientError) as e: module.fail_json_aws(e, msg="Trying to upload new code") # Describe function code and configuration response = get_current_function(client, name, qualifier=current_version) if not response: module.fail_json(msg='Unable to get function information after updating') # We're done module.exit_json(changed=changed, **camel_dict_to_snake_dict(response)) # Function doesn't exists, create new Lambda function elif state == 'present': if s3_bucket and s3_key: # If function is stored on S3 code = {'S3Bucket': s3_bucket, 'S3Key': s3_key} if s3_object_version: code.update({'S3ObjectVersion': s3_object_version}) elif zip_file: # If function is stored in local zipfile try: with open(zip_file, 'rb') as f: zip_content = f.read() code = {'ZipFile': zip_content} except IOError as e: module.fail_json(msg=str(e), exception=traceback.format_exc()) else: module.fail_json(msg='Either S3 object or path to zipfile required') func_kwargs = {'FunctionName': name, 'Publish': True, 'Runtime': runtime, 'Role': role_arn, 'Code': code, 'Timeout': timeout, 'MemorySize': memory_size, } if description is not None: func_kwargs.update({'Description': description}) if handler is not None: func_kwargs.update({'Handler': handler}) if environment_variables: func_kwargs.update({'Environment': {'Variables': environment_variables}}) if dead_letter_arn: func_kwargs.update({'DeadLetterConfig': {'TargetArn': dead_letter_arn}}) if tracing_mode: func_kwargs.update({'TracingConfig': {'Mode': tracing_mode}}) # If VPC configuration is given if vpc_subnet_ids or vpc_security_group_ids: if not vpc_subnet_ids or not vpc_security_group_ids: module.fail_json(msg='vpc connectivity requires at least one security group and one subnet') func_kwargs.update({'VpcConfig': {'SubnetIds': vpc_subnet_ids, 'SecurityGroupIds': vpc_security_group_ids}}) # Finally try to create function current_version = None try: if not check_mode: response = client.create_function(**func_kwargs) current_version = response['Version'] changed = True except (ParamValidationError, ClientError) as e: module.fail_json_aws(e, msg="Trying to create function") # Tag Function if tags is not None: if set_tag(client, module, tags, get_current_function(client, name)): changed = True response = get_current_function(client, name, qualifier=current_version) if not response: module.fail_json(msg='Unable to get function information after creating') module.exit_json(changed=changed, **camel_dict_to_snake_dict(response)) # Delete existing Lambda function if state == 'absent' and current_function: try: if not check_mode: client.delete_function(FunctionName=name) changed = True except (ParamValidationError, ClientError) as e: module.fail_json_aws(e, msg="Trying to delete Lambda function") module.exit_json(changed=changed) # Function already absent, do nothing elif state == 'absent': module.exit_json(changed=changed)
def main(): protocols = [ 'http', 'https', 'email', 'email_json', 'sms', 'sqs', 'application', 'lambda', ] argument_spec = dict( msg=dict(required=True, aliases=['default']), subject=dict(), topic=dict(required=True), message_attributes=dict(type='dict'), message_structure=dict(choices=['json', 'string'], default='json'), ) for p in protocols: argument_spec[p] = dict() module = AnsibleAWSModule(argument_spec=argument_spec) sns_kwargs = dict( Message=module.params['msg'], Subject=module.params['subject'], MessageStructure=module.params['message_structure'], ) if module.params['message_attributes']: if module.params['message_structure'] != 'string': module.fail_json(msg='message_attributes is only supported when the message_structure is "string".') sns_kwargs['MessageAttributes'] = module.params['message_attributes'] dict_msg = { 'default': sns_kwargs['Message'] } for p in protocols: if module.params[p]: if sns_kwargs['MessageStructure'] != 'json': module.fail_json(msg='Protocol-specific messages are only supported when message_structure is "json".') dict_msg[p.replace('_', '-')] = module.params[p] client = module.client('sns') topic = module.params['topic'] if ':' in topic: # Short names can't contain ':' so we'll assume this is the full ARN sns_kwargs['TopicArn'] = topic else: sns_kwargs['TopicArn'] = arn_topic_lookup(module, client, topic) if not sns_kwargs['TopicArn']: module.fail_json(msg='Could not find topic: {0}'.format(topic)) if sns_kwargs['MessageStructure'] == 'json': sns_kwargs['Message'] = json.dumps(dict_msg) try: result = client.publish(**sns_kwargs) except (BotoCoreError, ClientError) as e: module.fail_json_aws(e, msg='Failed to publish message') module.exit_json(msg='OK', message_id=result['MessageId'])
def main(): arg_spec = dict( state=dict(type='str', required=True, choices=['present', 'absent']), log_group_name=dict(type='str', required=True), filter_name=dict(type='str', required=True), filter_pattern=dict(type='str'), metric_transformation=dict(type='dict', options=dict( metric_name=dict(type='str'), metric_namespace=dict(type='str'), metric_value=dict(type='str'), default_value=dict(type='float'))), ) module = AnsibleAWSModule(argument_spec=arg_spec, supports_check_mode=True, required_if=[ ('state', 'present', ['metric_transformation', 'filter_pattern']) ]) log_group_name = module.params.get("log_group_name") filter_name = module.params.get("filter_name") filter_pattern = module.params.get("filter_pattern") metric_transformation = module.params.get("metric_transformation") state = module.params.get("state") cwl = module.client('logs') # check if metric filter exists response = cwl.describe_metric_filters(logGroupName=log_group_name, filterNamePrefix=filter_name) if len(response.get("metricFilters")) == 1: originMetricTransformations = response.get("metricFilters")[0].get( "metricTransformations")[0] originFilterPattern = response.get("metricFilters")[0].get( "filterPattern") else: originMetricTransformations = None originFilterPattern = None change = False metricTransformation = None if state == "absent" and originMetricTransformations: if not module.check_mode: response = cwl.delete_metric_filter(logGroupName=log_group_name, filterName=filter_name) change = True metricTransformation = [ camel_dict_to_snake_dict(item) for item in [originMetricTransformations] ] elif state == "present": metricTransformation, change = metricTransformationHandler( metricTransformations=metric_transformation, originMetricTransformations=originMetricTransformations) change = change or filter_pattern != originFilterPattern if change: if not module.check_mode: response = cwl.put_metric_filter( logGroupName=log_group_name, filterName=filter_name, filterPattern=filter_pattern, metricTransformations=metricTransformation) metricTransformation = [ camel_dict_to_snake_dict(item) for item in metricTransformation ] module.exit_json(changed=change, metric_filters=metricTransformation)
def main(): argument_spec = dict(cluster_name=dict(required=True), resource=dict(required=False), tags=dict(type='dict'), purge_tags=dict(type='bool', default=False), state=dict(default='present', choices=['present', 'absent']), resource_type=dict(default='cluster', choices=[ 'cluster', 'task', 'service', 'task_definition', 'container' ])) required_if = [('state', 'present', ['tags']), ('state', 'absent', ['tags'])] module = AnsibleAWSModule(argument_spec=argument_spec, required_if=required_if, supports_check_mode=True) resource_type = module.params['resource_type'] cluster_name = module.params['cluster_name'] if resource_type == 'cluster': resource = cluster_name else: resource = module.params['resource'] tags = module.params['tags'] state = module.params['state'] purge_tags = module.params['purge_tags'] result = {'changed': False} ecs = module.client('ecs') resource_arn = get_arn(ecs, module, cluster_name, resource_type, resource) current_tags = get_tags(ecs, module, resource_arn) add_tags, remove = compare_aws_tags(current_tags, tags, purge_tags=purge_tags) remove_tags = {} if state == 'absent': for key in tags: if key in current_tags and (tags[key] is None or current_tags[key] == tags[key]): remove_tags[key] = current_tags[key] for key in remove: remove_tags[key] = current_tags[key] if remove_tags: result['changed'] = True result['removed_tags'] = remove_tags if not module.check_mode: try: ecs.untag_resource(resourceArn=resource_arn, tagKeys=list(remove_tags.keys())) except (BotoCoreError, ClientError) as e: module.fail_json_aws( e, msg='Failed to remove tags {0} from resource {1}'.format( remove_tags, resource)) if state == 'present' and add_tags: result['changed'] = True result['added_tags'] = add_tags current_tags.update(add_tags) if not module.check_mode: try: tags = ansible_dict_to_boto3_tag_list( add_tags, tag_name_key_name='key', tag_value_key_name='value') ecs.tag_resource(resourceArn=resource_arn, tags=tags) except (BotoCoreError, ClientError) as e: module.fail_json_aws( e, msg='Failed to set tags {0} on resource {1}'.format( add_tags, resource)) result['tags'] = get_tags(ecs, module, resource_arn) module.exit_json(**result)
def main(): module = AnsibleAWSModule( argument_spec={ 'name': dict(required=True), 'state': dict(choices=['present', 'absent'], default='present'), 'description': dict(default=""), 'kms_key_id': dict(), 'secret_type': dict(choices=['binary', 'string'], default="string"), 'secret': dict(default=""), 'tags': dict(type='dict', default={}), 'rotation_lambda': dict(), 'rotation_interval': dict(type='int', default=30), 'recovery_window': dict(type='int', default=30), }, supports_check_mode=True, ) changed = False state = module.params.get('state') secrets_mgr = SecretsManagerInterface(module) recovery_window = module.params.get('recovery_window') secret = Secret(module.params.get('name'), module.params.get('secret_type'), module.params.get('secret'), description=module.params.get('description'), kms_key_id=module.params.get('kms_key_id'), tags=module.params.get('tags'), lambda_arn=module.params.get('rotation_lambda'), rotation_interval=module.params.get('rotation_interval')) current_secret = secrets_mgr.get_secret(secret.name) if state == 'absent': if current_secret: if not current_secret.get("DeletedDate"): result = camel_dict_to_snake_dict( secrets_mgr.delete_secret(secret.name, recovery_window=recovery_window)) changed = True elif current_secret.get("DeletedDate") and recovery_window == 0: result = camel_dict_to_snake_dict( secrets_mgr.delete_secret(secret.name, recovery_window=recovery_window)) changed = True else: result = "secret does not exist" if state == 'present': if current_secret is None: result = secrets_mgr.create_secret(secret) changed = True else: if current_secret.get("DeletedDate"): secrets_mgr.restore_secret(secret.name) changed = True if not secrets_mgr.secrets_match(secret, current_secret): result = secrets_mgr.update_secret(secret) changed = True if not rotation_match(secret, current_secret): result = secrets_mgr.update_rotation(secret) changed = True current_tags = boto3_tag_list_to_ansible_dict( current_secret.get('Tags', [])) tags_to_add, tags_to_remove = compare_aws_tags( current_tags, secret.tags) if tags_to_add: secrets_mgr.tag_secret( secret.name, ansible_dict_to_boto3_tag_list(tags_to_add)) changed = True if tags_to_remove: secrets_mgr.untag_secret(secret.name, tags_to_remove) changed = True result = camel_dict_to_snake_dict(secrets_mgr.get_secret(secret.name)) result.pop("response_metadata") module.exit_json(changed=changed, secret=result)
def main(): argument_spec = dict( name=dict(required=True), cidr_block=dict(type='list', required=True), ipv6_cidr=dict(type='bool', default=False), tenancy=dict(choices=['default', 'dedicated'], default='default'), dns_support=dict(type='bool', default=True), dns_hostnames=dict(type='bool', default=True), dhcp_opts_id=dict(), tags=dict(type='dict', aliases=['resource_tags']), state=dict(choices=['present', 'absent'], default='present'), multi_ok=dict(type='bool', default=False), purge_cidrs=dict(type='bool', default=False), ) module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True) name = module.params.get('name') cidr_block = get_cidr_network_bits(module, module.params.get('cidr_block')) ipv6_cidr = module.params.get('ipv6_cidr') purge_cidrs = module.params.get('purge_cidrs') tenancy = module.params.get('tenancy') dns_support = module.params.get('dns_support') dns_hostnames = module.params.get('dns_hostnames') dhcp_id = module.params.get('dhcp_opts_id') tags = module.params.get('tags') state = module.params.get('state') multi = module.params.get('multi_ok') changed = False connection = module.client( 'ec2', retry_decorator=AWSRetry.jittered_backoff( retries=8, delay=3, catch_extra_error_codes=['InvalidVpcID.NotFound'])) if dns_hostnames and not dns_support: module.fail_json( msg= 'In order to enable DNS Hostnames you must also enable DNS support' ) if state == 'present': # Check if VPC exists vpc_id = vpc_exists(module, connection, name, cidr_block, multi) if vpc_id is None: vpc_id = create_vpc(connection, module, cidr_block[0], tenancy) changed = True vpc_obj = get_vpc(module, connection, vpc_id) associated_cidrs = dict( (cidr['CidrBlock'], cidr['AssociationId']) for cidr in vpc_obj.get('CidrBlockAssociationSet', []) if cidr['CidrBlockState']['State'] != 'disassociated') to_add = [cidr for cidr in cidr_block if cidr not in associated_cidrs] to_remove = [ associated_cidrs[cidr] for cidr in associated_cidrs if cidr not in cidr_block ] expected_cidrs = [ cidr for cidr in associated_cidrs if associated_cidrs[cidr] not in to_remove ] + to_add if len(cidr_block) > 1: for cidr in to_add: changed = True try: connection.associate_vpc_cidr_block(CidrBlock=cidr, VpcId=vpc_id) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws( e, "Unable to associate CIDR {0}.".format(ipv6_cidr)) if ipv6_cidr: if 'Ipv6CidrBlockAssociationSet' in vpc_obj.keys(): module.warn( "Only one IPv6 CIDR is permitted per VPC, {0} already has CIDR {1}" .format( vpc_id, vpc_obj['Ipv6CidrBlockAssociationSet'][0] ['Ipv6CidrBlock'])) else: try: connection.associate_vpc_cidr_block( AmazonProvidedIpv6CidrBlock=ipv6_cidr, VpcId=vpc_id) changed = True except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws( e, "Unable to associate CIDR {0}.".format(ipv6_cidr)) if purge_cidrs: for association_id in to_remove: changed = True try: connection.disassociate_vpc_cidr_block( AssociationId=association_id) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws( e, "Unable to disassociate {0}. You must detach or delete all gateways and resources that " "are associated with the CIDR block before you can disassociate it." .format(association_id)) if dhcp_id is not None: try: if update_dhcp_opts(connection, module, vpc_obj, dhcp_id): changed = True except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, "Failed to update DHCP options") if tags is not None or name is not None: try: if update_vpc_tags(connection, module, vpc_id, tags, name): changed = True except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Failed to update tags") current_dns_enabled = connection.describe_vpc_attribute( Attribute='enableDnsSupport', VpcId=vpc_id, aws_retry=True)['EnableDnsSupport']['Value'] current_dns_hostnames = connection.describe_vpc_attribute( Attribute='enableDnsHostnames', VpcId=vpc_id, aws_retry=True)['EnableDnsHostnames']['Value'] if current_dns_enabled != dns_support: changed = True if not module.check_mode: try: connection.modify_vpc_attribute( VpcId=vpc_id, EnableDnsSupport={'Value': dns_support}) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws( e, "Failed to update enabled dns support attribute") if current_dns_hostnames != dns_hostnames: changed = True if not module.check_mode: try: connection.modify_vpc_attribute( VpcId=vpc_id, EnableDnsHostnames={'Value': dns_hostnames}) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws( e, "Failed to update enabled dns hostnames attribute") # wait for associated cidrs to match if to_add or to_remove: try: connection.get_waiter('vpc_available').wait( VpcIds=[vpc_id], Filters=[{ 'Name': 'cidr-block-association.cidr-block', 'Values': expected_cidrs }]) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, "Failed to wait for CIDRs to update") # try to wait for enableDnsSupport and enableDnsHostnames to match wait_for_vpc_attribute(connection, module, vpc_id, 'enableDnsSupport', dns_support) wait_for_vpc_attribute(connection, module, vpc_id, 'enableDnsHostnames', dns_hostnames) final_state = camel_dict_to_snake_dict( get_vpc(module, connection, vpc_id)) final_state['tags'] = boto3_tag_list_to_ansible_dict( final_state.get('tags', [])) final_state['id'] = final_state.pop('vpc_id') module.exit_json(changed=changed, vpc=final_state) elif state == 'absent': # Check if VPC exists vpc_id = vpc_exists(module, connection, name, cidr_block, multi) if vpc_id is not None: try: if not module.check_mode: connection.delete_vpc(VpcId=vpc_id) changed = True except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws( e, msg= "Failed to delete VPC {0} You may want to use the ec2_vpc_subnet, ec2_vpc_igw, " "and/or ec2_vpc_route_table modules to ensure the other components are absent." .format(vpc_id)) module.exit_json(changed=changed, vpc={})
def main(): argument_spec = dict( stack_name=dict(), all_facts=dict(required=False, default=False, type='bool'), stack_policy=dict(required=False, default=False, type='bool'), stack_events=dict(required=False, default=False, type='bool'), stack_resources=dict(required=False, default=False, type='bool'), stack_template=dict(required=False, default=False, type='bool'), stack_change_sets=dict(required=False, default=False, type='bool'), ) module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True) is_old_facts = module._name == 'cloudformation_facts' if is_old_facts: module.deprecate( "The 'cloudformation_facts' module has been renamed to 'cloudformation_info', " "and the renamed one no longer returns ansible_facts", version='2.13') service_mgr = CloudFormationServiceManager(module) if is_old_facts: result = {'ansible_facts': {'cloudformation': {}}} else: result = {'cloudformation': {}} for stack_description in service_mgr.describe_stacks( module.params.get('stack_name')): facts = {'stack_description': stack_description} stack_name = stack_description.get('StackName') # Create stack output and stack parameter dictionaries if facts['stack_description']: facts['stack_outputs'] = to_dict( facts['stack_description'].get('Outputs'), 'OutputKey', 'OutputValue') facts['stack_parameters'] = to_dict( facts['stack_description'].get('Parameters'), 'ParameterKey', 'ParameterValue') facts['stack_tags'] = boto3_tag_list_to_ansible_dict( facts['stack_description'].get('Tags')) # Create optional stack outputs all_facts = module.params.get('all_facts') if all_facts or module.params.get('stack_resources'): facts['stack_resource_list'] = service_mgr.list_stack_resources( stack_name) facts['stack_resources'] = to_dict( facts.get('stack_resource_list'), 'LogicalResourceId', 'PhysicalResourceId') if all_facts or module.params.get('stack_template'): facts['stack_template'] = service_mgr.get_template(stack_name) if all_facts or module.params.get('stack_policy'): facts['stack_policy'] = service_mgr.get_stack_policy(stack_name) if all_facts or module.params.get('stack_events'): facts['stack_events'] = service_mgr.describe_stack_events( stack_name) if all_facts or module.params.get('stack_change_sets'): facts[ 'stack_change_sets'] = service_mgr.describe_stack_change_sets( stack_name) if is_old_facts: result['ansible_facts']['cloudformation'][stack_name] = facts else: result['cloudformation'][stack_name] = camel_dict_to_snake_dict( facts, ignore_list=('stack_outputs', 'stack_parameters', 'stack_policy', 'stack_resources', 'stack_tags', 'stack_template')) module.exit_json(changed=False, **result)
def main(): argument_spec = dict( 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."), cache_enabled=dict(type='bool', default=False), cache_size=dict(type='str', default='0.5', choices=[ '0.5', '1.6', '6.1', '13.5', '28.4', '58.2', '118', '237' ]), stage_variables=dict(type='dict', default={}), stage_canary_settings=dict(type='dict', default={}), tracing_enabled=dict(type='bool', default=False), endpoint_type=dict(type='str', default='EDGE', choices=['EDGE', 'REGIONAL', 'PRIVATE'])) mutually_exclusive = [['swagger_file', 'swagger_dict', 'swagger_text']] # noqa: F841 module = AnsibleAWSModule( 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') endpoint_type = module.params.get('endpoint_type') client = module.client('apigateway') 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, endpoint_type) 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_data) 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 = dict(app_name=dict(aliases=['name'], type='str', required=False), description=dict(), state=dict(choices=['present', 'absent'], default='present'), terminate_by_force=dict(type='bool', default=False, required=False)) module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True) app_name = module.params['app_name'] description = module.params['description'] state = module.params['state'] terminate_by_force = module.params['terminate_by_force'] if app_name is None: module.fail_json(msg='Module parameter "app_name" is required') result = {} ebs = module.client('elasticbeanstalk') app = describe_app(ebs, app_name, module) if module.check_mode: check_app(ebs, app, module) module.fail_json( msg='ASSERTION FAILURE: check_app() should not return control.') if state == 'present': if app is None: try: create_app = ebs.create_application(**filter_empty( ApplicationName=app_name, Description=description)) except (BotoCoreError, ClientError) as e: module.fail_json_aws(e, msg="Could not create application") app = describe_app(ebs, app_name, module) result = dict(changed=True, app=app) else: if app.get("Description", None) != description: try: if not description: ebs.update_application(ApplicationName=app_name) else: ebs.update_application(ApplicationName=app_name, Description=description) except (BotoCoreError, ClientError) as e: module.fail_json_aws(e, msg="Could not update application") app = describe_app(ebs, app_name, module) result = dict(changed=True, app=app) else: result = dict(changed=False, app=app) else: if app is None: result = dict(changed=False, output='Application not found', app={}) else: try: if terminate_by_force: # Running environments will be terminated before deleting the application ebs.delete_application( ApplicationName=app_name, TerminateEnvByForce=terminate_by_force) else: ebs.delete_application(ApplicationName=app_name) changed = True except BotoCoreError as e: module.fail_json_aws(e, msg="Cannot terminate app") except ClientError as e: if 'It is currently pending deletion.' not in e.response[ 'Error']['Message']: module.fail_json_aws(e, msg="Cannot terminate app") else: changed = False result = dict(changed=changed, app=app) module.exit_json(**result)
def main(): argument_spec = dict( state=dict(choices=['present', 'absent'], default='present'), endpointidentifier=dict(required=True), endpointtype=dict(choices=['source', 'target'], required=True), enginename=dict(choices=[ 'mysql', 'oracle', 'postgres', 'mariadb', 'aurora', 'redshift', 's3', 'db2', 'azuredb', 'sybase', 'dynamodb', 'mongodb', 'sqlserver' ], required=True), username=dict(), password=dict(no_log=True), servername=dict(), port=dict(type='int'), databasename=dict(), extraconnectionattributes=dict(), kmskeyid=dict(), tags=dict(type='dict'), certificatearn=dict(), sslmode=dict(choices=['none', 'require', 'verify-ca', 'verify-full'], default='none'), serviceaccessrolearn=dict(), externaltabledefinition=dict(), dynamodbsettings=dict(type='dict'), s3settings=dict(type='dict'), dmstransfersettings=dict(type='dict'), mongodbsettings=dict(type='dict'), kinesissettings=dict(type='dict'), elasticsearchsettings=dict(type='dict'), wait=dict(type='bool', default=False), timeout=dict(type='int'), retries=dict(type='int')) global module module = AnsibleAWSModule(argument_spec=argument_spec, required_if=[ ["state", "absent", ["wait"]], ["wait", "True", ["timeout"]], ["wait", "True", ["retries"]], ], supports_check_mode=False) exit_message = None changed = False state = module.params.get('state') dmsclient = module.client('dms') endpoint = describe_endpoints(dmsclient, module.params.get('endpointidentifier')) if state == 'present': if endpoint_exists(endpoint): module.params['EndpointArn'] = \ endpoint['Endpoints'][0].get('EndpointArn') params_changed = compare_params(endpoint["Endpoints"][0]) if params_changed: updated_dms = modify_dms_endpoint(dmsclient) exit_message = updated_dms changed = True else: module.exit_json(changed=False, msg="Endpoint Already Exists") else: dms_properties = create_dms_endpoint(dmsclient) exit_message = dms_properties changed = True elif state == 'absent': if endpoint_exists(endpoint): delete_results = delete_dms_endpoint(dmsclient) exit_message = delete_results changed = True else: changed = False exit_message = 'DMS Endpoint does not exist' module.exit_json(changed=changed, msg=exit_message)
def main(): argument_spec = dict(device_id=dict(required=False, aliases=['instance_id']), public_ip=dict(required=False, aliases=['ip']), state=dict(required=False, default='present', choices=['present', 'absent']), in_vpc=dict(required=False, type='bool', default=False), reuse_existing_ip_allowed=dict(required=False, type='bool', default=False), release_on_disassociation=dict(required=False, type='bool', default=False), allow_reassociation=dict(type='bool', default=False), wait_timeout=dict(type='int', removed_in_version='2.14'), private_ip_address=dict(), tag_name=dict(), tag_value=dict(), public_ipv4_pool=dict()) module = AnsibleAWSModule( argument_spec=argument_spec, supports_check_mode=True, required_by={ 'private_ip_address': ['device_id'], }, ) ec2 = module.client('ec2', retry_decorator=AWSRetry.jittered_backoff()) device_id = module.params.get('device_id') instance_id = module.params.get('instance_id') public_ip = module.params.get('public_ip') private_ip_address = module.params.get('private_ip_address') state = module.params.get('state') in_vpc = module.params.get('in_vpc') domain = 'vpc' if in_vpc else None reuse_existing_ip_allowed = module.params.get('reuse_existing_ip_allowed') release_on_disassociation = module.params.get('release_on_disassociation') allow_reassociation = module.params.get('allow_reassociation') tag_name = module.params.get('tag_name') tag_value = module.params.get('tag_value') public_ipv4_pool = module.params.get('public_ipv4_pool') if instance_id: warnings = [ "instance_id is no longer used, please use device_id going forward" ] is_instance = True device_id = instance_id else: if device_id and device_id.startswith('i-'): is_instance = True elif device_id: if device_id.startswith('eni-') and not in_vpc: module.fail_json( msg="If you are specifying an ENI, in_vpc must be true") is_instance = False tag_dict = generate_tag_dict(module, tag_name, tag_value) try: if device_id: address = find_address(ec2, module, public_ip, device_id, is_instance=is_instance) else: address = find_address(ec2, module, public_ip, None) if state == 'present': if device_id: result = ensure_present(ec2, module, domain, address, private_ip_address, device_id, reuse_existing_ip_allowed, allow_reassociation, module.check_mode, is_instance=is_instance) else: if address: changed = False else: address, changed = allocate_address( ec2, module, domain, reuse_existing_ip_allowed, module.check_mode, tag_dict, public_ipv4_pool) result = { 'changed': changed, 'public_ip': address['PublicIp'], 'allocation_id': address['AllocationId'] } else: if device_id: disassociated = ensure_absent(ec2, module, address, device_id, module.check_mode, is_instance=is_instance) if release_on_disassociation and disassociated['changed']: released = release_address(ec2, module, address, module.check_mode) result = { 'changed': True, 'disassociated': disassociated, 'released': released } else: result = { 'changed': disassociated['changed'], 'disassociated': disassociated, 'released': { 'changed': False } } else: released = release_address(ec2, module, address, module.check_mode) result = { 'changed': released['changed'], 'disassociated': { 'changed': False }, 'released': released } except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: module.fail_json_aws(str(e)) if instance_id: result['warnings'] = warnings module.exit_json(**result)