diff --git a/changelogs/fragments/848-ec2_vpc_net-tagging-and-id.yml b/changelogs/fragments/848-ec2_vpc_net-tagging-and-id.yml new file mode 100644 index 00000000000..0dd0e6431bf --- /dev/null +++ b/changelogs/fragments/848-ec2_vpc_net-tagging-and-id.yml @@ -0,0 +1,7 @@ +minor_changes: +- ec2_vpc_net - add support for managing VPCs by ID (https://github.com/ansible-collections/amazon.aws/pull/848). +deprecated_features: +- ec2_vpc_net - the current default value of ``False`` for ``purge_tags`` has been deprecated and will be updated in release 5.0.0 to ``True`` (https://github.com/ansible-collections/amazon.aws/pull/848). +bugfixes: +- ec2_vpc_net - fix a bug where the module would get stuck if DNS options were updated in check mode (https://github.com/ansible/ansible/issues/62677). +- ec2_vpc_net - fix a bug where CIDR configuration would be updated in check mode (https://github.com/ansible/ansible/issues/62678). diff --git a/plugins/modules/ec2_vpc_net.py b/plugins/modules/ec2_vpc_net.py index 1f4d39fa779..069b5bcb837 100644 --- a/plugins/modules/ec2_vpc_net.py +++ b/plugins/modules/ec2_vpc_net.py @@ -19,14 +19,26 @@ options: name: description: - - The name to give your VPC. This is used in combination with C(cidr_block) to determine if a VPC already exists. - required: yes + - The name to give your VPC. This is used in combination with I(cidr_block) + to determine if a VPC already exists. + - The value of I(name) overrides any value set for C(Name) in the I(tags) + parameter. + - At least one of I(name) and I(vpc_id) must be specified. + - I(name) must be specified when creating a new VPC. + type: str + vpc_id: + version_added: 4.0.0 + description: + - The ID of the VPC. + - At least one of I(name) and I(vpc_id) must be specified. + - At least one of I(name) and I(cidr_block) must be specified. type: str cidr_block: description: - - The primary CIDR of the VPC. After 2.5 a list of CIDRs can be provided. The first in the list will be used as the primary CIDR - and is used in conjunction with the C(name) to ensure idempotence. - required: yes + - The primary CIDR of the VPC. + - The first in the list will be used as the primary CIDR + and is used in conjunction with I(name) to ensure idempotence. + - Required when I(vpc_id) is not set. type: list elements: str ipv6_cidr: @@ -37,35 +49,30 @@ type: bool purge_cidrs: description: - - Remove CIDRs that are associated with the VPC and are not specified in C(cidr_block). + - Remove CIDRs that are associated with the VPC and are not specified in I(cidr_block). default: no type: bool tenancy: description: - - Whether to be default or dedicated tenancy. This cannot be changed after the VPC has been created. + - Whether to be default or dedicated tenancy. + - This cannot be changed after the VPC has been created. default: default choices: [ 'default', 'dedicated' ] type: str dns_support: description: - Whether to enable AWS DNS support. - default: yes + - Default value is C(true) when creating a new VPC. type: bool dns_hostnames: description: - Whether to enable AWS hostname support. - default: yes + - Default value is C(true) when creating a new VPC. type: bool dhcp_opts_id: description: - The id of the DHCP options to use for this VPC. type: str - tags: - description: - - The tags you want attached to the VPC. This is independent of the name value, note if you pass a 'Name' key it would override the Name of - the VPC if it's different. - aliases: [ 'resource_tags' ] - type: dict state: description: - The state of the VPC. Either absent or present. @@ -81,6 +88,7 @@ extends_documentation_fragment: - amazon.aws.aws - amazon.aws.ec2 +- amazon.aws.tags.deprecated_purge ''' @@ -146,6 +154,12 @@ returned: always type: str sample: vpc-12345678 + name: + description: The Name tag of the VPC. + returned: When the Name tag has been set on the VPC + type: str + sample: MyVPC + version_added: 4.0.0 instance_tenancy: description: indicates whether VPC uses default or dedicated tenancy returned: always @@ -210,7 +224,8 @@ from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ansible_dict_to_boto3_filter_list from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ansible_dict_to_boto3_tag_list from ansible_collections.amazon.aws.plugins.module_utils.ec2 import boto3_tag_list_to_ansible_dict -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import compare_aws_tags +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ensure_ec2_tags +from ansible_collections.amazon.aws.plugins.module_utils.tagging import boto3_tag_specifications from ansible_collections.amazon.aws.plugins.module_utils.waiters import get_waiter @@ -270,7 +285,7 @@ def wait_for_vpc(module, connection, **params): module.fail_json_aws(e, msg="Unable to wait for VPC state to update.") -def get_vpc(module, connection, vpc_id): +def get_vpc(module, connection, vpc_id, wait=True): wait_for_vpc(module, connection, VpcIds=[vpc_id]) try: vpc_obj = connection.describe_vpcs(VpcIds=[vpc_id], aws_retry=True)['Vpcs'][0] @@ -282,56 +297,62 @@ def get_vpc(module, connection, vpc_id): return vpc_obj -def update_vpc_tags(connection, module, vpc_id, tags, name): +def update_vpc_tags(connection, module, vpc_id, tags, name, purge_tags): + # Name is a tag rather than a direct parameter, we need to inject 'Name' + # into tags, but since tags isn't explicitly passed we'll treat it not being + # set as purge_tags == False + if name: + if purge_tags and tags is None: + purge_tags = False + tags = tags or {} + tags.update({'Name': name}) + if tags is None: - tags = dict() + return False + + changed = ensure_ec2_tags(connection, module, vpc_id, tags=tags, purge_tags=purge_tags) + if not changed or module.check_mode: + return changed + + return True + + +def update_dhcp_opts(connection, module, vpc_obj, dhcp_id): + if dhcp_id is None: + return False + if vpc_obj['DhcpOptionsId'] == dhcp_id: + return False + if module.check_mode: + return True - tags.update({'Name': name}) - tags = dict((k, to_native(v)) for k, v in tags.items()) try: - filters = ansible_dict_to_boto3_filter_list({'resource-id': vpc_id}) - current_tags = dict((t['Key'], t['Value']) for t in connection.describe_tags(Filters=filters, aws_retry=True)['Tags']) - tags_to_update, dummy = compare_aws_tags(current_tags, tags, False) - if tags_to_update: - if not module.check_mode: - tags = ansible_dict_to_boto3_tag_list(tags_to_update) - vpc_obj = connection.create_tags(Resources=[vpc_id], Tags=tags, aws_retry=True) - - # Wait for tags to be updated - expected_tags = boto3_tag_list_to_ansible_dict(tags) - filters = [{'Name': 'tag:{0}'.format(key), 'Values': [value]} for key, value in expected_tags.items()] - wait_for_vpc(module, connection, VpcIds=[vpc_id], Filters=filters) - - return True - else: - return False + connection.associate_dhcp_options(DhcpOptionsId=dhcp_id, VpcId=vpc_obj['VpcId'], aws_retry=True) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Failed to update tags") + module.fail_json_aws(e, msg="Failed to associate DhcpOptionsId {0}".format(dhcp_id)) + return True -def update_dhcp_opts(connection, module, vpc_obj, dhcp_id): - if vpc_obj['DhcpOptionsId'] != dhcp_id: - if not module.check_mode: - try: - connection.associate_dhcp_options(DhcpOptionsId=dhcp_id, VpcId=vpc_obj['VpcId'], aws_retry=True) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Failed to associate DhcpOptionsId {0}".format(dhcp_id)) - # Wait for DhcpOptionsId to be updated - filters = [{'Name': 'dhcp-options-id', 'Values': [dhcp_id]}] - wait_for_vpc(module, connection, VpcIds=[vpc_obj['VpcId']], Filters=filters) +def create_vpc(connection, module, cidr_block, tenancy, tags, ipv6_cidr, name): + if module.check_mode: + module.exit_json(changed=True, msg="VPC would be created if not in check mode") - return True - else: - return False + create_args = dict( + CidrBlock=cidr_block, InstanceTenancy=tenancy, + ) + + if name: + tags = tags or {} + tags['Name'] = name + if tags: + create_args['TagSpecifications'] = boto3_tag_specifications(tags, 'vpc') + # Defaults to False (including None) + if ipv6_cidr: + create_args['AmazonProvidedIpv6CidrBlock'] = True -def create_vpc(connection, module, cidr_block, tenancy): try: - if not module.check_mode: - vpc_obj = connection.create_vpc(CidrBlock=cidr_block, InstanceTenancy=tenancy, aws_retry=True) - else: - module.exit_json(changed=True, msg="VPC would be created if not in check mode") + vpc_obj = connection.create_vpc(aws_retry=True, **create_args) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, "Failed to create the VPC") @@ -342,7 +363,7 @@ def create_vpc(connection, module, cidr_block, tenancy): WaiterConfig=dict(MaxAttempts=30) ) # Wait for the VPC to enter an 'Available' State - wait_for_vpc_to_exist( + wait_for_vpc( module, connection, VpcIds=[vpc_obj['Vpc']['VpcId']], WaiterConfig=dict(MaxAttempts=30) @@ -352,6 +373,11 @@ def create_vpc(connection, module, cidr_block, tenancy): def wait_for_vpc_attribute(connection, module, vpc_id, attribute, expected_value): + if expected_value is None: + return + if module.check_mode: + return + start_time = time() updated = False while time() < start_time + 300: @@ -374,6 +400,12 @@ def wait_for_vpc_ipv6_state(module, connection, vpc_id, ipv6_assoc_state): If ipv6_assoc_state is True, wait for VPC to be associated with at least one Amazon-provided IPv6 CIDR block. If ipv6_assoc_state is False, wait for VPC to be dissociated from all Amazon-provided IPv6 CIDR blocks. """ + + if ipv6_assoc_state is None: + return + if module.check_mode: + return + start_time = time() criteria_match = False while time() < start_time + 300: @@ -404,6 +436,9 @@ def wait_for_vpc_ipv6_state(module, connection, vpc_id, ipv6_assoc_state): def get_cidr_network_bits(module, cidr_block): + if cidr_block is None: + return None + fixed_cidrs = [] for cidr in cidr_block: split_addr = cidr.split('/') @@ -421,28 +456,197 @@ def get_cidr_network_bits(module, cidr_block): return fixed_cidrs +def update_ipv6_cidrs(connection, module, vpc_obj, vpc_id, ipv6_cidr): + if ipv6_cidr is None: + return False + + # Fetch current state from vpc_object + current_ipv6_cidr = False + if 'Ipv6CidrBlockAssociationSet' in vpc_obj.keys(): + for ipv6_assoc in vpc_obj['Ipv6CidrBlockAssociationSet']: + if ipv6_assoc['Ipv6Pool'] == 'Amazon' and ipv6_assoc['Ipv6CidrBlockState']['State'] in ['associated', 'associating']: + current_ipv6_cidr = True + break + + if ipv6_cidr == current_ipv6_cidr: + return False + + if module.check_mode: + return True + + # There's no block associated, and we want one to be associated + if ipv6_cidr: + try: + connection.associate_vpc_cidr_block(AmazonProvidedIpv6CidrBlock=ipv6_cidr, VpcId=vpc_id, aws_retry=True) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, "Unable to associate IPv6 CIDR") + else: + for ipv6_assoc in vpc_obj['Ipv6CidrBlockAssociationSet']: + if ipv6_assoc['Ipv6Pool'] == 'Amazon' and ipv6_assoc['Ipv6CidrBlockState']['State'] in ['associated', 'associating']: + try: + connection.disassociate_vpc_cidr_block(AssociationId=ipv6_assoc['AssociationId'], aws_retry=True) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, "Unable to disassociate IPv6 CIDR {0}.".format(ipv6_assoc['AssociationId'])) + return True + + +def update_cidrs(connection, module, vpc_obj, vpc_id, cidr_block, purge_cidrs): + if cidr_block is None: + return False, None + + associated_cidrs = dict((cidr['CidrBlock'], cidr['AssociationId']) for cidr in vpc_obj.get('CidrBlockAssociationSet', []) + if cidr['CidrBlockState']['State'] not in ['disassociating', 'disassociated']) + + current_cidrs = set(associated_cidrs.keys()) + desired_cidrs = set(cidr_block) + if not purge_cidrs: + desired_cidrs = desired_cidrs.union(current_cidrs) + + cidrs_to_add = list(desired_cidrs.difference(current_cidrs)) + cidrs_to_remove = list(current_cidrs.difference(desired_cidrs)) + + if not cidrs_to_add and not cidrs_to_remove: + return False, None + + if module.check_mode: + return True, list(desired_cidrs) + + for cidr in cidrs_to_add: + try: + connection.associate_vpc_cidr_block(CidrBlock=cidr, VpcId=vpc_id, aws_retry=True) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, "Unable to associate CIDR {0}.".format(cidr)) + + for cidr in cidrs_to_remove: + association_id = associated_cidrs[cidr] + try: + connection.disassociate_vpc_cidr_block(AssociationId=association_id, aws_retry=True) + 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)) + return True, list(desired_cidrs) + + +def update_dns_enabled(connection, module, vpc_id, dns_support): + if dns_support is None: + return False + + current_dns_enabled = connection.describe_vpc_attribute(Attribute='enableDnsSupport', VpcId=vpc_id, aws_retry=True)['EnableDnsSupport']['Value'] + if current_dns_enabled == dns_support: + return False + + if module.check_mode: + return True + + try: + connection.modify_vpc_attribute(VpcId=vpc_id, EnableDnsSupport={'Value': dns_support}, aws_retry=True) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, "Failed to update enabled dns support attribute") + return True + + +def update_dns_hostnames(connection, module, vpc_id, dns_hostnames): + if dns_hostnames is None: + return False + + current_dns_hostnames = connection.describe_vpc_attribute(Attribute='enableDnsHostnames', VpcId=vpc_id, aws_retry=True)['EnableDnsHostnames']['Value'] + if current_dns_hostnames == dns_hostnames: + return False + + if module.check_mode: + return True + + try: + connection.modify_vpc_attribute(VpcId=vpc_id, EnableDnsHostnames={'Value': dns_hostnames}, aws_retry=True) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, "Failed to update enabled dns hostnames attribute") + return True + + +def delete_vpc(connection, module, vpc_id): + if vpc_id is None: + return False + if module.check_mode: + return True + + try: + connection.delete_vpc(VpcId=vpc_id, aws_retry=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 that all depenednt components are absent.".format(vpc_id) + ) + + return True + + +def wait_for_updates(connection, module, vpc_id, ipv6_cidr, expected_cidrs, dns_support, dns_hostnames, tags, dhcp_id): + + if module.check_mode: + return + + if expected_cidrs: + wait_for_vpc( + module, connection, + VpcIds=[vpc_id], + Filters=[{'Name': 'cidr-block-association.cidr-block', 'Values': expected_cidrs}] + ) + wait_for_vpc_ipv6_state(module, connection, vpc_id, ipv6_cidr) + + if tags is not None: + tag_list = ansible_dict_to_boto3_tag_list(tags) + filters = [{'Name': 'tag:{0}'.format(t['Key']), 'Values': [t['Value']]} for t in tag_list] + wait_for_vpc(module, connection, VpcIds=[vpc_id], Filters=filters) + + wait_for_vpc_attribute(connection, module, vpc_id, 'enableDnsSupport', dns_support) + wait_for_vpc_attribute(connection, module, vpc_id, 'enableDnsHostnames', dns_hostnames) + + if dhcp_id is not None: + # Wait for DhcpOptionsId to be updated + filters = [{'Name': 'dhcp-options-id', 'Values': [dhcp_id]}] + wait_for_vpc(module, connection, VpcIds=[vpc_id], Filters=filters) + + return + + def main(): argument_spec = dict( - name=dict(required=True), - cidr_block=dict(type='list', required=True, elements='str'), + name=dict(required=False), + vpc_id=dict(type='str', required=False, default=None), + cidr_block=dict(type='list', elements='str'), ipv6_cidr=dict(type='bool', default=None), tenancy=dict(choices=['default', 'dedicated'], default='default'), - dns_support=dict(type='bool', default=True), - dns_hostnames=dict(type='bool', default=True), + dns_support=dict(type='bool'), + dns_hostnames=dict(type='bool'), dhcp_opts_id=dict(), tags=dict(type='dict', aliases=['resource_tags']), + purge_tags=dict(type='bool', default=None), state=dict(choices=['present', 'absent'], default='present'), multi_ok=dict(type='bool', default=False), purge_cidrs=dict(type='bool', default=False), ) + required_one_of = [ + ['vpc_id', 'name'], + ['vpc_id', 'cidr_block'], + ] module = AnsibleAWSModule( argument_spec=argument_spec, + required_one_of=required_one_of, supports_check_mode=True ) + if module.params.get('purge_tags') is None: + module.deprecate( + 'The purge_tags parameter currently defaults to False.' + ' For consistency across the collection, this default value' + ' will change to True in release 5.0.0.', + version='5.0.0', collection_name='amazon.aws') + module.params['purge_tags'] = False + name = module.params.get('name') - cidr_block = get_cidr_network_bits(module, module.params.get('cidr_block')) + vpc_id = module.params.get('vpc_id') + cidr_block = module.params.get('cidr_block') ipv6_cidr = module.params.get('ipv6_cidr') purge_cidrs = module.params.get('purge_cidrs') tenancy = module.params.get('tenancy') @@ -450,6 +654,7 @@ def main(): dns_hostnames = module.params.get('dns_hostnames') dhcp_id = module.params.get('dhcp_opts_id') tags = module.params.get('tags') + purge_tags = module.params.get('purge_tags') state = module.params.get('state') multi = module.params.get('multi_ok') @@ -459,165 +664,64 @@ def main(): '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') + cidr_block = get_cidr_network_bits(module, module.params.get('cidr_block')) + + if vpc_id is None: + vpc_id = vpc_exists(module, connection, name, cidr_block, multi) + if state == 'present': # Check if VPC exists - vpc_id = vpc_exists(module, connection, name, cidr_block, multi) - is_new_vpc = False if vpc_id is None: - is_new_vpc = True - vpc_id = create_vpc(connection, module, cidr_block[0], tenancy) + if module.params.get('name') is None: + module.fail_json('The name parameter must be specified when creating a new VPC.') + vpc_id = create_vpc(connection, module, cidr_block[0], tenancy, tags, ipv6_cidr, name) changed = True - if ipv6_cidr is None: - # default value when creating new VPC. - ipv6_cidr = False - - vpc_obj = get_vpc(module, connection, vpc_id) - if not is_new_vpc and ipv6_cidr is None: - # 'ipv6_cidr' wasn't specified in the task. - # Retain the value from the existing VPC. - ipv6_cidr = False - if 'Ipv6CidrBlockAssociationSet' in vpc_obj.keys(): - for ipv6_assoc in vpc_obj['Ipv6CidrBlockAssociationSet']: - if ipv6_assoc['Ipv6Pool'] == 'Amazon' and ipv6_assoc['Ipv6CidrBlockState']['State'] in ['associated', 'associating']: - ipv6_cidr = True - break - - 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 - if not module.check_mode: - try: - connection.associate_vpc_cidr_block(CidrBlock=cidr, VpcId=vpc_id, aws_retry=True) - 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' not in vpc_obj.keys(): - changed = True - if not module.check_mode: - try: - connection.associate_vpc_cidr_block(AmazonProvidedIpv6CidrBlock=ipv6_cidr, VpcId=vpc_id, aws_retry=True) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, "Unable to associate CIDR {0}.".format(ipv6_cidr)) + vpc_obj = get_vpc(module, connection, vpc_id) + if len(cidr_block) > 1: + cidrs_changed, desired_cidrs = update_cidrs(connection, module, vpc_obj, vpc_id, cidr_block, purge_cidrs) + changed |= cidrs_changed else: - # If the VPC has been created with IPv6 CIDR, and the ipv6 blocks were subsequently - # disassociated, a Amazon-provide block must be associate a new block. - assoc_needed = True - for ipv6_assoc in vpc_obj['Ipv6CidrBlockAssociationSet']: - if ipv6_assoc['Ipv6Pool'] == 'Amazon' and ipv6_assoc['Ipv6CidrBlockState']['State'] in ['associated', 'associating']: - assoc_needed = False - break - if assoc_needed: - changed = True - if not module.check_mode: - try: - connection.associate_vpc_cidr_block(AmazonProvidedIpv6CidrBlock=ipv6_cidr, VpcId=vpc_id, aws_retry=True) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, "Unable to associate CIDR {0}.".format(ipv6_cidr)) - wait_for_vpc_ipv6_state(module, connection, vpc_id, True) + desired_cidrs = None + # Set on-creation defaults + if dns_hostnames is None: + dns_hostnames = True + if dns_support is None: + dns_support = True else: - # ipv6_cidr is False - if 'Ipv6CidrBlockAssociationSet' in vpc_obj.keys() and len(vpc_obj['Ipv6CidrBlockAssociationSet']) > 0: - assoc_disable = False - for ipv6_assoc in vpc_obj['Ipv6CidrBlockAssociationSet']: - if ipv6_assoc['Ipv6Pool'] == 'Amazon' and ipv6_assoc['Ipv6CidrBlockState']['State'] in ['associated', 'associating']: - assoc_disable = True - changed = True - if not module.check_mode: - try: - connection.disassociate_vpc_cidr_block(AssociationId=ipv6_assoc['AssociationId'], aws_retry=True) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, "Unable to disassociate IPv6 CIDR {0}.".format(ipv6_assoc['AssociationId'])) - if assoc_disable and not module.check_mode: - wait_for_vpc_ipv6_state(module, connection, vpc_id, False) - if purge_cidrs: - for association_id in to_remove: - changed = True - if not module.check_mode: - try: - connection.disassociate_vpc_cidr_block(AssociationId=association_id, aws_retry=True) - 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}, aws_retry=True) - 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}, aws_retry=True) - 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: - wait_for_vpc( - module, connection, - VpcIds=[vpc_id], - Filters=[{'Name': 'cidr-block-association.cidr-block', 'Values': expected_cidrs}] - ) - - # 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', [])) + vpc_obj = get_vpc(module, connection, vpc_id) + cidrs_changed, desired_cidrs = update_cidrs(connection, module, vpc_obj, vpc_id, cidr_block, purge_cidrs) + changed |= cidrs_changed + ipv6_changed = update_ipv6_cidrs(connection, module, vpc_obj, vpc_id, ipv6_cidr) + changed |= ipv6_changed + tags_changed = update_vpc_tags(connection, module, vpc_id, tags, name, purge_tags) + changed |= tags_changed + + dhcp_changed = update_dhcp_opts(connection, module, vpc_obj, dhcp_id) + changed |= dhcp_changed + dns_changed = update_dns_enabled(connection, module, vpc_id, dns_support) + changed |= dns_changed + hostnames_changed = update_dns_hostnames(connection, module, vpc_id, dns_hostnames) + changed |= hostnames_changed + + wait_for_updates(connection, module, vpc_id, ipv6_cidr, desired_cidrs, dns_support, dns_hostnames, tags, dhcp_id) + + updated_obj = get_vpc(module, connection, vpc_id) + final_state = camel_dict_to_snake_dict(updated_obj) + final_state['tags'] = boto3_tag_list_to_ansible_dict(updated_obj.get('Tags', [])) + final_state['name'] = final_state['tags'].get('Name', None) final_state['id'] = final_state.pop('vpc_id') - debugging = dict(to_add=to_add, to_remove=to_remove, expected_cidrs=expected_cidrs) - module.exit_json(changed=changed, vpc=final_state, debugging=debugging) + 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, aws_retry=True) - 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)) - + changed = delete_vpc(connection, module, vpc_id) module.exit_json(changed=changed, vpc={}) diff --git a/tests/integration/targets/ec2_vpc_net/defaults/main.yml b/tests/integration/targets/ec2_vpc_net/defaults/main.yml index 3289b278359..f35d4cb8766 100644 --- a/tests/integration/targets/ec2_vpc_net/defaults/main.yml +++ b/tests/integration/targets/ec2_vpc_net/defaults/main.yml @@ -3,3 +3,6 @@ vpc_cidr: '10.{{ 256 | random(seed=resource_prefix) }}.0.0/24' vpc_cidr_a: '10.{{ 256 | random(seed=resource_prefix) }}.1.0/24' vpc_cidr_b: '10.{{ 256 | random(seed=resource_prefix) }}.2.0/24' + +vpc_name: '{{ resource_prefix }}-vpc-net' +vpc_name_updated: '{{ resource_prefix }}-updated-vpc-net' diff --git a/tests/integration/targets/ec2_vpc_net/tasks/main.yml b/tests/integration/targets/ec2_vpc_net/tasks/main.yml index 94cff369f20..da40c16f6b4 100644 --- a/tests/integration/targets/ec2_vpc_net/tasks/main.yml +++ b/tests/integration/targets/ec2_vpc_net/tasks/main.yml @@ -6,7 +6,34 @@ aws_secret_key: "{{ aws_secret_key }}" security_token: "{{ security_token | default(omit) }}" region: "{{ aws_region }}" - + vars: + first_tags: + 'Key with Spaces': Value with spaces + CamelCaseKey: CamelCaseValue + pascalCaseKey: pascalCaseValue + snake_case_key: snake_case_value + second_tags: + 'New Key with Spaces': Value with spaces + NewCamelCaseKey: CamelCaseValue + newPascalCaseKey: pascalCaseValue + new_snake_case_key: snake_case_value + third_tags: + 'Key with Spaces': Value with spaces + CamelCaseKey: CamelCaseValue + pascalCaseKey: pascalCaseValue + snake_case_key: snake_case_value + 'New Key with Spaces': Updated Value with spaces + final_tags: + 'Key with Spaces': Value with spaces + CamelCaseKey: CamelCaseValue + pascalCaseKey: pascalCaseValue + snake_case_key: snake_case_value + 'New Key with Spaces': Updated Value with spaces + NewCamelCaseKey: CamelCaseValue + newPascalCaseKey: pascalCaseValue + new_snake_case_key: snake_case_value + name_tags: + Name: "{{ vpc_name }}" block: # ============================================================ @@ -24,14 +51,15 @@ assert: that: - result is failed - - result.msg.startswith("missing required arguments") + #- result.msg.startswith("missing required arguments") + - result.msg.startswith("one of") # ============================================================ - name: Fetch existing VPC info ec2_vpc_net_info: filters: - "tag:Name": "{{ resource_prefix }}" + "tag:Name": "{{ vpc_name }}" register: vpc_info - name: Check no-one is using the Prefix before we start assert: @@ -42,12 +70,12 @@ ec2_vpc_net: state: present cidr_block: "{{ vpc_cidr }}" - name: "{{ resource_prefix }}" + name: "{{ vpc_name }}" check_mode: true register: result - ec2_vpc_net_info: filters: - "tag:Name": "{{ resource_prefix }}" + "tag:Name": "{{ vpc_name }}" register: vpc_info - name: check for a change @@ -62,12 +90,12 @@ ec2_vpc_net: state: present cidr_block: "{{ vpc_cidr }}" - name: "{{ resource_prefix }}" + name: "{{ vpc_name }}" ipv6_cidr: True register: result - ec2_vpc_net_info: filters: - "tag:Name": "{{ resource_prefix }}" + "tag:Name": "{{ vpc_name }}" register: vpc_info - name: assert the VPC was created successfully @@ -97,7 +125,7 @@ - '"is_default" in result.vpc' - '"state" in result.vpc' - result.vpc.tags.keys() | length == 1 - - result.vpc.tags.Name == resource_prefix + - result.vpc.tags.Name == vpc_name - name: set the first VPC's details as facts for comparison and cleanup set_fact: @@ -110,12 +138,12 @@ ec2_vpc_net: state: present cidr_block: "{{ vpc_cidr }}" - name: "{{ resource_prefix }}" + name: "{{ vpc_name }}" ipv6_cidr: True register: result - ec2_vpc_net_info: filters: - "tag:Name": "{{ resource_prefix }}" + "tag:Name": "{{ vpc_name }}" register: vpc_info - name: assert nothing changed @@ -141,14 +169,14 @@ - '"is_default" in result.vpc' - '"state" in result.vpc' - result.vpc.tags.keys() | length == 1 - - result.vpc.tags.Name == resource_prefix + - result.vpc.tags.Name == vpc_name - result.vpc.id == vpc_1 - name: No-op VPC configuration, missing ipv6_cidr property ec2_vpc_net: state: present cidr_block: "{{ vpc_cidr }}" - name: "{{ resource_prefix }}" + name: "{{ vpc_name }}" # Intentionaly commenting out 'ipv6_cidr' # When the 'ipv6_cidr' property is missing, the VPC should retain its configuration. # That should not cause the module to set default value 'false' and disassociate the IPv6 block. @@ -177,7 +205,7 @@ - name: VPC info (Simple tag filter) ec2_vpc_net_info: filters: - "tag:Name": "{{ resource_prefix }}" + "tag:Name": "{{ vpc_name }}" register: vpc_info - name: Test vpc_info results @@ -210,12 +238,12 @@ ec2_vpc_net: state: present cidr_block: "{{ vpc_cidr }}" - name: "{{ resource_prefix }}" + name: "{{ vpc_name }}" ipv6_cidr: True register: result - ec2_vpc_net_info: filters: - "tag:Name": "{{ resource_prefix }}" + "tag:Name": "{{ vpc_name }}" register: vpc_info - name: Assert no changes made @@ -230,14 +258,14 @@ ec2_vpc_net: state: present cidr_block: "{{ vpc_cidr }}" - name: "{{ resource_prefix }}" + name: "{{ vpc_name }}" ipv6_cidr: True multi_ok: yes check_mode: true register: result - ec2_vpc_net_info: filters: - "tag:Name": "{{ resource_prefix }}" + "tag:Name": "{{ vpc_name }}" register: vpc_info - name: assert a change would be made @@ -255,14 +283,14 @@ ec2_vpc_net: state: present cidr_block: "{{ vpc_cidr }}" - name: "{{ resource_prefix }}" + name: "{{ vpc_name }}" ipv6_cidr: True tenancy: dedicated multi_ok: yes register: result - ec2_vpc_net_info: filters: - "tag:Name": "{{ resource_prefix }}" + "tag:Name": "{{ vpc_name }}" register: vpc_info - name: assert a new VPC was created @@ -319,7 +347,7 @@ ec2_vpc_net: state: present cidr_block: "{{ vpc_cidr }}" - name: "{{ resource_prefix }}" + name: "{{ vpc_name }}" ipv6_cidr: True tenancy: dedicated multi_ok: no @@ -327,7 +355,7 @@ ignore_errors: yes - ec2_vpc_net_info: filters: - "tag:Name": "{{ resource_prefix }}" + "tag:Name": "{{ vpc_name }}" register: vpc_info - name: assert failure @@ -339,39 +367,66 @@ # ============================================================ - # FIXME: right now if there are multiple matching VPCs they cannot be removed, - # as there is no vpc_id option for idempotence. A workaround is to retag the VPC. - - name: remove Name tag on new VPC - ec2_tag: - state: absent - resource: "{{ vpc_2 }}" - tags: - Name: "{{ resource_prefix }}" - - - name: add a unique name tag - ec2_tag: - state: present - resource: "{{ vpc_2 }}" - tags: - Name: "{{ resource_prefix }}-changed" - - - name: delete one of the VPCs + - name: Set new name for second VPC ec2_vpc_net: - state: absent + state: present + vpc_id: "{{ vpc_2 }}" + name: "{{ vpc_name_updated }}" cidr_block: "{{ vpc_cidr }}" - name: "{{ resource_prefix }}-changed" register: result + + - name: assert name changed + assert: + that: + - '"cidr_block" in result.vpc' + - result.vpc.cidr_block == vpc_cidr + - result.vpc.cidr_block_association_set | length == 1 + - result.vpc.cidr_block_association_set[0].association_id.startswith("vpc-cidr-assoc-") + - result.vpc.cidr_block_association_set[0].cidr_block == vpc_cidr + - result.vpc.cidr_block_association_set[0].cidr_block_state.state in ["associated", "associating"] + - '"classic_link_enabled" in result.vpc' + - result.vpc.dhcp_options_id.startswith("dopt-") + - '"instance_tenancy" in result.vpc' + - result.vpc.ipv6_cidr_block_association_set | length == 1 + - result.vpc.ipv6_cidr_block_association_set[0].association_id.startswith("vpc-cidr-assoc-") + - result.vpc.ipv6_cidr_block_association_set[0].ipv6_cidr_block | ansible.utils.ipv6 + - result.vpc.ipv6_cidr_block_association_set[0].ipv6_cidr_block_state.state in ["associated", "associating"] + - '"is_default" in result.vpc' + - '"state" in result.vpc' + - result.vpc.tags.keys() | length == 1 + - result.vpc.tags.Name == vpc_name_updated + - result.vpc.id == vpc_2 + - ec2_vpc_net_info: filters: - "tag:Name": "{{ resource_prefix }}" + "tag:Name": "{{ vpc_name }}" + register: vpc_info + + - name: assert success + assert: + that: + - result is changed + - vpc_info.vpcs | length == 1 + - vpc_info.vpcs[0].vpc_id == vpc_1 + + - ec2_vpc_net_info: + filters: + "tag:Name": "{{ vpc_name_updated }}" register: vpc_info - name: assert success assert: that: - result is changed - - not result.vpc - vpc_info.vpcs | length == 1 + - vpc_info.vpcs[0].vpc_id == vpc_2 + + - name: delete second VPC (by id) + ec2_vpc_net: + vpc_id: "{{ vpc_2 }}" + state: absent + cidr_block: "{{ vpc_cidr }}" + register: result # ============================================================ @@ -379,7 +434,7 @@ ec2_vpc_net: state: absent cidr_block: "{{ vpc_cidr }}" - name: "{{ resource_prefix }}-changed" + name: "{{ vpc_name }}-does-not-exist" register: result - name: assert no changes were made @@ -396,7 +451,7 @@ - 4.4.4.4 - 8.8.8.8 tags: - Name: "{{ resource_prefix }}" + Name: "{{ vpc_name }}" register: new_dhcp - name: assert the DHCP option set was successfully created assert: @@ -407,13 +462,13 @@ ec2_vpc_net: state: present cidr_block: "{{ vpc_cidr }}" - name: "{{ resource_prefix }}" + name: "{{ vpc_name }}" dhcp_opts_id: "{{ new_dhcp.dhcp_options_id }}" register: result check_mode: True - ec2_vpc_net_info: filters: - "tag:Name": "{{ resource_prefix }}" + "tag:Name": "{{ vpc_name }}" register: vpc_info - name: assert the DHCP option set changed but didn't update @@ -428,12 +483,12 @@ ec2_vpc_net: state: present cidr_block: "{{ vpc_cidr }}" - name: "{{ resource_prefix }}" + name: "{{ vpc_name }}" dhcp_opts_id: "{{ new_dhcp.dhcp_options_id }}" register: result - ec2_vpc_net_info: filters: - "tag:Name": "{{ resource_prefix }}" + "tag:Name": "{{ vpc_name }}" register: vpc_info - name: assert the DHCP option set changed @@ -450,12 +505,12 @@ ec2_vpc_net: state: present cidr_block: "{{ vpc_cidr }}" - name: "{{ resource_prefix }}" + name: "{{ vpc_name }}" dhcp_opts_id: "{{ new_dhcp.dhcp_options_id }}" register: result - ec2_vpc_net_info: filters: - "tag:Name": "{{ resource_prefix }}" + "tag:Name": "{{ vpc_name }}" register: vpc_info - name: assert the DHCP option set changed @@ -469,39 +524,38 @@ # ============================================================ - # XXX #62677 - #- name: disable dns_hostnames (check mode) - # ec2_vpc_net: - # state: present - # cidr_block: "{{ vpc_cidr }}" - # name: "{{ resource_prefix }}" - # dns_hostnames: False - # register: result - # check_mode: True - #- ec2_vpc_net_info: - # filters: - # "tag:Name": "{{ resource_prefix }}" - # register: vpc_info - - #- name: assert changed was set but not made - # assert: - # that: - # - result is successful - # - result is changed - # - vpc_info.vpcs | length == 1 - # - vpc_info.vpcs[0].enable_dns_hostnames | bool == True - # - vpc_info.vpcs[0].enable_dns_support | bool == True + - name: disable dns_hostnames (check mode) + ec2_vpc_net: + state: present + cidr_block: "{{ vpc_cidr }}" + name: "{{ vpc_name }}" + dns_hostnames: False + register: result + check_mode: True + - ec2_vpc_net_info: + filters: + "tag:Name": "{{ vpc_name }}" + register: vpc_info + + - name: assert changed was set but not made + assert: + that: + - result is successful + - result is changed + - vpc_info.vpcs | length == 1 + - vpc_info.vpcs[0].enable_dns_hostnames | bool == True + - vpc_info.vpcs[0].enable_dns_support | bool == True - name: disable dns_hostnames ec2_vpc_net: state: present cidr_block: "{{ vpc_cidr }}" - name: "{{ resource_prefix }}" + name: "{{ vpc_name }}" dns_hostnames: False register: result - ec2_vpc_net_info: filters: - "tag:Name": "{{ resource_prefix }}" + "tag:Name": "{{ vpc_name }}" register: vpc_info - name: assert a change was made @@ -518,12 +572,12 @@ ec2_vpc_net: state: present cidr_block: "{{ vpc_cidr }}" - name: "{{ resource_prefix }}" + name: "{{ vpc_name }}" dns_hostnames: False register: result - ec2_vpc_net_info: filters: - "tag:Name": "{{ resource_prefix }}" + "tag:Name": "{{ vpc_name }}" register: vpc_info - name: assert a change was made @@ -536,42 +590,41 @@ - vpc_info.vpcs[0].enable_dns_hostnames | bool == False - vpc_info.vpcs[0].enable_dns_support | bool == True - # XXX #62677 - #- name: disable dns_support (check mode) - # ec2_vpc_net: - # state: present - # cidr_block: "{{ vpc_cidr }}" - # name: "{{ resource_prefix }}" - # dns_hostnames: False - # dns_support: False - # check_mode: True - # register: result - #- ec2_vpc_net_info: - # filters: - # "tag:Name": "{{ resource_prefix }}" - # register: vpc_info - - #- name: assert changed was set but not made - # assert: - # that: - # - result is successful - # - result is changed - # - result.vpc.id == vpc_1 - # - vpc_info.vpcs | length == 1 - # - vpc_info.vpcs[0].enable_dns_hostnames | bool == False - # - vpc_info.vpcs[0].enable_dns_support | bool == True + - name: disable dns_support (check mode) + ec2_vpc_net: + state: present + cidr_block: "{{ vpc_cidr }}" + name: "{{ vpc_name }}" + dns_hostnames: False + dns_support: False + check_mode: True + register: result + - ec2_vpc_net_info: + filters: + "tag:Name": "{{ vpc_name }}" + register: vpc_info + + - name: assert changed was set but not made + assert: + that: + - result is successful + - result is changed + - result.vpc.id == vpc_1 + - vpc_info.vpcs | length == 1 + - vpc_info.vpcs[0].enable_dns_hostnames | bool == False + - vpc_info.vpcs[0].enable_dns_support | bool == True - name: disable dns_support ec2_vpc_net: state: present cidr_block: "{{ vpc_cidr }}" - name: "{{ resource_prefix }}" + name: "{{ vpc_name }}" dns_hostnames: False dns_support: False register: result - ec2_vpc_net_info: filters: - "tag:Name": "{{ resource_prefix }}" + "tag:Name": "{{ vpc_name }}" register: vpc_info - name: assert a change was made @@ -588,13 +641,13 @@ ec2_vpc_net: state: present cidr_block: "{{ vpc_cidr }}" - name: "{{ resource_prefix }}" + name: "{{ vpc_name }}" dns_hostnames: False dns_support: False register: result - ec2_vpc_net_info: filters: - "tag:Name": "{{ resource_prefix }}" + "tag:Name": "{{ vpc_name }}" register: vpc_info - name: assert a change was not made @@ -607,38 +660,41 @@ - vpc_info.vpcs[0].enable_dns_hostnames | bool == False - vpc_info.vpcs[0].enable_dns_support | bool == False - # XXX #62677 - #- name: re-enable dns_support (check mode) - # ec2_vpc_net: - # state: present - # cidr_block: "{{ vpc_cidr }}" - # name: "{{ resource_prefix }}" - # register: result - # check_mode: True - #- ec2_vpc_net_info: - # filters: - # "tag:Name": "{{ resource_prefix }}" - # register: vpc_info - - #- name: assert a change was made - # assert: - # that: - # - result is successful - # - result is changed - # - result.vpc.id == vpc_1 - # - vpc_info.vpcs | length == 1 - # - vpc_info.vpcs[0].enable_dns_hostnames | bool == True - # - vpc_info.vpcs[0].enable_dns_support | bool == True + - name: re-enable dns_support (check mode) + ec2_vpc_net: + state: present + cidr_block: "{{ vpc_cidr }}" + name: "{{ vpc_name }}" + dns_hostnames: True + dns_support: True + register: result + check_mode: True + - ec2_vpc_net_info: + filters: + "tag:Name": "{{ vpc_name }}" + register: vpc_info + + - name: assert a change would be made but has not been + assert: + that: + - result is successful + - result is changed + - result.vpc.id == vpc_1 + - vpc_info.vpcs | length == 1 + - vpc_info.vpcs[0].enable_dns_hostnames | bool == False + - vpc_info.vpcs[0].enable_dns_support | bool == False - name: re-enable dns_support ec2_vpc_net: state: present cidr_block: "{{ vpc_cidr }}" - name: "{{ resource_prefix }}" + name: "{{ vpc_name }}" + dns_hostnames: True + dns_support: True register: result - ec2_vpc_net_info: filters: - "tag:Name": "{{ resource_prefix }}" + "tag:Name": "{{ vpc_name }}" register: vpc_info - name: assert a change was made @@ -655,11 +711,13 @@ ec2_vpc_net: state: present cidr_block: "{{ vpc_cidr }}" - name: "{{ resource_prefix }}" + name: "{{ vpc_name }}" + dns_hostnames: True + dns_support: True register: result - ec2_vpc_net_info: filters: - "tag:Name": "{{ resource_prefix }}" + "tag:Name": "{{ vpc_name }}" register: vpc_info - name: assert a change was not made @@ -674,18 +732,17 @@ # ============================================================ - - name: modify tags (check mode) + - name: add tags (check mode) ec2_vpc_net: state: present cidr_block: "{{ vpc_cidr }}" - name: "{{ resource_prefix }}" - tags: - Ansible: Test + name: "{{ vpc_name }}" + tags: "{{ first_tags }}" check_mode: true register: result - ec2_vpc_net_info: filters: - "tag:Name": "{{ resource_prefix }}" + "tag:Name": "{{ vpc_name }}" register: vpc_info - name: assert the VPC has Name but not Ansible tag @@ -695,28 +752,21 @@ - result is changed - result.vpc.id == vpc_1 - result.vpc.tags | length == 1 - - result.vpc.tags.Name == resource_prefix + - result.vpc.tags.Name == vpc_name - vpc_info.vpcs | length == 1 - - vpc_info.vpcs[0].tags | length == 1 - - vpc_info.vpcs[0].tags.Name == resource_prefix + - vpc_info.vpcs[0].tags == name_tags - - name: modify tags + - name: add tags ec2_vpc_net: state: present cidr_block: "{{ vpc_cidr }}" - name: "{{ resource_prefix }}" - tags: - Ansible: Test + name: "{{ vpc_name }}" + tags: "{{ first_tags }}" register: result - ec2_vpc_net_info: filters: - "tag:Name": "{{ resource_prefix }}" + "tag:Name": "{{ vpc_name }}" register: vpc_info - until: - - vpc_info.vpcs | length == 1 - - vpc_info.vpcs[0].tags | length == 2 - retries: 5 - delay: 5 - name: assert the VPC has Name and Ansible tags assert: @@ -724,27 +774,164 @@ - result is successful - result is changed - result.vpc.id == vpc_1 - - result.vpc.tags | length == 2 - - result.vpc.tags.Ansible == "Test" - - result.vpc.tags.Name == resource_prefix + - result.vpc.tags == (first_tags | combine(name_tags)) - vpc_info.vpcs | length == 1 - - vpc_info.vpcs[0].tags | length == 2 - - vpc_info.vpcs[0].tags.Ansible == "Test" - - vpc_info.vpcs[0].tags.Name == resource_prefix + - vpc_info.vpcs[0].tags == (first_tags | combine(name_tags)) - - name: modify tags (no change) + - name: add tags (no change) ec2_vpc_net: state: present cidr_block: "{{ vpc_cidr }}" - name: "{{ resource_prefix }}" - dns_support: True - dns_hostnames: True - tags: - Ansible: Test + name: "{{ vpc_name }}" + tags: "{{ first_tags }}" + register: result + - ec2_vpc_net_info: + filters: + "tag:Name": "{{ vpc_name }}" + register: vpc_info + + - name: assert the VPC has Name and Ansible tags + assert: + that: + - result is successful + - result is not changed + - result.vpc.id == vpc_1 + - result.vpc.tags == (first_tags | combine(name_tags)) + - vpc_info.vpcs | length == 1 + - vpc_info.vpcs[0].tags == (first_tags | combine(name_tags)) + + # ============================================================ + + - name: modify tags with purge (check mode) + ec2_vpc_net: + state: present + cidr_block: "{{ vpc_cidr }}" + name: "{{ vpc_name }}" + tags: "{{ second_tags }}" + purge_tags: true + check_mode: true + register: result + - ec2_vpc_net_info: + filters: + "tag:Name": "{{ vpc_name }}" + register: vpc_info + + - name: assert the VPC has Name but not Ansible tag + assert: + that: + - result is successful + - result is changed + - result.vpc.id == vpc_1 + - result.vpc.tags == (first_tags | combine(name_tags)) + - vpc_info.vpcs | length == 1 + - vpc_info.vpcs[0].tags == (first_tags | combine(name_tags)) + + - name: modify tags with purge + ec2_vpc_net: + state: present + cidr_block: "{{ vpc_cidr }}" + name: "{{ vpc_name }}" + tags: "{{ second_tags }}" + purge_tags: true + register: result + - ec2_vpc_net_info: + filters: + "tag:Name": "{{ vpc_name }}" + register: vpc_info + + - name: assert the VPC has Name and Ansible tags + assert: + that: + - result is successful + - result is changed + - result.vpc.id == vpc_1 + - result.vpc.tags == (second_tags | combine(name_tags)) + - vpc_info.vpcs | length == 1 + - vpc_info.vpcs[0].tags == (second_tags | combine(name_tags)) + + - name: modify tags with purge (no change) + ec2_vpc_net: + state: present + cidr_block: "{{ vpc_cidr }}" + name: "{{ vpc_name }}" + tags: "{{ second_tags }}" + purge_tags: true + register: result + - ec2_vpc_net_info: + filters: + "tag:Name": "{{ vpc_name }}" + register: vpc_info + + - name: assert the VPC has Name and Ansible tags + assert: + that: + - result is successful + - result is not changed + - result.vpc.id == vpc_1 + - result.vpc.tags == (second_tags | combine(name_tags)) + - vpc_info.vpcs | length == 1 + - vpc_info.vpcs[0].tags == (second_tags | combine(name_tags)) + + # ============================================================ + + - name: modify tags without purge (check mode) + ec2_vpc_net: + state: present + cidr_block: "{{ vpc_cidr }}" + name: "{{ vpc_name }}" + tags: "{{ third_tags }}" + purge_tags: false + check_mode: true + register: result + - ec2_vpc_net_info: + filters: + "tag:Name": "{{ vpc_name }}" + register: vpc_info + + - name: assert the VPC has Name but not Ansible tag + assert: + that: + - result is successful + - result is changed + - result.vpc.id == vpc_1 + - result.vpc.tags == (second_tags | combine(name_tags)) + - vpc_info.vpcs | length == 1 + - vpc_info.vpcs[0].tags == (second_tags | combine(name_tags)) + + - name: modify tags with purge + ec2_vpc_net: + state: present + cidr_block: "{{ vpc_cidr }}" + name: "{{ vpc_name }}" + tags: "{{ third_tags }}" + purge_tags: false + register: result + - ec2_vpc_net_info: + filters: + "tag:Name": "{{ vpc_name }}" + register: vpc_info + + - name: assert the VPC has Name and Ansible tags + assert: + that: + - result is successful + - result is changed + - result.vpc.id == vpc_1 + - result.vpc.tags == (final_tags | combine(name_tags)) + - vpc_info.vpcs | length == 1 + - vpc_info.vpcs[0].tags == (final_tags | combine(name_tags)) + + - name: modify tags with purge (no change) + ec2_vpc_net: + state: present + cidr_block: "{{ vpc_cidr }}" + name: "{{ vpc_name }}" + tags: "{{ third_tags }}" + purge_tags: false register: result - ec2_vpc_net_info: filters: - "tag:Name": "{{ resource_prefix }}" + "tag:Name": "{{ vpc_name }}" register: vpc_info - name: assert the VPC has Name and Ansible tags @@ -753,50 +940,43 @@ - result is successful - result is not changed - result.vpc.id == vpc_1 - - result.vpc.tags|length == 2 - - result.vpc.tags.Ansible == "Test" - - result.vpc.tags.Name == resource_prefix + - result.vpc.tags == (final_tags | combine(name_tags)) - vpc_info.vpcs | length == 1 - - vpc_info.vpcs[0].tags|length == 2 - - vpc_info.vpcs[0].tags.Ansible == "Test" - - vpc_info.vpcs[0].tags.Name == resource_prefix + - vpc_info.vpcs[0].tags == (final_tags | combine(name_tags)) # ============================================================ - # #62678 - #- name: modify CIDR (check mode) - # ec2_vpc_net: - # state: present - # cidr_block: - # - "{{ vpc_cidr }}" - # - "{{ vpc_cidr_a }}" - # name: "{{ resource_prefix }}" - # check_mode: true - # register: result - #- ec2_vpc_net_info: - # filters: - # "tag:Name": "{{ resource_prefix }}" - # register: vpc_info - - #- name: Check the CIDRs weren't changed - # assert: - # that: - # - result is successful - # - result is changed - # - result.vpc.id == vpc_1 - # - vpc_info.vpcs | length == 1 - # - vpc_info.vpcs[0].cidr_block == vpc_cidr - # - vpc_cidr in (result.vpc.cidr_block_association_set | map(attribute="cidr_block") | list) - # - vpc_cidr_a not in (result.vpc.cidr_block_association_set | map(attribute="cidr_block") | list) - # - vpc_cidr_b not in (result.vpc.cidr_block_association_set | map(attribute="cidr_block") | list) - # - vpc_info.vpcs[0].cidr_block_association_set | length == 1 - # - vpc_info.vpcs[0].cidr_block_association_set[0].association_id.startswith("vpc-cidr-assoc-") - # - vpc_info.vpcs[0].cidr_block_association_set[1].association_id.startswith("vpc-cidr-assoc-") - # - vpc_info.vpcs[0].cidr_block_association_set[0].cidr_block_state.state in ["associated", "associating"] - # - vpc_info.vpcs[0].cidr_block_association_set[1].cidr_block_state.state in ["associated", "associating"] - # - vpc_cidr in (vpc_info.vpcs[0].cidr_block_association_set | map(attribute="cidr_block") | list) - # - vpc_cidr_a not in (vpc_info.vpcs[0].cidr_block_association_set | map(attribute="cidr_block") | list) - # - vpc_cidr_b not in (result.vpc.cidr_block_association_set | map(attribute="cidr_block") | list) + - name: modify CIDR (check mode) + ec2_vpc_net: + state: present + cidr_block: + - "{{ vpc_cidr }}" + - "{{ vpc_cidr_a }}" + name: "{{ vpc_name }}" + check_mode: true + register: result + - ec2_vpc_net_info: + filters: + "tag:Name": "{{ vpc_name }}" + register: vpc_info + + - name: Check the CIDRs weren't changed + assert: + that: + - result is successful + - result is changed + - result.vpc.id == vpc_1 + - vpc_info.vpcs | length == 1 + - vpc_info.vpcs[0].cidr_block == vpc_cidr + - vpc_cidr in (result.vpc.cidr_block_association_set | map(attribute="cidr_block") | list) + - vpc_cidr_a not in (result.vpc.cidr_block_association_set | map(attribute="cidr_block") | list) + - vpc_cidr_b not in (result.vpc.cidr_block_association_set | map(attribute="cidr_block") | list) + - vpc_info.vpcs[0].cidr_block_association_set | length == 1 + - vpc_info.vpcs[0].cidr_block_association_set[0].association_id.startswith("vpc-cidr-assoc-") + - vpc_info.vpcs[0].cidr_block_association_set[0].cidr_block_state.state in ["associated", "associating"] + - vpc_cidr in (vpc_info.vpcs[0].cidr_block_association_set | map(attribute="cidr_block") | list) + - vpc_cidr_a not in (vpc_info.vpcs[0].cidr_block_association_set | map(attribute="cidr_block") | list) + - vpc_cidr_b not in (result.vpc.cidr_block_association_set | map(attribute="cidr_block") | list) - name: modify CIDR ec2_vpc_net: @@ -804,11 +984,11 @@ cidr_block: - "{{ vpc_cidr }}" - "{{ vpc_cidr_a }}" - name: "{{ resource_prefix }}" + name: "{{ vpc_name }}" register: result - ec2_vpc_net_info: filters: - "tag:Name": "{{ resource_prefix }}" + "tag:Name": "{{ vpc_name }}" register: vpc_info - name: assert the CIDRs changed @@ -843,11 +1023,11 @@ cidr_block: - "{{ vpc_cidr }}" - "{{ vpc_cidr_a }}" - name: "{{ resource_prefix }}" + name: "{{ vpc_name }}" register: result - ec2_vpc_net_info: filters: - "tag:Name": "{{ resource_prefix }}" + "tag:Name": "{{ vpc_name }}" register: vpc_info - name: assert the CIDRs didn't change @@ -876,39 +1056,38 @@ - vpc_cidr_a in (vpc_info.vpcs[0].cidr_block_association_set | map(attribute="cidr_block") | list) - vpc_cidr_b not in (vpc_info.vpcs[0].cidr_block_association_set | map(attribute="cidr_block") | list) - # #62678 - #- name: modify CIDR - no purge (check mode) - # ec2_vpc_net: - # state: present - # cidr_block: - # - "{{ vpc_cidr }}" - # - "{{ vpc_cidr_b }}" - # name: "{{ resource_prefix }}" - # check_mode: true - # register: result - #- ec2_vpc_net_info: - # filters: - # "tag:Name": "{{ resource_prefix }}" - # register: vpc_info - - #- name: Check the CIDRs weren't changed - # assert: - # that: - # - result is successful - # - result is changed - # - vpc_info.vpcs | length == 1 - # - vpc_info.vpcs[0].cidr_block == vpc_cidr - # - vpc_cidr in (result.vpc.cidr_block_association_set | map(attribute="cidr_block") | list) - # - vpc_cidr_a in (result.vpc.cidr_block_association_set | map(attribute="cidr_block") | list) - # - vpc_cidr_b not in (result.vpc.cidr_block_association_set | map(attribute="cidr_block") | list) - # - vpc_info.vpcs[0].cidr_block_association_set | length == 2 - # - vpc_info.vpcs[0].cidr_block_association_set[0].association_id.startswith("vpc-cidr-assoc-") - # - vpc_info.vpcs[0].cidr_block_association_set[1].association_id.startswith("vpc-cidr-assoc-") - # - vpc_info.vpcs[0].cidr_block_association_set[0].cidr_block_state.state in ["associated", "associating"] - # - vpc_info.vpcs[0].cidr_block_association_set[1].cidr_block_state.state in ["associated", "associating"] - # - vpc_cidr in (vpc_info.vpcs[0].cidr_block_association_set | map(attribute="cidr_block") | list) - # - vpc_cidr_a in (vpc_info.vpcs[0].cidr_block_association_set | map(attribute="cidr_block") | list) - # - vpc_cidr_b not in (vpc_info.vpcs[0].cidr_block_association_set | map(attribute="cidr_block") | list) + - name: modify CIDR - no purge (check mode) + ec2_vpc_net: + state: present + cidr_block: + - "{{ vpc_cidr }}" + - "{{ vpc_cidr_b }}" + name: "{{ vpc_name }}" + check_mode: true + register: result + - ec2_vpc_net_info: + filters: + "tag:Name": "{{ vpc_name }}" + register: vpc_info + + - name: Check the CIDRs weren't changed + assert: + that: + - result is successful + - result is changed + - vpc_info.vpcs | length == 1 + - vpc_info.vpcs[0].cidr_block == vpc_cidr + - vpc_cidr in (result.vpc.cidr_block_association_set | map(attribute="cidr_block") | list) + - vpc_cidr_a in (result.vpc.cidr_block_association_set | map(attribute="cidr_block") | list) + - vpc_cidr_b not in (result.vpc.cidr_block_association_set | map(attribute="cidr_block") | list) + - vpc_info.vpcs[0].cidr_block_association_set | length == 2 + - vpc_info.vpcs[0].cidr_block_association_set[0].association_id.startswith("vpc-cidr-assoc-") + - vpc_info.vpcs[0].cidr_block_association_set[1].association_id.startswith("vpc-cidr-assoc-") + - vpc_info.vpcs[0].cidr_block_association_set[0].cidr_block_state.state in ["associated", "associating"] + - vpc_info.vpcs[0].cidr_block_association_set[1].cidr_block_state.state in ["associated", "associating"] + - vpc_cidr in (vpc_info.vpcs[0].cidr_block_association_set | map(attribute="cidr_block") | list) + - vpc_cidr_a in (vpc_info.vpcs[0].cidr_block_association_set | map(attribute="cidr_block") | list) + - vpc_cidr_b not in (vpc_info.vpcs[0].cidr_block_association_set | map(attribute="cidr_block") | list) - name: modify CIDR - no purge ec2_vpc_net: @@ -916,11 +1095,11 @@ cidr_block: - "{{ vpc_cidr }}" - "{{ vpc_cidr_b }}" - name: "{{ resource_prefix }}" + name: "{{ vpc_name }}" register: result - ec2_vpc_net_info: filters: - "tag:Name": "{{ resource_prefix }}" + "tag:Name": "{{ vpc_name }}" register: vpc_info - name: assert the CIDRs changed @@ -959,11 +1138,11 @@ cidr_block: - "{{ vpc_cidr }}" - "{{ vpc_cidr_b }}" - name: "{{ resource_prefix }}" + name: "{{ vpc_name }}" register: result - ec2_vpc_net_info: filters: - "tag:Name": "{{ resource_prefix }}" + "tag:Name": "{{ vpc_name }}" register: vpc_info - name: assert the CIDRs didn't change @@ -1002,11 +1181,11 @@ - "{{ vpc_cidr }}" - "{{ vpc_cidr_a }}" - "{{ vpc_cidr_b }}" - name: "{{ resource_prefix }}" + name: "{{ vpc_name }}" register: result - ec2_vpc_net_info: filters: - "tag:Name": "{{ resource_prefix }}" + "tag:Name": "{{ vpc_name }}" register: vpc_info - name: assert the CIDRs didn't change @@ -1045,11 +1224,11 @@ - "{{ vpc_cidr }}" - "{{ vpc_cidr_a }}" - "{{ vpc_cidr_b }}" - name: "{{ resource_prefix }}" + name: "{{ vpc_name }}" register: result - ec2_vpc_net_info: filters: - "tag:Name": "{{ resource_prefix }}" + "tag:Name": "{{ vpc_name }}" register: vpc_info - name: assert the CIDRs didn't change @@ -1088,11 +1267,11 @@ - "{{ vpc_cidr }}" - "{{ vpc_cidr_b }}" - "{{ vpc_cidr_a }}" - name: "{{ resource_prefix }}" + name: "{{ vpc_name }}" register: result - ec2_vpc_net_info: filters: - "tag:Name": "{{ resource_prefix }}" + "tag:Name": "{{ vpc_name }}" register: vpc_info - name: assert the CIDRs didn't change @@ -1131,11 +1310,11 @@ - "{{ vpc_cidr }}" - "{{ vpc_cidr_b }}" - "{{ vpc_cidr_a }}" - name: "{{ resource_prefix }}" + name: "{{ vpc_name }}" register: result - ec2_vpc_net_info: filters: - "tag:Name": "{{ resource_prefix }}" + "tag:Name": "{{ vpc_name }}" register: vpc_info - name: assert the CIDRs didn't change @@ -1167,39 +1346,38 @@ - vpc_cidr_a in (vpc_info.vpcs[0].cidr_block_association_set | map(attribute="cidr_block") | list) - vpc_cidr_b in (vpc_info.vpcs[0].cidr_block_association_set | map(attribute="cidr_block") | list) - # #62678 - #- name: modify CIDR - purge (check mode) - # ec2_vpc_net: - # state: present - # cidr_block: - # - "{{ vpc_cidr }}" - # - "{{ vpc_cidr_b }}" - # name: "{{ resource_prefix }}" - # purge_cidrs: yes - # check_mode: true - # register: result - #- ec2_vpc_net_info: - # filters: - # "tag:Name": "{{ resource_prefix }}" - # register: vpc_info - - #- name: Check the CIDRs weren't changed - # assert: - # that: - # - result is successful - # - result is changed - # - vpc_info.vpcs | length == 1 - # - vpc_info.vpcs[0].cidr_block == vpc_cidr - # - vpc_info.vpcs[0].cidr_block_association_set | length == 3 - # - vpc_info.vpcs[0].cidr_block_association_set[0].association_id.startswith("vpc-cidr-assoc-") - # - vpc_info.vpcs[0].cidr_block_association_set[1].association_id.startswith("vpc-cidr-assoc-") - # - vpc_info.vpcs[0].cidr_block_association_set[2].association_id.startswith("vpc-cidr-assoc-") - # - vpc_info.vpcs[0].cidr_block_association_set[0].cidr_block_state.state in ["associated", "associating"] - # - vpc_info.vpcs[0].cidr_block_association_set[1].cidr_block_state.state in ["associated", "associating"] - # - vpc_info.vpcs[0].cidr_block_association_set[2].cidr_block_state.state in ["associated", "associating"] - # - vpc_cidr in (vpc_info.vpcs[0].cidr_block_association_set | map(attribute="cidr_block") | list) - # - vpc_cidr_a in (vpc_info.vpcs[0].cidr_block_association_set | map(attribute="cidr_block") | list) - # - vpc_cidr_b in (vpc_info.vpcs[0].cidr_block_association_set | map(attribute="cidr_block") | list) + - name: modify CIDR - purge (check mode) + ec2_vpc_net: + state: present + cidr_block: + - "{{ vpc_cidr }}" + - "{{ vpc_cidr_b }}" + name: "{{ vpc_name }}" + purge_cidrs: yes + check_mode: true + register: result + - ec2_vpc_net_info: + filters: + "tag:Name": "{{ vpc_name }}" + register: vpc_info + + - name: Check the CIDRs weren't changed + assert: + that: + - result is successful + - result is changed + - vpc_info.vpcs | length == 1 + - vpc_info.vpcs[0].cidr_block == vpc_cidr + - vpc_info.vpcs[0].cidr_block_association_set | length == 3 + - vpc_info.vpcs[0].cidr_block_association_set[0].association_id.startswith("vpc-cidr-assoc-") + - vpc_info.vpcs[0].cidr_block_association_set[1].association_id.startswith("vpc-cidr-assoc-") + - vpc_info.vpcs[0].cidr_block_association_set[2].association_id.startswith("vpc-cidr-assoc-") + - vpc_info.vpcs[0].cidr_block_association_set[0].cidr_block_state.state in ["associated", "associating"] + - vpc_info.vpcs[0].cidr_block_association_set[1].cidr_block_state.state in ["associated", "associating"] + - vpc_info.vpcs[0].cidr_block_association_set[2].cidr_block_state.state in ["associated", "associating"] + - vpc_cidr in (vpc_info.vpcs[0].cidr_block_association_set | map(attribute="cidr_block") | list) + - vpc_cidr_a in (vpc_info.vpcs[0].cidr_block_association_set | map(attribute="cidr_block") | list) + - vpc_cidr_b in (vpc_info.vpcs[0].cidr_block_association_set | map(attribute="cidr_block") | list) - name: modify CIDR - purge ec2_vpc_net: @@ -1207,12 +1385,12 @@ cidr_block: - "{{ vpc_cidr }}" - "{{ vpc_cidr_b }}" - name: "{{ resource_prefix }}" + name: "{{ vpc_name }}" purge_cidrs: yes register: result - ec2_vpc_net_info: filters: - "tag:Name": "{{ resource_prefix }}" + "tag:Name": "{{ vpc_name }}" register: vpc_info - name: assert the CIDRs changed @@ -1239,12 +1417,12 @@ cidr_block: - "{{ vpc_cidr }}" - "{{ vpc_cidr_b }}" - name: "{{ resource_prefix }}" + name: "{{ vpc_name }}" purge_cidrs: yes register: result - ec2_vpc_net_info: filters: - "tag:Name": "{{ resource_prefix }}" + "tag:Name": "{{ vpc_name }}" register: vpc_info - name: assert the CIDRs didn't change @@ -1271,7 +1449,7 @@ ec2_vpc_net: state: present cidr_block: "{{ vpc_cidr }}" - name: "{{ resource_prefix }}" + name: "{{ vpc_name }}" ipv6_cidr: False check_mode: true register: result @@ -1287,7 +1465,7 @@ ec2_vpc_net: state: present cidr_block: "{{ vpc_cidr }}" - name: "{{ resource_prefix }}" + name: "{{ vpc_name }}" ipv6_cidr: True register: result - name: assert configuration did not change @@ -1300,7 +1478,7 @@ ec2_vpc_net: state: present cidr_block: "{{ vpc_cidr }}" - name: "{{ resource_prefix }}" + name: "{{ vpc_name }}" ipv6_cidr: False register: result - name: assert IPv6 CIDR association removed from VPC @@ -1317,7 +1495,7 @@ ec2_vpc_net: state: present cidr_block: "{{ vpc_cidr }}" - name: "{{ resource_prefix }}" + name: "{{ vpc_name }}" ipv6_cidr: True register: result - name: assert configuration change @@ -1335,12 +1513,13 @@ - result.vpc.ipv6_cidr_block_association_set[1].ipv6_cidr_block | ansible.netcommon.ipv6 - result.vpc.ipv6_cidr_block_association_set[1].ipv6_cidr_block_state.state in ["associated", "associating"] + # ============================================================ - name: test check mode to delete a VPC ec2_vpc_net: cidr_block: "{{ vpc_cidr }}" - name: "{{ resource_prefix }}" + name: "{{ vpc_name }}" state: absent check_mode: true register: result @@ -1354,10 +1533,14 @@ always: + - name: Describe VPCs before deleting them (for debugging) + ec2_vpc_net_info: + ignore_errors: true + - name: replace the DHCP options set so the new one can be deleted ec2_vpc_net: cidr_block: "{{ vpc_cidr }}" - name: "{{ resource_prefix }}" + name: "{{ vpc_name }}" state: present multi_ok: no dhcp_opts_id: "{{ default_dhcp_options_id }}" @@ -1369,13 +1552,9 @@ state: absent ignore_errors: true - - name: Describe VPCs before deleting them (for debugging) - ec2_vpc_net_info: - ignore_errors: true - - name: remove the VPC ec2_vpc_net: cidr_block: "{{ vpc_cidr }}" - name: "{{ resource_prefix }}" + name: "{{ vpc_name }}" state: absent ignore_errors: true