diff --git a/changelogs/fragments/936-stabilize-ec2-eip.yml b/changelogs/fragments/936-stabilize-ec2-eip.yml new file mode 100644 index 00000000000..eec8e2cf49c --- /dev/null +++ b/changelogs/fragments/936-stabilize-ec2-eip.yml @@ -0,0 +1,2 @@ +minor_changes: + - ec2_eip - refactor module by fixing check_mode and more clear return obj. added integration tests (https://github.com/ansible-collections/community.aws/pull/936) \ No newline at end of file diff --git a/plugins/modules/ec2_eip.py b/plugins/modules/ec2_eip.py index ca883e5f715..e0031eaf10a 100644 --- a/plugins/modules/ec2_eip.py +++ b/plugins/modules/ec2_eip.py @@ -27,8 +27,8 @@ public_ip: description: - The IP address of a previously allocated EIP. - - When I(public_ip=present) and device is specified, the EIP is associated with the device. - - When I(public_ip=absent) and device is specified, the EIP is disassociated from the device. + - When I(state=present) and device is specified, the EIP is associated with the device. + - When I(state=absent) and device is specified, the EIP is disassociated from the device. aliases: [ ip ] type: str state: @@ -328,7 +328,7 @@ def find_address(ec2, module, public_ip, device_id, is_instance=True): except is_boto3_error_code('InvalidAddress.NotFound') as e: # If we're releasing and we can't find it, it's already gone... if module.params.get('state') == 'absent': - module.exit_json(changed=False) + module.exit_json(changed=False, disassociated=False, released=False) module.fail_json_aws(e, msg="Couldn't obtain list of existing Elastic IP addresses") addresses = addresses["Addresses"] @@ -385,6 +385,8 @@ def allocate_address(ec2, module, domain, reuse_existing_ip_allowed, check_mode, return allocate_address_from_pool(ec2, module, domain, check_mode, public_ipv4_pool), True try: + if check_mode: + return None, True result = ec2.allocate_address(Domain=domain, aws_retry=True), True except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: module.fail_json_aws(e, msg="Couldn't allocate Elastic IP address") @@ -493,8 +495,11 @@ def ensure_absent(ec2, module, address, device_id, check_mode, is_instance=True) def allocate_address_from_pool(ec2, module, domain, check_mode, public_ipv4_pool): - # type: (EC2Connection, str, bool, str) -> Address + # type: (EC2Connection, AnsibleAWSModule, str, bool, str) -> Address """ Overrides botocore's allocate_address function to support BYOIP """ + if check_mode: + return None + params = {} if domain is not None: @@ -503,9 +508,6 @@ def allocate_address_from_pool(ec2, module, domain, check_mode, public_ipv4_pool if public_ipv4_pool is not None: params['PublicIpv4Pool'] = public_ipv4_pool - if check_mode: - params['DryRun'] = 'true' - try: result = ec2.allocate_address(aws_retry=True, **params) except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: @@ -606,19 +608,33 @@ def main(): reuse_existing_ip_allowed, allow_reassociation, module.check_mode, is_instance=is_instance ) + if 'allocation_id' not in result: + # Don't check tags on check_mode here - no EIP to pass through + module.exit_json(**result) else: if address: - changed = False + result = { + 'changed': False, + 'public_ip': address['PublicIp'], + 'allocation_id': address['AllocationId'] + } else: address, changed = allocate_address( ec2, module, domain, reuse_existing_ip_allowed, module.check_mode, tag_dict, public_ipv4_pool ) - result = { - 'changed': changed, - 'public_ip': address['PublicIp'], - 'allocation_id': address['AllocationId'] - } + if address: + result = { + 'changed': changed, + 'public_ip': address['PublicIp'], + 'allocation_id': address['AllocationId'] + } + else: + # Don't check tags on check_mode here - no EIP to pass through + result = { + 'changed': changed + } + module.exit_json(**result) result['changed'] |= ensure_ec2_tags( ec2, module, result['allocation_id'], @@ -633,21 +649,21 @@ def main(): released = release_address(ec2, module, address, module.check_mode) result = { 'changed': True, - 'disassociated': disassociated, - 'released': released + 'disassociated': disassociated['changed'], + 'released': released['changed'] } else: result = { 'changed': disassociated['changed'], - 'disassociated': disassociated, - 'released': {'changed': False} + 'disassociated': disassociated['changed'], + 'released': False } else: released = release_address(ec2, module, address, module.check_mode) result = { 'changed': released['changed'], - 'disassociated': {'changed': False}, - 'released': released + 'disassociated': False, + 'released': released['changed'] } except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: diff --git a/plugins/modules/ec2_eip_info.py b/plugins/modules/ec2_eip_info.py index 4f560429e12..31d8145742b 100644 --- a/plugins/modules/ec2_eip_info.py +++ b/plugins/modules/ec2_eip_info.py @@ -44,7 +44,7 @@ register: my_vm_eips - ansible.builtin.debug: - msg: "{{ my_vm_eips.addresses | json_query(\"[?private_ip_address=='10.0.0.5']\") }}" + msg: "{{ my_vm_eips.addresses | selectattr('private_ip_address', 'equalto', '10.0.0.5') }}" - name: List all EIP addresses for several VMs. community.aws.ec2_eip_info: diff --git a/tests/integration/targets/ec2_asg/aliases b/tests/integration/targets/ec2_asg/aliases index a1db4545242..6f8f60998c7 100644 --- a/tests/integration/targets/ec2_asg/aliases +++ b/tests/integration/targets/ec2_asg/aliases @@ -1,6 +1,5 @@ # reason: slow # Tests take around 30 minutes -# reason: broken -disabled +slow cloud/aws diff --git a/tests/integration/targets/ec2_asg/defaults/main.yml b/tests/integration/targets/ec2_asg/defaults/main.yml index 80bf25cd001..be66f56f2d3 100644 --- a/tests/integration/targets/ec2_asg/defaults/main.yml +++ b/tests/integration/targets/ec2_asg/defaults/main.yml @@ -2,3 +2,4 @@ # defaults file for ec2_asg # Amazon Linux 2 AMI 2019.06.12 (HVM), GP2 Volume Type ec2_ami_name: 'amzn2-ami-hvm-2.0.20190612-x86_64-gp2' +load_balancer_name: "{{ tiny_prefix }}-lb" diff --git a/tests/integration/targets/ec2_asg/tasks/main.yml b/tests/integration/targets/ec2_asg/tasks/main.yml index 7f196442904..800c167bde8 100644 --- a/tests/integration/targets/ec2_asg/tasks/main.yml +++ b/tests/integration/targets/ec2_asg/tasks/main.yml @@ -47,15 +47,7 @@ - set_fact: ec2_ami_image: '{{ ec2_amis.images[0].image_id }}' - - name: load balancer name has to be less than 32 characters - # the 8 digit identifier at the end of resource_prefix helps determine during which test something - # was created - set_fact: - load_balancer_name: "{{ item }}-lb" - loop: "{{ resource_prefix | regex_findall('.{8}$') }}" - # Set up the testing dependencies: VPC, subnet, security group, and two launch configurations - - name: Create VPC for use in testing ec2_vpc_net: name: "{{ resource_prefix }}-vpc" @@ -239,8 +231,6 @@ that: - output is not changed - # - name: pause for a bit to make sure that the group can't be trivially deleted - # pause: seconds=30 - name: kill asg ec2_asg: name: "{{ resource_prefix }}-asg" @@ -349,7 +339,6 @@ # ============================================================ # grow scaling group to 3 - - name: add 2 more instances wait for instances to be deemed healthy (ELB) ec2_asg: name: "{{ resource_prefix }}-asg" @@ -373,7 +362,6 @@ # ============================================================ # Test max_instance_lifetime option - - name: enable asg max_instance_lifetime ec2_asg: name: "{{ resource_prefix }}-asg" @@ -409,8 +397,7 @@ # ============================================================ - # # perform rolling replace with different launch configuration - + # perform rolling replace with different launch configuration - name: perform rolling update to new AMI ec2_asg: name: "{{ resource_prefix }}-asg" @@ -442,7 +429,6 @@ # ============================================================ # perform rolling replace with the original launch configuration - - name: perform rolling update to new AMI while removing the load balancer ec2_asg: name: "{{ resource_prefix }}-asg" @@ -475,10 +461,6 @@ # ============================================================ # perform rolling replace with new launch configuration and lc_check:false - - # Note - this is done async so we can query asg_facts during - # the execution. Issues #28087 and #35993 result in correct - # end result, but spin up extraneous instances during execution. - name: "perform rolling update to new AMI with lc_check: false" ec2_asg: name: "{{ resource_prefix }}-asg" @@ -496,32 +478,18 @@ lc_check: false wait_timeout: 1800 state: present - async: 1800 - poll: 0 - register: asg_job - - name: get ec2_asg info for 3 minutes + # Collect ec2_asg_info + - name: get ec2_asg info ec2_asg_info: name: "{{ resource_prefix }}-asg" register: output - loop_control: - pause: 15 - loop: "{{ range(12) | list }}" - # Since we started with 3 servers and replace all of them. - # We should see 6 servers total. + # Since we started with 3 instances and replace all of them. + # We should see only 3 instances total. - assert: that: - - output | community.general.json_query(inst_id_json_query) | unique | length == 6 - vars: - inst_id_json_query: results[].results[].instances[].instance_id - - - name: Ensure ec2_asg task completes - async_status: jid="{{ asg_job.ansible_job_id }}" - register: status - until: status is finished - retries: 200 - delay: 15 + - output.results[0].instances | length == 3 # ============================================================ @@ -533,10 +501,6 @@ async: 400 # Create new asg with replace_all_instances and lc_check:false - - # Note - this is done async so we can query asg_facts during - # the execution. Issues #28087 results in correct - # end result, but spin up extraneous instances during execution. - name: "new asg with lc_check: false" ec2_asg: name: "{{ resource_prefix }}-asg" @@ -554,33 +518,18 @@ lc_check: false wait_timeout: 1800 state: present - async: 1800 - poll: 0 - register: asg_job - # Collect ec2_asg_info for 3 minutes + # Collect ec2_asg_info - name: get ec2_asg information ec2_asg_info: name: "{{ resource_prefix }}-asg" register: output - loop_control: - pause: 15 - loop: "{{ range(12) | list }}" # Get all instance_ids we saw and assert we saw number expected # Should only see 3 (don't replace instances we just created) - assert: that: - - output | community.general.json_query(inst_id_json_query) | unique | length == 3 - vars: - inst_id_json_query: results[].results[].instances[].instance_id - - - name: Ensure ec2_asg task completes - async_status: jid="{{ asg_job.ansible_job_id }}" - register: status - until: status is finished - retries: 200 - delay: 15 + - output.results[0].instances | length == 3 # we need a launch template, otherwise we cannot test the mixed instance policy - name: create launch template for autoscaling group to test its mixed instances policy @@ -610,7 +559,7 @@ mixed_instances_policy: instance_types: - t3.micro - - t3a.micro + - t2.nano wait_for_instances: yes register: output @@ -618,7 +567,7 @@ that: - "output.mixed_instances_policy | length == 2" - "output.mixed_instances_policy[0] == 't3.micro'" - - "output.mixed_instances_policy[1] == 't3a.micro'" + - "output.mixed_instances_policy[1] == 't2.nano'" - name: update autoscaling group with mixed-instances policy with instances_distribution ec2_asg: @@ -633,7 +582,7 @@ mixed_instances_policy: instance_types: - t3.micro - - t3a.micro + - t2.nano instances_distribution: on_demand_percentage_above_base_capacity: 0 spot_allocation_strategy: capacity-optimized @@ -643,7 +592,7 @@ - assert: that: - "output.mixed_instances_policy_full['launch_template']['overrides'][0]['instance_type'] == 't3.micro'" - - "output.mixed_instances_policy_full['launch_template']['overrides'][1]['instance_type'] == 't3a.micro'" + - "output.mixed_instances_policy_full['launch_template']['overrides'][1]['instance_type'] == 't2.nano'" - "output.mixed_instances_policy_full['instances_distribution']['on_demand_percentage_above_base_capacity'] == 0" - "output.mixed_instances_policy_full['instances_distribution']['spot_allocation_strategy'] == 'capacity-optimized'" @@ -683,7 +632,7 @@ - name: update autoscaling group with tg1 ec2_asg: name: "{{ resource_prefix }}-asg" - launch_template: + launch_template: launch_template_name: "{{ resource_prefix }}-lt" target_group_arns: - "{{ out_tg1.target_group_arn }}" @@ -701,7 +650,7 @@ - name: update autoscaling group add tg2 ec2_asg: name: "{{ resource_prefix }}-asg" - launch_template: + launch_template: launch_template_name: "{{ resource_prefix }}-lt" target_group_arns: - "{{ out_tg1.target_group_arn }}" @@ -720,7 +669,7 @@ - name: update autoscaling group remove tg1 ec2_asg: name: "{{ resource_prefix }}-asg" - launch_template: + launch_template: launch_template_name: "{{ resource_prefix }}-lt" target_group_arns: - "{{ out_tg2.target_group_arn }}" @@ -739,7 +688,7 @@ - name: update autoscaling group remove tg2 and add tg1 ec2_asg: name: "{{ resource_prefix }}-asg" - launch_template: + launch_template: launch_template_name: "{{ resource_prefix }}-lt" target_group_arns: - "{{ out_tg1.target_group_arn }}" @@ -758,7 +707,7 @@ - name: target group no change ec2_asg: name: "{{ resource_prefix }}-asg" - launch_template: + launch_template: launch_template_name: "{{ resource_prefix }}-lt" target_group_arns: - "{{ out_tg1.target_group_arn }}" @@ -789,7 +738,6 @@ retries: 10 # Remove the testing dependencies - - name: remove target group elb_target_group: name: "{{ item }}" diff --git a/tests/integration/targets/ec2_eip/aliases b/tests/integration/targets/ec2_eip/aliases index f396da939f0..78305e98909 100644 --- a/tests/integration/targets/ec2_eip/aliases +++ b/tests/integration/targets/ec2_eip/aliases @@ -1,4 +1,5 @@ # https://github.com/ansible-collections/community.aws/issues/159 -unstable +# unstable cloud/aws +ec2_eip_info \ No newline at end of file diff --git a/tests/integration/targets/ec2_eip/tasks/main.yml b/tests/integration/targets/ec2_eip/tasks/main.yml index 66e2eb5b4ba..9b93f107e2d 100644 --- a/tests/integration/targets/ec2_eip/tasks/main.yml +++ b/tests/integration/targets/ec2_eip/tasks/main.yml @@ -1,4 +1,7 @@ - name: Integration testing for ec2_eip + collections: + - amazon.aws + module_defaults: group/aws: aws_access_key: '{{ aws_access_key }}' @@ -7,922 +10,1389 @@ region: '{{ aws_region }}' ec2_eip: in_vpc: true - collections: - - amazon.aws + block: - # ===================================================== - - name: Get the current caller identity facts - aws_caller_info: null - register: caller_info - - name: list available AZs - aws_az_info: null - register: region_azs - - name: create a VPC - ec2_vpc_net: - name: '{{ resource_prefix }}-vpc' - state: present - cidr_block: '{{ vpc_cidr }}' - tags: - AnsibleEIPTest: Pending - AnsibleEIPTestPrefix: '{{ resource_prefix }}' - register: vpc_result - - name: create subnet - ec2_vpc_subnet: - cidr: '{{ subnet_cidr }}' - az: '{{ subnet_az }}' - vpc_id: '{{ vpc_result.vpc.id }}' - state: present - register: vpc_subnet_create - - ec2_vpc_igw: - state: present - vpc_id: '{{ vpc_result.vpc.id }}' - register: vpc_igw - - name: "create a security group" - ec2_group: - state: present - name: '{{ resource_prefix }}-sg' - description: a security group for ansible tests - vpc_id: '{{ vpc_result.vpc.id }}' - rules: - - proto: tcp - from_port: 22 - to_port: 22 - cidr_ip: 0.0.0.0/0 - register: security_group - - name: Create instance for attaching - ec2_instance: - name: '{{ resource_prefix }}-instance' - image_id: '{{ ec2_ami_id }}' - security_group: '{{ security_group.group_id }}' - vpc_subnet_id: '{{ vpc_subnet_create.subnet.id }}' - wait: yes - state: running - register: create_ec2_instance_result - - # ===================================================== - - name: Look for signs of concurrent EIP tests. Pause if they are running or their prefix comes before ours. - vars: - running_query: vpcs[?tags.AnsibleEIPTest=='Running'] - pending_query: vpcs[?tags.AnsibleEIPTest=='Pending'].tags.AnsibleEIPTestPrefix - ec2_vpc_net_info: - filters: - tag:AnsibleEIPTest: - - Pending - - Running - register: vpc_info - retries: 120 - delay: 5 - until: - - ( vpc_info | community.general.json_query(running_query) | length == 0 ) - - ( vpc_info | community.general.json_query(pending_query) | sort | first == resource_prefix ) - - name: Make a crude lock - ec2_vpc_net: - name: '{{ resource_prefix }}-vpc' - state: present - cidr_block: '{{ vpc_cidr }}' - tags: - AnsibleEIPTest: Running - AnsibleEIPTestPrefix: '{{ resource_prefix }}' - - # ===================================================== - - name: Get current state of EIPs - ec2_eip_info: null - register: eip_info_start - - name: Require that there are no free IPs when we start, otherwise we can't test things properly - assert: - that: - - eip_info_start is defined - - '"addresses" in eip_info_start' - - ( eip_info_start.addresses | length ) == ( eip_info_start | community.general.json_query("addresses[].association_id") | length ) - - - name: Allocate a new eip (no conditions) - ec2_eip: - state: present - tags: - AnsibleEIPTestPrefix: '{{ resource_prefix }}' - register: eip - - - ec2_eip_info: null - register: eip_info - - assert: - that: - - eip is defined - - eip is changed - - eip.public_ip is defined and ( eip.public_ip | ansible.utils.ipaddr ) - - eip.allocation_id is defined and eip.allocation_id.startswith("eipalloc-") - - ( eip_info_start.addresses | length ) + 1 == ( eip_info.addresses | length ) - - - ec2_eip_info: - filters: - public-ip: '{{ eip.public_ip }}' - - assert: - that: - - '"addresses" in eip_info' - - eip_info.addresses | length == 1 - - eip_info.addresses[0].allocation_id == eip.allocation_id - - eip_info.addresses[0].domain == "vpc" - - eip_info.addresses[0].public_ip == eip.public_ip - - '"AnsibleEIPTestPrefix" in eip_info.addresses[0].tags' - - eip_info.addresses[0].tags['AnsibleEIPTestPrefix'] == resource_prefix - - - ec2_eip_info: - filters: - allocation-id: '{{ eip.allocation_id }}' - - assert: - that: - - '"addresses" in eip_info' - - eip_info.addresses | length == 1 - - eip_info.addresses[0].allocation_id == eip.allocation_id - - eip_info.addresses[0].domain == "vpc" - - eip_info.addresses[0].public_ip == eip.public_ip - - - name: Release eip - ec2_eip: - state: absent - public_ip: '{{ eip.public_ip }}' - register: eip_release - - ec2_eip_info: null - register: eip_info - - assert: - that: - - eip_release is defined - - eip_release is changed - - ( eip_info_start.addresses | length ) == ( eip_info.addresses | length ) - - - name: Allocate a new eip - attempt reusing unallocated ones (none available) - ec2_eip: - state: present - reuse_existing_ip_allowed: true - register: eip - - ec2_eip_info: null - register: eip_info - - assert: - that: - - eip is defined - - eip is changed - - eip.public_ip is defined and ( eip.public_ip | ansible.utils.ipaddr ) - - eip.allocation_id is defined and eip.allocation_id.startswith("eipalloc-") - - ( eip_info_start.addresses | length ) + 1 == ( eip_info.addresses | length ) - - - name: Re-Allocate a new eip - attempt reusing unallocated ones (one available) - ec2_eip: - state: present - reuse_existing_ip_allowed: true - register: reallocate_eip - - ec2_eip_info: null - register: eip_info - - assert: - that: - - reallocate_eip is defined - - reallocate_eip is not changed - - reallocate_eip.public_ip is defined and ( reallocate_eip.public_ip | ansible.utils.ipaddr ) - - reallocate_eip.allocation_id is defined and reallocate_eip.allocation_id.startswith("eipalloc-") - - ( eip_info_start.addresses | length ) + 1 == ( eip_info.addresses | length ) - - - name: Release eip - ec2_eip: - state: absent - public_ip: '{{ eip.public_ip }}' - register: eip_release - - ec2_eip_info: null - register: eip_info - - assert: - that: - - ( eip_info_start.addresses | length ) == ( eip_info.addresses | length ) - - eip_release is defined - - eip_release is changed - - - name: Allocate a new eip - ec2_eip: - state: present - register: eip - - ec2_eip_info: null - register: eip_info - - assert: - that: - - eip is defined - - eip is changed - - eip.public_ip is defined and ( eip.public_ip | ansible.utils.ipaddr ) - - eip.allocation_id is defined and eip.allocation_id.startswith("eipalloc-") - - ( eip_info_start.addresses | length ) + 1 == ( eip_info.addresses | length ) - - - name: Match an existing eip (changed == false) - ec2_eip: - state: present - public_ip: '{{ eip.public_ip }}' - register: reallocate_eip - - ec2_eip_info: null - register: eip_info - - assert: - that: - - reallocate_eip is defined - - reallocate_eip is not changed - - reallocate_eip.public_ip is defined and ( reallocate_eip.public_ip | ansible.utils.ipaddr ) - - reallocate_eip.allocation_id is defined and reallocate_eip.allocation_id.startswith("eipalloc-") - - ( eip_info_start.addresses | length ) + 1 == ( eip_info.addresses | length ) - - - name: Release eip - ec2_eip: - state: absent - public_ip: '{{ eip.public_ip }}' - register: eip_release - - ec2_eip_info: null - register: eip_info - - assert: - that: - - eip_release is defined - - eip_release is changed - - ( eip_info_start.addresses | length ) == ( eip_info.addresses | length ) - - - name: Allocate a new eip (no tags) - ec2_eip: - state: present - register: eip - - ec2_eip_info: null - register: eip_info - - assert: - that: - - eip is defined - - eip is changed - - eip.public_ip is defined and ( eip.public_ip | ansible.utils.ipaddr ) - - eip.allocation_id is defined and eip.allocation_id.startswith("eipalloc-") - - ( eip_info_start.addresses | length ) + 1 == ( eip_info.addresses | length ) - - - name: attempt reusing an existing eip with a tag (No match available) - ec2_eip: - state: present - reuse_existing_ip_allowed: true - tag_name: Team - register: no_tagged_eip - - ec2_eip_info: null - register: eip_info - - assert: - that: - - no_tagged_eip is defined - - no_tagged_eip is changed - - no_tagged_eip.public_ip is defined and ( no_tagged_eip.public_ip | ansible.utils.ipaddr ) - - no_tagged_eip.allocation_id is defined and no_tagged_eip.allocation_id.startswith("eipalloc-") - - ( eip_info_start.addresses | length ) + 2 == ( eip_info.addresses | length ) - - - name: tag eip so we can try matching it - ec2_eip: - state: present - public_ip: '{{ eip.public_ip }}' - tags: - Team: Frontend + - name: Get the current caller identity facts + aws_caller_info: + register: caller_info - - name: attempt reusing an existing eip with a tag (Match available) - ec2_eip: - state: present - reuse_existing_ip_allowed: true - tag_name: Team - register: reallocate_eip - - ec2_eip_info: null - register: eip_info - - assert: - that: - - reallocate_eip is defined - - reallocate_eip is not changed - - reallocate_eip.public_ip is defined and ( reallocate_eip.public_ip | ansible.utils.ipaddr ) - - reallocate_eip.allocation_id is defined and reallocate_eip.allocation_id.startswith("eipalloc-") - - ( eip_info_start.addresses | length ) + 2 == ( eip_info.addresses | length ) - - - name: attempt reusing an existing eip with a tag and it's value (no match available) - ec2_eip: - state: present - reuse_existing_ip_allowed: true - tag_name: Team - tag_value: Backend - register: backend_eip - - ec2_eip_info: null - register: eip_info - - assert: - that: - - backend_eip is defined - - backend_eip is changed - - backend_eip.public_ip is defined and ( backend_eip.public_ip | ansible.utils.ipaddr ) - - backend_eip.allocation_id is defined and backend_eip.allocation_id.startswith("eipalloc-") - - ( eip_info_start.addresses | length ) + 3 == ( eip_info.addresses | length ) - - - name: tag eip so we can try matching it - ec2_eip: - state: present - public_ip: '{{ eip.public_ip }}' - tags: - Team: Backend + - name: List available AZs + aws_az_info: + register: region_azs - - name: attempt reusing an existing eip with a tag and it's value (match available) - ec2_eip: - state: present - reuse_existing_ip_allowed: true - tag_name: Team - tag_value: Backend - register: reallocate_eip - - ec2_eip_info: null - register: eip_info - - assert: - that: - - reallocate_eip is defined - - reallocate_eip is not changed - - reallocate_eip.public_ip is defined and reallocate_eip.public_ip != "" - - reallocate_eip.allocation_id is defined and reallocate_eip.allocation_id != "" - - ( eip_info_start.addresses | length ) + 3 == ( eip_info.addresses | length ) - - - name: Release backend_eip - ec2_eip: - state: absent - public_ip: '{{ backend_eip.public_ip }}' - register: eip_release - - ec2_eip_info: null - register: eip_info - - assert: - that: - - eip_release is defined - - eip_release is changed - - ( eip_info_start.addresses | length ) + 2 == ( eip_info.addresses | length ) - - - name: Release no_tagged_eip - ec2_eip: - state: absent - public_ip: '{{ no_tagged_eip.public_ip }}' - register: eip_release - - ec2_eip_info: null - register: eip_info - - assert: - that: - - eip_release is defined - - eip_release is changed - - ( eip_info_start.addresses | length ) + 1 == ( eip_info.addresses | length ) - - - name: Release eip - ec2_eip: - state: absent - public_ip: '{{ eip.public_ip }}' - register: eip_release - - ec2_eip_info: null - register: eip_info - - assert: - that: - - eip_release is defined - - eip_release is changed - - ( eip_info_start.addresses | length ) == ( eip_info.addresses | length ) - - - name: allocate a new eip from a pool - ec2_eip: - state: present - public_ipv4_pool: amazon - register: eip - - ec2_eip_info: null - register: eip_info - - assert: - that: - - eip is defined - - eip is changed - - eip.public_ip is defined and ( eip.public_ip | ansible.utils.ipaddr ) - - eip.allocation_id is defined and eip.allocation_id.startswith("eipalloc-") - - ( eip_info_start.addresses | length ) + 1 == ( eip_info.addresses | length ) - - - name: create ENI A - ec2_eni: - subnet_id: '{{ vpc_subnet_create.subnet.id }}' - register: eni_create_a - - - name: create ENI B - ec2_eni: - subnet_id: '{{ vpc_subnet_create.subnet.id }}' - register: eni_create_b - - - name: Attach EIP to ENI A - ec2_eip: - public_ip: '{{ eip.public_ip }}' - device_id: '{{ eni_create_a.interface.id }}' - register: associate_eip - - ec2_eip_info: - filters: - public-ip: '{{ eip.public_ip }}' - register: eip_info - - assert: - that: - - associate_eip is defined - - associate_eip is changed - - eip_info.addresses | length == 1 - - associate_eip.public_ip is defined and eip.public_ip == associate_eip.public_ip - - associate_eip.allocation_id is defined and eip.allocation_id == associate_eip.allocation_id - - eip_info.addresses[0].allocation_id == eip.allocation_id - - eip_info.addresses[0].domain == "vpc" - - eip_info.addresses[0].public_ip == eip.public_ip - - eip_info.addresses[0].association_id is defined and eip_info.addresses[0].association_id.startswith("eipassoc-") - - eip_info.addresses[0].network_interface_id == eni_create_a.interface.id - - eip_info.addresses[0].private_ip_address is defined and ( eip_info.addresses[0].private_ip_address | ansible.utils.ipaddr ) - - eip_info.addresses[0].network_interface_owner_id == caller_info.account - - - name: Re-Attach EIP to ENI A (no change) - ec2_eip: - public_ip: '{{ eip.public_ip }}' - device_id: '{{ eni_create_a.interface.id }}' - register: associate_eip - - ec2_eip_info: - filters: - public-ip: '{{ eip.public_ip }}' - register: eip_info - - assert: - that: - - associate_eip is defined - - associate_eip is not changed - - associate_eip.public_ip is defined and eip.public_ip == associate_eip.public_ip - - associate_eip.allocation_id is defined and eip.allocation_id == associate_eip.allocation_id - - eip_info.addresses | length == 1 - - eip_info.addresses[0].allocation_id == eip.allocation_id - - eip_info.addresses[0].domain == "vpc" - - eip_info.addresses[0].public_ip == eip.public_ip - - eip_info.addresses[0].association_id is defined and eip_info.addresses[0].association_id.startswith("eipassoc-") - - eip_info.addresses[0].network_interface_id == eni_create_a.interface.id - - eip_info.addresses[0].private_ip_address is defined and ( eip_info.addresses[0].private_ip_address | ansible.utils.ipaddr ) - - - name: Attach EIP to ENI B (should fail, already associated) - ec2_eip: - public_ip: '{{ eip.public_ip }}' - device_id: '{{ eni_create_b.interface.id }}' - register: associate_eip - ignore_errors: true - - ec2_eip_info: - filters: - public-ip: '{{ eip.public_ip }}' - register: eip_info - - assert: - that: - - associate_eip is defined - - associate_eip is failed - - eip_info.addresses | length == 1 - - eip_info.addresses[0].allocation_id == eip.allocation_id - - eip_info.addresses[0].domain == "vpc" - - eip_info.addresses[0].public_ip == eip.public_ip - - eip_info.addresses[0].association_id is defined and eip_info.addresses[0].association_id.startswith("eipassoc-") - - eip_info.addresses[0].network_interface_id == eni_create_a.interface.id - - eip_info.addresses[0].private_ip_address is defined and ( eip_info.addresses[0].private_ip_address | ansible.utils.ipaddr ) - - - name: Attach EIP to ENI B - ec2_eip: - public_ip: '{{ eip.public_ip }}' - device_id: '{{ eni_create_b.interface.id }}' - allow_reassociation: true - register: associate_eip - - ec2_eip_info: - filters: - public-ip: '{{ eip.public_ip }}' - register: eip_info - - assert: - that: - - associate_eip is defined - - associate_eip is changed - - associate_eip.public_ip is defined and eip.public_ip == associate_eip.public_ip - - associate_eip.allocation_id is defined and eip.allocation_id == associate_eip.allocation_id - - eip_info.addresses | length == 1 - - eip_info.addresses[0].allocation_id == eip.allocation_id - - eip_info.addresses[0].domain == "vpc" - - eip_info.addresses[0].public_ip == eip.public_ip - - eip_info.addresses[0].association_id is defined and eip_info.addresses[0].association_id.startswith("eipassoc-") - - eip_info.addresses[0].network_interface_id == eni_create_b.interface.id - - eip_info.addresses[0].private_ip_address is defined and ( eip_info.addresses[0].private_ip_address | ansible.utils.ipaddr ) - - - name: Detach EIP from ENI B, without enabling release on disassociation - ec2_eip: - state: absent - public_ip: '{{ eip.public_ip }}' - device_id: '{{ eni_create_b.interface.id }}' - register: disassociate_eip - - ec2_eip_info: - filters: - public-ip: '{{ eip.public_ip }}' - register: eip_info - - assert: - that: - - associate_eip is defined - - associate_eip is changed - - eip_info.addresses | length == 1 - - - name: Re-detach EIP from ENI B, without enabling release on disassociation - ec2_eip: - state: absent - public_ip: '{{ eip.public_ip }}' - device_id: '{{ eni_create_b.interface.id }}' - register: associate_eip - - ec2_eip_info: - filters: - public-ip: '{{ eip.public_ip }}' - register: eip_info - - assert: - that: - - associate_eip is defined - - associate_eip is not changed - - eip_info.addresses | length == 1 - - - name: Attach EIP to ENI A - ec2_eip: - public_ip: '{{ eip.public_ip }}' - device_id: '{{ eni_create_a.interface.id }}' - register: associate_eip - - ec2_eip_info: - filters: - public-ip: '{{ eip.public_ip }}' - register: eip_info - - assert: - that: - - associate_eip is defined - - associate_eip is changed - - associate_eip.public_ip is defined and eip.public_ip == associate_eip.public_ip - - associate_eip.allocation_id is defined and eip.allocation_id == associate_eip.allocation_id - - eip_info.addresses[0].network_interface_id == eni_create_a.interface.id - - - name: Detach EIP from ENI A, enabling release on disassociation - ec2_eip: - state: absent - public_ip: '{{ eip.public_ip }}' - device_id: '{{ eni_create_a.interface.id }}' - release_on_disassociation: true - register: disassociate_eip - - ec2_eip_info: - filters: - public-ip: '{{ eip.public_ip }}' - register: eip_info - - assert: - that: - - associate_eip is defined - - associate_eip is changed - - eip_info.addresses | length == 0 - - - name: Re-detach EIP from ENI A, enabling release on disassociation - ec2_eip: - state: absent - public_ip: '{{ eip.public_ip }}' - device_id: '{{ eni_create_a.interface.id }}' - release_on_disassociation: true - register: associate_eip - - ec2_eip_info: - filters: - public-ip: '{{ eip.public_ip }}' - register: eip_info - - assert: - that: - - associate_eip is defined - - associate_eip is not changed - - eip_info.addresses | length == 0 - - - ec2_eip_info: null - register: eip_info - - assert: - that: - - ( eip_info_start.addresses | length ) == ( eip_info.addresses | length ) - - - name: Cleanup ENI B - ec2_eni: - state: absent - eni_id: '{{ eni_create_b.interface.id }}' - - - name: Cleanup ENI A - ec2_eni: - state: absent - eni_id: '{{ eni_create_a.interface.id }}' - - - name: Attach eip to an EC2 instance - ec2_eip: - device_id: '{{ create_ec2_instance_result.instance_ids[0] }}' - state: present - release_on_disassociation: yes - register: instance_eip - - ec2_eip_info: - filters: - public-ip: '{{ instance_eip.public_ip }}' - register: eip_info - - assert: - that: - - instance_eip is success - - eip_info.addresses[0].allocation_id is defined - - eip_info.addresses[0].instance_id == '{{ create_ec2_instance_result.instance_ids[0] }}' - - - name: Attach eip to an EC2 instance with private Ip specified - ec2_eip: - device_id: '{{ create_ec2_instance_result.instance_ids[0] }}' - private_ip_address: '{{ create_ec2_instance_result.instances[0].private_ip_address }}' - state: present - release_on_disassociation: yes - register: instance_eip - - ec2_eip_info: - filters: - public-ip: '{{ instance_eip.public_ip }}' - register: eip_info - - assert: - that: - - instance_eip is success - - eip_info.addresses[0].allocation_id is defined - - eip_info.addresses[0].instance_id == '{{ create_ec2_instance_result.instance_ids[0] }}' - - # ===================================================== - - - name: Cleanup instance - ec2_instance: - instance_ids: '{{ create_ec2_instance_result.instance_ids }}' - state: absent - - - name: Cleanup instance eip - ec2_eip: - state: absent - public_ip: '{{ instance_eip.public_ip }}' - register: eip_cleanup - retries: 5 - delay: 5 - until: eip_cleanup is successful - - - name: Cleanup IGW - ec2_vpc_igw: - state: absent - vpc_id: '{{ vpc_result.vpc.id }}' - register: vpc_igw - - - name: Cleanup security group - ec2_group: - state: absent - name: '{{ resource_prefix }}-sg' - - - name: Cleanup Subnet - ec2_vpc_subnet: - state: absent - cidr: '{{ subnet_cidr }}' - vpc_id: '{{ vpc_result.vpc.id }}' - - - name: Release eip - ec2_eip: - state: absent - public_ip: '{{ eip.public_ip }}' - register: eip_release - ignore_errors: true + - name: Create a VPC + ec2_vpc_net: + name: '{{ resource_prefix }}-vpc' + state: present + cidr_block: '{{ vpc_cidr }}' + tags: + AnsibleEIPTest: Pending + AnsibleEIPTestPrefix: '{{ resource_prefix }}' + register: vpc_result + + - name: Look for signs of concurrent EIP tests. Pause if they are running or their prefix comes before ours. + vars: + running_query: vpcs[?tags.AnsibleEIPTest=='Running'] + pending_query: vpcs[?tags.AnsibleEIPTest=='Pending'].tags.AnsibleEIPTestPrefix + ec2_vpc_net_info: + filters: + tag:AnsibleEIPTest: + - Pending + - Running + register: vpc_info + retries: 10 + delay: 5 + until: + - ( vpc_info.vpcs | map(attribute='tags') | selectattr('AnsibleEIPTest', 'equalto', 'Running') | length == 0 ) + - ( vpc_info.vpcs | map(attribute='tags') | selectattr('AnsibleEIPTest', 'equalto', 'Pending') | map(attribute='AnsibleEIPTestPrefix') | sort | first == resource_prefix ) + + - name: Create subnet + ec2_vpc_subnet: + cidr: '{{ subnet_cidr }}' + az: '{{ subnet_az }}' + vpc_id: '{{ vpc_result.vpc.id }}' + state: present + register: vpc_subnet_create + + - name: Create internet gateway + amazon.aws.ec2_vpc_igw: + state: present + vpc_id: '{{ vpc_result.vpc.id }}' + register: vpc_igw + + - name: Create security group + ec2_group: + state: present + name: '{{ resource_prefix }}-sg' + description: a security group for ansible tests + vpc_id: '{{ vpc_result.vpc.id }}' + rules: + - proto: tcp + from_port: 22 + to_port: 22 + cidr_ip: 0.0.0.0/0 + register: security_group + + - name: Create instance for attaching + ec2_instance: + name: '{{ resource_prefix }}-instance' + image_id: '{{ ec2_ami_id }}' + security_group: '{{ security_group.group_id }}' + vpc_subnet_id: '{{ vpc_subnet_create.subnet.id }}' + wait: yes + state: running + register: create_ec2_instance_result + + - name: Create ENI A + ec2_eni: + subnet_id: '{{ vpc_subnet_create.subnet.id }}' + register: eni_create_a + + - name: Create ENI B + ec2_eni: + subnet_id: '{{ vpc_subnet_create.subnet.id }}' + register: eni_create_b + + - name: Make a crude lock + ec2_vpc_net: + name: '{{ resource_prefix }}-vpc' + state: present + cidr_block: '{{ vpc_cidr }}' + tags: + AnsibleEIPTest: Running + AnsibleEIPTestPrefix: '{{ resource_prefix }}' + + - name: Get current state of EIPs + ec2_eip_info: + register: eip_info_start + + - name: Require that there are no free IPs when we start, otherwise we can't test things properly + assert: + that: + - '"addresses" in eip_info_start' + - ( eip_info_start.addresses | length ) == ( eip_info_start.addresses | select('match', 'association_id') | length ) + + # ------------------------------------------------------------------------------------------ + + - name: Allocate a new EIP with no conditions - check_mode + ec2_eip: + state: present + tags: + AnsibleEIPTestPrefix: '{{ resource_prefix }}' + register: eip + check_mode: yes + + - assert: + that: + - eip is changed + + - name: Allocate a new EIP with no conditions + ec2_eip: + state: present + tags: + AnsibleEIPTestPrefix: '{{ resource_prefix }}' + register: eip + + - ec2_eip_info: + register: eip_info + check_mode: yes + + - assert: + that: + - eip is changed + - eip.public_ip is defined and ( eip.public_ip | ansible.utils.ipaddr ) + - eip.allocation_id is defined and eip.allocation_id.startswith("eipalloc-") + - ( eip_info_start.addresses | length ) + 1 == ( eip_info.addresses | length ) + + - name: Get EIP info via public ip + ec2_eip_info: + filters: + public-ip: '{{ eip.public_ip }}' + register: eip_info + + - assert: + that: + - '"addresses" in eip_info' + - eip_info.addresses | length == 1 + - eip_info.addresses[0].allocation_id == eip.allocation_id + - eip_info.addresses[0].domain == "vpc" + - eip_info.addresses[0].public_ip == eip.public_ip + - '"AnsibleEIPTestPrefix" in eip_info.addresses[0].tags' + - eip_info.addresses[0].tags['AnsibleEIPTestPrefix'] == resource_prefix + + - name: Get EIP info via allocation id + ec2_eip_info: + filters: + allocation-id: '{{ eip.allocation_id }}' + register: eip_info + + - assert: + that: + - '"addresses" in eip_info' + - eip_info.addresses | length == 1 + - eip_info.addresses[0].allocation_id == eip.allocation_id + - eip_info.addresses[0].domain == "vpc" + - eip_info.addresses[0].public_ip == eip.public_ip + - '"AnsibleEIPTestPrefix" in eip_info.addresses[0].tags' + - eip_info.addresses[0].tags['AnsibleEIPTestPrefix'] == resource_prefix + + - name: Allocate a new ip (idempotence) - check_mode + ec2_eip: + state: present + public_ip: '{{ eip.public_ip }}' + register: eip + check_mode: yes + + - assert: + that: + - eip is not changed + + - name: Allocate a new ip (idempotence) + ec2_eip: + state: present + public_ip: '{{ eip.public_ip }}' + register: eip + + - ec2_eip_info: + register: eip_info + + - assert: + that: + - eip is not changed + - eip.public_ip is defined and ( eip.public_ip | ansible.utils.ipaddr ) + - eip.allocation_id is defined and eip.allocation_id.startswith("eipalloc-") + - ( eip_info_start.addresses | length ) + 1 == ( eip_info.addresses | length ) + + # ------------------------------------------------------------------------------------------ + + - name: Release EIP - check_mode + ec2_eip: + state: absent + public_ip: '{{ eip.public_ip }}' + register: eip_release + check_mode: yes + + - assert: + that: + - eip_release.changed + + - name: Release eip + ec2_eip: + state: absent + public_ip: '{{ eip.public_ip }}' + register: eip_release + + - ec2_eip_info: + register: eip_info + + - assert: + that: + - eip_release.changed + - not eip_release.disassociated + - eip_release.released + - ( eip_info_start.addresses | length ) == ( eip_info.addresses | length ) + + - name: Release EIP (idempotence) - check_mode + ec2_eip: + state: absent + public_ip: '{{ eip.public_ip }}' + register: eip_release + check_mode: yes + + - assert: + that: + - eip_release is not changed + + - name: Release EIP (idempotence) + ec2_eip: + state: absent + public_ip: '{{ eip.public_ip }}' + register: eip_release + + - ec2_eip_info: + register: eip_info + + - assert: + that: + - not eip_release.changed + - not eip_release.disassociated + - not eip_release.released + - ( eip_info_start.addresses | length ) == ( eip_info.addresses | length ) + + # ------------------------------------------------------------------------------------------ + + - name: Allocate a new EIP - attempt reusing unallocated ones (none available) - check_mode + ec2_eip: + state: present + reuse_existing_ip_allowed: true + register: eip + check_mode: yes + + - assert: + that: + - eip is changed + + - name: Allocate a new EIP - attempt reusing unallocated ones (none available) + ec2_eip: + state: present + reuse_existing_ip_allowed: true + register: eip + + - ec2_eip_info: + register: eip_info + + - assert: + that: + - eip is changed + - eip.public_ip is defined and ( eip.public_ip | ansible.utils.ipaddr ) + - eip.allocation_id is defined and eip.allocation_id.startswith("eipalloc-") + - ( eip_info_start.addresses | length ) + 1 == ( eip_info.addresses | length ) + + - name: Re-Allocate a new EIP - attempt reusing unallocated ones (one available) - check_mode + ec2_eip: + state: present + reuse_existing_ip_allowed: true + register: reallocate_eip + check_mode: yes + + - assert: + that: + - reallocate_eip is not changed + + - name: Re-Allocate a new EIP - attempt reusing unallocated ones (one available) + ec2_eip: + state: present + reuse_existing_ip_allowed: true + register: reallocate_eip + + - ec2_eip_info: + register: eip_info + + - assert: + that: + - reallocate_eip is not changed + - reallocate_eip.public_ip is defined and ( reallocate_eip.public_ip | ansible.utils.ipaddr ) + - reallocate_eip.allocation_id is defined and reallocate_eip.allocation_id.startswith("eipalloc-") + - ( eip_info_start.addresses | length ) + 1 == ( eip_info.addresses | length ) + + # ------------------------------------------------------------------------------------------ + + - name: attempt reusing an existing EIP with a tag (No match available) - check_mode + ec2_eip: + state: present + reuse_existing_ip_allowed: true + tag_name: Team + register: no_tagged_eip + check_mode: yes + + - assert: + that: + - no_tagged_eip is changed + + - name: attempt reusing an existing EIP with a tag (No match available) + ec2_eip: + state: present + reuse_existing_ip_allowed: true + tag_name: Team + register: no_tagged_eip + + - ec2_eip_info: + register: eip_info + + - assert: + that: + - no_tagged_eip is changed + - no_tagged_eip.public_ip is defined and ( no_tagged_eip.public_ip | ansible.utils.ipaddr ) + - no_tagged_eip.allocation_id is defined and no_tagged_eip.allocation_id.startswith("eipalloc-") + - ( eip_info_start.addresses | length ) + 2 == ( eip_info.addresses | length ) + + # ------------------------------------------------------------------------------------------ + + - name: Tag EIP so we can try matching it + ec2_eip: + state: present + public_ip: '{{ eip.public_ip }}' + tags: + Team: Frontend + + - name: Attempt reusing an existing EIP with a tag (Match available) - check_mode + ec2_eip: + state: present + reuse_existing_ip_allowed: true + tag_name: Team + register: reallocate_eip + check_mode: yes + + - assert: + that: + - reallocate_eip is not changed + + - name: Attempt reusing an existing EIP with a tag (Match available) + ec2_eip: + state: present + reuse_existing_ip_allowed: true + tag_name: Team + register: reallocate_eip + + - ec2_eip_info: + register: eip_info + + - assert: + that: + - reallocate_eip is not changed + - reallocate_eip.public_ip is defined and ( reallocate_eip.public_ip | ansible.utils.ipaddr ) + - reallocate_eip.allocation_id is defined and reallocate_eip.allocation_id.startswith("eipalloc-") + - ( eip_info_start.addresses | length ) + 2 == ( eip_info.addresses | length ) + + - name: Attempt reusing an existing EIP with a tag and it's value (no match available) - check_mode + ec2_eip: + state: present + reuse_existing_ip_allowed: true + tag_name: Team + tag_value: Backend + register: backend_eip + check_mode: yes + + - assert: + that: + - backend_eip is changed + + - name: Attempt reusing an existing EIP with a tag and it's value (no match available) + ec2_eip: + state: present + reuse_existing_ip_allowed: true + tag_name: Team + tag_value: Backend + register: backend_eip + + - ec2_eip_info: + register: eip_info + + - assert: + that: + - backend_eip is changed + - backend_eip.public_ip is defined and ( backend_eip.public_ip | ansible.utils.ipaddr ) + - backend_eip.allocation_id is defined and backend_eip.allocation_id.startswith("eipalloc-") + - ( eip_info_start.addresses | length ) + 3 == ( eip_info.addresses | length ) + + # ------------------------------------------------------------------------------------------ + + - name: Tag EIP so we can try matching it + ec2_eip: + state: present + public_ip: '{{ eip.public_ip }}' + tags: + Team: Backend + + - name: Attempt reusing an existing EIP with a tag and it's value (match available) - check_mode + ec2_eip: + state: present + reuse_existing_ip_allowed: true + tag_name: Team + tag_value: Backend + register: reallocate_eip + check_mode: yes + + - assert: + that: + - reallocate_eip is not changed + + - name: Attempt reusing an existing EIP with a tag and it's value (match available) + ec2_eip: + state: present + reuse_existing_ip_allowed: true + tag_name: Team + tag_value: Backend + register: reallocate_eip + + - ec2_eip_info: + register: eip_info + + - assert: + that: + - reallocate_eip is not changed + - reallocate_eip.public_ip is defined and reallocate_eip.public_ip != "" + - reallocate_eip.allocation_id is defined and reallocate_eip.allocation_id != "" + - ( eip_info_start.addresses | length ) + 3 == ( eip_info.addresses | length ) + + - name: Release backend_eip + ec2_eip: + state: absent + public_ip: '{{ backend_eip.public_ip }}' + + - name: Release no_tagged_eip + ec2_eip: + state: absent + public_ip: '{{ no_tagged_eip.public_ip }}' + + - name: Release eip + ec2_eip: + state: absent + public_ip: '{{ eip.public_ip }}' + + - ec2_eip_info: + register: eip_info + + - assert: + that: + - ( eip_info_start.addresses | length ) == ( eip_info.addresses | length ) + + # ------------------------------------------------------------------------------------------ + + - name: Allocate a new EIP from a pool - check_mode + ec2_eip: + state: present + public_ipv4_pool: amazon + register: eip + check_mode: yes + + - assert: + that: + - eip is changed + + - name: Allocate a new EIP from a pool + ec2_eip: + state: present + public_ipv4_pool: amazon + register: eip + + - ec2_eip_info: + register: eip_info + + - assert: + that: + - eip is changed + - eip.public_ip is defined and ( eip.public_ip | ansible.utils.ipaddr ) + - eip.allocation_id is defined and eip.allocation_id.startswith("eipalloc-") + - ( eip_info_start.addresses | length ) + 1 == ( eip_info.addresses | length ) + + # ------------------------------------------------------------------------------------------ + + - name: Attach EIP to ENI A - check_mode + ec2_eip: + public_ip: '{{ eip.public_ip }}' + device_id: '{{ eni_create_a.interface.id }}' + register: associate_eip + check_mode: yes + + - assert: + that: + - associate_eip is changed + + - name: Attach EIP to ENI A + ec2_eip: + public_ip: '{{ eip.public_ip }}' + device_id: '{{ eni_create_a.interface.id }}' + register: associate_eip + + - ec2_eip_info: + filters: + public-ip: '{{ eip.public_ip }}' + register: eip_info + + - assert: + that: + - associate_eip is changed + - eip_info.addresses | length == 1 + - associate_eip.public_ip is defined and eip.public_ip == associate_eip.public_ip + - associate_eip.allocation_id is defined and eip.allocation_id == associate_eip.allocation_id + - eip_info.addresses[0].allocation_id == eip.allocation_id + - eip_info.addresses[0].domain == "vpc" + - eip_info.addresses[0].public_ip == eip.public_ip + - eip_info.addresses[0].association_id is defined and eip_info.addresses[0].association_id.startswith("eipassoc-") + - eip_info.addresses[0].network_interface_id == eni_create_a.interface.id + - eip_info.addresses[0].private_ip_address is defined and ( eip_info.addresses[0].private_ip_address | ansible.utils.ipaddr ) + - eip_info.addresses[0].network_interface_owner_id == caller_info.account + + - name: Attach EIP to ENI A (idempotence) - check_mode + ec2_eip: + public_ip: '{{ eip.public_ip }}' + device_id: '{{ eni_create_a.interface.id }}' + register: associate_eip + check_mode: yes + + - assert: + that: + - associate_eip is not changed + + - name: Attach EIP to ENI A (idempotence) + ec2_eip: + public_ip: '{{ eip.public_ip }}' + device_id: '{{ eni_create_a.interface.id }}' + register: associate_eip + + - ec2_eip_info: + filters: + public-ip: '{{ eip.public_ip }}' + register: eip_info + + - assert: + that: + - associate_eip is not changed + - associate_eip.public_ip is defined and eip.public_ip == associate_eip.public_ip + - associate_eip.allocation_id is defined and eip.allocation_id == associate_eip.allocation_id + - eip_info.addresses | length == 1 + - eip_info.addresses[0].allocation_id == eip.allocation_id + - eip_info.addresses[0].domain == "vpc" + - eip_info.addresses[0].public_ip == eip.public_ip + - eip_info.addresses[0].association_id is defined and eip_info.addresses[0].association_id.startswith("eipassoc-") + - eip_info.addresses[0].network_interface_id == eni_create_a.interface.id + - eip_info.addresses[0].private_ip_address is defined and ( eip_info.addresses[0].private_ip_address | ansible.utils.ipaddr ) + + # ------------------------------------------------------------------------------------------ + + - name: Attach EIP to ENI B (should fail, already associated) + ec2_eip: + public_ip: '{{ eip.public_ip }}' + device_id: '{{ eni_create_b.interface.id }}' + register: associate_eip + ignore_errors: true + + - ec2_eip_info: + filters: + public-ip: '{{ eip.public_ip }}' + register: eip_info + + - assert: + that: + - associate_eip is failed + - eip_info.addresses | length == 1 + - eip_info.addresses[0].allocation_id == eip.allocation_id + - eip_info.addresses[0].domain == "vpc" + - eip_info.addresses[0].public_ip == eip.public_ip + - eip_info.addresses[0].association_id is defined and eip_info.addresses[0].association_id.startswith("eipassoc-") + - eip_info.addresses[0].network_interface_id == eni_create_a.interface.id + - eip_info.addresses[0].private_ip_address is defined and ( eip_info.addresses[0].private_ip_address | ansible.utils.ipaddr ) + + - name: Attach EIP to ENI B - check_mode + ec2_eip: + public_ip: '{{ eip.public_ip }}' + device_id: '{{ eni_create_b.interface.id }}' + allow_reassociation: true + register: associate_eip + check_mode: yes + + - assert: + that: + - associate_eip is changed + + - name: Attach EIP to ENI B + ec2_eip: + public_ip: '{{ eip.public_ip }}' + device_id: '{{ eni_create_b.interface.id }}' + allow_reassociation: true + register: associate_eip + + - ec2_eip_info: + filters: + public-ip: '{{ eip.public_ip }}' + register: eip_info + + - assert: + that: + - associate_eip is changed + - associate_eip.public_ip is defined and eip.public_ip == associate_eip.public_ip + - associate_eip.allocation_id is defined and eip.allocation_id == associate_eip.allocation_id + - eip_info.addresses | length == 1 + - eip_info.addresses[0].allocation_id == eip.allocation_id + - eip_info.addresses[0].domain == "vpc" + - eip_info.addresses[0].public_ip == eip.public_ip + - eip_info.addresses[0].association_id is defined and eip_info.addresses[0].association_id.startswith("eipassoc-") + - eip_info.addresses[0].network_interface_id == eni_create_b.interface.id + - eip_info.addresses[0].private_ip_address is defined and ( eip_info.addresses[0].private_ip_address | ansible.utils.ipaddr ) + + - name: Attach EIP to ENI B (idempotence) - check_mode + ec2_eip: + public_ip: '{{ eip.public_ip }}' + device_id: '{{ eni_create_b.interface.id }}' + allow_reassociation: true + register: associate_eip + check_mode: yes + + - assert: + that: + - associate_eip is not changed + + - name: Attach EIP to ENI B (idempotence) + ec2_eip: + public_ip: '{{ eip.public_ip }}' + device_id: '{{ eni_create_b.interface.id }}' + allow_reassociation: true + register: associate_eip + + - ec2_eip_info: + filters: + public-ip: '{{ eip.public_ip }}' + register: eip_info + + - assert: + that: + - associate_eip is not changed + - associate_eip.public_ip is defined and eip.public_ip == associate_eip.public_ip + - associate_eip.allocation_id is defined and eip.allocation_id == associate_eip.allocation_id + - eip_info.addresses | length == 1 + - eip_info.addresses[0].allocation_id == eip.allocation_id + - eip_info.addresses[0].domain == "vpc" + - eip_info.addresses[0].public_ip == eip.public_ip + - eip_info.addresses[0].association_id is defined and eip_info.addresses[0].association_id.startswith("eipassoc-") + - eip_info.addresses[0].network_interface_id == eni_create_b.interface.id + - eip_info.addresses[0].private_ip_address is defined and ( eip_info.addresses[0].private_ip_address | ansible.utils.ipaddr ) + + # ------------------------------------------------------------------------------------------ + + - name: Detach EIP from ENI B, without enabling release on disassociation - check_mode + ec2_eip: + state: absent + public_ip: '{{ eip.public_ip }}' + device_id: '{{ eni_create_b.interface.id }}' + register: disassociate_eip + check_mode: yes + + - assert: + that: + - disassociate_eip is changed + + - name: Detach EIP from ENI B, without enabling release on disassociation + ec2_eip: + state: absent + public_ip: '{{ eip.public_ip }}' + device_id: '{{ eni_create_b.interface.id }}' + register: disassociate_eip + + - ec2_eip_info: + filters: + public-ip: '{{ eip.public_ip }}' + register: eip_info + + - assert: + that: + - disassociate_eip.changed + - disassociate_eip.disassociated + - not disassociate_eip.released + - eip_info.addresses | length == 1 + + - name: Detach EIP from ENI B, without enabling release on disassociation (idempotence) - check_mode + ec2_eip: + state: absent + public_ip: '{{ eip.public_ip }}' + device_id: '{{ eni_create_b.interface.id }}' + register: disassociate_eip + check_mode: yes + + - assert: + that: + - disassociate_eip is not changed + + - name: Detach EIP from ENI B, without enabling release on disassociation (idempotence) + ec2_eip: + state: absent + public_ip: '{{ eip.public_ip }}' + device_id: '{{ eni_create_b.interface.id }}' + register: disassociate_eip + + - ec2_eip_info: + filters: + public-ip: '{{ eip.public_ip }}' + register: eip_info + + - assert: + that: + - not disassociate_eip.changed + - not disassociate_eip.disassociated + - not disassociate_eip.released + - eip_info.addresses | length == 1 + + # ------------------------------------------------------------------------------------------ + + - name: Attach EIP to ENI A + ec2_eip: + public_ip: '{{ eip.public_ip }}' + device_id: '{{ eni_create_a.interface.id }}' + register: associate_eip + + - name: Detach EIP from ENI A, enabling release on disassociation - check_mode + ec2_eip: + state: absent + public_ip: '{{ eip.public_ip }}' + device_id: '{{ eni_create_a.interface.id }}' + release_on_disassociation: true + register: disassociate_eip + check_mode: yes + + - assert: + that: + - disassociate_eip is changed + + - name: Detach EIP from ENI A, enabling release on disassociation + ec2_eip: + state: absent + public_ip: '{{ eip.public_ip }}' + device_id: '{{ eni_create_a.interface.id }}' + release_on_disassociation: true + register: disassociate_eip + + - ec2_eip_info: + filters: + public-ip: '{{ eip.public_ip }}' + register: eip_info + + - assert: + that: + - disassociate_eip.changed + - disassociate_eip.disassociated + - disassociate_eip.released + - eip_info.addresses | length == 0 + + - name: Detach EIP from ENI A, enabling release on disassociation (idempotence) - check_mode + ec2_eip: + state: absent + public_ip: '{{ eip.public_ip }}' + device_id: '{{ eni_create_a.interface.id }}' + release_on_disassociation: true + register: disassociate_eip + check_mode: yes + + - assert: + that: + - disassociate_eip is not changed + + - name: Detach EIP from ENI A, enabling release on disassociation (idempotence) + ec2_eip: + state: absent + public_ip: '{{ eip.public_ip }}' + device_id: '{{ eni_create_a.interface.id }}' + release_on_disassociation: true + register: disassociate_eip + + - ec2_eip_info: + filters: + public-ip: '{{ eip.public_ip }}' + register: eip_info + + - assert: + that: + - not disassociate_eip.changed + - not disassociate_eip.disassociated + - not disassociate_eip.released + - eip_info.addresses | length == 0 + + # ------------------------------------------------------------------------------------------ + + - name: Attach EIP to an EC2 instance - check_mode + ec2_eip: + device_id: '{{ create_ec2_instance_result.instance_ids[0] }}' + state: present + release_on_disassociation: yes + register: instance_eip + check_mode: yes + + - assert: + that: + - instance_eip is changed + + - name: Attach EIP to an EC2 instance + ec2_eip: + device_id: '{{ create_ec2_instance_result.instance_ids[0] }}' + state: present + release_on_disassociation: yes + register: instance_eip + + - ec2_eip_info: + filters: + public-ip: '{{ instance_eip.public_ip }}' + register: eip_info + + - assert: + that: + - instance_eip is changed + - eip_info.addresses[0].allocation_id is defined + - eip_info.addresses[0].instance_id == '{{ create_ec2_instance_result.instance_ids[0] }}' + + - name: Attach EIP to an EC2 instance (idempotence) - check_mode + ec2_eip: + device_id: '{{ create_ec2_instance_result.instance_ids[0] }}' + state: present + release_on_disassociation: yes + register: instance_eip + check_mode: yes + + - assert: + that: + - instance_eip is not changed + + - name: Attach EIP to an EC2 instance (idempotence) + ec2_eip: + device_id: '{{ create_ec2_instance_result.instance_ids[0] }}' + state: present + release_on_disassociation: yes + register: instance_eip + + - ec2_eip_info: + filters: + public-ip: '{{ instance_eip.public_ip }}' + register: eip_info + + - assert: + that: + - instance_eip is not changed + - eip_info.addresses[0].allocation_id is defined + - eip_info.addresses[0].instance_id == '{{ create_ec2_instance_result.instance_ids[0] }}' + + # ------------------------------------------------------------------------------------------ + + - name: Detach EIP from EC2 instance, without enabling release on disassociation - check_mode + ec2_eip: + state: absent + device_id: '{{ create_ec2_instance_result.instance_ids[0] }}' + register: detach_eip + check_mode: yes + + - assert: + that: + - detach_eip is changed + + - name: Detach EIP from EC2 instance, without enabling release on disassociation + ec2_eip: + state: absent + device_id: '{{ create_ec2_instance_result.instance_ids[0] }}' + register: detach_eip + + - ec2_eip_info: + filters: + public-ip: '{{ instance_eip.public_ip }}' + register: eip_info + + - assert: + that: + - detach_eip.changed + - detach_eip.disassociated + - not detach_eip.released + - eip_info.addresses | length == 1 + + - name: Detach EIP from EC2 instance, without enabling release on disassociation (idempotence) - check_mode + ec2_eip: + state: absent + device_id: '{{ create_ec2_instance_result.instance_ids[0] }}' + register: detach_eip + check_mode: yes + + - assert: + that: + - detach_eip is not changed + + - name: Detach EIP from EC2 instance, without enabling release on disassociation (idempotence) + ec2_eip: + state: absent + device_id: '{{ create_ec2_instance_result.instance_ids[0] }}' + register: detach_eip + + - ec2_eip_info: + filters: + public-ip: '{{ instance_eip.public_ip }}' + register: eip_info + + - assert: + that: + - not detach_eip.changed + - not detach_eip.disassociated + - not detach_eip.released + - eip_info.addresses | length == 1 + + - name: Release EIP + ec2_eip: + state: absent + public_ip: '{{ instance_eip.public_ip }}' + + # ------------------------------------------------------------------------------------------ + + - name: Attach EIP to an EC2 instance with private Ip specified - check_mode + ec2_eip: + device_id: '{{ create_ec2_instance_result.instance_ids[0] }}' + private_ip_address: '{{ create_ec2_instance_result.instances[0].private_ip_address }}' + state: present + release_on_disassociation: yes + register: instance_eip + check_mode: yes + + - assert: + that: + - instance_eip is changed + + - name: Attach EIP to an EC2 instance with private Ip specified + ec2_eip: + device_id: '{{ create_ec2_instance_result.instance_ids[0] }}' + private_ip_address: '{{ create_ec2_instance_result.instances[0].private_ip_address }}' + state: present + release_on_disassociation: yes + register: instance_eip + + - ec2_eip_info: + filters: + public-ip: '{{ instance_eip.public_ip }}' + register: eip_info + + - assert: + that: + - instance_eip is changed + - eip_info.addresses[0].allocation_id is defined + - eip_info.addresses[0].instance_id == '{{ create_ec2_instance_result.instance_ids[0] }}' + + - name: Attach EIP to an EC2 instance with private Ip specified (idempotence) - check_mode + ec2_eip: + device_id: '{{ create_ec2_instance_result.instance_ids[0] }}' + private_ip_address: '{{ create_ec2_instance_result.instances[0].private_ip_address }}' + state: present + release_on_disassociation: yes + register: instance_eip + check_mode: yes + + - assert: + that: + - instance_eip is not changed + + - name: Attach EIP to an EC2 instance with private Ip specified (idempotence) + ec2_eip: + device_id: '{{ create_ec2_instance_result.instance_ids[0] }}' + private_ip_address: '{{ create_ec2_instance_result.instances[0].private_ip_address }}' + state: present + release_on_disassociation: yes + register: instance_eip + + - ec2_eip_info: + filters: + public-ip: '{{ instance_eip.public_ip }}' + register: eip_info + + - assert: + that: + - instance_eip is not changed + - eip_info.addresses[0].allocation_id is defined + - eip_info.addresses[0].instance_id == '{{ create_ec2_instance_result.instance_ids[0] }}' + + # ------------------------------------------------------------------------------------------ + + - name: Detach EIP from EC2 instance, enabling release on disassociation - check_mode + ec2_eip: + state: absent + device_id: '{{ create_ec2_instance_result.instance_ids[0] }}' + release_on_disassociation: yes + register: disassociate_eip + check_mode: yes + + - assert: + that: + - disassociate_eip is changed + + - name: Detach EIP from EC2 instance, enabling release on disassociation + ec2_eip: + state: absent + device_id: '{{ create_ec2_instance_result.instance_ids[0] }}' + release_on_disassociation: yes + register: disassociate_eip + + - ec2_eip_info: + filters: + public-ip: '{{ instance_eip.public_ip }}' + register: eip_info + + - assert: + that: + - disassociate_eip.changed + - disassociate_eip.disassociated + - disassociate_eip.released + - eip_info.addresses | length == 0 + + - name: Detach EIP from EC2 instance, enabling release on disassociation (idempotence) - check_mode + ec2_eip: + state: absent + device_id: '{{ create_ec2_instance_result.instance_ids[0] }}' + release_on_disassociation: yes + register: disassociate_eip + check_mode: yes + + - assert: + that: + - disassociate_eip is not changed + + - name: Detach EIP from EC2 instance, enabling release on disassociation (idempotence) + ec2_eip: + state: absent + device_id: '{{ create_ec2_instance_result.instance_ids[0] }}' + release_on_disassociation: yes + register: disassociate_eip + + - ec2_eip_info: + filters: + public-ip: '{{ instance_eip.public_ip }}' + register: eip_info + + - assert: + that: + - not disassociate_eip.changed + - not disassociate_eip.disassociated + - not disassociate_eip.released + - eip_info.addresses | length == 0 + + # ------------------------------------------------------------------------------------------ + + - name: Allocate a new eip + ec2_eip: + state: present + register: eip + + - name: Tag EIP - check_mode + ec2_eip: + state: present + public_ip: '{{ eip.public_ip }}' + tags: + AnsibleEIPTestPrefix: '{{ resource_prefix }}' + another_tag: 'another Value {{ resource_prefix }}' + register: tag_eip + check_mode: yes + + - assert: + that: + - tag_eip is changed + + - name: Tag EIP + ec2_eip: + state: present + public_ip: '{{ eip.public_ip }}' + tags: + AnsibleEIPTestPrefix: '{{ resource_prefix }}' + another_tag: 'another Value {{ resource_prefix }}' + register: tag_eip + + - ec2_eip_info: + filters: + public-ip: '{{ eip.public_ip }}' + register: eip_info + + - assert: + that: + - tag_eip is changed + - '"AnsibleEIPTestPrefix" in eip_info.addresses[0].tags' + - '"another_tag" in eip_info.addresses[0].tags' + - eip_info.addresses[0].tags['AnsibleEIPTestPrefix'] == resource_prefix + - eip_info.addresses[0].tags['another_tag'] == 'another Value ' + resource_prefix + - ( eip_info_start.addresses | length ) + 1 == ( eip_info.addresses | length ) + + - name: Tag EIP (idempotence) - check_mode + ec2_eip: + state: present + public_ip: '{{ eip.public_ip }}' + tags: + AnsibleEIPTestPrefix: '{{ resource_prefix }}' + another_tag: 'another Value {{ resource_prefix }}' + register: tag_eip + check_mode: yes + + - assert: + that: + - tag_eip is not changed + + - name: Tag EIP (idempotence) + ec2_eip: + state: present + public_ip: '{{ eip.public_ip }}' + tags: + AnsibleEIPTestPrefix: '{{ resource_prefix }}' + another_tag: 'another Value {{ resource_prefix }}' + register: tag_eip + + - ec2_eip_info: + filters: + public-ip: '{{ eip.public_ip }}' + register: eip_info + + - assert: + that: + - tag_eip is not changed + - '"AnsibleEIPTestPrefix" in eip_info.addresses[0].tags' + - '"another_tag" in eip_info.addresses[0].tags' + - eip_info.addresses[0].tags['AnsibleEIPTestPrefix'] == resource_prefix + - eip_info.addresses[0].tags['another_tag'] == 'another Value ' + resource_prefix + - ( eip_info_start.addresses | length ) + 1 == ( eip_info.addresses | length ) + + # ------------------------------------------------------------------------------------------ + + - name: Add another Tag - check_mode + ec2_eip: + state: present + public_ip: '{{ eip.public_ip }}' + tags: + "third tag": 'Third tag - {{ resource_prefix }}' + purge_tags: False + register: tag_eip + check_mode: yes + + - assert: + that: + - tag_eip is changed + + - name: Add another Tag + ec2_eip: + state: present + public_ip: '{{ eip.public_ip }}' + tags: + "third tag": 'Third tag - {{ resource_prefix }}' + purge_tags: False + register: tag_eip + + - ec2_eip_info: + filters: + public-ip: '{{ eip.public_ip }}' + register: eip_info + + - assert: + that: + - tag_eip is changed + - '"AnsibleEIPTestPrefix" in eip_info.addresses[0].tags' + - '"another_tag" in eip_info.addresses[0].tags' + - '"third tag" in eip_info.addresses[0].tags' + - eip_info.addresses[0].tags['AnsibleEIPTestPrefix'] == resource_prefix + - eip_info.addresses[0].tags['another_tag'] == 'another Value ' + resource_prefix + - eip_info.addresses[0].tags['third tag'] == 'Third tag - ' + resource_prefix + - ( eip_info_start.addresses | length ) + 1 == ( eip_info.addresses | length ) + + - name: Add another Tag (idempotence) - check_mode + ec2_eip: + state: present + public_ip: '{{ eip.public_ip }}' + tags: + "third tag": 'Third tag - {{ resource_prefix }}' + purge_tags: False + register: tag_eip + check_mode: yes + + - assert: + that: + - tag_eip is not changed + + - name: Add another Tag (idempotence) + ec2_eip: + state: present + public_ip: '{{ eip.public_ip }}' + tags: + "third tag": 'Third tag - {{ resource_prefix }}' + purge_tags: False + register: tag_eip + + - ec2_eip_info: + filters: + public-ip: '{{ eip.public_ip }}' + register: eip_info + + - assert: + that: + - tag_eip is not changed + - '"AnsibleEIPTestPrefix" in eip_info.addresses[0].tags' + - '"another_tag" in eip_info.addresses[0].tags' + - '"third tag" in eip_info.addresses[0].tags' + - eip_info.addresses[0].tags['AnsibleEIPTestPrefix'] == resource_prefix + - eip_info.addresses[0].tags['another_tag'] == 'another Value ' + resource_prefix + - eip_info.addresses[0].tags['third tag'] == 'Third tag - ' + resource_prefix + + # ------------------------------------------------------------------------------------------ + + - name: Purge tags - check_mode + ec2_eip: + state: present + public_ip: '{{ eip.public_ip }}' + tags: + "third tag": 'Third tag - {{ resource_prefix }}' + purge_tags: True + register: tag_eip + check_mode: yes + + - assert: + that: + - tag_eip is changed + + - name: Purge tags + ec2_eip: + state: present + public_ip: '{{ eip.public_ip }}' + tags: + "third tag": 'Third tag - {{ resource_prefix }}' + purge_tags: True + register: tag_eip + + - ec2_eip_info: + filters: + public-ip: '{{ eip.public_ip }}' + register: eip_info + + - assert: + that: + - tag_eip is changed + - '"AnsibleEIPTestPrefix" not in eip_info.addresses[0].tags' + - '"another_tag" not in eip_info.addresses[0].tags' + - '"third tag" in eip_info.addresses[0].tags' + - eip_info.addresses[0].tags['third tag'] == 'Third tag - ' + resource_prefix + + - name: Purge tags (idempotence) - check_mode + ec2_eip: + state: present + public_ip: '{{ eip.public_ip }}' + tags: + "third tag": 'Third tag - {{ resource_prefix }}' + purge_tags: True + register: tag_eip + check_mode: yes + + - assert: + that: + - tag_eip is not changed + + - name: Purge tags (idempotence) + ec2_eip: + state: present + public_ip: '{{ eip.public_ip }}' + tags: + "third tag": 'Third tag - {{ resource_prefix }}' + purge_tags: True + register: tag_eip + + - ec2_eip_info: + filters: + public-ip: '{{ eip.public_ip }}' + register: eip_info + + - assert: + that: + - tag_eip is not changed + - '"AnsibleEIPTestPrefix" not in eip_info.addresses[0].tags' + - '"another_tag" not in eip_info.addresses[0].tags' + - '"third tag" in eip_info.addresses[0].tags' + - eip_info.addresses[0].tags['third tag'] == 'Third tag - ' + resource_prefix + + # ----- Cleanup ------------------------------------------------------------------------------ - - name: allocate a new eip - ec2_eip: - state: present - register: eip - - ec2_eip_info: null - register: eip_info - - assert: - that: - - eip is defined - - eip is changed - - eip.public_ip is defined and ( eip.public_ip | ansible.utils.ipaddr ) - - eip.allocation_id is defined and eip.allocation_id.startswith("eipalloc-") - - ( eip_info_start.addresses | length ) + 1 == ( eip_info.addresses | length ) - - ############################################################################################# - - - name: Tag EIP - ec2_eip: - state: present - public_ip: '{{ eip.public_ip }}' - tags: - AnsibleEIPTestPrefix: '{{ resource_prefix }}' - another_tag: 'another Value {{ resource_prefix }}' - register: tag_eip - - ec2_eip_info: null - register: eip_info - - assert: - that: - - tag_eip is defined - - tag_eip is changed - - '"AnsibleEIPTestPrefix" in eip_info.addresses[0].tags' - - '"another_tag" in eip_info.addresses[0].tags' - - eip_info.addresses[0].tags['AnsibleEIPTestPrefix'] == resource_prefix - - eip_info.addresses[0].tags['another_tag'] == 'another Value ' + resource_prefix - - - name: Tag EIP - ec2_eip: - state: present - public_ip: '{{ eip.public_ip }}' - tags: - AnsibleEIPTestPrefix: '{{ resource_prefix }}' - another_tag: 'another Value {{ resource_prefix }}' - register: tag_eip - - ec2_eip_info: null - register: eip_info - - assert: - that: - - tag_eip is defined - - tag_eip is not changed - - '"AnsibleEIPTestPrefix" in eip_info.addresses[0].tags' - - '"another_tag" in eip_info.addresses[0].tags' - - eip_info.addresses[0].tags['AnsibleEIPTestPrefix'] == resource_prefix - - eip_info.addresses[0].tags['another_tag'] == 'another Value ' + resource_prefix - - - name: Add another Tag - ec2_eip: - state: present - public_ip: '{{ eip.public_ip }}' - tags: - "third tag": 'Third tag - {{ resource_prefix }}' - purge_tags: False - register: tag_eip - - ec2_eip_info: null - register: eip_info - - assert: - that: - - tag_eip is defined - - tag_eip is changed - - '"AnsibleEIPTestPrefix" in eip_info.addresses[0].tags' - - '"another_tag" in eip_info.addresses[0].tags' - - '"third tag" in eip_info.addresses[0].tags' - - eip_info.addresses[0].tags['AnsibleEIPTestPrefix'] == resource_prefix - - eip_info.addresses[0].tags['another_tag'] == 'another Value ' + resource_prefix - - eip_info.addresses[0].tags['third tag'] == 'Third tag - ' + resource_prefix - - - name: Add another Tag - ec2_eip: - state: present - public_ip: '{{ eip.public_ip }}' - tags: - "third tag": 'Third tag - {{ resource_prefix }}' - purge_tags: False - register: tag_eip - - ec2_eip_info: null - register: eip_info - - assert: - that: - - tag_eip is defined - - tag_eip is not changed - - '"AnsibleEIPTestPrefix" in eip_info.addresses[0].tags' - - '"another_tag" in eip_info.addresses[0].tags' - - '"third tag" in eip_info.addresses[0].tags' - - eip_info.addresses[0].tags['AnsibleEIPTestPrefix'] == resource_prefix - - eip_info.addresses[0].tags['another_tag'] == 'another Value ' + resource_prefix - - eip_info.addresses[0].tags['third tag'] == 'Third tag - ' + resource_prefix - - - name: Purge most tags - ec2_eip: - state: present - public_ip: '{{ eip.public_ip }}' - tags: - "third tag": 'Third tag - {{ resource_prefix }}' - purge_tags: True - register: tag_eip - - ec2_eip_info: null - register: eip_info - - assert: - that: - - tag_eip is defined - - tag_eip is changed - - '"AnsibleEIPTestPrefix" not in eip_info.addresses[0].tags' - - '"another_tag" not in eip_info.addresses[0].tags' - - '"third tag" in eip_info.addresses[0].tags' - - eip_info.addresses[0].tags['third tag'] == 'Third tag - ' + resource_prefix - - - name: Purge most tags - ec2_eip: - state: present - public_ip: '{{ eip.public_ip }}' - tags: - "third tag": 'Third tag - {{ resource_prefix }}' - purge_tags: True - register: tag_eip - - ec2_eip_info: null - register: eip_info - - assert: - that: - - tag_eip is defined - - tag_eip is not changed - - '"AnsibleEIPTestPrefix" not in eip_info.addresses[0].tags' - - '"another_tag" not in eip_info.addresses[0].tags' - - '"third tag" in eip_info.addresses[0].tags' - - eip_info.addresses[0].tags['third tag'] == 'Third tag - ' + resource_prefix - - ############################################################################################# - - - name: Release eip - ec2_eip: - state: absent - public_ip: '{{ eip.public_ip }}' - register: eip_release - - ec2_eip_info: null - register: eip_info - - assert: - that: - - eip_release is defined - - eip_release is changed - - ( eip_info_start.addresses | length ) == ( eip_info.addresses | length ) - - name: Rerelease eip (no change) - ec2_eip: - state: absent - public_ip: '{{ eip.public_ip }}' - register: eip_release - - ec2_eip_info: null - register: eip_info - - assert: - that: - - eip_release is defined - - eip_release is not changed - - ( eip_info_start.addresses | length ) == ( eip_info.addresses | length ) - - name: Cleanup VPC - ec2_vpc_net: - state: absent - name: '{{ resource_prefix }}-vpc' - cidr_block: '{{ vpc_cidr }}' - - - name: Create an EIP outside a VPC - ec2_eip: - state: present - in_vpc: '{{ omit }}' - register: unbound_eip - - assert: - that: - - unbound_eip is successful - - unbound_eip is changed - - name: Release EIP - ec2_eip: - state: absent - public_ip: '{{ unbound_eip.public_ip }}' - register: release_unbound_eip - - assert: - that: - - release_unbound_eip is successful - - release_unbound_eip is changed - # ===================================================== always: - - name: Cleanup instance (by id) - ec2_instance: - instance_ids: '{{ create_ec2_instance_result.instance_ids }}' - state: absent - wait: true - ignore_errors: true - - name: Cleanup instance (by name) - ec2_instance: - name: '{{ resource_prefix }}-instance' - state: absent - wait: true - ignore_errors: true - - name: Cleanup ENI A - ec2_eni: - state: absent - eni_id: '{{ eni_create_a.interface.id }}' - ignore_errors: true - - name: Cleanup ENI B - ec2_eni: - state: absent - eni_id: '{{ eni_create_b.interface.id }}' - ignore_errors: true - - name: Cleanup instance eip - ec2_eip: - state: absent - public_ip: '{{ instance_eip.public_ip }}' - retries: 5 - delay: 5 - until: eip_cleanup is successful - ignore_errors: true - - name: Cleanup IGW - ec2_vpc_igw: - state: absent - vpc_id: '{{ vpc_result.vpc.id }}' - register: vpc_igw - ignore_errors: true - - name: Cleanup security group - ec2_group: - state: absent - name: '{{ resource_prefix }}-sg' - ignore_errors: true - - name: Cleanup Subnet - ec2_vpc_subnet: - state: absent - cidr: '{{ subnet_cidr }}' - vpc_id: '{{ vpc_result.vpc.id }}' - ignore_errors: true - - name: Cleanup eip - ec2_eip: - state: absent - public_ip: '{{ eip.public_ip }}' - when: eip is changed - ignore_errors: true - - name: Cleanup reallocate_eip - ec2_eip: - state: absent - public_ip: '{{ reallocate_eip.public_ip }}' - when: reallocate_eip is changed - ignore_errors: true - - name: Cleanup backend_eip - ec2_eip: - state: absent - public_ip: '{{ backend_eip.public_ip }}' - when: backend_eip is changed - ignore_errors: true - - name: Cleanup no_tagged_eip - ec2_eip: - state: absent - public_ip: '{{ no_tagged_eip.public_ip }}' - when: no_tagged_eip is changed - ignore_errors: true - - name: Cleanup unbound_eip - ec2_eip: - state: absent - public_ip: '{{ unbound_eip.public_ip }}' - when: unbound_eip is changed - ignore_errors: true - - name: Cleanup VPC - ec2_vpc_net: - state: absent - name: '{{ resource_prefix }}-vpc' - cidr_block: '{{ vpc_cidr }}' - ignore_errors: true + + - name: Cleanup instance (by id) + ec2_instance: + instance_ids: '{{ create_ec2_instance_result.instance_ids }}' + state: absent + wait: true + ignore_errors: true + + - name: Cleanup instance (by name) + ec2_instance: + name: '{{ resource_prefix }}-instance' + state: absent + wait: true + ignore_errors: true + + - name: Cleanup ENI A + ec2_eni: + state: absent + eni_id: '{{ eni_create_a.interface.id }}' + ignore_errors: true + + - name: Cleanup ENI B + ec2_eni: + state: absent + eni_id: '{{ eni_create_b.interface.id }}' + ignore_errors: true + + - name: Cleanup instance eip + ec2_eip: + state: absent + public_ip: '{{ instance_eip.public_ip }}' + retries: 5 + delay: 5 + until: eip_cleanup is successful + ignore_errors: true + + - name: Cleanup IGW + ec2_vpc_igw: + state: absent + vpc_id: '{{ vpc_result.vpc.id }}' + register: vpc_igw + ignore_errors: true + + - name: Cleanup security group + ec2_group: + state: absent + name: '{{ resource_prefix }}-sg' + ignore_errors: true + + - name: Cleanup Subnet + ec2_vpc_subnet: + state: absent + cidr: '{{ subnet_cidr }}' + vpc_id: '{{ vpc_result.vpc.id }}' + ignore_errors: true + + - name: Cleanup eip + ec2_eip: + state: absent + public_ip: '{{ eip.public_ip }}' + ignore_errors: true + + - name: Cleanup reallocate_eip + ec2_eip: + state: absent + public_ip: '{{ reallocate_eip.public_ip }}' + ignore_errors: true + + - name: Cleanup backend_eip + ec2_eip: + state: absent + public_ip: '{{ backend_eip.public_ip }}' + ignore_errors: true + + - name: Cleanup no_tagged_eip + ec2_eip: + state: absent + public_ip: '{{ no_tagged_eip.public_ip }}' + ignore_errors: true + + - name: Cleanup VPC + ec2_vpc_net: + state: absent + name: '{{ resource_prefix }}-vpc' + cidr_block: '{{ vpc_cidr }}' + ignore_errors: true