diff --git a/plugins/modules/ec2_vpc_net.py b/plugins/modules/ec2_vpc_net.py index 6a254843d8f..2be5baa04ca 100644 --- a/plugins/modules/ec2_vpc_net.py +++ b/plugins/modules/ec2_vpc_net.py @@ -30,13 +30,14 @@ 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. - The first in the list will be used as the primary CIDR and is used in conjunction with I(name) to ensure idempotence. - required: yes + - Required when I(vpc_id) is not set. type: list elements: str ipv6_cidr: @@ -47,24 +48,25 @@ 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: @@ -151,6 +153,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 @@ -305,31 +313,26 @@ def update_vpc_tags(connection, module, vpc_id, tags, name, purge_tags): if not changed or module.check_mode: return changed - 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) - 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)) + if dhcp_id is None: + return False + if vpc_obj['DhcpOptionsId'] == dhcp_id: + return False + if module.check_mode: + return True - # 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) + 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)) - return True - else: - return False + return True -def create_vpc(connection, module, cidr_block, tenancy, tags): +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") @@ -337,9 +340,16 @@ def create_vpc(connection, module, cidr_block, tenancy, tags): 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 + try: vpc_obj = connection.create_vpc(aws_retry=True, **create_args) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: @@ -352,7 +362,7 @@ def create_vpc(connection, module, cidr_block, tenancy, tags): 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) @@ -362,6 +372,11 @@ def create_vpc(connection, module, cidr_block, tenancy, tags): 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: @@ -384,6 +399,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: @@ -414,6 +435,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('/') @@ -431,6 +455,110 @@ 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 + + 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 @@ -447,12 +575,40 @@ def delete_vpc(connection, module, 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=False), vpc_id=dict(type='str', required=False, default=None), - cidr_block=dict(type='list', required=True, elements='str'), + 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), @@ -464,10 +620,14 @@ def main(): 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=[['name', 'vpc_id']], + required_one_of=required_one_of, supports_check_mode=True ) @@ -481,7 +641,7 @@ def main(): name = module.params.get('name') vpc_id = module.params.get('vpc_id') - cidr_block = get_cidr_network_bits(module, module.params.get('cidr_block')) + 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') @@ -505,147 +665,55 @@ def main(): 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 - is_new_vpc = False if vpc_id is None: - is_new_vpc = True 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) + 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, purge_tags): - 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': changed = delete_vpc(connection, module, vpc_id) 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..9d67490dd38 100644 --- a/tests/integration/targets/ec2_vpc_net/tasks/main.yml +++ b/tests/integration/targets/ec2_vpc_net/tasks/main.yml @@ -6,7 +6,32 @@ 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 block: # ============================================================ @@ -24,14 +49,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 +68,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 +88,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 +123,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 +136,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 +167,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 +203,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 +236,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 +256,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 +281,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 +345,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 +353,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 +365,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 - - not result.vpc - 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 + - 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 +432,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 +449,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 +460,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 +481,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 +503,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 @@ -474,13 +527,13 @@ # ec2_vpc_net: # state: present # cidr_block: "{{ vpc_cidr }}" - # name: "{{ resource_prefix }}" + # name: "{{ vpc_name }}" # dns_hostnames: False # register: result # check_mode: True #- ec2_vpc_net_info: # filters: - # "tag:Name": "{{ resource_prefix }}" + # "tag:Name": "{{ vpc_name }}" # register: vpc_info #- name: assert changed was set but not made @@ -496,12 +549,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 @@ -518,12 +571,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 @@ -541,14 +594,14 @@ # ec2_vpc_net: # state: present # cidr_block: "{{ vpc_cidr }}" - # name: "{{ resource_prefix }}" + # name: "{{ vpc_name }}" # dns_hostnames: False # dns_support: False # check_mode: True # register: result #- ec2_vpc_net_info: # filters: - # "tag:Name": "{{ resource_prefix }}" + # "tag:Name": "{{ vpc_name }}" # register: vpc_info #- name: assert changed was set but not made @@ -565,13 +618,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 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 @@ -612,12 +665,12 @@ # ec2_vpc_net: # state: present # cidr_block: "{{ vpc_cidr }}" - # name: "{{ resource_prefix }}" + # name: "{{ vpc_name }}" # register: result # check_mode: True #- ec2_vpc_net_info: # filters: - # "tag:Name": "{{ resource_prefix }}" + # "tag:Name": "{{ vpc_name }}" # register: vpc_info #- name: assert a change was made @@ -634,11 +687,11 @@ ec2_vpc_net: state: present cidr_block: "{{ vpc_cidr }}" - 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 a change was made @@ -655,11 +708,11 @@ ec2_vpc_net: state: present cidr_block: "{{ vpc_cidr }}" - 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 a change was not made @@ -678,14 +731,14 @@ ec2_vpc_net: state: present cidr_block: "{{ vpc_cidr }}" - name: "{{ resource_prefix }}" + name: "{{ vpc_name }}" tags: Ansible: Test 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,22 +748,22 @@ - 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 == vpc_name - name: modify tags ec2_vpc_net: state: present cidr_block: "{{ vpc_cidr }}" - name: "{{ resource_prefix }}" + name: "{{ vpc_name }}" tags: Ansible: Test register: result - ec2_vpc_net_info: filters: - "tag:Name": "{{ resource_prefix }}" + "tag:Name": "{{ vpc_name }}" register: vpc_info until: - vpc_info.vpcs | length == 1 @@ -726,17 +779,17 @@ - result.vpc.id == vpc_1 - result.vpc.tags | length == 2 - result.vpc.tags.Ansible == "Test" - - result.vpc.tags.Name == resource_prefix + - result.vpc.tags.Name == vpc_name - 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.Name == vpc_name - name: modify tags (no change) ec2_vpc_net: state: present cidr_block: "{{ vpc_cidr }}" - name: "{{ resource_prefix }}" + name: "{{ vpc_name }}" dns_support: True dns_hostnames: True tags: @@ -744,7 +797,7 @@ 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 @@ -755,11 +808,11 @@ - result.vpc.id == vpc_1 - result.vpc.tags|length == 2 - result.vpc.tags.Ansible == "Test" - - result.vpc.tags.Name == resource_prefix + - result.vpc.tags.Name == vpc_name - 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.Name == vpc_name # ============================================================ @@ -770,12 +823,12 @@ # cidr_block: # - "{{ vpc_cidr }}" # - "{{ vpc_cidr_a }}" - # 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 the CIDRs weren't changed @@ -804,11 +857,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 +896,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 @@ -883,12 +936,12 @@ # cidr_block: # - "{{ vpc_cidr }}" # - "{{ vpc_cidr_b }}" - # 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 the CIDRs weren't changed @@ -916,11 +969,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 +1012,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 +1055,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 +1098,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 +1141,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 +1184,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 @@ -1174,13 +1227,13 @@ # cidr_block: # - "{{ vpc_cidr }}" # - "{{ vpc_cidr_b }}" - # name: "{{ resource_prefix }}" + # name: "{{ vpc_name }}" # purge_cidrs: yes # check_mode: true # register: result #- ec2_vpc_net_info: # filters: - # "tag:Name": "{{ resource_prefix }}" + # "tag:Name": "{{ vpc_name }}" # register: vpc_info #- name: Check the CIDRs weren't changed @@ -1207,12 +1260,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 +1292,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 +1324,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 +1340,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 +1353,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 +1370,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 +1388,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 @@ -1357,7 +1411,7 @@ - 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 }}" @@ -1376,6 +1430,6 @@ - name: remove the VPC ec2_vpc_net: cidr_block: "{{ vpc_cidr }}" - name: "{{ resource_prefix }}" + name: "{{ vpc_name }}" state: absent ignore_errors: true