def main(): module = AnsibleAWSModule( argument_spec={}, supports_check_mode=True, ) if module._name == 'aws_caller_facts': module.deprecate( "The 'aws_caller_facts' module has been renamed to 'aws_caller_info'", version='2.13') client = module.client('sts') try: caller_info = client.get_caller_identity() caller_info.pop('ResponseMetadata', None) except (BotoCoreError, ClientError) as e: module.fail_json_aws(e, msg='Failed to retrieve caller identity') iam_client = module.client('iam') try: # Although a list is returned by list_account_aliases AWS supports maximum one alias per account. # If an alias is defined it will be returned otherwise a blank string is filled in as account_alias. # see https://docs.aws.amazon.com/cli/latest/reference/iam/list-account-aliases.html#output response = iam_client.list_account_aliases() if response and response['AccountAliases']: caller_info['account_alias'] = response['AccountAliases'][0] else: caller_info['account_alias'] = '' except (BotoCoreError, ClientError) as e: # The iam:ListAccountAliases permission is required for this operation to succeed. # Lacking this permission is handled gracefully by not returning the account_alias. pass module.exit_json(changed=False, **camel_dict_to_snake_dict(caller_info))
def main(): argument_spec = dict(filters=dict(default={}, type='dict')) module = AnsibleAWSModule(argument_spec=argument_spec) if module._name == 'aws_az_facts': module.deprecate( "The 'aws_az_facts' module has been renamed to 'aws_az_info'", version='2.14') connection = module.client('ec2', retry_decorator=AWSRetry.jittered_backoff()) # Replace filter key underscores with dashes, for compatibility sanitized_filters = dict((k.replace('_', '-'), v) for k, v in module.params.get('filters').items()) try: availability_zones = connection.describe_availability_zones( Filters=ansible_dict_to_boto3_filter_list(sanitized_filters)) except (BotoCoreError, ClientError) as e: module.fail_json_aws(e, msg="Unable to describe availability zones.") # Turn the boto3 result into ansible_friendly_snaked_names snaked_availability_zones = [ camel_dict_to_snake_dict(az) for az in availability_zones['AvailabilityZones'] ] module.exit_json(availability_zones=snaked_availability_zones)
def main(): argument_spec = dict(iam_type=dict(required=True, choices=['user', 'group', 'role']), state=dict(default='present', choices=['present', 'absent']), iam_name=dict(required=True), policy_name=dict(required=True), policy_document=dict(default=None, required=False), policy_json=dict(type='json', default=None, required=False), skip_duplicates=dict(type='bool', default=None, required=False)) mutually_exclusive = [['policy_document', 'policy_json']] module = AnsibleAWSModule(argument_spec=argument_spec, mutually_exclusive=mutually_exclusive, supports_check_mode=True) skip_duplicates = module.params.get('skip_duplicates') if (skip_duplicates is None): module.deprecate( 'The skip_duplicates behaviour has caused confusion and' ' will be disabled by default in Ansible 2.14', version='2.14') skip_duplicates = True if module.params.get('policy_document'): module.deprecate( 'The policy_document option has been deprecated and' ' will be removed in Ansible 2.14', version='2.14') args = dict( client=module.client('iam'), name=module.params.get('iam_name'), policy_name=module.params.get('policy_name'), policy_document=module.params.get('policy_document'), policy_json=module.params.get('policy_json'), skip_duplicates=skip_duplicates, state=module.params.get('state'), check_mode=module.check_mode, ) iam_type = module.params.get('iam_type') try: if iam_type == 'user': policy = UserPolicy(**args) elif iam_type == 'role': policy = RolePolicy(**args) elif iam_type == 'group': policy = GroupPolicy(**args) module.exit_json(**(policy.run())) except (BotoCoreError, ClientError) as e: module.fail_json_aws(e) except PolicyError as e: module.fail_json(msg=str(e))
def main(): argument_spec = dict( iam_type=dict(required=True, choices=['user', 'group', 'role']), iam_name=dict(required=True), policy_name=dict(default=None, required=False), ) module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True) args = dict( client=module.client('iam'), name=module.params.get('iam_name'), policy_name=module.params.get('policy_name'), ) iam_type = module.params.get('iam_type') try: if iam_type == 'user': policy = UserPolicy(**args) elif iam_type == 'role': policy = RolePolicy(**args) elif iam_type == 'group': policy = GroupPolicy(**args) module.exit_json(**(policy.run())) except (BotoCoreError, ClientError) as e: if e.response['Error']['Code'] == 'NoSuchEntity': module.exit_json(changed=False, msg=e.response['Error']['Message']) module.fail_json_aws(e) except PolicyError as e: module.fail_json(msg=str(e))
def main(): argument_spec = dict( filters=dict(default={}, type='dict') ) module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True) if module._name == 'ec2_group_facts': module.deprecate("The 'ec2_group_facts' module has been renamed to 'ec2_group_info'", version='2.13') connection = module.client('ec2') # Replace filter key underscores with dashes, for compatibility, except if we're dealing with tags sanitized_filters = module.params.get("filters") for key in list(sanitized_filters): if not key.startswith("tag:"): sanitized_filters[key.replace("_", "-")] = sanitized_filters.pop(key) try: security_groups = connection.describe_security_groups( Filters=ansible_dict_to_boto3_filter_list(sanitized_filters) ) except (BotoCoreError, ClientError) as e: module.fail_json_aws(e, msg='Failed to describe security groups') snaked_security_groups = [] for security_group in security_groups['SecurityGroups']: # Modify boto3 tags list to be ansible friendly dict # but don't camel case tags security_group = camel_dict_to_snake_dict(security_group) security_group['tags'] = boto3_tag_list_to_ansible_dict(security_group.get('tags', {}), tag_name_key_name='key', tag_value_key_name='value') snaked_security_groups.append(security_group) module.exit_json(security_groups=snaked_security_groups)
def main(): argument_spec = dict( resource=dict(required=True), ) module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True) resource = module.params['resource'] ec2 = module.client('ec2') try: current_tags = get_tags(ec2, module, resource) except (BotoCoreError, ClientError) as e: module.fail_json_aws(e, msg='Failed to fetch tags for resource {0}'.format(resource)) module.exit_json(changed=False, tags=current_tags)
def main(): argument_spec = dict() result = dict(changed=False, original_message='') module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=False) cloudformation_client = module.client('cloudformation') try: result['export_items'] = list_exports(cloudformation_client) except (ClientError, BotoCoreError) as e: module.fail_json_aws(e) result.update() module.exit_json(**result)
def main(): argument_spec = ec2_argument_spec() argument_spec.update( dict(state=dict(required=True, choices=['present', 'absent']), id_to_associate=dict( required=True, aliases=['link_aggregation_group_id', 'connection_id']), public=dict(type='bool'), name=dict(), vlan=dict(type='int', default=100), bgp_asn=dict(type='int', default=65000), authentication_key=dict(), amazon_address=dict(), customer_address=dict(), address_type=dict(), cidr=dict(type='list'), virtual_gateway_id=dict(), virtual_interface_id=dict())) module = AnsibleAWSModule( argument_spec=argument_spec, required_one_of=[['virtual_interface_id', 'name']], required_if=[['state', 'present', ['public']], ['public', False, ['virtual_gateway_id']], ['public', True, ['amazon_address']], ['public', True, ['customer_address']], ['public', True, ['cidr']]]) region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module, boto3=True) connection = boto3_conn(module, conn_type='client', resource='directconnect', region=region, endpoint=ec2_url, **aws_connect_kwargs) try: changed, latest_state = ensure_state(connection, module) except DirectConnectError as e: if e.exception: module.fail_json_aws(exception=e.exception, msg=e.msg) else: module.fail_json(msg=e.msg) module.exit_json(changed=changed, **camel_dict_to_snake_dict(latest_state))
def test_botocore_exception_reports_nicely_via_fail_json_aws(self): basic._ANSIBLE_ARGS = to_bytes( json.dumps({ 'ANSIBLE_MODULE_ARGS': { '_ansible_tmpdir': '/tmp/ansible-abc' } })) module = AnsibleAWSModule(argument_spec=dict( fail_mode=dict(type='list', default=['success']))) fail_json_double = Mock() err_msg = {'Error': {'Code': 'FakeClass.FakeError'}} with patch.object(basic.AnsibleModule, 'fail_json', fail_json_double): try: raise botocore.exceptions.ClientError(err_msg, 'Could not find you') except Exception as e: print("exception is " + str(e)) module.fail_json_aws( e, msg="Fake failure for testing boto exception messages") assert (len(fail_json_double.mock_calls) > 0), "failed to call fail_json when should have" assert (len(fail_json_double.mock_calls) < 2), "called fail_json multiple times when once would do" assert("test_botocore_exception_reports_nicely" in fail_json_double.mock_calls[0][2]["exception"]), \ "exception traceback doesn't include correct function, fail call was actually: " \ + str(fail_json_double.mock_calls[0]) assert("Fake failure for testing boto exception messages:" in fail_json_double.mock_calls[0][2]["msg"]), \ "error message doesn't include the local message; was: " \ + str(fail_json_double.mock_calls[0]) assert("Could not find you" in fail_json_double.mock_calls[0][2]["msg"]), \ "error message doesn't include the botocore exception message; was: " \ + str(fail_json_double.mock_calls[0]) try: fail_json_double.mock_calls[0][2]["error"] except KeyError: raise Exception("error was missing; call was: " + str(fail_json_double.mock_calls[0])) assert("FakeClass.FakeError" == fail_json_double.mock_calls[0][2]["error"]["code"]), \ "Failed to find error/code; was: " + str(fail_json_double.mock_calls[0])
def main(): argument_spec = dict(names={'default': [], 'type': 'list'}) module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True) if module._name == 'elb_classic_lb_facts': module.deprecate( "The 'elb_classic_lb_facts' module has been renamed to 'elb_classic_lb_info'", version='2.13') connection = module.client('elb') try: elbs = list_elbs(connection, module.params.get('names')) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Failed to get load balancer information.") module.exit_json(elbs=elbs)
def main(): argument_spec = dict( state=dict(type='str', default='present', choices=['present', 'absent']), name=dict(type='str', required=True), queue_type=dict(type='str', default='standard', choices=['standard', 'fifo']), delay_seconds=dict(type='int', aliases=['delivery_delay']), maximum_message_size=dict(type='int'), message_retention_period=dict(type='int'), policy=dict(type='dict'), receive_message_wait_time_seconds=dict( type='int', aliases=['receive_message_wait_time']), redrive_policy=dict(type='dict'), visibility_timeout=dict(type='int', aliases=['default_visibility_timeout']), kms_master_key_id=dict(type='str'), kms_data_key_reuse_period_seconds=dict( type='int', aliases=['kms_data_key_reuse_period']), content_based_deduplication=dict(type='bool'), tags=dict(type='dict'), purge_tags=dict(type='bool', default=False), ) module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True) state = module.params.get('state') retry_decorator = AWSRetry.jittered_backoff( catch_extra_error_codes=['AWS.SimpleQueueService.NonExistentQueue']) try: client = module.client('sqs', retry_decorator=retry_decorator) if state == 'present': result = create_or_update_sqs_queue(client, module) elif state == 'absent': result = delete_sqs_queue(client, module) except (BotoCoreError, ClientError, ParamValidationError) as e: module.fail_json_aws(e, msg='Failed to control sqs queue') else: module.exit_json(**result)
def main(): argument_spec = dict( state=dict(type='str', default='present', choices=['present', 'absent']), filters=dict(type='dict', default={}), vpn_gateway_id=dict(type='str'), tags=dict(default={}, type='dict'), connection_type=dict(default='ipsec.1', type='str'), tunnel_options=dict(no_log=True, type='list', default=[]), static_only=dict(default=False, type='bool'), customer_gateway_id=dict(type='str'), vpn_connection_id=dict(type='str'), purge_tags=dict(type='bool', default=False), routes=dict(type='list', default=[]), purge_routes=dict(type='bool', default=False), wait_timeout=dict(type='int', default=600), delay=dict(type='int', default=15), ) module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True) connection = module.client('ec2') state = module.params.get('state') parameters = dict(module.params) try: if state == 'present': changed, response = ensure_present(connection, parameters, module.check_mode) elif state == 'absent': changed, response = ensure_absent(connection, parameters, module.check_mode) except VPNConnectionException as e: if e.exception: module.fail_json_aws(e.exception, msg=e.msg) else: module.fail_json(msg=e.msg) module.exit_json(changed=changed, **camel_dict_to_snake_dict(response))
def main(): module_args = dict( name=dict(type='str', required=True), definition=dict(type='json'), role_arn=dict(type='str'), state=dict(choices=['present', 'absent'], default='present'), tags=dict(default=None, type='dict'), purge_tags=dict(default=True, type='bool'), ) module = AnsibleAWSModule( argument_spec=module_args, required_if=[('state', 'present', ['role_arn']), ('state', 'present', ['definition'])], supports_check_mode=True ) sfn_client = module.client('stepfunctions', retry_decorator=AWSRetry.jittered_backoff(retries=5)) state = module.params.get('state') try: manage_state_machine(state, sfn_client, module) except (BotoCoreError, ClientError) as e: module.fail_json_aws(e, msg='Failed to manage state machine')
def main(): argument_spec = dict( az=dict(default=None, required=False), cidr=dict(required=True), ipv6_cidr=dict(default='', required=False), state=dict(default='present', choices=['present', 'absent']), tags=dict(default={}, required=False, type='dict', aliases=['resource_tags']), vpc_id=dict(required=True), map_public=dict(default=False, required=False, type='bool'), assign_instances_ipv6=dict(default=False, required=False, type='bool'), wait=dict(type='bool', default=True), wait_timeout=dict(type='int', default=300, required=False), purge_tags=dict(default=True, type='bool') ) required_if = [('assign_instances_ipv6', True, ['ipv6_cidr'])] module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True, required_if=required_if) if module.params.get('assign_instances_ipv6') and not module.params.get('ipv6_cidr'): module.fail_json(msg="assign_instances_ipv6 is True but ipv6_cidr is None or an empty string") if not module.botocore_at_least("1.7.0"): module.warn("botocore >= 1.7.0 is required to use wait_timeout for custom wait times") connection = module.client('ec2') state = module.params.get('state') try: if state == 'present': result = ensure_subnet_present(connection, module) elif state == 'absent': result = ensure_subnet_absent(connection, module) except botocore.exceptions.ClientError as e: module.fail_json_aws(e) module.exit_json(**result)
def main(): argument_spec = dict(filters=dict(default={}, type='dict')) module = AnsibleAWSModule(argument_spec=argument_spec) if module._name == 'aws_region_facts': module.deprecate( "The 'aws_region_facts' module has been renamed to 'aws_region_info'", version='2.13') connection = module.client('ec2', retry_decorator=AWSRetry.jittered_backoff()) # Replace filter key underscores with dashes, for compatibility sanitized_filters = dict((k.replace('_', '-'), v) for k, v in module.params.get('filters').items()) try: regions = connection.describe_regions( Filters=ansible_dict_to_boto3_filter_list(sanitized_filters)) except (BotoCoreError, ClientError) as e: module.fail_json_aws(e, msg="Unable to describe regions.") module.exit_json( regions=[camel_dict_to_snake_dict(r) for r in regions['Regions']])
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( 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(): 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, ) if not HAS_BOTO3: module.fail_json(msg='boto3 and botocore are required for this module') 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(): 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(): argument_spec = dict( name=dict(required=True), description=dict(), wait=dict(type='bool', default=False), wait_timeout=dict(type='int', default=900), state=dict(default='present', choices=['present', 'absent']), purge_stacks=dict(type='bool', default=True), parameters=dict(type='dict', default={}), template=dict(type='path'), template_url=dict(), template_body=dict(), capabilities=dict(type='list', choices=['CAPABILITY_IAM', 'CAPABILITY_NAMED_IAM']), regions=dict(type='list'), accounts=dict(type='list'), failure_tolerance=dict( type='dict', default={}, options=dict( fail_count=dict(type='int'), fail_percentage=dict(type='int'), parallel_percentage=dict(type='int'), parallel_count=dict(type='int'), ), mutually_exclusive=[ ['fail_count', 'fail_percentage'], ['parallel_count', 'parallel_percentage'], ], ), administration_role_arn=dict( aliases=['admin_role_arn', 'administration_role', 'admin_role']), execution_role_name=dict( aliases=['execution_role', 'exec_role', 'exec_role_name']), tags=dict(type='dict'), ) module = AnsibleAWSModule( argument_spec=argument_spec, mutually_exclusive=[['template_url', 'template', 'template_body']], supports_check_mode=True) if not (module.boto3_at_least('1.6.0') and module.botocore_at_least('1.10.26')): module.fail_json( msg= "Boto3 or botocore version is too low. This module requires at least boto3 1.6 and botocore 1.10.26" ) # Wrap the cloudformation client methods that this module uses with # automatic backoff / retry for throttling error codes cfn = module.client('cloudformation', retry_decorator=AWSRetry.jittered_backoff( retries=10, delay=3, max_delay=30)) existing_stack_set = stack_set_facts(cfn, module.params['name']) operation_uuid = to_native(uuid.uuid4()) operation_ids = [] # collect the parameters that are passed to boto3. Keeps us from having so many scalars floating around. stack_params = {} state = module.params['state'] if state == 'present' and not module.params['accounts']: module.fail_json( msg= "Can't create a stack set without choosing at least one account. " "To get the ID of the current account, use the aws_caller_info module." ) module.params['accounts'] = [ to_native(a) for a in module.params['accounts'] ] stack_params['StackSetName'] = module.params['name'] if module.params.get('description'): stack_params['Description'] = module.params['description'] if module.params.get('capabilities'): stack_params['Capabilities'] = module.params['capabilities'] if module.params['template'] is not None: with open(module.params['template'], 'r') as tpl: stack_params['TemplateBody'] = tpl.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'] else: # no template is provided, but if the stack set exists already, we can use the existing one. if existing_stack_set: stack_params['UsePreviousTemplate'] = True else: module.fail_json( msg= "The Stack Set {0} does not exist, and no template was provided. Provide one of `template`, " "`template_body`, or `template_url`".format( module.params['name'])) stack_params['Parameters'] = [] for k, v in module.params['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'] = to_native(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 module.params.get('tags') and isinstance(module.params.get('tags'), dict): stack_params['Tags'] = ansible_dict_to_boto3_tag_list( module.params['tags']) if module.params.get('administration_role_arn'): # TODO loosen the semantics here to autodetect the account ID and build the ARN stack_params['AdministrationRoleARN'] = module.params[ 'administration_role_arn'] if module.params.get('execution_role_name'): stack_params['ExecutionRoleName'] = module.params[ 'execution_role_name'] result = {} if module.check_mode: if state == 'absent' and existing_stack_set: module.exit_json(changed=True, msg='Stack set would be deleted', meta=[]) elif state == 'absent' and not existing_stack_set: module.exit_json(changed=False, msg='Stack set doesn\'t exist', meta=[]) elif state == 'present' and not existing_stack_set: module.exit_json(changed=True, msg='New stack set would be created', meta=[]) elif state == 'present' and existing_stack_set: new_stacks, existing_stacks, unspecified_stacks = compare_stack_instances( cfn, module.params['name'], module.params['accounts'], module.params['regions'], ) if new_stacks: module.exit_json(changed=True, msg='New stack instance(s) would be created', meta=[]) elif unspecified_stacks and module.params.get( 'purge_stack_instances'): module.exit_json(changed=True, msg='Old stack instance(s) would be deleted', meta=[]) else: # TODO: need to check the template and other settings for correct check mode module.exit_json(changed=False, msg='No changes detected', meta=[]) changed = False if state == 'present': if not existing_stack_set: # on create this parameter has a different name, and cannot be referenced later in the job log stack_params[ 'ClientRequestToken'] = 'Ansible-StackSet-Create-{0}'.format( operation_uuid) changed = True create_stack_set(module, stack_params, cfn) else: stack_params['OperationId'] = 'Ansible-StackSet-Update-{0}'.format( operation_uuid) operation_ids.append(stack_params['OperationId']) if module.params.get('regions'): stack_params[ 'OperationPreferences'] = get_operation_preferences(module) changed |= update_stack_set(module, stack_params, cfn) # now create/update any appropriate stack instances new_stack_instances, existing_stack_instances, unspecified_stack_instances = compare_stack_instances( cfn, module.params['name'], module.params['accounts'], module.params['regions'], ) if new_stack_instances: operation_ids.append( 'Ansible-StackInstance-Create-{0}'.format(operation_uuid)) changed = True cfn.create_stack_instances( StackSetName=module.params['name'], Accounts=list(set(acct for acct, region in new_stack_instances)), Regions=list( set(region for acct, region in new_stack_instances)), OperationPreferences=get_operation_preferences(module), OperationId=operation_ids[-1], ) else: operation_ids.append( 'Ansible-StackInstance-Update-{0}'.format(operation_uuid)) cfn.update_stack_instances( StackSetName=module.params['name'], Accounts=list( set(acct for acct, region in existing_stack_instances)), Regions=list( set(region for acct, region in existing_stack_instances)), OperationPreferences=get_operation_preferences(module), OperationId=operation_ids[-1], ) for op in operation_ids: await_stack_set_operation( module, cfn, operation_id=op, stack_set_name=module.params['name'], max_wait=module.params.get('wait_timeout'), ) elif state == 'absent': if not existing_stack_set: module.exit_json(msg='Stack set {0} does not exist'.format( module.params['name'])) if module.params.get('purge_stack_instances') is False: pass try: cfn.delete_stack_set(StackSetName=module.params['name'], ) module.exit_json( msg='Stack set {0} deleted'.format(module.params['name'])) except is_boto3_error_code('OperationInProgressException') as e: # pylint: disable=duplicate-except module.fail_json_aws( e, msg= 'Cannot delete stack {0} while there is an operation in progress' .format(module.params['name'])) except is_boto3_error_code('StackSetNotEmptyException'): # pylint: disable=duplicate-except delete_instances_op = 'Ansible-StackInstance-Delete-{0}'.format( operation_uuid) cfn.delete_stack_instances( StackSetName=module.params['name'], Accounts=module.params['accounts'], Regions=module.params['regions'], RetainStacks=(not module.params.get('purge_stacks')), OperationId=delete_instances_op) await_stack_set_operation( module, cfn, operation_id=delete_instances_op, stack_set_name=stack_params['StackSetName'], max_wait=module.params.get('wait_timeout'), ) try: cfn.delete_stack_set(StackSetName=module.params['name'], ) except is_boto3_error_code('StackSetNotEmptyException') as exc: # pylint: disable=duplicate-except # this time, it is likely that either the delete failed or there are more stacks. instances = cfn.list_stack_instances( StackSetName=module.params['name'], ) stack_states = ', '.join( '(account={Account}, region={Region}, state={Status})'. format(**i) for i in instances['Summaries']) module.fail_json_aws( exc, msg= 'Could not purge all stacks, or not all accounts/regions were chosen for deletion: ' + stack_states) module.exit_json(changed=True, msg='Stack set {0} deleted'.format( module.params['name'])) result.update(**describe_stack_tree( module, stack_params['StackSetName'], operation_ids=operation_ids)) if any(o['status'] == 'FAILED' for o in result['operations']): module.fail_json(msg="One or more operations failed to execute", **result) module.exit_json(changed=changed, **result)
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(): """ Main entry point. :return dict: ansible facts """ argument_spec = dict(function_name=dict(required=False, default=None, aliases=['function', 'name']), query=dict(required=False, choices=[ 'aliases', 'all', 'config', 'mappings', 'policy', 'versions' ], default='all'), event_source_arn=dict(required=False, default=None)) module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True, mutually_exclusive=[], required_together=[]) # validate function_name if present function_name = module.params['function_name'] if function_name: if not re.search(r"^[\w\-:]+$", function_name): module.fail_json( msg= 'Function name {0} is invalid. Names must contain only alphanumeric characters and hyphens.' .format(function_name)) if len(function_name) > 64: module.fail_json( msg='Function name "{0}" exceeds 64 character limit'.format( function_name)) try: region, endpoint, aws_connect_kwargs = get_aws_connection_info( module, boto3=True) aws_connect_kwargs.update( dict(region=region, endpoint=endpoint, conn_type='client', resource='lambda')) client = boto3_conn(module, **aws_connect_kwargs) except ClientError as e: module.fail_json_aws(e, "trying to set up boto connection") this_module = sys.modules[__name__] invocations = dict( aliases='alias_details', all='all_details', config='config_details', mappings='mapping_details', policy='policy_details', versions='version_details', ) this_module_function = getattr(this_module, invocations[module.params['query']]) all_facts = fix_return(this_module_function(client, module)) results = dict(ansible_facts={'lambda_facts': { 'function': all_facts }}, changed=False) if module.check_mode: results['msg'] = 'Check mode set but ignored for fact gathering only.' module.exit_json(**results)
def main(): argument_spec = ec2_argument_spec() argument_spec.update( 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 = {} region, ec2_url, aws_connect_params = get_aws_connection_info(module, boto3=True) ebs = boto3_conn(module, conn_type='client', resource='elasticbeanstalk', region=region, endpoint=ec2_url, **aws_connect_params) 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(required=True, choices=['present', 'absent', 'deleting']), name=dict(required=True, type='str'), cluster=dict(required=False, type='str'), task_definition=dict(required=False, type='str'), load_balancers=dict(required=False, default=[], type='list'), desired_count=dict(required=False, type='int'), client_token=dict(required=False, default='', type='str'), role=dict(required=False, default='', type='str'), delay=dict(required=False, type='int', default=10), repeat=dict(required=False, type='int', default=10), force_new_deployment=dict(required=False, default=False, type='bool'), deployment_configuration=dict(required=False, default={}, type='dict'), placement_constraints=dict(required=False, default=[], type='list'), placement_strategy=dict(required=False, default=[], type='list'), health_check_grace_period_seconds=dict(required=False, type='int'), network_configuration=dict( required=False, type='dict', options=dict(subnets=dict(type='list'), security_groups=dict(type='list'), assign_public_ip=dict(type='bool'))), launch_type=dict(required=False, choices=['EC2', 'FARGATE']), service_registries=dict(required=False, type='list', default=[]), scheduling_strategy=dict( required=False, choices=['DAEMON', 'REPLICA'])) module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True, required_if=[('state', 'present', ['task_definition']), ('launch_type', 'FARGATE', ['network_configuration'])], required_together=[['load_balancers', 'role']]) if module.params['state'] == 'present' and module.params[ 'scheduling_strategy'] == 'REPLICA': if module.params['desired_count'] is None: module.fail_json( msg= 'state is present, scheduling_strategy is REPLICA; missing desired_count' ) service_mgr = EcsServiceManager(module) if module.params['network_configuration']: if 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' ) network_configuration = service_mgr.format_network_configuration( module.params['network_configuration']) else: network_configuration = None deployment_configuration = map_complex_type( module.params['deployment_configuration'], DEPLOYMENT_CONFIGURATION_TYPE_MAP) deploymentConfiguration = snake_dict_to_camel_dict( deployment_configuration) serviceRegistries = list( map(snake_dict_to_camel_dict, module.params['service_registries'])) try: existing = service_mgr.describe_service(module.params['cluster'], module.params['name']) except Exception as e: module.fail_json(msg="Exception describing service '" + module.params['name'] + "' in cluster '" + module.params['cluster'] + "': " + str(e)) 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['force_new_deployment']: 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 force_new_deployment' ) if module.params['health_check_grace_period_seconds']: if not module.botocore_at_least('1.8.20'): module.fail_json( msg= 'botocore needs to be version 1.8.20 or higher to use health_check_grace_period_seconds' ) if module.params['state'] == 'present': matching = False update = False if existing and 'status' in existing and existing['status'] == "ACTIVE": if module.params['force_new_deployment']: update = True elif service_mgr.is_matching_service(module.params, existing): matching = True results['service'] = existing else: update = True if not matching: if not module.check_mode: role = module.params['role'] clientToken = module.params['client_token'] loadBalancers = [] for loadBalancer in module.params['load_balancers']: if 'containerPort' in loadBalancer: loadBalancer['containerPort'] = int( loadBalancer['containerPort']) loadBalancers.append(loadBalancer) for loadBalancer in loadBalancers: if 'containerPort' in loadBalancer: loadBalancer['containerPort'] = int( loadBalancer['containerPort']) if update: # check various parameters and boto versions and give a helpful error in boto is not new enough for feature if module.params['scheduling_strategy']: if not module.botocore_at_least('1.10.37'): module.fail_json( msg= 'botocore needs to be version 1.10.37 or higher to use scheduling_strategy' ) elif (existing['schedulingStrategy'] ) != module.params['scheduling_strategy']: module.fail_json( msg= "It is not possible to update the scheduling strategy of an existing service" ) if module.params['service_registries']: if not module.botocore_at_least('1.9.15'): module.fail_json( msg= 'botocore needs to be version 1.9.15 or higher to use service_registries' ) elif (existing['serviceRegistries'] or []) != serviceRegistries: module.fail_json( msg= "It is not possible to update the service registries of an existing service" ) if (existing['loadBalancers'] or []) != loadBalancers: module.fail_json( msg= "It is not possible to update the load balancers of an existing service" ) # update required response = service_mgr.update_service( module.params['name'], module.params['cluster'], module.params['task_definition'], module.params['desired_count'], deploymentConfiguration, network_configuration, module.params['health_check_grace_period_seconds'], module.params['force_new_deployment']) else: try: response = service_mgr.create_service( module.params['name'], module.params['cluster'], module.params['task_definition'], loadBalancers, module.params['desired_count'], clientToken, role, deploymentConfiguration, module.params['placement_constraints'], module.params['placement_strategy'], module.params['health_check_grace_period_seconds'], network_configuration, serviceRegistries, module.params['launch_type'], module.params['scheduling_strategy']) except botocore.exceptions.ClientError as e: module.fail_json_aws(e, msg="Couldn't create service") results['service'] = response results['changed'] = True elif module.params['state'] == 'absent': if not existing: pass else: # it exists, so we should delete it and mark changed. # return info about the cluster deleted del existing['deployments'] del existing['events'] results['ansible_facts'] = existing if 'status' in existing and existing['status'] == "INACTIVE": results['changed'] = False else: if not module.check_mode: try: service_mgr.delete_service(module.params['name'], module.params['cluster']) except botocore.exceptions.ClientError as e: module.fail_json_aws(e, msg="Couldn't delete service") results['changed'] = True elif module.params['state'] == 'deleting': if not existing: module.fail_json(msg="Service '" + module.params['name'] + " not found.") return # it exists, so we should delete it and mark changed. # return info about the cluster deleted delay = module.params['delay'] repeat = module.params['repeat'] time.sleep(delay) for i in range(repeat): existing = service_mgr.describe_service(module.params['cluster'], module.params['name']) status = existing['status'] if status == "INACTIVE": results['changed'] = True break time.sleep(delay) if i is repeat - 1: module.fail_json(msg="Service still not deleted after " + str(repeat) + " tries of " + str(delay) + " seconds each.") return module.exit_json(**results)
def main(): argument_spec = ec2_argument_spec() argument_spec.update(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(default=300, type='int'), 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)
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( 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: 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: 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)