diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 12:04:41 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 12:04:41 +0000 |
commit | 975f66f2eebe9dadba04f275774d4ab83f74cf25 (patch) | |
tree | 89bd26a93aaae6a25749145b7e4bca4a1e75b2be /ansible_collections/community/aws/tests/integration | |
parent | Initial commit. (diff) | |
download | ansible-975f66f2eebe9dadba04f275774d4ab83f74cf25.tar.xz ansible-975f66f2eebe9dadba04f275774d4ab83f74cf25.zip |
Adding upstream version 7.7.0+dfsg.upstream/7.7.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ansible_collections/community/aws/tests/integration')
637 files changed, 67741 insertions, 0 deletions
diff --git a/ansible_collections/community/aws/tests/integration/constraints.txt b/ansible_collections/community/aws/tests/integration/constraints.txt new file mode 100644 index 000000000..cd546e7c2 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/constraints.txt @@ -0,0 +1,7 @@ +# Specifically run tests against the oldest versions that we support +boto3==1.18.0 +botocore==1.21.0 + +# AWS CLI has `botocore==` dependencies, provide the one that matches botocore +# to avoid needing to download over a years worth of awscli wheels. +awscli==1.20.0 diff --git a/ansible_collections/community/aws/tests/integration/requirements.txt b/ansible_collections/community/aws/tests/integration/requirements.txt new file mode 100644 index 000000000..352e8b7ff --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/requirements.txt @@ -0,0 +1,13 @@ +# Our code is based on the AWS SDKs +boto3 +botocore + +# netaddr is needed for ansible.utils.ipv6 +netaddr +virtualenv +# Sometimes needed where we don't have features we need in modules +awscli +# Used for comparing SSH Public keys to the Amazon fingerprints +pycrypto +# Used by ec2_asg_scheduled_action +python-dateutil diff --git a/ansible_collections/community/aws/tests/integration/targets/__init__.py b/ansible_collections/community/aws/tests/integration/targets/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/__init__.py diff --git a/ansible_collections/community/aws/tests/integration/targets/accessanalyzer_validate_policy_info/aliases b/ansible_collections/community/aws/tests/integration/targets/accessanalyzer_validate_policy_info/aliases new file mode 100644 index 000000000..4ef4b2067 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/accessanalyzer_validate_policy_info/aliases @@ -0,0 +1 @@ +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/accessanalyzer_validate_policy_info/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/accessanalyzer_validate_policy_info/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/accessanalyzer_validate_policy_info/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/accessanalyzer_validate_policy_info/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/accessanalyzer_validate_policy_info/tasks/main.yml new file mode 100644 index 000000000..857a7c1b4 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/accessanalyzer_validate_policy_info/tasks/main.yml @@ -0,0 +1,284 @@ +--- +- module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key | default(omit) }}' + aws_secret_key: '{{ aws_secret_key | default(omit) }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region | default(omit) }}' + + block: + - name: get ARN of calling user + aws_caller_info: + register: aws_caller_info + + - name: Store Account ID for later use + set_fact: + aws_account_id: '{{ aws_caller_info.account }}' + + # Notes: + # - Because the policy validation includes both linting, security hints and error detection, we + # can't assume that we'll only get the entry we expect. + # - Because Ansible's done its thing and converted from YAML to dict to JSON string, line + # numbers aren't useful, as such we can test that the fields we expect are there, but we + # can't test things like locations + # - Some fields are arbitrary strings with recommendations, we shouldn't assume these will stay + # constant + + - name: Test with invalid role ('ERROR') + accessanalyzer_validate_policy_info: + policy: + Version: '2012-10-17' + Statement: + - Sid: AttachPolicyToInvalidRole + Effect: Allow + Action: + - iam:AttachRolePolicy + Resource: + - 'arn:aws:iam::{{ aws_account_id }}:invalid-role/example-role' + - 'arn:aws:iam::{{ aws_account_id }}:role/example-role' + Condition: + ArnLike: + iam:PolicyArn: + - 'arn:aws:iam::aws:policy/MyExamplePolicy' + register: results + + - assert: + that: + - "'findings' in results" + - results.findings | length >= 1 + - "'INVALID_ARN_RESOURCE' in (results.findings | map(attribute='issue_code'))" + - "'finding_details' in expected_finding" + # We should be relatively relaxed with this message, there's a risk AWS will change the + # wording, but make a vague check that we're getting English as expected + - "'does not match the expected' in expected_finding.finding_details" + - "'finding_type' in expected_finding" + - expected_finding.finding_type == 'ERROR' + - "'issue_code' in expected_finding" + - expected_finding.issue_code == 'INVALID_ARN_RESOURCE' + - "'learn_more_link' in expected_finding" + - "'locations' in expected_finding" + - "'path' in first_location" + - "'span' in first_location" + vars: + expected_finding: "{{ results.findings | selectattr('issue_code', 'equalto', 'INVALID_ARN_RESOURCE') | flatten | first }}" + first_location: "{{ expected_finding.locations | first }}" + + - name: Test with duplicate role ('SUGGESTION') + accessanalyzer_validate_policy_info: + policy: | + { + "Version": "2012-10-17", + "Statement": [{ + "Sid": "AttachPolicyToDuplicateWildcardRole", + "Effect": "Allow", + "Action": ["iam:AttachRolePolicy"], + "Resource": [ + "arn:aws:iam::123456789012:role/example-role", + "arn:aws:iam::123456789012:role/example*" + ], + "Condition": { + "ArnLike": { + "iam:PolicyArn": [ + "arn:aws:iam::aws:policy/MyExamplePolicy" + ] + } + } + }] + } + register: results + + - assert: + that: + - "'findings' in results" + - results.findings | length >= 1 + - "'REDUNDANT_RESOURCE' in (results.findings | map(attribute='issue_code'))" + - "'finding_details' in expected_finding" + - "'finding_type' in expected_finding" + - expected_finding.finding_type == 'SUGGESTION' + - "'issue_code' in expected_finding" + - expected_finding.issue_code == 'REDUNDANT_RESOURCE' + - "'learn_more_link' in expected_finding" + - "'locations' in expected_finding" + - "'path' in first_location" + - "'span' in first_location" + vars: + expected_finding: "{{ results.findings | selectattr('issue_code', 'equalto', 'REDUNDANT_RESOURCE') | flatten | first }}" + first_location: "{{ expected_finding.locations | first }}" + + - name: Test with duplicate SID ('WARNING') + accessanalyzer_validate_policy_info: + policy_type: resource + policy: + Version: '2012-10-17' + Statement: + - Sid: DuplicateSID + Effect: Allow + Action: + - sts:AssumeRole + Principal: + Service: 'ssm-incidents.amazonaws.com' + - Sid: DuplicateSID + Effect: Allow + Action: + - sts:AssumeRole + Principal: + Service: 'ec2.amazonaws.com' + register: results + + - assert: + that: + - "'findings' in results" + - results.findings | length >= 1 + - "'UNIQUE_SIDS_RECOMMENDED' in (results.findings | map(attribute='issue_code'))" + - "'finding_details' in expected_finding" + - "'finding_type' in expected_finding" + - expected_finding.finding_type == 'WARNING' + - "'issue_code' in expected_finding" + - expected_finding.issue_code == 'UNIQUE_SIDS_RECOMMENDED' + - "'learn_more_link' in expected_finding" + - "'locations' in expected_finding" + - "'path' in first_location" + - "'span' in first_location" + vars: + expected_finding: "{{ results.findings | selectattr('issue_code', 'equalto', 'UNIQUE_SIDS_RECOMMENDED') | flatten | first }}" + first_location: "{{ expected_finding.locations | first }}" + + - name: Test with wildcard to PassRole ('SECURITY_WARNING') + accessanalyzer_validate_policy_info: + policy: + Version: '2012-10-17' + Statement: + - Sid: PassRoleWithWildcard + Effect: Allow + Action: + - iam:PassRole + Resource: + - '*' + register: results + + - assert: + that: + - "'findings' in results" + - results.findings | length >= 1 + - "'PASS_ROLE_WITH_STAR_IN_RESOURCE' in (results.findings | map(attribute='issue_code'))" + - "'finding_details' in expected_finding" + - "'finding_type' in expected_finding" + - expected_finding.finding_type == 'SECURITY_WARNING' + - "'issue_code' in expected_finding" + - expected_finding.issue_code == 'PASS_ROLE_WITH_STAR_IN_RESOURCE' + - "'learn_more_link' in expected_finding" + - "'locations' in expected_finding" + - "'path' in first_location" + - "'span' in first_location" + vars: + expected_finding: "{{ results.findings | selectattr('issue_code', 'equalto', 'PASS_ROLE_WITH_STAR_IN_RESOURCE') | flatten | first }}" + first_location: "{{ expected_finding.locations | first }}" + + - name: Test with single filter value + accessanalyzer_validate_policy_info: + policy: + Version: '2012-10-17' + Statement: + - Sid: AttachPolicyToInvalidRole + Effect: Allow + Action: + - iam:AttachRolePolicy + Resource: + - 'arn:aws:iam::{{ aws_account_id }}:invalid-role/example-role' + - 'arn:aws:iam::{{ aws_account_id }}:role/example-role' + - 'arn:aws:iam::{{ aws_account_id }}:role/example-*' + Condition: + ArnLike: + iam:PolicyArn: + - 'arn:aws:iam::aws:policy/MyExamplePolicy' + - Sid: PassRoleWithWildcard + Effect: Allow + Action: + - iam:PassRole + Resource: + - '*' + results_filter: 'error' + register: results + + - assert: + that: + - "'findings' in results" + - results.findings | length >= 1 + - results.findings | map(attribute='finding_type') | unique | list == ['ERROR'] + - "'INVALID_ARN_RESOURCE' in (results.findings | map(attribute='issue_code'))" + + - name: Test with multiple filter value + accessanalyzer_validate_policy_info: + policy: + Version: '2012-10-17' + Statement: + - Sid: AttachPolicyToInvalidRole + Effect: Allow + Action: + - iam:AttachRolePolicy + Resource: + - 'arn:aws:iam::{{ aws_account_id }}:invalid-role/example-role' + - 'arn:aws:iam::{{ aws_account_id }}:role/example-role' + - 'arn:aws:iam::{{ aws_account_id }}:role/example-*' + Condition: + ArnLike: + iam:PolicyArn: + - 'arn:aws:iam::aws:policy/MyExamplePolicy' + - Sid: PassRoleWithWildcard + Effect: Allow + Action: + - iam:PassRole + Resource: + - '*' + results_filter: + - 'error' + - 'security' + register: results + + - assert: + that: + - "'findings' in results" + - results.findings | length >= 1 + - results.findings | map(attribute='finding_type') | unique | list | sort == ['ERROR', 'SECURITY_WARNING'] + - "'INVALID_ARN_RESOURCE' in (results.findings | map(attribute='issue_code'))" + - "'PASS_ROLE_WITH_STAR_IN_RESOURCE' in (results.findings | map(attribute='issue_code'))" + + - name: Test with Locale + accessanalyzer_validate_policy_info: + locale: 'DE' + policy: + Version: '2012-10-17' + Statement: + - Sid: AttachPolicyToInvalidRole + Effect: Allow + Action: + - iam:AttachRolePolicy + Resource: + - 'arn:aws:iam::{{ aws_account_id }}:invalid-role/example-role' + - 'arn:aws:iam::{{ aws_account_id }}:role/example-role' + Condition: + ArnLike: + iam:PolicyArn: + - 'arn:aws:iam::aws:policy/MyExamplePolicy' + register: results + + - assert: + that: + - "'findings' in results" + - results.findings | length >= 1 + - "'INVALID_ARN_RESOURCE' in (results.findings | map(attribute='issue_code'))" + - "'finding_details' in expected_finding" + # We should be relatively relaxed with this message, there's a risk AWS will change the + # wording, but make a vague check that we're getting German as expected + - "'stimmt nicht mit dem erwarteten' in expected_finding.finding_details" + - "'finding_type' in expected_finding" + - expected_finding.finding_type == 'ERROR' + - "'issue_code' in expected_finding" + - expected_finding.issue_code == 'INVALID_ARN_RESOURCE' + - "'learn_more_link' in expected_finding" + - "'locations' in expected_finding" + - "'path' in first_location" + - "'span' in first_location" + vars: + expected_finding: "{{ results.findings | selectattr('issue_code', 'equalto', 'INVALID_ARN_RESOURCE') | flatten | first }}" + first_location: "{{ expected_finding.locations | first }}" diff --git a/ansible_collections/community/aws/tests/integration/targets/acm_certificate/aliases b/ansible_collections/community/aws/tests/integration/targets/acm_certificate/aliases new file mode 100644 index 000000000..55246697e --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/acm_certificate/aliases @@ -0,0 +1,4 @@ +time=15m +cloud/aws + +acm_certificate_info diff --git a/ansible_collections/community/aws/tests/integration/targets/acm_certificate/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/acm_certificate/defaults/main.yml new file mode 100644 index 000000000..5d3648f8e --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/acm_certificate/defaults/main.yml @@ -0,0 +1,40 @@ +--- +# we'll generate 3 certificates locally for the test +# Upload the first +# overwrite it with the second +# and the third is unrelated, to check we only get info about the first when we want +local_certs: + - priv_key: "{{ remote_tmp_dir }}/private-1.pem" + cert: "{{ remote_tmp_dir }}/public-1.pem" + csr: "{{ remote_tmp_dir }}/csr-1.csr" + domain: "acm1.{{ aws_acm_test_uuid }}.ansible.com" + name: "{{ resource_prefix }}_{{ aws_acm_test_uuid }}_1" + + - priv_key: "{{ remote_tmp_dir }}/private-2.pem" + cert: "{{ remote_tmp_dir }}/public-2.pem" + csr: "{{ remote_tmp_dir }}/csr-2.csr" + domain: "acm2.{{ aws_acm_test_uuid }}.ansible.com" + name: "{{ resource_prefix }}_{{ aws_acm_test_uuid }}_2" + + - priv_key: "{{ remote_tmp_dir }}/private-3.pem" + cert: "{{ remote_tmp_dir }}/public-3.pem" + csr: "{{ remote_tmp_dir }}/csr-3.csr" + domain: "acm3.{{ aws_acm_test_uuid }}.ansible.com" + name: "{{ resource_prefix }}_{{ aws_acm_test_uuid }}_3" + +# we'll have one private key +# make 2 chains using it +# so we can test what happens when you change just the chain +# not the domain or key +chained_cert: + priv_key: "{{ remote_tmp_dir }}/private-ch-0.pem" + domain: "acm-ch.{{ aws_acm_test_uuid }}.ansible.com" + name: "{{ resource_prefix }}_{{ aws_acm_test_uuid }}_4" + chains: + - cert: "{{ remote_tmp_dir }}/public-ch-0.pem" + csr: "{{ remote_tmp_dir }}/csr-ch-0.csr" + ca: 0 # index into local_certs + - cert: "{{ remote_tmp_dir }}/public-ch-1.pem" + csr: "{{ remote_tmp_dir }}/csr-ch-1.csr" + ca: 1 # index into local_certs +
\ No newline at end of file diff --git a/ansible_collections/community/aws/tests/integration/targets/acm_certificate/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/acm_certificate/meta/main.yml new file mode 100644 index 000000000..1810d4bec --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/acm_certificate/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - setup_remote_tmp_dir diff --git a/ansible_collections/community/aws/tests/integration/targets/acm_certificate/tasks/full_acm_test.yml b/ansible_collections/community/aws/tests/integration/targets/acm_certificate/tasks/full_acm_test.yml new file mode 100644 index 000000000..5cbd156dd --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/acm_certificate/tasks/full_acm_test.yml @@ -0,0 +1,508 @@ +- name: AWS ACM integration test + module_defaults: + group/aws: + aws_region: '{{ aws_region }}' + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + block: + - name: list certs + aws_acm_info: null + register: list_all + - name: list certs with check mode + aws_acm_info: null + register: list_all_check + check_mode: yes # read-only task, should work the same as with no + - name: check certificate listing worked + assert: + that: + - list_all.certificates is defined + - list_all_check.certificates is defined + - list_all.certificates == list_all_check.certificates + - name: ensure absent cert which doesn't exist - first time + aws_acm: + name_tag: '{{ item.name }}' + state: absent + with_items: '{{ local_certs }}' + - name: ensure absent cert which doesn't exist - second time + aws_acm: + name_tag: '{{ item[0].name }}' + state: absent + check_mode: '{{ item[1] }}' + with_nested: + - '{{ local_certs }}' + - [true, false] + register: absent_start_two + - name: check no change when ensuring absent cert is absent + assert: + that: + - not item.changed + with_items: "{{ absent_start_two.results }}" + - name: list cert which shouldn't exist + aws_acm_info: + tags: + Name: '{{ item.name }}' + register: list_tag + with_items: '{{ local_certs }}' + - name: check listing of missing cert returns no result + with_items: '{{ list_tag.results }}' + assert: + that: + - (item.certificates | length) == 0 + - not list_tag.changed + - name: check directory was made + assert: + that: + - remote_tmp_dir is defined + - name: Generate private key for local certs + with_items: '{{ local_certs }}' + community.crypto.openssl_privatekey: + path: '{{ item.priv_key }}' + type: RSA + size: 2048 + - name: Generate an OpenSSL Certificate Signing Request for own certs + with_items: '{{ local_certs }}' + community.crypto.openssl_csr: + path: '{{ item.csr }}' + privatekey_path: '{{ item.priv_key }}' + common_name: '{{ item.domain }}' + - name: Generate a Self Signed OpenSSL certificate for own certs + with_items: '{{ local_certs }}' + community.crypto.x509_certificate: + provider: selfsigned + path: '{{ item.cert }}' + csr_path: '{{ item.csr }}' + privatekey_path: '{{ item.priv_key }}' + selfsigned_digest: sha256 + - name: upload certificate with check mode + aws_acm: + name_tag: '{{ item.name }}' + certificate: '{{ lookup(''file'', item.cert ) }}' + private_key: '{{ lookup(''file'', item.priv_key ) }}' + state: present + check_mode: yes + register: upload_check + with_items: '{{ local_certs }}' + - name: check whether cert was uploaded in check mode + aws_acm_info: + tags: + Name: '{{ item.name }}' + register: list_after_check_mode_upload + with_items: '{{ local_certs }}' + - name: check cert was not really uploaded in check mode + with_items: "{{ list_after_check_mode_upload.results }}" + assert: + that: + - upload_check.changed + - (item.certificates | length) == 0 + - name: upload certificates first time + aws_acm: + name_tag: '{{ item.name }}' + certificate: '{{ lookup(''file'', item.cert ) }}' + private_key: '{{ lookup(''file'', item.priv_key ) }}' + state: present + register: upload + check_mode: no + with_items: '{{ local_certs }}' + until: upload is succeeded + retries: 5 + delay: 10 + - assert: + that: + - prev_task.certificate.arn is defined + - ('arn:aws:acm:123' | regex_search( 'arn:aws:acm:' )) is defined + - (prev_task.certificate.arn | regex_search( 'arn:aws:acm:' )) is defined + - prev_task.certificate.domain_name == original_cert.domain + - prev_task.changed + with_items: '{{ upload.results }}' + vars: + original_cert: '{{ item.item }}' + prev_task: '{{ item }}' + - name: fetch data about cert just uploaded, by ARN + aws_acm_info: + certificate_arn: '{{ item.certificate.arn }}' + register: fetch_after_up + with_items: '{{ upload.results }}' + - name: check output of prior task (fetch data about cert just uploaded, by ARN) + assert: + that: + - fetch_after_up_result.certificates | length == 1 + - fetch_after_up_result.certificates[0].certificate_arn == upload_result.certificate.arn + - fetch_after_up_result.certificates[0].domain_name == original_cert.domain + - (fetch_after_up_result.certificates[0].certificate | replace( ' ', '' ) | replace( '\n', '')) == (lookup( 'file', original_cert.cert ) | replace( ' ', '' ) | replace( '\n', '' )) + - '''Name'' in fetch_after_up_result.certificates[0].tags' + - fetch_after_up_result.certificates[0].tags['Name'] == original_cert.name + with_items: '{{ fetch_after_up.results }}' + vars: + fetch_after_up_result: '{{ item }}' + upload_result: '{{ item.item }}' + original_cert: '{{ item.item.item }}' + - name: fetch data about cert just uploaded, by name + aws_acm_info: + tags: + Name: '{{ original_cert.name }}' + register: fetch_after_up_name + with_items: '{{ upload.results }}' + vars: + upload_result: '{{ item }}' + original_cert: '{{ item.item }}' + - name: check fetched data of cert we just uploaded + assert: + that: + - fetch_after_up_name_result.certificates | length == 1 + - fetch_after_up_name_result.certificates[0].certificate_arn == upload_result.certificate.arn + - fetch_after_up_name_result.certificates[0].domain_name == original_cert.domain + - (fetch_after_up_name_result.certificates[0].certificate | replace( ' ', '' ) | replace( '\n', '')) == (lookup('file', original_cert.cert ) | replace( ' ', '' ) | replace( '\n', '')) + - '''Name'' in fetch_after_up_name_result.certificates[0].tags' + - fetch_after_up_name_result.certificates[0].tags['Name'] == original_cert.name + with_items: '{{ fetch_after_up_name.results }}' + vars: + fetch_after_up_name_result: '{{ item }}' + upload_result: '{{ item.item }}' + original_cert: '{{ item.item.item }}' + - name: fetch data about cert just uploaded, by domain name + aws_acm_info: + domain_name: '{{ original_cert.domain }}' + register: fetch_after_up_domain + with_items: '{{ upload.results }}' + vars: + original_cert: '{{ item.item }}' + - name: compare fetched data of cert just uploaded to upload task + assert: + that: + - fetch_after_up_domain_result.certificates | length == 1 + - fetch_after_up_domain_result.certificates[0].certificate_arn == upload_result.certificate.arn + - fetch_after_up_domain_result.certificates[0].domain_name == original_cert.domain + - (fetch_after_up_domain_result.certificates[0].certificate | replace( ' ', '' ) | replace( '\n', '')) == (lookup('file', original_cert.cert ) | replace( ' ', '' ) | replace( '\n', '')) + - '''Name'' in fetch_after_up_domain_result.certificates[0].tags' + - fetch_after_up_domain_result.certificates[0].tags['Name'] == original_cert.name + with_items: '{{ fetch_after_up_domain.results }}' + vars: + fetch_after_up_domain_result: '{{ item }}' + upload_result: '{{ item.item }}' + original_cert: '{{ item.item.item }}' + - name: upload certificates again, check not changed + aws_acm: + name_tag: '{{ item.name }}' + certificate: '{{ lookup(''file'', item.cert ) }}' + private_key: '{{ lookup(''file'', item.priv_key ) }}' + state: present + register: upload2 + with_items: '{{ local_certs }}' + failed_when: upload2.changed + - name: update first cert with body of the second, first time, check mode + aws_acm: + state: present + name_tag: '{{ local_certs[0].name }}' + certificate: '{{ lookup(''file'', local_certs[1].cert ) }}' + private_key: '{{ lookup(''file'', local_certs[1].priv_key ) }}' + check_mode: yes + register: overwrite_check + - name: check update in check mode detected required update + assert: + that: + - overwrite_check.changed + - name: check previous tasks did not change real cert + aws_acm_info: + tags: + Name: '{{ local_certs[0].name }}' + register: fetch_after_overwrite_check + - name: check update with check mode did not change real cert + assert: + that: + - fetch_after_overwrite_check.certificates | length == 1 + - fetch_after_overwrite_check.certificates[0].certificate_arn == fetch_after_up.results[0].certificates[0].certificate_arn + - fetch_after_overwrite_check.certificates[0].domain_name == local_certs[0].domain + - (fetch_after_overwrite_check.certificates[0].certificate | replace( ' ', '' ) | replace( '\n', '')) == (lookup('file', local_certs[0].cert )| replace( ' ', '' ) | replace( '\n', '')) + - '''Name'' in fetch_after_overwrite_check.certificates[0].tags' + - fetch_after_overwrite_check.certificates[0].tags['Name'] == local_certs[0].name + - name: update first cert with body of the second, first real time + aws_acm: + state: present + name_tag: '{{ local_certs[0].name }}' + certificate: '{{ lookup(''file'', local_certs[1].cert ) }}' + private_key: '{{ lookup(''file'', local_certs[1].priv_key ) }}' + register: overwrite + - name: check output of previous task (update first cert with body of the second, first time) + assert: + that: + - overwrite.certificate.arn is defined + - overwrite.certificate.arn | regex_search( 'arn:aws:acm:' ) is defined + - overwrite.certificate.arn == upload.results[0].certificate.arn + - overwrite.certificate.domain_name == local_certs[1].domain + - overwrite.changed + - name: check update was sucessfull + aws_acm_info: + tags: + Name: '{{ local_certs[0].name }}' + register: fetch_after_overwrite + - name: check output of update fetch + assert: + that: + - fetch_after_overwrite.certificates | length == 1 + - fetch_after_overwrite.certificates[0].certificate_arn == fetch_after_up.results[0].certificates[0].certificate_arn + - fetch_after_overwrite.certificates[0].domain_name == local_certs[1].domain + - (fetch_after_overwrite.certificates[0].certificate | replace( ' ', '' ) | replace( '\n', '')) == (lookup('file', local_certs[1].cert )| replace( ' ', '' ) | replace( '\n', '')) + - '''Name'' in fetch_after_overwrite.certificates[0].tags' + - fetch_after_overwrite.certificates[0].tags['Name'] == local_certs[0].name + - name: fetch other cert + aws_acm_info: + tags: + Name: '{{ local_certs[1].name }}' + register: check_after_overwrite + - name: check other cert unaffected + assert: + that: + - check_after_overwrite.certificates | length == 1 + - check_after_overwrite.certificates[0].certificate_arn == fetch_after_up.results[1].certificates[0].certificate_arn + - check_after_overwrite.certificates[0].domain_name == local_certs[1].domain + - (check_after_overwrite.certificates[0].certificate | replace( ' ', '' ) | replace( '\n', '')) == (lookup('file', local_certs[1].cert ) | replace( ' ', '' ) | replace( '\n', '')) + - '''Name'' in check_after_overwrite.certificates[0].tags' + - check_after_overwrite.certificates[0].tags['Name'] == local_certs[1].name + - name: update first cert with body of the second again + aws_acm: + state: present + name_tag: '{{ local_certs[0].name }}' + certificate: '{{ lookup(''file'', local_certs[1].cert ) }}' + private_key: '{{ lookup(''file'', local_certs[1].priv_key ) }}' + register: overwrite2 + - name: check output of previous task (update first cert with body of the second again) + assert: + that: + - overwrite2.certificate.arn is defined + - overwrite2.certificate.arn | regex_search( 'arn:aws:acm:' ) is defined + - overwrite2.certificate.arn == upload.results[0].certificate.arn + - overwrite2.certificate.domain_name == local_certs[1].domain + - not overwrite2.changed + - name: delete certs 1 and 2 in check mode + aws_acm: + state: absent + domain_name: '{{ local_certs[1].domain }}' + check_mode: yes + register: delete_both_check + - name: test deletion with check mode detected change + assert: + that: + - delete_both_check.changed + - name: fetch info for certs 1 and 2 + aws_acm_info: + tags: + Name: '{{ local_certs[item].name }}' + register: check_del_one_check + with_items: + - 0 + - 1 + - name: test deletion with check mode detected change + with_items: '{{ check_del_one_check.results }}' + assert: + that: + - (item.certificates | length) == 1 + - name: delete certs 1 and 2 real + aws_acm: + state: absent + domain_name: '{{ local_certs[1].domain }}' + register: delete_both + - name: test prev task + assert: + that: + - delete_both.arns is defined + - check_after_overwrite.certificates[0].certificate_arn in delete_both.arns + - upload.results[0].certificate.arn in delete_both.arns + - delete_both.changed + - name: fetch info for certs 1 and 2 + aws_acm_info: + tags: + Name: '{{ local_certs[item].name }}' + register: check_del_one + with_items: + - 0 + - 1 + retries: 2 + until: + - check_del_one is not failed + - check_del_one.certificates | length == 0 + delay: 10 + - name: check certs 1 and 2 were already deleted + with_items: '{{ check_del_one.results }}' + assert: + that: (item.certificates | length) == 0 + - name: check cert 3 + aws_acm_info: + tags: + Name: '{{ local_certs[2].name }}' + register: check_del_one_remain + - name: check cert 3 not deleted + assert: + that: + - (check_del_one_remain.certificates | length) == 1 + - name: delete cert 3 + aws_acm: + state: absent + domain_name: '{{ local_certs[2].domain }}' + register: delete_third + - name: check cert 3 deletion went as expected + assert: + that: + - delete_third.arns is defined + - delete_third.arns | length == 1 + - delete_third.arns[0] == upload.results[2].certificate.arn + - delete_third.changed + - name: check cert 3 was deleted + aws_acm_info: + tags: + Name: '{{ local_certs[2].name }}' + register: check_del_three + failed_when: check_del_three.certificates | length != 0 + - name: delete cert 3 again + aws_acm: + state: absent + domain_name: '{{ local_certs[2].domain }}' + register: delete_third + - name: check deletion of cert 3 not changed, because already deleted + assert: + that: + - delete_third.arns is defined + - delete_third.arns | length == 0 + - not delete_third.changed + - name: delete cert 3 again, check mode + aws_acm: + state: absent + domain_name: '{{ local_certs[2].domain }}' + check_mode: yes + register: delete_third_check + - name: test deletion in check mode detected required change + assert: + that: + - not delete_third_check.changed + - name: check directory was made + assert: + that: + - remote_tmp_dir is defined + - name: Generate private key for cert to be chained + community.crypto.openssl_privatekey: + path: '{{ chained_cert.priv_key }}' + type: RSA + size: 2048 + - name: Generate two OpenSSL Certificate Signing Requests for cert to be chained + with_items: '{{ chained_cert.chains }}' + community.crypto.openssl_csr: + path: '{{ item.csr }}' + privatekey_path: '{{ chained_cert.priv_key }}' + common_name: '{{ chained_cert.domain }}' + - name: Sign new certs with cert 0 and 1 + with_items: '{{ chained_cert.chains }}' + community.crypto.x509_certificate: + provider: ownca + path: '{{ item.cert }}' + csr_path: '{{ item.csr }}' + ownca_path: '{{ local_certs[item.ca].cert }}' + ownca_privatekey_path: '{{ local_certs[item.ca].priv_key }}' + selfsigned_digest: sha256 + - name: check files exist (for next task) + file: + path: '{{ item }}' + state: file + with_items: + - '{{ local_certs[chained_cert.chains[0].ca].cert }}' + - '{{ local_certs[chained_cert.chains[1].ca].cert }}' + - '{{ chained_cert.chains[0].cert }}' + - '{{ chained_cert.chains[1].cert }}' + - name: Find chains + with_items: '{{ chained_cert.chains }}' + register: chains + community.crypto.certificate_complete_chain: + input_chain: '{{ lookup(''file'', item.cert ) }}' + root_certificates: + - '{{ local_certs[item.ca].cert }}' + - name: upload chained cert, first chain, first time + aws_acm: + name_tag: '{{ chained_cert.name }}' + certificate: '{{ lookup(''file'', chained_cert.chains[0].cert ) }}' + certificate_chain: '{{ chains.results[0].complete_chain | join('' + + '') }}' + private_key: '{{ lookup(''file'', chained_cert.priv_key ) }}' + state: present + register: upload_chain + failed_when: not upload_chain.changed + - name: fetch chain of cert we just uploaded + aws_acm_info: + tags: + Name: '{{ chained_cert.name }}' + register: check_chain + until: + - check_chain.certificates | length >= 1 + retries: 5 + delay: 2 + - name: check chain of cert we just uploaded + assert: + that: + - (check_chain.certificates[0].certificate_chain | replace( ' ', '' ) | replace( '\n', '')) == ( chains.results[0].complete_chain | join( '\n' ) | replace( ' ', '' ) | replace( '\n', '') ) + - (check_chain.certificates[0].certificate | replace( ' ', '' ) | replace( '\n', '')) == ( lookup('file', chained_cert.chains[0].cert ) | replace( ' ', '' ) | replace( '\n', '') ) + - name: upload chained cert again, check not changed + aws_acm: + name_tag: '{{ chained_cert.name }}' + certificate: '{{ lookup(''file'', chained_cert.chains[0].cert ) }}' + certificate_chain: '{{ chains.results[0].complete_chain | join('' + + '') }}' + private_key: '{{ lookup(''file'', chained_cert.priv_key ) }}' + state: present + register: upload_chain_2 + - name: check previous task not changed + assert: + that: + - upload_chain_2.certificate.arn == upload_chain.certificate.arn + - not upload_chain_2.changed + - name: upload chained cert, different chain + aws_acm: + name_tag: '{{ chained_cert.name }}' + certificate: '{{ lookup(''file'', chained_cert.chains[1].cert ) }}' + certificate_chain: '{{ chains.results[1].complete_chain | join('' + + '') }}' + private_key: '{{ lookup(''file'', chained_cert.priv_key ) }}' + state: present + register: upload_chain_3 + - name: check uploading with different chain is changed + assert: + that: + - upload_chain_3.changed + - upload_chain_3.certificate.arn == upload_chain.certificate.arn + - name: fetch info about chain of cert we just updated + aws_acm_info: + tags: + Name: '{{ chained_cert.name }}' + register: check_chain_2 + - name: check chain of cert we just uploaded + assert: + that: + - (check_chain_2.certificates[0].certificate_chain | replace( ' ', '' ) | replace( '\n', '')) == ( chains.results[1].complete_chain | join( '\n' ) | replace( ' ', '' ) | replace( '\n', '') ) + - (check_chain_2.certificates[0].certificate | replace( ' ', '' ) | replace( '\n', '')) == ( lookup('file', chained_cert.chains[1].cert ) | replace( ' ', '' ) | replace( '\n', '') ) + - name: delete chained cert + aws_acm: + name_tag: '{{ chained_cert.name }}' + state: absent + register: delete_chain_3 + - name: check deletion of chained cert 3 is changed + assert: + that: + - delete_chain_3.changed + - upload_chain.certificate.arn in delete_chain_3.arns + always: + - name: delete first bunch of certificates + aws_acm: + name_tag: '{{ item.name }}' + state: absent + with_items: '{{ local_certs }}' + ignore_errors: true + - name: delete chained cert + aws_acm: + state: absent + name_tag: '{{ chained_cert.name }}' + ignore_errors: true + - name: deleting local directory with test artefacts + file: + path: '{{ remote_tmp_dir }}' + state: directory + ignore_errors: true diff --git a/ansible_collections/community/aws/tests/integration/targets/acm_certificate/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/acm_certificate/tasks/main.yml new file mode 100644 index 000000000..bf70587e6 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/acm_certificate/tasks/main.yml @@ -0,0 +1,583 @@ +- name: AWS ACM integration test + module_defaults: + group/aws: + aws_region: '{{ aws_region }}' + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + block: + # The CI runs many of these tests in parallel + # Use this random ID to differentiate which resources + # are from which test + - set_fact: + aws_acm_test_uuid: "{{ (10**9) | random }}" + - name: attempt to delete cert without specifying required parameter + aws_acm: + state: absent + register: result + ignore_errors: true + - name: assert failure when name_tag conflicts with tags.Name + assert: + that: + - 'result.failed' + - '"If ''state'' is specified as ''absent'' then exactly one of ''name_tag''" in result.msg' + - name: list certs + aws_acm_info: null + register: list_all + failed_when: list_all.certificates is not defined + - name: ensure absent cert which doesn't exist - first time + aws_acm: + name_tag: '{{ item.name }}' + state: absent + with_items: '{{ local_certs }}' + - name: ensure absent cert which doesn't exist - second time + aws_acm: + name_tag: '{{ item.name }}' + state: absent + with_items: '{{ local_certs }}' + register: absent_start_two + failed_when: absent_start_two.changed + - name: list cert which shouldn't exist + aws_acm_info: + tags: + Name: '{{ item.name }}' + register: list_tag + with_items: '{{ local_certs }}' + failed_when: list_tag.certificates | length > 0 + - name: check directory was made + assert: + that: + - remote_tmp_dir is defined + - name: Generate private key for local certs + with_items: '{{ local_certs }}' + community.crypto.openssl_privatekey: + path: '{{ item.priv_key }}' + type: RSA + size: 2048 + - name: Generate an OpenSSL Certificate Signing Request for own certs + with_items: '{{ local_certs }}' + community.crypto.openssl_csr: + path: '{{ item.csr }}' + privatekey_path: '{{ item.priv_key }}' + common_name: '{{ item.domain }}' + - name: Generate a Self Signed OpenSSL certificate for own certs + with_items: '{{ local_certs }}' + community.crypto.x509_certificate: + provider: selfsigned + path: '{{ item.cert }}' + csr_path: '{{ item.csr }}' + privatekey_path: '{{ item.priv_key }}' + selfsigned_digest: sha256 + - name: try to upload certificate, but name_tag conflicts with tags.Name + vars: + local_cert: '{{ local_certs[0] }}' + aws_acm: + name_tag: '{{ local_cert.name }}' + certificate: '{{ lookup(''file'', local_cert.cert ) }}' + private_key: '{{ lookup(''file'', local_cert.priv_key ) }}' + state: present + tags: + Name: '{{ local_cert.name }}-other' + Application: search + Environment: development + register: result + ignore_errors: true + - name: assert failure when name_tag conflicts with tags.Name + assert: + that: + - 'result.failed' + - '"conflicts with value of" in result.msg' + - name: upload certificates first time + aws_acm: + name_tag: '{{ item.name }}' + certificate: '{{ lookup(''file'', item.cert ) }}' + private_key: '{{ lookup(''file'', item.priv_key ) }}' + state: present + # Add tags + tags: + Application: search + Environment: development + purge_tags: false + register: upload + with_items: '{{ local_certs }}' + until: upload is succeeded + retries: 5 + delay: 10 + - assert: + that: + - prev_task.certificate.arn is defined + - ('arn:aws:acm:123' | regex_search( 'arn:aws:acm:' )) is defined + - (prev_task.certificate.arn | regex_search( 'arn:aws:acm:' )) is defined + - prev_task.certificate.domain_name == original_cert.domain + - prev_task.changed + with_items: '{{ upload.results }}' + vars: + original_cert: '{{ item.item }}' + prev_task: '{{ item }}' + - name: fetch data about cert just uploaded, by ARN + aws_acm_info: + certificate_arn: '{{ item.certificate.arn }}' + register: fetch_after_up + with_items: '{{ upload.results }}' + - name: check output of prior task (fetch data about cert just uploaded, by ARN) + assert: + that: + - fetch_after_up_result.certificates | length == 1 + - fetch_after_up_result.certificates[0].certificate_arn == upload_result.certificate.arn + - fetch_after_up_result.certificates[0].domain_name == original_cert.domain + - (fetch_after_up_result.certificates[0].certificate | replace( ' ', '' ) | replace( '\n', '')) == (lookup( 'file', original_cert.cert ) | replace( ' ', '' ) | replace( '\n', '' )) + - '''Name'' in fetch_after_up_result.certificates[0].tags' + - '''Application'' in fetch_after_up_result.certificates[0].tags' + - '''Environment'' in fetch_after_up_result.certificates[0].tags' + - fetch_after_up_result.certificates[0].tags['Name'] == original_cert.name + - fetch_after_up_result.certificates[0].tags['Application'] == 'search' + - fetch_after_up_result.certificates[0].tags['Environment'] == 'development' + with_items: '{{ fetch_after_up.results }}' + vars: + fetch_after_up_result: '{{ item }}' + upload_result: '{{ item.item }}' + original_cert: '{{ item.item.item }}' + - name: fetch data about cert just uploaded, by name + aws_acm_info: + tags: + Name: '{{ original_cert.name }}' + register: fetch_after_up_name + with_items: '{{ upload.results }}' + vars: + upload_result: '{{ item }}' + original_cert: '{{ item.item }}' + - name: check fetched data of cert we just uploaded + assert: + that: + - fetch_after_up_name_result.certificates | length == 1 + - fetch_after_up_name_result.certificates[0].certificate_arn == upload_result.certificate.arn + - fetch_after_up_name_result.certificates[0].domain_name == original_cert.domain + - (fetch_after_up_name_result.certificates[0].certificate | replace( ' ', '' ) | replace( '\n', '')) == (lookup('file', original_cert.cert ) | replace( ' ', '' ) | replace( '\n', '')) + - '''Name'' in fetch_after_up_name_result.certificates[0].tags' + - fetch_after_up_name_result.certificates[0].tags['Name'] == original_cert.name + with_items: '{{ fetch_after_up_name.results }}' + vars: + fetch_after_up_name_result: '{{ item }}' + upload_result: '{{ item.item }}' + original_cert: '{{ item.item.item }}' + - name: fetch data about cert just uploaded, by domain name + aws_acm_info: + domain_name: '{{ original_cert.domain }}' + register: fetch_after_up_domain + with_items: '{{ upload.results }}' + vars: + original_cert: '{{ item.item }}' + - name: compare fetched data of cert just uploaded to upload task + assert: + that: + - fetch_after_up_domain_result.certificates | length == 1 + - fetch_after_up_domain_result.certificates[0].certificate_arn == upload_result.certificate.arn + - fetch_after_up_domain_result.certificates[0].domain_name == original_cert.domain + - (fetch_after_up_domain_result.certificates[0].certificate | replace( ' ', '' ) | replace( '\n', '')) == (lookup('file', original_cert.cert ) | replace( ' ', '' ) | replace( '\n', '')) + - '''Name'' in fetch_after_up_domain_result.certificates[0].tags' + - fetch_after_up_domain_result.certificates[0].tags['Name'] == original_cert.name + with_items: '{{ fetch_after_up_domain.results }}' + vars: + fetch_after_up_domain_result: '{{ item }}' + upload_result: '{{ item.item }}' + original_cert: '{{ item.item.item }}' + - name: upload certificates again, check not changed + aws_acm: + name_tag: '{{ item.name }}' + certificate: '{{ lookup(''file'', item.cert ) }}' + private_key: '{{ lookup(''file'', item.priv_key ) }}' + state: present + register: upload2 + with_items: '{{ local_certs }}' + failed_when: upload2.changed + - name: change tags of existing certificate, check mode + aws_acm: + certificate_arn: '{{ certificate_arn }}' + tags: + Name: '{{ name_tag }}' + Application: search + Environment: staging + Owner: Bob + register: certificate_with_tags + check_mode: true + vars: + name_tag: '{{ upload2.results[0].item.name }}' + certificate_arn: '{{ upload2.results[0].certificate.arn }}' + domain_name: '{{ upload2.results[0].certificate.domain_name }}' + - assert: + that: + - certificate_with_tags.changed + - name: change tags of existing certificate, changes expected + aws_acm: + # When applying tags to an existing certificate, it is sufficient to specify the 'certificate_arn'. + # Previously, the 'aws_acm' module was requiring the 'certificate', 'name_tag' and 'domain_name' + # attributes. + certificate_arn: '{{ certificate_arn }}' + tags: + Name: '{{ name_tag }}' + Application: search + Environment: staging + Owner: Bob + register: certificate_with_tags + vars: + name_tag: '{{ upload2.results[0].item.name }}' + certificate_arn: '{{ upload2.results[0].certificate.arn }}' + domain_name: '{{ upload2.results[0].certificate.domain_name }}' + - name: assert certificate tags + assert: + that: + - certificate_with_tags.certificate.tags | length == 4 + - '''Name'' in certificate_with_tags.certificate.tags' + - '''Application'' in certificate_with_tags.certificate.tags' + - '''Environment'' in certificate_with_tags.certificate.tags' + - '''Owner'' in certificate_with_tags.certificate.tags' + - certificate_with_tags.certificate.tags['Name'] == name_tag + - certificate_with_tags.certificate.tags['Application'] == 'search' + - certificate_with_tags.certificate.tags['Environment'] == 'staging' + - certificate_with_tags.certificate.tags['Owner'] == 'Bob' + - certificate_with_tags.changed + vars: + name_tag: '{{ upload2.results[0].item.name }}' + - name: change tags of existing certificate, check mode again + aws_acm: + certificate_arn: '{{ certificate_arn }}' + tags: + Name: '{{ name_tag }}' + Application: search + Environment: staging + Owner: Bob + register: certificate_with_tags + check_mode: true + vars: + name_tag: '{{ upload2.results[0].item.name }}' + certificate_arn: '{{ upload2.results[0].certificate.arn }}' + - assert: + that: + - not certificate_with_tags.changed + - name: change tags of existing certificate, no change expected + aws_acm: + certificate_arn: '{{ certificate_arn }}' + tags: + Name: '{{ name_tag }}' + Application: search + Environment: staging + Owner: Bob + register: certificate_with_tags + vars: + name_tag: '{{ upload2.results[0].item.name }}' + certificate_arn: '{{ upload2.results[0].certificate.arn }}' + - name: assert certificate tags + assert: + that: + - certificate_with_tags.certificate.tags | length == 4 + - '''Name'' in certificate_with_tags.certificate.tags' + - '''Application'' in certificate_with_tags.certificate.tags' + - '''Environment'' in certificate_with_tags.certificate.tags' + - '''Owner'' in certificate_with_tags.certificate.tags' + - certificate_with_tags.certificate.tags['Name'] == name_tag + - certificate_with_tags.certificate.tags['Application'] == 'search' + - certificate_with_tags.certificate.tags['Environment'] == 'staging' + - certificate_with_tags.certificate.tags['Owner'] == 'Bob' + - not certificate_with_tags.changed + vars: + name_tag: '{{ upload2.results[0].item.name }}' + - name: check fetched data of cert we just uploaded + vars: + certificate_arn: '{{ upload2.results[0].certificate.arn }}' + domain_name: '{{ upload2.results[0].certificate.domain_name }}' + name_tag: '{{ upload2.results[0].item.name }}' + assert: + that: + - certificate_with_tags.certificate.arn == certificate_arn + - certificate_with_tags.certificate.tags | length == 4 + - '''Name'' in certificate_with_tags.certificate.tags' + - '''Application'' in certificate_with_tags.certificate.tags' + - '''Environment'' in certificate_with_tags.certificate.tags' + - '''Owner'' in certificate_with_tags.certificate.tags' + - certificate_with_tags.certificate.tags['Name'] == name_tag + - certificate_with_tags.certificate.tags['Application'] == 'search' + - certificate_with_tags.certificate.tags['Environment'] == 'staging' + - certificate_with_tags.certificate.tags['Owner'] == 'Bob' + - name: change tags of existing certificate, purge tags + aws_acm: + certificate_arn: '{{ certificate_arn }}' + tags: + Name: '{{ name_tag }}' + Application: search + Environment: staging + # 'Owner' tag should be removed because 'purge_tags: true' + purge_tags: true + register: certificate_with_tags + vars: + name_tag: '{{ upload2.results[0].item.name }}' + certificate_arn: '{{ upload2.results[0].certificate.arn }}' + domain_name: '{{ upload2.results[0].certificate.domain_name }}' + - name: check fetched data of cert we just uploaded + vars: + name_tag: '{{ upload2.results[0].item.name }}' + certificate_arn: '{{ upload2.results[0].certificate.arn }}' + domain_name: '{{ upload2.results[0].certificate.domain_name }}' + assert: + that: + - certificate_with_tags.certificate.arn == certificate_arn + - certificate_with_tags.certificate.tags | length == 3 + - '''Name'' in certificate_with_tags.certificate.tags' + - '''Application'' in certificate_with_tags.certificate.tags' + - '''Environment'' in certificate_with_tags.certificate.tags' + - certificate_with_tags.certificate.tags['Name'] == name_tag + - certificate_with_tags.certificate.tags['Application'] == 'search' + - certificate_with_tags.certificate.tags['Environment'] == 'staging' + - name: update first cert with body of the second, first time + aws_acm: + state: present + name_tag: '{{ local_certs[0].name }}' + certificate: '{{ lookup(''file'', local_certs[1].cert ) }}' + private_key: '{{ lookup(''file'', local_certs[1].priv_key ) }}' + register: overwrite + - name: check output of previous task (update first cert with body of the second, first time) + assert: + that: + - overwrite.certificate.arn is defined + - overwrite.certificate.arn | regex_search( 'arn:aws:acm:' ) is defined + - overwrite.certificate.arn == upload.results[0].certificate.arn + - overwrite.certificate.domain_name == local_certs[1].domain + - overwrite.changed + - name: check update was sucessfull + aws_acm_info: + tags: + Name: '{{ local_certs[0].name }}' + register: fetch_after_overwrite + - name: check output of update fetch + assert: + that: + - fetch_after_overwrite.certificates | length == 1 + - fetch_after_overwrite.certificates[0].certificate_arn == fetch_after_up.results[0].certificates[0].certificate_arn + - fetch_after_overwrite.certificates[0].domain_name == local_certs[1].domain + - (fetch_after_overwrite.certificates[0].certificate | replace( ' ', '' ) | replace( '\n', '')) == (lookup('file', local_certs[1].cert )| replace( ' ', '' ) | replace( '\n', '')) + - '''Name'' in fetch_after_overwrite.certificates[0].tags' + - fetch_after_overwrite.certificates[0].tags['Name'] == local_certs[0].name + - name: fetch other cert + aws_acm_info: + tags: + Name: '{{ local_certs[1].name }}' + register: check_after_overwrite + - name: check other cert unaffected + assert: + that: + - check_after_overwrite.certificates | length == 1 + - check_after_overwrite.certificates[0].certificate_arn == fetch_after_up.results[1].certificates[0].certificate_arn + - check_after_overwrite.certificates[0].domain_name == local_certs[1].domain + - (check_after_overwrite.certificates[0].certificate | replace( ' ', '' ) | replace( '\n', '')) == (lookup('file', local_certs[1].cert ) | replace( ' ', '' ) | replace( '\n', '')) + - '''Name'' in check_after_overwrite.certificates[0].tags' + - check_after_overwrite.certificates[0].tags['Name'] == local_certs[1].name + - name: update first cert with body of the second again + aws_acm: + state: present + name_tag: '{{ local_certs[0].name }}' + certificate: '{{ lookup(''file'', local_certs[1].cert ) }}' + private_key: '{{ lookup(''file'', local_certs[1].priv_key ) }}' + register: overwrite2 + - name: check output of previous task (update first cert with body of the second again) + assert: + that: + - overwrite2.certificate.arn is defined + - overwrite2.certificate.arn | regex_search( 'arn:aws:acm:' ) is defined + - overwrite2.certificate.arn == upload.results[0].certificate.arn + - overwrite2.certificate.domain_name == local_certs[1].domain + - not overwrite2.changed + - name: delete certs 1 and 2 + aws_acm: + state: absent + domain_name: '{{ local_certs[1].domain }}' + register: delete_both + - name: test prev task + assert: + that: + - delete_both.arns is defined + - check_after_overwrite.certificates[0].certificate_arn in delete_both.arns + - upload.results[0].certificate.arn in delete_both.arns + - delete_both.changed + - name: fetch info for certs 1 and 2 + aws_acm_info: + tags: + Name: '{{ local_certs[item].name }}' + register: check_del_one + with_items: + - 0 + - 1 + retries: 2 + until: + - check_del_one is not failed + - check_del_one.certificates | length == 0 + delay: 10 + - name: check certs 1 and 2 were already deleted + with_items: '{{ check_del_one.results }}' + assert: + that: item.certificates | length == 0 + - name: check cert 3 not deleted + aws_acm_info: + tags: + Name: '{{ local_certs[2].name }}' + register: check_del_one_remain + failed_when: check_del_one_remain.certificates | length != 1 + - name: delete cert 3 + aws_acm: + state: absent + domain_name: '{{ local_certs[2].domain }}' + register: delete_third + - name: check cert 3 deletion went as expected + assert: + that: + - delete_third.arns is defined + - delete_third.arns | length == 1 + - delete_third.arns[0] == upload.results[2].certificate.arn + - delete_third.changed + - name: check cert 3 was deleted + aws_acm_info: + tags: + Name: '{{ local_certs[2].name }}' + register: check_del_three + failed_when: check_del_three.certificates | length != 0 + - name: delete cert 3 again + aws_acm: + state: absent + domain_name: '{{ local_certs[2].domain }}' + register: delete_third + - name: check deletion of cert 3 not changed, because already deleted + assert: + that: + - delete_third.arns is defined + - delete_third.arns | length == 0 + - not delete_third.changed + - name: check directory was made + assert: + that: + - remote_tmp_dir is defined + - name: Generate private key for cert to be chained + community.crypto.openssl_privatekey: + path: '{{ chained_cert.priv_key }}' + type: RSA + size: 2048 + - name: Generate two OpenSSL Certificate Signing Requests for cert to be chained + with_items: '{{ chained_cert.chains }}' + community.crypto.openssl_csr: + path: '{{ item.csr }}' + privatekey_path: '{{ chained_cert.priv_key }}' + common_name: '{{ chained_cert.domain }}' + - name: Sign new certs with cert 0 and 1 + with_items: '{{ chained_cert.chains }}' + community.crypto.x509_certificate: + provider: ownca + path: '{{ item.cert }}' + csr_path: '{{ item.csr }}' + ownca_path: '{{ local_certs[item.ca].cert }}' + ownca_privatekey_path: '{{ local_certs[item.ca].priv_key }}' + selfsigned_digest: sha256 + - name: check files exist (for next task) + file: + path: '{{ item }}' + state: file + with_items: + - '{{ local_certs[chained_cert.chains[0].ca].cert }}' + - '{{ local_certs[chained_cert.chains[1].ca].cert }}' + - '{{ chained_cert.chains[0].cert }}' + - '{{ chained_cert.chains[1].cert }}' + - name: Find chains + with_items: '{{ chained_cert.chains }}' + register: chains + community.crypto.certificate_complete_chain: + input_chain: '{{ lookup(''file'', item.cert ) }}' + root_certificates: + - '{{ local_certs[item.ca].cert }}' + - name: upload chained cert, first chain, first time + aws_acm: + name_tag: '{{ chained_cert.name }}' + certificate: '{{ lookup(''file'', chained_cert.chains[0].cert ) }}' + certificate_chain: '{{ chains.results[0].complete_chain | join('' + + '') }}' + private_key: '{{ lookup(''file'', chained_cert.priv_key ) }}' + state: present + register: upload_chain + failed_when: not upload_chain.changed + - name: fetch chain of cert we just uploaded + aws_acm_info: + tags: + Name: '{{ chained_cert.name }}' + register: check_chain + until: check_chain.certificates|length > 0 + retries: 3 + - name: check chain of cert we just uploaded + assert: + that: + - (check_chain.certificates[0].certificate_chain | replace( ' ', '' ) | replace( '\n', '')) == ( chains.results[0].complete_chain | join( '\n' ) | replace( ' ', '' ) | replace( '\n', '') ) + - (check_chain.certificates[0].certificate | replace( ' ', '' ) | replace( '\n', '')) == ( lookup('file', chained_cert.chains[0].cert ) | replace( ' ', '' ) | replace( '\n', '') ) + - name: upload chained cert again, check not changed + aws_acm: + name_tag: '{{ chained_cert.name }}' + certificate: '{{ lookup(''file'', chained_cert.chains[0].cert ) }}' + certificate_chain: '{{ chains.results[0].complete_chain | join('' + + '') }}' + private_key: '{{ lookup(''file'', chained_cert.priv_key ) }}' + state: present + register: upload_chain_2 + - name: check previous task not changed + assert: + that: + - upload_chain_2.certificate.arn == upload_chain.certificate.arn + - not upload_chain_2.changed + - name: upload chained cert, different chain + aws_acm: + name_tag: '{{ chained_cert.name }}' + certificate: '{{ lookup(''file'', chained_cert.chains[1].cert ) }}' + certificate_chain: '{{ chains.results[1].complete_chain | join('' + + '') }}' + private_key: '{{ lookup(''file'', chained_cert.priv_key ) }}' + state: present + register: upload_chain_3 + - name: check uploading with different chain is changed + assert: + that: + - upload_chain_3.changed + - upload_chain_3.certificate.arn == upload_chain.certificate.arn + - name: fetch info about chain of cert we just updated + aws_acm_info: + tags: + Name: '{{ chained_cert.name }}' + register: check_chain_2 + until: check_chain_2.certificates|length > 0 + retries: 3 + - name: check chain of cert we just uploaded + assert: + that: + - (check_chain_2.certificates[0].certificate_chain | replace( ' ', '' ) | replace( '\n', '')) == ( chains.results[1].complete_chain | join( '\n' ) | replace( ' ', '' ) | replace( '\n', '') ) + - (check_chain_2.certificates[0].certificate | replace( ' ', '' ) | replace( '\n', '')) == ( lookup('file', chained_cert.chains[1].cert ) | replace( ' ', '' ) | replace( '\n', '') ) + - name: delete chained cert + aws_acm: + name_tag: '{{ chained_cert.name }}' + state: absent + register: delete_chain_3 + - name: check deletion of chained cert 3 is changed + assert: + that: + - delete_chain_3.changed + - upload_chain.certificate.arn in delete_chain_3.arns + always: + - name: delete first bunch of certificates + aws_acm: + name_tag: '{{ item.name }}' + state: absent + with_items: '{{ local_certs }}' + ignore_errors: true + - name: delete chained cert + aws_acm: + state: absent + name_tag: '{{ chained_cert.name }}' + ignore_errors: true + - name: deleting local directory with test artefacts + file: + path: '{{ remote_tmp_dir }}' + state: directory + ignore_errors: true diff --git a/ansible_collections/community/aws/tests/integration/targets/api_gateway/aliases b/ansible_collections/community/aws/tests/integration/targets/api_gateway/aliases new file mode 100644 index 000000000..4ef4b2067 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/api_gateway/aliases @@ -0,0 +1 @@ +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/api_gateway/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/api_gateway/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/api_gateway/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/api_gateway/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/api_gateway/tasks/main.yml new file mode 100644 index 000000000..51db07f0d --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/api_gateway/tasks/main.yml @@ -0,0 +1,166 @@ +- name: Wrap API Gateway tests with credentials by default + module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + + block: + + # ====================== testing failure cases: ================================== + + - name: test with no parameters + aws_api_gateway: + register: result + ignore_errors: true + + - name: assert failure when called with no parameters + assert: + that: + - 'result.failed' + - '"no swagger info provided" in result.msg' + + - name: test for disallowing multiple swagger sources + aws_api_gateway: + api_id: 'fake-api-doesnt-exist' + swagger_file: foo.yml + swagger_text: "this is not really an API" + register: result + ignore_errors: true + + - name: assert failure when called with with minimal parameters but no region + assert: + that: + - 'result.failed' + - 'result.msg.startswith("parameters are mutually exclusive")' + + + # ====================== regular testing: =================================== + + - name: build API file + template: + src: minimal-swagger-api.yml.j2 + dest: "{{output_dir}}/minimal-swagger-api.yml" + + - name: deploy new API + aws_api_gateway: + api_file: "{{output_dir}}/minimal-swagger-api.yml" + stage: "minimal" + endpoint_type: 'REGIONAL' + state: present + register: create_result + + - name: assert deploy new API worked + assert: + that: + - 'create_result.changed == True' + - 'create_result.failed == False' + - 'create_result.deploy_response.description == "Automatic deployment by Ansible."' + - 'create_result.configure_response.id == create_result.api_id' + - '"apigateway:CreateRestApi" in create_result.resource_actions' + - 'create_result.configure_response.endpoint_configuration.types.0 == "REGIONAL"' + + - name: check if API endpoint works + uri: url="https://{{create_result.api_id}}.execute-api.{{aws_region}}.amazonaws.com/minimal" + register: uri_result + + - name: assert API works success + assert: + that: + - 'uri_result.status == 200' + + - name: check if nonexistent endpoint causes error + uri: url="https://{{create_result.api_id}}.execute-api.{{aws_region}}.amazonaws.com/nominal" + register: bad_uri_result + ignore_errors: true + + - name: assert + assert: + that: + - bad_uri_result is failed + + - name: Update API to test params effect + aws_api_gateway: + api_id: '{{create_result.api_id}}' + api_file: "{{output_dir}}/minimal-swagger-api.yml" + cache_enabled: true + cache_size: '1.6' + tracing_enabled: true + state: present + register: update_result + + - name: assert update result + assert: + that: + - 'update_result.changed == True' + - 'update_result.failed == False' + - '"apigateway:PutRestApi" in update_result.resource_actions' + + # ==== additional create/delete tests ==== + + - name: deploy first API + aws_api_gateway: + api_file: "{{output_dir}}/minimal-swagger-api.yml" + stage: "minimal" + cache_enabled: false + state: present + register: create_result_1 + + - name: deploy second API rapidly after first + aws_api_gateway: + api_file: "{{output_dir}}/minimal-swagger-api.yml" + stage: "minimal" + state: present + register: create_result_2 + + - name: assert both APIs deployed successfully + assert: + that: + - 'create_result_1.changed == True' + - 'create_result_2.changed == True' + - '"api_id" in create_result_1' + - '"api_id" in create_result_1' + - 'create_result_1.configure_response.endpoint_configuration.types.0 == "EDGE"' + + - name: destroy first API + aws_api_gateway: + state: absent + api_id: '{{create_result_1.api_id}}' + register: destroy_result_1 + + - name: destroy second API rapidly after first + aws_api_gateway: + state: absent + api_id: '{{create_result_2.api_id}}' + register: destroy_result_2 + + - name: assert both APIs deployed successfully + assert: + that: + - 'destroy_result_1.changed == True' + - 'destroy_result_2.changed == True' + - '"apigateway:DeleteRestApi" in destroy_result_1.resource_actions' + - '"apigateway:DeleteRestApi" in destroy_result_2.resource_actions' + + # ================= end testing ==================================== + + always: + + - name: Ensure cleanup of API deploy + aws_api_gateway: + state: absent + api_id: '{{create_result.api_id}}' + ignore_errors: true + + - name: Ensure cleanup of API deploy 1 + aws_api_gateway: + state: absent + api_id: '{{create_result_1.api_id}}' + ignore_errors: true + + - name: Ensure cleanup of API deploy 2 + aws_api_gateway: + state: absent + api_id: '{{create_result_2.api_id}}' + ignore_errors: true diff --git a/ansible_collections/community/aws/tests/integration/targets/api_gateway/templates/minimal-swagger-api.yml.j2 b/ansible_collections/community/aws/tests/integration/targets/api_gateway/templates/minimal-swagger-api.yml.j2 new file mode 100644 index 000000000..8c5c05810 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/api_gateway/templates/minimal-swagger-api.yml.j2 @@ -0,0 +1,33 @@ +--- +swagger: "2.0" +info: + version: "2017-05-11T12:14:59Z" + title: "{{resource_prefix}}Empty_API" +host: "fakeexample.execute-api.us-east-1.amazonaws.com" +basePath: "/minimal" +schemes: +- "https" +paths: + /: + get: + consumes: + - "application/json" + produces: + - "application/json" + responses: + 200: + description: "200 response" + schema: + $ref: "#/definitions/Empty" + x-amazon-apigateway-integration: + responses: + default: + statusCode: "200" + requestTemplates: + application/json: "{\"statusCode\": 200}" + passthroughBehavior: "when_no_match" + type: "mock" +definitions: + Empty: + type: "object" + title: "Empty Schema" diff --git a/ansible_collections/community/aws/tests/integration/targets/api_gateway_domain/aliases b/ansible_collections/community/aws/tests/integration/targets/api_gateway_domain/aliases new file mode 100644 index 000000000..445fd3a03 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/api_gateway_domain/aliases @@ -0,0 +1,8 @@ +# reason: missing real ACM TLS cert ARN +disabled +# https://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-custom-domains.html +# To use an alternate domain name you need a *trusted* SSL cert *and* access to the DNS for a *real* +# domain. + +cloud/aws +aws_api_gateway_domain diff --git a/ansible_collections/community/aws/tests/integration/targets/api_gateway_domain/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/api_gateway_domain/defaults/main.yml new file mode 100644 index 000000000..937e44d5d --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/api_gateway_domain/defaults/main.yml @@ -0,0 +1,5 @@ +--- +# defaults file for aws_api_gateway_domain + +api_gateway_domain_name: "{{ resource_prefix }}-api-gw-test.foobar.com" +api_gateway_domain_tls_arn: "arn:aws:acm:{{ aws_region }}:1234567890:certificate/abc123-acb123-abc123-abc123" diff --git a/ansible_collections/community/aws/tests/integration/targets/api_gateway_domain/files/api_gw_swagger.yml b/ansible_collections/community/aws/tests/integration/targets/api_gateway_domain/files/api_gw_swagger.yml new file mode 100644 index 000000000..b58f3f3c0 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/api_gateway_domain/files/api_gw_swagger.yml @@ -0,0 +1,32 @@ +--- +swagger: "2.0" +info: + version: "2019-06-20T12:00:00Z" + title: "ansible-api-gw-test" +basePath: "/test" +schemes: +- "https" +paths: + /: + get: + consumes: + - "application/json" + produces: + - "application/json" + responses: + 200: + description: "200 response" + schema: + $ref: "#/definitions/Empty" + x-amazon-apigateway-integration: + responses: + default: + statusCode: "200" + passthroughBehavior: "when_no_match" + requestTemplates: + application/json: "{\"statusCode\": 200}" + type: "mock" +definitions: + Empty: + type: "object" + title: "Empty Schema" diff --git a/ansible_collections/community/aws/tests/integration/targets/api_gateway_domain/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/api_gateway_domain/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/api_gateway_domain/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/api_gateway_domain/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/api_gateway_domain/tasks/main.yml new file mode 100644 index 000000000..76de2657e --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/api_gateway_domain/tasks/main.yml @@ -0,0 +1,115 @@ +--- +# tasks file for aws_api_gateway_domain + +- name: Run aws_api_gateway_domain module integration tests + module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + + # NOTE: To make tests work set TLS ARN in defaults/main.yml to an existing and + # validated TLS cert on ACM that matches given domain. + + block: + + # ==================== preparations ======================================== + + - name: Preperations - Create REST API Gateway on AWS API Gateway service to reference from domain tests + aws_api_gateway: + swagger_file: files/api_gw_swagger.yml + stage: test + state: present + register: api_gateway_result + + # ================== integration tests ========================================== + + - name: Create Test - API gateway custom domain setup + aws_api_gateway_domain: + domain_name: "{{ api_gateway_domain_name }}" + certificate_arn: "{{ api_gateway_domain_tls_arn }}" + security_policy: 'TLS_1_0' + endpoint_type: EDGE + domain_mappings: + - { rest_api_id: "{{ api_gateway_result.api_id }}", stage: test } + state: present + register: create_result + + - assert: + that: + - create_result.changed == True + - create_result.response.domain.domain_name == "{{ api_gateway_domain_name }}" + - create_result.response.domain.distribution_domain_name is defined + - create_result.response.domain.distribution_hosted_zone_id is defined + - create_result.response.path_mappings is defined + + - name: Idempotence Test - API gateway custom domain setup + aws_api_gateway_domain: + domain_name: "{{ api_gateway_domain_name }}" + certificate_arn: "{{ api_gateway_domain_tls_arn }}" + security_policy: 'TLS_1_0' + endpoint_type: EDGE + domain_mappings: + - { rest_api_id: "{{ api_gateway_result.api_id }}", stage: test } + state: present + register: repeat_result + + - assert: + that: + - repeat_result.changed == False + - repeat_result.failed == False + - repeat_result.response.domain_name == "{{ api_gateway_domain_name }}" + + - name: Update Test - API gateway custom domain setup, change settings + aws_api_gateway_domain: + domain_name: "{{ api_gateway_domain_name }}" + certificate_arn: "{{ api_gateway_domain_tls_arn }}" + security_policy: 'TLS_1_2' + endpoint_type: REGIONAL + domain_mappings: + - { base_path: v1, rest_api_id: "{{ api_gateway_result.api_id }}", stage: test } + state: present + register: update_result + + - assert: + that: + - update_result.changed == True + - update_result.response.domain.domain_name == "{{ api_gateway_domain_name }}" + - update_result.response.domain.security_policy == 'TLS_1_2' + - update_result.response.domain.endpoint_configuration.types.0 == 'REGIONAL' + - update_result.response.path_mappings.0.base_path = '/v1' + + - name: Delete - API gateway custom domain setup deletion + aws_api_gateway_domain: + domain_name: "{{ api_gateway_domain_name }}" + certificate_arn: "{{ api_gateway_domain_tls_arn }}" + security_policy: 'TLS_1_2' + endpoint_type: REGIONAL + domain_mappings: + - { base_path: v1, rest_api_id: "{{ api_gateway_result.api_id }}", stage: test } + state: absent + register: delete_result + + - assert: + that: + - delete_result.changed == True + - delete_result.response_metadata.http_status_code == 202 + + # ==================== cleanup ======================= + + always: + + - name: Cleanup - delete test domain setup + aws_api_gateway_domain: + domain_name: "{{ api_gateway_domain_name }}" + certificate_arn: "{{ api_gateway_domain_tls_arn }}" + domain_mappings: [] + state: absent + ignore_errors: true + + - name: Cleanup - remove REST API Gateway on AWS API Gateway service + aws_api_gateway: + api_id: "{{ api_gateway_result.api_id }}" + swagger_file: files/api_gw_swagger.yml + state: absent diff --git a/ansible_collections/community/aws/tests/integration/targets/autoscaling_complete_lifecycle_action/aliases b/ansible_collections/community/aws/tests/integration/targets/autoscaling_complete_lifecycle_action/aliases new file mode 100644 index 000000000..443606b02 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/autoscaling_complete_lifecycle_action/aliases @@ -0,0 +1,2 @@ +cloud/aws +time=7m diff --git a/ansible_collections/community/aws/tests/integration/targets/autoscaling_complete_lifecycle_action/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/autoscaling_complete_lifecycle_action/defaults/main.yml new file mode 100644 index 000000000..e65619430 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/autoscaling_complete_lifecycle_action/defaults/main.yml @@ -0,0 +1,19 @@ +--- +# defaults file for ec2_asg_lifecycle_hook +resource_sufix: 'asg-hook-complete' +load_balancer_name: '{{ tiny_prefix }}-{{ resource_sufix }}' + +asg_name: '{{ tiny_prefix }}-{{ resource_sufix }}' + +vpc_name: '{{ tiny_prefix }}-{{ resource_sufix }}' +vpc_seed: '{{ resource_prefix }}' +vpc_cidr: '10.{{ 256 | random(seed=vpc_seed) }}.0.0/16' + +rtb_name: '{{ tiny_prefix }}-{{ resource_sufix }}' +sg_name: '{{ tiny_prefix }}-{{ resource_sufix }}' +lc_name: '{{ tiny_prefix }}-{{ resource_sufix }}' + +subnet_az: '{{ ec2_availability_zone_names[0] }}' +subnet_cidr: '10.{{ 256 | random(seed=vpc_seed) }}.157.0/24' +subnet_startswith: '10.{{ 256 | random(seed=vpc_seed) }}.157.' +subnet_name: '{{ tiny_prefix }}-{{ resource_sufix }}' diff --git a/ansible_collections/community/aws/tests/integration/targets/autoscaling_complete_lifecycle_action/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/autoscaling_complete_lifecycle_action/meta/main.yml new file mode 100644 index 000000000..1471b11f6 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/autoscaling_complete_lifecycle_action/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - setup_ec2_facts diff --git a/ansible_collections/community/aws/tests/integration/targets/autoscaling_complete_lifecycle_action/tasks/env_cleanup.yml b/ansible_collections/community/aws/tests/integration/targets/autoscaling_complete_lifecycle_action/tasks/env_cleanup.yml new file mode 100644 index 000000000..75d1ecfad --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/autoscaling_complete_lifecycle_action/tasks/env_cleanup.yml @@ -0,0 +1,73 @@ +- name: kill asg + ec2_asg: + name: "{{ asg_name }}" + state: absent + register: removed + until: removed is not failed + ignore_errors: true + retries: 10 + +- name: remove launch configs + ec2_lc: + name: "{{ lc_name }}" + state: absent + register: removed + until: removed is not failed + ignore_errors: true + retries: 10 + +- name: remove the security group + ec2_group: + name: "{{ sg_name }}" + description: a security group for ansible tests + vpc_id: "{{ testing_vpc.vpc.id }}" + state: absent + register: removed + until: removed is not failed + ignore_errors: true + retries: 10 + +- name: remove routing rules + ec2_vpc_route_table: + state: absent + vpc_id: "{{ testing_vpc.vpc.id }}" + tags: + Name: '{{ rtb_name }}' + routes: + - dest: 0.0.0.0/0 + gateway_id: "{{ igw.gateway_id }}" + subnets: + - "{{ testing_subnet.subnet.id }}" + register: removed + until: removed is not failed + ignore_errors: true + retries: 10 + +- name: remove internet gateway + ec2_vpc_igw: + vpc_id: "{{ testing_vpc.vpc.id }}" + state: absent + register: removed + until: removed is not failed + ignore_errors: true + retries: 10 + +- name: remove the subnet + ec2_vpc_subnet: + state: absent + vpc_id: "{{ testing_vpc.vpc.id }}" + cidr: "{{ subnet_cidr }}" + register: removed + until: removed is not failed + ignore_errors: true + retries: 10 + +- name: remove the VPC + ec2_vpc_net: + name: "{{ vpc_name }}" + cidr_block: "{{ vpc_cidr }}" + state: absent + register: removed + until: removed is not failed + ignore_errors: true + retries: 10 diff --git a/ansible_collections/community/aws/tests/integration/targets/autoscaling_complete_lifecycle_action/tasks/env_setup.yml b/ansible_collections/community/aws/tests/integration/targets/autoscaling_complete_lifecycle_action/tasks/env_setup.yml new file mode 100644 index 000000000..ae958cd89 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/autoscaling_complete_lifecycle_action/tasks/env_setup.yml @@ -0,0 +1,86 @@ +- name: Run ec2_asg_lifecycle_hook integration tests. + + block: + # Set up the testing dependencies: VPC, subnet, security group, and two launch configurations + - name: Create VPC for use in testing + ec2_vpc_net: + name: "{{ vpc_name }}" + cidr_block: "{{ vpc_cidr }}" + tenancy: default + register: testing_vpc + + - name: Create internet gateway for use in testing + ec2_vpc_igw: + vpc_id: "{{ testing_vpc.vpc.id }}" + state: present + register: igw + + - name: Create subnet for use in testing + ec2_vpc_subnet: + state: present + vpc_id: "{{ testing_vpc.vpc.id }}" + cidr: "{{ subnet_cidr }}" + az: "{{ subnet_az }}" + resource_tags: + Name: "{{ subnet_name }}" + register: testing_subnet + + - name: create routing rules + ec2_vpc_route_table: + vpc_id: "{{ testing_vpc.vpc.id }}" + tags: + Name: "{{ rtb_name }}" + routes: + - dest: 0.0.0.0/0 + gateway_id: "{{ igw.gateway_id }}" + subnets: + - "{{ testing_subnet.subnet.id }}" + + - name: create a security group with the vpc created in the ec2_setup + ec2_group: + name: "{{ sg_name }}" + description: a security group for ansible tests + vpc_id: "{{ testing_vpc.vpc.id }}" + rules: + - proto: tcp + from_port: 22 + to_port: 22 + cidr_ip: 0.0.0.0/0 + - proto: tcp + from_port: 80 + to_port: 80 + cidr_ip: 0.0.0.0/0 + register: sg + + - name: create a launch configuration + ec2_lc: + name: "{{ lc_name }}" + image_id: "{{ ec2_ami_id }}" + instance_type: t2.micro + assign_public_ip: yes + register: create_lc + + - name: ensure that lc is created + assert: + that: + - create_lc is changed + - create_lc.failed is false + + - name: create a AutoScalingGroup + ec2_asg: + name: "{{ asg_name }}" + launch_config_name: "{{ lc_name }}" + health_check_period: 60 + health_check_type: ELB + replace_all_instances: yes + min_size: 0 + max_size: 0 + desired_capacity: 0 + register: create_asg + + - name: ensure that AutoScalingGroup is created + assert: + that: + - create_asg is changed + - create_asg.failed is false + - '"autoscaling:CreateAutoScalingGroup" in create_asg.resource_actions' diff --git a/ansible_collections/community/aws/tests/integration/targets/autoscaling_complete_lifecycle_action/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/autoscaling_complete_lifecycle_action/tasks/main.yml new file mode 100644 index 000000000..d8380d913 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/autoscaling_complete_lifecycle_action/tasks/main.yml @@ -0,0 +1,16 @@ +--- +- name: "Wrap up all tests and setup AWS credentials" + module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + collections: + - community.aws + block: + - include_tasks: 'env_setup.yml' + - include_tasks: 'tests.yml' + - include_tasks: 'env_cleanup.yml' + always: + - include_tasks: 'env_cleanup.yml' diff --git a/ansible_collections/community/aws/tests/integration/targets/autoscaling_complete_lifecycle_action/tasks/tests.yml b/ansible_collections/community/aws/tests/integration/targets/autoscaling_complete_lifecycle_action/tasks/tests.yml new file mode 100644 index 000000000..7d326c6ff --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/autoscaling_complete_lifecycle_action/tasks/tests.yml @@ -0,0 +1,120 @@ +--- +- name: Test Lifecycle hooks + block: + #---------------------------------------------------------------------- + - name: Create lifecycle hook + ec2_asg_lifecycle_hook: + autoscaling_group_name: "{{ asg_name }}" + lifecycle_hook_name: "{{ resource_prefix }}-lifecycle-hook" + transition: autoscaling:EC2_INSTANCE_LAUNCHING + heartbeat_timeout: 7000 + default_result: ABANDON + state: present + register: output + + - assert: + that: + - output is changed + - output is not failed + + - name: Create lifecycle hook + ec2_asg_lifecycle_hook: + autoscaling_group_name: "{{ asg_name }}" + lifecycle_hook_name: "{{ resource_prefix }}-lifecycle-hook-terminate" + transition: autoscaling:EC2_INSTANCE_TERMINATING + heartbeat_timeout: 90 + default_result: ABANDON + state: present + register: output + + - assert: + that: + - output is changed + - output is not failed + + - name: Trigger scale-up + ec2_asg: + name: "{{ asg_name }}" + replace_all_instances: yes + min_size: 0 + max_size: 3 + desired_capacity: 2 + wait_for_instances: no + register: scale_asg + + - assert: + that: + - scale_asg is changed + + - name: Describe ASG + ec2_asg_info: + name: "{{ asg_name }}" + register: scaled_asg + retries: 24 + until: + - scaled_asg.results[0].instances | length == 2 + - scaled_asg.results[0].instances[0].lifecycle_state == "Pending:Wait" + - scaled_asg.results[0].instances[1].lifecycle_state == "Pending:Wait" + delay: 5 + ignore_errors: true + + - set_fact: + instance_ids: '{{ scaled_asg.results[0].instances | map(attribute="instance_id") | list }}' + + - name: Describe ASG + ec2_asg_info: + name: "{{ asg_name }}" + + - name: Complete Lifecycle Hook + autoscaling_complete_lifecycle_action: + asg_name: '{{ asg_name }}' + lifecycle_hook_name: '{{ resource_prefix }}-lifecycle-hook' + lifecycle_action_result: 'CONTINUE' + instance_id: '{{ instance_ids[0] }}' + + - name: Abandon Lifecycle Hook + autoscaling_complete_lifecycle_action: + asg_name: '{{ asg_name }}' + lifecycle_hook_name: '{{ resource_prefix }}-lifecycle-hook' + lifecycle_action_result: 'ABANDON' + instance_id: '{{ instance_ids[1] }}' + + - name: Describe ASG + ec2_asg_info: + name: "{{ asg_name }}" + register: hooks_pending + retries: 24 + delay: 5 + ignore_errors: true + until: + - instance_a.lifecycle_state == 'InService' + - instance_b.lifecycle_state == 'Terminating:Wait' + vars: + instance_a: '{{ hooks_pending.results[0].instances | selectattr("instance_id", "==", instance_ids[0]) | first }}' + instance_b: '{{ hooks_pending.results[0].instances | selectattr("instance_id", "==", instance_ids[1]) | first }}' + + - name: 'Assert that instances have entered the expected instance states' + assert: + that: + - instance_a.lifecycle_state == 'InService' + - instance_b.lifecycle_state == 'Terminating:Wait' + vars: + instance_a: '{{ hooks_pending.results[0].instances | selectattr("instance_id", "==", instance_ids[0]) | first }}' + instance_b: '{{ hooks_pending.results[0].instances | selectattr("instance_id", "==", instance_ids[1]) | first }}' + + always: + - name: Delete lifecycle hook + community.aws.ec2_asg_lifecycle_hook: + autoscaling_group_name: "{{ asg_name }}" + lifecycle_hook_name: "{{ resource_prefix }}-lifecycle-hook" + state: absent + register: output + ignore_errors: True + + - name: Delete lifecycle hook + community.aws.ec2_asg_lifecycle_hook: + autoscaling_group_name: "{{ asg_name }}" + lifecycle_hook_name: "{{ resource_prefix }}-lifecycle-hook-terminate" + state: absent + register: output + ignore_errors: True diff --git a/ansible_collections/community/aws/tests/integration/targets/autoscaling_instance_refresh/aliases b/ansible_collections/community/aws/tests/integration/targets/autoscaling_instance_refresh/aliases new file mode 100644 index 000000000..6ce549da4 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/autoscaling_instance_refresh/aliases @@ -0,0 +1,3 @@ +time=14m +cloud/aws +autoscaling_instance_refresh_info diff --git a/ansible_collections/community/aws/tests/integration/targets/autoscaling_instance_refresh/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/autoscaling_instance_refresh/defaults/main.yml new file mode 100644 index 000000000..08e57d255 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/autoscaling_instance_refresh/defaults/main.yml @@ -0,0 +1,16 @@ +--- +# defaults file for ec2_asg +vpc_seed: '{{ tiny_prefix }}' +subnet_a_cidr: '10.{{ 256 | random(seed=vpc_seed) }}.32.0/24' + +default_resource_name: '{{ resource_prefix }}-asg-refresh' +short_resource_name: '{{ tiny_prefix }}-asg-refresh' + +vpc_name: '{{ default_resource_name }}' +subnet_name: '{{ default_resource_name }}' +route_name: '{{ default_resource_name }}' +sg_name: '{{ default_resource_name }}' +asg_name: '{{ default_resource_name }}' +lc_name_1: '{{ default_resource_name }}-1' +lc_name_2: '{{ default_resource_name }}-2' +load_balancer_name: '{{ short_resource_name }}' diff --git a/ansible_collections/community/aws/tests/integration/targets/autoscaling_instance_refresh/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/autoscaling_instance_refresh/meta/main.yml new file mode 100644 index 000000000..1471b11f6 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/autoscaling_instance_refresh/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - setup_ec2_facts diff --git a/ansible_collections/community/aws/tests/integration/targets/autoscaling_instance_refresh/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/autoscaling_instance_refresh/tasks/main.yml new file mode 100644 index 000000000..32cfd5378 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/autoscaling_instance_refresh/tasks/main.yml @@ -0,0 +1,517 @@ +--- +- name: setup credentials and region + module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + + collections: + - amazon.aws + + block: + # Set up the testing dependencies: VPC, subnet, security group, and two launch configurations + - name: Create VPC for use in testing + ec2_vpc_net: + name: "{{ vpc_name }}" + cidr_block: '{{ subnet_a_cidr }}' + tenancy: default + register: testing_vpc + + - name: Create internet gateway for use in testing + ec2_vpc_igw: + vpc_id: "{{ testing_vpc.vpc.id }}" + state: present + register: igw + + - name: Create subnet for use in testing + ec2_vpc_subnet: + state: present + vpc_id: "{{ testing_vpc.vpc.id }}" + cidr: '{{ subnet_a_cidr }}' + az: "{{ aws_region }}a" + resource_tags: + Name: "{{ subnet_name }}" + register: testing_subnet + + - name: create routing rules + ec2_vpc_route_table: + vpc_id: "{{ testing_vpc.vpc.id }}" + tags: + created: "{{ route_name }}" + routes: + - dest: 0.0.0.0/0 + gateway_id: "{{ igw.gateway_id }}" + subnets: + - "{{ testing_subnet.subnet.id }}" + + - name: create a security group with the vpc created in the ec2_setup + ec2_group: + name: "{{ sg_name }}" + description: a security group for ansible tests + vpc_id: "{{ testing_vpc.vpc.id }}" + rules: + - proto: tcp + from_port: 22 + to_port: 22 + cidr_ip: 0.0.0.0/0 + - proto: tcp + from_port: 80 + to_port: 80 + cidr_ip: 0.0.0.0/0 + register: sg + + - name: ensure launch configs exist + ec2_lc: + name: "{{ item }}" + assign_public_ip: true + image_id: "{{ ec2_ami_id }}" + user_data: | + package_upgrade: true + package_update: true + packages: + - httpd + runcmd: + - "service httpd start" + security_groups: "{{ sg.group_id }}" + instance_type: t3.micro + loop: + - "{{ lc_name_1 }}" + - "{{ lc_name_2 }}" + + - name: launch asg and do not wait for instances to be deemed healthy (no ELB) + ec2_asg: + name: "{{ asg_name }}" + launch_config_name: "{{ lc_name_1 }}" + desired_capacity: 1 + min_size: 1 + max_size: 1 + vpc_zone_identifier: "{{ testing_subnet.subnet.id }}" + wait_for_instances: no + state: present + register: output + + - assert: + that: + - "output.viable_instances == 0" + + # ============================================================ + + - name: test invalid cancelation - V1 - (pre-refresh) + ec2_asg_instance_refresh: + name: "{{ asg_name }}" + state: "cancelled" + ignore_errors: yes + register: result + + - assert: + that: + - "'An error occurred (ActiveInstanceRefreshNotFound) when calling the CancelInstanceRefresh operation: No in progress or pending Instance Refresh found for Auto Scaling group {{ resource_prefix }}-asg' in result.msg" + + - name: test starting a refresh with a valid ASG name - check_mode + ec2_asg_instance_refresh: + name: "{{ asg_name }}" + state: "started" + check_mode: true + register: output + + - assert: + that: + - output is not failed + - output is changed + - '"autoscaling:StartInstanceRefresh" not in output.resource_actions' + + - name: test starting a refresh with a valid ASG name + ec2_asg_instance_refresh: + name: "{{ asg_name }}" + state: "started" + register: output + + - assert: + that: + - "'instance_refresh_id' in output.instance_refreshes" + + - name: test starting a refresh with a valid ASG name - Idempotent + ec2_asg_instance_refresh: + name: "{{ asg_name }}" + state: "started" + ignore_errors: true + register: output + + - assert: + that: + - output is not changed + - '"Failed to start InstanceRefresh: An error occurred (InstanceRefreshInProgress) when calling the StartInstanceRefresh operation: An Instance Refresh is already in progress and blocks the execution of this Instance Refresh." in output.msg' + + - name: test starting a refresh with a valid ASG name - Idempotent (check_mode) + ec2_asg_instance_refresh: + name: "{{ asg_name }}" + state: "started" + ignore_errors: true + check_mode: true + register: output + + - assert: + that: + - output is not changed + - output is not failed + - '"In check_mode - Instance Refresh is already in progress, can not start new instance refresh." in output.msg' + + - name: test starting a refresh with a nonexistent ASG name + ec2_asg_instance_refresh: + name: "nonexistentname-asg" + state: "started" + ignore_errors: yes + register: result + + - assert: + that: + - "'Failed to start InstanceRefresh: An error occurred (ValidationError) when calling the StartInstanceRefresh operation: AutoScalingGroup name not found' in result.msg" + + - name: test canceling a refresh with an ASG name - check_mode + ec2_asg_instance_refresh: + name: "{{ asg_name }}" + state: "cancelled" + check_mode: true + register: output + + - assert: + that: + - output is not failed + - output is changed + - '"autoscaling:CancelInstanceRefresh" not in output.resource_actions' + + - name: test canceling a refresh with an ASG name + ec2_asg_instance_refresh: + name: "{{ asg_name }}" + state: "cancelled" + register: output + + - assert: + that: + - "'instance_refresh_id' in output.instance_refreshes" + + - name: test canceling a refresh with a ASG name - Idempotent + ec2_asg_instance_refresh: + name: "{{ asg_name }}" + state: "cancelled" + ignore_errors: yes + register: output + + - assert: + that: + - output is not changed + + - name: test cancelling a refresh with a valid ASG name - Idempotent (check_mode) + ec2_asg_instance_refresh: + name: "{{ asg_name }}" + state: "cancelled" + ignore_errors: true + check_mode: true + register: output + + - assert: + that: + - output is not changed + - output is not failed + + - name: test starting a refresh with an ASG name and preferences dict + ec2_asg_instance_refresh: + name: "{{ asg_name }}" + state: "started" + preferences: + min_healthy_percentage: 10 + instance_warmup: 10 + retries: 5 + register: output + until: output is not failed + + - assert: + that: + - "'instance_refresh_id' in output.instance_refreshes" + + - name: re-test canceling a refresh with an ASG name + ec2_asg_instance_refresh: + name: "{{ asg_name }}" + state: "cancelled" + register: output + + - assert: + that: + - "'instance_refresh_id' in output.instance_refreshes" + + - name: test valid start - V1 - (with preferences missing instance_warmup) + ec2_asg_instance_refresh: + name: "{{ asg_name }}" + state: "started" + preferences: + min_healthy_percentage: 10 + ignore_errors: yes + retries: 5 + register: output + until: output is not failed + + - assert: + that: + - "'instance_refresh_id' in output.instance_refreshes" + + - name: re-test canceling a refresh with an ASG name + ec2_asg_instance_refresh: + name: "{{ asg_name }}" + state: "cancelled" + register: output + + - assert: + that: + - "'instance_refresh_id' in output.instance_refreshes" + + - name: test valid start - V2 - (with preferences missing min_healthy_percentage) + ec2_asg_instance_refresh: + name: "{{ asg_name }}" + state: "started" + preferences: + instance_warmup: 10 + retries: 5 + register: output + until: output is not failed + ignore_errors: yes + + - assert: + that: + - "'instance_refresh_id' in output.instance_refreshes" + + - name: test invalid cancelation - V2 - (with preferences) + ec2_asg_instance_refresh: + name: "{{ asg_name }}" + state: "cancelled" + preferences: + min_healthy_percentage: 10 + instance_warmup: 10 + ignore_errors: yes + register: result + + - assert: + that: + - "'can not pass preferences dict when canceling a refresh' in result.msg" + + # ============================================================ + + - name: run setup with refresh_and_cancel_three_times.yml + include_tasks: refresh_and_cancel_three_times.yml + loop: "{{ query('sequence', 'start=1 end=3') }}" + + - name: test getting info for an ASG name + ec2_asg_instance_refresh_info: + name: "{{ asg_name }}" + region: "{{ aws_region }}" + ignore_errors: yes + register: output + + - assert: + that: + - output | community.general.json_query(inst_refresh_id_json_query) | unique | length == 7 + vars: + inst_refresh_id_json_query: instance_refreshes[].instance_refresh_id + + - name: test using fake refresh ID + ec2_asg_instance_refresh_info: + name: "{{ asg_name }}" + ids: ['0e367f58-blabla-bla-bla-ca870dc5dbfe'] + ignore_errors: yes + register: output + + - assert: + that: + - "{{ output.instance_refreshes|length }} == 0" + + - name: test using a real refresh ID + ec2_asg_instance_refresh_info: + name: "{{ asg_name }}" + ids: [ '{{ refreshout.instance_refreshes.instance_refresh_id }}' ] + ignore_errors: yes + register: output + + - assert: + that: + - "{{ output.instance_refreshes |length }} == 1" + + - name: test getting info for an ASG name which doesn't exist + ec2_asg_instance_refresh_info: + name: n0n3x1stentname27b + ignore_errors: yes + register: output + + - assert: + that: + - "'Failed to describe InstanceRefreshes: An error occurred (ValidationError) when calling the DescribeInstanceRefreshes operation: AutoScalingGroup name not found - AutoScalingGroup n0n3x1stentname27b not found' == output.msg" + + - name: assert that the correct number of records are returned + ec2_asg_instance_refresh_info: + name: "{{ asg_name }}" + ignore_errors: yes + register: output + + - assert: + that: + - "{{ output.instance_refreshes|length }} == 7" + + - name: assert that valid message with fake-token is returned + ec2_asg_instance_refresh_info: + name: "{{ asg_name }}" + next_token: "fake-token-123" + ignore_errors: yes + register: output + + - assert: + that: + - '"Failed to describe InstanceRefreshes: An error occurred (InvalidNextToken) when calling the DescribeInstanceRefreshes operation: The token ''********'' is invalid." == output.msg' + + - name: assert that max records=1 returns no more than one record + ec2_asg_instance_refresh_info: + name: "{{ asg_name }}" + max_records: 1 + ignore_errors: yes + register: output + + - assert: + that: + - "{{ output.instance_refreshes|length }} < 2" + + - name: assert that valid message with real-token is returned + ec2_asg_instance_refresh_info: + name: "{{ asg_name }}" + next_token: "{{ output.next_token }}" + ignore_errors: yes + register: output + + - assert: + that: + - "{{ output.instance_refreshes|length }} == 7" + + - name: test using both real nextToken and max_records=1 + ec2_asg_instance_refresh_info: + name: "{{ asg_name }}" + max_records: 1 + next_token: "{{ output.next_token }}" + ignore_errors: yes + register: output + + - assert: + that: + - "{{ output.instance_refreshes|length }} == 1" + + always: + + - name: kill asg + ec2_asg: + name: "{{ asg_name }}" + state: absent + register: removed + until: removed is not failed + ignore_errors: yes + retries: 10 + # Remove the testing dependencies + + - name: remove the load balancer + ec2_elb_lb: + name: "{{ load_balancer_name }}" + state: absent + security_group_ids: + - "{{ sg.group_id }}" + subnets: "{{ testing_subnet.subnet.id }}" + wait: yes + connection_draining_timeout: 60 + listeners: + - protocol: http + load_balancer_port: 80 + instance_port: 80 + health_check: + ping_protocol: tcp + ping_port: 80 + ping_path: "/" + response_timeout: 5 + interval: 10 + unhealthy_threshold: 4 + healthy_threshold: 2 + register: removed + until: removed is not failed + ignore_errors: yes + retries: 10 + + - name: remove launch configs + ec2_lc: + name: "{{ item }}" + state: absent + register: removed + until: removed is not failed + ignore_errors: yes + retries: 10 + loop: + - "{{ lc_name_1 }}" + - "{{ lc_name_2 }}" + + - name: delete launch template + ec2_launch_template: + name: "{{ resource_prefix }}-lt" + state: absent + register: del_lt + retries: 10 + until: del_lt is not failed + ignore_errors: true + + - name: remove the security group + ec2_group: + name: "{{ sg_name }}" + description: a security group for ansible tests + vpc_id: "{{ testing_vpc.vpc.id }}" + state: absent + register: removed + until: removed is not failed + ignore_errors: yes + retries: 10 + + - name: remove routing rules + ec2_vpc_route_table: + state: absent + vpc_id: "{{ testing_vpc.vpc.id }}" + tags: + created: "{{ route_name }}" + routes: + - dest: 0.0.0.0/0 + gateway_id: "{{ igw.gateway_id }}" + subnets: + - "{{ testing_subnet.subnet.id }}" + register: removed + until: removed is not failed + ignore_errors: yes + retries: 10 + + - name: remove internet gateway + ec2_vpc_igw: + vpc_id: "{{ testing_vpc.vpc.id }}" + state: absent + register: removed + until: removed is not failed + ignore_errors: yes + retries: 10 + + - name: remove the subnet + ec2_vpc_subnet: + state: absent + vpc_id: "{{ testing_vpc.vpc.id }}" + cidr: '{{ subnet_a_cidr }}' + register: removed + until: removed is not failed + ignore_errors: yes + retries: 10 + + - name: remove the VPC + ec2_vpc_net: + name: "{{ vpc_name }}" + cidr_block: '{{ subnet_a_cidr }}' + state: absent + register: removed + until: removed is not failed + ignore_errors: yes + retries: 10 diff --git a/ansible_collections/community/aws/tests/integration/targets/autoscaling_instance_refresh/tasks/refresh_and_cancel_three_times.yml b/ansible_collections/community/aws/tests/integration/targets/autoscaling_instance_refresh/tasks/refresh_and_cancel_three_times.yml new file mode 100644 index 000000000..15fa2100c --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/autoscaling_instance_refresh/tasks/refresh_and_cancel_three_times.yml @@ -0,0 +1,29 @@ +--- + +- name: try to cancel pre-loop + ec2_asg_instance_refresh: + name: "{{ asg_name }}" + state: "cancelled" + ignore_errors: yes + +- name: test starting a refresh with an ASG name + ec2_asg_instance_refresh: + name: "{{ asg_name }}" + state: "started" + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + region: "{{ aws_region }}" + ignore_errors: no + retries: 10 + delay: 5 + register: refreshout + until: refreshout is not failed + +- name: test cancelling a refresh with an ASG name + ec2_asg_instance_refresh: + name: "{{ asg_name }}" + state: "cancelled" + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + region: "{{ aws_region }}" + ignore_errors: yes diff --git a/ansible_collections/community/aws/tests/integration/targets/autoscaling_instance_refresh/vars/main.yml b/ansible_collections/community/aws/tests/integration/targets/autoscaling_instance_refresh/vars/main.yml new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/autoscaling_instance_refresh/vars/main.yml diff --git a/ansible_collections/community/aws/tests/integration/targets/autoscaling_launch_config/aliases b/ansible_collections/community/aws/tests/integration/targets/autoscaling_launch_config/aliases new file mode 100644 index 000000000..77fdadbae --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/autoscaling_launch_config/aliases @@ -0,0 +1,4 @@ +cloud/aws + +autoscaling_launch_config_info +autoscaling_launch_config_find diff --git a/ansible_collections/community/aws/tests/integration/targets/autoscaling_launch_config/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/autoscaling_launch_config/defaults/main.yml new file mode 100644 index 000000000..db5c2b6f5 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/autoscaling_launch_config/defaults/main.yml @@ -0,0 +1,6 @@ +--- +# defaults file for ec2_instance +ec2_instance_name: '{{ resource_prefix }}-node' +ec2_instance_owner: 'integration-run-{{ resource_prefix }}' +ec2_instance_type: t2.micro +alarm_prefix: "ansible-test" diff --git a/ansible_collections/community/aws/tests/integration/targets/autoscaling_launch_config/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/autoscaling_launch_config/meta/main.yml new file mode 100644 index 000000000..1471b11f6 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/autoscaling_launch_config/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - setup_ec2_facts diff --git a/ansible_collections/community/aws/tests/integration/targets/autoscaling_launch_config/tasks/env_cleanup.yml b/ansible_collections/community/aws/tests/integration/targets/autoscaling_launch_config/tasks/env_cleanup.yml new file mode 100644 index 000000000..9e5ae6a93 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/autoscaling_launch_config/tasks/env_cleanup.yml @@ -0,0 +1,94 @@ +- name: remove any instances in the test VPC + ec2_instance: + filters: + vpc_id: "{{ testing_vpc.vpc.id }}" + state: absent + register: removed + until: removed is not failed + ignore_errors: yes + retries: 10 + +- name: Get ENIs + ec2_eni_info: + filters: + vpc-id: "{{ testing_vpc.vpc.id }}" + register: enis + +- name: delete all ENIs + ec2_eni: + eni_id: "{{ item.id }}" + state: absent + until: removed is not failed + with_items: "{{ enis.network_interfaces }}" + ignore_errors: yes + retries: 10 + +- name: remove the security group + ec2_group: + name: "{{ resource_prefix }}-sg" + description: a security group for ansible tests + vpc_id: "{{ testing_vpc.vpc.id }}" + state: absent + register: removed + until: removed is not failed + ignore_errors: yes + retries: 10 + +- name: remove routing rules + ec2_vpc_route_table: + state: absent + vpc_id: "{{ testing_vpc.vpc.id }}" + tags: + created: "{{ resource_prefix }}-route" + routes: + - dest: 0.0.0.0/0 + gateway_id: "{{ igw.gateway_id }}" + subnets: + - "{{ testing_subnet_a.subnet.id }}" + - "{{ testing_subnet_b.subnet.id }}" + register: removed + until: removed is not failed + ignore_errors: yes + retries: 10 + +- name: remove internet gateway + ec2_vpc_igw: + vpc_id: "{{ testing_vpc.vpc.id }}" + state: absent + register: removed + until: removed is not failed + ignore_errors: yes + retries: 10 + +- name: remove subnet A + ec2_vpc_subnet: + state: absent + vpc_id: "{{ testing_vpc.vpc.id }}" + cidr: 10.22.32.0/24 + register: removed + until: removed is not failed + ignore_errors: yes + retries: 10 + +- name: remove subnet B + ec2_vpc_subnet: + state: absent + vpc_id: "{{ testing_vpc.vpc.id }}" + cidr: 10.22.33.0/24 + register: removed + until: removed is not failed + ignore_errors: yes + retries: 10 + +- name: remove the VPC + ec2_vpc_net: + name: "{{ resource_prefix }}-vpc" + cidr_block: 10.22.32.0/23 + state: absent + tags: + Name: Ansible Testing VPC + tenancy: default + register: removed + until: removed is not failed + ignore_errors: yes + retries: 10 diff --git a/ansible_collections/community/aws/tests/integration/targets/autoscaling_launch_config/tasks/env_setup.yml b/ansible_collections/community/aws/tests/integration/targets/autoscaling_launch_config/tasks/env_setup.yml new file mode 100644 index 000000000..88f5bb6fe --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/autoscaling_launch_config/tasks/env_setup.yml @@ -0,0 +1,64 @@ +- name: Create VPC for use in testing + ec2_vpc_net: + name: "{{ resource_prefix }}-vpc" + cidr_block: 10.22.32.0/23 + tags: + Name: Ansible ec2_lc Testing VPC + tenancy: default + register: testing_vpc + +- name: Create internet gateway for use in testing + ec2_vpc_igw: + vpc_id: "{{ testing_vpc.vpc.id }}" + state: present + tags: + Name: Ansible ec2_lc Testing gateway + register: igw + +- name: Create default subnet in zone A + ec2_vpc_subnet: + state: present + vpc_id: "{{ testing_vpc.vpc.id }}" + cidr: 10.22.32.0/24 + az: "{{ aws_region }}a" + resource_tags: + Name: "{{ resource_prefix }}-subnet-a" + register: testing_subnet_a + +- name: Create secondary subnet in zone B + ec2_vpc_subnet: + state: present + vpc_id: "{{ testing_vpc.vpc.id }}" + cidr: 10.22.33.0/24 + az: "{{ aws_region }}b" + resource_tags: + Name: "{{ resource_prefix }}-subnet-b" + register: testing_subnet_b + +- name: create routing rules + ec2_vpc_route_table: + vpc_id: "{{ testing_vpc.vpc.id }}" + tags: + created: "{{ resource_prefix }}-route" + routes: + - dest: 0.0.0.0/0 + gateway_id: "{{ igw.gateway_id }}" + subnets: + - "{{ testing_subnet_a.subnet.id }}" + - "{{ testing_subnet_b.subnet.id }}" + +- name: create a security group with the vpc + ec2_group: + name: "{{ resource_prefix }}-sg" + description: a security group for ansible tests + vpc_id: "{{ testing_vpc.vpc.id }}" + rules: + - proto: tcp + from_port: 22 + to_port: 22 + cidr_ip: 0.0.0.0/0 + - proto: tcp + from_port: 80 + to_port: 80 + cidr_ip: 0.0.0.0/0 + register: sg diff --git a/ansible_collections/community/aws/tests/integration/targets/autoscaling_launch_config/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/autoscaling_launch_config/tasks/main.yml new file mode 100644 index 000000000..6606484b1 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/autoscaling_launch_config/tasks/main.yml @@ -0,0 +1,266 @@ +- name: run ec2_lc tests + module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + collections: + - amazon.aws + + block: + + - name: set up environment for testing. + include_tasks: env_setup.yml + + - name: Create launch configuration 1 + community.aws.ec2_lc: + name: '{{ resource_prefix }}-lc1' + image_id: '{{ ec2_ami_id }}' + assign_public_ip: yes + instance_type: '{{ ec2_instance_type }}' + security_groups: '{{ sg.group_id }}' + volumes: + - device_name: /dev/xvda + volume_size: 10 + volume_type: gp2 + delete_on_termination: true + register: lc_1_create + + - name: Gather information about launch configuration 1 + community.aws.ec2_lc_info: + name: '{{ resource_prefix }}-lc1' + register: lc_1_info_result + + - assert: + that: + - lc_1_create is changed + - '"autoscaling:CreateLaunchConfiguration" in lc_1_create.resource_actions' + - '"throughput" not in lc_1_info_result.launch_configurations[0].block_device_mappings[0].ebs' + - lc_1_info_result.launch_configurations[0].block_device_mappings[0].ebs.volume_size == 10 + - lc_1_info_result.launch_configurations[0].block_device_mappings[0].ebs.volume_type == 'gp2' + - lc_1_info_result.launch_configurations[0].instance_type == 't2.micro' + + - name: Create launch configuration 1 - Idempotency + community.aws.ec2_lc: + name: '{{ resource_prefix }}-lc1' + image_id: '{{ ec2_ami_id }}' + assign_public_ip: yes + instance_type: '{{ ec2_instance_type }}' + security_groups: '{{ sg.group_id }}' + volumes: + - device_name: /dev/xvda + volume_size: 10 + volume_type: gp2 + delete_on_termination: true + register: lc_1_create_idem + + - assert: + that: + - lc_1_create_idem is not changed + - '"autoscaling:CreateLaunchConfiguration" not in lc_1_create_idem.resource_actions' + + - name: Create launch configuration 2 + community.aws.ec2_lc: + name: '{{ resource_prefix }}-lc2' + image_id: '{{ ec2_ami_id }}' + assign_public_ip: yes + instance_type: 't3.small' + security_groups: '{{ sg.group_id }}' + volumes: + - device_name: /dev/xvda + volume_size: 10 + volume_type: gp2 + delete_on_termination: true + register: lc_2_create + + - name: Gather information about launch configuration 2 + community.aws.ec2_lc_info: + name: '{{ resource_prefix }}-lc2' + register: lc_2_info_result + + - assert: + that: + - lc_2_create is changed + - '"autoscaling:CreateLaunchConfiguration" in lc_2_create.resource_actions' + - '"throughput" not in lc_2_info_result.launch_configurations[0].block_device_mappings[0].ebs' + - lc_2_info_result.launch_configurations[0].block_device_mappings[0].ebs.volume_size == 10 + - lc_2_info_result.launch_configurations[0].block_device_mappings[0].ebs.volume_type == 'gp2' + - lc_2_info_result.launch_configurations[0].instance_type == 't3.small' + - '"autoscaling:CreateLaunchConfiguration" in lc_2_create.resource_actions' + + - name: Create launch configuration 2 - Idempotency + community.aws.ec2_lc: + name: '{{ resource_prefix }}-lc2' + image_id: '{{ ec2_ami_id }}' + assign_public_ip: yes + instance_type: '{{ ec2_instance_type }}' + security_groups: '{{ sg.group_id }}' + volumes: + - device_name: /dev/xvda + volume_size: 10 + volume_type: gp2 + delete_on_termination: true + register: lc_2_create_idem + + - assert: + that: + - lc_2_create_idem is not changed + - '"autoscaling:CreateLaunchConfiguration" not in lc_2_create_idem.resource_actions' + + - name: Create launch configuration 3 - test throughput parameter + community.aws.ec2_lc: + name: '{{ resource_prefix }}-lc3' + image_id: '{{ ec2_ami_id }}' + instance_type: '{{ ec2_instance_type }}' + volumes: + - device_name: /dev/sda1 + volume_size: 10 + volume_type: gp3 + throughput: 250 + delete_on_termination: true + register: lc_3_create + + - name: Gather information about launch configuration 3 + community.aws.ec2_lc_info: + name: '{{ resource_prefix }}-lc3' + register: lc_3_info_result + + - assert: + that: + - lc_3_create is changed + - '"throughput" in lc_3_info_result.launch_configurations[0].block_device_mappings[0].ebs' + - lc_3_info_result.launch_configurations[0].block_device_mappings[0].ebs.throughput == 250 + - lc_3_info_result.launch_configurations[0].block_device_mappings[0].ebs.volume_size == 10 + - lc_3_info_result.launch_configurations[0].block_device_mappings[0].ebs.volume_type == 'gp3' + - lc_3_info_result.launch_configurations[0].instance_type == 't2.micro' + - '"autoscaling:CreateLaunchConfiguration" in lc_3_create.resource_actions' + + - name: Create launch configuration 3 - Idempotency + community.aws.ec2_lc: + name: '{{ resource_prefix }}-lc3' + image_id: '{{ ec2_ami_id }}' + instance_type: '{{ ec2_instance_type }}' + volumes: + - device_name: /dev/sda1 + volume_size: 10 + volume_type: gp3 + throughput: 250 + delete_on_termination: true + register: lc_3_create_idem + + - assert: + that: + - lc_3_create_idem is not changed + - '"autoscaling:CreateLaunchConfiguration" not in lc_3_create_idem.resource_actions' + + - name: Search for the Launch Configurations that start with test resource_prefix + community.aws.ec2_lc_find: + name_regex: '{{ resource_prefix }}*' + sort_order: descending + register: lc_find_result + + - assert: + that: + - lc_find_result.results | length == 3 + - '"autoscaling:DescribeLaunchConfigurations" in lc_find_result.resource_actions' + + - name: Delete launch configuration 1 + community.aws.ec2_lc: + name: '{{ resource_prefix }}-lc1' + state: absent + register: lc_1_delete + + - assert: + that: + - lc_1_delete is changed + - '"autoscaling:DeleteLaunchConfiguration" in lc_1_delete.resource_actions' + + - name: Delete launch configuration 1 - Idempotency + community.aws.ec2_lc: + name: '{{ resource_prefix }}-lc1' + state: absent + register: lc_1_delete_idem + + - assert: + that: + - lc_1_delete_idem is not changed + - '"autoscaling:DeleteLaunchConfiguration" not in lc_1_delete_idem.resource_actions' + + - name: Gather information about launch configuration 1 + community.aws.ec2_lc_info: + name: '{{ resource_prefix }}-lc1' + register: lc_1_info_result + + - assert: + that: + - lc_1_info_result is not changed + - lc_1_info_result.launch_configurations | length == 0 + + - name: Delete launch configuration 2 + community.aws.ec2_lc: + name: '{{ resource_prefix }}-lc2' + state: absent + register: lc_2_delete + + - assert: + that: + - lc_2_delete is changed + - '"autoscaling:DeleteLaunchConfiguration" in lc_2_delete.resource_actions' + + - name: Delete launch configuration 2 - Idempotency + community.aws.ec2_lc: + name: '{{ resource_prefix }}-lc2' + state: absent + register: lc_2_delete_idem + + - assert: + that: + - lc_2_delete_idem is not changed + - '"autoscaling:DeleteLaunchConfiguration" not in lc_2_delete_idem.resource_actions' + + - name: Gather information about launch configuration 2 + community.aws.ec2_lc_info: + name: '{{ resource_prefix }}-lc2' + register: lc_2_info_result + + - assert: + that: + - lc_2_info_result is not changed + - lc_2_info_result.launch_configurations | length == 0 + + - name: Delete launch configuration 3 + community.aws.ec2_lc: + name: '{{ resource_prefix }}-lc3' + state: absent + register: lc_3_delete + + - assert: + that: + - lc_3_delete is changed + - '"autoscaling:DeleteLaunchConfiguration" in lc_3_delete.resource_actions' + + - name: Delete launch configuration 3 - Idempotency + community.aws.ec2_lc: + name: '{{ resource_prefix }}-lc3' + state: absent + register: lc_3_delete_idem + + - assert: + that: + - lc_3_delete_idem is not changed + - '"autoscaling:DeleteLaunchConfiguration" not in lc_3_delete_idem.resource_actions' + + - name: Gather information about launch configuration 3 + community.aws.ec2_lc_info: + name: '{{ resource_prefix }}-lc2' + register: lc_3_info_result + + - assert: + that: + - lc_3_info_result is not changed + - lc_3_info_result.launch_configurations | length == 0 + + always: + + - include_tasks: env_cleanup.yml diff --git a/ansible_collections/community/aws/tests/integration/targets/autoscaling_launch_config/vars/main.yml b/ansible_collections/community/aws/tests/integration/targets/autoscaling_launch_config/vars/main.yml new file mode 100644 index 000000000..ed97d539c --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/autoscaling_launch_config/vars/main.yml @@ -0,0 +1 @@ +--- diff --git a/ansible_collections/community/aws/tests/integration/targets/autoscaling_lifecycle_hook/aliases b/ansible_collections/community/aws/tests/integration/targets/autoscaling_lifecycle_hook/aliases new file mode 100644 index 000000000..4ef4b2067 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/autoscaling_lifecycle_hook/aliases @@ -0,0 +1 @@ +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/autoscaling_lifecycle_hook/inventory b/ansible_collections/community/aws/tests/integration/targets/autoscaling_lifecycle_hook/inventory new file mode 100644 index 000000000..2081f49e6 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/autoscaling_lifecycle_hook/inventory @@ -0,0 +1,6 @@ +[tests] +create_update_delete + +[all:vars] +ansible_connection=local +ansible_python_interpreter="{{ ansible_playbook_python }}" diff --git a/ansible_collections/community/aws/tests/integration/targets/autoscaling_lifecycle_hook/main.yml b/ansible_collections/community/aws/tests/integration/targets/autoscaling_lifecycle_hook/main.yml new file mode 100644 index 000000000..a22182146 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/autoscaling_lifecycle_hook/main.yml @@ -0,0 +1,42 @@ +--- +# Beware: most of our tests here are run in parallel. +# To add new tests you'll need to add a new host to the inventory and a matching +# '{{ inventory_hostname }}'.yml file in roles/ec2_asg_lifecycle_hook/tasks/ + + +# Prepare the VPC and figure out which AMI to use +- hosts: all + gather_facts: no + tasks: + - module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + vars: + # We can't just use "run_once" because the facts don't propagate when + # running an 'include' that was run_once + setup_run_once: yes + block: + - include_role: + name: 'setup_ec2_facts' + - include_role: + name: 'ec2_asg_lifecycle_hook' + tasks_from: env_setup.yml + rescue: + - include_role: + name: 'ec2_asg_lifecycle_hook' + tasks_from: env_cleanup.yml + run_once: yes + - fail: + msg: 'Environment preparation failed' + run_once: yes + +# VPC should get cleaned up once all hosts have run +- hosts: all + gather_facts: no + strategy: free + serial: 6 + roles: + - ec2_asg_lifecycle_hook diff --git a/ansible_collections/community/aws/tests/integration/targets/autoscaling_lifecycle_hook/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/autoscaling_lifecycle_hook/meta/main.yml new file mode 100644 index 000000000..1471b11f6 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/autoscaling_lifecycle_hook/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - setup_ec2_facts diff --git a/ansible_collections/community/aws/tests/integration/targets/autoscaling_lifecycle_hook/roles/ec2_asg_lifecycle_hook/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/autoscaling_lifecycle_hook/roles/ec2_asg_lifecycle_hook/defaults/main.yml new file mode 100644 index 000000000..1bbe062af --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/autoscaling_lifecycle_hook/roles/ec2_asg_lifecycle_hook/defaults/main.yml @@ -0,0 +1,4 @@ +--- +# defaults file for ec2_asg_lifecycle_hook +# Amazon Linux 2 AMI 2019.06.12 (HVM), GP2 Volume Type +load_balancer_name: "{{ tiny_prefix }}-lb" diff --git a/ansible_collections/community/aws/tests/integration/targets/autoscaling_lifecycle_hook/roles/ec2_asg_lifecycle_hook/tasks/create_update_delete.yml b/ansible_collections/community/aws/tests/integration/targets/autoscaling_lifecycle_hook/roles/ec2_asg_lifecycle_hook/tasks/create_update_delete.yml new file mode 100644 index 000000000..800ee6358 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/autoscaling_lifecycle_hook/roles/ec2_asg_lifecycle_hook/tasks/create_update_delete.yml @@ -0,0 +1,245 @@ +--- +- name: Test create/update/delete AutoScalingGroups Lifecycle Hooks with ec2_asg_lifecycle_hook + + block: + #---------------------------------------------------------------------- + - name: create a launch configuration + ec2_lc: + name: "{{ resource_prefix }}-lc" + image_id: "{{ ec2_ami_id }}" + region: "{{ aws_region }}" + instance_type: t2.micro + assign_public_ip: yes + register: create_lc + + - name: ensure that lc is created + assert: + that: + - create_lc is changed + - create_lc.failed is false + + #---------------------------------------------------------------------- + - name: create a AutoScalingGroup + ec2_asg: + name: "{{ resource_prefix }}-asg" + launch_config_name: "{{ resource_prefix }}-lc" + health_check_period: 60 + health_check_type: ELB + replace_all_instances: yes + min_size: 1 + max_size: 1 + desired_capacity: 1 + region: "{{ aws_region }}" + register: create_asg + + - name: ensure that AutoScalingGroup is created + assert: + that: + - create_asg is changed + - create_asg.failed is false + - '"autoscaling:CreateAutoScalingGroup" in create_asg.resource_actions' + + #---------------------------------------------------------------------- + + - name: Create lifecycle hook - check_mode + community.aws.ec2_asg_lifecycle_hook: + region: "{{ aws_region }}" + autoscaling_group_name: "{{ resource_prefix }}-asg" + lifecycle_hook_name: "{{ resource_prefix }}-test-hook" + transition: autoscaling:EC2_INSTANCE_LAUNCHING + heartbeat_timeout: 7000 + default_result: ABANDON + state: present + check_mode: true + register: output + + - assert: + that: + - output is changed + - output is not failed + - '"lifecycle_hook_info" not in output' + - '"Would have created AutoScalingGroup Lifecycle Hook if not in check_mode" in output.msg' + + - name: Create lifecycle hook + community.aws.ec2_asg_lifecycle_hook: + region: "{{ aws_region }}" + autoscaling_group_name: "{{ resource_prefix }}-asg" + lifecycle_hook_name: "{{ resource_prefix }}-test-hook" + transition: autoscaling:EC2_INSTANCE_LAUNCHING + heartbeat_timeout: 7000 + default_result: ABANDON + state: present + register: output + + - assert: + that: + - output is changed + - output is not failed + - '"lifecycle_hook_info" in output' + - output.lifecycle_hook_info[0].heartbeat_timeout == 7000 + + - name: Create lifecycle hook - Idempotency + community.aws.ec2_asg_lifecycle_hook: + region: "{{ aws_region }}" + autoscaling_group_name: "{{ resource_prefix }}-asg" + lifecycle_hook_name: "{{ resource_prefix }}-test-hook" + transition: autoscaling:EC2_INSTANCE_LAUNCHING + heartbeat_timeout: 7000 + default_result: ABANDON + state: present + register: output + + - assert: + that: + - output is not changed + - output is not failed + - '"lifecycle_hook_info" not in output' + + - name: Create lifecycle hook - check_mode (Idempotency) + community.aws.ec2_asg_lifecycle_hook: + region: "{{ aws_region }}" + autoscaling_group_name: "{{ resource_prefix }}-asg" + lifecycle_hook_name: "{{ resource_prefix }}-test-hook" + transition: autoscaling:EC2_INSTANCE_LAUNCHING + heartbeat_timeout: 7000 + default_result: ABANDON + state: present + check_mode: true + register: output + + - assert: + that: + - output is not changed + - output is not failed + - '"lifecycle_hook_info" not in output' + + - name: Update lifecycle hook - check_mode + community.aws.ec2_asg_lifecycle_hook: + region: "{{ aws_region }}" + autoscaling_group_name: "{{ resource_prefix }}-asg" + lifecycle_hook_name: "{{ resource_prefix }}-test-hook" + transition: autoscaling:EC2_INSTANCE_LAUNCHING + heartbeat_timeout: 6000 + default_result: ABANDON + state: present + check_mode: true + register: output + + - assert: + that: + - output is changed + - output is not failed + - '"lifecycle_hook_info" not in output' + - '"Would have modified AutoScalingGroup Lifecycle Hook if not in check_mode." in output.msg' + + - name: Update lifecycle hook + community.aws.ec2_asg_lifecycle_hook: + region: "{{ aws_region }}" + autoscaling_group_name: "{{ resource_prefix }}-asg" + lifecycle_hook_name: "{{ resource_prefix }}-test-hook" + transition: autoscaling:EC2_INSTANCE_LAUNCHING + heartbeat_timeout: 6000 + default_result: ABANDON + state: present + register: output + + - assert: + that: + - output is changed + - output is not failed + - '"lifecycle_hook_info" in output' + - output.lifecycle_hook_info[0].heartbeat_timeout == 6000 + + - name: Update lifecycle hook - Idempotency + community.aws.ec2_asg_lifecycle_hook: + region: "{{ aws_region }}" + autoscaling_group_name: "{{ resource_prefix }}-asg" + lifecycle_hook_name: "{{ resource_prefix }}-test-hook" + transition: autoscaling:EC2_INSTANCE_LAUNCHING + heartbeat_timeout: 6000 + default_result: ABANDON + state: present + register: output + + - assert: + that: + - output is not changed + - output is not failed + - '"lifecycle_hook_info" not in output' + + - name: Update lifecycle hook - check_mode (Idempotency) + community.aws.ec2_asg_lifecycle_hook: + region: "{{ aws_region }}" + autoscaling_group_name: "{{ resource_prefix }}-asg" + lifecycle_hook_name: "{{ resource_prefix }}-test-hook" + transition: autoscaling:EC2_INSTANCE_LAUNCHING + heartbeat_timeout: 6000 + default_result: ABANDON + state: present + check_mode: true + register: output + + - assert: + that: + - output is not changed + - output is not failed + - '"lifecycle_hook_info" not in output' + + - name: Delete lifecycle hook - check_mode + community.aws.ec2_asg_lifecycle_hook: + region: "{{ aws_region }}" + autoscaling_group_name: "{{ resource_prefix }}-asg" + lifecycle_hook_name: "{{ resource_prefix }}-test-hook" + state: absent + check_mode: true + register: output + + - assert: + that: + - output is changed + - output is not failed + - '"lifecycle_hook_removed" not in output' + - '"Would have deleted AutoScalingGroup Lifecycle Hook if not in check_mode." in output.msg' + + - name: Delete lifecycle hook + community.aws.ec2_asg_lifecycle_hook: + region: "{{ aws_region }}" + autoscaling_group_name: "{{ resource_prefix }}-asg" + lifecycle_hook_name: "{{ resource_prefix }}-test-hook" + state: absent + register: output + + - assert: + that: + - output is changed + - output is not failed + - '"lifecycle_hook_removed" in output' + + - name: Delete lifecycle hook - Idempotency + community.aws.ec2_asg_lifecycle_hook: + region: "{{ aws_region }}" + autoscaling_group_name: "{{ resource_prefix }}-asg" + lifecycle_hook_name: "{{ resource_prefix }}-test-hook" + state: absent + register: output + + - assert: + that: + - output is not changed + - output is not failed + - '"lifecycle_hook_removed" not in output' + + - name: Delete lifecycle hook - check_mode (Idempotency) + community.aws.ec2_asg_lifecycle_hook: + region: "{{ aws_region }}" + autoscaling_group_name: "{{ resource_prefix }}-asg" + lifecycle_hook_name: "{{ resource_prefix }}-test-hook" + state: absent + check_mode: true + register: output + + - assert: + that: + - output is not changed + - output is not failed + - '"lifecycle_hook_removed" not in output' diff --git a/ansible_collections/community/aws/tests/integration/targets/autoscaling_lifecycle_hook/roles/ec2_asg_lifecycle_hook/tasks/env_cleanup.yml b/ansible_collections/community/aws/tests/integration/targets/autoscaling_lifecycle_hook/roles/ec2_asg_lifecycle_hook/tasks/env_cleanup.yml new file mode 100644 index 000000000..3b4ee869b --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/autoscaling_lifecycle_hook/roles/ec2_asg_lifecycle_hook/tasks/env_cleanup.yml @@ -0,0 +1,123 @@ +- name: kill asg + ec2_asg: + name: "{{ resource_prefix }}-asg" + state: absent + register: removed + until: removed is not failed + ignore_errors: true + retries: 10 + +# Remove the testing dependencies +- name: remove target group + elb_target_group: + name: "{{ item }}" + state: absent + register: removed + until: removed is not failed + ignore_errors: true + retries: 10 + loop: + - "{{ tg1_name }}" + - "{{ tg2_name }}" + +- name: remove the load balancer + ec2_elb_lb: + name: "{{ load_balancer_name }}" + state: absent + security_group_ids: + - "{{ sg.group_id }}" + subnets: "{{ testing_subnet.subnet.id }}" + wait: true + connection_draining_timeout: 60 + listeners: + - protocol: http + load_balancer_port: 80 + instance_port: 80 + health_check: + ping_protocol: tcp + ping_port: 80 + ping_path: "/" + response_timeout: 5 + interval: 10 + unhealthy_threshold: 4 + healthy_threshold: 2 + register: removed + until: removed is not failed + ignore_errors: true + retries: 10 + +- name: remove launch configs + ec2_lc: + name: "{{ item }}" + state: absent + register: removed + until: removed is not failed + ignore_errors: true + retries: 10 + loop: + - "{{ resource_prefix }}-lc" + +- name: delete launch template + ec2_launch_template: + name: "{{ resource_prefix }}-lt" + state: absent + register: del_lt + retries: 10 + until: del_lt is not failed + ignore_errors: true + +- name: remove the security group + ec2_group: + name: "{{ resource_prefix }}-sg" + description: a security group for ansible tests + vpc_id: "{{ testing_vpc.vpc.id }}" + state: absent + register: removed + until: removed is not failed + ignore_errors: true + retries: 10 + +- name: remove routing rules + ec2_vpc_route_table: + state: absent + vpc_id: "{{ testing_vpc.vpc.id }}" + tags: + created: "{{ resource_prefix }}-route" + routes: + - dest: 0.0.0.0/0 + gateway_id: "{{ igw.gateway_id }}" + subnets: + - "{{ testing_subnet.subnet.id }}" + register: removed + until: removed is not failed + ignore_errors: true + retries: 10 + +- name: remove internet gateway + ec2_vpc_igw: + vpc_id: "{{ testing_vpc.vpc.id }}" + state: absent + register: removed + until: removed is not failed + ignore_errors: true + retries: 10 + +- name: remove the subnet + ec2_vpc_subnet: + state: absent + vpc_id: "{{ testing_vpc.vpc.id }}" + cidr: 10.55.77.0/24 + register: removed + until: removed is not failed + ignore_errors: true + retries: 10 + +- name: remove the VPC + ec2_vpc_net: + name: "{{ resource_prefix }}-vpc" + cidr_block: 10.55.77.0/24 + state: absent + register: removed + until: removed is not failed + ignore_errors: true + retries: 10 diff --git a/ansible_collections/community/aws/tests/integration/targets/autoscaling_lifecycle_hook/roles/ec2_asg_lifecycle_hook/tasks/env_setup.yml b/ansible_collections/community/aws/tests/integration/targets/autoscaling_lifecycle_hook/roles/ec2_asg_lifecycle_hook/tasks/env_setup.yml new file mode 100644 index 000000000..8e9be1d55 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/autoscaling_lifecycle_hook/roles/ec2_asg_lifecycle_hook/tasks/env_setup.yml @@ -0,0 +1,56 @@ +- name: Run ec2_asg_lifecycle_hook integration tests. + + block: + + # ============================================================ + + # 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" + cidr_block: 10.55.77.0/24 + tenancy: default + register: testing_vpc + + - name: Create internet gateway for use in testing + ec2_vpc_igw: + vpc_id: "{{ testing_vpc.vpc.id }}" + state: present + register: igw + + - name: Create subnet for use in testing + ec2_vpc_subnet: + state: present + vpc_id: "{{ testing_vpc.vpc.id }}" + cidr: 10.55.77.0/24 + az: "{{ aws_region }}a" + resource_tags: + Name: "{{ resource_prefix }}-subnet" + register: testing_subnet + + - name: create routing rules + ec2_vpc_route_table: + vpc_id: "{{ testing_vpc.vpc.id }}" + tags: + created: "{{ resource_prefix }}-route" + routes: + - dest: 0.0.0.0/0 + gateway_id: "{{ igw.gateway_id }}" + subnets: + - "{{ testing_subnet.subnet.id }}" + + - name: create a security group with the vpc created in the ec2_setup + ec2_group: + name: "{{ resource_prefix }}-sg" + description: a security group for ansible tests + vpc_id: "{{ testing_vpc.vpc.id }}" + rules: + - proto: tcp + from_port: 22 + to_port: 22 + cidr_ip: 0.0.0.0/0 + - proto: tcp + from_port: 80 + to_port: 80 + cidr_ip: 0.0.0.0/0 + register: sg diff --git a/ansible_collections/community/aws/tests/integration/targets/autoscaling_lifecycle_hook/roles/ec2_asg_lifecycle_hook/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/autoscaling_lifecycle_hook/roles/ec2_asg_lifecycle_hook/tasks/main.yml new file mode 100644 index 000000000..16442c7fa --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/autoscaling_lifecycle_hook/roles/ec2_asg_lifecycle_hook/tasks/main.yml @@ -0,0 +1,40 @@ +--- +# Beware: most of our tests here are run in parallel. +# To add new tests you'll need to add a new host to the inventory and a matching +# '{{ inventory_hostname }}'.yml file in roles/ec2_asg_lifecycle_hook/tasks/ + +- name: "Wrap up all tests and setup AWS credentials" + module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + aws_config: + retries: + # Unfortunately AWSRetry doesn't support paginators and boto3's paginators + # don't support any configuration of the delay between retries. + max_attempts: 20 + collections: + - community.aws + block: + - debug: + msg: "{{ inventory_hostname }} start: {{ lookup('pipe','date') }}" + - include_tasks: '{{ inventory_hostname }}.yml' + - debug: + msg: "{{ inventory_hostname }} finish: {{ lookup('pipe','date') }}" + + always: + - set_fact: + _role_complete: True + - vars: + completed_hosts: '{{ ansible_play_hosts_all | map("extract", hostvars, "_role_complete") | list | select("defined") | list | length }}' + hosts_in_play: '{{ ansible_play_hosts_all | length }}' + debug: + msg: "{{ completed_hosts }} of {{ hosts_in_play }} complete" + - include_tasks: env_cleanup.yml + vars: + completed_hosts: '{{ ansible_play_hosts_all | map("extract", hostvars, "_role_complete") | list | select("defined") | list | length }}' + hosts_in_play: '{{ ansible_play_hosts_all | length }}' + when: + - completed_hosts == hosts_in_play diff --git a/ansible_collections/community/aws/tests/integration/targets/autoscaling_lifecycle_hook/runme.sh b/ansible_collections/community/aws/tests/integration/targets/autoscaling_lifecycle_hook/runme.sh new file mode 100755 index 000000000..aa324772b --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/autoscaling_lifecycle_hook/runme.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +# +# Beware: most of our tests here are run in parallel. +# To add new tests you'll need to add a new host to the inventory and a matching +# '{{ inventory_hostname }}'.yml file in roles/ec2_instance/tasks/ + + +set -eux + +export ANSIBLE_ROLES_PATH=../ + +ansible-playbook main.yml -i inventory "$@" diff --git a/ansible_collections/community/aws/tests/integration/targets/autoscaling_policy/aliases b/ansible_collections/community/aws/tests/integration/targets/autoscaling_policy/aliases new file mode 100644 index 000000000..4ef4b2067 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/autoscaling_policy/aliases @@ -0,0 +1 @@ +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/autoscaling_policy/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/autoscaling_policy/defaults/main.yml new file mode 100644 index 000000000..ccbee4a46 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/autoscaling_policy/defaults/main.yml @@ -0,0 +1,2 @@ +scaling_policy_lc_name: "{{ resource_prefix }}_lc" +scaling_policy_asg_name: "{{ resource_prefix }}_asg" diff --git a/ansible_collections/community/aws/tests/integration/targets/autoscaling_policy/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/autoscaling_policy/meta/main.yml new file mode 100644 index 000000000..1471b11f6 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/autoscaling_policy/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - setup_ec2_facts diff --git a/ansible_collections/community/aws/tests/integration/targets/autoscaling_policy/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/autoscaling_policy/tasks/main.yml new file mode 100644 index 000000000..24b3eea62 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/autoscaling_policy/tasks/main.yml @@ -0,0 +1,317 @@ +--- +# __Test Outline__ +# +# __ec2_scaling_policy__ +# create simplescaling scaling policy +# update simplescaling scaling policy +# remove simplescaling scaling policy +# create stepscaling scaling policy +# update stepscaling scaling policy +# remove stepscaling scaling policy + +- module_defaults: + group/aws: + region: "{{ aws_region }}" + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + collections: + - amazon.aws + block: + + - name: create trivial launch_configuration + ec2_lc: + name: "{{ scaling_policy_lc_name }}" + state: present + instance_type: t3.nano + image_id: "{{ ec2_ami_id }}" + + - name: create trivial ASG + ec2_asg: + name: "{{ scaling_policy_asg_name }}" + state: present + launch_config_name: "{{ scaling_policy_lc_name }}" + min_size: 0 + max_size: 1 + desired_capacity: 0 + + - name: Create Simple Scaling policy using implicit defaults + ec2_scaling_policy: + name: "{{ resource_prefix }}_simplescaling_policy" + asg_name: "{{ scaling_policy_asg_name }}" + state: present + adjustment_type: ChangeInCapacity + scaling_adjustment: 1 + register: result + + - assert: + that: + - result.policy_name == "{{ resource_prefix }}_simplescaling_policy" + - result.changed + + - name: Update Simple Scaling policy using explicit defaults + ec2_scaling_policy: + name: "{{ resource_prefix }}_simplescaling_policy" + asg_name: "{{ scaling_policy_asg_name }}" + state: present + adjustment_type: ChangeInCapacity + scaling_adjustment: 1 + policy_type: SimpleScaling + register: result + + - assert: + that: + - result.policy_name == "{{ resource_prefix }}_simplescaling_policy" + - not result.changed + + - name: min_adjustment_step is ignored with ChangeInCapacity + ec2_scaling_policy: + name: "{{ resource_prefix }}_simplescaling_policy" + asg_name: "{{ scaling_policy_asg_name }}" + state: present + adjustment_type: ChangeInCapacity + scaling_adjustment: 1 + min_adjustment_step: 1 + policy_type: SimpleScaling + register: result + + - assert: + that: + - result.policy_name == "{{ resource_prefix }}_simplescaling_policy" + - not result.changed + - result.adjustment_type == "ChangeInCapacity" + + - name: Change Simple Scaling policy adjustment_type to PercentChangeInCapacity + ec2_scaling_policy: + name: "{{ resource_prefix }}_simplescaling_policy" + asg_name: "{{ scaling_policy_asg_name }}" + state: present + adjustment_type: PercentChangeInCapacity + scaling_adjustment: 1 + min_adjustment_step: 1 + policy_type: SimpleScaling + register: result + + - assert: + that: + - result.policy_name == "{{ resource_prefix }}_simplescaling_policy" + - result.changed + - result.adjustment_type == "PercentChangeInCapacity" + + - name: Remove Simple Scaling policy + ec2_scaling_policy: + name: "{{ resource_prefix }}_simplescaling_policy" + asg_name: "{{ scaling_policy_asg_name }}" + state: absent + register: result + + - assert: + that: + - result.changed + + - name: Create Step Scaling policy + ec2_scaling_policy: + name: "{{ resource_prefix }}_stepscaling_policy" + asg_name: "{{ scaling_policy_asg_name }}" + state: present + policy_type: StepScaling + metric_aggregation: Maximum + step_adjustments: + - upper_bound: 20 + scaling_adjustment: 50 + - lower_bound: 20 + scaling_adjustment: 100 + adjustment_type: "PercentChangeInCapacity" + register: result + + - assert: + that: + - result.policy_name == "{{ resource_prefix }}_stepscaling_policy" + - result.changed + + - name: Add another step + ec2_scaling_policy: + name: "{{ resource_prefix }}_stepscaling_policy" + asg_name: "{{ scaling_policy_asg_name }}" + state: present + policy_type: StepScaling + metric_aggregation: Maximum + step_adjustments: + - upper_bound: 20 + scaling_adjustment: 50 + - lower_bound: 20 + upper_bound: 40 + scaling_adjustment: 75 + - lower_bound: 40 + scaling_adjustment: 100 + adjustment_type: "PercentChangeInCapacity" + register: result + + - assert: + that: + - result.policy_name == "{{ resource_prefix }}_stepscaling_policy" + - result.changed + - result.adjustment_type == "PercentChangeInCapacity" + + - name: Remove Step Scaling policy + ec2_scaling_policy: + name: "{{ resource_prefix }}_stepscaling_policy" + asg_name: "{{ scaling_policy_asg_name }}" + state: absent + register: result + + - assert: + that: + - result.changed + + - name: Remove Step Scaling policy (idemopotency) + ec2_scaling_policy: + name: "{{ resource_prefix }}_stepscaling_policy" + asg_name: "{{ scaling_policy_asg_name }}" + state: absent + register: result + + - assert: + that: + - result is not changed + - result is successful + + - name: create TargetTracking predefined policy + ec2_scaling_policy: + name: "{{ resource_prefix }}_targettracking_predefined_policy" + policy_type: TargetTrackingScaling + target_tracking_config: + predefined_metric_spec: + predefined_metric_type: ASGAverageCPUUtilization + target_value: 98.0 + asg_name: "{{ scaling_policy_asg_name }}" + register: result + + - assert: + that: + - result.policy_name == "{{ resource_prefix }}_targettracking_predefined_policy" + - result.changed + - result is successful + + - name: create TargetTrackingScaling predefined policy (idempotency) + ec2_scaling_policy: + name: "{{ resource_prefix }}_targettracking_predefined_policy" + policy_type: TargetTrackingScaling + target_tracking_config: + predefined_metric_spec: + predefined_metric_type: ASGAverageCPUUtilization + target_value: 98.0 + asg_name: "{{ scaling_policy_asg_name }}" + register: result + + - assert: + that: + - result.policy_name == "{{ resource_prefix }}_targettracking_predefined_policy" + - result is not changed + +# # It would be good to also test this but we would need an Target group and an ALB +# - name: create TargetTracking predefined policy with resource_label +# ec2_scaling_policy: +# name: "{{ resource_prefix }}_targettracking_predefined_rl_policy" +# policy_type: TargetTrackingScaling +# target_tracking_config: +# predefined_metric_spec: +# predefined_metric_type: ALBRequestCountPerTarget +# resource_label: "{{ alb_resource_label }}" +# target_value: 98.0 +# asg_name: "{{ scaling_policy_asg_name }}" +# register: result +# +# - assert: +# that: +# - result.policy_name == "{{ resource_prefix }}_targettracking_predefined_rl_policy" +# - result.changed +# - result is successful +# +# - name: create TargetTracking predefined policy with resource_label (idempotency) +# ec2_scaling_policy: +# name: "{{ resource_prefix }}_targettracking_predefined_rl_policy" +# policy_type: TargetTrackingScaling +# target_tracking_config: +# predefined_metric_spec: +# predefined_metric_type: ALBRequestCountPerTarget +# resource_label: "{{ alb_resource_label }}" +# target_value: 98.0 +# asg_name: "{{ scaling_policy_asg_name }}" +# register: result +# +# - assert: +# that: +# - result.policy_name == "{{ resource_prefix }}_targettracking_predefined_rl_policy" +# - result is not changed + + - name: create TargetTrackingScaling custom policy + ec2_scaling_policy: + name: "{{ resource_prefix }}_targettracking_custom_policy" + policy_type: TargetTrackingScaling + target_tracking_config: + customized_metric_spec: + metric_name: metric_1 + namespace: namespace_1 + statistic: Minimum + unit: Gigabits + dimensions: [{'Name': 'dimension1', 'Value': 'value1'}] + disable_scalein: true + target_value: 98.0 + asg_name: "{{ scaling_policy_asg_name }}" + register: result + + - assert: + that: + - result.policy_name == "{{ resource_prefix }}_targettracking_custom_policy" + - result.changed + - result is successful + + - name: create TargetTrackingScaling custom policy (idempotency) + ec2_scaling_policy: + name: "{{ resource_prefix }}_targettracking_custom_policy" + policy_type: TargetTrackingScaling + target_tracking_config: + customized_metric_spec: + metric_name: metric_1 + namespace: namespace_1 + statistic: Minimum + unit: Gigabits + dimensions: [{'Name': 'dimension1', 'Value': 'value1'}] + disable_scalein: true + target_value: 98.0 + asg_name: "{{ scaling_policy_asg_name }}" + register: result + + - assert: + that: + - result.policy_name == "{{ resource_prefix }}_targettracking_custom_policy" + - result is not changed + + always: + + # ============================================================ + - name: Remove the scaling policies + ec2_scaling_policy: + name: "{{ item }}" + state: absent + register: result + with_items: + - "{{ resource_prefix }}_simplescaling_policy" + - "{{ resource_prefix }}_stepscaling_policy" + - "{{ resource_prefix }}_targettracking_predefined_policy" + - "{{ resource_prefix }}_targettracking_predefined_rl_policy" + - "{{ resource_prefix }}_targettracking_custom_policy" + ignore_errors: yes + + - name: remove the ASG + ec2_asg: + name: "{{ scaling_policy_asg_name }}" + state: absent + ignore_errors: yes + + - name: remove the Launch Configuration + ec2_lc: + name: "{{ scaling_policy_lc_name }}" + state: absent + ignore_errors: yes diff --git a/ansible_collections/community/aws/tests/integration/targets/autoscaling_scheduled_action/aliases b/ansible_collections/community/aws/tests/integration/targets/autoscaling_scheduled_action/aliases new file mode 100644 index 000000000..4ef4b2067 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/autoscaling_scheduled_action/aliases @@ -0,0 +1 @@ +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/autoscaling_scheduled_action/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/autoscaling_scheduled_action/defaults/main.yml new file mode 100644 index 000000000..ed97d539c --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/autoscaling_scheduled_action/defaults/main.yml @@ -0,0 +1 @@ +--- diff --git a/ansible_collections/community/aws/tests/integration/targets/autoscaling_scheduled_action/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/autoscaling_scheduled_action/meta/main.yml new file mode 100644 index 000000000..aef5ca0ee --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/autoscaling_scheduled_action/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - role: setup_ec2_facts diff --git a/ansible_collections/community/aws/tests/integration/targets/autoscaling_scheduled_action/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/autoscaling_scheduled_action/tasks/main.yml new file mode 100644 index 000000000..c78c7efae --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/autoscaling_scheduled_action/tasks/main.yml @@ -0,0 +1,335 @@ +--- +- name: "ec2_asg_scheduled_action integration tests" + collections: + - amazon.aws + - community.aws + module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + block: + ## Set up the testing dependencies: VPC, subnet, security group, and launch configuration + - name: Create VPC for use in testing + ec2_vpc_net: + name: "{{ resource_prefix }}-vpc" + cidr_block: 10.55.77.0/24 + tenancy: default + register: testing_vpc + + - name: Create subnet for use in testing + ec2_vpc_subnet: + state: present + vpc_id: "{{ testing_vpc.vpc.id }}" + cidr: 10.55.77.0/24 + az: "{{ aws_region }}a" + resource_tags: + Name: "{{ resource_prefix }}-subnet" + register: testing_subnet + + - name: create a security group with the vpc created in the ec2_setup + ec2_group: + name: "{{ resource_prefix }}-sg" + description: a security group for ansible tests + vpc_id: "{{ testing_vpc.vpc.id }}" + rules: + - proto: tcp + from_port: 22 + to_port: 22 + cidr_ip: 0.0.0.0/0 + - proto: tcp + from_port: 80 + to_port: 80 + cidr_ip: 0.0.0.0/0 + register: sg + + - name: ensure launch configs exist + ec2_lc: + name: "{{ resource_prefix }}-lc" + assign_public_ip: true + image_id: "{{ ec2_ami_id }}" + security_groups: "{{ sg.group_id }}" + instance_type: t3.micro + + - name: Create ASG ready + ec2_asg: + name: "{{ resource_prefix }}-asg" + launch_config_name: "{{ resource_prefix }}-lc" + desired_capacity: 1 + min_size: 1 + max_size: 2 + vpc_zone_identifier: "{{ testing_subnet.subnet.id }}" + state: present + wait_for_instances: yes + register: output + + - assert: + that: + - "output.viable_instances == 1" + + ## Create minimal basic scheduled action + - name: Create basic scheduled_action - check_mode + ec2_asg_scheduled_action: + autoscaling_group_name: "{{ resource_prefix }}-asg" + scheduled_action_name: "{{ resource_prefix }}-test" + start_time: 2022 October 25 08:00 UTC + recurrence: 40 22 * * 1-5 + desired_capacity: 2 + state: present + register: scheduled_action + check_mode: True + + - name: Check results - Create basic scheduled_action - check_mode + assert: + that: + - scheduled_action is successful + - scheduled_action is changed + + - name: Create basic scheduled_action + ec2_asg_scheduled_action: + autoscaling_group_name: "{{ resource_prefix }}-asg" + scheduled_action_name: "{{ resource_prefix }}-test" + start_time: 2022 October 25 08:00 UTC + recurrence: 40 22 * * 1-5 + desired_capacity: 2 + state: present + register: scheduled_action + + - name: Check results - Create basic scheduled_action + assert: + that: + - scheduled_action is successful + - scheduled_action is changed + - scheduled_action.scheduled_action_name == "{{ resource_prefix }}-test" + - scheduled_action.desired_capacity == 2 + + - name: Create basic scheduled_action - idempotent + ec2_asg_scheduled_action: + autoscaling_group_name: "{{ resource_prefix }}-asg" + scheduled_action_name: "{{ resource_prefix }}-test" + start_time: 2022 October 25 08:00 UTC + recurrence: 40 22 * * 1-5 + desired_capacity: 2 + state: present + register: scheduled_action + + - name: Check results - Create advanced scheduled_action - idempotent + assert: + that: + - scheduled_action is successful + - scheduled_action is not changed + + ## Update minimal basic scheduled action + - name: Update basic scheduled_action - check_mode + ec2_asg_scheduled_action: + autoscaling_group_name: "{{ resource_prefix }}-asg" + scheduled_action_name: "{{ resource_prefix }}-test" + start_time: 2022 October 25 08:00 UTC + recurrence: 40 22 * * 1-5 + desired_capacity: 3 + min_size: 3 + state: present + register: scheduled_action + check_mode: True + + - name: Check results - Update basic scheduled_action - check_mode + assert: + that: + - scheduled_action is successful + - scheduled_action is changed + + - name: Update basic scheduled_action + ec2_asg_scheduled_action: + autoscaling_group_name: "{{ resource_prefix }}-asg" + scheduled_action_name: "{{ resource_prefix }}-test" + start_time: 2022 October 25 08:00 UTC + recurrence: 40 22 * * 1-5 + desired_capacity: 3 + min_size: 3 + state: present + register: scheduled_action + + - name: Check results - Update basic scheduled_action + assert: + that: + - scheduled_action is successful + - scheduled_action is changed + - scheduled_action.scheduled_action_name == "{{ resource_prefix }}-test" + - scheduled_action.desired_capacity == 3 + - scheduled_action.min_size == 3 + + - name: Update basic scheduled_action - idempotent + ec2_asg_scheduled_action: + autoscaling_group_name: "{{ resource_prefix }}-asg" + scheduled_action_name: "{{ resource_prefix }}-test" + start_time: 2022 October 25 08:00 UTC + recurrence: 40 22 * * 1-5 + desired_capacity: 3 + min_size: 3 + state: present + register: scheduled_action + + - name: Check results - Update advanced scheduled_action - idempotent + assert: + that: + - scheduled_action is successful + - scheduled_action is not changed + + ## Create advanced scheduled action + - name: Create advanced scheduled_action - check_mode + ec2_asg_scheduled_action: + autoscaling_group_name: "{{ resource_prefix }}-asg" + scheduled_action_name: "{{ resource_prefix }}-test" + start_time: 2022 October 25 09:00 UTC + end_time: 2022 October 25 10:00 UTC + time_zone: Europe/London + recurrence: 40 22 * * 1-5 + min_size: 2 + max_size: 5 + desired_capacity: 2 + state: present + register: advanced_scheduled_action + check_mode: True + + - name: Check results - Create basic scheduled_action - check_mode + assert: + that: + - advanced_scheduled_action is successful + - advanced_scheduled_action is changed + + - name: Create advanced scheduled_action + ec2_asg_scheduled_action: + autoscaling_group_name: "{{ resource_prefix }}-asg" + scheduled_action_name: "{{ resource_prefix }}-test1" + start_time: 2022 October 25 09:00 UTC + end_time: 2022 October 25 10:00 UTC + time_zone: Europe/London + recurrence: 40 22 * * 1-5 + min_size: 2 + max_size: 5 + desired_capacity: 2 + state: present + register: advanced_scheduled_action + + - name: Check results - Create advanced scheduled_action + assert: + that: + - advanced_scheduled_action is successful + - advanced_scheduled_action is changed + - advanced_scheduled_action.scheduled_action_name == "{{ resource_prefix }}-test1" + - advanced_scheduled_action.desired_capacity == 2 + - advanced_scheduled_action.min_size == 2 + - advanced_scheduled_action.max_size == 5 + - advanced_scheduled_action.time_zone == "Europe/London" + + - name: Create advanced scheduled_action - idempotent + ec2_asg_scheduled_action: + autoscaling_group_name: "{{ resource_prefix }}-asg" + scheduled_action_name: "{{ resource_prefix }}-test1" + start_time: 2022 October 25 09:00 UTC + end_time: 2022 October 25 10:00 UTC + time_zone: Europe/London + recurrence: 40 22 * * 1-5 + min_size: 2 + max_size: 5 + desired_capacity: 2 + state: present + register: advanced_scheduled_action + + - name: Check results - Create basic scheduled_action - idempotent + assert: + that: + - advanced_scheduled_action is successful + - advanced_scheduled_action is not changed + + ## Delete scheduled action + - name: Delete scheduled_action - check_mode + ec2_asg_scheduled_action: + autoscaling_group_name: "{{ resource_prefix }}-asg" + scheduled_action_name: "{{ resource_prefix }}-test1" + state: absent + register: scheduled_action_deletion + check_mode: True + + - name: Delete scheduled_action - check_mode + assert: + that: + - scheduled_action_deletion is successful + - scheduled_action_deletion is changed + + - name: Delete scheduled_action + ec2_asg_scheduled_action: + autoscaling_group_name: "{{ resource_prefix }}-asg" + scheduled_action_name: "{{ resource_prefix }}-test1" + state: absent + register: scheduled_action_deletion + + - name: Delete scheduled_action + assert: + that: + - scheduled_action_deletion is successful + - scheduled_action_deletion is changed + + - name: Delete scheduled_action - idempotent + ec2_asg_scheduled_action: + autoscaling_group_name: "{{ resource_prefix }}-asg" + scheduled_action_name: "{{ resource_prefix }}-test1" + state: absent + register: scheduled_action_deletion + + - name: Delete scheduled_action - idempotent + assert: + that: + - scheduled_action_deletion is successful + - scheduled_action_deletion is not changed + always: + - name: Remove ASG + ec2_asg: + name: "{{ resource_prefix }}-asg" + state: absent + register: removed + until: removed is not failed + ignore_errors: yes + retries: 10 + + # Remove the testing dependencies + - name: Remove launch configs + ec2_lc: + name: "{{ resource_prefix }}-lc" + state: absent + register: removed + until: removed is not failed + ignore_errors: yes + retries: 10 + + - name: Remove the security group + ec2_group: + name: "{{ resource_prefix }}-sg" + description: a security group for ansible tests + vpc_id: "{{ testing_vpc.vpc.id }}" + state: absent + register: removed + until: removed is not failed + ignore_errors: yes + retries: 10 + + - name: Remove the subnet + ec2_vpc_subnet: + state: absent + vpc_id: "{{ testing_vpc.vpc.id }}" + cidr: 10.55.77.0/24 + register: removed + until: removed is not failed + ignore_errors: yes + retries: 10 + + - name: Remove the VPC + ec2_vpc_net: + name: "{{ resource_prefix }}-vpc" + cidr_block: 10.55.77.0/24 + state: absent + register: removed + until: removed is not failed + ignore_errors: yes + retries: 10 diff --git a/ansible_collections/community/aws/tests/integration/targets/aws_region_info/aliases b/ansible_collections/community/aws/tests/integration/targets/aws_region_info/aliases new file mode 100644 index 000000000..4ef4b2067 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/aws_region_info/aliases @@ -0,0 +1 @@ +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/aws_region_info/main.yml b/ansible_collections/community/aws/tests/integration/targets/aws_region_info/main.yml new file mode 100644 index 000000000..abffda916 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/aws_region_info/main.yml @@ -0,0 +1,5 @@ +- hosts: localhost + connection: local + environment: "{{ ansible_test.environment }}" + tasks: + - include_tasks: 'tasks/tests.yml' diff --git a/ansible_collections/community/aws/tests/integration/targets/aws_region_info/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/aws_region_info/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/aws_region_info/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/aws_region_info/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/aws_region_info/tasks/main.yml new file mode 100644 index 000000000..3edbbaded --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/aws_region_info/tasks/main.yml @@ -0,0 +1,107 @@ +--- +- module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key | default(omit) }}' + aws_secret_key: '{{ aws_secret_key | default(omit) }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region | default(omit) }}' + + block: + - name: 'List available Regions' + aws_region_info: + register: regions + + - name: check task return attributes + vars: + first_region: '{{ regions.regions[0] }}' + assert: + that: + - regions is successful + - regions is not changed + - '"regions" in regions' + - '"endpoint" in first_region' + - '"opt_in_status" in first_region' + - '"region_name" in first_region' + + - name: 'List available Regions - check_mode' + aws_region_info: + register: check_regions + + - name: check task return attributes - check_mode + vars: + first_region: '{{ check_regions.regions[0] }}' + assert: + that: + - check_regions is successful + - check_regions is not changed + - '"regions" in check_regions' + - '"endpoint" in first_region' + - '"opt_in_status" in first_region' + - '"region_name" in first_region' + + - name: 'Filter available Regions using - ("region-name")' + aws_region_info: + filters: + region-name: 'us-west-1' + register: us_west_1 + + - name: check task return attributes - filtering using - + vars: + first_region: '{{ us_west_1.regions[0] }}' + assert: + that: + - us_west_1 is successful + - us_west_1 is not changed + - '"regions" in us_west_1' + - us_west_1.regions | length == 1 + - '"endpoint" in first_region' + - first_region.endpoint == 'ec2.us-west-1.amazonaws.com' + - '"opt_in_status" in first_region' + - first_region.opt_in_status == 'opt-in-not-required' + - '"region_name" in first_region' + - first_region.region_name == 'us-west-1' + + - name: 'Filter available Regions using _ ("region_name")' + aws_region_info: + filters: + region_name: 'us-west-2' + register: us_west_2 + + - name: check task return attributes - filtering using _ + vars: + first_region: '{{ us_west_2.regions[0] }}' + assert: + that: + - us_west_2 is successful + - us_west_2 is not changed + - '"regions" in us_west_2' + - us_west_2.regions | length == 1 + - '"endpoint" in first_region' + - first_region.endpoint == 'ec2.us-west-2.amazonaws.com' + - '"opt_in_status" in first_region' + - first_region.opt_in_status == 'opt-in-not-required' + - '"region_name" in first_region' + - first_region.region_name == 'us-west-2' + + - name: 'Filter available Regions using _ and - to check precedence' + aws_region_info: + filters: + region-name: 'eu-west-1' + region_name: 'eu-central-1' + register: regions_prededence + + - name: check task return attributes - precedence + vars: + first_region: '{{ regions_prededence.regions[0] }}' + assert: + that: + - regions_prededence is successful + - regions_prededence is not changed + - '"regions" in regions_prededence' + - regions_prededence.regions | length == 1 + - '"endpoint" in first_region' + - first_region.endpoint == 'ec2.eu-central-1.amazonaws.com' + - '"opt_in_status" in first_region' + - first_region.opt_in_status == 'opt-in-not-required' + - '"region_name" in first_region' + - first_region.region_name == 'eu-central-1' diff --git a/ansible_collections/community/aws/tests/integration/targets/cloudformation_exports_info/aliases b/ansible_collections/community/aws/tests/integration/targets/cloudformation_exports_info/aliases new file mode 100644 index 000000000..4ef4b2067 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/cloudformation_exports_info/aliases @@ -0,0 +1 @@ +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/cloudformation_exports_info/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/cloudformation_exports_info/defaults/main.yml new file mode 100644 index 000000000..4edd7475e --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/cloudformation_exports_info/defaults/main.yml @@ -0,0 +1 @@ +stack_name: "{{ resource_prefix }}" diff --git a/ansible_collections/community/aws/tests/integration/targets/cloudformation_exports_info/files/test_stack.yml b/ansible_collections/community/aws/tests/integration/targets/cloudformation_exports_info/files/test_stack.yml new file mode 100644 index 000000000..f1dcba303 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/cloudformation_exports_info/files/test_stack.yml @@ -0,0 +1,24 @@ +AWSTemplateFormatVersion: 2010-09-09 +Description: Create some item in Exports +Parameters: + TestParamValue: + Type: String + Description: A param Value to be placed in Exports + TestParamName: + Type: String + Description: A param Name for SSM Parameter Store + BucketSuffix: + Type: String +Resources: + TestBucket: + Type: AWS::S3::Bucket + Properties: + BucketName: + Fn::Sub: "cf-export-${BucketSuffix}" +Outputs: + TestParamValue: + Value: + Ref: TestParamValue + Export: + Name: + Fn::Sub: "${TestParamName}" diff --git a/ansible_collections/community/aws/tests/integration/targets/cloudformation_exports_info/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/cloudformation_exports_info/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/cloudformation_exports_info/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/cloudformation_exports_info/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/cloudformation_exports_info/tasks/main.yml new file mode 100644 index 000000000..eb703d49e --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/cloudformation_exports_info/tasks/main.yml @@ -0,0 +1,41 @@ +- name: set connection information for aws modules and run tasks + module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + collections: + - amazon.aws + + block: + - name: Create a minimal stack with an export set by parameter + cloudformation: + stack_name: "{{ stack_name }}" + template_body: "{{ lookup('file','test_stack.yml') }}" + template_parameters: + TestParamName: "cf-exports-param" + TestParamValue: "Set By CF Exports" + BucketSuffix: "{{ resource_prefix }}" + register: cf_stack + - name: Read from Exports + cloudformation_exports_info: + region: "{{ aws_region }}" + register: exports_result + - set_fact: + export_items: "{{ exports_result['export_items'] }}" + - assert: + that: + - export_items is defined + - export_items['cf-exports-param'] is defined + # - export_items | length == 1 + + +# Cleanup + always: + + - name: delete stack + cloudformation: + stack_name: "{{ stack_name }}" + state: absent + ignore_errors: yes diff --git a/ansible_collections/community/aws/tests/integration/targets/cloudformation_stack_set/aliases b/ansible_collections/community/aws/tests/integration/targets/cloudformation_stack_set/aliases new file mode 100644 index 000000000..47eb21d53 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/cloudformation_stack_set/aliases @@ -0,0 +1,5 @@ +# reason: broken +# Tests rely on additional non-standard secondary_aws_ credentials +disabled + +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/cloudformation_stack_set/files/test_bucket_stack.yml b/ansible_collections/community/aws/tests/integration/targets/cloudformation_stack_set/files/test_bucket_stack.yml new file mode 100644 index 000000000..dfbc52241 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/cloudformation_stack_set/files/test_bucket_stack.yml @@ -0,0 +1,6 @@ +AWSTemplateFormatVersion: "2010-09-09" +Parameters: {} +Resources: + Bukkit: + Type: "AWS::S3::Bucket" + Properties: {} diff --git a/ansible_collections/community/aws/tests/integration/targets/cloudformation_stack_set/files/test_modded_bucket_stack.yml b/ansible_collections/community/aws/tests/integration/targets/cloudformation_stack_set/files/test_modded_bucket_stack.yml new file mode 100644 index 000000000..68df61c61 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/cloudformation_stack_set/files/test_modded_bucket_stack.yml @@ -0,0 +1,9 @@ +AWSTemplateFormatVersion: "2010-09-09" +Parameters: {} +Resources: + Bukkit: + Type: "AWS::S3::Bucket" + Properties: {} + other: + Type: "AWS::SNS::Topic" + Properties: {} diff --git a/ansible_collections/community/aws/tests/integration/targets/cloudformation_stack_set/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/cloudformation_stack_set/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/cloudformation_stack_set/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/cloudformation_stack_set/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/cloudformation_stack_set/tasks/main.yml new file mode 100644 index 000000000..afd614a55 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/cloudformation_stack_set/tasks/main.yml @@ -0,0 +1,190 @@ +--- +# tasks file for cloudformation_stack_set module tests +# These tests require access to two separate AWS accounts + +- name: set up aws connection info + set_fact: + aws_connection_info: &aws_connection_info + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token }}" + region: "{{ aws_region }}" + aws_secondary_connection_info: &aws_secondary_connection_info + aws_access_key: "{{ secondary_aws_access_key }}" + aws_secret_key: "{{ secondary_aws_secret_key }}" + security_token: "{{ secondary_security_token }}" + region: "{{ aws_region }}" + no_log: true + +- name: cloudformation_stack_set tests + collections: + - amazon.aws + + block: + - name: Get current account ID + aws_caller_info: + <<: *aws_connection_info + register: whoami + - name: Get current account ID + aws_caller_info: + <<: *aws_secondary_connection_info + register: target_acct + + - name: Policy to allow assuming stackset execution role + iam_managed_policy: + policy_name: AssumeCfnStackSetExecRole + state: present + <<: *aws_connection_info + policy: + Version: '2012-10-17' + Statement: + - Action: 'sts:AssumeRole' + Effect: Allow + Resource: arn:aws:iam::*:role/CfnStackSetExecRole + policy_description: Assume CfnStackSetExecRole + + - name: Create an execution role for us to use + iam_role: + name: CfnStackSetExecRole + <<: *aws_secondary_connection_info + assume_role_policy_document: + Version: '2012-10-17' + Statement: + - Action: 'sts:AssumeRole' + Effect: Allow + Principal: + AWS: '{{ whoami.account }}' + managed_policy: + - arn:aws:iam::aws:policy/PowerUserAccess + + - name: Create an administration role for us to use + iam_role: + name: CfnStackSetAdminRole + <<: *aws_connection_info + assume_role_policy_document: + Version: '2012-10-17' + Statement: + - Action: 'sts:AssumeRole' + Effect: Allow + Principal: + Service: 'cloudformation.amazonaws.com' + managed_policy: + - arn:aws:iam::{{ whoami.account }}:policy/AssumeCfnStackSetExecRole + #- arn:aws:iam::aws:policy/PowerUserAccess + + - name: Should fail without account/regions + cloudformation_stack_set: + <<: *aws_connection_info + name: TestSetOne + description: TestStack Prime + tags: + Some: Thing + Type: Test + wait: true + template: test_bucket_stack.yml + register: result + ignore_errors: true + - name: assert that running with no account fails + assert: + that: + - result is failed + - > + "Can't create a stack set without choosing at least one account" in result.msg + - name: Should fail without roles + cloudformation_stack_set: + <<: *aws_connection_info + name: TestSetOne + description: TestStack Prime + tags: + Some: Thing + Type: Test + wait: true + regions: + - '{{ aws_region }}' + accounts: + - '{{ whoami.account }}' + template_body: '{{ lookup("file", "test_bucket_stack.yml") }}' + register: result + ignore_errors: true + - name: assert that running with no account fails + assert: + that: + - result is failed + + - name: Create an execution role for us to use + iam_role: + name: CfnStackSetExecRole + state: absent + <<: *aws_connection_info + assume_role_policy_document: + Version: '2012-10-17' + Statement: + - Action: 'sts:AssumeRole' + Effect: Allow + Principal: + AWS: arn:aws:iam::{{ whoami.account }}:root + managed_policy: + - arn:aws:iam::aws:policy/PowerUserAccess + + - name: Create stack with roles + cloudformation_stack_set: + <<: *aws_connection_info + name: TestSetTwo + description: TestStack Dos + tags: + Some: Thing + Type: Test + wait: true + regions: + - '{{ aws_region }}' + accounts: + - '{{ target_acct.account }}' + exec_role_name: CfnStackSetExecRole + admin_role_arn: arn:aws:iam::{{ whoami.account }}:role/CfnStackSetAdminRole + template_body: '{{ lookup("file", "test_bucket_stack.yml") }}' + register: result + + - name: Update stack with roles + cloudformation_stack_set: + <<: *aws_connection_info + name: TestSetTwo + description: TestStack Dos + tags: + Some: Thing + Type: Test + wait: true + regions: + - '{{ aws_region }}' + accounts: + - '{{ target_acct.account }}' + exec_role_name: CfnStackSetExecRole + admin_role_arn: arn:aws:iam::{{ whoami.account }}:role/CfnStackSetAdminRole + template_body: '{{ lookup("file", "test_modded_bucket_stack.yml") }}' + always: + - name: Clean up stack one + cloudformation_stack_set: + <<: *aws_connection_info + name: TestSetOne + wait: true + regions: + - '{{ aws_region }}' + accounts: + - '{{ whoami.account }}' + purge_stacks: true + state: absent + - name: Clean up stack two + cloudformation_stack_set: + <<: *aws_connection_info + name: TestSetTwo + description: TestStack Dos + purge_stacks: true + tags: + Some: Thing + Type: Test + wait: true + regions: + - '{{ aws_region }}' + accounts: + - '{{ target_acct.account }}' + template_body: '{{ lookup("file", "test_bucket_stack.yml") }}' + state: absent diff --git a/ansible_collections/community/aws/tests/integration/targets/cloudfront_distribution/aliases b/ansible_collections/community/aws/tests/integration/targets/cloudfront_distribution/aliases new file mode 100644 index 000000000..e04e1b287 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/cloudfront_distribution/aliases @@ -0,0 +1,4 @@ +# reason: broken +disabled + +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/cloudfront_distribution/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/cloudfront_distribution/defaults/main.yml new file mode 100644 index 000000000..b88dbc244 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/cloudfront_distribution/defaults/main.yml @@ -0,0 +1,49 @@ +cloudfront_hostname: "{{ resource_prefix }}01" + +# Use a domain that has a wildcard DNS +# Using an alias requires also having an SSL cert... +#cloudfront_alias: "{{ cloudfront_hostname }}.github.io" +#cloudfront_viewer_cert: +# acm_certificate_arn: ... +# certificate: ... +# certificate_source: ... +# minimum_protocol_version: ... +# ssl_support_method: ... + +cloudfront_test_cache_behaviors: + - path_pattern: /test/path + forwarded_values: + headers: + - Host + - X-HTTP-Forwarded-For + - CloudFront-Forwarded-Proto + - Origin + - Referer + allowed_methods: + items: + - GET + - HEAD + - POST + - PATCH + - PUT + - OPTIONS + - DELETE + cached_methods: + - GET + - HEAD + - path_pattern: /another/path + forwarded_values: + cookies: + forward: whitelist + whitelisted_names: + - my_header + query_string: yes + query_string_cache_keys: + - whatever + allowed_methods: + items: + - GET + - HEAD + cached_methods: + - GET + - HEAD diff --git a/ansible_collections/community/aws/tests/integration/targets/cloudfront_distribution/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/cloudfront_distribution/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/cloudfront_distribution/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/cloudfront_distribution/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/cloudfront_distribution/tasks/main.yml new file mode 100644 index 000000000..a6ac0571a --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/cloudfront_distribution/tasks/main.yml @@ -0,0 +1,514 @@ +- module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + cloudfront_distribution: + alias: "{{ cloudfront_alias | default(omit) }}" + viewer_certificate: "{{ cloudfront_viewer_cert | default(omit) }}" + collections: + - amazon.aws + + block: + + - name: create cloudfront distribution using defaults + cloudfront_distribution: + origins: + - domain_name: "{{ cloudfront_hostname }}-origin.example.com" + id: "{{ cloudfront_hostname }}-origin.example.com" + default_cache_behavior: + target_origin_id: "{{ cloudfront_hostname }}-origin.example.com" + state: present + purge_origins: yes + register: cf_distribution + + - set_fact: + distribution_id: '{{ cf_distribution.id }}' + + - name: ensure that default value of 'ipv6_enabled' is 'false' + assert: + that: + - cf_distribution.changed + - not cf_distribution.is_ipv6_enabled + + - name: Update the distribution with tags + cloudfront_distribution: + state: present + distribution_id: "{{ distribution_id }}" + tags: + property: ansible + register: cf_update_tags + + - name: ensure the 'ipv6_enabled' value has not changed + assert: + that: + - cf_update_tags.changed + - not cf_update_tags.is_ipv6_enabled + + - name: Update the distribution ipv6_enabled set to true + cloudfront_distribution: + state: present + distribution_id: "{{ distribution_id }}" + ipv6_enabled: True + register: cf_update_ipv6 + + - name: ensure the 'ipv6_enabled' value has changed (new value is true) + assert: + that: + - cf_update_ipv6.changed + - cf_update_ipv6.is_ipv6_enabled + + - name: Update the distribution with tags + cloudfront_distribution: + state: present + distribution_id: "{{ distribution_id }}" + tags: + test: integration + register: cf_update_tags + + - name: ensure the 'ipv6_enabled' value has not changed (value remains true) + assert: + that: + - cf_update_tags.changed + - cf_update_tags.is_ipv6_enabled + + - name: Test idempotency updating cloudfront_distribution with same value of ipv6_enabled + cloudfront_distribution: + state: present + distribution_id: "{{ distribution_id }}" + ipv6_enabled: True + register: cf_update_ipv6 + + - name: ensure the 'ipv6_enabled' value has changed (new value is true) + assert: + that: + # FixMe : idempotency issues + # - not cf_update_ipv6.changed + - cf_update_ipv6.is_ipv6_enabled + + - name: re-run cloudfront distribution with same defaults + cloudfront_distribution: + distribution_id: "{{ distribution_id }}" + origins: + - domain_name: "{{ cloudfront_hostname }}-origin.example.com" + state: present + register: cf_dist_no_update + + - name: ensure distribution was not updated + assert: + that: + - not cf_dist_no_update.changed + + - name: re-run cloudfront distribution using distribution id + cloudfront_distribution: + distribution_id: "{{ distribution_id }}" + purge_origins: no + state: present + register: cf_dist_with_id + + - name: ensure distribution was not updated + assert: + that: + - not cf_dist_with_id.changed + + - name: update origin http port + cloudfront_distribution: + distribution_id: "{{ distribution_id }}" + origins: + - domain_name: "{{ cloudfront_hostname }}-origin.example.com" + custom_origin_config: + http_port: 8080 + state: present + register: update_origin_http_port + + - name: ensure http port was updated + assert: + that: + - update_origin_http_port.changed + + - name: update restrictions + cloudfront_distribution: + distribution_id: "{{ distribution_id }}" + restrictions: + geo_restriction: + restriction_type: "whitelist" + items: + - "US" + state: present + register: update_restrictions + + - name: ensure restrictions was updated + assert: + that: + - update_restrictions.changed + + - name: set a random comment + set_fact: + comment: "{{'ABCDEFabcdef123456'|shuffle|join }}" + + - name: update comment + cloudfront_distribution: + distribution_id: "{{ distribution_id }}" + comment: "{{ comment }}" + state: present + register: cf_comment + + - name: ensure comment was updated + assert: + that: + - cf_comment.changed + - 'cf_comment.comment == comment' + + - name: create second origin + cloudfront_distribution: + distribution_id: "{{ distribution_id }}" + origins: + - domain_name: "{{ resource_prefix }}2.example.com" + id: "{{ resource_prefix }}2.example.com" + default_root_object: index.html + state: present + wait: yes + register: cf_add_origin + + - name: ensure origin was added + assert: + that: + - cf_add_origin.origins.quantity == 2 + - cf_add_origin.changed + - "cf_add_origin.default_root_object == 'index.html'" + + - name: re-run second origin + cloudfront_distribution: + distribution_id: "{{ distribution_id }}" + origins: + - domain_name: "{{ cloudfront_hostname }}-origin.example.com" + custom_origin_config: + http_port: 8080 + - domain_name: "{{ resource_prefix }}2.example.com" + default_root_object: index.html + wait: yes + state: present + register: cf_rerun_second_origin + + - name: ensure nothing changed after re-run + assert: + that: + - cf_rerun_second_origin.origins.quantity == 2 + - not cf_rerun_second_origin.changed + + - name: run with origins in reverse order + cloudfront_distribution: + distribution_id: "{{ distribution_id }}" + origins: + - domain_name: "{{ resource_prefix }}2.example.com" + - domain_name: "{{ cloudfront_hostname }}-origin.example.com" + custom_origin_config: + http_port: 8080 + state: present + register: cf_rerun_second_origin_reversed + + - name: ensure nothing changed after reversed re-run + assert: + that: + - cf_rerun_second_origin_reversed.origins.quantity == 2 + - not cf_rerun_second_origin_reversed.changed + + + - name: purge first origin + cloudfront_distribution: + distribution_id: "{{ distribution_id }}" + origins: + - domain_name: "{{ resource_prefix }}2.example.com" + default_cache_behavior: + target_origin_id: "{{ resource_prefix }}2.example.com" + purge_origins: yes + state: present + register: cf_purge_origin + + - name: ensure origin was removed + assert: + that: + - cf_purge_origin.origins.quantity == 1 + - cf_purge_origin.changed + + - name: update default_root_object of existing distribution + cloudfront_distribution: + distribution_id: "{{ distribution_id }}" + origins: + - domain_name: "{{ resource_prefix }}2.example.com" + default_root_object: index.php + state: present + register: cf_update_default_root_object + + - name: update default_root_object of existing distribution with retries + cloudfront_distribution: + distribution_id: "{{ distribution_id }}" + origins: + - domain_name: "{{ resource_prefix }}2.example.com" + default_root_object: index.php + state: present + retries: 3 + delay: 3 + register: cf_update_default_root_object + + - name: ensure origin was updated + assert: + that: + - "cf_update_default_root_object.default_root_object == 'index.php'" + - cf_update_default_root_object.changed + + - name: add tags to existing distribution + cloudfront_distribution: + distribution_id: "{{ distribution_id }}" + origins: + - domain_name: "{{ resource_prefix }}2.example.com" + tags: + ATag: tag1 + Another: tag + default_root_object: index.php + state: present + register: cf_add_tags + + - name: ensure tags were added + assert: + that: + - cf_add_tags.changed + - cf_add_tags.tags|length == 2 + + - name: delete distribution + cloudfront_distribution: + distribution_id: "{{ distribution_id }}" + enabled: no + wait: yes + state: absent + + - name: create distribution with tags + cloudfront_distribution: + origins: + - domain_name: "{{ resource_prefix }}2.example.com" + id: "{{ resource_prefix }}2.example.com" + tags: + ATag: tag1 + Another: tag + state: present + register: cf_second_distribution + + - set_fact: + distribution_id: '{{ cf_second_distribution.id }}' + + - name: ensure tags were set on creation + assert: + that: + - cf_second_distribution.changed + - cf_second_distribution.tags|length == 2 + - "'ATag' in cf_second_distribution.tags" + - "'Another' in cf_second_distribution.tags" + + - name: re-run create distribution with same tags and purge_tags + cloudfront_distribution: + distribution_id: "{{ distribution_id }}" + origins: + - domain_name: "{{ resource_prefix }}2.example.com" + id: "{{ resource_prefix }}2.example.com" + tags: + ATag: tag1 + Another: tag + purge_tags: yes + state: present + register: rerun_with_purge_tags + + - name: ensure that re-running didn't change + assert: + that: + - not rerun_with_purge_tags.changed + - rerun_with_purge_tags.tags|length == 2 + + - name: add new tag to distribution + cloudfront_distribution: + distribution_id: "{{ distribution_id }}" + origins: + - domain_name: "{{ resource_prefix }}2.example.com" + tags: + Third: thing + purge_tags: no + state: present + register: update_with_new_tag + + - name: ensure tags are correct + assert: + that: + - update_with_new_tag.changed + - "'Third' in update_with_new_tag.tags" + - "'Another' in update_with_new_tag.tags" + - "'ATag' in update_with_new_tag.tags" + - update_with_new_tag.tags|length == 3 + + - name: create some cache behaviors + cloudfront_distribution: + distribution_id: "{{ distribution_id }}" + origins: + - domain_name: "{{ resource_prefix }}2.example.com" + cache_behaviors: "{{ cloudfront_test_cache_behaviors }}" + state: present + register: add_cache_behaviors + + - name: reverse some cache behaviors + cloudfront_distribution: + distribution_id: "{{ distribution_id }}" + origins: + - domain_name: "{{ resource_prefix }}2.example.com" + cache_behaviors: "{{ cloudfront_test_cache_behaviors|reverse|list }}" + state: present + register: reverse_cache_behaviors + + - name: check that reversing cache behaviors changes nothing when purge_cache_behaviors unset + assert: + that: + - not reverse_cache_behaviors.changed + - reverse_cache_behaviors.cache_behaviors|length == 2 + + - name: reverse some cache behaviors properly + cloudfront_distribution: + distribution_id: "{{ distribution_id }}" + origins: + - domain_name: "{{ resource_prefix }}2.example.com" + cache_behaviors: "{{ cloudfront_test_cache_behaviors|reverse|list }}" + purge_cache_behaviors: yes + state: present + register: reverse_cache_behaviors_with_purge + + - name: check that reversing cache behaviors changes nothing when purge_cache_behaviors unset + assert: + that: + - reverse_cache_behaviors_with_purge.changed + - reverse_cache_behaviors_with_purge.cache_behaviors|length == 2 + + - name: update origin that changes target id (failure expected) + cloudfront_distribution: + distribution_id: "{{ distribution_id }}" + origins: + - domain_name: "{{ resource_prefix }}3.example.com" + id: "{{ resource_prefix }}3.example.com" + purge_origins: yes + state: present + register: remove_origin_in_use + ignore_errors: yes + + - name: check that removing in use origin fails + assert: + that: + - remove_origin_in_use.failed + + # FIXME: This currently fails due to AWS side problems + # not clear whether to hope they fix or prevent this issue from happening + #- name: update origin and update cache behavior to point to new origin + # cloudfront_distribution: + # origins: + # - domain_name: "{{ resource_prefix }}3.example.com" + # id: "{{ resource_prefix }}3.example.com" + # cache_behaviors: + # - path_pattern: /test/path + # target_origin_id: "{{ resource_prefix }}3.example.com" + # - path_pattern: /another/path + # target_origin_id: "{{ resource_prefix }}3.example.com" + # state: present + # aws_access_key: "{{ aws_access_key|default(omit) }}" + # aws_secret_key: "{{ aws_secret_key|default(omit) }}" + # security_token: "{{ security_token|default(omit) }}" + # profile: "{{ profile|default(omit) }}" + # register: update_cache_behaviors in use + + - name: create an s3 bucket for next test + # note that although public-read allows reads that we want to stop with origin_access_identity, + # we also need to test without origin_access_identity and it's hard to change bucket perms later + aws_s3: + bucket: "{{ resource_prefix }}-bucket" + mode: create + + - name: update origin to point to the s3 bucket + cloudfront_distribution: + distribution_id: "{{ distribution_id }}" + origins: + - domain_name: "{{ resource_prefix }}-bucket.s3.amazonaws.com" + id: "{{ resource_prefix }}3.example.com" + s3_origin_access_identity_enabled: yes + state: present + register: update_origin_to_s3 + + - name: check that s3 origin access is in result + assert: + that: + - item.s3_origin_config.origin_access_identity.startswith('origin-access-identity/cloudfront/') + when: "'s3_origin_config' in item" + loop: "{{ update_origin_to_s3.origins['items'] }}" + + - name: update origin to remove s3 origin access identity + cloudfront_distribution: + distribution_id: "{{ distribution_id }}" + origins: + - domain_name: "{{ resource_prefix }}-bucket.s3.amazonaws.com" + id: "{{ resource_prefix }}3.example.com" + s3_origin_access_identity_enabled: no + state: present + register: update_origin_to_s3_without_origin_access + + - name: check that s3 origin access is not in result + assert: + that: + - not item.s3_origin_config.origin_access_identity + when: "'s3_origin_config' in item" + loop: "{{ update_origin_to_s3_without_origin_access.origins['items'] }}" + + - name: delete the s3 bucket + aws_s3: + bucket: "{{ resource_prefix }}-bucket" + mode: delete + + - name: check that custom_origin_config can't be used with origin_access_identity enabled + cloudfront_distribution: + distribution_id: "{{ distribution_id }}" + origins: + - domain_name: "{{ resource_prefix }}-bucket.s3.amazonaws.com" + id: "{{ resource_prefix }}3.example.com" + s3_origin_access_identity_enabled: yes + custom_origin_config: + origin_protocol_policy: 'http-only' + state: present + register: update_origin_to_s3_with_origin_access_and_with_custom_origin_config + ignore_errors: True + + - name: check that custom origin with origin access identity fails + assert: + that: + - update_origin_to_s3_with_origin_access_and_with_custom_origin_config.failed + + - name: Update distribution to use specific access identity + cloudfront_distribution: + distribution_id: "{{ distribution_id }}" + origins: + - id: "{{ resource_prefix }}" + domain_name: "{{ resource_prefix }}.s3.amazonaws.com" + s3_origin_access_identity_enabled: true + s3_origin_config: + origin_access_identity: origin-access-identity/cloudfront/ANYTHING + register: update_distribution_with_specific_access_identity + + - name: check that custom origin uses the provided origin_access_identity + assert: + that: + - update_distribution_with_specific_access_identity.changed + - update_distribution_with_specific_access_identity.origins.items[0].s3_origin_config.origin_access_identity == 'origin-access-identity/cloudfront/ANYTHING' + + always: + # TEARDOWN STARTS HERE + - name: delete the s3 bucket + aws_s3: + bucket: "{{ resource_prefix }}-bucket" + mode: delete + + - name: clean up cloudfront distribution + cloudfront_distribution: + distribution_id: "{{ distribution_id }}" + enabled: no + wait: yes + state: absent diff --git a/ansible_collections/community/aws/tests/integration/targets/cloudfront_reponse_headers_policy/aliases b/ansible_collections/community/aws/tests/integration/targets/cloudfront_reponse_headers_policy/aliases new file mode 100644 index 000000000..4ef4b2067 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/cloudfront_reponse_headers_policy/aliases @@ -0,0 +1 @@ +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/cloudfront_reponse_headers_policy/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/cloudfront_reponse_headers_policy/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/cloudfront_reponse_headers_policy/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/cloudfront_reponse_headers_policy/task/main.yml b/ansible_collections/community/aws/tests/integration/targets/cloudfront_reponse_headers_policy/task/main.yml new file mode 100644 index 000000000..ee30f5ab5 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/cloudfront_reponse_headers_policy/task/main.yml @@ -0,0 +1,96 @@ +--- + +- name: Integration testing for the cloudfront_response_headers_policy module + module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + block: + + - name: Create a simple header policy + cloudfront_response_headers_policy: + name: "{{ resource_prefix }}-my-header-policy" + comment: Created by Ansible test + security_headers_config: + content_type_options: + override: true + state: present + register: create_result + + - name: Assert creation withouth errors and return values + assert: + that: + - create_result is changed + - create_result is not failed + - create_result.response_headers_policy.response_headers_policy_config.name == "{{ resource_prefix }}-my-header-policy" + + - name: Rerun same task to ensure idempotence + cloudfront_response_headers_policy: + name: "{{ resource_prefix }}-my-header-policy" + comment: Created by Ansible test + security_headers_config: + content_type_options: + override: true + state: present + register: rerun_result + + - name: Assert no change and no errors + assert: + that: + - rerun_result is not changed + - rerun_result is not failed + + - name: Update existing policy with more header configs + cloudfront_response_headers_policy: + name: "{{ resource_prefix }}-my-header-policy" + comment: Created by Ansible test + cors_config: + access_control_allow_origins: + items: + - 'https://foo.com/bar' + - 'https://bar.com/foo' + access_control_allow_methods: + items: + - OPTIONS + - HEAD + - GET + access_control_max_age_sec: 1800 + origin_override: true + security_headers_config: + content_type_options: + override: true + custom_headers_config: + items: + - { header: 'X-Test-Header', value: 'Foo', override: true } + state: present + register: update_result + + - name: Assert update and updated return values + assert: + that: + - update_result is changed + - update_result.response_headers_policy.response_headers_policy_config.cors_config.access_control_max_age_sec == 1800 + + - name: Ensure policy is deleted + cloudfront_response_headers_policy: + name: "{{ resource_prefix }}-my-header-policy" + comment: Created by Ansible test + state: absent + register: delete_result + + - name: Assert deletion without errors + assert: + that: + - delete_result is changed + - delete_result is not failed + - update_result.response_headers_policy is undefined + + always: + + - name: Ensure policy is deleted + cloudfront_response_headers_policy: + name: "{{ resource_prefix }}-my-header-policy" + state: absent + ignore_errors: true diff --git a/ansible_collections/community/aws/tests/integration/targets/codebuild_project/aliases b/ansible_collections/community/aws/tests/integration/targets/codebuild_project/aliases new file mode 100644 index 000000000..4ef4b2067 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/codebuild_project/aliases @@ -0,0 +1 @@ +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/codebuild_project/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/codebuild_project/defaults/main.yml new file mode 100644 index 000000000..663264d55 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/codebuild_project/defaults/main.yml @@ -0,0 +1,7 @@ +--- +# defaults file for aws_codebuild + +# IAM role names have to be less than 64 characters +# we hash the resource_prefix to get a shorter, unique string +iam_role_name: "ansible-test-{{ tiny_prefix }}-codebuild-service-role" +project_name: "{{ resource_prefix }}-codebuild" diff --git a/ansible_collections/community/aws/tests/integration/targets/codebuild_project/files/codebuild_iam_trust_policy.json b/ansible_collections/community/aws/tests/integration/targets/codebuild_project/files/codebuild_iam_trust_policy.json new file mode 100644 index 000000000..3af7c6412 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/codebuild_project/files/codebuild_iam_trust_policy.json @@ -0,0 +1,12 @@ +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Service": "codebuild.amazonaws.com" + }, + "Action": "sts:AssumeRole" + } + ] +} diff --git a/ansible_collections/community/aws/tests/integration/targets/codebuild_project/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/codebuild_project/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/codebuild_project/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/codebuild_project/tasks/description.yml b/ansible_collections/community/aws/tests/integration/targets/codebuild_project/tasks/description.yml new file mode 100644 index 000000000..13c12b5b6 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/codebuild_project/tasks/description.yml @@ -0,0 +1,125 @@ +- name: Tests relating to setting description on aws_codebuild + vars: + description_one: 'a Description - {{ resource_prefix }}' + description_two: 'Another_Description - {{ resource_prefix }}' + # Mandatory settings + module_defaults: + community.aws.aws_codebuild: + name: '{{ project_name }}' +# community.aws.aws_codebuild_info: +# name: '{{ project_name }}' + block: + +# - name: test setting description aws_codebuild (check mode) +# aws_codebuild: +# description: '{{ description_one }}' +# register: update_result +# check_mode: yes +# - name: assert that update succeeded +# assert: +# that: +# - update_result is changed + + - name: test setting description aws_codebuild + aws_codebuild: + description: '{{ description_one }}' + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is changed + - update_result.project.description == description_one + +# - name: test setting description aws_codebuild - idempotency (check mode) +# aws_codebuild: +# description: '{{ description_one }}' +# register: update_result +# check_mode: yes +# - name: assert that update succeeded +# assert: +# that: +# - update_result is not changed + + - name: test setting description aws_codebuild - idempotency + aws_codebuild: + description: '{{ description_one }}' + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is not changed + - update_result.project.description == description_one + + ### + +# - name: test updating description on aws_codebuild (check mode) +# aws_codebuild: +# description: '{{ description_two }}' +# register: update_result +# check_mode: yes +# - name: assert that update succeeded +# assert: +# that: +# - update_result is changed + + - name: test updating description on aws_codebuild + aws_codebuild: + description: '{{ description_two }}' + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is changed + - update_result.project.description == description_two + +# - name: test updating description on aws_codebuild - idempotency (check mode) +# aws_codebuild: +# description: '{{ description_two }}' +# register: update_result +# check_mode: yes +# - name: assert that update succeeded +# assert: +# that: +# - update_result is not changed + + - name: test updating description on aws_codebuild - idempotency + aws_codebuild: + description: '{{ description_two }}' + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is not changed + - update_result.project.description == description_two + +# ### +# +# - name: test that aws_codebuild_info returns the description +# aws_codebuild_info: +# register: tag_info +# - name: assert description present +# assert: +# that: +# - tag_info.project.description == description_two +# +# ### + +# - name: test no description param aws_codebuild (check mode) +# aws_codebuild: {} +# register: update_result +# check_mode: yes +# - name: assert no change +# assert: +# that: +# - update_result is not changed +# - update_result.project.description == description_two + + + - name: test no description param aws_codebuild + aws_codebuild: {} + register: update_result + - name: assert no change + assert: + that: + - update_result is not changed + - update_result.project.description == description_two diff --git a/ansible_collections/community/aws/tests/integration/targets/codebuild_project/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/codebuild_project/tasks/main.yml new file mode 100644 index 000000000..f674aba24 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/codebuild_project/tasks/main.yml @@ -0,0 +1,115 @@ +--- +- name: 'aws_codebuild integration tests' + collections: + - amazon.aws + module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + block: + + # ==================== preparations ======================================== + + - name: create IAM role needed for CodeBuild + iam_role: + name: "{{ iam_role_name }}" + description: Role with permissions for CodeBuild actions. + assume_role_policy_document: "{{ lookup('file', 'codebuild_iam_trust_policy.json') }}" + state: present + register: codebuild_iam_role + + - name: Set variable with aws account id + set_fact: + aws_account_id: "{{ codebuild_iam_role.iam_role.arn.split(':')[4] }}" + + # ================== integration test ========================================== + + - name: create CodeBuild project + aws_codebuild: + name: "{{ project_name }}" + description: Build project for testing the Ansible aws_codebuild module + service_role: "{{ codebuild_iam_role.iam_role.arn }}" + timeout_in_minutes: 30 + source: + type: CODEPIPELINE + buildspec: '' + artifacts: + namespace_type: NONE + packaging: NONE + type: CODEPIPELINE + name: test + environment: + compute_type: BUILD_GENERAL1_SMALL + privileged_mode: true + image: 'aws/codebuild/docker:17.09.0' + type: LINUX_CONTAINER + environment_variables: + - { name: 'FOO_ENV', value: 'other' } + tags: + - { key: 'purpose', value: 'ansible-test' } + state: present + register: output + retries: 10 + delay: 5 + until: output is success + + - assert: + that: + - "output.project.description == 'Build project for testing the Ansible aws_codebuild module'" + - output.project.resource_tags.purpose == "ansible-test" + + - name: idempotence check rerunning same Codebuild task + aws_codebuild: + name: "{{ project_name }}" + description: Build project for testing the Ansible aws_codebuild module + service_role: "{{ codebuild_iam_role.iam_role.arn }}" + timeout_in_minutes: 30 + source: + type: CODEPIPELINE + buildspec: '' + artifacts: + namespace_type: NONE + packaging: NONE + type: CODEPIPELINE + name: test + encryption_key: 'arn:aws:kms:{{ aws_region }}:{{ aws_account_id }}:alias/aws/s3' + environment: + compute_type: BUILD_GENERAL1_SMALL + privileged_mode: true + image: 'aws/codebuild/docker:17.09.0' + type: LINUX_CONTAINER + environment_variables: + - { name: 'FOO_ENV', value: 'other' } + tags: + - { key: 'purpose', value: 'ansible-test' } + state: present + register: rerun_test_output + + - assert: + that: + - "rerun_test_output.project.created == output.project.created" + - rerun_test_output.project.resource_tags.purpose == "ansible-test" + + - include_tasks: 'tagging.yml' + - include_tasks: 'description.yml' + + - name: delete CodeBuild project + aws_codebuild: + name: "{{ output.project.name }}" + source: + type: CODEPIPELINE + buildspec: '' + artifacts: {} + state: absent + async: 300 + + # ============================== cleanup ====================================== + + always: + + - name: cleanup IAM role created for CodeBuild test + iam_role: + name: "{{ iam_role_name }}" + state: absent diff --git a/ansible_collections/community/aws/tests/integration/targets/codebuild_project/tasks/tagging.yml b/ansible_collections/community/aws/tests/integration/targets/codebuild_project/tasks/tagging.yml new file mode 100644 index 000000000..a26f2a337 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/codebuild_project/tasks/tagging.yml @@ -0,0 +1,250 @@ +- name: Tests relating to tagging aws_codebuild + 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 + # Mandatory settings + module_defaults: + community.aws.aws_codebuild: + name: '{{ project_name }}' +# community.aws.aws_codebuild_info: +# name: '{{ project_name }}' + block: + + ### + +# - name: test adding tags to aws_codebuild (check mode) +# aws_codebuild: +# resource_tags: '{{ first_tags }}' +# purge_tags: True +# register: update_result +# check_mode: yes +# - name: assert that update succeeded +# assert: +# that: +# - update_result is changed + + - name: test adding tags to aws_codebuild + aws_codebuild: + resource_tags: '{{ first_tags }}' + purge_tags: True + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is changed + - update_result.project.resource_tags == first_tags + +# - name: test adding tags to aws_codebuild - idempotency (check mode) +# aws_codebuild: +# resource_tags: '{{ first_tags }}' +# purge_tags: True +# register: update_result +# check_mode: yes +# - name: assert that update succeeded +# assert: +# that: +# - update_result is not changed + + - name: test adding tags to aws_codebuild - idempotency + aws_codebuild: + resource_tags: '{{ first_tags }}' + purge_tags: True + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is not changed + - update_result.project.resource_tags == first_tags + + ### + +# - name: test updating tags with purge on aws_codebuild (check mode) +# aws_codebuild: +# resource_tags: '{{ second_tags }}' +# purge_tags: True +# register: update_result +# check_mode: yes +# - name: assert that update succeeded +# assert: +# that: +# - update_result is changed + + - name: test updating tags with purge on aws_codebuild + aws_codebuild: + resource_tags: '{{ second_tags }}' + purge_tags: True + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is changed + - update_result.project.resource_tags == second_tags + +# - name: test updating tags with purge on aws_codebuild - idempotency (check mode) +# aws_codebuild: +# resource_tags: '{{ second_tags }}' +# purge_tags: True +# register: update_result +# check_mode: yes +# - name: assert that update succeeded +# assert: +# that: +# - update_result is not changed + + - name: test updating tags with purge on aws_codebuild - idempotency + aws_codebuild: + resource_tags: '{{ second_tags }}' + purge_tags: True + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is not changed + - update_result.project.resource_tags == second_tags + + ### + +# - name: test updating tags without purge on aws_codebuild (check mode) +# aws_codebuild: +# resource_tags: '{{ third_tags }}' +# purge_tags: False +# register: update_result +# check_mode: yes +# - name: assert that update succeeded +# assert: +# that: +# - update_result is changed + + - name: test updating tags without purge on aws_codebuild + aws_codebuild: + resource_tags: '{{ third_tags }}' + purge_tags: False + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is changed + - update_result.project.resource_tags == final_tags + +# - name: test updating tags without purge on aws_codebuild - idempotency (check mode) +# aws_codebuild: +# resource_tags: '{{ third_tags }}' +# purge_tags: False +# register: update_result +# check_mode: yes +# - name: assert that update succeeded +# assert: +# that: +# - update_result is not changed + + - name: test updating tags without purge on aws_codebuild - idempotency + aws_codebuild: + resource_tags: '{{ third_tags }}' + purge_tags: False + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is not changed + - update_result.project.resource_tags == final_tags + +# ### +# +# - name: test that aws_codebuild_info returns the tags +# aws_codebuild_info: +# register: tag_info +# - name: assert tags present +# assert: +# that: +# - tag_info.project.tags == final_tags +# +# ### + +# - name: test no tags param aws_codebuild (check mode) +# aws_codebuild: {} +# register: update_result +# check_mode: yes +# - name: assert no change +# assert: +# that: +# - update_result is not changed +# - update_result.project.resource_tags == final_tags +# + + - name: test no tags param aws_codebuild + aws_codebuild: {} + register: update_result + - name: assert no change + assert: + that: + - update_result is not changed + - update_result.project.resource_tags == final_tags + + ### + +# - name: test removing tags from aws_codebuild (check mode) +# aws_codebuild: +# resource_tags: {} +# purge_tags: True +# register: update_result +# check_mode: yes +# - name: assert that update succeeded +# assert: +# that: +# - update_result is changed + + - name: test removing tags from aws_codebuild + aws_codebuild: + resource_tags: {} + purge_tags: True + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is changed + - update_result.project.resource_tags == {} + +# - name: test removing tags from aws_codebuild - idempotency (check mode) +# aws_codebuild: +# resource_tags: {} +# purge_tags: True +# register: update_result +# check_mode: yes +# - name: assert that update succeeded +# assert: +# that: +# - update_result is not changed + + - name: test removing tags from aws_codebuild - idempotency + aws_codebuild: + resource_tags: {} + purge_tags: True + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is not changed + - update_result.project.resource_tags == {} diff --git a/ansible_collections/community/aws/tests/integration/targets/codebuild_project/vars/main.yml b/ansible_collections/community/aws/tests/integration/targets/codebuild_project/vars/main.yml new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/codebuild_project/vars/main.yml diff --git a/ansible_collections/community/aws/tests/integration/targets/codecommit_repository/aliases b/ansible_collections/community/aws/tests/integration/targets/codecommit_repository/aliases new file mode 100644 index 000000000..4ef4b2067 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/codecommit_repository/aliases @@ -0,0 +1 @@ +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/codecommit_repository/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/codecommit_repository/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/codecommit_repository/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/codecommit_repository/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/codecommit_repository/tasks/main.yml new file mode 100644 index 000000000..acf194e1e --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/codecommit_repository/tasks/main.yml @@ -0,0 +1,134 @@ +--- +- module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + block: + # ============================================================ + - name: Create a repository (CHECK MODE) + aws_codecommit: + name: "{{ resource_prefix }}_repo" + description: original comment + state: present + register: output + check_mode: yes + - assert: + that: + - output is changed + + - name: Create a repository + aws_codecommit: + name: "{{ resource_prefix }}_repo" + description: original comment + state: present + register: output + - assert: + that: + - output is changed + - output.repository_metadata.repository_name == '{{ resource_prefix }}_repo' + - output.repository_metadata.repository_description == 'original comment' + + - name: No-op update to repository + aws_codecommit: + name: "{{ resource_prefix }}_repo" + description: original comment + state: present + register: output + - assert: + that: + - output is not changed + - output.repository_metadata.repository_name == '{{ resource_prefix }}_repo' + - output.repository_metadata.repository_description == 'original comment' + + - name: Update repository description (CHECK MODE) + aws_codecommit: + name: "{{ resource_prefix }}_repo" + description: new comment + state: present + register: output + check_mode: yes + - assert: + that: + - output is changed + - output.repository_metadata.repository_name == '{{ resource_prefix }}_repo' + - output.repository_metadata.repository_description == 'original comment' + + - name: Update repository description + aws_codecommit: + name: "{{ resource_prefix }}_repo" + description: new comment + state: present + register: output + - assert: + that: + - output is changed + - output.repository_metadata.repository_name == '{{ resource_prefix }}_repo' + - output.repository_metadata.repository_description == 'new comment' + + # ============================================================ + - name: Delete a repository (CHECK MODE) + aws_codecommit: + name: "{{ resource_prefix }}_repo" + state: absent + register: output + check_mode: yes + - assert: + that: + - output is changed + + - name: Delete a repository + aws_codecommit: + name: "{{ resource_prefix }}_repo" + state: absent + register: output + - assert: + that: + - output is changed + + - name: Delete a non-existent repository + aws_codecommit: + name: "{{ resource_prefix }}_repo" + state: absent + register: output + - assert: + that: + - output is not changed + + - name: Create a repository without description + aws_codecommit: + name: "{{ resource_prefix }}_repo" + state: present + register: output + - assert: + that: + - output is changed + - output.repository_metadata.repository_name == '{{ resource_prefix }}_repo' + + - name: No-op update to repository without description + aws_codecommit: + name: "{{ resource_prefix }}_repo" + state: present + register: output + - assert: + that: + - output is not changed + - output.repository_metadata.repository_name == '{{ resource_prefix }}_repo' + + - name: Delete a repository without description + aws_codecommit: + name: "{{ resource_prefix }}_repo" + state: absent + register: output + - assert: + that: + - output is changed + + always: + ###### TEARDOWN STARTS HERE ###### + - name: Delete a repository + aws_codecommit: + name: "{{ resource_prefix }}_repo" + state: absent + ignore_errors: yes diff --git a/ansible_collections/community/aws/tests/integration/targets/codepipeline/aliases b/ansible_collections/community/aws/tests/integration/targets/codepipeline/aliases new file mode 100644 index 000000000..4ef4b2067 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/codepipeline/aliases @@ -0,0 +1 @@ +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/codepipeline/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/codepipeline/defaults/main.yml new file mode 100644 index 000000000..6c578ddb8 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/codepipeline/defaults/main.yml @@ -0,0 +1,6 @@ +--- +# defaults file for aws_codepipeline +codepipeline_name: "{{ tiny_prefix }}-test-codepipeline" +# IAM role names have to be less than 64 characters +# we hash the resource_prefix to get a shorter, unique string +codepipeline_service_role_name: "ansible-test-{{ tiny_prefix | truncate(6, True, '') }}-codepipeline-role" diff --git a/ansible_collections/community/aws/tests/integration/targets/codepipeline/files/codepipeline_iam_trust_policy.json b/ansible_collections/community/aws/tests/integration/targets/codepipeline/files/codepipeline_iam_trust_policy.json new file mode 100644 index 000000000..9be3f72b6 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/codepipeline/files/codepipeline_iam_trust_policy.json @@ -0,0 +1,12 @@ +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Service": "codepipeline.amazonaws.com" + }, + "Action": "sts:AssumeRole" + } + ] +} diff --git a/ansible_collections/community/aws/tests/integration/targets/codepipeline/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/codepipeline/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/codepipeline/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/codepipeline/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/codepipeline/tasks/main.yml new file mode 100644 index 000000000..2e8e7d8f3 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/codepipeline/tasks/main.yml @@ -0,0 +1,146 @@ +--- +- name: 'aws_codebuild integration tests' + collections: + - amazon.aws + module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + block: + + # ==================== preparaions ======================================== + + - name: create IAM role needed for CodePipeline test + iam_role: + name: "{{ codepipeline_service_role_name }}" + description: Role with permissions for CodePipeline actions. + assume_role_policy_document: "{{ lookup('file', 'codepipeline_iam_trust_policy.json') }}" + state: present + register: codepipeline_iam_role + + # ================== integration test ========================================== + + - name: create CodePipeline + aws_codepipeline: + name: "{{ codepipeline_name }}" + role_arn: "{{ codepipeline_iam_role.iam_role.arn }}" + artifact_store: + type: S3 + location: foo + stages: + - name: step_1 + actions: + - name: action + actionTypeId: + category: Source + owner: AWS + provider: S3 + version: '1' + configuration: + S3Bucket: foo + S3ObjectKey: bar + outputArtifacts: + - { name: step_one_output } + - name: step_2 + actions: + - name: action + actionTypeId: + category: Build + owner: AWS + provider: CodeBuild + version: '1' + inputArtifacts: + - { name: step_one_output } + outputArtifacts: + - { name: step_two_output } + configuration: + ProjectName: foo + state: present + register: output + retries: 10 + delay: 5 + until: output is success + + - assert: + that: + - output.changed == True + - output.pipeline.name == "{{ codepipeline_name }}" + - output.pipeline.stages|length > 1 + + - name: idempotence check rerunning same CodePipeline task + aws_codepipeline: + name: "{{ codepipeline_name }}" + role_arn: "{{ codepipeline_iam_role.iam_role.arn }}" + artifact_store: + type: S3 + location: foo + stages: + - name: step_1 + actions: + - name: action + actionTypeId: + category: Source + owner: AWS + provider: S3 + version: '1' + configuration: + S3Bucket: foo + S3ObjectKey: bar + outputArtifacts: + - { name: step_one_output } + - name: step_2 + actions: + - name: action + actionTypeId: + category: Build + owner: AWS + provider: CodeBuild + version: '1' + inputArtifacts: + - { name: step_one_output } + outputArtifacts: + - { name: step_two_output } + configuration: + ProjectName: foo + state: present + register: rerun_test_output + + - assert: + that: + - rerun_test_output.changed == False + - rerun_test_output.pipeline == output.pipeline + + - name: Test deletion of CodePipeline + aws_codepipeline: + name: "{{ codepipeline_name }}" + role_arn: '' + artifact_store: {} + stages: [] + state: absent + register: absent_test_output + + - assert: + that: + - absent_test_output.changed == True + - absent_test_output.pipeline is undefined + + # ==================== cleanup ======================= + + always: + + - name: Cleanup - delete test CodePipeline + aws_codepipeline: + name: "{{ codepipeline_name }}" + role_arn: '' + artifact_store: {} + stages: [] + state: absent + ignore_errors: true + + - name: Cleanup - delete IAM role needed for CodePipeline test + iam_role: + name: "{{ codepipeline_name }}" + state: absent + ignore_errors: true diff --git a/ansible_collections/community/aws/tests/integration/targets/config/aliases b/ansible_collections/community/aws/tests/integration/targets/config/aliases new file mode 100644 index 000000000..c0d012a08 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/config/aliases @@ -0,0 +1,15 @@ +# reason: missing-policy +# AWS Config will test that it's able to deliver using its assumed role. +# Either we'll need to grant the role global access to S3/SNS/SQS or we'll need +# some custom Managed Policies. +# reason: serial +# Only one Config Recorder per region per account is permitted +unsupported + +cloud/aws + +config_aggregation_authorization +config_aggregator +config_delivery_channel +config_recorder +config_rule diff --git a/ansible_collections/community/aws/tests/integration/targets/config/defaults/main.yaml b/ansible_collections/community/aws/tests/integration/targets/config/defaults/main.yaml new file mode 100644 index 000000000..26b39c583 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/config/defaults/main.yaml @@ -0,0 +1,4 @@ +--- +config_s3_bucket: '{{ resource_prefix }}-config-records' +config_sns_name: '{{ resource_prefix }}-delivery-channel-test-topic' +config_role_name: 'ansible-test-{{ resource_prefix }}' diff --git a/ansible_collections/community/aws/tests/integration/targets/config/files/config-trust-policy.json b/ansible_collections/community/aws/tests/integration/targets/config/files/config-trust-policy.json new file mode 100644 index 000000000..532b3ed5a --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/config/files/config-trust-policy.json @@ -0,0 +1,13 @@ +{ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "", + "Effect": "Allow", + "Principal": { + "Service": "config.amazonaws.com" + }, + "Action": "sts:AssumeRole" + } + ] +} diff --git a/ansible_collections/community/aws/tests/integration/targets/config/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/config/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/config/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/config/tasks/main.yaml b/ansible_collections/community/aws/tests/integration/targets/config/tasks/main.yaml new file mode 100644 index 000000000..313f9f677 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/config/tasks/main.yaml @@ -0,0 +1,467 @@ +--- +- name: 'aws_config integration tests' + collections: + - amazon.aws + module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + block: + + # ============================================================ + # Prerequisites + # ============================================================ + + - name: ensure IAM role exists + iam_role: + name: '{{ config_role_name }}' + assume_role_policy_document: "{{ lookup('file','config-trust-policy.json') }}" + state: present + create_instance_profile: no + managed_policy: + - 'arn:aws:iam::aws:policy/service-role/AWSConfigRole' + register: config_iam_role + + - name: ensure SNS topic exists + sns_topic: + name: '{{ config_sns_name }}' + state: present + subscriptions: + - endpoint: "rando_email_address@rando.com" + protocol: "email" + register: config_sns_topic + + - name: ensure S3 bucket exists + s3_bucket: + name: "{{ config_s3_bucket }}" + + - name: ensure S3 access for IAM role + iam_policy: + iam_type: role + iam_name: '{{ config_role_name }}' + policy_name: AwsConfigRecorderTestRoleS3Policy + state: present + policy_json: "{{ lookup( 'template', 'config-s3-policy.json.j2') }}" + + # ============================================================ + # Module requirement testing + # ============================================================ + - name: test rule with no source parameter + aws_config_rule: + name: random_name + state: present + register: output + ignore_errors: true + + - name: assert failure when called with no source parameter + assert: + that: + - output.failed + - 'output.msg.startswith("missing required arguments:")' + + - name: test resource_type delivery_channel with no s3_bucket parameter + aws_config_delivery_channel: + name: random_name + state: present + register: output + ignore_errors: true + + - name: assert failure when called with no s3_bucket parameter + assert: + that: + - output.failed + - 'output.msg.startswith("missing required arguments:")' + + - name: test resource_type configuration_recorder with no role_arn parameter + aws_config_recorder: + name: random_name + state: present + register: output + ignore_errors: true + + - name: assert failure when called with no role_arn parameter + assert: + that: + - output.failed + - 'output.msg.startswith("state is present but all of the following are missing")' + + - name: test resource_type configuration_recorder with no recording_group parameter + aws_config_recorder: + name: random_name + state: present + role_arn: 'arn:aws:iam::123456789012:role/AwsConfigRecorder' + register: output + ignore_errors: true + + - name: assert failure when called with no recording_group parameter + assert: + that: + - output.failed + - 'output.msg.startswith("state is present but all of the following are missing")' + + - name: test resource_type aggregation_authorization with no authorized_account_id parameter + aws_config_aggregation_authorization: + state: present + register: output + ignore_errors: true + + - name: assert failure when called with no authorized_account_id parameter + assert: + that: + - output.failed + - 'output.msg.startswith("missing required arguments:")' + + - name: test resource_type aggregation_authorization with no authorized_aws_region parameter + aws_config_aggregation_authorization: + state: present + authorized_account_id: '123456789012' + register: output + ignore_errors: true + + - name: assert failure when called with no authorized_aws_region parameter + assert: + that: + - output.failed + - 'output.msg.startswith("missing required arguments:")' + + - name: test resource_type configuration_aggregator with no account_sources parameter + aws_config_aggregator: + name: random_name + state: present + register: output + ignore_errors: true + + - name: assert failure when called with no account_sources parameter + assert: + that: + - output.failed + - 'output.msg.startswith("missing required arguments: account_sources")' + + - name: test resource_type configuration_aggregator with no organization_source parameter + aws_config_aggregator: + name: random_name + state: present + account_sources: [] + register: output + ignore_errors: true + + - name: assert failure when called with no organization_source parameter + assert: + that: + - output.failed + - 'output.msg.startswith("missing required arguments: organization_source")' + + # ============================================================ + # Creation testing + # ============================================================ + - name: Create Configuration Recorder for AWS Config + aws_config_recorder: + name: '{{ resource_prefix }}-recorder' + state: present + role_arn: "{{ config_iam_role.arn }}" + recording_group: + all_supported: true + include_global_types: true + register: output + + - assert: + that: + - output.changed + + - name: Create Delivery Channel for AWS Config + aws_config_delivery_channel: + name: '{{ resource_prefix }}-channel' + state: present + s3_bucket: "{{ config_s3_bucket }}" + s3_prefix: "foo/bar" + sns_topic_arn: "{{ config_sns_topic.sns_arn }}" + delivery_frequency: 'Twelve_Hours' + register: output + + - assert: + that: + - output.changed + + - name: Create Config Rule for AWS Config + aws_config_rule: + name: '{{ resource_prefix }}-rule' + state: present + description: 'This AWS Config rule checks for public write access on S3 buckets' + scope: + compliance_types: + - 'AWS::S3::Bucket' + source: + owner: AWS + identifier: 'S3_BUCKET_PUBLIC_WRITE_PROHIBITED' + register: output + + - assert: + that: + - output.changed + + - name: Create aws_config_aggregator + aws_config_aggregator: + name: random_name + state: present + account_sources: [] + organization_source: + all_aws_regions: true + role_arn: "{{ config_iam_role.arn }}" + register: output + + - name: assert success + assert: + that: + - output is changed + + - name: Create aws_config_aggregator - idempotency + aws_config_aggregator: + name: random_name + state: present + account_sources: [] + organization_source: + all_aws_regions: true + role_arn: "{{ config_iam_role.arn }}" + register: output + + - name: assert not changed + assert: + that: + - output is not changed + + # ============================================================ + # Update testing + # ============================================================ + - name: Update Configuration Recorder + aws_config_recorder: + name: '{{ resource_prefix }}-recorder' + state: present + role_arn: "{{ config_iam_role.arn }}" + recording_group: + all_supported: false + include_global_types: false + resource_types: + - 'AWS::S3::Bucket' + register: output + + - assert: + that: + - output.changed + + - name: Update Delivery Channel + aws_config_delivery_channel: + name: '{{ resource_prefix }}-channel' + state: present + s3_bucket: "{{ config_s3_bucket }}" + sns_topic_arn: "{{ config_sns_topic.sns_arn }}" + delivery_frequency: 'TwentyFour_Hours' + register: output + + - assert: + that: + - output.changed + + - name: Update Config Rule + aws_config_rule: + name: '{{ resource_prefix }}-rule' + state: present + description: 'This AWS Config rule checks for public write access on S3 buckets' + scope: + compliance_types: + - 'AWS::S3::Bucket' + source: + owner: AWS + identifier: 'S3_BUCKET_PUBLIC_READ_PROHIBITED' + register: output + + - assert: + that: + - output.changed + + - name: Update Config Rule - idempotency + aws_config_rule: + name: '{{ resource_prefix }}-rule' + state: present + description: 'This AWS Config rule checks for public write access on S3 buckets' + scope: + compliance_types: + - 'AWS::S3::Bucket' + source: + owner: AWS + identifier: 'S3_BUCKET_PUBLIC_READ_PROHIBITED' + register: output + + - assert: + that: + - output is not changed + + - name: Update aws_config_aggregator + aws_config_aggregator: + name: random_name + state: present + account_sources: [] + organization_source: + all_aws_regions: false + aws_regions: + - '{{ aws_region }}' + role_arn: "{{ config_iam_role.arn }}" + register: output + + - name: assert success + assert: + that: + - output is changed + + - name: Update aws_config_aggregator - idempotency + aws_config_aggregator: + name: random_name + state: present + account_sources: [] + organization_source: + all_aws_regions: false + aws_regions: + - '{{ aws_region }}' + role_arn: "{{ config_iam_role.arn }}" + register: output + + - name: assert success + assert: + that: + - output is not changed + + # ============================================================ + # Read testing + # ============================================================ + - name: Don't update Configuration Recorder + aws_config_recorder: + name: '{{ resource_prefix }}-recorder' + state: present + role_arn: "{{ config_iam_role.arn }}" + recording_group: + all_supported: false + include_global_types: false + resource_types: + - 'AWS::S3::Bucket' + register: output + + - assert: + that: + - not output.changed + + - name: Don't update Delivery Channel + aws_config_delivery_channel: + name: '{{ resource_prefix }}-channel' + state: present + s3_bucket: "{{ config_s3_bucket }}" + sns_topic_arn: "{{ config_sns_topic.sns_arn }}" + delivery_frequency: 'TwentyFour_Hours' + register: output + + - assert: + that: + - not output.changed + + - name: Don't update Config Rule + aws_config_rule: + name: '{{ resource_prefix }}-rule' + state: present + description: 'This AWS Config rule checks for public write access on S3 buckets' + scope: + compliance_types: + - 'AWS::S3::Bucket' + source: + owner: AWS + identifier: 'S3_BUCKET_PUBLIC_READ_PROHIBITED' + register: output + + - assert: + that: + - not output.changed + + always: + + - name: delete aws_config_aggregator + aws_config_aggregator: + name: random_name + state: absent + register: output + ignore_errors: true + + # ============================================================ + # Destroy testing + # ============================================================ + - name: Destroy Configuration Recorder + aws_config_recorder: + name: '{{ resource_prefix }}-recorder' + state: absent + register: output + ignore_errors: yes + +# - assert: +# that: +# - output.changed + + - name: Destroy Delivery Channel + aws_config_delivery_channel: + name: '{{ resource_prefix }}-channel' + state: absent + s3_bucket: "{{ config_s3_bucket }}" + sns_topic_arn: "{{ config_sns_topic.sns_arn }}" + delivery_frequency: 'TwentyFour_Hours' + register: output + ignore_errors: yes + +# - assert: +# that: +# - output.changed + + - name: Destroy Config Rule + aws_config_rule: + name: '{{ resource_prefix }}-rule' + state: absent + description: 'This AWS Config rule checks for public write access on S3 buckets' + scope: + compliance_types: + - 'AWS::S3::Bucket' + source: + owner: AWS + identifier: 'S3_BUCKET_PUBLIC_READ_PROHIBITED' + register: output + ignore_errors: yes + +# - assert: +# that: +# - output.changed + + # ============================================================ + # Clean up prerequisites + # ============================================================ + - name: remove S3 access from IAM role + iam_policy: + iam_type: role + iam_name: '{{ config_role_name }}' + policy_name: AwsConfigRecorderTestRoleS3Policy + state: absent + policy_json: "{{ lookup( 'template', 'config-s3-policy.json.j2') }}" + ignore_errors: yes + + - name: remove IAM role + iam_role: + name: '{{ config_role_name }}' + state: absent + ignore_errors: yes + + - name: remove SNS topic + sns_topic: + name: '{{ config_sns_name }}' + state: absent + ignore_errors: yes + + - name: remove S3 bucket + s3_bucket: + name: "{{ config_s3_bucket }}" + state: absent + force: yes + ignore_errors: yes diff --git a/ansible_collections/community/aws/tests/integration/targets/config/templates/config-s3-policy.json.j2 b/ansible_collections/community/aws/tests/integration/targets/config/templates/config-s3-policy.json.j2 new file mode 100644 index 000000000..530933000 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/config/templates/config-s3-policy.json.j2 @@ -0,0 +1,23 @@ +{ + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sns:Publish", + "Resource": "{{ config_sns_topic.sns_arn }}", + "Effect": "Allow", + "Sid": "PublishToSNS" + }, + { + "Action": "s3:PutObject", + "Resource": "arn:aws:s3:::{{ config_s3_bucket }}/*", + "Effect": "Allow", + "Sid": "AllowPutS3Object" + }, + { + "Action": "s3:GetBucketAcl", + "Resource": "arn:aws:s3:::{{ config_s3_bucket }}", + "Effect": "Allow", + "Sid": "AllowGetS3Acl" + } + ] +} diff --git a/ansible_collections/community/aws/tests/integration/targets/connection/aliases b/ansible_collections/community/aws/tests/integration/targets/connection/aliases new file mode 100644 index 000000000..8019bed39 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection/aliases @@ -0,0 +1,3 @@ +# Used to test basic operation once a connection plugin has established a connection +hidden +disabled diff --git a/ansible_collections/community/aws/tests/integration/targets/connection/test.sh b/ansible_collections/community/aws/tests/integration/targets/connection/test.sh new file mode 100755 index 000000000..52af74d7d --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection/test.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +set -eux + +[ -f "${INVENTORY}" ] + +ansible-playbook test_connection.yml -i "${INVENTORY}" "$@" + +# Ansible 2.14 dropped support for non UTF-8 Locale +# https://github.com/ansible/ansible/pull/78175 +# LC_ALL=C LANG=C ansible-playbook test_connection.yml -i "${INVENTORY}" "$@" diff --git a/ansible_collections/community/aws/tests/integration/targets/connection/test_connection.yml b/ansible_collections/community/aws/tests/integration/targets/connection/test_connection.yml new file mode 100644 index 000000000..829ac93b3 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection/test_connection.yml @@ -0,0 +1,65 @@ +- hosts: "{{ target_hosts }}" + gather_facts: no + strategy: free + vars: + local_dir: '{{ local_tmp }}-{{ inventory_hostname }}-汉è¯' + local_file: '{{ local_dir }}/汉è¯.txt' + remote_dir: '{{ remote_tmp }}-汉è¯' + remote_file: '{{ remote_dir }}/汉è¯.txt' + remote_empty_file: '{{ remote_dir }}/empty.txt' + tasks: + + ### test wait_for_connection plugin + - wait_for_connection: + timeout: '{{ wait_for_timeout | default(100) }}' + + - name: Gather facts + ansible.builtin.setup: + + ### raw with unicode arg and output + + - name: raw with unicode arg and output + raw: echo æ±‰è¯ + register: command + - name: check output of raw with unicode arg and output + assert: + that: + - "'汉è¯' in command.stdout" + - command is changed # as of 2.2, raw should default to changed: true for consistency w/ shell/command/script modules + + ### copy local file with unicode filename and content + + - name: create local file with unicode filename and content + local_action: lineinfile dest={{ local_file }} create=true line=æ±‰è¯ + - name: remove remote file with unicode filename and content + action: "{{ action_prefix }}file path={{ remote_file }} state=absent" + - name: create remote directory with unicode name + action: "{{ action_prefix }}file path={{ remote_dir }} state=directory" + - name: copy local file with unicode filename and content + action: "{{ action_prefix }}copy src={{ local_file }} dest={{ remote_file }}" + + ### fetch remote file with unicode filename and content + + - name: remove local file with unicode filename and content + local_action: file path={{ local_file }} state=absent + - name: fetch remote file with unicode filename and content + fetch: src={{ remote_file }} dest={{ local_file }} fail_on_missing=true validate_checksum=true flat=true + + ### remove local and remote temp files + + - name: remove local temp file + local_action: file path={{ local_file }} state=absent + - name: remove remote temp file + action: "{{ action_prefix }}file path={{ remote_file }} state=absent" + + ### copy an empty file + - name: copy an empty file + action: "{{ action_prefix }}copy content= dest={{ remote_empty_file }}" + - name: stat empty file + action: "{{ action_prefix }}stat path={{ remote_empty_file }}" + register: stat_empty_file_cmd + - name: check that empty file exists + assert: + that: + - stat_empty_file_cmd.stat.isreg # it is a regular file + - stat_empty_file_cmd.stat.size == 0 diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_addressing/aliases b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_addressing/aliases new file mode 100644 index 000000000..eb8e0b891 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_addressing/aliases @@ -0,0 +1,4 @@ +time=10m + +cloud/aws +connection_aws_ssm diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_addressing/aws_ssm_integration_test_setup.yml b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_addressing/aws_ssm_integration_test_setup.yml new file mode 100644 index 000000000..db519fb63 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_addressing/aws_ssm_integration_test_setup.yml @@ -0,0 +1,9 @@ +- hosts: localhost + roles: + - role: ../setup_connection_aws_ssm + vars: + target_os: fedora + encrypted_bucket: False + s3_bucket_region: 'eu-central-1' + s3_addressing_style: virtual + test_suffix: addressing diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_addressing/aws_ssm_integration_test_teardown.yml b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_addressing/aws_ssm_integration_test_teardown.yml new file mode 100644 index 000000000..3ab6f74cf --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_addressing/aws_ssm_integration_test_teardown.yml @@ -0,0 +1,5 @@ +- hosts: localhost + tasks: + - include_role: + name: ../setup_connection_aws_ssm + tasks_from: cleanup.yml diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_addressing/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_addressing/meta/main.yml new file mode 100644 index 000000000..d055eb86e --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_addressing/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - connection + - setup_connection_aws_ssm diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_addressing/runme.sh b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_addressing/runme.sh new file mode 100755 index 000000000..c99b3b066 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_addressing/runme.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +PLAYBOOK_DIR=$(pwd) +set -eux + +CMD_ARGS=("$@") + +# Destroy Environment +cleanup() { + + cd "${PLAYBOOK_DIR}" + ansible-playbook -c local aws_ssm_integration_test_teardown.yml "${CMD_ARGS[@]}" + +} + +trap "cleanup" EXIT + +# Setup Environment +ansible-playbook -c local aws_ssm_integration_test_setup.yml "$@" + +# Export the AWS Keys +set +x +. ./aws-env-vars.sh +set -x + +cd ../connection + +# Execute Integration tests +INVENTORY="${PLAYBOOK_DIR}/ssm_inventory" ./test.sh \ + -e target_hosts=aws_ssm \ + "$@" diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_amazon/aliases b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_amazon/aliases new file mode 100644 index 000000000..eb8e0b891 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_amazon/aliases @@ -0,0 +1,4 @@ +time=10m + +cloud/aws +connection_aws_ssm diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_amazon/aws_ssm_integration_test_setup.yml b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_amazon/aws_ssm_integration_test_setup.yml new file mode 100644 index 000000000..17cc6bce7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_amazon/aws_ssm_integration_test_setup.yml @@ -0,0 +1,5 @@ +- hosts: localhost + roles: + - role: ../setup_connection_aws_ssm + vars: + target_os: amazon diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_amazon/aws_ssm_integration_test_teardown.yml b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_amazon/aws_ssm_integration_test_teardown.yml new file mode 100644 index 000000000..3ab6f74cf --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_amazon/aws_ssm_integration_test_teardown.yml @@ -0,0 +1,5 @@ +- hosts: localhost + tasks: + - include_role: + name: ../setup_connection_aws_ssm + tasks_from: cleanup.yml diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_amazon/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_amazon/meta/main.yml new file mode 100644 index 000000000..d055eb86e --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_amazon/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - connection + - setup_connection_aws_ssm diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_amazon/runme.sh b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_amazon/runme.sh new file mode 100755 index 000000000..c99b3b066 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_amazon/runme.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +PLAYBOOK_DIR=$(pwd) +set -eux + +CMD_ARGS=("$@") + +# Destroy Environment +cleanup() { + + cd "${PLAYBOOK_DIR}" + ansible-playbook -c local aws_ssm_integration_test_teardown.yml "${CMD_ARGS[@]}" + +} + +trap "cleanup" EXIT + +# Setup Environment +ansible-playbook -c local aws_ssm_integration_test_setup.yml "$@" + +# Export the AWS Keys +set +x +. ./aws-env-vars.sh +set -x + +cd ../connection + +# Execute Integration tests +INVENTORY="${PLAYBOOK_DIR}/ssm_inventory" ./test.sh \ + -e target_hosts=aws_ssm \ + "$@" diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_cross_region/aliases b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_cross_region/aliases new file mode 100644 index 000000000..eb8e0b891 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_cross_region/aliases @@ -0,0 +1,4 @@ +time=10m + +cloud/aws +connection_aws_ssm diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_cross_region/aws_ssm_integration_test_setup.yml b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_cross_region/aws_ssm_integration_test_setup.yml new file mode 100644 index 000000000..1f223757c --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_cross_region/aws_ssm_integration_test_setup.yml @@ -0,0 +1,10 @@ +- hosts: localhost + roles: + - role: ../setup_connection_aws_ssm + vars: + target_os: fedora + s3_bucket_region: 'eu-central-1' + # Post 2019 regions behave differently from other regions + # they're worth testing but it's not possible in CI today. + #s3_bucket_region: 'eu-south-1' + test_suffix: crossregion diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_cross_region/aws_ssm_integration_test_teardown.yml b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_cross_region/aws_ssm_integration_test_teardown.yml new file mode 100644 index 000000000..3ab6f74cf --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_cross_region/aws_ssm_integration_test_teardown.yml @@ -0,0 +1,5 @@ +- hosts: localhost + tasks: + - include_role: + name: ../setup_connection_aws_ssm + tasks_from: cleanup.yml diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_cross_region/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_cross_region/meta/main.yml new file mode 100644 index 000000000..d055eb86e --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_cross_region/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - connection + - setup_connection_aws_ssm diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_cross_region/runme.sh b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_cross_region/runme.sh new file mode 100755 index 000000000..c99b3b066 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_cross_region/runme.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +PLAYBOOK_DIR=$(pwd) +set -eux + +CMD_ARGS=("$@") + +# Destroy Environment +cleanup() { + + cd "${PLAYBOOK_DIR}" + ansible-playbook -c local aws_ssm_integration_test_teardown.yml "${CMD_ARGS[@]}" + +} + +trap "cleanup" EXIT + +# Setup Environment +ansible-playbook -c local aws_ssm_integration_test_setup.yml "$@" + +# Export the AWS Keys +set +x +. ./aws-env-vars.sh +set -x + +cd ../connection + +# Execute Integration tests +INVENTORY="${PLAYBOOK_DIR}/ssm_inventory" ./test.sh \ + -e target_hosts=aws_ssm \ + "$@" diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_encrypted_s3/aliases b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_encrypted_s3/aliases new file mode 100644 index 000000000..eb8e0b891 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_encrypted_s3/aliases @@ -0,0 +1,4 @@ +time=10m + +cloud/aws +connection_aws_ssm diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_encrypted_s3/aws_ssm_integration_test_setup.yml b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_encrypted_s3/aws_ssm_integration_test_setup.yml new file mode 100644 index 000000000..bfea0d0dc --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_encrypted_s3/aws_ssm_integration_test_setup.yml @@ -0,0 +1,7 @@ +- hosts: localhost + roles: + - role: ../setup_connection_aws_ssm + vars: + target_os: fedora + encrypted_bucket: True + test_suffix: encrypteds3 diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_encrypted_s3/aws_ssm_integration_test_teardown.yml b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_encrypted_s3/aws_ssm_integration_test_teardown.yml new file mode 100644 index 000000000..3ab6f74cf --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_encrypted_s3/aws_ssm_integration_test_teardown.yml @@ -0,0 +1,5 @@ +- hosts: localhost + tasks: + - include_role: + name: ../setup_connection_aws_ssm + tasks_from: cleanup.yml diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_encrypted_s3/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_encrypted_s3/meta/main.yml new file mode 100644 index 000000000..d055eb86e --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_encrypted_s3/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - connection + - setup_connection_aws_ssm diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_encrypted_s3/runme.sh b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_encrypted_s3/runme.sh new file mode 100755 index 000000000..c99b3b066 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_encrypted_s3/runme.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +PLAYBOOK_DIR=$(pwd) +set -eux + +CMD_ARGS=("$@") + +# Destroy Environment +cleanup() { + + cd "${PLAYBOOK_DIR}" + ansible-playbook -c local aws_ssm_integration_test_teardown.yml "${CMD_ARGS[@]}" + +} + +trap "cleanup" EXIT + +# Setup Environment +ansible-playbook -c local aws_ssm_integration_test_setup.yml "$@" + +# Export the AWS Keys +set +x +. ./aws-env-vars.sh +set -x + +cd ../connection + +# Execute Integration tests +INVENTORY="${PLAYBOOK_DIR}/ssm_inventory" ./test.sh \ + -e target_hosts=aws_ssm \ + "$@" diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_endpoint/aliases b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_endpoint/aliases new file mode 100644 index 000000000..eb8e0b891 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_endpoint/aliases @@ -0,0 +1,4 @@ +time=10m + +cloud/aws +connection_aws_ssm diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_endpoint/aws_ssm_integration_test_setup.yml b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_endpoint/aws_ssm_integration_test_setup.yml new file mode 100644 index 000000000..71c850e9d --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_endpoint/aws_ssm_integration_test_setup.yml @@ -0,0 +1,7 @@ +- hosts: localhost + roles: + - role: ../setup_connection_aws_ssm + vars: + target_os: fedora + test_suffix: endpoint + endpoint_url: 'https://s3.dualstack.{{ aws_region }}.amazonaws.com' diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_endpoint/aws_ssm_integration_test_teardown.yml b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_endpoint/aws_ssm_integration_test_teardown.yml new file mode 100644 index 000000000..3ab6f74cf --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_endpoint/aws_ssm_integration_test_teardown.yml @@ -0,0 +1,5 @@ +- hosts: localhost + tasks: + - include_role: + name: ../setup_connection_aws_ssm + tasks_from: cleanup.yml diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_endpoint/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_endpoint/meta/main.yml new file mode 100644 index 000000000..d055eb86e --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_endpoint/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - connection + - setup_connection_aws_ssm diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_endpoint/runme.sh b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_endpoint/runme.sh new file mode 100755 index 000000000..c99b3b066 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_endpoint/runme.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +PLAYBOOK_DIR=$(pwd) +set -eux + +CMD_ARGS=("$@") + +# Destroy Environment +cleanup() { + + cd "${PLAYBOOK_DIR}" + ansible-playbook -c local aws_ssm_integration_test_teardown.yml "${CMD_ARGS[@]}" + +} + +trap "cleanup" EXIT + +# Setup Environment +ansible-playbook -c local aws_ssm_integration_test_setup.yml "$@" + +# Export the AWS Keys +set +x +. ./aws-env-vars.sh +set -x + +cd ../connection + +# Execute Integration tests +INVENTORY="${PLAYBOOK_DIR}/ssm_inventory" ./test.sh \ + -e target_hosts=aws_ssm \ + "$@" diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_fedora/aliases b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_fedora/aliases new file mode 100644 index 000000000..eb8e0b891 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_fedora/aliases @@ -0,0 +1,4 @@ +time=10m + +cloud/aws +connection_aws_ssm diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_fedora/aws_ssm_integration_test_setup.yml b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_fedora/aws_ssm_integration_test_setup.yml new file mode 100644 index 000000000..353757e33 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_fedora/aws_ssm_integration_test_setup.yml @@ -0,0 +1,5 @@ +- hosts: localhost + roles: + - role: ../setup_connection_aws_ssm + vars: + target_os: fedora diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_fedora/aws_ssm_integration_test_teardown.yml b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_fedora/aws_ssm_integration_test_teardown.yml new file mode 100644 index 000000000..3ab6f74cf --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_fedora/aws_ssm_integration_test_teardown.yml @@ -0,0 +1,5 @@ +- hosts: localhost + tasks: + - include_role: + name: ../setup_connection_aws_ssm + tasks_from: cleanup.yml diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_fedora/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_fedora/meta/main.yml new file mode 100644 index 000000000..d055eb86e --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_fedora/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - connection + - setup_connection_aws_ssm diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_fedora/runme.sh b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_fedora/runme.sh new file mode 100755 index 000000000..c99b3b066 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_fedora/runme.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +PLAYBOOK_DIR=$(pwd) +set -eux + +CMD_ARGS=("$@") + +# Destroy Environment +cleanup() { + + cd "${PLAYBOOK_DIR}" + ansible-playbook -c local aws_ssm_integration_test_teardown.yml "${CMD_ARGS[@]}" + +} + +trap "cleanup" EXIT + +# Setup Environment +ansible-playbook -c local aws_ssm_integration_test_setup.yml "$@" + +# Export the AWS Keys +set +x +. ./aws-env-vars.sh +set -x + +cd ../connection + +# Execute Integration tests +INVENTORY="${PLAYBOOK_DIR}/ssm_inventory" ./test.sh \ + -e target_hosts=aws_ssm \ + "$@" diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_profile/aliases b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_profile/aliases new file mode 100644 index 000000000..eb8e0b891 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_profile/aliases @@ -0,0 +1,4 @@ +time=10m + +cloud/aws +connection_aws_ssm diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_profile/aws_ssm_integration_test_setup.yml b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_profile/aws_ssm_integration_test_setup.yml new file mode 100644 index 000000000..3f4c2e47d --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_profile/aws_ssm_integration_test_setup.yml @@ -0,0 +1,6 @@ +- hosts: localhost + roles: + - role: ../setup_connection_aws_ssm + vars: + target_os: fedora + profile_name: test_profile diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_profile/aws_ssm_integration_test_teardown.yml b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_profile/aws_ssm_integration_test_teardown.yml new file mode 100644 index 000000000..3ab6f74cf --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_profile/aws_ssm_integration_test_teardown.yml @@ -0,0 +1,5 @@ +- hosts: localhost + tasks: + - include_role: + name: ../setup_connection_aws_ssm + tasks_from: cleanup.yml diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_profile/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_profile/meta/main.yml new file mode 100644 index 000000000..d055eb86e --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_profile/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - connection + - setup_connection_aws_ssm diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_profile/runme.sh b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_profile/runme.sh new file mode 100755 index 000000000..de73decad --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_profile/runme.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash + +PLAYBOOK_DIR=$(pwd) +set -eux + +CMD_ARGS=("$@") + +# Destroy Environment +cleanup() { + + cd "${PLAYBOOK_DIR}" + ansible-playbook -c local aws_ssm_integration_test_teardown.yml "${CMD_ARGS[@]}" + +} + +trap "cleanup" EXIT + +# Setup Environment +ansible-playbook -c local aws_ssm_integration_test_setup.yml "$@" + +AWS_CONFIG_FILE="$( pwd )/boto3_config" +export AWS_CONFIG_FILE + +cd ../connection + +# Execute Integration tests +INVENTORY="${PLAYBOOK_DIR}/ssm_inventory" ./test.sh \ + -e target_hosts=aws_ssm \ + "$@" diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_ssm_document/aliases b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_ssm_document/aliases new file mode 100644 index 000000000..eb8e0b891 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_ssm_document/aliases @@ -0,0 +1,4 @@ +time=10m + +cloud/aws +connection_aws_ssm diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_ssm_document/aws_ssm_integration_test_setup.yml b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_ssm_document/aws_ssm_integration_test_setup.yml new file mode 100644 index 000000000..992426976 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_ssm_document/aws_ssm_integration_test_setup.yml @@ -0,0 +1,7 @@ +- hosts: localhost + roles: + - role: ../setup_connection_aws_ssm + vars: + target_os: fedora + use_ssm_document: True + test_suffix: document diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_ssm_document/aws_ssm_integration_test_teardown.yml b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_ssm_document/aws_ssm_integration_test_teardown.yml new file mode 100644 index 000000000..3ab6f74cf --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_ssm_document/aws_ssm_integration_test_teardown.yml @@ -0,0 +1,5 @@ +- hosts: localhost + tasks: + - include_role: + name: ../setup_connection_aws_ssm + tasks_from: cleanup.yml diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_ssm_document/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_ssm_document/meta/main.yml new file mode 100644 index 000000000..d055eb86e --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_ssm_document/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - connection + - setup_connection_aws_ssm diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_ssm_document/runme.sh b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_ssm_document/runme.sh new file mode 100755 index 000000000..c99b3b066 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_ssm_document/runme.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +PLAYBOOK_DIR=$(pwd) +set -eux + +CMD_ARGS=("$@") + +# Destroy Environment +cleanup() { + + cd "${PLAYBOOK_DIR}" + ansible-playbook -c local aws_ssm_integration_test_teardown.yml "${CMD_ARGS[@]}" + +} + +trap "cleanup" EXIT + +# Setup Environment +ansible-playbook -c local aws_ssm_integration_test_setup.yml "$@" + +# Export the AWS Keys +set +x +. ./aws-env-vars.sh +set -x + +cd ../connection + +# Execute Integration tests +INVENTORY="${PLAYBOOK_DIR}/ssm_inventory" ./test.sh \ + -e target_hosts=aws_ssm \ + "$@" diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_ubuntu/aliases b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_ubuntu/aliases new file mode 100644 index 000000000..eb8e0b891 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_ubuntu/aliases @@ -0,0 +1,4 @@ +time=10m + +cloud/aws +connection_aws_ssm diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_ubuntu/aws_ssm_integration_test_setup.yml b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_ubuntu/aws_ssm_integration_test_setup.yml new file mode 100644 index 000000000..c50b8e689 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_ubuntu/aws_ssm_integration_test_setup.yml @@ -0,0 +1,5 @@ +- hosts: localhost + roles: + - role: ../setup_connection_aws_ssm + vars: + target_os: ubuntu diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_ubuntu/aws_ssm_integration_test_teardown.yml b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_ubuntu/aws_ssm_integration_test_teardown.yml new file mode 100644 index 000000000..3ab6f74cf --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_ubuntu/aws_ssm_integration_test_teardown.yml @@ -0,0 +1,5 @@ +- hosts: localhost + tasks: + - include_role: + name: ../setup_connection_aws_ssm + tasks_from: cleanup.yml diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_ubuntu/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_ubuntu/meta/main.yml new file mode 100644 index 000000000..d055eb86e --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_ubuntu/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - connection + - setup_connection_aws_ssm diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_ubuntu/runme.sh b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_ubuntu/runme.sh new file mode 100755 index 000000000..c99b3b066 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_ubuntu/runme.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +PLAYBOOK_DIR=$(pwd) +set -eux + +CMD_ARGS=("$@") + +# Destroy Environment +cleanup() { + + cd "${PLAYBOOK_DIR}" + ansible-playbook -c local aws_ssm_integration_test_teardown.yml "${CMD_ARGS[@]}" + +} + +trap "cleanup" EXIT + +# Setup Environment +ansible-playbook -c local aws_ssm_integration_test_setup.yml "$@" + +# Export the AWS Keys +set +x +. ./aws-env-vars.sh +set -x + +cd ../connection + +# Execute Integration tests +INVENTORY="${PLAYBOOK_DIR}/ssm_inventory" ./test.sh \ + -e target_hosts=aws_ssm \ + "$@" diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_vars/aliases b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_vars/aliases new file mode 100644 index 000000000..eb8e0b891 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_vars/aliases @@ -0,0 +1,4 @@ +time=10m + +cloud/aws +connection_aws_ssm diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_vars/aws_ssm_integration_test_setup.yml b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_vars/aws_ssm_integration_test_setup.yml new file mode 100644 index 000000000..ff67bc2c3 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_vars/aws_ssm_integration_test_setup.yml @@ -0,0 +1,6 @@ +- hosts: localhost + roles: + - role: ../setup_connection_aws_ssm + vars: + target_os: fedora + credential_vars: True diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_vars/aws_ssm_integration_test_teardown.yml b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_vars/aws_ssm_integration_test_teardown.yml new file mode 100644 index 000000000..3ab6f74cf --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_vars/aws_ssm_integration_test_teardown.yml @@ -0,0 +1,5 @@ +- hosts: localhost + tasks: + - include_role: + name: ../setup_connection_aws_ssm + tasks_from: cleanup.yml diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_vars/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_vars/meta/main.yml new file mode 100644 index 000000000..d055eb86e --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_vars/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - connection + - setup_connection_aws_ssm diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_vars/runme.sh b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_vars/runme.sh new file mode 100755 index 000000000..59ca6d0e9 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_vars/runme.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +PLAYBOOK_DIR=$(pwd) +set -eux + +CMD_ARGS=("$@") + +# Destroy Environment +cleanup() { + + cd "${PLAYBOOK_DIR}" + ansible-playbook -c local aws_ssm_integration_test_teardown.yml "${CMD_ARGS[@]}" + +} + +trap "cleanup" EXIT + +# Setup Environment +ansible-playbook -c local aws_ssm_integration_test_setup.yml "$@" + +cd ../connection + +# Execute Integration tests +INVENTORY="${PLAYBOOK_DIR}/ssm_inventory" ./test.sh \ + -e target_hosts=aws_ssm \ + "$@" diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_windows/aliases b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_windows/aliases new file mode 100644 index 000000000..eb8e0b891 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_windows/aliases @@ -0,0 +1,4 @@ +time=10m + +cloud/aws +connection_aws_ssm diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_windows/aws_ssm_integration_test_setup.yml b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_windows/aws_ssm_integration_test_setup.yml new file mode 100644 index 000000000..bc6bf3335 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_windows/aws_ssm_integration_test_setup.yml @@ -0,0 +1,6 @@ +- hosts: localhost + roles: + - role: ../setup_connection_aws_ssm + vars: + target_os: windows + wait_for_timeout: 200 diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_windows/aws_ssm_integration_test_teardown.yml b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_windows/aws_ssm_integration_test_teardown.yml new file mode 100644 index 000000000..3ab6f74cf --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_windows/aws_ssm_integration_test_teardown.yml @@ -0,0 +1,5 @@ +- hosts: localhost + tasks: + - include_role: + name: ../setup_connection_aws_ssm + tasks_from: cleanup.yml diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_windows/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_windows/meta/main.yml new file mode 100644 index 000000000..d055eb86e --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_windows/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - connection + - setup_connection_aws_ssm diff --git a/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_windows/runme.sh b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_windows/runme.sh new file mode 100755 index 000000000..c99b3b066 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/connection_aws_ssm_windows/runme.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +PLAYBOOK_DIR=$(pwd) +set -eux + +CMD_ARGS=("$@") + +# Destroy Environment +cleanup() { + + cd "${PLAYBOOK_DIR}" + ansible-playbook -c local aws_ssm_integration_test_teardown.yml "${CMD_ARGS[@]}" + +} + +trap "cleanup" EXIT + +# Setup Environment +ansible-playbook -c local aws_ssm_integration_test_setup.yml "$@" + +# Export the AWS Keys +set +x +. ./aws-env-vars.sh +set -x + +cd ../connection + +# Execute Integration tests +INVENTORY="${PLAYBOOK_DIR}/ssm_inventory" ./test.sh \ + -e target_hosts=aws_ssm \ + "$@" diff --git a/ansible_collections/community/aws/tests/integration/targets/dms_endpoint/aliases b/ansible_collections/community/aws/tests/integration/targets/dms_endpoint/aliases new file mode 100644 index 000000000..4ef4b2067 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/dms_endpoint/aliases @@ -0,0 +1 @@ +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/dms_endpoint/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/dms_endpoint/defaults/main.yml new file mode 100644 index 000000000..2fcb620b7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/dms_endpoint/defaults/main.yml @@ -0,0 +1,2 @@ +--- +dms_identifier: "{{ resource_prefix }}-dms" diff --git a/ansible_collections/community/aws/tests/integration/targets/dms_endpoint/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/dms_endpoint/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/dms_endpoint/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/dms_endpoint/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/dms_endpoint/tasks/main.yml new file mode 100644 index 000000000..328ea17a5 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/dms_endpoint/tasks/main.yml @@ -0,0 +1,133 @@ +--- +- name: 'dms_endpoint integration tests' + collections: + - amazon.aws + module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + block: + + - name: create endpoints + dms_endpoint: + state: present + endpointidentifier: "{{ dms_identifier }}" + endpointtype: source + enginename: aurora + username: testing + password: testint1234 + servername: "{{ resource_prefix }}.exampledomain.com" + port: 3306 + databasename: 'testdb' + sslmode: none + register: result + + - assert: + that: + - result is changed + - result is not failed + + - name: create endpoints no change + dms_endpoint: + state: present + endpointidentifier: "{{ dms_identifier }}" + endpointtype: source + enginename: aurora + username: testing + password: testint1234 + servername: "{{ resource_prefix }}.exampledomain.com" + port: 3306 + databasename: 'testdb' + sslmode: none + register: result + + - assert: + that: + - result is not changed + - result is not failed + + - name: update endpoints + dms_endpoint: + state: present + endpointidentifier: "{{ dms_identifier }}" + endpointtype: source + enginename: aurora + username: testing + password: testint1234 + servername: "{{ resource_prefix }}.exampledomain.com" + port: 3306 + databasename: 'testdb2' + sslmode: none + register: result + + - assert: + that: + - result is changed + - result is not failed + + - name: update endpoints no change + dms_endpoint: + state: present + endpointidentifier: "{{ dms_identifier }}" + endpointtype: source + enginename: aurora + username: testing + password: testint1234 + servername: "{{ resource_prefix }}.exampledomain.com" + port: 3306 + databasename: 'testdb2' + sslmode: none + register: result + + - assert: + that: + - result is not changed + - result is not failed + + - include_tasks: 'tags.yml' + + - name: delete endpoints + dms_endpoint: + state: absent + endpointidentifier: "{{ dms_identifier }}" + wait: True + timeout: 60 + retries: 10 + register: result + + - assert: + that: + - result is changed + - result is not failed + + - name: delete endpoints no change + dms_endpoint: + state: absent + endpointidentifier: "{{ dms_identifier }}" + wait: False + register: result + + - assert: + that: + - result is not changed + - result is not failed + + always: + - name: delete endpoints + dms_endpoint: + state: absent + endpointidentifier: "{{ dms_identifier }}" + endpointtype: source + enginename: aurora + username: testing + password: testint1234 + servername: "{{ resource_prefix }}.exampledomain.com" + port: 3306 + databasename: 'testdb' + sslmode: none + wait: True + timeout: 60 + retries: 10 + ignore_errors: True diff --git a/ansible_collections/community/aws/tests/integration/targets/dms_endpoint/tasks/tags.yml b/ansible_collections/community/aws/tests/integration/targets/dms_endpoint/tasks/tags.yml new file mode 100644 index 000000000..ca7c1aa0a --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/dms_endpoint/tasks/tags.yml @@ -0,0 +1,258 @@ +- name: Tests relating to setting tags on dms_endpoint + 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 + # Mandatory settings + module_defaults: + community.aws.dms_endpoint: + state: present + endpointidentifier: "{{ dms_identifier }}" + # These shouldn't be necessary, but that's the way the module works today. + endpointtype: source + enginename: aurora + username: testing + password: testint1234 + servername: "{{ resource_prefix }}.exampledomain.com" + port: 3306 + databasename: 'testdb2' + sslmode: none + # community.aws.dms_endpoint_info: + # endpointidentifier: "{{ dms_identifier }}" + block: + +# - name: test adding tags to dms_endpoint (check mode) +# dms_endpoint: +# tags: '{{ first_tags }}' +# purge_tags: True +# register: update_result +# check_mode: yes +# - name: assert that update succeeded +# assert: +# that: +# - update_result is changed + + - name: test adding tags to dms_endpoint + dms_endpoint: + tags: '{{ first_tags }}' + purge_tags: True + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is changed + - update_result.endpoint.tags == first_tags + +# - name: test adding tags to dms_endpoint - idempotency (check mode) +# dms_endpoint: +# tags: '{{ first_tags }}' +# purge_tags: True +# register: update_result +# check_mode: yes +# - name: assert that update succeeded +# assert: +# that: +# - update_result is not changed + + - name: test adding tags to dms_endpoint - idempotency + dms_endpoint: + tags: '{{ first_tags }}' + purge_tags: True + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is not changed + - update_result.endpoint.tags == first_tags + + ### + +# - name: test updating tags with purge on dms_endpoint (check mode) +# dms_endpoint: +# tags: '{{ second_tags }}' +# purge_tags: True +# register: update_result +# check_mode: yes +# - name: assert that update succeeded +# assert: +# that: +# - update_result is changed + + - name: test updating tags with purge on dms_endpoint + dms_endpoint: + tags: '{{ second_tags }}' + purge_tags: True + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is changed + - update_result.endpoint.tags == second_tags + +# - name: test updating tags with purge on dms_endpoint - idempotency (check mode) +# dms_endpoint: +# tags: '{{ second_tags }}' +# purge_tags: True +# register: update_result +# check_mode: yes +# - name: assert that update succeeded +# assert: +# that: +# - update_result is not changed + + - name: test updating tags with purge on dms_endpoint - idempotency + dms_endpoint: + tags: '{{ second_tags }}' + purge_tags: True + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is not changed + - update_result.endpoint.tags == second_tags + + ### + +# - name: test updating tags without purge on dms_endpoint (check mode) +# dms_endpoint: +# tags: '{{ third_tags }}' +# purge_tags: False +# register: update_result +# check_mode: yes +# - name: assert that update succeeded +# assert: +# that: +# - update_result is changed + + - name: test updating tags without purge on dms_endpoint + dms_endpoint: + tags: '{{ third_tags }}' + purge_tags: False + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is changed + - update_result.endpoint.tags == final_tags + +# - name: test updating tags without purge on dms_endpoint - idempotency (check mode) +# dms_endpoint: +# tags: '{{ third_tags }}' +# purge_tags: False +# register: update_result +# check_mode: yes +# - name: assert that update succeeded +# assert: +# that: +# - update_result is not changed + + - name: test updating tags without purge on dms_endpoint - idempotency + dms_endpoint: + tags: '{{ third_tags }}' + purge_tags: False + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is not changed + - update_result.endpoint.tags == final_tags + + ### +# +# - name: test that dms_endpoint_info returns the tags +# dms_endpoint_info: +# register: tag_info +# - name: assert tags present +# assert: +# that: +# - tag_info.endpoint.tags == final_tags +# + ### + +# - name: test no tags param dms_endpoint (check mode) +# dms_endpoint: {} +# register: update_result +# check_mode: yes +# - name: assert no change +# assert: +# that: +# - update_result is not changed +# - update_result.endpoint.tags == final_tags +# + + - name: test no tags param dms_endpoint + dms_endpoint: {} + register: update_result + - name: assert no change + assert: + that: + - update_result is not changed + - update_result.endpoint.tags == final_tags + + ### + +# - name: test removing tags from dms_endpoint (check mode) +# dms_endpoint: +# tags: {} +# purge_tags: True +# register: update_result +# check_mode: yes +# - name: assert that update succeeded +# assert: +# that: +# - update_result is changed + + - name: test removing tags from dms_endpoint + dms_endpoint: + tags: {} + purge_tags: True + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is changed + - update_result.endpoint.tags == {} + +# - name: test removing tags from dms_endpoint - idempotency (check mode) +# dms_endpoint: +# tags: {} +# purge_tags: True +# register: update_result +# check_mode: yes +# - name: assert that update succeeded +# assert: +# that: +# - update_result is not changed + + - name: test removing tags from dms_endpoint - idempotency + dms_endpoint: + tags: {} + purge_tags: True + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is not changed + - update_result.endpoint.tags == {} diff --git a/ansible_collections/community/aws/tests/integration/targets/dms_replication_subnet_group/aliases b/ansible_collections/community/aws/tests/integration/targets/dms_replication_subnet_group/aliases new file mode 100644 index 000000000..4ef4b2067 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/dms_replication_subnet_group/aliases @@ -0,0 +1 @@ +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/dms_replication_subnet_group/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/dms_replication_subnet_group/defaults/main.yml new file mode 100644 index 000000000..2c12e7703 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/dms_replication_subnet_group/defaults/main.yml @@ -0,0 +1,2 @@ +dms_sg_identifier: "{{ resource_prefix }}-dms" +dms_role_role_name: dms-vpc-role diff --git a/ansible_collections/community/aws/tests/integration/targets/dms_replication_subnet_group/files/dmsAssumeRolePolicyDocument.json b/ansible_collections/community/aws/tests/integration/targets/dms_replication_subnet_group/files/dmsAssumeRolePolicyDocument.json new file mode 100644 index 000000000..69ee87eea --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/dms_replication_subnet_group/files/dmsAssumeRolePolicyDocument.json @@ -0,0 +1,12 @@ +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Service": "dms.amazonaws.com" + }, + "Action": "sts:AssumeRole" + } + ] +}
\ No newline at end of file diff --git a/ansible_collections/community/aws/tests/integration/targets/dms_replication_subnet_group/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/dms_replication_subnet_group/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/dms_replication_subnet_group/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/dms_replication_subnet_group/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/dms_replication_subnet_group/tasks/main.yml new file mode 100644 index 000000000..0952602f1 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/dms_replication_subnet_group/tasks/main.yml @@ -0,0 +1,173 @@ +--- +- name: 'dms_replication_subnet_group integration tests' + collections: + - amazon.aws + module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + block: + + - name: ensure IAM role exists + iam_role: + name: "{{ dms_role_role_name }}" + assume_role_policy_document: "{{ lookup('file','dmsAssumeRolePolicyDocument.json') }}" + state: present + create_instance_profile: no + managed_policy: + - 'arn:aws:iam::aws:policy/service-role/AmazonDMSVPCManagementRole' + register: iam_role_output + ignore_errors: yes + + - name: Create VPC for use in testing + ec2_vpc_net: + name: "{{ resource_prefix }}-vpc" + cidr_block: 10.22.32.0/23 + tags: + Name: Ansible ec2_instance Testing VPC + tenancy: default + register: testing_vpc + + - name: 'Fetch AZ availability' + aws_az_info: + register: az_info + + - name: 'Assert that we have multiple AZs available to us' + assert: + that: az_info.availability_zones | length >= 2 + + - name: 'Pick AZs' + set_fact: + az_one: '{{ az_info.availability_zones[0].zone_name }}' + az_two: '{{ az_info.availability_zones[1].zone_name }}' + + - name: create subnet1 + ec2_vpc_subnet: + state: present + vpc_id: "{{ testing_vpc.vpc.id }}" + cidr: 10.22.32.16/28 + az: "{{ az_one }}" + register: subnet1 + + - name: create subnet2 + ec2_vpc_subnet: + state: present + vpc_id: "{{ testing_vpc.vpc.id }}" + cidr: 10.22.32.32/28 + az: "{{ az_two }}" + register: subnet2 + + - name: create replication subnet group + dms_replication_subnet_group: + state: present + identifier: "{{ dms_sg_identifier }}" + description: "Development Subnet Group" + subnet_ids: [ "{{ subnet1.subnet.id }}", "{{ subnet2.subnet.id }}"] + register: result + + - assert: + that: + - result is changed + - result is not failed + + - name: create subnet group no change + dms_replication_subnet_group: + state: present + identifier: "{{ dms_sg_identifier }}" + description: "Development Subnet Group" + subnet_ids: [ "{{ subnet1.subnet.id }}", "{{ subnet2.subnet.id }}"] + register: result + + - assert: + that: + - result is not changed + - result is not failed + + - name: update subnet group + dms_replication_subnet_group: + state: present + identifier: "{{ dms_sg_identifier }}" + description: "Development Subnet Group updated" + subnet_ids: [ "{{ subnet1.subnet.id }}", "{{ subnet2.subnet.id }}"] + register: result + + - assert: + that: + - result is changed + - result is not failed + + - name: update subnet group no change + dms_replication_subnet_group: + state: present + identifier: "{{ dms_sg_identifier }}" + description: "Development Subnet Group updated" + subnet_ids: [ "{{ subnet1.subnet.id }}", "{{ subnet2.subnet.id }}"] + register: result + + - assert: + that: + - result is not changed + - result is not failed + + always: + - name: delete subnet group no change + dms_replication_subnet_group: + state: absent + identifier: "{{ dms_sg_identifier }}" + description: "Development Subnet Group updated" + subnet_ids: [ "{{ subnet1.subnet.id }}", "{{ subnet2.subnet.id }}"] + register: result + + - assert: + that: + - result is changed + - result is not failed + + - name: delete subnet group no change + dms_replication_subnet_group: + state: absent + identifier: "{{ dms_sg_identifier }}" + description: "Development Subnet Group updated" + subnet_ids: [ "{{ subnet1.subnet.id }}", "{{ subnet2.subnet.id }}"] + register: result + + - assert: + that: + - result is not changed + - result is not failed + + - name: delete subnet1 + ec2_vpc_subnet: + state: absent + vpc_id: "{{ testing_vpc.vpc.id }}" + cidr: 10.22.32.16/28 + az: "{{ az_one }}" + + - name: delete subnet2 + ec2_vpc_subnet: + state: absent + vpc_id: "{{ testing_vpc.vpc.id }}" + cidr: 10.22.32.32/28 + az: "{{ az_two }}" + + - name: delete VPC for use in testing + ec2_vpc_net: + name: "{{ resource_prefix }}-vpc" + cidr_block: 10.22.32.0/23 + tags: + Name: Ansible ec2_instance Testing VPC + tenancy: default + state: absent + + - name: delete dms-vpc role + iam_role: + name: "{{ dms_role_role_name }}" + assume_role_policy_document: "{{ lookup('file','dmsAssumeRolePolicyDocument.json') }}" + state: absent + create_instance_profile: no + managed_policy: + - 'arn:aws:iam::aws:policy/service-role/AmazonDMSVPCManagementRole' + register: iam_role_output + ignore_errors: yes diff --git a/ansible_collections/community/aws/tests/integration/targets/dynamodb_table/aliases b/ansible_collections/community/aws/tests/integration/targets/dynamodb_table/aliases new file mode 100644 index 000000000..dc5eacd6f --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/dynamodb_table/aliases @@ -0,0 +1,2 @@ +cloud/aws +time=50m diff --git a/ansible_collections/community/aws/tests/integration/targets/dynamodb_table/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/dynamodb_table/defaults/main.yml new file mode 100644 index 000000000..8b92884a4 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/dynamodb_table/defaults/main.yml @@ -0,0 +1,94 @@ +--- +table_name: "{{ resource_prefix }}" +table_name_on_demand: "{{ resource_prefix }}-pay-per-request" +table_name_on_demand_complex: "{{ resource_prefix }}-pay-per-request-complex" + +table_index: "id" +table_index_type: "NUMBER" + +range_index: "variety" +range_index_type: "STRING" + +indexes: + - name: NamedIndex + type: global_include + hash_key_name: idx + range_key_name: create_time + includes: + - other_field + - other_field2 + read_capacity: 10 + write_capacity: 10 + - name: AnotherIndex + type: global_all + hash_key_name: foo + range_key_name: bar + read_capacity: 5 + write_capacity: 5 + - name: KeysOnlyIndex + type: global_keys_only + hash_key_name: create_time + read_capacity: 2 + write_capacity: 2 + +indexes_pay_per_request: + - name: NamedIndex + type: global_include + hash_key_name: idx + range_key_name: create_time + includes: + - other_field + - other_field2 + - name: AnotherIndex + type: global_all + hash_key_name: foo + range_key_name: bar + - name: KeysOnlyIndex + type: global_keys_only + hash_key_name: create_time + +indexes_pay_per_request_bad: + - name: NamedIndex + type: global_include + hash_key_name: idx + range_key_name: create_time + includes: + - other_field + - other_field2 + - name: AnotherIndex + type: global_all + hash_key_name: foo + range_key_name: bar + includes: + - another_field + - another_field2 + - name: KeysOnlyIndex + type: global_keys_only + hash_key_name: create_time + +index_updated: + - name: NamedIndex + type: global_include + read_capacity: 3 + write_capacity: 3 + - name: AnotherIndex + type: global_all + read_capacity: 4 + +tags_default: + snake_case_key: snake_case_value + camelCaseKey: camelCaseValue + PascalCaseKey: PascalCaseValue + "key with spaces": value with spaces + "Upper With Spaces": Upper With Spaces + +partial_tags: + snake_case_key: snake_case_value + camelCaseKey: camelCaseValue + +updated_tags: + updated_snake_case_key: updated_snake_case_value + updatedCamelCaseKey: updatedCamelCaseValue + UpdatedPascalCaseKey: UpdatedPascalCaseValue + "updated key with spaces": updated value with spaces + "updated Upper With Spaces": Updated Upper With Spaces diff --git a/ansible_collections/community/aws/tests/integration/targets/dynamodb_table/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/dynamodb_table/meta/main.yml new file mode 100644 index 000000000..504e72117 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/dynamodb_table/meta/main.yml @@ -0,0 +1,4 @@ +dependencies: + - role: setup_botocore_pip + vars: + botocore_version: "1.23.18" diff --git a/ansible_collections/community/aws/tests/integration/targets/dynamodb_table/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/dynamodb_table/tasks/main.yml new file mode 100644 index 000000000..b208f4ca5 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/dynamodb_table/tasks/main.yml @@ -0,0 +1,888 @@ +--- +# dynamodb_table integration tests +# +# Current module limitations: +# - changed very flakey +# - various parameters have defaults set so reset undefined value +# +- module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + block: + - include: "test_pay_per_request.yml" + + # ============================================== + + - name: Create table - check_mode + dynamodb_table: + state: present + name: "{{ table_name }}" + hash_key_name: "{{ table_index }}" + hash_key_type: "{{ table_index_type }}" + register: create_table + check_mode: True + + - name: Check results - Create table - check_mode + assert: + that: + - create_table is successful + - create_table is changed + + - name: Create table + dynamodb_table: + state: present + name: "{{ table_name }}" + hash_key_name: "{{ table_index }}" + hash_key_type: "{{ table_index_type }}" + register: create_table + + - name: Check results - Create table + assert: + that: + - create_table is successful + - create_table is changed + - '"hash_key_name" in create_table' + - '"hash_key_type" in create_table' + - '"indexes" in create_table' + - '"range_key_name" in create_table' + - '"range_key_type" in create_table' + - '"read_capacity" in create_table' + - '"region" in create_table' + - '"table_name" in create_table' + - '"table_status" in create_table' + - '"tags" in create_table' + - '"write_capacity" in create_table' + - create_table.hash_key_name == table_index + - create_table.hash_key_type == table_index_type + - create_table.indexes | length == 0 + - create_table.range_key_name is none + # We used to return "STRING" even if there wasn't a key + - create_table.range_key_type is none + - create_table.read_capacity == 1 + - create_table.table_name == table_name + - create_table.write_capacity == 1 + + - name: Create table - idempotent - check_mode + dynamodb_table: + state: present + name: "{{ table_name }}" + hash_key_name: "{{ table_index }}" + hash_key_type: "{{ table_index_type }}" + register: create_table + check_mode: True + + - name: Check results - Create table - idempotent - check_mode + assert: + that: + - create_table is successful + - create_table is not changed + + - name: Create table - idempotent + dynamodb_table: + state: present + name: "{{ table_name }}" + hash_key_name: "{{ table_index }}" + hash_key_type: "{{ table_index_type }}" + register: create_table + + - name: Check results - Create table - idempotent + assert: + that: + - create_table is successful + - create_table is not changed + - '"hash_key_name" in create_table' + - '"hash_key_type" in create_table' + - '"indexes" in create_table' + - '"range_key_name" in create_table' + - '"range_key_type" in create_table' + - '"read_capacity" in create_table' + - '"region" in create_table' + - '"table_name" in create_table' + - '"table_status" in create_table' + - '"tags" in create_table' + - '"write_capacity" in create_table' + - create_table.hash_key_name == table_index + - create_table.hash_key_type == table_index_type + - create_table.indexes | length == 0 + - create_table.range_key_name is none + # We used to return "STRING" even if there wasn't a key + - create_table.range_key_type is none + - create_table.read_capacity == 1 + - create_table.table_name == table_name + - create_table.write_capacity == 1 + + # ============================================== + + - name: Tag table - check_mode + dynamodb_table: + state: present + name: "{{ table_name }}" + tags: "{{ tags_default }}" + register: tag_table + check_mode: True + + - name: Check results - Tag table - check_mode + assert: + that: + - tag_table is successful + - tag_table is changed + + - name: Tag table + dynamodb_table: + state: present + name: "{{ table_name }}" + tags: "{{ tags_default }}" + register: tag_table + + - name: Check results - Tag table + assert: + that: + - tag_table is successful + - tag_table is changed + - '"hash_key_name" in tag_table' + - '"hash_key_type" in tag_table' + - '"indexes" in tag_table' + - '"range_key_name" in tag_table' + - '"range_key_type" in tag_table' + - '"read_capacity" in tag_table' + - '"region" in tag_table' + - '"table_name" in tag_table' + - '"table_status" in tag_table' + - '"tags" in tag_table' + - '"write_capacity" in tag_table' + - tag_table.hash_key_name == table_index + - tag_table.hash_key_type == table_index_type + - tag_table.indexes | length == 0 + - tag_table.range_key_name is none + # We used to return "STRING" even if there wasn't a key + - tag_table.range_key_type is none + - tag_table.read_capacity == 1 + - tag_table.table_name == table_name + - tag_table.write_capacity == 1 + - tag_table.tags == tags_default + + - name: Tag table - idempotent - check_mode + dynamodb_table: + state: present + name: "{{ table_name }}" + tags: "{{ tags_default }}" + register: tag_table + check_mode: True + + - name: Check results - Tag table - idempotent - check_mode + assert: + that: + - tag_table is successful + - tag_table is not changed + + - name: Tag table - idempotent + dynamodb_table: + state: present + name: "{{ table_name }}" + tags: "{{ tags_default }}" + register: tag_table + + - name: Check results - Tag table - idempotent + assert: + that: + - tag_table is successful + - tag_table is not changed + - '"hash_key_name" in tag_table' + - '"hash_key_type" in tag_table' + - '"indexes" in tag_table' + - '"range_key_name" in tag_table' + - '"range_key_type" in tag_table' + - '"read_capacity" in tag_table' + - '"region" in tag_table' + - '"table_name" in tag_table' + - '"table_status" in tag_table' + - '"tags" in tag_table' + - '"write_capacity" in tag_table' + - tag_table.hash_key_name == table_index + - tag_table.hash_key_type == table_index_type + - tag_table.indexes | length == 0 + - tag_table.range_key_name is none + # We used to return "STRING" even if there wasn't a key + - tag_table.range_key_type is none + - tag_table.read_capacity == 1 + - tag_table.table_name == table_name + - tag_table.write_capacity == 1 + - tag_table.tags == tags_default + + # ============================================== + + - name: Update table read capacity - check_mode + dynamodb_table: + state: present + name: "{{ table_name }}" + read_capacity: 3 + register: update_read + check_mode: True + + - name: Check results - Update table read capacity - check_mode + assert: + that: + - update_read is successful + - update_read is changed + + - name: Update table read capacity + dynamodb_table: + state: present + name: "{{ table_name }}" + read_capacity: 3 + register: update_read + + - name: Check results - Update table read capacity + assert: + that: + - update_read is successful + - update_read is changed + - '"hash_key_name" in update_read' + - '"hash_key_type" in update_read' + - '"indexes" in update_read' + - '"range_key_name" in update_read' + - '"range_key_type" in update_read' + - '"read_capacity" in update_read' + - '"region" in update_read' + - '"table_name" in update_read' + - '"table_status" in update_read' + - '"tags" in update_read' + - '"write_capacity" in update_read' + - update_read.hash_key_name == table_index + - update_read.hash_key_type == table_index_type + - update_read.indexes | length == 0 + - update_read.range_key_name is none + # We used to return "STRING" even if there wasn't a key + - update_read.range_key_type is none + - update_read.read_capacity == 3 + - update_read.table_name == table_name + - update_read.write_capacity == 1 + - update_read.tags == tags_default + + - name: Update table read capacity - idempotent - check_mode + dynamodb_table: + state: present + name: "{{ table_name }}" + read_capacity: 3 + register: update_read + check_mode: True + + - name: Check results - Update table read capacity - idempotent - check_mode + assert: + that: + - update_read is successful + - update_read is not changed + + - name: Update table read capacity - idempotent + dynamodb_table: + state: present + name: "{{ table_name }}" + read_capacity: 3 + register: update_read + + - name: Check results - Update table read capacity - idempotent + assert: + that: + - update_read is successful + - update_read is not changed + - '"hash_key_name" in update_read' + - '"hash_key_type" in update_read' + - '"indexes" in update_read' + - '"range_key_name" in update_read' + - '"range_key_type" in update_read' + - '"read_capacity" in update_read' + - '"region" in update_read' + - '"table_name" in update_read' + - '"table_status" in update_read' + - '"tags" in update_read' + - '"write_capacity" in update_read' + - update_read.hash_key_name == table_index + - update_read.hash_key_type == table_index_type + - update_read.indexes | length == 0 + - update_read.range_key_name is none + # We used to return "STRING" even if there wasn't a key + - update_read.range_key_type is none + - update_read.read_capacity == 3 + - update_read.table_name == table_name + - update_read.write_capacity == 1 + - update_read.tags == tags_default + + # ============================================== + + - name: Update table write capacity - check_mode + dynamodb_table: + state: present + name: "{{ table_name }}" + write_capacity: 3 + register: update_write + check_mode: True + + - name: Check results - Update table write capacity - check_mode + assert: + that: + - update_write is successful + - update_write is changed + + - name: Update table write capacity + dynamodb_table: + state: present + name: "{{ table_name }}" + write_capacity: 3 + register: update_write + + - name: Check results - Update table write capacity + assert: + that: + - update_write is successful + - update_write is changed + - '"hash_key_name" in update_write' + - '"hash_key_type" in update_write' + - '"indexes" in update_write' + - '"range_key_name" in update_write' + - '"range_key_type" in update_write' + - '"read_capacity" in update_write' + - '"region" in update_write' + - '"table_name" in update_write' + - '"table_status" in update_write' + - '"tags" in update_write' + - '"write_capacity" in update_write' + - update_write.hash_key_name == table_index + - update_write.hash_key_type == table_index_type + - update_write.indexes | length == 0 + - update_write.range_key_name is none + # We used to return "STRING" even if there wasn't a key + - update_write.range_key_type is none + - update_write.read_capacity == 3 + - update_write.table_name == table_name + - update_write.write_capacity == 3 + - update_write.tags == tags_default + + - name: Update table write capacity - idempotent - check_mode + dynamodb_table: + state: present + name: "{{ table_name }}" + write_capacity: 3 + register: update_write + check_mode: True + + - name: Check results - Update table write capacity - idempotent - check_mode + assert: + that: + - update_write is successful + - update_write is not changed + + - name: Update table write capacity - idempotent + dynamodb_table: + state: present + name: "{{ table_name }}" + write_capacity: 3 + register: update_write + + - name: Check results - Update table write capacity - idempotent + assert: + that: + - update_write is successful + - update_write is not changed + - '"hash_key_name" in update_write' + - '"hash_key_type" in update_write' + - '"indexes" in update_write' + - '"range_key_name" in update_write' + - '"range_key_type" in update_write' + - '"read_capacity" in update_write' + - '"region" in update_write' + - '"table_name" in update_write' + - '"table_status" in update_write' + - '"tags" in update_write' + - '"write_capacity" in update_write' + - update_write.hash_key_name == table_index + - update_write.hash_key_type == table_index_type + - update_write.indexes | length == 0 + - update_write.range_key_name is none + # We used to return "STRING" even if there wasn't a key + - update_write.range_key_type is none + - update_write.read_capacity == 3 + - update_write.table_name == table_name + - update_write.write_capacity == 3 + - update_write.tags == tags_default + + # ============================================== + # Attempting to update the primary indexes now will result in an expected failure. + + - name: Update table add range index - test failure + dynamodb_table: + state: present + name: "{{ table_name }}" + range_key_name: "{{ range_index }}" + range_key_type: "{{ range_index_type }}" + ignore_errors: yes + register: update_range_index + + - name: Check results - Update table add range index + assert: + that: + - update_range_index is failed + + # ============================================== + + - name: Update table add indexes - check_mode + dynamodb_table: + state: present + name: "{{ table_name }}" + indexes: "{{ indexes }}" + register: update_indexes + check_mode: True + + - name: Check results - Update table add indexes - check_mode + assert: + that: + - update_indexes is successful + - update_indexes is changed + + - name: Update table add indexes + dynamodb_table: + state: present + name: "{{ table_name }}" + indexes: "{{ indexes }}" + register: update_indexes + + - name: Check results - Update table add indexes + assert: + that: + - update_indexes is successful + - update_indexes is changed + - '"hash_key_name" in update_indexes' + - '"hash_key_type" in update_indexes' + - '"indexes" in update_indexes' + - '"range_key_name" in update_indexes' + - '"range_key_type" in update_indexes' + - '"read_capacity" in update_indexes' + - '"region" in update_indexes' + - '"table_name" in update_indexes' + - '"table_status" in update_indexes' + - '"tags" in update_indexes' + - '"write_capacity" in update_indexes' + - update_indexes.hash_key_name == table_index + - update_indexes.hash_key_type == table_index_type + - update_indexes.indexes | length == 3 + # - update_indexes.range_key_name == range_index + # - update_indexes.range_key_type == range_index_type + - update_indexes.read_capacity == 3 + - update_indexes.table_name == table_name + - update_indexes.write_capacity == 3 + - update_indexes.tags == tags_default + + - name: Update table add indexes - idempotent - check_mode + dynamodb_table: + state: present + name: "{{ table_name }}" + indexes: "{{ indexes }}" + register: update_indexes + check_mode: True + + - name: Check results - Update table add indexes - idempotent - check_mode + assert: + that: + - update_indexes is successful + - update_indexes is not changed + + - name: Update table add indexes - idempotent + dynamodb_table: + state: present + name: "{{ table_name }}" + indexes: "{{ indexes }}" + register: update_indexes + + - name: Check results - Update table add indexes - idempotent + assert: + that: + - update_indexes is successful + - update_indexes is not changed + - '"hash_key_name" in update_indexes' + - '"hash_key_type" in update_indexes' + - '"indexes" in update_indexes' + - '"range_key_name" in update_indexes' + - '"range_key_type" in update_indexes' + - '"read_capacity" in update_indexes' + - '"region" in update_indexes' + - '"table_name" in update_indexes' + - '"table_status" in update_indexes' + - '"tags" in update_indexes' + - '"write_capacity" in update_indexes' + - update_indexes.hash_key_name == table_index + - update_indexes.hash_key_type == table_index_type + - update_indexes.indexes | length == 3 + # - update_indexes.range_key_name == range_index + # - update_indexes.range_key_type == range_index_type + - update_indexes.read_capacity == 3 + - update_indexes.table_name == table_name + - update_indexes.write_capacity == 3 + - update_indexes.tags == tags_default + + # ============================================== + + - name: Delete table - check_mode + dynamodb_table: + state: absent + name: "{{ table_name }}" + register: delete_table + check_mode: True + + - name: Check results - Delete table - check_mode + assert: + that: + - delete_table is successful + - delete_table is changed + + - name: Delete table + dynamodb_table: + state: absent + name: "{{ table_name }}" + register: delete_table + + - name: Check results - Delete table + assert: + that: + - delete_table is successful + - delete_table is changed + + - name: Delete table - idempotent - check_mode + dynamodb_table: + state: absent + name: "{{ table_name }}" + register: delete_table + check_mode: True + + - name: Check results - Delete table - idempotent - check_mode + assert: + that: + - delete_table is successful + - delete_table is not changed + + - name: Delete table - idempotent + dynamodb_table: + state: absent + name: "{{ table_name }}" + register: delete_table + + - name: Check results - Delete table - idempotent + assert: + that: + - delete_table is successful + - delete_table is not changed + + # ============================================== + - name: Create complex table - check_mode + dynamodb_table: + state: present + name: "{{ table_name }}" + hash_key_name: "{{ table_index }}" + hash_key_type: "{{ table_index_type }}" + range_key_name: "{{ range_index }}" + range_key_type: "{{ range_index_type }}" + read_capacity: 3 + write_capacity: 3 + table_class: "STANDARD_INFREQUENT_ACCESS" + tags: "{{ tags_default }}" + indexes: "{{ indexes }}" + register: create_complex_table + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + check_mode: True + + - name: Check results - Create complex table - check_mode + assert: + that: + - create_complex_table is successful + - create_complex_table is changed + + - name: Create complex table + dynamodb_table: + state: present + name: "{{ table_name }}" + hash_key_name: "{{ table_index }}" + hash_key_type: "{{ table_index_type }}" + range_key_name: "{{ range_index }}" + range_key_type: "{{ range_index_type }}" + read_capacity: 3 + write_capacity: 3 + table_class: "STANDARD_INFREQUENT_ACCESS" + tags: "{{ tags_default }}" + indexes: "{{ indexes }}" + register: create_complex_table + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + + - name: Check results - Create complex table + assert: + that: + - create_complex_table is successful + - create_complex_table is changed + - '"hash_key_name" in create_complex_table' + - '"hash_key_type" in create_complex_table' + - '"indexes" in create_complex_table' + - '"range_key_name" in create_complex_table' + - '"range_key_type" in create_complex_table' + - '"read_capacity" in create_complex_table' + - '"region" in create_complex_table' + - '"table_name" in create_complex_table' + - '"table_status" in create_complex_table' + - '"tags" in create_complex_table' + - '"write_capacity" in create_complex_table' + - create_complex_table.hash_key_name == table_index + - create_complex_table.hash_key_type == table_index_type + - create_complex_table.indexes | length == 3 + - create_complex_table.range_key_name == range_index + - create_complex_table.range_key_type == range_index_type + - create_complex_table.read_capacity == 3 + - create_complex_table.table_name == table_name + - create_complex_table.table_class == "STANDARD_INFREQUENT_ACCESS" + - create_complex_table.write_capacity == 3 + - create_complex_table.tags == tags_default + + - name: Create complex table - idempotent - check_mode + dynamodb_table: + state: present + name: "{{ table_name }}" + hash_key_name: "{{ table_index }}" + hash_key_type: "{{ table_index_type }}" + range_key_name: "{{ range_index }}" + range_key_type: "{{ range_index_type }}" + read_capacity: 3 + write_capacity: 3 + table_class: "STANDARD_INFREQUENT_ACCESS" + tags: "{{ tags_default }}" + indexes: "{{ indexes }}" + register: create_complex_table + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + check_mode: True + + - name: Check results - Create complex table - idempotent - check_mode + assert: + that: + - create_complex_table is successful + - create_complex_table is not changed + + - name: Create complex table - idempotent + dynamodb_table: + state: present + name: "{{ table_name }}" + hash_key_name: "{{ table_index }}" + hash_key_type: "{{ table_index_type }}" + range_key_name: "{{ range_index }}" + range_key_type: "{{ range_index_type }}" + read_capacity: 3 + write_capacity: 3 + table_class: "STANDARD_INFREQUENT_ACCESS" + tags: "{{ tags_default }}" + indexes: "{{ indexes }}" + register: create_complex_table + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + + - name: Check results - Create complex table - idempotent + assert: + that: + - create_complex_table is successful + - create_complex_table is not changed + - '"hash_key_name" in create_complex_table' + - '"hash_key_type" in create_complex_table' + - '"indexes" in create_complex_table' + - '"range_key_name" in create_complex_table' + - '"range_key_type" in create_complex_table' + - '"read_capacity" in create_complex_table' + - '"region" in create_complex_table' + - '"table_name" in create_complex_table' + - '"table_status" in create_complex_table' + - '"tags" in create_complex_table' + - '"write_capacity" in create_complex_table' + - create_complex_table.hash_key_name == table_index + - create_complex_table.hash_key_type == table_index_type + - create_complex_table.indexes | length == 3 + - create_complex_table.range_key_name == range_index + - create_complex_table.range_key_type == range_index_type + - create_complex_table.read_capacity == 3 + - create_complex_table.table_name == table_name + - create_complex_table.table_class == "STANDARD_INFREQUENT_ACCESS" + - create_complex_table.write_capacity == 3 + - create_complex_table.tags == tags_default + + # ============================================== + # Update table class on exisiting table + + - name: Update table class - check_mode + dynamodb_table: + state: present + name: "{{ table_name }}" + table_class: "STANDARD" + register: update_class + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + check_mode: True + + - name: Check results - Update table class - check_mode + assert: + that: + - update_class is successful + - update_class is changed + + - name: Update table class + dynamodb_table: + state: present + name: "{{ table_name }}" + table_class: "STANDARD" + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + register: update_class + + - name: Check results - Update table class + assert: + that: + - update_class is successful + - update_class is changed + - update_class.table_name == table_name + - update_class.table_class == "STANDARD" + + # ============================================== + # Update table index on exisiting table + + - name: Update table update index - check_mode + dynamodb_table: + state: present + name: "{{ table_name }}" + indexes: "{{ index_updated }}" + register: update_index + check_mode: True + + - name: Check results - Update table update index - check_mode + assert: + that: + - update_index is successful + - update_index is changed + + - name: Update table update index + dynamodb_table: + state: present + name: "{{ table_name }}" + indexes: "{{ index_updated }}" + register: update_index + + - name: Check results - Update table update index + assert: + that: + - update_index is successful + - update_index is changed + - '"hash_key_name" in update_index' + - '"hash_key_type" in update_index' + - '"indexes" in update_index' + - '"range_key_name" in update_index' + - '"range_key_type" in update_index' + - '"read_capacity" in update_index' + - '"region" in update_index' + - '"table_name" in update_index' + - '"table_status" in update_index' + - '"tags" in update_index' + - '"write_capacity" in update_index' + - update_index.hash_key_name == table_index + - update_index.hash_key_type == table_index_type + - update_index.indexes | length == 3 + - update_index.range_key_name == range_index + - update_index.range_key_type == range_index_type + - update_index.read_capacity == 3 + - update_index.table_name == table_name + - update_index.write_capacity == 3 + - update_index.tags == tags_default + + - name: Pause to allow index to finish updating + pause: + seconds: 20 + + - name: Update table update index - idempotent - check_mode + dynamodb_table: + state: present + name: "{{ table_name }}" + indexes: "{{ index_updated }}" + register: update_index + check_mode: True + + - name: Check results - Update table update index - idempotent - check_mode + assert: + that: + - update_index is successful + - update_index is not changed + + - name: Update table update index - idempotent + dynamodb_table: + state: present + name: "{{ table_name }}" + indexes: "{{ index_updated }}" + register: update_index + + - name: Check results - Update table update index - idempotent + assert: + that: + - update_index is successful + - update_index is not changed + - '"hash_key_name" in update_index' + - '"hash_key_type" in update_index' + - '"indexes" in update_index' + - '"range_key_name" in update_index' + - '"range_key_type" in update_index' + - '"read_capacity" in update_index' + - '"region" in update_index' + - '"table_name" in update_index' + - '"table_status" in update_index' + - '"tags" in update_index' + - '"write_capacity" in update_index' + - update_index.hash_key_name == table_index + - update_index.hash_key_type == table_index_type + - update_index.indexes | length == 3 + - update_index.range_key_name == range_index + - update_index.range_key_type == range_index_type + - update_index.read_capacity == 3 + - update_index.table_name == table_name + - update_index.write_capacity == 3 + - update_index.tags == tags_default + + # ============================================== + + - name: Delete table + dynamodb_table: + state: absent + name: "{{ table_name }}" + register: delete_table + + - name: Check results - Delete table + assert: + that: + - delete_table is successful + - delete_table is changed + + always: + ################################################ + # TEARDOWN STARTS HERE + ################################################ + + - name: Delete provisoned table + dynamodb_table: + state: absent + name: "{{ table_name }}" + wait: false + register: delete_table + + - name: Delete on-demand table + dynamodb_table: + state: absent + name: "{{ table_name_on_demand }}" + wait: false + register: delete_table + + - name: Delete complex on-demand table + dynamodb_table: + state: absent + name: "{{ table_name_on_demand_complex }}" + wait: false + register: delete_table diff --git a/ansible_collections/community/aws/tests/integration/targets/dynamodb_table/tasks/test_pay_per_request.yml b/ansible_collections/community/aws/tests/integration/targets/dynamodb_table/tasks/test_pay_per_request.yml new file mode 100644 index 000000000..a05021154 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/dynamodb_table/tasks/test_pay_per_request.yml @@ -0,0 +1,141 @@ +--- +- name: Create table - pay-per-request - check_mode + dynamodb_table: + state: present + name: "{{ table_name_on_demand }}" + hash_key_name: "{{ table_index }}" + hash_key_type: "{{ table_index_type }}" + billing_mode: PAY_PER_REQUEST + register: create_table + check_mode: True + +- name: Check results - Create table - check_mode + assert: + that: + - create_table is successful + - create_table is changed + +- name: Create table - pay-per-request + dynamodb_table: + state: present + name: "{{ table_name_on_demand }}" + hash_key_name: "{{ table_index }}" + hash_key_type: "{{ table_index_type }}" + billing_mode: PAY_PER_REQUEST + register: create_table + +- name: Check results - Create table + assert: + that: + - create_table is successful + - create_table is changed + - create_table.billing_mode == "PAY_PER_REQUEST" + +- name: Create table - pay-per-request - check failure + dynamodb_table: + state: present + name: "{{ table_name_on_demand }}" + hash_key_name: "{{ table_index }}" + hash_key_type: "{{ table_index_type }}" + range_key_name: "{{ range_index }}" + range_key_type: "{{ range_index_type }}" + billing_mode: PAY_PER_REQUEST + ignore_errors: yes + register: create_table_bad + +- name: Check results - Create table + assert: + that: + - create_table_bad is failed + +# ============================================== + +- name: Create complex table - check_mode + dynamodb_table: + state: present + name: "{{ table_name_on_demand_complex }}" + hash_key_name: "{{ table_index }}" + hash_key_type: "{{ table_index_type }}" + range_key_name: "{{ range_index }}" + range_key_type: "{{ range_index_type }}" + billing_mode: PAY_PER_REQUEST + tags: "{{ tags_default }}" + indexes: "{{ indexes_pay_per_request }}" + register: create_complex_table + check_mode: True + +- name: Check results - Create complex table - check_mode + assert: + that: + - create_complex_table is successful + - create_complex_table is changed + +- name: Create complex table - check failure on index + dynamodb_table: + state: present + name: "{{ table_name_on_demand_complex }}" + hash_key_name: "{{ table_index }}" + hash_key_type: "{{ table_index_type }}" + billing_mode: PAY_PER_REQUEST + tags: "{{ tags_default }}" + indexes: "{{ indexes_pay_per_request_bad }}" + ignore_errors: yes + register: create_complex_table_bad + +- name: Check results - Create complex table + assert: + that: + - create_complex_table_bad is failure + +- name: Create complex table + dynamodb_table: + state: present + name: "{{ table_name_on_demand_complex }}" + hash_key_name: "{{ table_index }}" + hash_key_type: "{{ table_index_type }}" + billing_mode: PAY_PER_REQUEST + tags: "{{ tags_default }}" + indexes: "{{ indexes_pay_per_request }}" + register: create_complex_table + +- name: Check results - Create complex table + assert: + that: + - create_complex_table is successful + - create_complex_table is changed + - '"hash_key_name" in create_complex_table' + - '"hash_key_type" in create_complex_table' + - '"indexes" in create_complex_table' + - '"range_key_name" in create_complex_table' + - '"range_key_type" in create_complex_table' + - '"billing_mode" in create_complex_table' + - '"region" in create_complex_table' + - '"table_name" in create_complex_table' + - '"table_status" in create_complex_table' + - '"tags" in create_complex_table' + - create_complex_table.hash_key_name == table_index + - create_complex_table.hash_key_type == table_index_type + - create_complex_table.indexes | length == 3 + - create_complex_table.table_name == table_name_on_demand_complex + - create_complex_table.tags == tags_default + +- name: Update complex table billing_mode + dynamodb_table: + state: present + name: "{{ table_name_on_demand_complex }}" + hash_key_name: "{{ table_index }}" + hash_key_type: "{{ table_index_type }}" + billing_mode: PROVISIONED + read_capacity: 1 + write_capacity: 1 + tags: "{{ tags_default }}" + indexes: "{{ indexes }}" + register: convert_complex_table + +- name: Check results - Update complex table billing_mode + assert: + that: + - convert_complex_table is successful + - convert_complex_table is changed + - '"billing_mode" in convert_complex_table' + - convert_complex_table.billing_mode == "PROVISIONED" diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_launch_template/aliases b/ansible_collections/community/aws/tests/integration/targets/ec2_launch_template/aliases new file mode 100644 index 000000000..4ef4b2067 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_launch_template/aliases @@ -0,0 +1 @@ +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_launch_template/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/ec2_launch_template/defaults/main.yml new file mode 100644 index 000000000..269083ee5 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_launch_template/defaults/main.yml @@ -0,0 +1,2 @@ +--- +test_role_name: ansible-test-{{ tiny_prefix }} diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_launch_template/files/assume-role-policy.json b/ansible_collections/community/aws/tests/integration/targets/ec2_launch_template/files/assume-role-policy.json new file mode 100644 index 000000000..72413abdd --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_launch_template/files/assume-role-policy.json @@ -0,0 +1,13 @@ +{ + "Version": "2008-10-17", + "Statement": [ + { + "Sid": "", + "Effect": "Allow", + "Principal": { + "Service": "ec2.amazonaws.com" + }, + "Action": "sts:AssumeRole" + } + ] +} diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_launch_template/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/ec2_launch_template/meta/main.yml new file mode 100644 index 000000000..ca18dd30f --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_launch_template/meta/main.yml @@ -0,0 +1,5 @@ +dependencies: + - setup_ec2_facts + - role: setup_botocore_pip + vars: + botocore_version: "1.23.30" diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_launch_template/tasks/cpu_options.yml b/ansible_collections/community/aws/tests/integration/targets/ec2_launch_template/tasks/cpu_options.yml new file mode 100644 index 000000000..92d7fac5f --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_launch_template/tasks/cpu_options.yml @@ -0,0 +1,38 @@ +- block: + - name: delete a non-existent template + ec2_launch_template: + name: "{{ resource_prefix }}-not-a-real-template" + state: absent + register: del_fake_lt + ignore_errors: true + - assert: + that: + - del_fake_lt is not failed + - name: create c4.large instance with cpu_options + ec2_launch_template: + name: "{{ resource_prefix }}-c4large-1-threads-per-core" + image_id: "{{ ec2_ami_id }}" + tags: + TestId: "{{ resource_prefix }}" + instance_type: c4.large + cpu_options: + core_count: 1 + threads_per_core: 1 + register: lt + + - name: instance with cpu_options created with the right options + assert: + that: + - lt is success + - lt is changed + - "lt.latest_template.launch_template_data.cpu_options.core_count == 1" + - "lt.latest_template.launch_template_data.cpu_options.threads_per_core == 1" + always: + - name: delete the template + ec2_launch_template: + name: "{{ resource_prefix }}-c4large-1-threads-per-core" + state: absent + register: del_lt + retries: 10 + until: del_lt is not failed + ignore_errors: true diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_launch_template/tasks/iam_instance_role.yml b/ansible_collections/community/aws/tests/integration/targets/ec2_launch_template/tasks/iam_instance_role.yml new file mode 100644 index 000000000..c26b96d69 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_launch_template/tasks/iam_instance_role.yml @@ -0,0 +1,134 @@ +- block: + - name: Create IAM role for test + iam_role: + name: "{{ test_role_name }}-1" + assume_role_policy_document: "{{ lookup('file','assume-role-policy.json') }}" + state: present + create_instance_profile: yes + managed_policy: + - AWSDenyAll + register: iam_role + + - name: Create second IAM role for test + iam_role: + name: "{{ test_role_name }}-2" + assume_role_policy_document: "{{ lookup('file','assume-role-policy.json') }}" + state: present + create_instance_profile: yes + managed_policy: + - AWSDenyAll + register: iam_role_2 + + - name: Make instance with an instance_role + ec2_launch_template: + name: "{{ resource_prefix }}-test-instance-role" + image_id: "{{ ec2_ami_id }}" + instance_type: t2.micro + iam_instance_profile: "{{ test_role_name }}-1" + register: template_with_role + + - assert: + that: + - 'template_with_role.default_template.launch_template_data.iam_instance_profile.arn == iam_role.arn.replace(":role/", ":instance-profile/")' + + - name: Create template again, with no change to instance_role + ec2_launch_template: + name: "{{ resource_prefix }}-test-instance-role" + image_id: "{{ ec2_ami_id }}" + instance_type: t2.micro + iam_instance_profile: "{{ test_role_name }}-1" + register: template_with_role + + - assert: + that: + - 'template_with_role.default_template.launch_template_data.iam_instance_profile.arn == iam_role.arn.replace(":role/", ":instance-profile/")' + - 'template_with_role is not changed' + + - name: Update instance with new instance_role + ec2_launch_template: + name: "{{ resource_prefix }}-test-instance-role" + image_id: "{{ ec2_ami_id }}" + instance_type: t2.micro + iam_instance_profile: "{{ test_role_name }}-2" + register: template_with_updated_role + + - assert: + that: + - 'template_with_updated_role.default_template.launch_template_data.iam_instance_profile.arn == iam_role_2.arn.replace(":role/", ":instance-profile/")' + - 'template_with_updated_role.default_template.launch_template_data.iam_instance_profile.arn == iam_role_2.arn.replace(":role/", ":instance-profile/")' + - 'template_with_role.default_template.version_number < template_with_updated_role.default_template.version_number' + - 'template_with_updated_role is changed' + - 'template_with_updated_role is not failed' + + - name: Re-set with same new instance_role + ec2_launch_template: + name: "{{ resource_prefix }}-test-instance-role" + image_id: "{{ ec2_ami_id }}" + instance_type: t2.micro + iam_instance_profile: "{{ test_role_name }}-2" + register: template_with_updated_role + + - assert: + that: + - 'template_with_updated_role is not changed' + - 'template_with_updated_role.default_template.launch_template_data.iam_instance_profile.arn == iam_role_2.arn.replace(":role/", ":instance-profile/")' + + - name: Update instance with original instance_role (pass profile ARN) + ec2_launch_template: + name: "{{ resource_prefix }}-test-instance-role" + image_id: "{{ ec2_ami_id }}" + instance_type: t2.micro + # By default an instance profile will be created with the same name as the role + iam_instance_profile: '{{ iam_role.arn.replace(":role/", ":instance-profile/") }}' + register: template_with_updated_role + + - assert: + that: + - 'template_with_updated_role.default_template.launch_template_data.iam_instance_profile.arn == iam_role.arn.replace(":role/", ":instance-profile/")' + - 'template_with_updated_role.default_template.launch_template_data.iam_instance_profile.arn == iam_role.arn.replace(":role/", ":instance-profile/")' + - 'template_with_role.default_template.version_number < template_with_updated_role.default_template.version_number' + - 'template_with_updated_role is changed' + - 'template_with_updated_role is not failed' + + - name: Re-set with same new instance_role (pass profile ARN) + ec2_launch_template: + name: "{{ resource_prefix }}-test-instance-role" + image_id: "{{ ec2_ami_id }}" + instance_type: t2.micro + iam_instance_profile: '{{ iam_role.arn.replace(":role/", ":instance-profile/") }}' + register: template_with_updated_role + + - assert: + that: + - 'template_with_updated_role is not changed' + - 'template_with_updated_role.default_template.launch_template_data.iam_instance_profile.arn == iam_role.arn.replace(":role/", ":instance-profile/")' + + always: + - name: delete launch template + ec2_launch_template: + name: "{{ resource_prefix }}-test-instance-role" + state: absent + register: lt_removed + until: lt_removed is not failed + ignore_errors: yes + retries: 10 + - name: Delete IAM role for test + iam_role: + name: "{{ test_role_name }}-1" + assume_role_policy_document: "{{ lookup('file','assume-role-policy.json') }}" + state: absent + delete_instance_profile: yes + register: iam_removed + until: iam_removed is not failed + ignore_errors: yes + retries: 10 + - name: Delete IAM role for test + iam_role: + name: "{{ test_role_name }}-2" + assume_role_policy_document: "{{ lookup('file','assume-role-policy.json') }}" + state: absent + delete_instance_profile: yes + register: iam_2_removed + until: iam_2_removed is not failed + ignore_errors: yes + retries: 10 diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_launch_template/tasks/instance-metadata.yml b/ansible_collections/community/aws/tests/integration/targets/ec2_launch_template/tasks/instance-metadata.yml new file mode 100644 index 000000000..afe907f4f --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_launch_template/tasks/instance-metadata.yml @@ -0,0 +1,78 @@ +--- +- name: test with older boto3 version that does not support instance_metadata_tags + block: + - name: fail metadata_options + ec2_launch_template: + name: "{{ resource_prefix }}-test-metadata" + metadata_options: + http_put_response_hop_limit: 1 + http_tokens: required + http_protocol_ipv6: enabled + instance_metadata_tags: enabled + state: present + register: metadata_options_launch_template + ignore_errors: yes + - name: verify fail with usefull error message + assert: + that: + - metadata_options_launch_template.failed + - metadata_options_launch_template is not changed + - "'This is required to set instance_metadata_tags' in metadata_options_launch_template.msg" + + - name: success metadata_options + ec2_launch_template: + name: "{{ resource_prefix }}-test-metadata" + metadata_options: + http_put_response_hop_limit: 1 + http_tokens: required + state: present + register: metadata_options_launch_template + - name: instance with metadata_options created with the right options + assert: + that: + - metadata_options_launch_template is changed + - "metadata_options_launch_template.latest_template.launch_template_data.metadata_options.http_put_response_hop_limit == 1" + - "metadata_options_launch_template.latest_template.launch_template_data.metadata_options.http_tokens == 'required'" + - "metadata_options_launch_template.latest_template.launch_template_data.metadata_options.http_protocol_ipv6 is not defined" + - "metadata_options_launch_template.latest_template.launch_template_data.metadata_options.instance_metadata_tags is not defined" + always: + - name: delete the template + ec2_launch_template: + name: "{{ resource_prefix }}-test-metadata" + state: absent + register: del_lt + retries: 10 + until: del_lt is not failed + ignore_errors: true + +- name: test with boto3 version that supports instance_metadata_tags + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + block: + - name: metadata_options + ec2_launch_template: + name: "{{ resource_prefix }}-test-metadata" + metadata_options: + http_put_response_hop_limit: 1 + http_tokens: required + http_protocol_ipv6: enabled + instance_metadata_tags: enabled + state: present + register: metadata_options_launch_template + - name: instance with metadata_options created with the right options + assert: + that: + - metadata_options_launch_template is changed + - "metadata_options_launch_template.latest_template.launch_template_data.metadata_options.http_put_response_hop_limit == 1" + - "metadata_options_launch_template.latest_template.launch_template_data.metadata_options.http_tokens == 'required'" + - "metadata_options_launch_template.latest_template.launch_template_data.metadata_options.http_protocol_ipv6 == 'enabled'" + - "metadata_options_launch_template.latest_template.launch_template_data.metadata_options.instance_metadata_tags == 'enabled'" + always: + - name: delete the template + ec2_launch_template: + name: "{{ resource_prefix }}-test-metadata" + state: absent + register: del_lt + retries: 10 + until: del_lt is not failed + ignore_errors: true diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_launch_template/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/ec2_launch_template/tasks/main.yml new file mode 100644 index 000000000..aa87871ce --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_launch_template/tasks/main.yml @@ -0,0 +1,13 @@ +--- +- module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + block: + - include_tasks: cpu_options.yml + - include_tasks: iam_instance_role.yml + - include_tasks: versions.yml + - include_tasks: instance-metadata.yml + - include_tasks: network_interfaces.yml diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_launch_template/tasks/network_interfaces.yml b/ansible_collections/community/aws/tests/integration/targets/ec2_launch_template/tasks/network_interfaces.yml new file mode 100644 index 000000000..a2ca0e5f6 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_launch_template/tasks/network_interfaces.yml @@ -0,0 +1,53 @@ +- block: + - name: network_interfaces + ec2_launch_template: + name: "{{ resource_prefix }}-test-nic" + state: present + network_interfaces: + - device_index: 0 + associate_public_ip_address: false + delete_on_termination: true + - device_index: 1 + associate_public_ip_address: true + delete_on_termination: false + ipv6_address_count: 1 + register: nic_template + - name: instance with network_interfaces created with the right settings + assert: + that: + - nic_template is changed + - nic_template.default_template.launch_template_data.network_interfaces[0].associate_public_ip_address == False + - nic_template.default_template.launch_template_data.network_interfaces[0].delete_on_termination == True + - nic_template.default_template.launch_template_data.network_interfaces[0].device_index == 0 + - nic_template.default_template.launch_template_data.network_interfaces[1].associate_public_ip_address == True + - nic_template.default_template.launch_template_data.network_interfaces[1].delete_on_termination == False + - nic_template.default_template.launch_template_data.network_interfaces[1].device_index == 1 + - nic_template.default_template.launch_template_data.network_interfaces[1].ipv6_address_count == 1 + + - name: network_interfaces + ec2_launch_template: + name: "{{ resource_prefix }}-test-nic" + state: present + network_interfaces: + - device_index: 0 + associate_public_ip_address: false + delete_on_termination: true + - device_index: 1 + associate_public_ip_address: true + delete_on_termination: false + ipv6_address_count: 1 + register: nic_template + - name: instance with network_interfaces created with the right settings + assert: + that: + - nic_template is not changed + + always: + - name: delete the template + ec2_launch_template: + name: "{{ resource_prefix }}-test-nic" + state: absent + register: del_lt + retries: 10 + until: del_lt is not failed + ignore_errors: true diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_launch_template/tasks/tags_and_vpc_settings.yml b/ansible_collections/community/aws/tests/integration/targets/ec2_launch_template/tasks/tags_and_vpc_settings.yml new file mode 100644 index 000000000..026c59907 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_launch_template/tasks/tags_and_vpc_settings.yml @@ -0,0 +1,208 @@ +- block: + # ============================================================ + # set up VPC + - name: Create VPC for use in testing + ec2_vpc_net: + name: "{{ resource_prefix }}-vpc" + cidr_block: 10.99.0.0/16 + tags: + Name: Ansible ec2_instance Testing VPC + tenancy: default + register: testing_vpc + + - name: Create default subnet in zone A + ec2_vpc_subnet: + state: present + vpc_id: "{{ testing_vpc.vpc.id }}" + cidr: 10.99.0.0/24 + az: "{{ aws_region }}a" + resource_tags: + Name: "{{ resource_prefix }}-subnet-a" + register: testing_subnet_a + + - name: Create secondary subnet in zone B + ec2_vpc_subnet: + state: present + vpc_id: "{{ testing_vpc.vpc.id }}" + cidr: 10.99.1.0/24 + az: "{{ aws_region }}b" + resource_tags: + Name: "{{ resource_prefix }}-subnet-b" + register: testing_subnet_b + + - name: create a security group with the vpc + ec2_group: + name: "{{ resource_prefix }}-sg" + description: a security group for ansible tests + vpc_id: "{{ testing_vpc.vpc.id }}" + rules: + - proto: tcp + ports: [22, 80] + cidr_ip: 0.0.0.0/0 + register: sg + # TODO: switch these tests from instances + - assert: + that: + - 1 == 0 + # ============================================================ + # start subnet/sg testing + - name: Make instance in the testing subnet created in the test VPC + ec2_instance: + name: "{{ resource_prefix }}-test-basic-vpc-create" + image_id: "{{ ec2_ami_id }}" + user_data: | + #cloud-config + package_upgrade: true + package_update: true + tags: + TestId: "{{ resource_prefix }}" + Something: else + security_groups: "{{ sg.group_id }}" + network: + source_dest_check: false + vpc_subnet_id: "{{ testing_subnet_b.subnet.id }}" + instance_type: t2.micro + volumes: + - device_name: /dev/sda1 + ebs: + delete_on_termination: true + register: in_test_vpc + + - name: Try to re-make the instance, hopefully this shows changed=False + ec2_instance: + name: "{{ resource_prefix }}-test-basic-vpc-create" + image_id: "{{ ec2_ami_id }}" + user_data: | + #cloud-config + package_upgrade: true + package_update: true + tags: + TestId: "{{ resource_prefix }}" + Something: else + security_groups: "{{ sg.group_id }}" + vpc_subnet_id: "{{ testing_subnet_b.subnet.id }}" + instance_type: t2.micro + register: remake_in_test_vpc + - name: "Remaking the same instance resulted in no changes" + assert: + that: not remake_in_test_vpc.changed + - name: check that instance IDs match anyway + assert: + that: 'remake_in_test_vpc.instance_ids[0] == in_test_vpc.instance_ids[0]' + - name: check that source_dest_check was set to false + assert: + that: 'not remake_in_test_vpc.instances[0].source_dest_check' + + - name: Alter it by adding tags + ec2_instance: + name: "{{ resource_prefix }}-test-basic-vpc-create" + image_id: "{{ ec2_ami_id }}" + tags: + TestId: "{{ resource_prefix }}" + Another: thing + security_groups: "{{ sg.group_id }}" + vpc_subnet_id: "{{ testing_subnet_b.subnet.id }}" + instance_type: t2.micro + register: add_another_tag + + - ec2_instance_info: + instance_ids: "{{ add_another_tag.instance_ids }}" + register: check_tags + - name: "Remaking the same instance resulted in no changes" + assert: + that: + - check_tags.instances[0].tags.Another == 'thing' + - check_tags.instances[0].tags.Something == 'else' + + - name: Purge a tag + ec2_instance: + name: "{{ resource_prefix }}-test-basic-vpc-create" + image_id: "{{ ec2_ami_id }}" + purge_tags: true + tags: + TestId: "{{ resource_prefix }}" + Another: thing + security_groups: "{{ sg.group_id }}" + vpc_subnet_id: "{{ testing_subnet_b.subnet.id }}" + instance_type: t2.micro + - ec2_instance_info: + instance_ids: "{{ add_another_tag.instance_ids }}" + register: check_tags + - name: "Remaking the same instance resulted in no changes" + assert: + that: + - "'Something' not in check_tags.instances[0].tags" + + - name: Terminate instance + ec2_instance: + filters: + tag:TestId: "{{ resource_prefix }}" + state: absent + register: result + - assert: + that: result.changed + + - name: Terminate instance + ec2_instance: + instance_ids: "{{ in_test_vpc.instance_ids }}" + state: absent + register: result + - assert: + that: not result.changed + + - name: check that subnet-default public IP rule was followed + assert: + that: + - in_test_vpc.instances[0].public_dns_name == "" + - in_test_vpc.instances[0].private_ip_address.startswith("10.22.33") + - in_test_vpc.instances[0].subnet_id == testing_subnet_b.subnet.id + - name: check that tags were applied + assert: + that: + - in_test_vpc.instances[0].tags.Name.startswith(resource_prefix) + - in_test_vpc.instances[0].state.name == 'running' + + always: + - name: remove the security group + ec2_group: + name: "{{ resource_prefix }}-sg" + description: a security group for ansible tests + vpc_id: "{{ testing_vpc.vpc.id }}" + state: absent + register: removed + until: removed is not failed + ignore_errors: yes + retries: 10 + + - name: remove subnet A + ec2_vpc_subnet: + state: absent + vpc_id: "{{ testing_vpc.vpc.id }}" + cidr: 10.99.0.0/24 + register: removed + until: removed is not failed + ignore_errors: yes + retries: 10 + + - name: remove subnet B + ec2_vpc_subnet: + state: absent + vpc_id: "{{ testing_vpc.vpc.id }}" + cidr: 10.99.1.0/24 + register: removed + until: removed is not failed + ignore_errors: yes + retries: 10 + + - name: remove the VPC + ec2_vpc_net: + name: "{{ resource_prefix }}-vpc" + cidr_block: 10.99.0.0/16 + state: absent + tags: + Name: Ansible Testing VPC + tenancy: default + register: removed + until: removed is not failed + ignore_errors: yes + retries: 10 diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_launch_template/tasks/versions.yml b/ansible_collections/community/aws/tests/integration/targets/ec2_launch_template/tasks/versions.yml new file mode 100644 index 000000000..a9e40cd08 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_launch_template/tasks/versions.yml @@ -0,0 +1,95 @@ +- block: + - name: create simple instance template + ec2_launch_template: + name: "{{ resource_prefix }}-simple" + image_id: "{{ ec2_ami_id }}" + tags: + TestId: "{{ resource_prefix }}" + instance_type: c4.large + register: lt + + - name: instance with cpu_options created with the right options + assert: + that: + - lt is success + - lt is changed + - lt.default_version == 1 + - lt.latest_version == 1 + + - name: update simple instance template + ec2_launch_template: + name: "{{ resource_prefix }}-simple" + default_version: 1 + image_id: "{{ ec2_ami_id }}" + tags: + TestId: "{{ resource_prefix }}" + instance_type: m5.large + register: lt + + - name: instance with cpu_options created with the right options + assert: + that: + - lt is success + - lt is changed + - lt.default_version == 1 + - lt.latest_version == 2 + + - name: update simple instance template + ec2_launch_template: + name: "{{ resource_prefix }}-simple" + image_id: "{{ ec2_ami_id }}" + tags: + TestId: "{{ resource_prefix }}" + instance_type: t3.medium + register: lt + + - name: instance with cpu_options created with the right options + assert: + that: + - lt is success + - lt is changed + - lt.default_version == 3 + - lt.latest_version == 3 + + - name: create new template version based on an old version + ec2_launch_template: + name: "{{ resource_prefix }}-simple" + cpu_options: + core_count: 1 + threads_per_core: 1 + source_version: 1 + register: lt + + - name: instance with cpu_options created with the right options + assert: + that: + - lt is success + - lt is changed + - lt.default_version == 4 + - lt.latest_version == 4 + - lt.latest_template.launch_template_data.instance_type == "c4.large" + + - name: update simple instance template + ec2_launch_template: + name: "{{ resource_prefix }}-simple" + version_description: "Fix something." + register: lt + + - name: instance with cpu_options created with the right options + assert: + that: + - lt is success + - lt is changed + - lt.default_version == 5 + - lt.latest_version == 5 + - lt.latest_template.version_description == "Fix something." + + always: + - name: delete the template + ec2_launch_template: + name: "{{ resource_prefix }}-simple" + state: absent + register: del_lt + retries: 10 + until: del_lt is not failed + ignore_errors: true diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_placement_group/aliases b/ansible_collections/community/aws/tests/integration/targets/ec2_placement_group/aliases new file mode 100644 index 000000000..c6944e7b9 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_placement_group/aliases @@ -0,0 +1,3 @@ +cloud/aws + +ec2_placement_group_info diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_placement_group/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/ec2_placement_group/defaults/main.yml new file mode 100644 index 000000000..ed97d539c --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_placement_group/defaults/main.yml @@ -0,0 +1 @@ +--- diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_placement_group/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/ec2_placement_group/meta/main.yml new file mode 100644 index 000000000..ed97d539c --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_placement_group/meta/main.yml @@ -0,0 +1 @@ +--- diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_placement_group/tasks/env_cleanup.yml b/ansible_collections/community/aws/tests/integration/targets/ec2_placement_group/tasks/env_cleanup.yml new file mode 100644 index 000000000..9e5ae6a93 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_placement_group/tasks/env_cleanup.yml @@ -0,0 +1,94 @@ +- name: remove any instances in the test VPC + ec2_instance: + filters: + vpc_id: "{{ testing_vpc.vpc.id }}" + state: absent + register: removed + until: removed is not failed + ignore_errors: yes + retries: 10 + +- name: Get ENIs + ec2_eni_info: + filters: + vpc-id: "{{ testing_vpc.vpc.id }}" + register: enis + +- name: delete all ENIs + ec2_eni: + eni_id: "{{ item.id }}" + state: absent + until: removed is not failed + with_items: "{{ enis.network_interfaces }}" + ignore_errors: yes + retries: 10 + +- name: remove the security group + ec2_group: + name: "{{ resource_prefix }}-sg" + description: a security group for ansible tests + vpc_id: "{{ testing_vpc.vpc.id }}" + state: absent + register: removed + until: removed is not failed + ignore_errors: yes + retries: 10 + +- name: remove routing rules + ec2_vpc_route_table: + state: absent + vpc_id: "{{ testing_vpc.vpc.id }}" + tags: + created: "{{ resource_prefix }}-route" + routes: + - dest: 0.0.0.0/0 + gateway_id: "{{ igw.gateway_id }}" + subnets: + - "{{ testing_subnet_a.subnet.id }}" + - "{{ testing_subnet_b.subnet.id }}" + register: removed + until: removed is not failed + ignore_errors: yes + retries: 10 + +- name: remove internet gateway + ec2_vpc_igw: + vpc_id: "{{ testing_vpc.vpc.id }}" + state: absent + register: removed + until: removed is not failed + ignore_errors: yes + retries: 10 + +- name: remove subnet A + ec2_vpc_subnet: + state: absent + vpc_id: "{{ testing_vpc.vpc.id }}" + cidr: 10.22.32.0/24 + register: removed + until: removed is not failed + ignore_errors: yes + retries: 10 + +- name: remove subnet B + ec2_vpc_subnet: + state: absent + vpc_id: "{{ testing_vpc.vpc.id }}" + cidr: 10.22.33.0/24 + register: removed + until: removed is not failed + ignore_errors: yes + retries: 10 + +- name: remove the VPC + ec2_vpc_net: + name: "{{ resource_prefix }}-vpc" + cidr_block: 10.22.32.0/23 + state: absent + tags: + Name: Ansible Testing VPC + tenancy: default + register: removed + until: removed is not failed + ignore_errors: yes + retries: 10 diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_placement_group/tasks/env_setup.yml b/ansible_collections/community/aws/tests/integration/targets/ec2_placement_group/tasks/env_setup.yml new file mode 100644 index 000000000..88f5bb6fe --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_placement_group/tasks/env_setup.yml @@ -0,0 +1,64 @@ +- name: Create VPC for use in testing + ec2_vpc_net: + name: "{{ resource_prefix }}-vpc" + cidr_block: 10.22.32.0/23 + tags: + Name: Ansible ec2_lc Testing VPC + tenancy: default + register: testing_vpc + +- name: Create internet gateway for use in testing + ec2_vpc_igw: + vpc_id: "{{ testing_vpc.vpc.id }}" + state: present + tags: + Name: Ansible ec2_lc Testing gateway + register: igw + +- name: Create default subnet in zone A + ec2_vpc_subnet: + state: present + vpc_id: "{{ testing_vpc.vpc.id }}" + cidr: 10.22.32.0/24 + az: "{{ aws_region }}a" + resource_tags: + Name: "{{ resource_prefix }}-subnet-a" + register: testing_subnet_a + +- name: Create secondary subnet in zone B + ec2_vpc_subnet: + state: present + vpc_id: "{{ testing_vpc.vpc.id }}" + cidr: 10.22.33.0/24 + az: "{{ aws_region }}b" + resource_tags: + Name: "{{ resource_prefix }}-subnet-b" + register: testing_subnet_b + +- name: create routing rules + ec2_vpc_route_table: + vpc_id: "{{ testing_vpc.vpc.id }}" + tags: + created: "{{ resource_prefix }}-route" + routes: + - dest: 0.0.0.0/0 + gateway_id: "{{ igw.gateway_id }}" + subnets: + - "{{ testing_subnet_a.subnet.id }}" + - "{{ testing_subnet_b.subnet.id }}" + +- name: create a security group with the vpc + ec2_group: + name: "{{ resource_prefix }}-sg" + description: a security group for ansible tests + vpc_id: "{{ testing_vpc.vpc.id }}" + rules: + - proto: tcp + from_port: 22 + to_port: 22 + cidr_ip: 0.0.0.0/0 + - proto: tcp + from_port: 80 + to_port: 80 + cidr_ip: 0.0.0.0/0 + register: sg diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_placement_group/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/ec2_placement_group/tasks/main.yml new file mode 100644 index 000000000..91fd9497c --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_placement_group/tasks/main.yml @@ -0,0 +1,408 @@ +- name: run ec2_placement_group tests + module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + collections: + - amazon.aws + vars: + placement_group_names: [] + + block: + + - name: set up environment for testing. + include_tasks: env_setup.yml + + - name: Create a placement group 1 - check_mode + community.aws.ec2_placement_group: + name: '{{ resource_prefix }}-pg1' + state: present + check_mode: true + register: pg_1_create_check_mode + + - assert: + that: + - pg_1_create_check_mode is changed + - pg_1_create_check_mode.placement_group.name == '{{ resource_prefix }}-pg1' + - pg_1_create_check_mode.placement_group.state == "DryRun" + - '"ec2:CreatePlacementGroup" in pg_1_create_check_mode.resource_actions' + + - name: Create a placement group 1 + community.aws.ec2_placement_group: + name: '{{ resource_prefix }}-pg1' + state: present + register: pg_1_create + + - set_fact: + placement_group_names: "{{ placement_group_names + [pg_1_create.placement_group.name] }}" + + - assert: + that: + - pg_1_create is changed + - pg_1_create.placement_group.name == '{{ resource_prefix }}-pg1' + - pg_1_create.placement_group.state == "available" + - '"ec2:CreatePlacementGroup" in pg_1_create.resource_actions' + + - name: Gather information about placement group 1 + community.aws.ec2_placement_group_info: + names: + - '{{ resource_prefix }}-pg1' + register: pg_1_info_result + + - assert: + that: + - pg_1_info_result is not changed + - pg_1_info_result.placement_groups[0].name == '{{ resource_prefix }}-pg1' + - pg_1_info_result.placement_groups[0].state == "available" + - pg_1_info_result.placement_groups[0].strategy == "cluster" + - '"ec2:DescribePlacementGroups" in pg_1_info_result.resource_actions' + + - name: Create a placement group 1 - Idempotency + community.aws.ec2_placement_group: + name: '{{ resource_prefix }}-pg1' + state: present + register: pg_1_create + + - assert: + that: + - pg_1_create is not changed + - pg_1_create.placement_group.name == '{{ resource_prefix }}-pg1' + - pg_1_create.placement_group.state == "available" + - '"ec2:CreatePlacementGroup" not in pg_1_create.resource_actions' + + - name: Create a placement group 1 - check_mode Idempotency + community.aws.ec2_placement_group: + name: '{{ resource_prefix }}-pg1' + state: present + check_mode: true + register: pg_1_create_check_mode_idem + + - assert: + that: + - pg_1_create_check_mode_idem is not changed + - pg_1_create_check_mode_idem.placement_group.name == '{{ resource_prefix }}-pg1' + - pg_1_create_check_mode_idem.placement_group.state == "available" + - '"ec2:CreatePlacementGroup" not in pg_1_create_check_mode_idem.resource_actions' + + - name: Create a placement group 2 - check_mode + community.aws.ec2_placement_group: + name: '{{ resource_prefix }}-pg2' + state: present + strategy: spread + check_mode: true + register: pg_2_create_check_mode + + - assert: + that: + - pg_2_create_check_mode is changed + - pg_2_create_check_mode.placement_group.name == '{{ resource_prefix }}-pg2' + - pg_2_create_check_mode.placement_group.state == "DryRun" + - '"ec2:CreatePlacementGroup" in pg_2_create_check_mode.resource_actions' + + - name: Create a placement group 2 with spread strategy + community.aws.ec2_placement_group: + name: '{{ resource_prefix }}-pg2' + state: present + strategy: spread + register: pg_2_create + + - assert: + that: + - pg_2_create is changed + - pg_2_create.placement_group.name == '{{ resource_prefix }}-pg2' + - pg_2_create.placement_group.state == "available" + - '"ec2:CreatePlacementGroup" in pg_2_create.resource_actions' + + - set_fact: + placement_group_names: "{{ placement_group_names + [pg_2_create.placement_group.name] }}" + + - name: Gather information about placement group 2 + community.aws.ec2_placement_group_info: + names: + - '{{ resource_prefix }}-pg2' + register: pg_2_info_result + + - assert: + that: + - pg_2_info_result is not changed + - pg_2_info_result.placement_groups[0].name == '{{ resource_prefix }}-pg2' + - pg_2_info_result.placement_groups[0].state == "available" + - pg_2_info_result.placement_groups[0].strategy == "spread" + - '"ec2:DescribePlacementGroups" in pg_2_info_result.resource_actions' + + - name: Create a placement group 2 with spread strategy - Idempotency + community.aws.ec2_placement_group: + name: '{{ resource_prefix }}-pg2' + state: present + strategy: spread + register: pg_2_create + + - assert: + that: + - pg_2_create is not changed + - pg_2_create.placement_group.name == '{{ resource_prefix }}-pg2' + - pg_2_create.placement_group.state == "available" + - '"ec2:CreatePlacementGroup" not in pg_2_create.resource_actions' + + - name: Create a placement group 2 - check_mode Idempotency + community.aws.ec2_placement_group: + name: '{{ resource_prefix }}-pg2' + state: present + strategy: spread + check_mode: true + register: pg_2_create_check_mode_idem + + - assert: + that: + - pg_2_create_check_mode_idem is not changed + - pg_2_create_check_mode_idem.placement_group.name == '{{ resource_prefix }}-pg2' + - pg_2_create_check_mode_idem.placement_group.state == "available" + - '"ec2:CreatePlacementGroup" not in pg_2_create_check_mode_idem.resource_actions' + + - name: Create a placement group 3 - check_mode + community.aws.ec2_placement_group: + name: '{{ resource_prefix }}-pg3' + state: present + strategy: partition + partition_count: 4 + check_mode: true + register: pg_3_create_check_mode + + - assert: + that: + - pg_3_create_check_mode is changed + - pg_3_create_check_mode.placement_group.name == '{{ resource_prefix }}-pg3' + - pg_3_create_check_mode.placement_group.state == "DryRun" + - '"ec2:CreatePlacementGroup" in pg_3_create_check_mode.resource_actions' + + - name: Create a placement group 3 with Partition strategy + community.aws.ec2_placement_group: + name: '{{ resource_prefix }}-pg3' + state: present + strategy: partition + partition_count: 4 + register: pg_3_create + + - assert: + that: + - pg_3_create is changed + - pg_3_create.placement_group.name == '{{ resource_prefix }}-pg3' + - pg_3_create.placement_group.state == "available" + - '"ec2:CreatePlacementGroup" in pg_3_create.resource_actions' + + - set_fact: + placement_group_names: "{{ placement_group_names + [pg_3_create.placement_group.name] }}" + + + - name: Gather information about placement group 3 + community.aws.ec2_placement_group_info: + names: + - '{{ resource_prefix }}-pg3' + register: pg_3_info_result + + - assert: + that: + - pg_3_info_result is not changed + - pg_3_info_result.placement_groups[0].name == '{{ resource_prefix }}-pg3' + - pg_3_info_result.placement_groups[0].state == "available" + - pg_3_info_result.placement_groups[0].strategy == "partition" + - '"ec2:DescribePlacementGroups" in pg_3_info_result.resource_actions' + + - name: Create a placement group 3 with Partition strategy - Idempotency + community.aws.ec2_placement_group: + name: '{{ resource_prefix }}-pg3' + state: present + strategy: partition + partition_count: 4 + register: pg_3_create + + - assert: + that: + - pg_3_create is not changed + - pg_3_create.placement_group.name == '{{ resource_prefix }}-pg3' + - pg_3_create.placement_group.state == "available" + - '"ec2:CreatePlacementGroup" not in pg_3_create.resource_actions' + + - name: Create a placement group 3 - check_mode Idempotency + community.aws.ec2_placement_group: + name: '{{ resource_prefix }}-pg3' + state: present + strategy: partition + partition_count: 4 + check_mode: true + register: pg_3_create_check_mode_idem + + - assert: + that: + - pg_3_create_check_mode_idem is not changed + - pg_3_create_check_mode_idem.placement_group.name == '{{ resource_prefix }}-pg3' + - pg_3_create_check_mode_idem.placement_group.state == "available" + - '"ec2:CreatePlacementGroup" not in pg_3_create_check_mode_idem.resource_actions' + + - name: List all placement groups. + community.aws.ec2_placement_group_info: + register: all_ec2_placement_groups + +# Delete Placement Group ========================================== + + # On using check_mode for delete placement group operation + # If operation would have succeeded, the error response is DryRunOperation. + # Otherwise, it is UnauthorizedOperation . + - name: Delete a placement group 1 - check_mode + community.aws.ec2_placement_group: + name: '{{ resource_prefix }}-pg1' + state: absent + check_mode: true + register: pg_1_delete_check_mode + ignore_errors: true + + - assert: + that: + - pg_1_delete_check_mode is not changed + - pg_1_delete_check_mode.error.code == 'DryRunOperation' + - '"ec2:DeletePlacementGroup" in pg_1_delete_check_mode.resource_actions' + + - name: Delete a placement group 1 + community.aws.ec2_placement_group: + name: '{{ resource_prefix }}-pg1' + state: absent + register: pg_1_delete + + - assert: + that: + - pg_1_delete is changed + - '"ec2:DeletePlacementGroup" in pg_1_delete.resource_actions' + + - name: Delete a placement group 1 - Idempotency + community.aws.ec2_placement_group: + name: '{{ resource_prefix }}-pg1' + state: absent + register: pg_1_delete + + - assert: + that: + - pg_1_delete is not changed + - '"ec2:DeletePlacementGroup" not in pg_1_delete.resource_actions' + + - name: Delete a placement group 1 - check_mode Idempotency + community.aws.ec2_placement_group: + name: '{{ resource_prefix }}-pg1' + state: absent + check_mode: true + register: pg_1_delete_check_mode_idem + ignore_errors: true + + - assert: + that: + - pg_1_delete_check_mode_idem is not changed + - '"ec2:DeletePlacementGroup" not in pg_1_delete_check_mode_idem.resource_actions' + + - name: Delete a placement group 2 - check_mode + community.aws.ec2_placement_group: + name: '{{ resource_prefix }}-pg2' + state: absent + check_mode: true + register: pg_2_delete_check_mode + ignore_errors: true + + - assert: + that: + - pg_2_delete_check_mode is not changed + - pg_2_delete_check_mode.error.code == 'DryRunOperation' + - '"ec2:DeletePlacementGroup" in pg_2_delete_check_mode.resource_actions' + + - name: Delete a placement group 2 + community.aws.ec2_placement_group: + name: '{{ resource_prefix }}-pg2' + state: absent + register: pg_2_delete + + - assert: + that: + - pg_2_delete is changed + - '"ec2:DeletePlacementGroup" in pg_2_delete.resource_actions' + + - name: Delete a placement group 2 - Idempotency + community.aws.ec2_placement_group: + name: '{{ resource_prefix }}-pg2' + state: absent + register: pg_2_delete + + - assert: + that: + - pg_2_delete is not changed + - '"ec2:DeletePlacementGroup" not in pg_2_delete.resource_actions' + + - name: Delete a placement group 2 - check_mode Idempotency + community.aws.ec2_placement_group: + name: '{{ resource_prefix }}-pg2' + state: absent + check_mode: true + register: pg_2_delete_check_mode_idem + ignore_errors: true + + - assert: + that: + - pg_2_delete_check_mode_idem is not changed + - '"ec2:DeletePlacementGroup" not in pg_2_delete_check_mode_idem.resource_actions' + + - name: Delete a placement group 3 - check_mode + community.aws.ec2_placement_group: + name: '{{ resource_prefix }}-pg3' + state: absent + check_mode: true + register: pg_3_delete_check_mode + ignore_errors: true + + - assert: + that: + - pg_3_delete_check_mode is not changed + - pg_3_delete_check_mode.error.code == 'DryRunOperation' + - '"ec2:DeletePlacementGroup" in pg_3_delete_check_mode.resource_actions' + + - name: Delete a placement group 3 + community.aws.ec2_placement_group: + name: '{{ resource_prefix }}-pg3' + state: absent + register: pg_3_delete + + - assert: + that: + - pg_3_delete is changed + - '"ec2:DeletePlacementGroup" in pg_3_delete.resource_actions' + + - name: Delete a placement group 3 - Idempotency + community.aws.ec2_placement_group: + name: '{{ resource_prefix }}-pg3' + state: absent + register: pg_3_delete + + - assert: + that: + - pg_3_delete is not changed + - '"ec2:DeletePlacementGroup" not in pg_3_delete.resource_actions' + + - name: Delete a placement group 3 - check_mode Idempotency + community.aws.ec2_placement_group: + name: '{{ resource_prefix }}-pg3' + state: absent + check_mode: true + register: pg_3_delete_check_mode_idem + ignore_errors: true + + - assert: + that: + - pg_3_delete_check_mode_idem is not changed + - '"ec2:DeletePlacementGroup" not in pg_3_delete_check_mode_idem.resource_actions' + + always: + + - name: Make sure placement groups created during test are deleted + community.aws.ec2_placement_group: + name: '{{ item }}' + state: absent + with_items: '{{ placement_group_names }}' + + - include_tasks: env_cleanup.yml diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_placement_group/vars/main.yml b/ansible_collections/community/aws/tests/integration/targets/ec2_placement_group/vars/main.yml new file mode 100644 index 000000000..ed97d539c --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_placement_group/vars/main.yml @@ -0,0 +1 @@ +--- diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_transit_gateway/aliases b/ansible_collections/community/aws/tests/integration/targets/ec2_transit_gateway/aliases new file mode 100644 index 000000000..cfcde0de3 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_transit_gateway/aliases @@ -0,0 +1,3 @@ +cloud/aws + +ec2_transit_gateway_info diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_transit_gateway/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/ec2_transit_gateway/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_transit_gateway/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_transit_gateway/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/ec2_transit_gateway/tasks/main.yml new file mode 100644 index 000000000..6cb279f77 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_transit_gateway/tasks/main.yml @@ -0,0 +1,138 @@ +--- +- name: 'ec2_transit_gateway integration tests' + collections: + - amazon.aws + module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + block: + + - name: generate unique value for testing + set_fact: + tgw_description: "{{ resource_prefix }}-tgw" + + - name: test create transit gateway without tags + ec2_transit_gateway: + description: "{{ tgw_description }}" + register: create_result + - name: assert changed is True + assert: + that: + - create_result.changed == True + + - name: test update transit gateway with tags by description + ec2_transit_gateway: + description: "{{ tgw_description }}" + tags: + Name: Ansible Test TGW + register: result + - name: assert changed is True + assert: + that: + - result.changed == True + - result.transit_gateway.tags | length == 1 + - "'Name' in result.transit_gateway.tags" + + - name: test update transit gateway with new tag and purge_tags false + ec2_transit_gateway: + transit_gateway_id: '{{ create_result.transit_gateway.transit_gateway_id }}' + purge_tags: False + tags: + status: ok to delete + register: result + - name: assert changed is True and have 2 tags + assert: + that: + - result.changed == True + - result.transit_gateway.tags | length == 2 + - "'Name' in result.transit_gateway.tags" + + - name: test update transit gateway with purge_tags true + ec2_transit_gateway: + transit_gateway_id: '{{ create_result.transit_gateway.transit_gateway_id }}' + purge_tags: True + tags: + status: ok to delete + register: result + - name: assert changed is True and TGW tag is absent + assert: + that: + - result.changed == True + - result.transit_gateway.tags | length == 1 + - "'Name' not in result.transit_gateway.tags" + + - name: test idempotence + ec2_transit_gateway: + description: "{{ tgw_description }}" + purge_tags: True + tags: + status: ok to delete + register: result + - name: assert changed is False + assert: + that: + - result.changed == False + + # ==== Combine ec2_transit_gateway_info ====================== + - name: test success with no parameters + ec2_transit_gateway_info: + register: result + - name: assert success with no parameters + assert: + that: + - 'result.changed == false' + - 'result.transit_gateways != []' + + - name: test success with single filter + ec2_transit_gateway_info: + filters: + transit-gateway-id: "{{ create_result.transit_gateway.transit_gateway_id }}" + register: result + - name: assert success with transit_gateway_id filter + assert: + that: + - 'result.changed == false' + - 'result.transit_gateways != []' + + - name: test empty result set for non-existent tgw id via filter + ec2_transit_gateway_info: + filters: + transit-gateway-id: tgw-00000011111111122 + register: result + - name: assert success with transit_gateway_id filter + assert: + that: + - 'result.changed == false' + - 'result.transit_gateways == []' + + - name: test NotFound exception caught and returned empty result set + ec2_transit_gateway_info: + transit_gateway_id: tgw-00000011111111122 + register: result + - name: assert success with transit_gateway_id filter + assert: + that: + - 'result.changed == false' + - 'result.transit_gateways == []' + + - name: test success with multiple filters + ec2_transit_gateway_info: + filters: + options.dns-support: enable + options.vpn-ecmp-support: enable + register: result + - name: assert success with transit_gateway_id filter + assert: + that: + - 'result.changed == false' + - 'result.transit_gateways != []' + always: + ###### TEARDOWN STARTS HERE ###### + - name: delete transit gateway + ec2_transit_gateway: + description: "{{ tgw_description }}" + state: absent + ignore_errors: yes diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_transit_gateway_vpc_attachment/aliases b/ansible_collections/community/aws/tests/integration/targets/ec2_transit_gateway_vpc_attachment/aliases new file mode 100644 index 000000000..94fa60d71 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_transit_gateway_vpc_attachment/aliases @@ -0,0 +1,3 @@ +cloud/aws +time=35m +# ec2_transit_gateway_vpc_attachment_info diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_transit_gateway_vpc_attachment/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/ec2_transit_gateway_vpc_attachment/defaults/main.yml new file mode 100644 index 000000000..c97277465 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_transit_gateway_vpc_attachment/defaults/main.yml @@ -0,0 +1,26 @@ +_resource_prefix: 'AnsibleTest' +#_resource_prefix: 'AnsibleTest-{{ tiny_prefix }}-TGW-Attach' +cidr_prefix: '10.{{ 255 | random(seed=_resource_prefix) }}' +tgw_name: '{{ _resource_prefix }}' +tgw_name_2: '{{ _resource_prefix }}-2' +vpc_name_a: '{{ _resource_prefix }}-1' +vpc_name_b: '{{ _resource_prefix }}-2' +vpc_cidr_a: '{{ cidr_prefix }}.1.0/24' +vpc_cidr_b: '{{ cidr_prefix }}.2.0/24' + +subnet_cidr_a_1: '{{ cidr_prefix }}.1.0/26' +subnet_cidr_a_2: '{{ cidr_prefix }}.1.64/26' +subnet_cidr_a_3: '{{ cidr_prefix }}.1.128/26' +subnet_cidr_a_1a: '{{ cidr_prefix }}.1.192/26' +subnet_cidr_b_1: '{{ cidr_prefix }}.2.0/26' +subnet_cidr_b_2: '{{ cidr_prefix }}.2.64/26' + +subnet_name_a_1: '{{ _resource_prefix }}-a-1' +subnet_name_a_1a: '{{ _resource_prefix }}-a-1a' +subnet_name_a_2: '{{ _resource_prefix }}-a-2' +subnet_name_a_3: '{{ _resource_prefix }}-a-3' +subnet_name_b_1: '{{ _resource_prefix }}-b-1' +subnet_name_b_2: '{{ _resource_prefix }}-b-2' + +attachment_name: '{{ _resource_prefix }}' +attachment_name_complex: '{{ _resource_prefix }}-complex' diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_transit_gateway_vpc_attachment/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/ec2_transit_gateway_vpc_attachment/meta/main.yml new file mode 100644 index 000000000..aef5ca0ee --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_transit_gateway_vpc_attachment/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - role: setup_ec2_facts diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_transit_gateway_vpc_attachment/tasks/cleanup.yml b/ansible_collections/community/aws/tests/integration/targets/ec2_transit_gateway_vpc_attachment/tasks/cleanup.yml new file mode 100644 index 000000000..e59723bdc --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_transit_gateway_vpc_attachment/tasks/cleanup.yml @@ -0,0 +1,64 @@ +--- +- name: 'Describe all attachments on our VPC' + ec2_transit_gateway_vpc_attachment_info: + filters: + transit-gateway-id: '{{ tgw_id }}' + register: info + ignore_errors: True + +- name: 'Start deletion of all attachments' + ec2_transit_gateway_vpc_attachment: + state: absent + id: '{{ item.transit_gateway_attachment_id }}' + wait: False + loop: '{{ info.attachments }}' + ignore_errors: True + +- name: 'Wait for deletion of all attachments' + ec2_transit_gateway_vpc_attachment: + state: absent + id: '{{ item.transit_gateway_attachment_id }}' + wait: True + loop: '{{ info.attachments }}' + ignore_errors: True + +- name: 'Delete subnets' + ec2_vpc_subnet: + state: absent + cidr: '{{ item.cidr }}' + vpc_id: '{{ item.vpc_id }}' + loop: + - cidr: '{{ subnet_cidr_a_1 }}' + vpc_id: '{{ vpc_id_a }}' + - cidr: '{{ subnet_cidr_a_2 }}' + vpc_id: '{{ vpc_id_a }}' + - cidr: '{{ subnet_cidr_a_3 }}' + vpc_id: '{{ vpc_id_a }}' + - cidr: '{{ subnet_cidr_b_1 }}' + vpc_id: '{{ vpc_id_b }}' + - cidr: '{{ subnet_cidr_b_2 }}' + vpc_id: '{{ vpc_id_b }}' + - cidr: '{{ subnet_cidr_a_1a }}' + vpc_id: '{{ vpc_id_a }}' + ignore_errors: True + +- name: 'Create VPCs to attach to TGW' + ec2_vpc_net: + state: absent + cidr_block: '{{ item.cidr }}' + name: '{{ item.name }}' + loop: + - cidr: '{{ vpc_cidr_a }}' + name: '{{ vpc_name_a }}' + - cidr: '{{ vpc_cidr_b }}' + name: '{{ vpc_name_b }}' + ignore_errors: True + +- name: 'Create Transit Gateways' + ec2_transit_gateway: + state: absent + transit_gateway_id: '{{ item.tgw_id }}' + loop: + - tgw_id: '{{ tgw_id }}' + - tgw_id: '{{ tgw_id_2 }}' + ignore_errors: True diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_transit_gateway_vpc_attachment/tasks/complex.yml b/ansible_collections/community/aws/tests/integration/targets/ec2_transit_gateway_vpc_attachment/tasks/complex.yml new file mode 100644 index 000000000..eda3ab2ac --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_transit_gateway_vpc_attachment/tasks/complex.yml @@ -0,0 +1,443 @@ +--- +# Tests the setting of most parameters at the same time +# +# Note: Does not delete the attachment, so that there's a second VPC attached to +# the TGW when we run our _info tests in simple.yml +# +# ============================================================================= +# Creation + +- block: + - name: '(CHECK_MODE) Create an attachment - complex parameters' + check_mode: True + ec2_transit_gateway_vpc_attachment: + name: '{{ attachment_name_complex }}' + transit_gateway: '{{ tgw_id }}' + subnets: + - '{{ subnet_id_b_1 }}' + - '{{ subnet_id_b_2 }}' + tags: + tagA: 'example Value' + Tag_B: 'second value' + appliance_mode_support: True + ipv6_support: True + register: complex_attach + + - assert: + that: + - complex_attach is changed + - '"attachments" in complex_attach' + - complex_attach.attachments | length == 1 + - '"options" in attachment' + - '"subnet_ids" in attachment' + - '"tags" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == "enable" + - attachment.options.ipv6_support == "enable" + - attachment.subnet_ids | length == 2 + - subnet_id_b_1 in attachment.subnet_ids + - subnet_id_b_2 in attachment.subnet_ids + - attachment.tags | length == 3 + - '"Name" in attachment.tags' + - '"tagA" in attachment.tags' + - '"Tag_B" in attachment.tags' + - attachment.tags.Name == attachment_name_complex + - attachment.tags.tagA == "example Value" + - attachment.tags.Tag_B == "second value" + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_b + vars: + attachment: '{{ complex_attach.attachments[0] }}' + + - name: 'Create an attachment - complex parameters' + ec2_transit_gateway_vpc_attachment: + name: '{{ attachment_name_complex }}' + transit_gateway: '{{ tgw_id }}' + subnets: + - '{{ subnet_id_b_1 }}' + - '{{ subnet_id_b_2 }}' + tags: + tagA: 'example Value' + Tag_B: 'second value' + appliance_mode_support: True + ipv6_support: True + register: complex_attach + + - assert: + that: + - complex_attach is changed + - '"attachments" in complex_attach' + - complex_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 2 + - subnet_id_b_1 in attachment.subnet_ids + - subnet_id_b_2 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_b + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'enable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'enable' + - attachment.transit_gateway_attachment_id.startswith('tgw-attach-') + - attachment.state == 'available' + - attachment.tags | length == 3 + - '"Name" in attachment.tags' + - '"tagA" in attachment.tags' + - '"Tag_B" in attachment.tags' + - attachment.tags.Name == attachment_name_complex + - attachment.tags.tagA == "example Value" + - attachment.tags.Tag_B == "second value" + - attachment.vpc_owner_id == vpc_owner_b + vars: + attachment: '{{ complex_attach.attachments[0] }}' + + - name: Save Attachment ID + set_fact: + complex_attachment_id: '{{ complex_attach.attachments[0].transit_gateway_attachment_id }}' + + - name: '(CHECK_MODE) Create an attachment - complex parameters -- IDEMPOTENCY' + check_mode: True + ec2_transit_gateway_vpc_attachment: + name: '{{ attachment_name_complex }}' + transit_gateway: '{{ tgw_id }}' + subnets: + - '{{ subnet_id_b_1 }}' + - '{{ subnet_id_b_2 }}' + tags: + tagA: 'example Value' + Tag_B: 'second value' + appliance_mode_support: True + ipv6_support: True + register: complex_attach + + - assert: + that: + - complex_attach is not changed + - '"attachments" in complex_attach' + - complex_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 2 + - subnet_id_b_1 in attachment.subnet_ids + - subnet_id_b_2 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_b + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'enable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'enable' + - attachment.transit_gateway_attachment_id == complex_attachment_id + - attachment.state == 'available' + - attachment.tags | length == 3 + - '"Name" in attachment.tags' + - '"tagA" in attachment.tags' + - '"Tag_B" in attachment.tags' + - attachment.tags.Name == attachment_name_complex + - attachment.tags.tagA == "example Value" + - attachment.tags.Tag_B == "second value" + - attachment.vpc_owner_id == vpc_owner_b + vars: + attachment: '{{ complex_attach.attachments[0] }}' + + - name: 'Create an attachment - complex parameters -- IDEMPOTENCY' + ec2_transit_gateway_vpc_attachment: + name: '{{ attachment_name_complex }}' + transit_gateway: '{{ tgw_id }}' + subnets: + - '{{ subnet_id_b_1 }}' + - '{{ subnet_id_b_2 }}' + tags: + tagA: 'example Value' + Tag_B: 'second value' + appliance_mode_support: True + ipv6_support: True + register: complex_attach + + - assert: + that: + - complex_attach is not changed + - '"attachments" in complex_attach' + - complex_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 2 + - subnet_id_b_1 in attachment.subnet_ids + - subnet_id_b_2 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_b + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'enable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'enable' + - attachment.transit_gateway_attachment_id == complex_attachment_id + - attachment.state == 'available' + - attachment.tags | length == 3 + - '"Name" in attachment.tags' + - '"tagA" in attachment.tags' + - '"Tag_B" in attachment.tags' + - attachment.tags.Name == attachment_name_complex + - attachment.tags.tagA == "example Value" + - attachment.tags.Tag_B == "second value" + - attachment.vpc_owner_id == vpc_owner_b + vars: + attachment: '{{ complex_attach.attachments[0] }}' + +# ============================================================================= +# Update + + - name: '(CHECK_MODE) Update an attachment - complex parameters' + check_mode: True + ec2_transit_gateway_vpc_attachment: + name: '{{ attachment_name_complex }}' + transit_gateway: '{{ tgw_id }}' + subnets: + - '{{ subnet_id_b_1 }}' + purge_subnets: True + tags: + tagC: '3' + Tag_D: 'Hello again dear world' + purge_tags: False + dns_support: False + ipv6_support: False + register: complex_attach + + - assert: + that: + - complex_attach is changed + - '"attachments" in complex_attach' + - complex_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_b_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_b + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'enable' + - attachment.options.dns_support == 'disable' + - attachment.options.ipv6_support == 'disable' + - attachment.transit_gateway_attachment_id == complex_attachment_id + - attachment.state == 'available' + - attachment.tags | length == 5 + - '"Name" in attachment.tags' + - '"tagA" in attachment.tags' + - '"Tag_B" in attachment.tags' + - '"tagC" in attachment.tags' + - '"Tag_D" in attachment.tags' + - attachment.tags.Name == attachment_name_complex + - attachment.tags.tagA == "example Value" + - attachment.tags.Tag_B == "second value" + - attachment.tags.tagC == "3" + - attachment.tags.Tag_D == "Hello again dear world" + - attachment.vpc_owner_id == vpc_owner_b + vars: + attachment: '{{ complex_attach.attachments[0] }}' + + - name: 'Update an attachment - complex parameters' + ec2_transit_gateway_vpc_attachment: + name: '{{ attachment_name_complex }}' + transit_gateway: '{{ tgw_id }}' + subnets: + - '{{ subnet_id_b_1 }}' + purge_subnets: True + tags: + tagC: '3' + Tag_D: 'Hello again dear world' + purge_tags: False + dns_support: False + ipv6_support: False + register: complex_attach + + - assert: + that: + - complex_attach is changed + - '"attachments" in complex_attach' + - complex_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_b_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_b + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'enable' + - attachment.options.dns_support == 'disable' + - attachment.options.ipv6_support == 'disable' + - attachment.transit_gateway_attachment_id == complex_attachment_id + - attachment.state == 'available' + - attachment.tags | length == 5 + - '"Name" in attachment.tags' + - '"tagA" in attachment.tags' + - '"Tag_B" in attachment.tags' + - '"tagC" in attachment.tags' + - '"Tag_D" in attachment.tags' + - attachment.tags.Name == attachment_name_complex + - attachment.tags.tagA == "example Value" + - attachment.tags.Tag_B == "second value" + - attachment.tags.tagC == "3" + - attachment.tags.Tag_D == "Hello again dear world" + - attachment.vpc_owner_id == vpc_owner_b + vars: + attachment: '{{ complex_attach.attachments[0] }}' + + - name: '(CHECK_MODE) Update an attachment - complex parameters -- IDEMPOTENCY' + check_mode: True + ec2_transit_gateway_vpc_attachment: + name: '{{ attachment_name_complex }}' + transit_gateway: '{{ tgw_id }}' + subnets: + - '{{ subnet_id_b_1 }}' + purge_subnets: True + tags: + tagC: '3' + Tag_D: 'Hello again dear world' + purge_tags: False + dns_support: False + ipv6_support: False + register: complex_attach + + - assert: + that: + - complex_attach is not changed + - '"attachments" in complex_attach' + - complex_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_b_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_b + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'enable' + - attachment.options.dns_support == 'disable' + - attachment.options.ipv6_support == 'disable' + - attachment.transit_gateway_attachment_id == complex_attachment_id + - attachment.state == 'available' + - attachment.tags | length == 5 + - '"Name" in attachment.tags' + - '"tagA" in attachment.tags' + - '"Tag_B" in attachment.tags' + - '"tagC" in attachment.tags' + - '"Tag_D" in attachment.tags' + - attachment.tags.Name == attachment_name_complex + - attachment.tags.tagA == "example Value" + - attachment.tags.Tag_B == "second value" + - attachment.tags.tagC == "3" + - attachment.tags.Tag_D == "Hello again dear world" + - attachment.vpc_owner_id == vpc_owner_b + vars: + attachment: '{{ complex_attach.attachments[0] }}' + + - name: 'Update an attachment - complex parameters -- IDEMPOTENCY' + ec2_transit_gateway_vpc_attachment: + name: '{{ attachment_name_complex }}' + transit_gateway: '{{ tgw_id }}' + subnets: + - '{{ subnet_id_b_1 }}' + purge_subnets: True + tags: + tagC: '3' + Tag_D: 'Hello again dear world' + purge_tags: False + dns_support: False + ipv6_support: False + register: complex_attach + + - assert: + that: + - complex_attach is not changed + - '"attachments" in complex_attach' + - complex_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_b_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_b + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'enable' + - attachment.options.dns_support == 'disable' + - attachment.options.ipv6_support == 'disable' + - attachment.transit_gateway_attachment_id == complex_attachment_id + - attachment.state == 'available' + - attachment.tags | length == 5 + - '"Name" in attachment.tags' + - '"tagA" in attachment.tags' + - '"Tag_B" in attachment.tags' + - '"tagC" in attachment.tags' + - '"Tag_D" in attachment.tags' + - attachment.tags.Name == attachment_name_complex + - attachment.tags.tagA == "example Value" + - attachment.tags.Tag_B == "second value" + - attachment.tags.tagC == "3" + - attachment.tags.Tag_D == "Hello again dear world" + - attachment.vpc_owner_id == vpc_owner_b + vars: + attachment: '{{ complex_attach.attachments[0] }}' diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_transit_gateway_vpc_attachment/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/ec2_transit_gateway_vpc_attachment/tasks/main.yml new file mode 100644 index 000000000..8694b829e --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_transit_gateway_vpc_attachment/tasks/main.yml @@ -0,0 +1,24 @@ +--- +- name: 'ec2_transit_gateway_vpc_attachment integration tests' + collections: + - amazon.aws + module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + + block: + # Prepares various resources + - include_tasks: 'setup.yml' + + # Tests create / update on parameters simulatniously + - include_tasks: 'complex.yml' + + # Tests create / update / delete on individual parameters + - include_tasks: 'simple.yml' + + always: + # Cleanup after ourselves + - include_tasks: 'cleanup.yml' diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_transit_gateway_vpc_attachment/tasks/setup.yml b/ansible_collections/community/aws/tests/integration/targets/ec2_transit_gateway_vpc_attachment/tasks/setup.yml new file mode 100644 index 000000000..86d5aa51b --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_transit_gateway_vpc_attachment/tasks/setup.yml @@ -0,0 +1,101 @@ +--- +- name: 'Pick 2 AZs available for use' + set_fact: + subnet_az_a_1: '{{ ec2_availability_zone_names[0] }}' + subnet_az_a_1a: '{{ ec2_availability_zone_names[0] }}' + subnet_az_a_2: '{{ ec2_availability_zone_names[1] }}' + subnet_az_a_3: '{{ ec2_availability_zone_names[2] }}' + subnet_az_b_1: '{{ ec2_availability_zone_names[0] }}' + subnet_az_b_2: '{{ ec2_availability_zone_names[1] }}' + +- name: 'Create Transit Gateways' + ec2_transit_gateway: + description: '{{ item.description }}' + tags: + Name: '{{ item.name }}' + loop: + - description: 'Transit Gateway for testing ec2_transit_gateway_attachment' + name: '{{ tgw_name }}' + - description: 'Second Transit Gateway for testing ec2_transit_gateway_attachment' + name: '{{ tgw_name_2 }}' + register: create_tgws + +- name: 'Create VPCs to attach to TGW' + ec2_vpc_net: + cidr_block: '{{ item.cidr }}' + name: '{{ item.name }}' + ipv6_cidr: True + loop: + - cidr: '{{ vpc_cidr_a }}' + name: '{{ vpc_name_a }}' + - cidr: '{{ vpc_cidr_b }}' + name: '{{ vpc_name_b }}' + register: create_vpcs + +- set_fact: + tgw_id: '{{ create_tgws.results[0].transit_gateway.transit_gateway_id }}' + tgw_id_2: '{{ create_tgws.results[1].transit_gateway.transit_gateway_id }}' + vpc_id_a: '{{ vpc_a.id }}' + vpc_id_b: '{{ vpc_b.id }}' + vpc_owner_a: '{{ vpc_a.owner_id }}' + vpc_owner_b: '{{ vpc_b.owner_id }}' + subnet_ipv6_a_1: '{{ vpc_ipv6_a | replace("0::/56","0::/64") }}' + subnet_ipv6_a_2: '{{ vpc_ipv6_a | replace("0::/56","1::/64") }}' + subnet_ipv6_a_3: '{{ vpc_ipv6_a | replace("0::/56","2::/64") }}' + subnet_ipv6_a_1a: '{{ vpc_ipv6_a | replace("0::/56","3::/64") }}' + subnet_ipv6_b_1: '{{ vpc_ipv6_b | replace("0::/56","0::/64") }}' + subnet_ipv6_b_2: '{{ vpc_ipv6_b | replace("0::/56","1::/64") }}' + vars: + vpc_a: '{{ create_vpcs.results[0].vpc }}' + vpc_b: '{{ create_vpcs.results[1].vpc }}' + vpc_ipv6_a: '{{ vpc_a.ipv6_cidr_block_association_set[0].ipv6_cidr_block }}' + vpc_ipv6_b: '{{ vpc_b.ipv6_cidr_block_association_set[0].ipv6_cidr_block }}' + +- name: 'Create subnets' + ec2_vpc_subnet: + az: '{{ item.az }}' + cidr: '{{ item.cidr }}' + ipv6_cidr: '{{ item.ipv6_cidr }}' + tags: + Name: '{{ item.name }}' + vpc_id: '{{ item.vpc_id }}' + loop: + - az: '{{ subnet_az_a_1 }}' + cidr: '{{ subnet_cidr_a_1 }}' + ipv6_cidr: '{{ subnet_ipv6_a_1 }}' + vpc_id: '{{ vpc_id_a }}' + name: '{{ subnet_name_a_1 }}' + - az: '{{ subnet_az_a_2 }}' + cidr: '{{ subnet_cidr_a_2 }}' + ipv6_cidr: '{{ subnet_ipv6_a_2 }}' + vpc_id: '{{ vpc_id_a }}' + name: '{{ subnet_name_a_2 }}' + - az: '{{ subnet_az_a_3 }}' + cidr: '{{ subnet_cidr_a_3 }}' + ipv6_cidr: '{{ subnet_ipv6_a_3 }}' + vpc_id: '{{ vpc_id_a }}' + name: '{{ subnet_name_a_3 }}' + - az: '{{ subnet_az_b_1 }}' + cidr: '{{ subnet_cidr_b_1 }}' + ipv6_cidr: '{{ subnet_ipv6_b_1 }}' + vpc_id: '{{ vpc_id_b }}' + name: '{{ subnet_name_b_1 }}' + - az: '{{ subnet_az_b_2 }}' + cidr: '{{ subnet_cidr_b_2 }}' + ipv6_cidr: '{{ subnet_ipv6_b_2 }}' + vpc_id: '{{ vpc_id_b }}' + name: '{{ subnet_name_b_2 }}' + - az: '{{ subnet_az_a_1a }}' + cidr: '{{ subnet_cidr_a_1a }}' + ipv6_cidr: '{{ subnet_ipv6_a_1a }}' + vpc_id: '{{ vpc_id_a }}' + name: '{{ subnet_name_a_1a }}' + register: create_subnets + +- set_fact: + subnet_id_a_1: '{{ create_subnets.results[0].subnet.id }}' + subnet_id_a_2: '{{ create_subnets.results[1].subnet.id }}' + subnet_id_a_3: '{{ create_subnets.results[2].subnet.id }}' + subnet_id_b_1: '{{ create_subnets.results[3].subnet.id }}' + subnet_id_b_2: '{{ create_subnets.results[4].subnet.id }}' + subnet_id_a_1a: '{{ create_subnets.results[5].subnet.id }}' diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_transit_gateway_vpc_attachment/tasks/simple.yml b/ansible_collections/community/aws/tests/integration/targets/ec2_transit_gateway_vpc_attachment/tasks/simple.yml new file mode 100644 index 000000000..0085813a3 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_transit_gateway_vpc_attachment/tasks/simple.yml @@ -0,0 +1,3611 @@ +--- +# ============================================================================= +# Creation +- block: + - name: '(CHECK_MODE) Create an attachment - minimal parameters' + check_mode: True + ec2_transit_gateway_vpc_attachment: + transit_gateway: '{{ tgw_id }}' + subnets: + - '{{ subnet_id_a_1 }}' + register: simple_attach + + - assert: + that: + - simple_attach is changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: 'Create an attachment - minimal parameters' + ec2_transit_gateway_vpc_attachment: + transit_gateway: '{{ tgw_id }}' + subnets: + - '{{ subnet_id_a_1 }}' + register: simple_attach + + - assert: + that: + - simple_attach is changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.transit_gateway_attachment_id.startswith('tgw-attach-') + - attachment.state == 'available' + - attachment.tags | length == 0 + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: Save Attachment ID + set_fact: + simple_attachment_id: '{{ simple_attach.attachments[0].transit_gateway_attachment_id }}' + + - name: '(CHECK_MODE) Create an attachment - minimal parameters -- IDEMPOTENCY' + check_mode: True + ec2_transit_gateway_vpc_attachment: + transit_gateway: '{{ tgw_id }}' + subnets: + - '{{ subnet_id_a_1 }}' + register: simple_attach + + - assert: + that: + - simple_attach is not changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 0 + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: 'Create an attachment - minimal parameters -- IDEMPOTENCY' + ec2_transit_gateway_vpc_attachment: + transit_gateway: '{{ tgw_id }}' + subnets: + - '{{ subnet_id_a_1 }}' + register: simple_attach + + - assert: + that: + - simple_attach is not changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 0 + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + +# ===== + + - name: '(CHECK_MODE) By Id - minimal parameters -- IDEMPOTENCY' + check_mode: True + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + subnets: + - '{{ subnet_id_a_1 }}' + register: simple_attach + + - assert: + that: + - simple_attach is not changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 0 + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: 'By Id - minimal parameters -- IDEMPOTENCY' + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + subnets: + - '{{ subnet_id_a_1 }}' + register: simple_attach + + - assert: + that: + - simple_attach is not changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 0 + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + +# ============================================================================= +# Set a name + + - name: '(CHECK_MODE) Set name' + check_mode: True + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + name: '{{ attachment_name }}' + register: simple_attach + + - assert: + that: + - simple_attach is changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 1 + - '"Name" in attachment.tags' + - attachment.tags.Name == attachment_name + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: 'Set name' + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + name: '{{ attachment_name }}' + register: simple_attach + + - assert: + that: + - simple_attach is changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 1 + - '"Name" in attachment.tags' + - attachment.tags.Name == attachment_name + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: '(CHECK_MODE) Set name -- IDEMPOTENCY' + check_mode: True + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + name: '{{ attachment_name }}' + register: simple_attach + + - assert: + that: + - simple_attach is not changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 1 + - '"Name" in attachment.tags' + - attachment.tags.Name == attachment_name + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: 'Set name -- IDEMPOTENCY' + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + name: '{{ attachment_name }}' + register: simple_attach + + - assert: + that: + - simple_attach is not changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 1 + - '"Name" in attachment.tags' + - attachment.tags.Name == attachment_name + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + +# ===== + + - name: '(CHECK_MODE) By Name - minimal parameters -- IDEMPOTENCY' + check_mode: True + ec2_transit_gateway_vpc_attachment: + name: '{{ attachment_name }}' + subnets: + - '{{ subnet_id_a_1 }}' + register: simple_attach + + - assert: + that: + - simple_attach is not changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 1 + - '"Name" in attachment.tags' + - attachment.tags.Name == attachment_name + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: 'By Name - minimal parameters -- IDEMPOTENCY' + ec2_transit_gateway_vpc_attachment: + name: '{{ attachment_name }}' + subnets: + - '{{ subnet_id_a_1 }}' + register: simple_attach + + - assert: + that: + - simple_attach is not changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 1 + - '"Name" in attachment.tags' + - attachment.tags.Name == attachment_name + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + +# ============================================================================= +# Describe + + - name: 'Describe all attachments' + ec2_transit_gateway_vpc_attachment_info: + register: info + + - assert: + that: + - info is not changed + - '"attachments" in info' + - info.attachments | length >= 2 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length >= 1 + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - '"Name" in attachment.tags' + vars: + attachment: '{{ info.attachments[0] }}' + + - name: 'Describe attachments on a specific VPC' + ec2_transit_gateway_vpc_attachment_info: + filters: + transit-gateway-id: '{{ tgw_id }}' + register: info + + - assert: + that: + - info is not changed + - '"attachments" in info' + - info.attachments | length == 2 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length >= 1 + - attachment.transit_gateway_id == tgw_id + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - '"Name" in attachment.tags' + vars: + attachment: '{{ info.attachments[0] }}' + + - name: 'Describe attachment with a specific name' + ec2_transit_gateway_vpc_attachment_info: + name: '{{ attachment_name }}' + register: info + + - assert: + that: + - info is not changed + - '"attachments" in info' + - info.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 1 + - '"Name" in attachment.tags' + - attachment.tags.Name == attachment_name + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ info.attachments[0] }}' + + - name: 'Describe attachment by ID' + ec2_transit_gateway_vpc_attachment_info: + id: '{{ simple_attachment_id }}' + register: info + + - assert: + that: + - info is not changed + - '"attachments" in info' + - info.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 1 + - '"Name" in attachment.tags' + - attachment.tags.Name == attachment_name + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ info.attachments[0] }}' + +# ============================================================================= +# Tag attachment + + - name: '(CHECK_MODE) Set tags' + check_mode: True + ec2_transit_gateway_vpc_attachment: + name: '{{ attachment_name }}' + tags: + CamelCase: CamelCaseValue + pascalCase: pascalCaseValue + snake_case: snake_case_value + "Tag with Space": value with space + register: simple_attach + + - assert: + that: + - simple_attach is changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 5 + - '"Name" in attachment.tags' + - '"CamelCase" in attachment.tags' + - '"pascalCase" in attachment.tags' + - '"snake_case" in attachment.tags' + - '"Tag with Space" in attachment.tags' + - attachment.tags.Name == attachment_name + - attachment.tags.CamelCase == 'CamelCaseValue' + - attachment.tags.pascalCase == 'pascalCaseValue' + - attachment.tags.snake_case == 'snake_case_value' + - attachment.tags['Tag with Space'] == 'value with space' + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: 'Set tags' + ec2_transit_gateway_vpc_attachment: + name: '{{ attachment_name }}' + tags: + CamelCase: CamelCaseValue + pascalCase: pascalCaseValue + snake_case: snake_case_value + "Tag with Space": value with space + register: simple_attach + + - assert: + that: + - simple_attach is changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 5 + - '"Name" in attachment.tags' + - '"CamelCase" in attachment.tags' + - '"pascalCase" in attachment.tags' + - '"snake_case" in attachment.tags' + - '"Tag with Space" in attachment.tags' + - attachment.tags.Name == attachment_name + - attachment.tags.CamelCase == 'CamelCaseValue' + - attachment.tags.pascalCase == 'pascalCaseValue' + - attachment.tags.snake_case == 'snake_case_value' + - attachment.tags['Tag with Space'] == 'value with space' + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: '(CHECK_MODE) Set tags -- IDEMPOTENCY' + check_mode: True + ec2_transit_gateway_vpc_attachment: + name: '{{ attachment_name }}' + tags: + CamelCase: CamelCaseValue + pascalCase: pascalCaseValue + snake_case: snake_case_value + "Tag with Space": value with space + register: simple_attach + + - assert: + that: + - simple_attach is not changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 5 + - '"Name" in attachment.tags' + - '"CamelCase" in attachment.tags' + - '"pascalCase" in attachment.tags' + - '"snake_case" in attachment.tags' + - '"Tag with Space" in attachment.tags' + - attachment.tags.Name == attachment_name + - attachment.tags.CamelCase == 'CamelCaseValue' + - attachment.tags.pascalCase == 'pascalCaseValue' + - attachment.tags.snake_case == 'snake_case_value' + - attachment.tags['Tag with Space'] == 'value with space' + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: 'Set tags -- IDEMPOTENCY' + ec2_transit_gateway_vpc_attachment: + name: '{{ attachment_name }}' + tags: + CamelCase: CamelCaseValue + pascalCase: pascalCaseValue + snake_case: snake_case_value + "Tag with Space": value with space + register: simple_attach + + - assert: + that: + - simple_attach is not changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 5 + - '"Name" in attachment.tags' + - '"CamelCase" in attachment.tags' + - '"pascalCase" in attachment.tags' + - '"snake_case" in attachment.tags' + - '"Tag with Space" in attachment.tags' + - attachment.tags.Name == attachment_name + - attachment.tags.CamelCase == 'CamelCaseValue' + - attachment.tags.pascalCase == 'pascalCaseValue' + - attachment.tags.snake_case == 'snake_case_value' + - attachment.tags['Tag with Space'] == 'value with space' + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: 'Describe attachment with tags set' + ec2_transit_gateway_vpc_attachment_info: + id: '{{ simple_attachment_id }}' + register: info + + - assert: + that: + - info is not changed + - '"attachments" in info' + - info.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 5 + - '"Name" in attachment.tags' + - '"CamelCase" in attachment.tags' + - '"pascalCase" in attachment.tags' + - '"snake_case" in attachment.tags' + - '"Tag with Space" in attachment.tags' + - attachment.tags.Name == attachment_name + - attachment.tags.CamelCase == 'CamelCaseValue' + - attachment.tags.pascalCase == 'pascalCaseValue' + - attachment.tags.snake_case == 'snake_case_value' + - attachment.tags['Tag with Space'] == 'value with space' + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ info.attachments[0] }}' + +# ===== + + - name: '(CHECK_MODE) No change to tags with name set -- IDEMPOTENCY' + check_mode: True + ec2_transit_gateway_vpc_attachment: + name: '{{ attachment_name }}' + register: simple_attach + + - assert: + that: + - simple_attach is not changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 5 + - '"Name" in attachment.tags' + - '"CamelCase" in attachment.tags' + - '"pascalCase" in attachment.tags' + - '"snake_case" in attachment.tags' + - '"Tag with Space" in attachment.tags' + - attachment.tags.Name == attachment_name + - attachment.tags.CamelCase == 'CamelCaseValue' + - attachment.tags.pascalCase == 'pascalCaseValue' + - attachment.tags.snake_case == 'snake_case_value' + - attachment.tags['Tag with Space'] == 'value with space' + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: 'No change to tags with name set -- IDEMPOTENCY' + ec2_transit_gateway_vpc_attachment: + name: '{{ attachment_name }}' + register: simple_attach + + - assert: + that: + - simple_attach is not changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 5 + - '"Name" in attachment.tags' + - '"CamelCase" in attachment.tags' + - '"pascalCase" in attachment.tags' + - '"snake_case" in attachment.tags' + - '"Tag with Space" in attachment.tags' + - attachment.tags.Name == attachment_name + - attachment.tags.CamelCase == 'CamelCaseValue' + - attachment.tags.pascalCase == 'pascalCaseValue' + - attachment.tags.snake_case == 'snake_case_value' + - attachment.tags['Tag with Space'] == 'value with space' + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + +# ===== + + - name: '(CHECK_MODE) Update tags' + check_mode: True + ec2_transit_gateway_vpc_attachment: + name: '{{ attachment_name }}' + purge_tags: False + tags: + snake_case: snake_case_value 2 + "Tag with Space": value with space 2 + register: simple_attach + + - assert: + that: + - simple_attach is changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 5 + - '"Name" in attachment.tags' + - '"CamelCase" in attachment.tags' + - '"pascalCase" in attachment.tags' + - '"snake_case" in attachment.tags' + - '"Tag with Space" in attachment.tags' + - attachment.tags.Name == attachment_name + - attachment.tags.CamelCase == 'CamelCaseValue' + - attachment.tags.pascalCase == 'pascalCaseValue' + - attachment.tags.snake_case == 'snake_case_value 2' + - attachment.tags['Tag with Space'] == 'value with space 2' + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: 'Update tags' + ec2_transit_gateway_vpc_attachment: + name: '{{ attachment_name }}' + purge_tags: False + tags: + snake_case: snake_case_value 2 + "Tag with Space": value with space 2 + register: simple_attach + + - assert: + that: + - simple_attach is changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 5 + - '"Name" in attachment.tags' + - '"CamelCase" in attachment.tags' + - '"pascalCase" in attachment.tags' + - '"snake_case" in attachment.tags' + - '"Tag with Space" in attachment.tags' + - attachment.tags.Name == attachment_name + - attachment.tags.CamelCase == 'CamelCaseValue' + - attachment.tags.pascalCase == 'pascalCaseValue' + - attachment.tags.snake_case == 'snake_case_value 2' + - attachment.tags['Tag with Space'] == 'value with space 2' + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: '(CHECK_MODE) Update tags -- IDEMPOTENCY' + check_mode: True + ec2_transit_gateway_vpc_attachment: + name: '{{ attachment_name }}' + purge_tags: False + tags: + snake_case: snake_case_value 2 + "Tag with Space": value with space 2 + register: simple_attach + + - assert: + that: + - simple_attach is not changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 5 + - '"Name" in attachment.tags' + - '"CamelCase" in attachment.tags' + - '"pascalCase" in attachment.tags' + - '"snake_case" in attachment.tags' + - '"Tag with Space" in attachment.tags' + - attachment.tags.Name == attachment_name + - attachment.tags.CamelCase == 'CamelCaseValue' + - attachment.tags.pascalCase == 'pascalCaseValue' + - attachment.tags.snake_case == 'snake_case_value 2' + - attachment.tags['Tag with Space'] == 'value with space 2' + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: 'Update tags -- IDEMPOTENCY' + ec2_transit_gateway_vpc_attachment: + name: '{{ attachment_name }}' + purge_tags: False + tags: + snake_case: snake_case_value 2 + "Tag with Space": value with space 2 + register: simple_attach + + - assert: + that: + - simple_attach is not changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 5 + - '"Name" in attachment.tags' + - '"CamelCase" in attachment.tags' + - '"pascalCase" in attachment.tags' + - '"snake_case" in attachment.tags' + - '"Tag with Space" in attachment.tags' + - attachment.tags.Name == attachment_name + - attachment.tags.CamelCase == 'CamelCaseValue' + - attachment.tags.pascalCase == 'pascalCaseValue' + - attachment.tags.snake_case == 'snake_case_value 2' + - attachment.tags['Tag with Space'] == 'value with space 2' + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + +# ===== + + - name: '(CHECK_MODE) Remove tags' + check_mode: True + ec2_transit_gateway_vpc_attachment: + name: '{{ attachment_name }}' + tags: + CamelCase: CamelCaseValue + pascalCase: pascalCaseValue + register: simple_attach + + - assert: + that: + - simple_attach is changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 3 + - '"Name" in attachment.tags' + - '"CamelCase" in attachment.tags' + - '"pascalCase" in attachment.tags' + - attachment.tags.Name == attachment_name + - attachment.tags.CamelCase == 'CamelCaseValue' + - attachment.tags.pascalCase == 'pascalCaseValue' + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: 'Remove tags' + ec2_transit_gateway_vpc_attachment: + name: '{{ attachment_name }}' + tags: + CamelCase: CamelCaseValue + pascalCase: pascalCaseValue + register: simple_attach + + - assert: + that: + - simple_attach is changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 3 + - '"Name" in attachment.tags' + - '"CamelCase" in attachment.tags' + - '"pascalCase" in attachment.tags' + - attachment.tags.Name == attachment_name + - attachment.tags.CamelCase == 'CamelCaseValue' + - attachment.tags.pascalCase == 'pascalCaseValue' + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: '(CHECK_MODE) Remove tags -- IDEMPOTENCY' + check_mode: True + ec2_transit_gateway_vpc_attachment: + name: '{{ attachment_name }}' + tags: + CamelCase: CamelCaseValue + pascalCase: pascalCaseValue + register: simple_attach + + - assert: + that: + - simple_attach is not changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 3 + - '"Name" in attachment.tags' + - '"CamelCase" in attachment.tags' + - '"pascalCase" in attachment.tags' + - attachment.tags.Name == attachment_name + - attachment.tags.CamelCase == 'CamelCaseValue' + - attachment.tags.pascalCase == 'pascalCaseValue' + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: 'Remove tags -- IDEMPOTENCY' + ec2_transit_gateway_vpc_attachment: + name: '{{ attachment_name }}' + tags: + CamelCase: CamelCaseValue + pascalCase: pascalCaseValue + register: simple_attach + + - assert: + that: + - simple_attach is not changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 3 + - '"Name" in attachment.tags' + - '"CamelCase" in attachment.tags' + - '"pascalCase" in attachment.tags' + - attachment.tags.Name == attachment_name + - attachment.tags.CamelCase == 'CamelCaseValue' + - attachment.tags.pascalCase == 'pascalCaseValue' + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + +# ===== + + - name: '(CHECK_MODE) Add tags with no purge' + check_mode: True + ec2_transit_gateway_vpc_attachment: + name: '{{ attachment_name }}' + purge_tags: False + tags: + AnotherTag: Another Value + register: simple_attach + + - assert: + that: + - simple_attach is changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 4 + - '"Name" in attachment.tags' + - '"CamelCase" in attachment.tags' + - '"pascalCase" in attachment.tags' + - '"AnotherTag" in attachment.tags' + - attachment.tags.Name == attachment_name + - attachment.tags.CamelCase == 'CamelCaseValue' + - attachment.tags.pascalCase == 'pascalCaseValue' + - attachment.tags.AnotherTag == 'Another Value' + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: 'Add tags with no purge' + ec2_transit_gateway_vpc_attachment: + name: '{{ attachment_name }}' + purge_tags: False + tags: + AnotherTag: Another Value + register: simple_attach + + - assert: + that: + - simple_attach is changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 4 + - '"Name" in attachment.tags' + - '"CamelCase" in attachment.tags' + - '"pascalCase" in attachment.tags' + - '"AnotherTag" in attachment.tags' + - attachment.tags.Name == attachment_name + - attachment.tags.CamelCase == 'CamelCaseValue' + - attachment.tags.pascalCase == 'pascalCaseValue' + - attachment.tags.AnotherTag == 'Another Value' + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: '(CHECK_MODE) Add tags with no purge -- IDEMPOTENCY' + check_mode: True + ec2_transit_gateway_vpc_attachment: + name: '{{ attachment_name }}' + purge_tags: False + tags: + AnotherTag: Another Value + register: simple_attach + + - assert: + that: + - simple_attach is not changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 4 + - '"Name" in attachment.tags' + - '"CamelCase" in attachment.tags' + - '"pascalCase" in attachment.tags' + - '"AnotherTag" in attachment.tags' + - attachment.tags.Name == attachment_name + - attachment.tags.CamelCase == 'CamelCaseValue' + - attachment.tags.pascalCase == 'pascalCaseValue' + - attachment.tags.AnotherTag == 'Another Value' + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: 'Add tags with no purge -- IDEMPOTENCY' + ec2_transit_gateway_vpc_attachment: + name: '{{ attachment_name }}' + purge_tags: False + tags: + AnotherTag: Another Value + register: simple_attach + + - assert: + that: + - simple_attach is not changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 4 + - '"Name" in attachment.tags' + - '"CamelCase" in attachment.tags' + - '"pascalCase" in attachment.tags' + - '"AnotherTag" in attachment.tags' + - attachment.tags.Name == attachment_name + - attachment.tags.CamelCase == 'CamelCaseValue' + - attachment.tags.pascalCase == 'pascalCaseValue' + - attachment.tags.AnotherTag == 'Another Value' + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + +# ===== + + - name: '(CHECK_MODE) Remove all tags with name set' + check_mode: True + ec2_transit_gateway_vpc_attachment: + name: '{{ attachment_name }}' + tags: {} + register: simple_attach + + - assert: + that: + - simple_attach is changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 1 + - '"Name" in attachment.tags' + - attachment.tags.Name == attachment_name + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: 'Remove all tags with name set' + ec2_transit_gateway_vpc_attachment: + name: '{{ attachment_name }}' + tags: {} + register: simple_attach + + - assert: + that: + - simple_attach is changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 1 + - '"Name" in attachment.tags' + - attachment.tags.Name == attachment_name + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: '(CHECK_MODE) Remove all tags with name set -- IDEMPOTENCY' + check_mode: True + ec2_transit_gateway_vpc_attachment: + name: '{{ attachment_name }}' + tags: {} + register: simple_attach + + - assert: + that: + - simple_attach is not changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 1 + - '"Name" in attachment.tags' + - attachment.tags.Name == attachment_name + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: 'Remove all tags with name set -- IDEMPOTENCY' + ec2_transit_gateway_vpc_attachment: + name: '{{ attachment_name }}' + tags: {} + register: simple_attach + + - assert: + that: + - simple_attach is not changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 1 + - '"Name" in attachment.tags' + - attachment.tags.Name == attachment_name + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + +# ===== + + - name: '(CHECK_MODE) Remove all tags including name' + check_mode: True + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + tags: {} + register: simple_attach + + - assert: + that: + - simple_attach is changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 0 + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: 'Remove all tags including name' + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + tags: {} + register: simple_attach + + - assert: + that: + - simple_attach is changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 0 + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: '(CHECK_MODE) Remove all tags including name -- IDEMPOTENCY' + check_mode: True + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + tags: {} + register: simple_attach + + - assert: + that: + - simple_attach is not changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 0 + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: 'Remove all tags including name -- IDEMPOTENCY' + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + tags: {} + register: simple_attach + + - assert: + that: + - simple_attach is not changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 0 + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + +# ============================================================================= +# Options + + - name: '(CHECK_MODE) Set IPv6 support' + check_mode: True + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + ipv6_support: True + register: simple_attach + + - assert: + that: + - simple_attach is changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'enable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 0 + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: 'Set IPv6 support' + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + ipv6_support: True + register: simple_attach + + - assert: + that: + - simple_attach is changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'enable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 0 + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: '(CHECK_MODE) Set IPv6 support -- IDEMPOTENCY' + check_mode: True + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + ipv6_support: True + register: simple_attach + + - assert: + that: + - simple_attach is not changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'enable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 0 + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: 'Set IPv6 support -- IDEMPOTENCY' + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + ipv6_support: True + register: simple_attach + + - assert: + that: + - simple_attach is not changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'enable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 0 + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + +# ===== + + - name: '(CHECK_MODE) Set DNS support' + check_mode: True + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + dns_support: False + register: simple_attach + + - assert: + that: + - simple_attach is changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'disable' + - attachment.options.ipv6_support == 'enable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 0 + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: 'Set DNS support' + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + dns_support: False + register: simple_attach + + - assert: + that: + - simple_attach is changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'disable' + - attachment.options.ipv6_support == 'enable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 0 + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: '(CHECK_MODE) Set DNS support -- IDEMPOTENCY' + check_mode: True + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + dns_support: False + register: simple_attach + + - assert: + that: + - simple_attach is not changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'disable' + - attachment.options.ipv6_support == 'enable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 0 + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: 'Set DNS support -- IDEMPOTENCY' + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + dns_support: False + register: simple_attach + + - assert: + that: + - simple_attach is not changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'disable' + - attachment.options.ipv6_support == 'enable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 0 + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + +# ===== + + - name: '(CHECK_MODE) Set Appliance Mode support' + check_mode: True + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + appliance_mode_support: True + register: simple_attach + + - assert: + that: + - simple_attach is changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'enable' + - attachment.options.dns_support == 'disable' + - attachment.options.ipv6_support == 'enable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 0 + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: 'Set Appliance Mode support' + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + appliance_mode_support: True + register: simple_attach + + - assert: + that: + - simple_attach is changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'enable' + - attachment.options.dns_support == 'disable' + - attachment.options.ipv6_support == 'enable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 0 + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: '(CHECK_MODE) Set Appliance Mode support -- IDEMPOTENCY' + check_mode: True + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + appliance_mode_support: True + register: simple_attach + + - assert: + that: + - simple_attach is not changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'enable' + - attachment.options.dns_support == 'disable' + - attachment.options.ipv6_support == 'enable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 0 + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: 'Set Appliance Mode support -- IDEMPOTENCY' + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + appliance_mode_support: True + register: simple_attach + + - assert: + that: + - simple_attach is not changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'enable' + - attachment.options.dns_support == 'disable' + - attachment.options.ipv6_support == 'enable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 0 + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + +# ===== + + - name: '(CHECK_MODE) Update IPv6 support' + check_mode: True + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + ipv6_support: False + register: simple_attach + + - assert: + that: + - simple_attach is changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'enable' + - attachment.options.dns_support == 'disable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 0 + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: 'Update IPv6 support' + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + ipv6_support: False + register: simple_attach + + - assert: + that: + - simple_attach is changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'enable' + - attachment.options.dns_support == 'disable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 0 + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: '(CHECK_MODE) Update IPv6 support -- IDEMPOTENCY' + check_mode: True + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + ipv6_support: False + register: simple_attach + + - assert: + that: + - simple_attach is not changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'enable' + - attachment.options.dns_support == 'disable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 0 + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: 'Update IPv6 support -- IDEMPOTENCY' + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + ipv6_support: False + register: simple_attach + + - assert: + that: + - simple_attach is not changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'enable' + - attachment.options.dns_support == 'disable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 0 + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + +# ===== + + - name: '(CHECK_MODE) Update DNS support' + check_mode: True + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + dns_support: True + register: simple_attach + + - assert: + that: + - simple_attach is changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'enable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 0 + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: 'Update DNS support' + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + dns_support: True + register: simple_attach + + - assert: + that: + - simple_attach is changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'enable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 0 + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: '(CHECK_MODE) Update DNS support -- IDEMPOTENCY' + check_mode: True + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + dns_support: True + register: simple_attach + + - assert: + that: + - simple_attach is not changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'enable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 0 + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: 'Update DNS support -- IDEMPOTENCY' + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + dns_support: True + register: simple_attach + + - assert: + that: + - simple_attach is not changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'enable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 0 + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + +# ===== + + - name: '(CHECK_MODE) Update Appliance Mode support' + check_mode: True + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + appliance_mode_support: False + register: simple_attach + + - assert: + that: + - simple_attach is changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 0 + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: 'Update Appliance Mode support' + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + appliance_mode_support: False + register: simple_attach + + - assert: + that: + - simple_attach is changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 0 + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: '(CHECK_MODE) Update Appliance Mode support -- IDEMPOTENCY' + check_mode: True + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + appliance_mode_support: False + register: simple_attach + + - assert: + that: + - simple_attach is not changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 0 + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: 'Update Appliance Mode support -- IDEMPOTENCY' + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + appliance_mode_support: False + register: simple_attach + + - assert: + that: + - simple_attach is not changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 1 + - subnet_id_a_1 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 0 + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + +# ============================================================================= +# Subnet Management + + - name: '(CHECK_MODE) Try to add subnet from a different VPC - no purge' + check_mode: True + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + subnets: + - '{{ subnet_id_b_2 }}' + purge_subnets: False + register: simple_attach + ignore_errors: True + + - assert: + that: + - simple_attach is failed + + - name: 'Try to add subnet from a different VPC - no purge' + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + subnets: + - '{{ subnet_id_b_2 }}' + purge_subnets: False + register: simple_attach + ignore_errors: True + + - assert: + that: + - simple_attach is failed + +# ===== + + - name: '(CHECK_MODE) Try to add subnet from a different VPC - with purge' + check_mode: True + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + subnets: + - '{{ subnet_id_a_1 }}' + - '{{ subnet_id_b_2 }}' + purge_subnets: True + register: simple_attach + ignore_errors: True + + - assert: + that: + - simple_attach is failed + + - name: 'Try to add subnet from a different VPC - with purge' + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + subnets: + - '{{ subnet_id_a_1 }}' + - '{{ subnet_id_b_2 }}' + purge_subnets: True + register: simple_attach + ignore_errors: True + + - assert: + that: + - simple_attach is failed + +# ===== + + - name: '(CHECK_MODE) Try to add subnet in the same AZ - no purge' + check_mode: True + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + subnets: + - '{{ subnet_id_b_1a }}' + purge_subnets: False + register: simple_attach + ignore_errors: True + + - assert: + that: + - simple_attach is failed + + - name: 'Try to add subnet in the same AZ - no purge' + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + subnets: + - '{{ subnet_id_a_1a }}' + purge_subnets: False + register: simple_attach + ignore_errors: True + + - assert: + that: + - simple_attach is failed + +# ===== + + - name: '(CHECK_MODE) Try to add subnet in the same AZ - with purge' + check_mode: True + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + subnets: + - '{{ subnet_id_a_1 }}' + - '{{ subnet_id_a_1a }}' + purge_subnets: True + register: simple_attach + ignore_errors: True + + - assert: + that: + - simple_attach is failed + + - name: 'Try to add subnet in the same AZ - with purge' + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + subnets: + - '{{ subnet_id_a_1 }}' + - '{{ subnet_id_a_1a }}' + purge_subnets: True + register: simple_attach + ignore_errors: True + + - assert: + that: + - simple_attach is failed + +# ===== + + - name: '(CHECK_MODE) Add subnet - without purge' + check_mode: True + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + subnets: + - '{{ subnet_id_a_2 }}' + purge_subnets: False + register: simple_attach + + - assert: + that: + - simple_attach is changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 2 + - subnet_id_a_1 in attachment.subnet_ids + - subnet_id_a_2 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 0 + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: 'Add subnet - without purge' + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + subnets: + - '{{ subnet_id_a_2 }}' + purge_subnets: False + register: simple_attach + + - assert: + that: + - simple_attach is changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 2 + - subnet_id_a_1 in attachment.subnet_ids + - subnet_id_a_2 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 0 + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: '(CHECK_MODE) Add subnet - without purge -- IDEMPOTENCY' + check_mode: True + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + subnets: + - '{{ subnet_id_a_2 }}' + purge_subnets: False + register: simple_attach + + - assert: + that: + - simple_attach is not changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 2 + - subnet_id_a_1 in attachment.subnet_ids + - subnet_id_a_2 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 0 + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: 'Add subnet - without purge -- IDEMPOTENCY' + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + subnets: + - '{{ subnet_id_a_2 }}' + purge_subnets: False + register: simple_attach + + - assert: + that: + - simple_attach is not changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 2 + - subnet_id_a_1 in attachment.subnet_ids + - subnet_id_a_2 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 0 + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + +# ===== + + - name: '(CHECK_MODE) Add subnet - with purge' + check_mode: True + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + subnets: + - '{{ subnet_id_a_1 }}' + - '{{ subnet_id_a_2 }}' + - '{{ subnet_id_a_3 }}' + purge_subnets: True + register: simple_attach + + - assert: + that: + - simple_attach is changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 3 + - subnet_id_a_1 in attachment.subnet_ids + - subnet_id_a_2 in attachment.subnet_ids + - subnet_id_a_3 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 0 + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: 'Add subnet - with purge' + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + subnets: + - '{{ subnet_id_a_1 }}' + - '{{ subnet_id_a_2 }}' + - '{{ subnet_id_a_3 }}' + purge_subnets: True + register: simple_attach + + - assert: + that: + - simple_attach is changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 3 + - subnet_id_a_1 in attachment.subnet_ids + - subnet_id_a_2 in attachment.subnet_ids + - subnet_id_a_3 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 0 + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: '(CHECK_MODE) Add subnet - with purge -- IDEMPOTENCY' + check_mode: True + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + subnets: + - '{{ subnet_id_a_1 }}' + - '{{ subnet_id_a_2 }}' + - '{{ subnet_id_a_3 }}' + purge_subnets: True + register: simple_attach + + - assert: + that: + - simple_attach is not changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 3 + - subnet_id_a_1 in attachment.subnet_ids + - subnet_id_a_2 in attachment.subnet_ids + - subnet_id_a_3 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 0 + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: 'Add subnet - with purge -- IDEMPOTENCY' + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + subnets: + - '{{ subnet_id_a_1 }}' + - '{{ subnet_id_a_2 }}' + - '{{ subnet_id_a_3 }}' + purge_subnets: True + register: simple_attach + + - assert: + that: + - simple_attach is not changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 3 + - subnet_id_a_1 in attachment.subnet_ids + - subnet_id_a_2 in attachment.subnet_ids + - subnet_id_a_3 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 0 + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + +# ===== + + - name: '(CHECK_MODE) Remove subnet' + check_mode: True + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + subnets: + - '{{ subnet_id_a_2 }}' + - '{{ subnet_id_a_3 }}' + purge_subnets: True + register: simple_attach + + - assert: + that: + - simple_attach is changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 2 + - subnet_id_a_2 in attachment.subnet_ids + - subnet_id_a_3 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 0 + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: 'Remove subnet' + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + subnets: + - '{{ subnet_id_a_2 }}' + - '{{ subnet_id_a_3 }}' + purge_subnets: True + register: simple_attach + + - assert: + that: + - simple_attach is changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 2 + - subnet_id_a_2 in attachment.subnet_ids + - subnet_id_a_3 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 0 + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: '(CHECK_MODE) Remove subnet -- IDEMPOTENCY' + check_mode: True + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + subnets: + - '{{ subnet_id_a_2 }}' + - '{{ subnet_id_a_3 }}' + purge_subnets: True + register: simple_attach + + - assert: + that: + - simple_attach is not changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 2 + - subnet_id_a_2 in attachment.subnet_ids + - subnet_id_a_3 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 0 + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: 'Remove subnet -- IDEMPOTENCY' + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + subnets: + - '{{ subnet_id_a_2 }}' + - '{{ subnet_id_a_3 }}' + purge_subnets: True + register: simple_attach + + - assert: + that: + - simple_attach is not changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 2 + - subnet_id_a_2 in attachment.subnet_ids + - subnet_id_a_3 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 0 + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + +# ===== + + - name: '(CHECK_MODE) Remove and add subnet' + check_mode: True + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + subnets: + - '{{ subnet_id_a_1 }}' + - '{{ subnet_id_a_2 }}' + purge_subnets: True + register: simple_attach + + - assert: + that: + - simple_attach is changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 2 + - subnet_id_a_1 in attachment.subnet_ids + - subnet_id_a_2 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 0 + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: 'Remove and add subnet' + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + subnets: + - '{{ subnet_id_a_1 }}' + - '{{ subnet_id_a_2 }}' + purge_subnets: True + register: simple_attach + + - assert: + that: + - simple_attach is changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 2 + - subnet_id_a_1 in attachment.subnet_ids + - subnet_id_a_2 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 0 + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: '(CHECK_MODE) Remove and add subnet -- IDEMPOTENCY' + check_mode: True + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + subnets: + - '{{ subnet_id_a_1 }}' + - '{{ subnet_id_a_2 }}' + purge_subnets: True + register: simple_attach + + - assert: + that: + - simple_attach is not changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 2 + - subnet_id_a_1 in attachment.subnet_ids + - subnet_id_a_2 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 0 + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + + - name: 'Remove and add subnet -- IDEMPOTENCY' + ec2_transit_gateway_vpc_attachment: + id: '{{ simple_attachment_id }}' + subnets: + - '{{ subnet_id_a_1 }}' + - '{{ subnet_id_a_2 }}' + purge_subnets: True + register: simple_attach + + - assert: + that: + - simple_attach is not changed + - '"attachments" in simple_attach' + - simple_attach.attachments | length == 1 + - '"subnet_ids" in attachment' + - '"transit_gateway_id" in attachment' + - '"vpc_id" in attachment' + - attachment.subnet_ids | length == 2 + - subnet_id_a_1 in attachment.subnet_ids + - subnet_id_a_2 in attachment.subnet_ids + - attachment.transit_gateway_id == tgw_id + - attachment.vpc_id == vpc_id_a + - '"creation_time" in attachment' + - '"options" in attachment' + - '"state" in attachment' + - '"tags" in attachment' + - '"transit_gateway_attachment_id" in attachment' + - '"vpc_owner_id" in attachment' + - '"appliance_mode_support" in attachment.options' + - '"dns_support" in attachment.options' + - '"ipv6_support" in attachment.options' + - attachment.options.appliance_mode_support == 'disable' + - attachment.options.dns_support == 'enable' + - attachment.options.ipv6_support == 'disable' + - attachment.state == 'available' + - attachment.transit_gateway_attachment_id == simple_attachment_id + - attachment.tags | length == 0 + - attachment.vpc_owner_id == vpc_owner_a + vars: + attachment: '{{ simple_attach.attachments[0] }}' + +# ============================================================================= +# Deletion + + - name: '(CHECK_MODE) Delete an attachment - minimal parameters' + check_mode: True + ec2_transit_gateway_vpc_attachment: + state: absent + id: '{{ simple_attachment_id }}' + wait: False + register: simple_attach + + - assert: + that: + - simple_attach is changed + + - name: 'Delete an attachment - minimal parameters' + ec2_transit_gateway_vpc_attachment: + state: absent + id: '{{ simple_attachment_id }}' + wait: False + register: simple_attach + + - assert: + that: + - simple_attach is changed + + - name: '(CHECK_MODE) Delete an attachment - minimal parameters -- IDEMPOTENCY' + check_mode: True + ec2_transit_gateway_vpc_attachment: + state: absent + id: '{{ simple_attachment_id }}' + wait: False + register: simple_attach + + - assert: + that: + - simple_attach is not changed + + - name: 'Delete an attachment - minimal parameters -- IDEMPOTENCY' + ec2_transit_gateway_vpc_attachment: + state: absent + id: '{{ simple_attachment_id }}' + wait: False + register: simple_attach + + - assert: + that: + - simple_attach is not changed + + always: + - name: 'Delete attachment' + ec2_transit_gateway_vpc_attachment: + state: absent + id: '{{ simple_attachment_id }}' + wait: False + ignore_errors: True diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_egress_igw/aliases b/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_egress_igw/aliases new file mode 100644 index 000000000..4ef4b2067 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_egress_igw/aliases @@ -0,0 +1 @@ +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_egress_igw/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_egress_igw/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_egress_igw/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_egress_igw/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_egress_igw/tasks/main.yml new file mode 100644 index 000000000..41540b8d4 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_egress_igw/tasks/main.yml @@ -0,0 +1,104 @@ +--- +- name: 'ec2_vpc_egress_igw integration tests' + collections: + - amazon.aws + module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + block: + + # ============================================================ + - name: test failure with no parameters + ec2_vpc_egress_igw: + register: result + ignore_errors: true + + - name: assert failure with no parameters + assert: + that: + - 'result.failed' + - 'result.msg == "missing required arguments: vpc_id"' + + # ============================================================ + - name: test failure with non-existent VPC ID + ec2_vpc_egress_igw: + state: present + vpc_id: vpc-02394e50abc1807e8 + register: result + ignore_errors: true + + - name: assert failure with non-existent VPC ID + assert: + that: + - 'result.failed' + - 'result.error.code == "InvalidVpcID.NotFound"' + - '"invalid vpc ID" in result.msg' + + # ============================================================ + - name: create a VPC + ec2_vpc_net: + name: "{{ resource_prefix }}-vpc" + state: present + cidr_block: "10.232.232.128/26" + tags: + Name: "{{ resource_prefix }}-vpc" + Description: "Created by ansible-test" + register: vpc_result + + # ============================================================ + - name: create egress-only internet gateway (expected changed=true) + ec2_vpc_egress_igw: + state: present + vpc_id: "{{ vpc_result.vpc.id }}" + register: vpc_eigw_create + + - name: assert creation happened (expected changed=true) + assert: + that: + - 'vpc_eigw_create' + - 'vpc_eigw_create.gateway_id.startswith("eigw-")' + - 'vpc_eigw_create.vpc_id == vpc_result.vpc.id' + + # ============================================================ + - name: attempt to recreate egress-only internet gateway on VPC (expected changed=false) + ec2_vpc_egress_igw: + state: present + vpc_id: "{{ vpc_result.vpc.id }}" + register: vpc_eigw_recreate + + - name: assert recreation did nothing (expected changed=false) + assert: + that: + - 'vpc_eigw_recreate.changed == False' + - 'vpc_eigw_recreate.gateway_id == vpc_eigw_create.gateway_id' + - 'vpc_eigw_recreate.vpc_id == vpc_eigw_create.vpc_id' + + # ============================================================ + - name: test state=absent (expected changed=true) + ec2_vpc_egress_igw: + state: absent + vpc_id: "{{ vpc_result.vpc.id }}" + register: vpc_eigw_delete + + - name: assert state=absent (expected changed=true) + assert: + that: + - 'vpc_eigw_delete.changed' + + always: + # ============================================================ + - name: tidy up EIGW + ec2_vpc_egress_igw: + state: absent + vpc_id: "{{ vpc_result.vpc.id }}" + ignore_errors: true + + - name: tidy up VPC + ec2_vpc_net: + name: "{{ resource_prefix }}-vpc" + state: absent + cidr_block: "10.232.232.128/26" + ignore_errors: true diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_nacl/aliases b/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_nacl/aliases new file mode 100644 index 000000000..04109c268 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_nacl/aliases @@ -0,0 +1,3 @@ +cloud/aws + +ec2_vpc_nacl_info diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_nacl/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_nacl/defaults/main.yml new file mode 100644 index 000000000..5ac931209 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_nacl/defaults/main.yml @@ -0,0 +1,12 @@ +--- +vpc_name: '{{ resource_prefix }}-ec2-vpc-nacl' +nacl_name: '{{ resource_prefix }}-ec2-vpc-nacl' +subnet_name: '{{ resource_prefix }}-ec2-vpc-nacl' +vpc_cidr: '10.{{ 256 | random(seed=resource_prefix) }}.0.0/16' +subnet_1: '10.{{ 256 | random(seed=resource_prefix) }}.1.0/24' +subnet_2: '10.{{ 256 | random(seed=resource_prefix) }}.2.0/24' +subnet_3: '10.{{ 256 | random(seed=resource_prefix) }}.3.0/24' +subnet_4: '10.{{ 256 | random(seed=resource_prefix) }}.4.0/24' + +vpc_ipv6_cidr: '10.{{ 256 | random(seed=resource_prefix) }}.5.0/25' +vpc_ipv6_name: '{{ vpc_name }}-ipv6' diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_nacl/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_nacl/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_nacl/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_nacl/tasks/ingress_and_egress.yml b/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_nacl/tasks/ingress_and_egress.yml new file mode 100644 index 000000000..875e7f0b2 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_nacl/tasks/ingress_and_egress.yml @@ -0,0 +1,158 @@ +# ============================================================ +- block: + - name: create ingress and egress rules using subnet IDs + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ nacl_name }}" + subnets: "{{ subnet_ids }}" + tags: + Created_by: "Ansible test {{ resource_prefix }}" + ingress: + - [100, 'tcp', 'allow', '0.0.0.0/0', null, null, 22, 22] + - [200, 'tcp', 'allow', '0.0.0.0/0', null, null, 80, 80] + - [300, 'icmp', 'allow', '0.0.0.0/0', 0, 8] + egress: + - [100, 'all', 'allow', '0.0.0.0/0', null, null, null, null] + state: 'present' + register: nacl + + - name: assert the network acl was created + assert: + that: + - nacl.changed + - nacl.nacl_id.startswith('acl-') + + - name: get network ACL facts + ec2_vpc_nacl_info: + nacl_ids: + - "{{ nacl.nacl_id }}" + register: nacl_facts + + - name: assert the nacl has the correct attributes + assert: + that: + - nacl_facts.nacls | length == 1 + - nacl_facts.nacls[0].ingress | length == 3 + - nacl_facts.nacls[0].egress | length == 1 + + # ============================================================ + + - name: remove an ingress rule + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ nacl_name }}" + subnets: "{{ subnet_ids }}" + tags: + Created_by: "Ansible test {{ resource_prefix }}" + ingress: + - [100, 'tcp', 'allow', '0.0.0.0/0', null, null, 22, 22] + - [200, 'tcp', 'allow', '0.0.0.0/0', null, null, 80, 80] + egress: + - [100, 'all', 'allow', '0.0.0.0/0', null, null, null, null] + state: 'present' + register: nacl + + - name: assert the network acl changed + assert: + that: + - nacl.changed + - nacl.nacl_id.startswith('acl-') + + - name: get network ACL facts + ec2_vpc_nacl_info: + nacl_ids: + - "{{ nacl.nacl_id }}" + register: nacl_facts + + - name: assert the nacl has the correct attributes + assert: + that: + - nacl_facts.nacls | length == 1 + - nacl_facts.nacls[0].ingress | length == 2 + - nacl_facts.nacls[0].egress | length == 1 + + # ============================================================ + + - name: remove the egress rule + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ nacl_name }}" + subnets: "{{ subnet_ids }}" + tags: + Created_by: "Ansible test {{ resource_prefix }}" + ingress: + - [100, 'tcp', 'allow', '0.0.0.0/0', null, null, 22, 22] + - [200, 'tcp', 'allow', '0.0.0.0/0', null, null, 80, 80] + egress: [] + state: 'present' + register: nacl + + - name: assert the network acl changed + assert: + that: + - nacl.changed + - nacl.nacl_id.startswith('acl-') + + - name: get network ACL facts + ec2_vpc_nacl_info: + nacl_ids: + - "{{ nacl.nacl_id }}" + register: nacl_facts + + - name: assert the nacl has the correct attributes + assert: + that: + - nacl_facts.nacls | length == 1 + - nacl_facts.nacls[0].ingress | length == 2 + - nacl_facts.nacls[0].egress | length == 0 + + # ============================================================ + + - name: add egress rules + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ nacl_name }}" + subnets: "{{ subnet_ids }}" + tags: + Created_by: "Ansible test {{ resource_prefix }}" + ingress: + - [100, 'tcp', 'allow', '0.0.0.0/0', null, null, 22, 22] + - [200, 'tcp', 'allow', '0.0.0.0/0', null, null, 80, 80] + egress: + - [100, 'tcp', 'allow', '10.0.0.0/24', null, null, 22, 22] + - [200, 'udp', 'allow', '10.0.0.0/24', null, null, 22, 22] + state: 'present' + register: nacl + + - name: assert the network acl changed + assert: + that: + - nacl.changed + - nacl.nacl_id.startswith('acl-') + + - name: get network ACL facts + ec2_vpc_nacl_info: + nacl_ids: + - "{{ nacl.nacl_id }}" + register: nacl_facts + + - name: assert the nacl has the correct attributes + assert: + that: + - nacl_facts.nacls | length == 1 + - nacl_facts.nacls[0].ingress | length == 2 + - nacl_facts.nacls[0].egress | length == 2 + + # ============================================================ + + - name: remove the network ACL + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ nacl_name }}" + state: absent + register: nacl + + - name: assert nacl was removed + assert: + that: + - nacl.changed diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_nacl/tasks/ipv6.yml b/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_nacl/tasks/ipv6.yml new file mode 100644 index 000000000..136697161 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_nacl/tasks/ipv6.yml @@ -0,0 +1,124 @@ +- block: + + # ============================================================ + + - name: create ingress and egress rules using subnet names + ec2_vpc_nacl: + vpc_id: "{{ vpc_ipv6_id }}" + name: "{{ nacl_name }}" + subnets: + - "{{ subnet_name }}-ipv6" + tags: + Created_by: "Ansible test {{ resource_prefix }}" + ingress: + - [100, 'tcp', 'allow', '0.0.0.0/0', null, null, 22, 22] + - [200, 'tcp', 'allow', '0.0.0.0/0', null, null, 80, 80] + - [300, 'icmp', 'allow', '0.0.0.0/0', 0, 8] + egress: + - [100, 'all', 'allow', '0.0.0.0/0', null, null, null, null] + state: 'present' + register: nacl + - assert: + that: + - nacl.nacl_id + + - set_fact: + nacl_id: "{{ nacl.nacl_id }}" + + - name: add ipv6 entries + ec2_vpc_nacl: + vpc_id: "{{ vpc_ipv6_id }}" + name: "{{ nacl_name }}" + subnets: + - "{{ subnet_name }}-ipv6" + tags: + Created_by: "Ansible test {{ resource_prefix }}" + ingress: + - [100, 'tcp', 'allow', '0.0.0.0/0', null, null, 22, 22] + - [200, 'tcp', 'allow', '0.0.0.0/0', null, null, 80, 80] + - [205, 'tcp', 'allow', '::/0', null, null, 80, 80] + - [300, 'icmp', 'allow', '0.0.0.0/0', 0, 8] + - [305, 'ipv6-icmp', 'allow', '::/0', 0, 8] + egress: + - [100, 'all', 'allow', '0.0.0.0/0', null, null, null, null] + - [105, 'all', 'allow', '::/0', null, null, null, null] + state: 'present' + register: nacl + + - assert: + that: + - nacl.changed + - nacl.nacl_id == nacl_id + + - name: get network ACL facts (test that it works with ipv6 entries) + ec2_vpc_nacl_info: + nacl_ids: + - "{{ nacl_id }}" + register: nacl_facts + + - name: assert the nacl has the correct attributes + assert: + that: + - nacl_facts.nacls | length == 1 + - nacl_facts.nacls[0].ingress | length == 5 + - nacl_facts.nacls[0].egress | length == 2 + + - name: purge ingress entries + ec2_vpc_nacl: + vpc_id: "{{ vpc_ipv6_id }}" + name: "{{ nacl_name }}" + subnets: + - "{{ subnet_name }}-ipv6" + tags: + Created_by: "Ansible test {{ resource_prefix }}" + ingress: [] + egress: + - [100, 'all', 'allow', '0.0.0.0/0', null, null, null, null] + - [105, 'all', 'allow', '::/0', null, null, null, null] + state: 'present' + register: nacl + + - assert: + that: + - nacl.changed + - nacl.nacl_id == nacl_id + + - name: purge egress entries + ec2_vpc_nacl: + vpc_id: "{{ vpc_ipv6_id }}" + name: "{{ nacl_name }}" + subnets: + - "{{ subnet_name }}-ipv6" + tags: + Created_by: "Ansible test {{ resource_prefix }}" + ingress: [] + egress: [] + state: 'present' + register: nacl + + - assert: + that: + - nacl.changed + + - name: get network ACL facts (test that removed entries are gone) + ec2_vpc_nacl_info: + nacl_ids: + - "{{ nacl_id }}" + register: nacl_facts + + - name: assert the nacl has the correct attributes + assert: + that: + - nacl_facts.nacls | length == 1 + - nacl_facts.nacls[0].ingress | length == 0 + - nacl_facts.nacls[0].egress | length == 0 + + always: + + - name: remove network ACL + ec2_vpc_nacl: + vpc_id: "{{ vpc_ipv6_id }}" + name: "{{ nacl_name }}" + state: absent + register: removed_acl + ignore_errors: yes diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_nacl/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_nacl/tasks/main.yml new file mode 100644 index 000000000..e1538049a --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_nacl/tasks/main.yml @@ -0,0 +1,175 @@ +--- +- module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + collections: + - amazon.aws + block: + + # ============================================================ + + - name: test without any parameters + ec2_vpc_nacl: + register: result + ignore_errors: yes + + - name: assert required parameters + assert: + that: + - result.failed + - "result.msg == 'one of the following is required: name, nacl_id'" + + - name: get network ACL info without any parameters + ec2_vpc_nacl_info: + register: nacl_facts + + - name: assert we don't error + assert: + that: + - nacl_facts is succeeded + + - name: get network ACL info with invalid ID + ec2_vpc_nacl_info: + nacl_ids: + - 'acl-000000000000' + register: nacl_facts + ignore_errors: yes + + - name: assert message mentions missing ACLs + assert: + that: + - nacl_facts is failed + - '"does not exist" in nacl_facts.msg' + + # ============================================================ + + - name: fetch AZ availability + aws_az_info: + register: az_info + + - name: Assert that we have multiple AZs available to us + assert: + that: az_info.availability_zones | length >= 2 + + - name: pick AZs + set_fact: + az_one: '{{ az_info.availability_zones[0].zone_name }}' + az_two: '{{ az_info.availability_zones[1].zone_name }}' + + # ============================================================ + + - name: create a VPC + ec2_vpc_net: + cidr_block: "{{ vpc_cidr }}" + name: "{{ vpc_name }}" + state: present + register: vpc + + - name: Save VPC ID for later + set_fact: + vpc_id: "{{ vpc.vpc.id }}" + + - name: create subnets + ec2_vpc_subnet: + cidr: "{{ item.cidr }}" + az: "{{ item.az }}" + vpc_id: "{{ vpc_id }}" + state: present + tags: + Name: "{{ item.name }}" + with_items: + - cidr: "{{ subnet_1 }}" + az: "{{ az_one }}" + name: "{{ subnet_name }}-1" + - cidr: "{{ subnet_2 }}" + az: "{{ az_two }}" + name: "{{ subnet_name }}-2" + - cidr: "{{ subnet_3 }}" + az: "{{ az_one }}" + name: "{{ subnet_name }}-3" + - cidr: "{{ subnet_4 }}" + az: "{{ az_two }}" + name: "{{ subnet_name }}-4" + register: subnets + + - name: set helpful facts about subnets + set_fact: + subnet_ids: "{{ subnets | community.general.json_query('results[*].subnet.id') }}" + subnet_names: "{{ subnets | community.general.json_query('results[*].subnet.tags.Name') }}" + + - name: create VPC for IPv6 tests + ec2_vpc_net: + cidr_block: "{{ vpc_ipv6_cidr }}" + name: "{{ vpc_ipv6_name }}" + state: present + ipv6_cidr: yes + register: vpc_result + - set_fact: + vpc_ipv6_id: "{{ vpc_result.vpc.id }}" + vpc_ipv6_cidr_v6: "{{ _ipv6_cidr }}" + subnet_ipv6: "{{ _ipv6_cidr | regex_replace('::/56', '::/64') }}" + vars: + _ipv6_cidr: "{{ vpc_result.vpc.ipv6_cidr_block_association_set[0].ipv6_cidr_block }}" + + - name: create subnet with IPv6 + ec2_vpc_subnet: + cidr: "{{ vpc_ipv6_cidr }}" + vpc_id: "{{ vpc_ipv6_id }}" + ipv6_cidr: "{{ subnet_ipv6 }}" + state: present + tags: + Name: "{{ subnet_name }}-ipv6" + + # ============================================================ + + - include_tasks: tasks/subnet_ids.yml + + - include_tasks: tasks/subnet_names.yml + + - include_tasks: tasks/tags.yml + + - include_tasks: tasks/ingress_and_egress.yml + + - include_tasks: tasks/ipv6.yml + + # ============================================================ + + always: + + - name: remove network ACL + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ nacl_name }}" + state: absent + register: removed_acl + ignore_errors: yes + + - name: remove subnets + ec2_vpc_subnet: + cidr: "{{ item.cidr }}" + vpc_id: "{{ item.vpc_id | default(vpc_id) }}" + state: absent + with_items: + - cidr: "{{ subnet_1 }}" + - cidr: "{{ subnet_2 }}" + - cidr: "{{ subnet_3 }}" + - cidr: "{{ subnet_4 }}" + - cidr: "{{ vpc_ipv6_cidr }}" + vpc_id: "{{ vpc_ipv6_id }}" + ignore_errors: yes + register: removed_subnets + + - name: remove the VPCs + ec2_vpc_net: + vpc_id: "{{ item }}" + state: absent + ignore_errors: yes + register: removed_vpc + with_items: + - '{{ vpc_id }}' + - '{{ vpc_ipv6_id }}' + + # ============================================================ diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_nacl/tasks/subnet_ids.yml b/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_nacl/tasks/subnet_ids.yml new file mode 100644 index 000000000..4e1affa1f --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_nacl/tasks/subnet_ids.yml @@ -0,0 +1,161 @@ +# ============================================================ + +- name: create ingress and egress rules using subnet IDs + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ nacl_name }}" + subnets: "{{ subnet_ids }}" + tags: + Created_by: "Ansible test {{ resource_prefix }}" + ingress: + - [100, 'tcp', 'allow', '0.0.0.0/0', null, null, 22, 22] + - [200, 'tcp', 'allow', '0.0.0.0/0', null, null, 80, 80] + - [300, 'icmp', 'allow', '0.0.0.0/0', 0, 8] + egress: + - [100, 'all', 'allow', '0.0.0.0/0', null, null, null, null] + state: 'present' + register: nacl + +- set_fact: + nacl_id: "{{ nacl.nacl_id }}" + +- name: assert the network acl was created + assert: + that: + - nacl.changed + - nacl.nacl_id.startswith('acl-') + +- name: get network ACL facts + ec2_vpc_nacl_info: + nacl_ids: + - "{{ nacl_id }}" + register: nacl_facts + +- name: assert the nacl has the correct attributes + assert: + that: + - nacl_facts.nacls | length == 1 + - nacl_facts.nacls[0].nacl_id == nacl_id + - nacl_facts.nacls[0].subnets | length == 4 + - nacl_facts.nacls[0].subnets | sort == subnet_ids | sort + - nacl_facts.nacls[0].ingress | length == 3 + - nacl_facts.nacls[0].egress | length == 1 + - nacl_facts.nacls[0].tags.Name == nacl_name + +# ============================================================ + +- name: test idempotence + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ nacl_name }}" + subnets: "{{ subnet_ids }}" + tags: + Created_by: "Ansible test {{ resource_prefix }}" + ingress: + - [100, 'tcp', 'allow', '0.0.0.0/0', null, null, 22, 22] + - [200, 'tcp', 'allow', '0.0.0.0/0', null, null, 80, 80] + - [300, 'icmp', 'allow', '0.0.0.0/0', 0, 8] + egress: + - [100, 'all', 'allow', '0.0.0.0/0', null, null, null, null] + state: 'present' + register: nacl + +- name: assert the network acl already existed + assert: + that: + - not nacl.changed + - nacl.nacl_id == nacl_id + - nacl.nacl_id.startswith('acl-') + +- name: get network ACL facts + ec2_vpc_nacl_info: + nacl_ids: + - "{{ nacl.nacl_id }}" + register: nacl_facts_idem + +- name: assert the facts are the same as before + assert: + that: + - nacl_facts_idem == nacl_facts + +# ============================================================ + +- name: remove a subnet from the network ACL + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ nacl_name }}" + subnets: + - "{{ subnet_ids[0] }}" + - "{{ subnet_ids[1] }}" + - "{{ subnet_ids[2] }}" + tags: + Created_by: "Ansible test {{ resource_prefix }}" + ingress: + - [100, 'tcp', 'allow', '0.0.0.0/0', null, null, 22, 22] + - [200, 'tcp', 'allow', '0.0.0.0/0', null, null, 80, 80] + - [300, 'icmp', 'allow', '0.0.0.0/0', 0, 8] + egress: + - [100, 'all', 'allow', '0.0.0.0/0', null, null, null, null] + state: 'present' + register: nacl + +- name: assert the network ACL changed + assert: + that: + - nacl.changed + - nacl.nacl_id.startswith('acl-') + - nacl.nacl_id == nacl_id + +- name: get network ACL facts + ec2_vpc_nacl_info: + nacl_id: + - "{{ nacl.nacl_id }}" + register: nacl_facts + +- name: assert the nacl has the correct attributes + assert: + that: + - nacl_facts.nacls | length == 1 + - nacl_facts.nacls[0].nacl_id == nacl_id + - nacl_facts.nacls[0].subnets | length == 3 + - subnet_ids[3] not in nacl_facts.nacls[0].subnets + - nacl_facts.nacls[0].ingress | length == 3 + - nacl_facts.nacls[0].egress | length == 1 + - nacl_facts.nacls[0].tags.Name == nacl_name + +# ============================================================ + +- name: remove the network ACL + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ nacl_name }}" + state: absent + register: nacl + +- name: assert nacl was removed + assert: + that: + - nacl.changed + +- name: re-remove the network ACL by name (test idempotency) + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ nacl_name }}" + state: absent + register: nacl +- name: assert nacl was removed + assert: + that: + - nacl is not changed + +- name: re-remove the network ACL by id (test idempotency) + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + nacl_id: "{{ nacl_id }}" + state: absent + register: nacl + +- name: assert nacl was removed + assert: + that: + - nacl is not changed diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_nacl/tasks/subnet_names.yml b/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_nacl/tasks/subnet_names.yml new file mode 100644 index 000000000..4db7e1b20 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_nacl/tasks/subnet_names.yml @@ -0,0 +1,136 @@ +# ============================================================ + +- name: create ingress and egress rules using subnet names + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ nacl_name }}" + subnets: "{{ subnet_names }}" + tags: + Created_by: "Ansible test {{ resource_prefix }}" + ingress: + - [100, 'tcp', 'allow', '0.0.0.0/0', null, null, 22, 22] + - [200, 'tcp', 'allow', '0.0.0.0/0', null, null, 80, 80] + - [300, 'icmp', 'allow', '0.0.0.0/0', 0, 8] + egress: + - [100, 'all', 'allow', '0.0.0.0/0', null, null, null, null] + state: 'present' + register: nacl + +- set_fact: + nacl_id: "{{ nacl.nacl_id }}" + +- name: assert the network acl was created + assert: + that: + - nacl.changed + - nacl.nacl_id.startswith('acl-') + +- name: get network ACL facts + ec2_vpc_nacl_info: + nacl_ids: + - "{{ nacl_id }}" + register: nacl_facts + +- name: assert the nacl has the correct attributes + assert: + that: + - nacl_facts.nacls | length == 1 + - nacl_facts.nacls[0].nacl_id == nacl_id + - nacl_facts.nacls[0].subnets | length == 4 + - nacl_facts.nacls[0].ingress | length == 3 + - nacl_facts.nacls[0].egress | length == 1 + - nacl_facts.nacls[0].tags.Name == nacl_name + +# ============================================================ + +- name: test idempotence + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ nacl_name }}" + subnets: "{{ subnet_names }}" + tags: + Created_by: "Ansible test {{ resource_prefix }}" + ingress: + - [100, 'tcp', 'allow', '0.0.0.0/0', null, null, 22, 22] + - [200, 'tcp', 'allow', '0.0.0.0/0', null, null, 80, 80] + - [300, 'icmp', 'allow', '0.0.0.0/0', 0, 8] + egress: + - [100, 'all', 'allow', '0.0.0.0/0', null, null, null, null] + state: 'present' + register: nacl + +- name: assert the network acl already existed + assert: + that: + - not nacl.changed + - nacl.nacl_id == nacl_id + - nacl.nacl_id.startswith('acl-') + +- name: get network ACL facts + ec2_vpc_nacl_info: + nacl_ids: + - "{{ nacl.nacl_id }}" + register: nacl_facts_idem + +- name: assert the facts are the same as before + assert: + that: + - nacl_facts_idem == nacl_facts + +# ============================================================ + +- name: remove a subnet from the network ACL + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ nacl_name }}" + subnets: + - "{{ subnet_names[0] }}" + - "{{ subnet_names[1] }}" + - "{{ subnet_names[2] }}" + tags: + Created_by: "Ansible test {{ resource_prefix }}" + ingress: + - [100, 'tcp', 'allow', '0.0.0.0/0', null, null, 22, 22] + - [200, 'tcp', 'allow', '0.0.0.0/0', null, null, 80, 80] + - [300, 'icmp', 'allow', '0.0.0.0/0', 0, 8] + egress: + - [100, 'all', 'allow', '0.0.0.0/0', null, null, null, null] + state: 'present' + register: nacl + +- name: assert the network ACL changed + assert: + that: + - nacl.changed + - nacl.nacl_id == nacl_id + - nacl.nacl_id.startswith('acl-') + +- name: get network ACL facts + ec2_vpc_nacl_info: + nacl_ids: + - "{{ nacl.nacl_id }}" + register: nacl_facts + +- name: assert the nacl has the correct attributes + assert: + that: + - nacl_facts.nacls | length == 1 + - nacl_facts.nacls[0].nacl_id == nacl_id + - nacl_facts.nacls[0].subnets | length == 3 + - nacl_facts.nacls[0].ingress | length == 3 + - nacl_facts.nacls[0].egress | length == 1 + - nacl_facts.nacls[0].tags.Name == nacl_name + +# ============================================================ + +- name: remove the network ACL + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ nacl_name }}" + state: absent + register: nacl + +- name: assert nacl was removed + assert: + that: + - nacl.changed diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_nacl/tasks/tags.yml b/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_nacl/tasks/tags.yml new file mode 100644 index 000000000..da3ad71dd --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_nacl/tasks/tags.yml @@ -0,0 +1,445 @@ +- vars: + first_tags: + 'Key with Spaces': Value with spaces + CamelCaseKey: CamelCaseValue + pascalCaseKey: pascalCaseValue + snake_case_key: snake_case_value + second_tags: + 'New Key with Spaces': Value with spaces + NewCamelCaseKey: CamelCaseValue + newPascalCaseKey: pascalCaseValue + new_snake_case_key: snake_case_value + third_tags: + 'Key with Spaces': Value with spaces + CamelCaseKey: CamelCaseValue + pascalCaseKey: pascalCaseValue + snake_case_key: snake_case_value + 'New Key with Spaces': Updated Value with spaces + final_tags: + 'Key with Spaces': Value with spaces + CamelCaseKey: CamelCaseValue + pascalCaseKey: pascalCaseValue + snake_case_key: snake_case_value + 'New Key with Spaces': Updated Value with spaces + NewCamelCaseKey: CamelCaseValue + newPascalCaseKey: pascalCaseValue + new_snake_case_key: snake_case_value + name_tags: + Name: '{{ nacl_name }}' + block: + + # ============================================================ + + - name: create a network ACL using subnet IDs + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ nacl_name }}" + subnets: "{{ subnet_ids }}" + state: 'present' + register: nacl + + - name: assert the network acl was created + assert: + that: + - nacl.changed + - nacl.nacl_id.startswith('acl-') + + - name: Store NACL ID + set_fact: + nacl_id: '{{ nacl.nacl_id }}' + + - name: get network ACL facts + ec2_vpc_nacl_info: + nacl_ids: + - "{{ nacl_id }}" + register: nacl_info + + - name: assert the nacl has the correct attributes + assert: + that: + - nacl_info.nacls[0].nacl_id == nacl_id + - nacl_info.nacls[0].tags == name_tags + + # ============================================================ + + - name: (check) add tags + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ nacl_name }}" + subnets: "{{ subnet_ids }}" + tags: "{{ first_tags }}" + state: 'present' + register: nacl + check_mode: True + + - name: assert would change + assert: + that: + - nacl is changed + - nacl.nacl_id == nacl_id + + - name: add tags + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ nacl_name }}" + subnets: "{{ subnet_ids }}" + tags: "{{ first_tags }}" + state: 'present' + register: nacl + - name: get network ACL facts + ec2_vpc_nacl_info: + nacl_ids: + - "{{ nacl.nacl_id }}" + register: nacl_info + + - name: verify the tags were added + assert: + that: + - nacl is changed + - nacl.nacl_id == nacl_id + - nacl_info.nacls[0].nacl_id == nacl_id + - nacl_info.nacls[0].tags == ( first_tags | combine(name_tags) ) + + - name: (check) add tags - IDEMPOTENCY + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ nacl_name }}" + subnets: "{{ subnet_ids }}" + tags: "{{ first_tags }}" + state: 'present' + register: nacl + check_mode: True + + - name: assert would not change + assert: + that: + - nacl is not changed + - nacl.nacl_id == nacl_id + + - name: add tags - IDEMPOTENCY + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ nacl_name }}" + subnets: "{{ subnet_ids }}" + tags: "{{ first_tags }}" + state: 'present' + register: nacl + - name: get network ACL facts + ec2_vpc_nacl_info: + nacl_ids: + - "{{ nacl.nacl_id }}" + register: nacl_info + + - name: verify no change + assert: + that: + - nacl is not changed + - nacl.nacl_id == nacl_id + - nacl_info.nacls[0].nacl_id == nacl_id + - nacl_info.nacls[0].tags == ( first_tags | combine(name_tags) ) + + # ============================================================ + + - name: get network ACL facts by filter + ec2_vpc_nacl_info: + filters: + "tag:Name": "{{ nacl_name }}" + register: nacl_info + + - name: assert the facts are the same as before + assert: + that: + - nacl_info.nacls | length == 1 + - nacl.nacl_id == nacl_id + - nacl_info.nacls[0].nacl_id == nacl_id + + # ============================================================ + + - name: (check) modify tags with purge + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ nacl_name }}" + subnets: "{{ subnet_ids }}" + tags: "{{ second_tags }}" + state: 'present' + register: nacl + check_mode: True + + - name: assert would change + assert: + that: + - nacl is changed + - nacl.nacl_id == nacl_id + + - name: modify tags with purge + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ nacl_name }}" + subnets: "{{ subnet_ids }}" + tags: "{{ second_tags }}" + state: 'present' + register: nacl + - name: get network ACL facts + ec2_vpc_nacl_info: + nacl_ids: + - "{{ nacl.nacl_id }}" + register: nacl_info + + - name: verify the tags were added + assert: + that: + - nacl is changed + - nacl.nacl_id == nacl_id + - nacl_info.nacls[0].nacl_id == nacl_id + - nacl_info.nacls[0].tags == ( second_tags | combine(name_tags) ) + + - name: (check) modify tags with purge - IDEMPOTENCY + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ nacl_name }}" + subnets: "{{ subnet_ids }}" + tags: "{{ second_tags }}" + state: 'present' + register: nacl + check_mode: True + + - name: assert would not change + assert: + that: + - nacl is not changed + - nacl.nacl_id == nacl_id + + - name: modify tags with purge - IDEMPOTENCY + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ nacl_name }}" + subnets: "{{ subnet_ids }}" + tags: "{{ second_tags }}" + state: 'present' + register: nacl + - name: get network ACL facts + ec2_vpc_nacl_info: + nacl_ids: + - "{{ nacl.nacl_id }}" + register: nacl_info + + - name: verify no change + assert: + that: + - nacl is not changed + - nacl.nacl_id == nacl_id + - nacl_info.nacls[0].nacl_id == nacl_id + - nacl_info.nacls[0].tags == ( second_tags | combine(name_tags) ) + + # ============================================================ + + - name: (check) modify tags without purge + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ nacl_name }}" + subnets: "{{ subnet_ids }}" + tags: "{{ third_tags }}" + state: 'present' + purge_tags: False + register: nacl + check_mode: True + + - name: assert would change + assert: + that: + - nacl is changed + - nacl.nacl_id == nacl_id + + - name: modify tags without purge + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ nacl_name }}" + subnets: "{{ subnet_ids }}" + tags: "{{ third_tags }}" + state: 'present' + purge_tags: False + register: nacl + - name: get network ACL facts + ec2_vpc_nacl_info: + nacl_ids: + - "{{ nacl.nacl_id }}" + register: nacl_info + + - name: verify the tags were added + assert: + that: + - nacl is changed + - nacl.nacl_id == nacl_id + - nacl_info.nacls[0].nacl_id == nacl_id + - nacl_info.nacls[0].tags == ( final_tags | combine(name_tags) ) + + - name: (check) modify tags without purge - IDEMPOTENCY + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ nacl_name }}" + subnets: "{{ subnet_ids }}" + tags: "{{ third_tags }}" + state: 'present' + purge_tags: False + register: nacl + check_mode: True + + - name: assert would not change + assert: + that: + - nacl is not changed + - nacl.nacl_id == nacl_id + + - name: modify tags without purge - IDEMPOTENCY + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ nacl_name }}" + subnets: "{{ subnet_ids }}" + tags: "{{ third_tags }}" + state: 'present' + purge_tags: False + register: nacl + - name: get network ACL facts + ec2_vpc_nacl_info: + nacl_ids: + - "{{ nacl.nacl_id }}" + register: nacl_info + + - name: verify no change + assert: + that: + - nacl is not changed + - nacl.nacl_id == nacl_id + - nacl_info.nacls[0].nacl_id == nacl_id + - nacl_info.nacls[0].tags == ( final_tags | combine(name_tags) ) + + # ============================================================ + + - name: (check) No change to tags without setting tags + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ nacl_name }}" + subnets: "{{ subnet_ids }}" + state: 'present' + register: nacl + check_mode: True + + - name: assert would change + assert: + that: + - nacl is not changed + - nacl.nacl_id == nacl_id + + - name: No change to tags without setting tags + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ nacl_name }}" + subnets: "{{ subnet_ids }}" + state: 'present' + register: nacl + - name: get network ACL facts + ec2_vpc_nacl_info: + nacl_ids: + - "{{ nacl.nacl_id }}" + register: nacl_info + + - name: verify the tags were added + assert: + that: + - nacl is not changed + - nacl.nacl_id == nacl_id + - nacl_info.nacls[0].nacl_id == nacl_id + - nacl_info.nacls[0].tags == ( final_tags | combine(name_tags) ) + + # ============================================================ + + - name: (check) remove non name tags + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ nacl_name }}" + subnets: "{{ subnet_ids }}" + tags: {} + state: 'present' + register: nacl + check_mode: True + + - name: assert would change + assert: + that: + - nacl is changed + - nacl.nacl_id == nacl_id + + - name: remove non name tags + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ nacl_name }}" + subnets: "{{ subnet_ids }}" + tags: {} + state: 'present' + register: nacl + - name: get network ACL facts + ec2_vpc_nacl_info: + nacl_ids: + - "{{ nacl.nacl_id }}" + register: nacl_info + + - name: verify the tags were added + assert: + that: + - nacl is changed + - nacl.nacl_id == nacl_id + - nacl_info.nacls[0].nacl_id == nacl_id + - nacl_info.nacls[0].tags == name_tags + + - name: (check) remove non name tags - IDEMPOTENCY + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ nacl_name }}" + subnets: "{{ subnet_ids }}" + tags: {} + state: 'present' + register: nacl + check_mode: True + + - name: assert would not change + assert: + that: + - nacl is not changed + - nacl.nacl_id == nacl_id + + - name: remove non name tags - IDEMPOTENCY + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ nacl_name }}" + subnets: "{{ subnet_ids }}" + tags: {} + state: 'present' + register: nacl + - name: get network ACL facts + ec2_vpc_nacl_info: + nacl_ids: + - "{{ nacl.nacl_id }}" + register: nacl_info + + - name: verify no change + assert: + that: + - nacl is not changed + - nacl.nacl_id == nacl_id + - nacl_info.nacls[0].nacl_id == nacl_id + - nacl_info.nacls[0].tags == name_tags + + # ============================================================ + + always: + - name: remove the network ACL + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ nacl_name }}" + state: absent + register: nacl + + - name: assert nacl was removed + assert: + that: + - nacl.changed diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_peer/aliases b/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_peer/aliases new file mode 100644 index 000000000..8807cb251 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_peer/aliases @@ -0,0 +1,3 @@ +cloud/aws + +ec2_vpc_peering_info diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_peer/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_peer/defaults/main.yml new file mode 100644 index 000000000..0ff34455b --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_peer/defaults/main.yml @@ -0,0 +1,6 @@ +--- +vpc_seed: '{{ resource_prefix }}' +vpc_1_name: '{{ resource_prefix }}-vpc-1' +vpc_1_cidr: '10.{{ 256 | random(seed=vpc_seed) }}.0.0/23' +vpc_2_name: '{{ resource_prefix }}-vpc-1' +vpc_2_cidr: '10.{{ 256 | random(seed=vpc_seed) }}.2.0/23' diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_peer/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_peer/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_peer/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_peer/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_peer/tasks/main.yml new file mode 100644 index 000000000..cdb7c6680 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_peer/tasks/main.yml @@ -0,0 +1,514 @@ +--- +- name: ec2_vpc_igw tests + collections: + - amazon.aws + module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + block: + - name: get ARN of calling user + aws_caller_info: + register: aws_caller_info + - name: Store Account ID for later use + set_fact: + account_id: '{{ aws_caller_info.account }}' + + # ============================================================ + - name: Fetch Peers in check_mode + ec2_vpc_peering_info: + register: peers_info + check_mode: True + - name: Assert success + assert: + that: + - peers_info is successful + - '"result" in peers_info' + + # ============================================================ + - name: create VPC 1 + ec2_vpc_net: + name: "{{ vpc_1_name }}" + state: present + cidr_block: "{{ vpc_1_cidr }}" + tags: + Name: "{{ vpc_1_name }}" + TestPrefex: "{{ resource_prefix }}" + register: vpc_1_result + - name: Assert success + assert: + that: + - vpc_1_result is successful + + - name: create VPC 2 + ec2_vpc_net: + name: "{{ vpc_2_name }}" + state: present + cidr_block: "{{ vpc_2_cidr }}" + tags: + Name: "{{ vpc_2_name }}" + TestPrefex: "{{ resource_prefix }}" + register: vpc_2_result + - name: Assert success + assert: + that: + - vpc_2_result is successful + + - name: Store VPC IDs + set_fact: + vpc_1: '{{ vpc_1_result.vpc.id }}' + vpc_2: '{{ vpc_2_result.vpc.id }}' + + - name: Set a name to use with the connections + set_fact: + connection_name: 'Peering connection for VPC {{ vpc_1 }} to VPC {{ vpc_2 }}' + + - name: Create local account VPC peering Connection request + ec2_vpc_peer: + vpc_id: '{{ vpc_1 }}' + peer_vpc_id: '{{ vpc_2 }}' + state: present + tags: + Name: '{{ connection_name }}' + register: vpc_peer + + - name: Assert success + assert: + that: + - vpc_peer is changed + - vpc_peer is successful + - "'peering_id' in vpc_peer" + - vpc_peer.vpc_peering_connection.requester_vpc_info.cidr_block == vpc_1_cidr + - vpc_peer.peering_id.startswith('pcx-') + + - name: Store Connection ID + set_fact: + peer_id_1: '{{ vpc_peer.peering_id }}' + + - name: (re-) Create local account VPC peering Connection request (idempotency) + ec2_vpc_peer: + vpc_id: '{{ vpc_1 }}' + peer_vpc_id: '{{ vpc_2 }}' + state: present + tags: + Name: '{{ connection_name }}' + register: vpc_peer + + - name: Assert success + assert: + that: + - vpc_peer is not changed + - vpc_peer is successful + - vpc_peer.peering_id == peer_id_1 + + - name: (re-) Create local account VPC peering Connection request with accepter/requester reversed (idempotency) + ec2_vpc_peer: + vpc_id: '{{ vpc_2 }}' + peer_vpc_id: '{{ vpc_1 }}' + state: present + tags: + Name: '{{ connection_name }}' + register: vpc_peer + + - name: Assert success + assert: + that: + - vpc_peer is not changed + - vpc_peer is successful + - vpc_peer.peering_id == peer_id_1 + + - name: Get details on specific VPC peer + ec2_vpc_peering_info: + peer_connection_ids: + - '{{ peer_id_1 }}' + register: peer_info + - name: Assert expected values + assert: + that: + - peer_info is successful + - "'vpc_peering_connections' in peer_info" + - "'result' in peer_info" + - "'accepter_vpc_info' in peer_details" + - "'requester_vpc_info' in peer_details" + - "'status' in peer_details" + - "'code' in peer_details.status" + - peer_details.status.code == "pending-acceptance" + - "'message' in peer_details.status" + - "'tags' in peer_details" + - "'Name' in peer_details.tags" + - peer_details.tags.Name == connection_name + - "'vpc_peering_connection_id' in peer_details" + - peer_details.vpc_peering_connection_id == peer_id_1 + # Acceptor info isn't available until the connection has been accepted + - "'cidr_block' not in acceptor_details" + - "'cidr_block_set' not in acceptor_details" + - "'peering_options' not in acceptor_details" + - "'owner_id' in acceptor_details" + - acceptor_details.owner_id == account_id + - "'region' in acceptor_details" + - acceptor_details.region == aws_region + - "'vpc_id' in acceptor_details" + - acceptor_details.vpc_id == vpc_2 + # Information about the 'requesting' VPC + - "'cidr_block' in requester_details" + - requester_details.cidr_block == vpc_1_cidr + - "'cidr_block_set' in requester_details" + - requester_details.cidr_block_set | length == 1 + - "'cidr_block' in requester_details.cidr_block_set[0]" + - requester_details.cidr_block_set[0].cidr_block == vpc_1_cidr + - "'peering_options' in requester_details" + - "'owner_id' in requester_details" + - requester_details.owner_id == account_id + - "'region' in requester_details" + - requester_details.region == aws_region + - "'vpc_id' in requester_details" + - requester_details.vpc_id == vpc_1 + vars: + peer_details: '{{ peer_info.vpc_peering_connections[0] }}' + acceptor_details: '{{ peer_details["accepter_vpc_info"] }}' + requester_details: '{{ peer_details["requester_vpc_info"] }}' + + - name: Get all vpc peers with specific filters + ec2_vpc_peering_info: + filters: + status-code: ['pending-acceptance'] + register: pending_vpc_peers + - name: Assert expected values + assert: + that: + # Not guaranteed to just be us, only assert the shape + - pending_vpc_peers is successful + - "'vpc_peering_connections' in peer_info" + - "'result' in peer_info" + - "'accepter_vpc_info' in peer_details" + - "'requester_vpc_info' in peer_details" + - "'status' in peer_details" + - "'code' in peer_details.status" + - peer_details.status.code == "pending-acceptance" + - "'message' in peer_details.status" + - "'tags' in peer_details" + - "'vpc_peering_connection_id' in peer_details" + # Acceptor info isn't available until the connection has been accepted + - "'cidr_block' not in acceptor_details" + - "'cidr_block_set' not in acceptor_details" + - "'peering_options' not in acceptor_details" + - "'owner_id' in acceptor_details" + - "'region' in acceptor_details" + - "'vpc_id' in acceptor_details" + # Information about the 'requesting' VPC + - "'cidr_block' in requester_details" + - "'cidr_block_set' in requester_details" + - "'cidr_block' in requester_details.cidr_block_set[0]" + - "'peering_options' in requester_details" + - "'owner_id' in requester_details" + - "'region' in requester_details" + - "'vpc_id' in requester_details" + vars: + peer_details: '{{ pending_vpc_peers.vpc_peering_connections[0] }}' + acceptor_details: '{{ peer_details["accepter_vpc_info"] }}' + requester_details: '{{ peer_details["requester_vpc_info"] }}' + + - name: Update tags on the VPC Peering Connection + ec2_vpc_peer: + vpc_id: '{{ vpc_1 }}' + peer_vpc_id: '{{ vpc_2 }}' + state: present + tags: + Name: '{{ connection_name }}' + testPrefix: '{{ resource_prefix }}' + register: tag_peer + - name: Assert success + assert: + that: + - tag_peer is changed + - tag_peer is successful + - tag_peer.peering_id == peer_id_1 + + - name: (re-) Update tags on the VPC Peering Connection (idempotency) + ec2_vpc_peer: + vpc_id: '{{ vpc_1 }}' + peer_vpc_id: '{{ vpc_2 }}' + state: present + tags: + Name: '{{ connection_name }}' + testPrefix: '{{ resource_prefix }}' + register: tag_peer + - name: Assert success + assert: + that: + - tag_peer is not changed + - tag_peer is successful + - tag_peer.peering_id == peer_id_1 + + - name: Get details on specific VPC peer + ec2_vpc_peering_info: + peer_connection_ids: + - '{{ peer_id_1 }}' + register: peer_info + - name: Assert expected tags + assert: + that: + - peer_info is successful + - "'tags' in peer_details" + - "'Name' in peer_details.tags" + - "'testPrefix' in peer_details.tags" + - peer_details.tags.Name == connection_name + - peer_details.tags.testPrefix == resource_prefix + vars: + peer_details: '{{ peer_info.vpc_peering_connections[0] }}' + + - name: Accept local VPC peering request + ec2_vpc_peer: + peering_id: "{{ vpc_peer.peering_id }}" + state: accept + wait: True + register: action_peer + - name: Assert success + assert: + that: + - action_peer is changed + - action_peer is successful + - action_peer.peering_id == peer_id_1 + - action_peer.vpc_peering_connection.accepter_vpc_info.cidr_block == vpc_2_cidr + - action_peer.vpc_peering_connection.vpc_peering_connection_id == peer_id_1 + + - name: Get details on specific VPC peer + ec2_vpc_peering_info: + peer_connection_ids: + - '{{ peer_id_1 }}' + register: peer_info + - name: Assert expected values + assert: + that: + - peer_info is successful + - "'vpc_peering_connections' in peer_info" + - "'result' in peer_info" + - "'accepter_vpc_info' in peer_details" + - "'requester_vpc_info' in peer_details" + - "'status' in peer_details" + - "'code' in peer_details.status" + - peer_details.status.code == "active" + - "'message' in peer_details.status" + - "'tags' in peer_details" + - "'Name' in peer_details.tags" + - peer_details.tags.Name == connection_name + - "'testPrefix' in peer_details.tags" + - peer_details.tags.testPrefix == resource_prefix + - "'vpc_peering_connection_id' in peer_details" + - peer_details.vpc_peering_connection_id == peer_id_1 + # Information about the 'accepting' VPC should be available now + - "'cidr_block' in acceptor_details" + - acceptor_details.cidr_block == vpc_2_cidr + - "'cidr_block_set' in acceptor_details" + - acceptor_details.cidr_block_set | length == 1 + - "'cidr_block' in acceptor_details.cidr_block_set[0]" + - acceptor_details.cidr_block_set[0].cidr_block == vpc_2_cidr + - "'peering_options' in acceptor_details" + - "'owner_id' in acceptor_details" + - acceptor_details.owner_id == account_id + - "'region' in acceptor_details" + - acceptor_details.region == aws_region + - "'vpc_id' in acceptor_details" + - acceptor_details.vpc_id == vpc_2 + # Information about the 'requesting' VPC + - "'cidr_block' in requester_details" + - requester_details.cidr_block == vpc_1_cidr + - "'cidr_block_set' in requester_details" + - requester_details.cidr_block_set | length == 1 + - "'cidr_block' in requester_details.cidr_block_set[0]" + - requester_details.cidr_block_set[0].cidr_block == vpc_1_cidr + - "'peering_options' in requester_details" + - "'owner_id' in requester_details" + - requester_details.owner_id == account_id + - "'region' in requester_details" + - requester_details.region == aws_region + - "'vpc_id' in requester_details" + - requester_details.vpc_id == vpc_1 + vars: + peer_details: '{{ peer_info.vpc_peering_connections[0] }}' + acceptor_details: '{{ peer_details["accepter_vpc_info"] }}' + requester_details: '{{ peer_details["requester_vpc_info"] }}' + + - name: (re-) Accept local VPC peering request (idempotency) + ec2_vpc_peer: + peering_id: "{{ vpc_peer.peering_id }}" + state: accept + register: action_peer + - name: Assert success + assert: + that: + - action_peer is not changed + - action_peer is successful + - action_peer.peering_id == peer_id_1 + - action_peer.vpc_peering_connection.vpc_peering_connection_id == peer_id_1 + + - name: delete a local VPC peering Connection + ec2_vpc_peer: + peering_id: "{{ vpc_peer.peering_id }}" + state: absent + register: delete_peer + - name: Assert success + assert: + that: + - delete_peer is changed + - delete_peer is successful + - "'peering_id' in delete_peer" + + - name: Get details on specific VPC peer + ec2_vpc_peering_info: + peer_connection_ids: + - '{{ peer_id_1}}' + register: peer_info + - name: Assert expected values + assert: + that: + - peer_info is successful + - "'vpc_peering_connections' in peer_info" + - "'result' in peer_info" + - "'accepter_vpc_info' in peer_details" + - "'requester_vpc_info' in peer_details" + - "'status' in peer_details" + - "'code' in peer_details.status" + - peer_details.status.code == "deleted" + - "'message' in peer_details.status" + - "'tags' in peer_details" + - "'Name' in peer_details.tags" + - peer_details.tags.Name == connection_name + - "'testPrefix' in peer_details.tags" + - peer_details.tags.testPrefix == resource_prefix + - "'vpc_peering_connection_id' in peer_details" + - peer_details.vpc_peering_connection_id == peer_id_1 + # Information about the 'accepting' VPC is reduced again + - "'cidr_block' not in acceptor_details" + - "'cidr_block_set' not in acceptor_details" + - "'peering_options' not in acceptor_details" + - "'owner_id' in acceptor_details" + - acceptor_details.owner_id == account_id + - "'region' in acceptor_details" + - acceptor_details.region == aws_region + - "'vpc_id' in acceptor_details" + - acceptor_details.vpc_id == vpc_2 + # Information about the 'requesting' VPC is reduced once the VPC's deleted + - "'cidr_block' not in requester_details" + - "'cidr_block_set' not in requester_details" + - "'peering_options' not in requester_details" + - "'owner_id' in requester_details" + - requester_details.owner_id == account_id + - "'region' in requester_details" + - requester_details.region == aws_region + - "'vpc_id' in requester_details" + - requester_details.vpc_id == vpc_1 + vars: + peer_details: '{{ peer_info.vpc_peering_connections[0] }}' + acceptor_details: '{{ peer_details["accepter_vpc_info"] }}' + requester_details: '{{ peer_details["requester_vpc_info"] }}' + + - name: (re-) delete a local VPC peering Connection (idempotency) + ec2_vpc_peer: + peering_id: "{{ vpc_peer.peering_id }}" + state: absent + register: delete_peer + - name: Assert success + assert: + that: + - delete_peer is not changed + - delete_peer is successful + + - name: Create local account VPC peering Connection + ec2_vpc_peer: + vpc_id: '{{ vpc_1 }}' + peer_vpc_id: '{{ vpc_2 }}' + state: present + tags: + Name: 'Peering connection for VPC {{ vpc_1 }} to VPC {{ vpc_2 }}' + register: vpc_peer2 + - name: Assert success + assert: + that: + - vpc_peer2 is changed + - vpc_peer2 is successful + - "'peering_id' in vpc_peer2" + - vpc_peer2.peering_id.startswith('pcx-') + + - name: Store Connection ID + set_fact: + peer_id_2: '{{ vpc_peer2.peering_id }}' + + - name: reject a local VPC peering Connection + ec2_vpc_peer: + peering_id: "{{ vpc_peer2.peering_id }}" + state: reject + wait: True + register: reject_peer + - name: Assert success + assert: + that: + - reject_peer is changed + - reject_peer is successful + - reject_peer.peering_id == peer_id_2 + + - name: (re-) reject a local VPC peering Connection + ec2_vpc_peer: + peering_id: "{{ vpc_peer2.peering_id }}" + state: reject + register: reject_peer + - name: Assert success + assert: + that: + - reject_peer is not changed + - reject_peer is successful + - reject_peer.peering_id == peer_id_2 + - reject_peer.vpc_peering_connection.vpc_peering_connection_id == peer_id_2 + + - name: delete a local VPC peering Connection + ec2_vpc_peer: + peering_id: "{{ vpc_peer2.peering_id }}" + state: absent + register: delete_peer + - name: Assert success + assert: + that: + - delete_peer is not changed + - delete_peer is successful + + always: + + - name: Find all VPC Peering connections for our VPCs + ec2_vpc_peering_info: + filters: + accepter-vpc-info.vpc-id: '{{ item }}' + register: peering_info + loop: + - '{{ vpc_1 }}' + - '{{ vpc_2 }}' + + - set_fact: + vpc_peering_connection_ids: '{{ _vpc_peering_connections | map(attribute="vpc_peering_connection_id") | list }}' + vars: + _vpc_peering_connections: '{{ peering_info.results | map(attribute="vpc_peering_connections") | flatten }}' + ignore_errors: True + + # ============================================================ + + - name: Delete remaining Peering connections + ec2_vpc_peer: + peering_id: "{{ item }}" + state: absent + ignore_errors: True + loop: '{{ vpc_peering_connection_ids }}' + + - name: tidy up VPC 2 + ec2_vpc_net: + name: "{{ vpc_2_name }}" + state: absent + cidr_block: "{{ vpc_2_cidr }}" + ignore_errors: true + + - name: tidy up VPC 1 + ec2_vpc_net: + name: "{{ vpc_1_name }}" + state: absent + cidr_block: "{{ vpc_1_cidr }}" + ignore_errors: true diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_vgw/aliases b/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_vgw/aliases new file mode 100644 index 000000000..9daebe592 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_vgw/aliases @@ -0,0 +1,3 @@ +cloud/aws + +ec2_vpc_vgw_info diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_vgw/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_vgw/defaults/main.yml new file mode 100644 index 000000000..b10650336 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_vgw/defaults/main.yml @@ -0,0 +1,12 @@ +--- +vpc_name: '{{ resource_prefix }}-ec2-vpc-vgw' +vgw_name: '{{ resource_prefix }}-ec2-vpc-vgw' +subnet_name: '{{ resource_prefix }}-ec2-vpc-vgw' +vpc_cidr: '10.{{ 256 | random(seed=resource_prefix) }}.0.0/16' +subnet_1: '10.{{ 256 | random(seed=resource_prefix) }}.1.0/24' +subnet_2: '10.{{ 256 | random(seed=resource_prefix) }}.2.0/24' +subnet_3: '10.{{ 256 | random(seed=resource_prefix) }}.3.0/24' +subnet_4: '10.{{ 256 | random(seed=resource_prefix) }}.4.0/24' + +vpc_ipv6_cidr: '10.{{ 256 | random(seed=resource_prefix) }}.5.0/25' +vpc_ipv6_name: '{{ vpc_name }}-ipv6' diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_vgw/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_vgw/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_vgw/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_vgw/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_vgw/tasks/main.yml new file mode 100644 index 000000000..37bbf5e37 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_vgw/tasks/main.yml @@ -0,0 +1,233 @@ +--- +- name: 'ec2_vpc_vgw integration tests' + collections: + - amazon.aws + module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + block: + + # ============================================================ + - debug: msg="Setting up test dependencies" + + - name: create a VPC + ec2_vpc_net: + name: "{{ vpc_name }}-{{ item }}" + state: present + cidr_block: "{{ vpc_cidr }}" + tags: + Description: "Created by ansible-test for IGW tests" + register: vpc_result + loop: [1, 2] + + - name: use set fact for vpc ids + set_fact: + vpc_id_1: '{{ vpc_result.results.0.vpc.id }}' + vpc_id_2: '{{ vpc_result.results.1.vpc.id }}' + + # ============================================================ + - debug: msg="Running tests" + + - name: create vpn gateway and attach it to vpc + ec2_vpc_vgw: + state: present + vpc_id: '{{ vpc_id_1 }}' + name: "{{ vgw_name }}" + register: vgw + + - name: use set fact for vgw ids + set_fact: + vgw_id: '{{ vgw.vgw.id }}' + + - assert: + that: + - vgw.changed + - vgw.vgw.vpc_id == vpc_id_1 + - vgw.vgw.tags.Name == vgw_name + + - name: test idempotence + ec2_vpc_vgw: + state: present + vpc_id: '{{ vpc_id_1 }}' + name: "{{ vgw_name }}" + register: vgw + + - assert: + that: + - not vgw.changed + - vgw.vgw.id == vgw_id + + # ============================================================ + - name: attach vpn gateway to the other VPC + ec2_vpc_vgw: + state: present + vpc_id: '{{ vpc_id_2 }}' + name: "{{ vgw_name }}" + register: vgw + + - assert: + that: + - vgw.changed + - vgw.vgw.id == vgw_id + - vgw.vgw.vpc_id == vpc_id_2 + + # ============================================================ + + - name: get VPC VGW facts by ID (CHECK) + ec2_vpc_vgw_info: + vpn_gateway_ids: ['{{ vgw_id }}'] + register: vgw_info + check_mode: True + + - name: verify expected facts + vars: + vgw_details: '{{ vgw_info.virtual_gateways[0] }}' + attach_1_description: + state: 'detached' + vpc_id: '{{ vpc_id_1 }}' + attach_2_description: + state: 'attached' + vpc_id: '{{ vpc_id_2 }}' + assert: + that: + - vgw_info.virtual_gateways | length == 1 + - '"resource_tags" in vgw_details' + - '"state" in vgw_details' + - '"tags" in vgw_details' + - '"type" in vgw_details' + - '"vpc_attachments" in vgw_details' + - '"vpn_gateway_id" in vgw_details' + - vgw_details.vpn_gateway_id == vgw_id + - vgw_details.type == 'ipsec.1' + - vgw_details.state == 'available' + - '"Name" in vgw_details.resource_tags' + - vgw_details.resource_tags.Name == vgw_name + - ( + attach_1_description in vgw_details.vpc_attachments + and + vgw_details.vpc_attachments | length == 2 + ) or ( vgw_details.vpc_attachments | length == 1 ) + - attach_2_description in vgw_details.vpc_attachments + + - name: get VPC VGW facts by Tag + ec2_vpc_vgw_info: + filters: + "tag:Name": "{{ vgw_name }}" + register: vgw_info + + - name: verify expected facts + vars: + vgw_details: '{{ vgw_info.virtual_gateways[0] }}' + attach_1_description: + state: 'detached' + vpc_id: '{{ vpc_id_1 }}' + attach_2_description: + state: 'attached' + vpc_id: '{{ vpc_id_2 }}' + assert: + that: + - vgw_info.virtual_gateways | length == 1 + - '"resource_tags" in vgw_details' + - '"state" in vgw_details' + - '"tags" in vgw_details' + - '"type" in vgw_details' + - '"vpc_attachments" in vgw_details' + - '"vpn_gateway_id" in vgw_details' + - vgw_details.vpn_gateway_id == vgw_id + - vgw_details.type == 'ipsec.1' + - vgw_details.state == 'available' + - '"Name" in vgw_details.resource_tags' + - vgw_details.resource_tags.Name == vgw_name + - ( + attach_1_description in vgw_details.vpc_attachments + and + vgw_details.vpc_attachments | length == 2 + ) or ( vgw_details.vpc_attachments | length == 1 ) + - attach_2_description in vgw_details.vpc_attachments + + # ============================================================ + + - name: get all VGWs + ec2_vpc_vgw_info: + register: vgw_info + + - name: verify test VGW is in the results + vars: + vgw_id_list: '{{ vgw_info.virtual_gateways | map(attribute="vpn_gateway_id") | list }}' + assert: + that: + - vgw_id in vgw_id_list + + # ============================================================ + + - name: detach vpn gateway + ec2_vpc_vgw: + state: present + name: "{{ vgw_name }}" + register: vgw + + - assert: + that: + - vgw.changed + - not vgw.vgw.vpc_id + + - name: test idempotence + ec2_vpc_vgw: + state: present + name: "{{ vgw_name }}" + register: vgw + + - assert: + that: + - not vgw.changed + + # ============================================================ + + - include_tasks: 'tags.yml' + + # ============================================================ + + - name: delete vpn gateway + ec2_vpc_vgw: + state: absent + name: "{{ vgw_name }}" + register: vgw + + - assert: + that: + - vgw.changed + + - name: test idempotence + ec2_vpc_vgw: + state: absent + name: "{{ vgw_name }}" + register: vgw + + - assert: + that: + - not vgw.changed + + always: + + - debug: msg="Removing test dependencies" + + - name: delete vpn gateway + ec2_vpc_vgw: + state: absent + vpn_gateway_id: '{{ vgw.vgw.id | default(vgw_id) }}' + ignore_errors: yes + + - name: delete vpc + ec2_vpc_net: + name: "{{ vpc_name }}-{{ item }}" + state: absent + cidr_block: "{{ vpc_cidr }}" + loop: [1, 2] + register: result + retries: 10 + delay: 5 + until: result is not failed + ignore_errors: true diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_vgw/tasks/tags.yml b/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_vgw/tasks/tags.yml new file mode 100644 index 000000000..a80521313 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_vgw/tasks/tags.yml @@ -0,0 +1,333 @@ +- vars: + first_tags: + 'Key with Spaces': Value with spaces + CamelCaseKey: CamelCaseValue + pascalCaseKey: pascalCaseValue + snake_case_key: snake_case_value + second_tags: + 'New Key with Spaces': Value with spaces + NewCamelCaseKey: CamelCaseValue + newPascalCaseKey: pascalCaseValue + new_snake_case_key: snake_case_value + third_tags: + 'Key with Spaces': Value with spaces + CamelCaseKey: CamelCaseValue + pascalCaseKey: pascalCaseValue + snake_case_key: snake_case_value + 'New Key with Spaces': Updated Value with spaces + final_tags: + 'Key with Spaces': Value with spaces + CamelCaseKey: CamelCaseValue + pascalCaseKey: pascalCaseValue + snake_case_key: snake_case_value + 'New Key with Spaces': Updated Value with spaces + NewCamelCaseKey: CamelCaseValue + newPascalCaseKey: pascalCaseValue + new_snake_case_key: snake_case_value + name_tags: + Name: '{{ vgw_name }}' + module_defaults: + ec2_vpc_vgw: + name: '{{ vgw_name }}' + ec2_vpc_vgw_info: + vpn_gateway_ids: ['{{ vgw_id }}'] + block: + + # ============================================================ + +# - name: (check) add tags +# ec2_vpc_vgw: +# tags: '{{ first_tags }}' +# state: 'present' +# register: tag_vgw +# check_mode: True +# +# - name: assert would change +# assert: +# that: +# - tag_vgw is changed +# - tag_vgw.vgw.id == vgw_id + + - name: add tags + ec2_vpc_vgw: + tags: '{{ first_tags }}' + state: 'present' + register: tag_vgw + + - name: get VPC VGW facts + ec2_vpc_vgw_info: {} + register: tag_vgw_info + + - name: verify the tags were added + assert: + that: + - tag_vgw is changed + - tag_vgw.vgw.id == vgw_id + - tag_vgw_info.virtual_gateways[0].vpn_gateway_id == vgw_id + - tag_vgw_info.virtual_gateways[0].resource_tags == ( first_tags | combine(name_tags) ) + +# - name: (check) add tags - IDEMPOTENCY +# ec2_vpc_vgw: +# tags: '{{ first_tags }}' +# state: 'present' +# register: tag_vgw +# check_mode: True +# +# - name: assert would not change +# assert: +# that: +# - tag_vgw is not changed +# - tag_vgw.vgw.id == vgw_id + + - name: add tags - IDEMPOTENCY + ec2_vpc_vgw: + tags: '{{ first_tags }}' + state: 'present' + register: tag_vgw + - name: get VPC VGW facts + ec2_vpc_vgw_info: {} + register: tag_vgw_info + + - name: verify no change + assert: + that: + - tag_vgw is not changed + - tag_vgw.vgw.id == vgw_id + - tag_vgw_info.virtual_gateways[0].vpn_gateway_id == vgw_id + - tag_vgw_info.virtual_gateways[0].resource_tags == ( first_tags | combine(name_tags) ) + + # ============================================================ + + - name: get VPC VGW facts by filter + ec2_vpc_vgw_info: + filters: + 'tag:Name': '{{ vgw_name }}' + vpn_gateway_ids: '{{ omit }}' + register: tag_vgw_info + + - name: assert the facts are the same as before + assert: + that: + - tag_vgw_info.virtual_gateways | length == 1 + - tag_vgw.vgw.id == vgw_id + - tag_vgw_info.virtual_gateways[0].vpn_gateway_id == vgw_id + + # ============================================================ + +# - name: (check) modify tags with purge +# ec2_vpc_vgw: +# tags: '{{ second_tags }}' +# state: 'present' +# register: tag_vgw +# check_mode: True +# +# - name: assert would change +# assert: +# that: +# - tag_vgw is changed +# - tag_vgw.vgw.id == vgw_id + + - name: modify tags with purge + ec2_vpc_vgw: + tags: '{{ second_tags }}' + state: 'present' + register: tag_vgw + - name: get VPC VGW facts + ec2_vpc_vgw_info: + register: tag_vgw_info + + - name: verify the tags were added + assert: + that: + - tag_vgw is changed + - tag_vgw.vgw.id == vgw_id + - tag_vgw_info.virtual_gateways[0].vpn_gateway_id == vgw_id + - tag_vgw_info.virtual_gateways[0].resource_tags == ( second_tags | combine(name_tags) ) + +# - name: (check) modify tags with purge - IDEMPOTENCY +# ec2_vpc_vgw: +# tags: '{{ second_tags }}' +# state: 'present' +# register: tag_vgw +# check_mode: True +# +# - name: assert would not change +# assert: +# that: +# - tag_vgw is not changed +# - tag_vgw.vgw.id == vgw_id + + - name: modify tags with purge - IDEMPOTENCY + ec2_vpc_vgw: + tags: '{{ second_tags }}' + state: 'present' + register: tag_vgw + - name: get VPC VGW facts + ec2_vpc_vgw_info: + register: tag_vgw_info + + - name: verify no change + assert: + that: + - tag_vgw is not changed + - tag_vgw.vgw.id == vgw_id + - tag_vgw_info.virtual_gateways[0].vpn_gateway_id == vgw_id + - tag_vgw_info.virtual_gateways[0].resource_tags == ( second_tags | combine(name_tags) ) + + # ============================================================ + +# - name: (check) modify tags without purge +# ec2_vpc_vgw: +# tags: '{{ third_tags }}' +# state: 'present' +# purge_tags: False +# register: tag_vgw +# check_mode: True +# +# - name: assert would change +# assert: +# that: +# - tag_vgw is changed +# - tag_vgw.vgw.id == vgw_id + + - name: modify tags without purge + ec2_vpc_vgw: + tags: '{{ third_tags }}' + state: 'present' + purge_tags: False + register: tag_vgw + - name: get VPC VGW facts + ec2_vpc_vgw_info: + register: tag_vgw_info + + - name: verify the tags were added + assert: + that: + - tag_vgw is changed + - tag_vgw.vgw.id == vgw_id + - tag_vgw_info.virtual_gateways[0].vpn_gateway_id == vgw_id + - tag_vgw_info.virtual_gateways[0].resource_tags == ( final_tags | combine(name_tags) ) + +# - name: (check) modify tags without purge - IDEMPOTENCY +# ec2_vpc_vgw: +# tags: '{{ third_tags }}' +# state: 'present' +# purge_tags: False +# register: tag_vgw +# check_mode: True +# +# - name: assert would not change +# assert: +# that: +# - tag_vgw is not changed +# - tag_vgw.vgw.id == vgw_id + + - name: modify tags without purge - IDEMPOTENCY + ec2_vpc_vgw: + tags: '{{ third_tags }}' + state: 'present' + purge_tags: False + register: tag_vgw + - name: get VPC VGW facts + ec2_vpc_vgw_info: + register: tag_vgw_info + + - name: verify no change + assert: + that: + - tag_vgw is not changed + - tag_vgw.vgw.id == vgw_id + - tag_vgw_info.virtual_gateways[0].vpn_gateway_id == vgw_id + - tag_vgw_info.virtual_gateways[0].resource_tags == ( final_tags | combine(name_tags) ) + + # ============================================================ + +# - name: (check) No change to tags without setting tags +# ec2_vpc_vgw: +# state: 'present' +# register: tag_vgw +# check_mode: True +# +# - name: assert would change +# assert: +# that: +# - tag_vgw is not changed +# - tag_vgw.vgw.id == vgw_id + + - name: No change to tags without setting tags + ec2_vpc_vgw: + state: 'present' + register: tag_vgw + - name: get VPC VGW facts + ec2_vpc_vgw_info: + register: tag_vgw_info + + - name: verify the tags were added + assert: + that: + - tag_vgw is not changed + - tag_vgw.vgw.id == vgw_id + - tag_vgw_info.virtual_gateways[0].vpn_gateway_id == vgw_id + - tag_vgw_info.virtual_gateways[0].resource_tags == ( final_tags | combine(name_tags) ) + + # ============================================================ + +# - name: (check) remove non name tags +# ec2_vpc_vgw: +# tags: {} +# state: 'present' +# register: tag_vgw +# check_mode: True +# +# - name: assert would change +# assert: +# that: +# - tag_vgw is changed +# - tag_vgw.vgw.id == vgw_id + + - name: remove non name tags + ec2_vpc_vgw: + tags: {} + state: 'present' + register: tag_vgw + - name: get VPC VGW facts + ec2_vpc_vgw_info: + register: tag_vgw_info + + - name: verify the tags were added + assert: + that: + - tag_vgw is changed + - tag_vgw.vgw.id == vgw_id + - tag_vgw_info.virtual_gateways[0].vpn_gateway_id == vgw_id + - tag_vgw_info.virtual_gateways[0].resource_tags == name_tags + +# - name: (check) remove non name tags - IDEMPOTENCY +# ec2_vpc_vgw: +# tags: {} +# state: 'present' +# register: tag_vgw +# check_mode: True +# +# - name: assert would not change +# assert: +# that: +# - tag_vgw is not changed +# - tag_vgw.vgw.id == vgw_id + + - name: remove non name tags - IDEMPOTENCY + ec2_vpc_vgw: + tags: {} + state: 'present' + register: tag_vgw + - name: get VPC VGW facts + ec2_vpc_vgw_info: + register: tag_vgw_info + + - name: verify no change + assert: + that: + - tag_vgw is not changed + - tag_vgw.vgw.id == vgw_id + - tag_vgw_info.virtual_gateways[0].vpn_gateway_id == vgw_id + - tag_vgw_info.virtual_gateways[0].resource_tags == name_tags diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_vpn/aliases b/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_vpn/aliases new file mode 100644 index 000000000..28e84af91 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_vpn/aliases @@ -0,0 +1,3 @@ +cloud/aws + +ec2_vpc_vpn_info diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_vpn/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_vpn/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_vpn/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_vpn/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_vpn/tasks/main.yml new file mode 100644 index 000000000..a4c740887 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_vpn/tasks/main.yml @@ -0,0 +1,165 @@ +--- +- name: 'ec2_vpc_vpn_info integration tests' + collections: + - amazon.aws + module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + block: + + # ============================================================ + - name: create a VPC + ec2_vpc_net: + name: "{{ resource_prefix }}-vpc" + state: present + cidr_block: "10.0.0.0/26" + tags: + Name: "{{ resource_prefix }}-vpc" + Description: "Created by ansible-test" + register: vpc_result + + - name: create vpn gateway and attach it to vpc + ec2_vpc_vgw: + state: present + vpc_id: '{{ vpc_result.vpc.id }}' + name: "{{ resource_prefix }}-vgw" + register: vgw + + - name: create customer gateway + ec2_customer_gateway: + bgp_asn: 12345 + ip_address: 1.2.3.4 + name: testcgw + register: cgw + + - name: create vpn connection, with customer gateway + ec2_vpc_vpn: + customer_gateway_id: '{{ cgw.gateway.customer_gateway.customer_gateway_id }}' + vpn_gateway_id: '{{ vgw.vgw.id }}' + state: present + register: vpn + + - name: Store ID of VPN + set_fact: + vpn_id: '{{ vpn.vpn_connection_id }}' + + # ============================================================ + - name: test success with no parameters + ec2_vpc_vpn_info: + register: result + + - name: assert success with no parameters + assert: + that: + - 'result.changed == false' + - 'result.vpn_connections != []' + + - name: test success with customer gateway id as a filter + ec2_vpc_vpn_info: + filters: + customer-gateway-id: '{{ cgw.gateway.customer_gateway.customer_gateway_id }}' + vpn-connection-id: '{{ vpn.vpn_connection_id }}' + register: result + + - name: assert success with customer gateway id as filter + assert: + that: + - 'result.changed == false' + - 'result.vpn_connections != []' + + # ============================================================ + + - include_tasks: 'tags.yml' + + # ============================================================ + + - name: delete vpn connection (check) + ec2_vpc_vpn: + state: absent + vpn_connection_id: '{{ vpn_id }}' + register: result + check_mode: True + + - assert: + that: + - result is changed + + - name: delete vpn connection + ec2_vpc_vpn: + state: absent + vpn_connection_id: '{{ vpn_id }}' + register: result + + - assert: + that: + - result is changed + + - name: delete vpn connection - idempotency (check) + ec2_vpc_vpn: + state: absent + vpn_connection_id: '{{ vpn_id }}' + register: result + check_mode: True + + - assert: + that: + - result is not changed + + - name: delete vpn connection - idempotency + ec2_vpc_vpn: + state: absent + vpn_connection_id: '{{ vpn_id }}' + register: result + + - assert: + that: + - result is not changed + + # ============================================================ + always: + + - name: delete vpn connection + ec2_vpc_vpn: + state: absent + vpn_connection_id: '{{ vpn.vpn_connection_id }}' + register: result + retries: 10 + delay: 3 + until: result is not failed + ignore_errors: true + + - name: delete customer gateway + ec2_customer_gateway: + state: absent + ip_address: 1.2.3.4 + name: testcgw + bgp_asn: 12345 + register: result + retries: 10 + delay: 3 + until: result is not failed + ignore_errors: true + + - name: delete vpn gateway + ec2_vpc_vgw: + state: absent + vpn_gateway_id: '{{ vgw.vgw.id }}' + register: result + retries: 10 + delay: 3 + until: result is not failed + ignore_errors: true + + - name: delete vpc + ec2_vpc_net: + name: "{{ resource_prefix }}-vpc" + state: absent + cidr_block: "10.0.0.0/26" + register: result + retries: 10 + delay: 3 + until: result is not failed + ignore_errors: true diff --git a/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_vpn/tasks/tags.yml b/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_vpn/tasks/tags.yml new file mode 100644 index 000000000..fb97f01fa --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ec2_vpc_vpn/tasks/tags.yml @@ -0,0 +1,338 @@ +- 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 + module_defaults: + ec2_vpc_vpn: + vpn_connection_id: '{{ vpn_id }}' + ec2_vpc_vpn_info: + filters: + vpn-connection-id: '{{ vpn_id }}' + block: + + # ============================================================ + + - name: (check) add tags + ec2_vpc_vpn: + tags: '{{ first_tags }}' + state: 'present' + register: tag_vpn + check_mode: True + + - name: assert would change + assert: + that: + - tag_vpn is changed + - tag_vpn.vpn_connection_id == vpn_id + + - name: add tags + ec2_vpc_vpn: + tags: '{{ first_tags }}' + state: 'present' + register: tag_vpn + + - name: get VPC VPN facts + ec2_vpc_vpn_info: {} + register: tag_vpn_info + + - name: verify the tags were added + assert: + that: + - tag_vpn is changed + - tag_vpn.vpn_connection_id == vpn_id + - tag_vpn_info.vpn_connections[0].vpn_connection_id == vpn_id + - tag_vpn_info.vpn_connections[0].tags == first_tags + + - name: (check) add tags - IDEMPOTENCY + ec2_vpc_vpn: + tags: '{{ first_tags }}' + state: 'present' + register: tag_vpn + check_mode: True + + - name: assert would not change + assert: + that: + - tag_vpn is not changed + - tag_vpn.vpn_connection_id == vpn_id + + - name: add tags - IDEMPOTENCY + ec2_vpc_vpn: + tags: '{{ first_tags }}' + state: 'present' + register: tag_vpn + - name: get VPC VPN facts + ec2_vpc_vpn_info: {} + register: tag_vpn_info + + - name: verify no change + assert: + that: + - tag_vpn is not changed + - tag_vpn.vpn_connection_id == vpn_id + - tag_vpn_info.vpn_connections[0].vpn_connection_id == vpn_id + - tag_vpn_info.vpn_connections[0].tags == first_tags + + # ============================================================ + +# - name: get VPC VPN facts by filter +# ec2_vpc_vpn_info: +# filters: +# 'tag:Name': '{{ vgw_name }}' +# vpn_connection_ids: '{{ omit }}' +# register: tag_vpn_info +# +# - name: assert the facts are the same as before +# assert: +# that: +# - tag_vpn_info.vpn_connections | length == 1 +# - tag_vpn.vpn_connection_id == vpn_id +# - tag_vpn_info.vpn_connections[0].vpn_connection_id == vpn_id + + # ============================================================ + + - name: (check) modify tags with purge + ec2_vpc_vpn: + tags: '{{ second_tags }}' + state: 'present' + purge_tags: true + register: tag_vpn + check_mode: True + + - name: assert would change + assert: + that: + - tag_vpn is changed + - tag_vpn.vpn_connection_id == vpn_id + + - name: modify tags with purge + ec2_vpc_vpn: + tags: '{{ second_tags }}' + state: 'present' + purge_tags: true + register: tag_vpn + - name: get VPC VPN facts + ec2_vpc_vpn_info: + register: tag_vpn_info + + - name: verify the tags were added + assert: + that: + - tag_vpn is changed + - tag_vpn.vpn_connection_id == vpn_id + - tag_vpn_info.vpn_connections[0].vpn_connection_id == vpn_id + - tag_vpn_info.vpn_connections[0].tags == second_tags + + - name: (check) modify tags with purge - IDEMPOTENCY + ec2_vpc_vpn: + tags: '{{ second_tags }}' + state: 'present' + purge_tags: true + register: tag_vpn + check_mode: True + + - name: assert would not change + assert: + that: + - tag_vpn is not changed + - tag_vpn.vpn_connection_id == vpn_id + + - name: modify tags with purge - IDEMPOTENCY + ec2_vpc_vpn: + tags: '{{ second_tags }}' + state: 'present' + purge_tags: true + register: tag_vpn + - name: get VPC VPN facts + ec2_vpc_vpn_info: + register: tag_vpn_info + + - name: verify no change + assert: + that: + - tag_vpn is not changed + - tag_vpn.vpn_connection_id == vpn_id + - tag_vpn_info.vpn_connections[0].vpn_connection_id == vpn_id + - tag_vpn_info.vpn_connections[0].tags == second_tags + + # ============================================================ + + - name: (check) modify tags without purge + ec2_vpc_vpn: + tags: '{{ third_tags }}' + state: 'present' + purge_tags: False + register: tag_vpn + check_mode: True + + - name: assert would change + assert: + that: + - tag_vpn is changed + - tag_vpn.vpn_connection_id == vpn_id + + - name: modify tags without purge + ec2_vpc_vpn: + tags: '{{ third_tags }}' + state: 'present' + purge_tags: False + register: tag_vpn + - name: get VPC VPN facts + ec2_vpc_vpn_info: + register: tag_vpn_info + + - name: verify the tags were added + assert: + that: + - tag_vpn is changed + - tag_vpn.vpn_connection_id == vpn_id + - tag_vpn_info.vpn_connections[0].vpn_connection_id == vpn_id + - tag_vpn_info.vpn_connections[0].tags == final_tags + + - name: (check) modify tags without purge - IDEMPOTENCY + ec2_vpc_vpn: + tags: '{{ third_tags }}' + state: 'present' + purge_tags: False + register: tag_vpn + check_mode: True + + - name: assert would not change + assert: + that: + - tag_vpn is not changed + - tag_vpn.vpn_connection_id == vpn_id + + - name: modify tags without purge - IDEMPOTENCY + ec2_vpc_vpn: + tags: '{{ third_tags }}' + state: 'present' + purge_tags: False + register: tag_vpn + - name: get VPC VPN facts + ec2_vpc_vpn_info: + register: tag_vpn_info + + - name: verify no change + assert: + that: + - tag_vpn is not changed + - tag_vpn.vpn_connection_id == vpn_id + - tag_vpn_info.vpn_connections[0].vpn_connection_id == vpn_id + - tag_vpn_info.vpn_connections[0].tags == final_tags + + # ============================================================ + + - name: (check) No change to tags without setting tags + ec2_vpc_vpn: + state: 'present' + register: tag_vpn + check_mode: True + + - name: assert would change + assert: + that: + - tag_vpn is not changed + - tag_vpn.vpn_connection_id == vpn_id + + - name: No change to tags without setting tags + ec2_vpc_vpn: + state: 'present' + register: tag_vpn + - name: get VPC VPN facts + ec2_vpc_vpn_info: + register: tag_vpn_info + + - name: verify no tags were added + assert: + that: + - tag_vpn is not changed + - tag_vpn.vpn_connection_id == vpn_id + - tag_vpn_info.vpn_connections[0].vpn_connection_id == vpn_id + - tag_vpn_info.vpn_connections[0].tags == final_tags + + # ============================================================ + + - name: (check) remove tags + ec2_vpc_vpn: + tags: {} + state: 'present' + purge_tags: True + register: tag_vpn + check_mode: True + + - name: assert would change + assert: + that: + - tag_vpn is changed + - tag_vpn.vpn_connection_id == vpn_id + + - name: remove tags + ec2_vpc_vpn: + tags: {} + state: 'present' + purge_tags: True + register: tag_vpn + - name: get VPC VPN facts + ec2_vpc_vpn_info: + register: tag_vpn_info + + - name: verify the tags were removed + assert: + that: + - tag_vpn is changed + - tag_vpn.vpn_connection_id == vpn_id + - tag_vpn_info.vpn_connections[0].vpn_connection_id == vpn_id + + - name: (check) remove tags - IDEMPOTENCY + ec2_vpc_vpn: + tags: {} + state: 'present' + purge_tags: True + register: tag_vpn + check_mode: True + + - name: assert would not change + assert: + that: + - tag_vpn is not changed + - tag_vpn.vpn_connection_id == vpn_id + + - name: remove tags - IDEMPOTENCY + ec2_vpc_vpn: + tags: {} + state: 'present' + purge_tags: True + register: tag_vpn + - name: get VPC VPN facts + ec2_vpc_vpn_info: + register: tag_vpn_info + + - name: verify no change + assert: + that: + - tag_vpn is not changed + - tag_vpn.vpn_connection_id == vpn_id + - tag_vpn_info.vpn_connections[0].vpn_connection_id == vpn_id diff --git a/ansible_collections/community/aws/tests/integration/targets/ecs_cluster/aliases b/ansible_collections/community/aws/tests/integration/targets/ecs_cluster/aliases new file mode 100644 index 000000000..3a010d0ed --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ecs_cluster/aliases @@ -0,0 +1,9 @@ +time=20m + +cloud/aws + +ecs_service +ecs_service_info +ecs_task +ecs_taskdefinition +ecs_taskdefinition_info diff --git a/ansible_collections/community/aws/tests/integration/targets/ecs_cluster/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/ecs_cluster/defaults/main.yml new file mode 100644 index 000000000..77a9efb07 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ecs_cluster/defaults/main.yml @@ -0,0 +1,51 @@ +ecs_cluster_name: "{{ resource_prefix }}" +user_data: | + #!/bin/bash + echo ECS_CLUSTER={{ ecs_cluster_name }} >> /etc/ecs/ecs.config + +ecs_service_name: "{{ resource_prefix }}-service" +ecs_service_role_name: "ansible-test-ecsServiceRole-{{ tiny_prefix }}" +ecs_task_role_name: "ansible-test-ecsServiceRole-task-{{ tiny_prefix }}" +ecs_task_image_path: nginx +ecs_task_name: "{{ resource_prefix }}-task" +ecs_task_memory: 128 +target_swap_mb: 0 +target_swappiness: 80 +ecs_task_containers: +- name: "{{ ecs_task_name }}" + image: "{{ ecs_task_image_path }}" + essential: true + memory: "{{ ecs_task_memory }}" + portMappings: + - containerPort: "{{ ecs_task_container_port }}" + hostPort: "{{ ecs_task_host_port|default(0) }}" + linuxParameters: + maxSwap: "{{ target_swap_mb }}" + swappiness: "{{ target_swappiness }}" + mountPoints: "{{ ecs_task_mount_points|default([]) }}" +ecs_service_deployment_configuration: + minimum_healthy_percent: 0 + maximum_percent: 100 + deployment_circuit_breaker: + enable: true + rollback: true +ecs_service_placement_strategy: + - type: binpack + field: memory + - type: spread + field: attribute:ecs.availability-zone +ecs_task_container_port: 8080 +ecs_target_group_name: "{{ resource_prefix[:28] }}-tg" +ecs_load_balancer_name: "{{ resource_prefix[:29] }}-lb" +ecs_service_health_check_grace_period: 60 +ecs_fargate_task_containers: +- name: "{{ ecs_task_name }}" + image: "{{ ecs_task_image_path }}" + essential: true + portMappings: + - containerPort: "{{ ecs_task_container_port }}" + hostPort: "{{ ecs_task_host_port|default(0) }}" + #mountPoints: "{{ ecs_task_mount_points|default([]) }}" +ecs_taskdefinition_placement_constraints: + - type: memberOf + expression: 'attribute:ecs.instance-type == t3.micro' diff --git a/ansible_collections/community/aws/tests/integration/targets/ecs_cluster/files/ec2-trust-policy.json b/ansible_collections/community/aws/tests/integration/targets/ecs_cluster/files/ec2-trust-policy.json new file mode 100644 index 000000000..72413abdd --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ecs_cluster/files/ec2-trust-policy.json @@ -0,0 +1,13 @@ +{ + "Version": "2008-10-17", + "Statement": [ + { + "Sid": "", + "Effect": "Allow", + "Principal": { + "Service": "ec2.amazonaws.com" + }, + "Action": "sts:AssumeRole" + } + ] +} diff --git a/ansible_collections/community/aws/tests/integration/targets/ecs_cluster/files/ecs-trust-policy.json b/ansible_collections/community/aws/tests/integration/targets/ecs_cluster/files/ecs-trust-policy.json new file mode 100644 index 000000000..f871b34d9 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ecs_cluster/files/ecs-trust-policy.json @@ -0,0 +1,16 @@ +{ + "Version": "2008-10-17", + "Statement": [ + { + "Sid": "", + "Effect": "Allow", + "Principal": { + "Service": [ + "ecs.amazonaws.com", + "ecs-tasks.amazonaws.com" + ] + }, + "Action": "sts:AssumeRole" + } + ] +} diff --git a/ansible_collections/community/aws/tests/integration/targets/ecs_cluster/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/ecs_cluster/meta/main.yml new file mode 100644 index 000000000..7f42526eb --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ecs_cluster/meta/main.yml @@ -0,0 +1,4 @@ +dependencies: + - role: setup_botocore_pip + vars: + botocore_version: "1.24.14" diff --git a/ansible_collections/community/aws/tests/integration/targets/ecs_cluster/tasks/01_create_requirements.yml b/ansible_collections/community/aws/tests/integration/targets/ecs_cluster/tasks/01_create_requirements.yml new file mode 100644 index 000000000..31ca3cf27 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ecs_cluster/tasks/01_create_requirements.yml @@ -0,0 +1,160 @@ +- name: ensure IAM service role exists + iam_role: + name: "{{ ecs_service_role_name }}" + assume_role_policy_document: "{{ lookup('file','ecs-trust-policy.json') }}" + state: present + create_instance_profile: yes + managed_policy: + - AmazonEC2ContainerServiceRole + wait: True + register: iam_role_creation + +- name: ensure AmazonECSTaskExecutionRolePolicy exists + iam_role: + name: "{{ ecs_task_role_name }}" + assume_role_policy_document: "{{ lookup('file','ecs-trust-policy.json') }}" + description: "Allows ECS containers to make calls to ECR" + state: present + create_instance_profile: false + managed_policy: + - AmazonECSTaskExecutionRolePolicy + wait: True + register: iam_execution_role + +- name: ensure AWSServiceRoleForECS role exists + iam_role_info: + name: AWSServiceRoleForECS + register: iam_role_result + +# # This should happen automatically with the right permissions... +#- name: fail if AWSServiceRoleForECS role does not exist +# fail: +# msg: > +# Run `aws iam create-service-linked-role --aws-service-name=ecs.amazonaws.com ` to create +# a linked role for AWS VPC load balancer management +# when: not iam_role_result.iam_roles + +- name: create a VPC to work in + ec2_vpc_net: + cidr_block: 10.0.0.0/16 + state: present + name: '{{ resource_prefix }}_ecs_cluster' + resource_tags: + Name: '{{ resource_prefix }}_ecs_cluster' + register: setup_vpc + +- name: create a key pair to use for creating an ec2 instance + ec2_key: + name: '{{ resource_prefix }}_ecs_cluster' + state: present + when: ec2_keypair is not defined # allow override in cloud-config-aws.ini + register: setup_key + +- name: create subnets + ec2_vpc_subnet: + az: '{{ aws_region }}{{ item.zone }}' + tags: + Name: '{{ resource_prefix }}_ecs_cluster-subnet-{{ item.zone }}' + vpc_id: '{{ setup_vpc.vpc.id }}' + cidr: "{{ item.cidr }}" + state: present + register: setup_subnet + with_items: + - zone: a + cidr: 10.0.1.0/24 + - zone: b + cidr: 10.0.2.0/24 + +- name: create an internet gateway so that ECS agents can talk to ECS + ec2_vpc_igw: + vpc_id: '{{ setup_vpc.vpc.id }}' + state: present + register: igw + +- name: create a security group to use for creating an ec2 instance + ec2_group: + name: '{{ resource_prefix }}_ecs_cluster-sg' + description: 'created by Ansible integration tests' + state: present + vpc_id: '{{ setup_vpc.vpc.id }}' + rules: # allow all ssh traffic but nothing else + - ports: 22 + cidr_ip: 0.0.0.0/0 + register: setup_sg + +- set_fact: + # As a lookup plugin we don't have access to module_defaults + connection_args: + region: "{{ aws_region }}" + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + aws_security_token: "{{ security_token | default(omit) }}" + no_log: True + +- name: set image id fact + set_fact: + ecs_image_id: "{{ lookup('aws_ssm', '/aws/service/ecs/optimized-ami/amazon-linux-2/recommended/image_id', **connection_args) }}" + +- name: provision ec2 instance to create an image + ec2_instance: + key_name: '{{ ec2_keypair|default(setup_key.key.name) }}' + instance_type: t3.micro + state: present + image_id: '{{ ecs_image_id }}' + wait: yes + user_data: "{{ user_data }}" + instance_role: "{{ ecs_service_role_name }}" + tags: + Name: '{{ resource_prefix }}_ecs_agent' + security_group: '{{ setup_sg.group_id }}' + vpc_subnet_id: '{{ setup_subnet.results[0].subnet.id }}' + register: setup_instance + +- name: create target group + elb_target_group: + name: "{{ ecs_target_group_name }}1" + state: present + protocol: HTTP + port: 8080 + modify_targets: no + vpc_id: '{{ setup_vpc.vpc.id }}' + target_type: instance + health_check_interval: 5 + health_check_timeout: 2 + healthy_threshold_count: 2 + unhealthy_threshold_count: 2 + register: elb_target_group_instance + +- name: create second target group to use ip target_type + elb_target_group: + name: "{{ ecs_target_group_name }}2" + state: present + protocol: HTTP + port: 8080 + modify_targets: no + vpc_id: '{{ setup_vpc.vpc.id }}' + target_type: ip + health_check_interval: 5 + health_check_timeout: 2 + healthy_threshold_count: 2 + unhealthy_threshold_count: 2 + register: elb_target_group_ip + +- name: create load balancer + elb_application_lb: + name: "{{ ecs_load_balancer_name }}" + state: present + scheme: internal + security_groups: '{{ setup_sg.group_id }}' + subnets: "{{ setup_subnet.results | map(attribute='subnet.id') | list }}" + listeners: + - Protocol: HTTP + Port: 80 + DefaultActions: + - Type: forward + TargetGroupName: "{{ ecs_target_group_name }}1" + - Protocol: HTTP + Port: 81 + DefaultActions: + - Type: forward + TargetGroupName: "{{ ecs_target_group_name }}2" diff --git a/ansible_collections/community/aws/tests/integration/targets/ecs_cluster/tasks/10_ecs_cluster.yml b/ansible_collections/community/aws/tests/integration/targets/ecs_cluster/tasks/10_ecs_cluster.yml new file mode 100644 index 000000000..f2c868674 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ecs_cluster/tasks/10_ecs_cluster.yml @@ -0,0 +1,76 @@ +# cluster "{{ ecs_cluster_name }}" is used for ecs_service tests +- name: create an ECS cluster + ecs_cluster: + name: "{{ ecs_cluster_name }}" + state: present + register: ecs_cluster + +- name: check that ecs_cluster changed + assert: + that: + - ecs_cluster.changed + +- name: immutable create same ECS cluster + ecs_cluster: + name: "{{ ecs_cluster_name }}" + state: present + register: ecs_cluster_again + +- name: check that ecs_cluster did not change + assert: + that: + - not ecs_cluster_again.changed + +- name: create an ECS cluster to test capacity provider strategy + ecs_cluster: + name: "{{ ecs_cluster_name }}-cps" + state: present + register: ecs_cluster + +- name: add capacity providers and strategy + ecs_cluster: + name: "{{ ecs_cluster_name }}-cps" + state: present + purge_capacity_providers: True + capacity_providers: + - FARGATE + - FARGATE_SPOT + capacity_provider_strategy: + - capacity_provider: FARGATE + base: 1 + weight: 1 + - capacity_provider: FARGATE_SPOT + weight: 100 + register: ecs_cluster_update + +- name: check that ecs_cluster was correctly updated + assert: + that: + - ecs_cluster_update.changed + - ecs_cluster_update.cluster is defined + - ecs_cluster_update.cluster.capacityProviders is defined + - "'FARGATE' in ecs_cluster_update.cluster.capacityProviders" + +- name: immutable add capacity providers and strategy + ecs_cluster: + name: "{{ ecs_cluster_name }}-cps" + state: present + purge_capacity_providers: True + capacity_providers: + - FARGATE + - FARGATE_SPOT + capacity_provider_strategy: + - capacity_provider: FARGATE + base: 1 + weight: 1 + - capacity_provider: FARGATE_SPOT + weight: 100 + register: ecs_cluster_update + +- name: check that ecs_cluster was correctly updated + assert: + that: + - not ecs_cluster_update.changed + - ecs_cluster_update.cluster is defined + - ecs_cluster_update.cluster.capacityProviders is defined + - "'FARGATE' in ecs_cluster_update.cluster.capacityProviders" diff --git a/ansible_collections/community/aws/tests/integration/targets/ecs_cluster/tasks/20_ecs_service.yml b/ansible_collections/community/aws/tests/integration/targets/ecs_cluster/tasks/20_ecs_service.yml new file mode 100644 index 000000000..4e0620555 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ecs_cluster/tasks/20_ecs_service.yml @@ -0,0 +1,960 @@ +- name: create task definition + ecs_taskdefinition: + containers: "{{ ecs_task_containers }}" + family: "{{ ecs_task_name }}" + state: present + register: ecs_task_definition + +- name: check that initial task definition changes + assert: + that: + - ecs_task_definition.changed + +- name: recreate task definition + ecs_taskdefinition: + containers: "{{ ecs_task_containers }}" + family: "{{ ecs_task_name }}" + state: present + register: ecs_task_definition_again + +- name: check that task definition does not change + assert: + that: + - not ecs_task_definition_again.changed + +- name: obtain ECS task definition facts + ecs_taskdefinition_info: + task_definition: "{{ ecs_task_name }}:{{ ecs_task_definition.taskdefinition.revision }}" + +- name: create ECS service definition + ecs_service: + state: present + name: "{{ ecs_service_name }}" + cluster: "{{ ecs_cluster_name }}" + task_definition: "{{ ecs_task_name }}:{{ ecs_task_definition.taskdefinition.revision }}" + desired_count: 1 + deployment_configuration: "{{ ecs_service_deployment_configuration }}" + placement_strategy: "{{ ecs_service_placement_strategy }}" + placement_constraints: + - type: distinctInstance + health_check_grace_period_seconds: "{{ ecs_service_health_check_grace_period }}" + load_balancers: + - targetGroupArn: "{{ elb_target_group_instance.target_group_arn }}" + containerName: "{{ ecs_task_name }}" + containerPort: "{{ ecs_task_container_port }}" + role: "{{ ecs_service_role_name }}" + register: ecs_service + +- name: check that ECS service creation changed + assert: + that: + - ecs_service.changed + +- name: check that placement constraint has been applied + assert: + that: + - "ecs_service.service.placementConstraints[0].type == 'distinctInstance'" + +- name: check that ECS service was created with deployment_circuit_breaker + assert: + that: + - ecs_service.service.deploymentConfiguration.deploymentCircuitBreaker.enable + - ecs_service.service.deploymentConfiguration.deploymentCircuitBreaker.rollback + +- name: create same ECS service definition (should not change) + ecs_service: + state: present + name: "{{ ecs_service_name }}" + cluster: "{{ ecs_cluster_name }}" + task_definition: "{{ ecs_task_name }}:{{ ecs_task_definition.taskdefinition.revision }}" + desired_count: 1 + deployment_configuration: "{{ ecs_service_deployment_configuration }}" + placement_strategy: "{{ ecs_service_placement_strategy }}" + placement_constraints: + - type: distinctInstance + health_check_grace_period_seconds: "{{ ecs_service_health_check_grace_period }}" + load_balancers: + - targetGroupArn: "{{ elb_target_group_instance.target_group_arn }}" + containerName: "{{ ecs_task_name }}" + containerPort: "{{ ecs_task_container_port }}" + role: "{{ ecs_service_role_name }}" + register: ecs_service_again + +- name: check that ECS service recreation changed nothing + assert: + that: + - not ecs_service_again.changed + +- name: create same ECS service definition via force_new_deployment + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + ecs_service: + state: present + force_new_deployment: true + name: "{{ ecs_service_name }}" + cluster: "{{ ecs_cluster_name }}" + task_definition: "{{ ecs_task_name }}:{{ ecs_task_definition.taskdefinition.revision }}" + desired_count: 1 + deployment_configuration: "{{ ecs_service_deployment_configuration }}" + placement_strategy: "{{ ecs_service_placement_strategy }}" + placement_constraints: + - type: distinctInstance + health_check_grace_period_seconds: "{{ ecs_service_health_check_grace_period }}" + load_balancers: + - targetGroupArn: "{{ elb_target_group_instance.target_group_arn }}" + containerName: "{{ ecs_task_name }}" + containerPort: "{{ ecs_task_container_port }}" + role: "{{ ecs_service_role_name }}" + register: ecs_service_again + +- name: check that ECS service recreation changed again due force_new_deployment + assert: + that: + - ecs_service_again.changed + +- name: force_new_deployment should work without providing a task_definition + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + ecs_service: + state: present + force_new_deployment: yes + name: "{{ ecs_service_name }}" + cluster: "{{ ecs_cluster_name }}" + desired_count: 1 + deployment_configuration: "{{ ecs_service_deployment_configuration }}" + placement_strategy: "{{ ecs_service_placement_strategy }}" + placement_constraints: + - type: distinctInstance + health_check_grace_period_seconds: "{{ ecs_service_health_check_grace_period }}" + load_balancers: + - targetGroupArn: "{{ elb_target_group_instance.target_group_arn }}" + containerName: "{{ ecs_task_name }}" + containerPort: "{{ ecs_task_container_port }}" + role: "{{ ecs_service_role_name }}" + register: ecs_service_notaskdef + +- name: check that ECS service changed again due to force_new_deployment with no task definition + assert: + that: + - ecs_service_notaskdef.changed + +- name: attempt to use ECS network configuration on task definition without awsvpc network_mode (expected to fail) + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + ecs_service: + state: present + name: "{{ ecs_service_name }}3" + cluster: "{{ ecs_cluster_name }}" + task_definition: "{{ ecs_task_name }}:{{ ecs_task_definition.taskdefinition.revision }}" + desired_count: 1 + deployment_configuration: "{{ ecs_service_deployment_configuration }}" + placement_strategy: "{{ ecs_service_placement_strategy }}" + load_balancers: + - targetGroupArn: "{{ elb_target_group_instance.target_group_arn }}" + containerName: "{{ ecs_task_name }}" + containerPort: "{{ ecs_task_container_port }}" + network_configuration: + subnets: "{{ setup_subnet.results | map(attribute='subnet.id') | list }}" + security_groups: + - '{{ setup_sg.group_id }}' + register: ecs_service_network_without_awsvpc_task + ignore_errors: true + +- name: assert that using ECS network configuration with non AWSVPC task definition fails + assert: + that: + - ecs_service_network_without_awsvpc_task is failed + +- name: scale down ECS service + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + ecs_service: + state: present + name: "{{ ecs_service_name }}" + cluster: "{{ ecs_cluster_name }}" + task_definition: "{{ ecs_task_name }}:{{ ecs_task_definition.taskdefinition.revision }}" + desired_count: 0 + deployment_configuration: "{{ ecs_service_deployment_configuration }}" + placement_strategy: "{{ ecs_service_placement_strategy }}" + load_balancers: + - targetGroupArn: "{{ elb_target_group_instance.target_group_arn }}" + containerName: "{{ ecs_task_name }}" + containerPort: "{{ ecs_task_container_port }}" + role: "{{ ecs_service_role_name }}" + wait: true + register: ecs_service_scale_down + +- name: assert that ECS service is scaled down + assert: + that: + - ecs_service_scale_down.changed + - ecs_service_scale_down.service.desiredCount == 0 + +- name: scale down ECS service again + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + ecs_service: + state: present + name: "{{ ecs_service_name }}" + cluster: "{{ ecs_cluster_name }}" + task_definition: "{{ ecs_task_name }}:{{ ecs_task_definition.taskdefinition.revision }}" + desired_count: 0 + deployment_configuration: "{{ ecs_service_deployment_configuration }}" + placement_strategy: "{{ ecs_service_placement_strategy }}" + load_balancers: + - targetGroupArn: "{{ elb_target_group_instance.target_group_arn }}" + containerName: "{{ ecs_task_name }}" + containerPort: "{{ ecs_task_container_port }}" + role: "{{ ecs_service_role_name }}" + register: ecs_service_scale_down + +- name: assert no change + assert: + that: + - not ecs_service_scale_down.changed + - ecs_service_scale_down.service.desiredCount == 0 + +- name: update task definition + ecs_taskdefinition: + containers: "{{ ecs_task_containers }}" + family: "{{ ecs_task_name }}" + task_role_arn: "{{ ecs_task_role_name }}" + state: present + register: ecs_task_update + +- name: check that initial task definition changes + assert: + that: + - ecs_task_update.changed + +- name: Enable ExecuteCommand + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + ecs_service: + state: present + name: "{{ ecs_service_name }}" + cluster: "{{ ecs_cluster_name }}" + task_definition: "{{ ecs_task_name }}:{{ ecs_task_update.taskdefinition.revision }}" + desired_count: 0 + deployment_configuration: "{{ ecs_service_deployment_configuration }}" + placement_strategy: "{{ ecs_service_placement_strategy }}" + placement_constraints: + - type: distinctInstance + health_check_grace_period_seconds: "{{ ecs_service_health_check_grace_period }}" + load_balancers: + - targetGroupArn: "{{ elb_target_group_instance.target_group_arn }}" + containerName: "{{ ecs_task_name }}" + containerPort: "{{ ecs_task_container_port }}" + role: "{{ ecs_service_role_name }}" + enable_execute_command: True + register: ecs_service_execute + +- name: check that ECS service changed + assert: + that: + - ecs_service_execute.changed + +- name: delete ECS service definition + ecs_service: + state: absent + name: "{{ ecs_service_name }}" + cluster: "{{ ecs_cluster_name }}" + wait: true + register: delete_ecs_service + +- name: assert that deleting ECS service worked + assert: + that: + - delete_ecs_service.changed + +- name: delete ECS service definition again + ecs_service: + state: absent + name: "{{ ecs_service_name }}" + cluster: "{{ ecs_cluster_name }}" + register: delete_ecs_service + +- name: assert no change + assert: + that: + - not delete_ecs_service.changed + +- name: create VPC-networked task definition with host port set to 0 (expected to fail) + ecs_taskdefinition: + containers: "{{ ecs_task_containers }}" + family: "{{ ecs_task_name }}-vpc" + state: present + network_mode: awsvpc + register: ecs_task_definition_vpc_no_host_port + ignore_errors: true + +- name: check that awsvpc task definition with host port 0 fails gracefully + assert: + that: + - ecs_task_definition_vpc_no_host_port is failed + - "'error' not in ecs_task_definition_vpc_no_host_port" + +- name: create VPC-networked task definition with host port set to 8080 + ecs_taskdefinition: + containers: "{{ ecs_task_containers }}" + family: "{{ ecs_task_name }}-vpc" + network_mode: awsvpc + state: present + vars: + ecs_task_host_port: 8080 + register: ecs_task_definition_vpc_with_host_port + +- name: obtain ECS task definition facts + ecs_taskdefinition_info: + task_definition: "{{ ecs_task_name }}-vpc:{{ ecs_task_definition_vpc_with_host_port.taskdefinition.revision }}" + register: ecs_taskdefinition_info + +- name: assert that network mode is awsvpc + assert: + that: + - "ecs_taskdefinition_info.network_mode == 'awsvpc'" + +- name: create ECS service definition with network configuration + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + ecs_service: + state: present + name: "{{ ecs_service_name }}2" + cluster: "{{ ecs_cluster_name }}" + task_definition: "{{ ecs_task_name }}-vpc:{{ ecs_task_definition_vpc_with_host_port.taskdefinition.revision }}" + desired_count: 1 + deployment_configuration: "{{ ecs_service_deployment_configuration }}" + placement_strategy: "{{ ecs_service_placement_strategy }}" + load_balancers: + - targetGroupArn: "{{ elb_target_group_ip.target_group_arn }}" + containerName: "{{ ecs_task_name }}" + containerPort: "{{ ecs_task_container_port }}" + network_configuration: + subnets: "{{ setup_subnet.results | map(attribute='subnet.id') | list }}" + security_groups: + - '{{ setup_sg.group_id }}' + register: create_ecs_service_with_vpc + +- name: assert that network configuration is correct + assert: + that: + - "'networkConfiguration' in create_ecs_service_with_vpc.service" + - "'awsvpcConfiguration' in create_ecs_service_with_vpc.service.networkConfiguration" + - "create_ecs_service_with_vpc.service.networkConfiguration.awsvpcConfiguration.subnets|length == 2" + - "create_ecs_service_with_vpc.service.networkConfiguration.awsvpcConfiguration.securityGroups|length == 1" + +- name: create ecs_service using health_check_grace_period_seconds + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + ecs_service: + name: "{{ ecs_service_name }}-mft" + cluster: "{{ ecs_cluster_name }}" + load_balancers: + - targetGroupArn: "{{ elb_target_group_instance.target_group_arn }}" + containerName: "{{ ecs_task_name }}" + containerPort: "{{ ecs_task_container_port }}" + task_definition: "{{ ecs_task_name }}:{{ ecs_task_definition.taskdefinition.revision }}" + scheduling_strategy: "REPLICA" + health_check_grace_period_seconds: 30 + desired_count: 1 + state: present + register: ecs_service_creation_hcgp + +- name: health_check_grace_period_seconds sets HealthChecGracePeriodSeconds + assert: + that: + - ecs_service_creation_hcgp.changed + - "{{ecs_service_creation_hcgp.service.healthCheckGracePeriodSeconds}} == 30" + +- name: update ecs_service using health_check_grace_period_seconds + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + ecs_service: + name: "{{ ecs_service_name }}-mft" + cluster: "{{ ecs_cluster_name }}" + load_balancers: + - targetGroupArn: "{{ elb_target_group_instance.target_group_arn }}" + containerName: "{{ ecs_task_name }}" + containerPort: "{{ ecs_task_container_port }}" + task_definition: "{{ ecs_task_name }}:{{ ecs_task_definition.taskdefinition.revision }}" + desired_count: 1 + health_check_grace_period_seconds: 10 + state: present + register: ecs_service_creation_hcgp2 + +- name: check that module returns success + assert: + that: + - ecs_service_creation_hcgp2.changed + - "{{ecs_service_creation_hcgp2.service.healthCheckGracePeriodSeconds}} == 10" + +- name: update ecs_service using REPLICA scheduling_strategy + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + ecs_service: + name: "{{ ecs_service_name }}-replica" + cluster: "{{ ecs_cluster_name }}" + load_balancers: + - targetGroupArn: "{{ elb_target_group_instance.target_group_arn }}" + containerName: "{{ ecs_task_name }}" + containerPort: "{{ ecs_task_container_port }}" + scheduling_strategy: "REPLICA" + task_definition: "{{ ecs_task_name }}:{{ ecs_task_definition.taskdefinition.revision }}" + desired_count: 1 + state: present + register: ecs_service_creation_replica + +- name: obtain facts for all ECS services in the cluster + ecs_service_info: + cluster: "{{ ecs_cluster_name }}" + details: true + events: false + register: ecs_service_info + +- name: assert that facts are useful + assert: + that: + - "'services' in ecs_service_info" + - ecs_service_info.services | length > 0 + - "'events' not in ecs_service_info.services[0]" + +- name: obtain facts for existing service in the cluster + ecs_service_info: + cluster: "{{ ecs_cluster_name }}" + service: "{{ ecs_service_name }}" + details: true + events: false + register: ecs_service_info + +- name: assert that existing service is available and running + assert: + that: + - "ecs_service_info.services|length == 1" + - "ecs_service_info.services_not_running|length == 0" + +- name: obtain facts for non-existent service in the cluster + ecs_service_info: + cluster: "{{ ecs_cluster_name }}" + service: madeup + details: true + events: false + register: ecs_service_info + +- name: assert that non-existent service is missing + assert: + that: + - "ecs_service_info.services_not_running[0].reason == 'MISSING'" + +- name: obtain specific ECS service facts + ecs_service_info: + service: "{{ ecs_service_name }}2" + cluster: "{{ ecs_cluster_name }}" + details: true + register: ecs_service_info + +- name: check that facts contain network configuration + assert: + that: + - "'networkConfiguration' in ecs_service_info.services[0]" + +- name: attempt to get facts from missing task definition + ecs_taskdefinition_info: + task_definition: "{{ ecs_task_name }}-vpc:{{ ecs_task_definition.taskdefinition.revision + 1}}" + +- name: Create another task definition with placement constraints + ecs_taskdefinition: + containers: "{{ ecs_task_containers }}" + family: "{{ ecs_task_name }}-constraints" + state: present + placement_constraints: "{{ ecs_taskdefinition_placement_constraints }}" + register: ecs_task_definition_constraints + +- name: Check that task definition has been created + assert: + that: + - ecs_task_definition_constraints is changed + - ecs_task_definition_constraints.taskdefinition.placementConstraints[0].type == "{{ ecs_taskdefinition_placement_constraints[0].type }}" + - ecs_task_definition_constraints.taskdefinition.placementConstraints[0].expression == "{{ ecs_taskdefinition_placement_constraints[0].expression }}" + +- name: Remove ecs task definition with placement constraints + ecs_taskdefinition: + containers: "{{ ecs_task_containers }}" + arn: "{{ ecs_task_definition_constraints.taskdefinition.taskDefinitionArn }}" + state: absent + register: ecs_task_definition_constraints_delete + +- name: Check that task definition has been deleted + assert: + that: + - ecs_task_definition_constraints_delete is changed + +- name: Remove ecs task definition with placement constraints again + ecs_taskdefinition: + containers: "{{ ecs_task_containers }}" + arn: "{{ ecs_task_definition_constraints.taskdefinition.taskDefinitionArn }}" + state: absent + register: ecs_task_definition_constraints_delete + +- name: Assert no change + assert: + that: + - ecs_task_definition_constraints_delete is not changed + +- name: Create ecs_service without load balancer + ecs_service: + name: "{{ ecs_service_name }}-lb" + cluster: "{{ ecs_cluster_name }}" + task_definition: "{{ ecs_task_name }}:{{ ecs_task_definition.taskdefinition.revision }}" + scheduling_strategy: "REPLICA" + desired_count: 1 + state: present + register: ecs_service_create_no_load_balancer + +- name: Check ecs_service does not have load balancer + assert: + that: + - ecs_service_create_no_load_balancer.changed + - "ecs_service_create_no_load_balancer.service.loadBalancers | length == 0" + +- name: Update ecs_service load balancer + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + ecs_service: + name: "{{ ecs_service_name }}-lb" + cluster: "{{ ecs_cluster_name }}" + load_balancers: + - targetGroupArn: "{{ elb_target_group_instance.target_group_arn }}" + containerName: "{{ ecs_task_name }}" + containerPort: "{{ ecs_task_container_port }}" + task_definition: "{{ ecs_task_name }}:{{ ecs_task_definition.taskdefinition.revision }}" + desired_count: 1 + state: present + register: ecs_service_update_load_balancer + +- name: Check ecs_service load balancer updated + assert: + that: + - ecs_service_update_load_balancer.changed + - "ecs_service_update_load_balancer.service.loadBalancers | length == 1" + - "ecs_service_update_load_balancer.service.loadBalancers[0].containerName == ecs_task_name" + - "ecs_service_update_load_balancer.service.loadBalancers[0].containerPort == ecs_task_container_port" + - "ecs_service_update_load_balancer.service.loadBalancers[0].targetGroupArn == elb_target_group_instance.target_group_arn" + +- name: Create ecs service with placement constraints + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + ecs_service: + name: "{{ ecs_service_name }}-constraint" + cluster: "{{ ecs_cluster_name }}" + load_balancers: + - targetGroupArn: "{{ elb_target_group_instance.target_group_arn }}" + containerName: "{{ ecs_task_name }}" + containerPort: "{{ ecs_task_container_port }}" + task_definition: "{{ ecs_task_name }}:{{ ecs_task_definition.taskdefinition.revision }}" + scheduling_strategy: "REPLICA" + placement_constraints: + - type: distinctInstance + desired_count: 1 + state: present + register: ecs_service_creation_constraints + +- name: Assert ecs service constraint + assert: + that: + - ecs_service_creation_constraints.changed + - "ecs_service_creation_constraints.service.placementConstraints | length == 1" + - "ecs_service_creation_constraints.service.placementConstraints[0].type == 'distinctInstance'" + +- name: > + wait until deployment is completed | + we're facing here multiple issues when testing constraints and later also placement_strategy + + >> "rolloutStateReason": "ECS deployment ecs-svc/5156684577543126023 in progress.", + constraints and placement strategies are only changeable if the rollout state is "COMPLETED" + + a) ecs_service has currently no waiter function. so this is a DIY waiter + b) the state reached never "COMPLETED" because something if wrong with the ECS EC2 Instances + or the network setup. The EC2 instance never arrived as an active instance in the cluster. + + >> no container instance met all of its requirements. Reason: No Container Instances were found in your cluster. + >> For more information, see the Troubleshooting section of the Amazon ECS Developer Guide. + >> ec2_instance networking does not work correctly, no instance available for the cluster + + Because all of this, all following tasks, that test the change of a constraint or placement stragegy are + using `force_new_deployment: true`. That ignores a) and b). + ignore_errors: true + ecs_service_info: + name: "{{ ecs_service_name }}-constraint" + cluster: "{{ ecs_cluster_name }}" + details: true + register: ECS + retries: 10 + delay: 5 + until: "ECS.services[0].deployments[0].rolloutState == 'COMPLETED'" + +- name: Update ecs service's placement constraints + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + ecs_service: + name: "{{ ecs_service_name }}-constraint" + cluster: "{{ ecs_cluster_name }}" + force_new_deployment: true + load_balancers: + - targetGroupArn: "{{ elb_target_group_instance.target_group_arn }}" + containerName: "{{ ecs_task_name }}" + containerPort: "{{ ecs_task_container_port }}" + task_definition: "{{ ecs_task_name }}:{{ ecs_task_definition.taskdefinition.revision }}" + scheduling_strategy: "REPLICA" + placement_constraints: + - type: memberOf + expression: 'attribute:ecs.instance-type == t3.micro' + desired_count: 1 + state: present + register: ecs_service_update_constraints + +- name: Assert ecs service constraint + assert: + that: + - ecs_service_update_constraints.changed + - "ecs_service_update_constraints.service.placementConstraints | length == 1" + - "ecs_service_update_constraints.service.placementConstraints[0].type == 'memberOf'" + - "ecs_service_update_constraints.service.placementConstraints[0].expression == 'attribute:ecs.instance-type == t3.micro'" + +- name: Remove ecs service's placement constraints + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + ecs_service: + name: "{{ ecs_service_name }}-constraint" + cluster: "{{ ecs_cluster_name }}" + force_new_deployment: true + load_balancers: + - targetGroupArn: "{{ elb_target_group_instance.target_group_arn }}" + containerName: "{{ ecs_task_name }}" + containerPort: "{{ ecs_task_container_port }}" + task_definition: "{{ ecs_task_name }}:{{ ecs_task_definition.taskdefinition.revision }}" + scheduling_strategy: "REPLICA" + purge_placement_constraints: true + desired_count: 1 + state: present + register: ecs_service_remove_constraints + +- name: Assert ecs service constraint + assert: + that: + - ecs_service_remove_constraints.changed + - "ecs_service_remove_constraints.service.placementConstraints | length == 0" + +- name: Create ecs service with placement strategy + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + ecs_service: + name: "{{ ecs_service_name }}-strategy" + cluster: "{{ ecs_cluster_name }}" + load_balancers: + - targetGroupArn: "{{ elb_target_group_instance.target_group_arn }}" + containerName: "{{ ecs_task_name }}" + containerPort: "{{ ecs_task_container_port }}" + task_definition: "{{ ecs_task_name }}:{{ ecs_task_definition.taskdefinition.revision }}" + scheduling_strategy: "REPLICA" + placement_strategy: + - type: binpack + field: MEMORY + desired_count: 1 + state: present + register: ecs_service_creation_strategy + +- name: Assert ecs service strategy + assert: + that: + - ecs_service_creation_strategy.changed + - "ecs_service_creation_strategy.service.placementStrategy | length == 1" + - "ecs_service_creation_strategy.service.placementStrategy[0].type == 'binpack'" + - "ecs_service_creation_strategy.service.placementStrategy[0].field == 'MEMORY'" + +- name: Update ecs service's placement strategy + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + ecs_service: + name: "{{ ecs_service_name }}-strategy" + cluster: "{{ ecs_cluster_name }}" + force_new_deployment: true + load_balancers: + - targetGroupArn: "{{ elb_target_group_instance.target_group_arn }}" + containerName: "{{ ecs_task_name }}" + containerPort: "{{ ecs_task_container_port }}" + task_definition: "{{ ecs_task_name }}:{{ ecs_task_definition.taskdefinition.revision }}" + scheduling_strategy: "REPLICA" + placement_strategy: + - type: spread + field: instanceId + desired_count: 1 + state: present + register: ecs_service_update_strategy + +- name: Assert ecs service strategy + assert: + that: + - ecs_service_update_strategy.changed + - "ecs_service_update_strategy.service.placementStrategy | length == 1" + - "ecs_service_update_strategy.service.placementStrategy[0].type == 'spread'" + - "ecs_service_update_strategy.service.placementStrategy[0].field == 'instanceId'" + +- name: Remove ecs service's placement strategy + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + ecs_service: + name: "{{ ecs_service_name }}-strategy" + cluster: "{{ ecs_cluster_name }}" + force_new_deployment: true + load_balancers: + - targetGroupArn: "{{ elb_target_group_instance.target_group_arn }}" + containerName: "{{ ecs_task_name }}" + containerPort: "{{ ecs_task_container_port }}" + task_definition: "{{ ecs_task_name }}:{{ ecs_task_definition.taskdefinition.revision }}" + scheduling_strategy: "REPLICA" + purge_placement_strategy: true + desired_count: 1 + state: present + register: ecs_service_remove_strategy + +- name: Assert ecs service strategy + assert: + that: + - ecs_service_remove_strategy.changed + - "ecs_service_remove_strategy.service.placementStrategy | length == 0" + +# ============================================================ +# Begin tests for Fargate + +- name: create Fargate VPC-networked task definition with host port set to 8080 and unsupported network mode (expected to fail) + ecs_taskdefinition: + containers: "{{ ecs_fargate_task_containers }}" + family: "{{ ecs_task_name }}-vpc" + network_mode: bridge + launch_type: FARGATE + cpu: 512 + memory: 1024 + state: present + vars: + ecs_task_host_port: 8080 + ignore_errors: true + register: ecs_fargate_task_definition_bridged_with_host_port + +- name: check that fargate task definition with bridged networking fails gracefully + assert: + that: + - ecs_fargate_task_definition_bridged_with_host_port is failed + - 'ecs_fargate_task_definition_bridged_with_host_port.msg == "To use FARGATE launch type, network_mode must be awsvpc"' + +- name: create Fargate VPC-networked task definition without CPU or Memory (expected to Fail) + ecs_taskdefinition: + containers: "{{ ecs_fargate_task_containers }}" + family: "{{ ecs_task_name }}-vpc" + network_mode: awsvpc + launch_type: FARGATE + state: present + ignore_errors: true + register: ecs_fargate_task_definition_vpc_no_mem + +- name: check that fargate task definition without memory or cpu fails gracefully + assert: + that: + - ecs_fargate_task_definition_vpc_no_mem is failed + - 'ecs_fargate_task_definition_vpc_no_mem.msg == "launch_type is FARGATE but all of the following are missing: cpu, memory"' + +- name: create Fargate VPC-networked task definition with CPU or Memory and execution role + ecs_taskdefinition: + containers: "{{ ecs_fargate_task_containers }}" + family: "{{ ecs_task_name }}-vpc" + network_mode: awsvpc + launch_type: FARGATE + cpu: 512 + memory: 1024 + execution_role_arn: "{{ iam_execution_role.arn }}" + state: present + vars: + ecs_task_host_port: 8080 + register: ecs_fargate_task_definition + +- name: create EC2 VPC-networked task definition with CPU or Memory and execution role + ecs_taskdefinition: + containers: "{{ ecs_fargate_task_containers }}" + family: "{{ ecs_task_name }}-vpc" + network_mode: awsvpc + launch_type: EC2 + cpu: 512 + memory: 1024 + execution_role_arn: "{{ iam_execution_role.arn }}" + state: present + vars: + ecs_task_host_port: 8080 + register: ecs_ec2_task_definition + +- name: check that changing task definiton launch type created a new task definition revision + assert: + that: + - ecs_fargate_task_definition.taskdefinition.revision != ecs_ec2_task_definition.taskdefinition.revision + +- name: create fargate ECS service without network config (expected to fail) + ecs_service: + state: present + name: "{{ ecs_service_name }}4" + cluster: "{{ ecs_cluster_name }}" + task_definition: "{{ ecs_task_name }}-vpc:{{ ecs_fargate_task_definition.taskdefinition.revision }}" + desired_count: 1 + deployment_configuration: "{{ ecs_service_deployment_configuration }}" + launch_type: FARGATE + register: ecs_fargate_service_network_without_awsvpc + ignore_errors: true + +- name: assert that using Fargate ECS service fails + assert: + that: + - ecs_fargate_service_network_without_awsvpc is failed + +- name: create fargate ECS service with network config + ecs_service: + state: present + name: "{{ ecs_service_name }}4" + cluster: "{{ ecs_cluster_name }}" + task_definition: "{{ ecs_task_name }}-vpc:{{ ecs_fargate_task_definition.taskdefinition.revision }}" + desired_count: 1 + deployment_configuration: "{{ ecs_service_deployment_configuration }}" + launch_type: FARGATE + network_configuration: + subnets: "{{ setup_subnet.results | map(attribute='subnet.id') | list }}" + security_groups: + - '{{ setup_sg.group_id }}' + assign_public_ip: true + register: ecs_fargate_service_network_with_awsvpc + +- name: assert that public IP assignment is enabled + assert: + that: + - 'ecs_fargate_service_network_with_awsvpc.service.networkConfiguration.awsvpcConfiguration.assignPublicIp == "ENABLED"' + +### FIX - run tasks are all failing with CannotPullContainerError in AWS +### So using wait: True fails when waiting for tasks to be started +- name: create fargate ECS task with run task + ecs_task: + operation: run + cluster: "{{ ecs_cluster_name }}" + task_definition: "{{ ecs_task_name }}-vpc" + launch_type: FARGATE + count: 1 + network_configuration: + subnets: "{{ setup_subnet.results | map(attribute='subnet.id') | list }}" + security_groups: + - '{{ setup_sg.group_id }}' + assign_public_ip: true + started_by: ansible_user + # wait: true + register: fargate_run_task_output + +- name: Assert changed + assert: + that: + - fargate_run_task_output.changed + +# - name: create fargate ECS task with run task again +# ecs_task: +# operation: run +# cluster: "{{ ecs_cluster_name }}" +# task_definition: "{{ ecs_task_name }}-vpc" +# launch_type: FARGATE +# count: 1 +# network_configuration: +# subnets: "{{ setup_subnet.results | map(attribute='subnet.id') | list }}" +# security_groups: +# - '{{ setup_sg.group_id }}' +# assign_public_ip: true +# started_by: ansible_user +# register: fargate_run_task_output + +# - name: Assert no change +# assert: +# that: +# - not fargate_run_task_output.changed + +### This does not fail +- name: create fargate ECS task with run task and tags (LF disabled) (should fail) + ecs_task: + operation: run + cluster: "{{ ecs_cluster_name }}" + task_definition: "{{ ecs_task_name }}-vpc" + launch_type: FARGATE + count: 1 + tags: + tag_key: tag_value + tag_key2: tag_value2 + network_configuration: + subnets: "{{ setup_subnet.results | map(attribute='subnet.id') | list }}" + security_groups: + - '{{ setup_sg.group_id }}' + assign_public_ip: true + started_by: ansible_user + register: fargate_run_task_output_with_tags_fail + ignore_errors: true + +# - name: assert that using Fargate ECS service fails +# assert: +# that: +# - fargate_run_task_output_with_tags_fail is failed + +- name: enable taskLongArnFormat + command: aws ecs put-account-setting --name taskLongArnFormat --value enabled + environment: + AWS_ACCESS_KEY_ID: "{{ aws_access_key }}" + AWS_SECRET_ACCESS_KEY: "{{ aws_secret_key }}" + AWS_SESSION_TOKEN: "{{ security_token | default('') }}" + AWS_DEFAULT_REGION: "{{ aws_region }}" + +- name: create fargate ECS task with run task and tags + ecs_task: + operation: run + cluster: "{{ ecs_cluster_name }}" + task_definition: "{{ ecs_task_name }}-vpc" + launch_type: FARGATE + count: 1 + tags: + tag_key: tag_value + tag_key2: tag_value2 + network_configuration: + subnets: "{{ setup_subnet.results | map(attribute='subnet.id') | list }}" + security_groups: + - '{{ setup_sg.group_id }}' + assign_public_ip: true + started_by: ansible_user + register: fargate_run_task_output_with_tags + +- name: create fargate ECS task with run task and assign public ip disable + ecs_task: + operation: run + cluster: "{{ ecs_cluster_name }}" + task_definition: "{{ ecs_task_name }}-vpc" + launch_type: FARGATE + count: 1 + network_configuration: + subnets: "{{ setup_subnet.results | map(attribute='subnet.id') | list }}" + security_groups: + - '{{ setup_sg.group_id }}' + assign_public_ip: false + started_by: ansible_user + register: fargate_run_task_output_with_assign_ip + + +# ============================================================ +# End tests for Fargate + +- name: create task definition for absent with arn regression test + ecs_taskdefinition: + containers: "{{ ecs_task_containers }}" + family: "{{ ecs_task_name }}-absent" + state: present + register: ecs_task_definition_absent_with_arn + +- name: absent task definition by arn + ecs_taskdefinition: + containers: "{{ ecs_task_containers }}" + arn: "{{ ecs_task_definition_absent_with_arn.taskdefinition.taskDefinitionArn }}" + state: absent diff --git a/ansible_collections/community/aws/tests/integration/targets/ecs_cluster/tasks/99_terminate_everything.yml b/ansible_collections/community/aws/tests/integration/targets/ecs_cluster/tasks/99_terminate_everything.yml new file mode 100644 index 000000000..7016f9e70 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ecs_cluster/tasks/99_terminate_everything.yml @@ -0,0 +1,334 @@ +- name: Announce teardown start + debug: + msg: "***** TESTING COMPLETE. COMMENCE TEARDOWN *****" + +- name: remove setup ec2 instance + ec2_instance: + instance_ids: '{{ setup_instance.instance_ids }}' + state: absent + wait: true + ignore_errors: true + +- name: obtain ECS service facts + ecs_service_info: + service: "{{ ecs_service_name }}" + cluster: "{{ ecs_cluster_name }}" + details: true + register: ecs_service_info + ignore_errors: true + +- name: scale down ECS service + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + ecs_service: + state: present + name: "{{ ecs_service_name }}" + cluster: "{{ ecs_cluster_name }}" + task_definition: "{{ ecs_service_info.services[0].taskDefinition }}" + desired_count: 0 + deployment_configuration: "{{ ecs_service_deployment_configuration }}" + placement_strategy: "{{ ecs_service_placement_strategy }}" + load_balancers: + - targetGroupArn: "{{ ecs_service_info.services[0].loadBalancers[0].targetGroupArn }}" + containerName: "{{ ecs_task_name }}" + containerPort: "{{ ecs_task_container_port }}" + ignore_errors: true + register: ecs_service_scale_down + +- name: obtain second ECS service facts + ecs_service_info: + service: "{{ ecs_service_name }}2" + cluster: "{{ ecs_cluster_name }}" + details: true + ignore_errors: true + register: ecs_service_info + +- name: scale down second ECS service + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + ecs_service: + state: present + name: "{{ ecs_service_name }}2" + cluster: "{{ ecs_cluster_name }}" + task_definition: "{{ ecs_service_info.services[0].taskDefinition }}" + desired_count: 0 + deployment_configuration: "{{ ecs_service_deployment_configuration }}" + placement_strategy: "{{ ecs_service_placement_strategy }}" + load_balancers: + - targetGroupArn: "{{ ecs_service_info.services[0].loadBalancers[0].targetGroupArn }}" + containerName: "{{ ecs_task_name }}" + containerPort: "{{ ecs_task_container_port }}" + ignore_errors: true + register: ecs_service_scale_down + +- name: scale down multifunction-test service + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + ecs_service: + name: "{{ ecs_service_name }}-mft" + cluster: "{{ ecs_cluster_name }}" + state: present + load_balancers: + - targetGroupArn: "{{ elb_target_group_instance.target_group_arn }}" + containerName: "{{ ecs_task_name }}" + containerPort: "{{ ecs_task_container_port }}" + task_definition: "{{ ecs_task_name }}:{{ ecs_task_definition.taskdefinition.revision }}" + desired_count: 0 + ignore_errors: true + register: ecs_service_scale_down + +- name: scale down scheduling_strategy service + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + ecs_service: + name: "{{ ecs_service_name }}-replica" + cluster: "{{ ecs_cluster_name }}" + state: present + load_balancers: + - targetGroupArn: "{{ elb_target_group_instance.target_group_arn }}" + containerName: "{{ ecs_task_name }}" + containerPort: "{{ ecs_task_container_port }}" + task_definition: "{{ ecs_task_name }}:{{ ecs_task_definition.taskdefinition.revision }}" + desired_count: 0 + ignore_errors: true + register: ecs_service_scale_down + +- name: scale down Fargate ECS service + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + ecs_service: + state: present + name: "{{ ecs_service_name }}4" + cluster: "{{ ecs_cluster_name }}" + task_definition: "{{ ecs_task_name }}-vpc:{{ ecs_fargate_task_definition.taskdefinition.revision }}" + desired_count: 0 + ignore_errors: true + register: ecs_service_scale_down + +- name: stop Fargate ECS tasks + ecs_task: + task: "{{ item.task[0].taskArn }}" + task_definition: "{{ ecs_task_name }}-vpc" + operation: stop + cluster: "{{ ecs_cluster_name }}" + wait: true + ignore_errors: true + with_items: + - "{{ fargate_run_task_output }}" + - "{{ fargate_run_task_output_with_tags }}" + - "{{ fargate_run_task_output_with_assign_ip }}" + - "{{ fargate_run_task_output_with_tags_fail }}" + +- name: remove ecs service + ecs_service: + state: absent + cluster: "{{ ecs_cluster_name }}" + name: "{{ ecs_service_name }}" + force_deletion: true + wait: true + ignore_errors: true + +- name: remove second ecs service + ecs_service: + state: absent + cluster: "{{ ecs_cluster_name }}" + name: "{{ ecs_service_name }}2" + force_deletion: true + wait: true + ignore_errors: true + +- name: remove mft ecs service + ecs_service: + state: absent + cluster: "{{ ecs_cluster_name }}" + name: "{{ ecs_service_name }}-mft" + force_deletion: true + wait: true + ignore_errors: true + +- name: remove constraints ecs service + ecs_service: + state: absent + cluster: "{{ ecs_cluster_name }}" + name: "{{ ecs_service_name }}-constraint" + force_deletion: true + wait: true + ignore_errors: true + +- name: remove strategy ecs service + ecs_service: + state: absent + cluster: "{{ ecs_cluster_name }}" + name: "{{ ecs_service_name }}-strategy" + force_deletion: true + wait: true + ignore_errors: true + +- name: remove scheduling_strategy ecs service + ecs_service: + state: absent + cluster: "{{ ecs_cluster_name }}" + name: "{{ ecs_service_name }}-replica" + force_deletion: true + wait: true + ignore_errors: true + +- name: remove load balancer ecs service + ecs_service: + state: absent + cluster: "{{ ecs_cluster_name }}" + name: "{{ ecs_service_name }}-lb" + force_deletion: true + wait: true + ignore_errors: true + +- name: remove fargate ECS service + ecs_service: + state: absent + name: "{{ ecs_service_name }}4" + cluster: "{{ ecs_cluster_name }}" + force_deletion: true + wait: true + ignore_errors: true + register: ecs_fargate_service_network_with_awsvpc + +- name: remove ecs task definition + ecs_taskdefinition: + containers: "{{ ecs_task_containers }}" + family: "{{ ecs_task_name }}" + revision: "{{ ecs_task_definition.taskdefinition.revision }}" + state: absent + vars: + ecs_task_host_port: 8080 + ignore_errors: true + +- name: remove ecs task definition again + ecs_taskdefinition: + containers: "{{ ecs_task_containers }}" + family: "{{ ecs_task_name }}" + revision: "{{ ecs_task_definition_again.taskdefinition.revision }}" + state: absent + vars: + ecs_task_host_port: 8080 + ignore_errors: true + +- name: remove second ecs task definition + ecs_taskdefinition: + containers: "{{ ecs_task_containers }}" + family: "{{ ecs_task_name }}-vpc" + revision: "{{ ecs_task_definition_vpc_with_host_port.taskdefinition.revision }}" + state: absent + vars: + ecs_task_host_port: 8080 + ignore_errors: true + +- name: remove fargate ecs task definition + ecs_taskdefinition: + containers: "{{ ecs_fargate_task_containers }}" + family: "{{ ecs_task_name }}-vpc" + revision: "{{ ecs_fargate_task_definition.taskdefinition.revision }}" + state: absent + ignore_errors: true + +- name: remove ec2 ecs task definition + ecs_taskdefinition: + containers: "{{ ecs_fargate_task_containers }}" + family: "{{ ecs_task_name }}-vpc" + revision: "{{ ecs_ec2_task_definition.taskdefinition.revision }}" + state: absent + ignore_errors: true + +- name: remove ecs task definition for absent with arn + ecs_taskdefinition: + containers: "{{ ecs_task_containers }}" + family: "{{ ecs_task_name }}-absent" + revision: "{{ ecs_task_definition_absent_with_arn.taskdefinition.revision }}" + state: absent + ignore_errors: true + +- name: remove load balancer + elb_application_lb: + name: "{{ ecs_load_balancer_name }}" + state: absent + wait: true + ignore_errors: true + register: elb_application_lb_remove + +- name: remove setup keypair + ec2_key: + name: '{{ resource_prefix }}_ecs_cluster' + state: absent + ignore_errors: true + +- name: remove ECS cluster + with_items: + - "{{ ecs_cluster_name }}" + - "{{ ecs_cluster_name }}-cps" + ecs_cluster: + name: "{{ item }}" + state: absent + ignore_errors: true + register: this_deletion + +- name: remove security groups + ec2_group: + name: '{{ item }}' + description: 'created by Ansible integration tests' + state: absent + vpc_id: '{{ setup_vpc.vpc.id }}' + with_items: + - '{{ resource_prefix }}_ecs_cluster-sg' + ignore_errors: true + register: this_deletion + retries: 10 + delay: 10 + until: this_deletion is not failed + +- name: remove target groups + elb_target_group: + name: "{{ item }}" + state: absent + with_items: + - "{{ ecs_target_group_name }}1" + - "{{ ecs_target_group_name }}2" + ignore_errors: true + +- name: remove IGW + ec2_vpc_igw: + state: absent + vpc_id: '{{ setup_vpc.vpc.id }}' + ignore_errors: true + +- name: remove setup subnet + ec2_vpc_subnet: + az: '{{ aws_region }}{{ item.zone }}' + vpc_id: '{{ setup_vpc.vpc.id }}' + cidr: "{{ item.cidr}}" + state: absent + with_items: + - zone: a + cidr: 10.0.1.0/24 + - zone: b + cidr: 10.0.2.0/24 + ignore_errors: true + +- name: remove setup VPC + ec2_vpc_net: + cidr_block: 10.0.0.0/16 + state: absent + name: '{{ resource_prefix }}_ecs_cluster' + ignore_errors: true + +- name: Delete IAM service role + iam_role: + name: '{{ ecs_service_role_name }}' + state: absent + delete_instance_profile: True + ignore_errors: true + +- name: Delete IAM task execution role + iam_role: + name: '{{ ecs_task_role_name }}' + state: absent + delete_instance_profile: True + ignore_errors: true diff --git a/ansible_collections/community/aws/tests/integration/targets/ecs_cluster/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/ecs_cluster/tasks/main.yml new file mode 100644 index 000000000..1d27cdc73 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ecs_cluster/tasks/main.yml @@ -0,0 +1,18 @@ +--- +- name: 'ecs_cluster integration tests' + collections: + - amazon.aws + module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + + block: + - include: 01_create_requirements.yml + - include: 10_ecs_cluster.yml + - include: 20_ecs_service.yml + + always: + - include: 99_terminate_everything.yml diff --git a/ansible_collections/community/aws/tests/integration/targets/ecs_ecr/aliases b/ansible_collections/community/aws/tests/integration/targets/ecs_ecr/aliases new file mode 100644 index 000000000..4ef4b2067 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ecs_ecr/aliases @@ -0,0 +1 @@ +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/ecs_ecr/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/ecs_ecr/defaults/main.yml new file mode 100644 index 000000000..4a9127942 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ecs_ecr/defaults/main.yml @@ -0,0 +1,22 @@ +policy: + Version: '2008-10-17' + Statement: + - Sid: new statement + Effect: Allow + Principal: "*" + Action: + - ecr:GetDownloadUrlForLayer + - ecr:BatchGetImage + - ecr:BatchCheckLayerAvailability + +lifecycle_policy: + rules: + - rulePriority: 1 + description: new policy + selection: + tagStatus: untagged + countType: sinceImagePushed + countUnit: days + countNumber: 365 + action: + type: expire diff --git a/ansible_collections/community/aws/tests/integration/targets/ecs_ecr/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/ecs_ecr/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ecs_ecr/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/ecs_ecr/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/ecs_ecr/tasks/main.yml new file mode 100644 index 000000000..e0ce4f3f6 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ecs_ecr/tasks/main.yml @@ -0,0 +1,612 @@ +--- +- module_defaults: + group/aws: + region: "{{ aws_region }}" + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + + block: + - set_fact: + ecr_name: '{{ resource_prefix }}-ecr' + + - name: get ARN of calling user + aws_caller_info: + register: aws_caller_info + + - name: create KMS key for testing + aws_kms: + alias: "{{ resource_prefix }}-ecr" + description: a key used for testing ECR + state: present + enabled: yes + key_spec: SYMMETRIC_DEFAULT + key_usage: ENCRYPT_DECRYPT + policy: "{{ lookup('template', 'kms_policy.j2') }}" + tags: + Name: "{{ resource_prefix }}-ecr" + AnsibleTest: AnsibleTestVpc + register: kms_test_key + + - name: When creating with check mode + ecs_ecr: + name: '{{ ecr_name }}' + register: result + check_mode: yes + + - name: it should skip, change and create + assert: + that: + - result is skipped + - result is changed + - result.created + + + - name: When specifying a registry that is inaccessible + ecs_ecr: + registry_id: 999999999999 + name: '{{ ecr_name }}' + register: result + ignore_errors: true + + - name: it should fail with an AccessDeniedException + assert: + that: + - result is failed + - '"AccessDeniedException" in result.msg' + + + - name: When creating a repository + ecs_ecr: + name: '{{ ecr_name }}' + register: result + + - name: it should change and create + assert: + that: + - result is changed + - result.created + + - name: it should have been configured as mutable by default + assert: + that: + - result.repository.imageTagMutability == "MUTABLE" + + - name: it should use AES256 encryption by default + assert: + that: + - result.repository.encryptionConfiguration.encryptionType == "AES256" + + - name: When pulling an existing repository that has no existing policy + ecs_ecr: + name: '{{ ecr_name }}' + register: result + + - name: it should return the repo but without a policy and not create or change + assert: + that: + - result.repository + - '"policy" not in result' + - not result.created + - not result.changed + + - name: When creating a repository that already exists in check mode + ecs_ecr: + name: '{{ ecr_name }}' + register: result + check_mode: yes + + - name: it should not skip, should not change + assert: + that: + - result is not skipped + - result is not changed + + + - name: When creating a repository that already exists + ecs_ecr: + name: '{{ ecr_name }}' + register: result + + - name: it should not change + assert: + that: + - result is not changed + + + - name: When in check mode, and deleting a policy that does not exist + ecs_ecr: + name: '{{ ecr_name }}' + purge_policy: yes + register: result + check_mode: yes + + - name: it should not skip and not change + assert: + that: + - result is not skipped + - result is not changed + + + - name: When in check mode, setting policy on a repository that has no policy + ecs_ecr: + name: '{{ ecr_name }}' + policy: '{{ policy }}' + register: result + check_mode: yes + + - name: it should skip, change and not create + assert: + that: + - result is skipped + - result is changed + - not result.created + + + - name: When setting policy on a repository that has no policy + ecs_ecr: + name: '{{ ecr_name }}' + policy: '{{ policy }}' + register: result + + - name: it should change and not create + assert: + that: + - result is changed + - not result.created + + - name: When pulling an existing repository that has an existing policy + ecs_ecr: + name: '{{ ecr_name }}' + register: result + + - name: it should return the policy but not create or change + assert: + that: + - '"policy" in result' + - not result.created + - not result.changed + + - name: When in check mode, and deleting a policy that exists + ecs_ecr: + name: '{{ ecr_name }}' + purge_policy: yes + register: result + check_mode: yes + + - name: it should skip, change but not create + assert: + that: + - result is skipped + - result is changed + - not result.created + + - name: When purging a policy that exists + ecs_ecr: + name: '{{ ecr_name }}' + purge_policy: yes + register: result + + - name: it should change and not create + assert: + that: + - result is changed + - not result.created + + + - name: When setting a policy as a string + ecs_ecr: + name: '{{ ecr_name }}' + policy: '{{ policy | to_json }}' + register: result + + - name: it should change and not create + assert: + that: + - result is changed + - not result.created + + + - name: When setting a policy to its current value + ecs_ecr: + name: '{{ ecr_name }}' + policy: '{{ policy }}' + register: result + + - name: it should not change + assert: + that: + - result is not changed + + - name: When omitting policy on a repository that has a policy + ecs_ecr: + name: '{{ ecr_name }}' + register: result + + - name: it should not change + assert: + that: + - result is not changed + + - name: When specifying both policy and purge_policy + ecs_ecr: + name: '{{ ecr_name }}' + policy: '{{ policy }}' + purge_policy: yes + register: result + ignore_errors: true + + - name: it should fail + assert: + that: + - result is failed + + + - name: When specifying invalid JSON for policy + ecs_ecr: + name: '{{ ecr_name }}' + policy: "Ceci n'est pas une JSON" + register: result + ignore_errors: true + + - name: it should fail + assert: + that: + - result is failed + + + - name: When in check mode, and purging a lifecycle policy that does not exists + ecs_ecr: + name: '{{ ecr_name }}' + purge_lifecycle_policy: yes + register: result + check_mode: yes + + - name: it should not skip and not change + assert: + that: + - not result is skipped + - not result is changed + + + - name: When in check mode, setting lifecyle policy on a repository that has no policy + ecs_ecr: + name: '{{ ecr_name }}' + lifecycle_policy: '{{ lifecycle_policy }}' + register: result + check_mode: yes + + - name: it should skip, change and not create + assert: + that: + - result is skipped + - result is changed + - not result.created + + + - name: When setting lifecycle policy on a repository that has no policy + ecs_ecr: + name: '{{ ecr_name }}' + lifecycle_policy: '{{ lifecycle_policy }}' + register: result + + - name: it should change and not create + assert: + that: + - result is changed + - not result.created + - '"lifecycle_policy" in result' + - result.lifecycle_policy.rules|length == 1 + + + - name: When in check mode, and purging a lifecyle policy that exists + ecs_ecr: + name: '{{ ecr_name }}' + purge_lifecycle_policy: yes + register: result + check_mode: yes + + - name: it should skip, change but not create + assert: + that: + - result is skipped + - result is changed + - not result.created + + + - name: When purging a lifecycle policy that exists + ecs_ecr: + name: '{{ ecr_name }}' + purge_lifecycle_policy: yes + register: result + + - name: it should change and not create + assert: + that: + - result is changed + - not result.created + + + - name: When setting a lifecyle policy as a string + ecs_ecr: + name: '{{ ecr_name }}' + lifecycle_policy: '{{ lifecycle_policy | to_json }}' + register: result + + - name: it should change and not create + assert: + that: + - result is changed + - not result.created + + + - name: When setting a lifecycle policy to its current value + ecs_ecr: + name: '{{ ecr_name }}' + lifecycle_policy: '{{ lifecycle_policy }}' + register: result + + - name: it should not change + assert: + that: + - not result is changed + + + - name: When omitting lifecycle policy on a repository that has a policy + ecs_ecr: + name: '{{ ecr_name }}' + register: result + + - name: it should not change + assert: + that: + - not result is changed + + + - name: When specifying both lifecycle_policy and purge_lifecycle_policy + ecs_ecr: + name: '{{ ecr_name }}' + lifecycle_policy: '{{ lifecycle_policy }}' + purge_lifecycle_policy: yes + register: result + ignore_errors: true + + - name: it should fail + assert: + that: + - result is failed + + + - name: When specifying invalid JSON for lifecycle policy + ecs_ecr: + name: '{{ ecr_name }}' + lifecycle_policy: "Ceci n'est pas une JSON" + register: result + ignore_errors: true + + - name: it should fail + assert: + that: + - result is failed + + + - name: When specifying an invalid document for lifecycle policy + ecs_ecr: + name: '{{ ecr_name }}' + lifecycle_policy: + rules: + - invalid: "Ceci n'est pas une rule" + register: result + ignore_errors: true + + - name: it should fail + assert: + that: + - result is failed + + + - name: When in check mode, deleting a repository that exists + ecs_ecr: + name: '{{ ecr_name }}' + state: absent + register: result + check_mode: yes + + - name: it should skip, change and not create + assert: + that: + - result is skipped + - result is changed + - not result.created + + + - name: When deleting a repository that exists + ecs_ecr: + name: '{{ ecr_name }}' + state: absent + register: result + + - name: it should change + assert: + that: + - result is changed + + + - name: When in check mode, deleting a repository that does not exist + ecs_ecr: + name: '{{ ecr_name }}' + state: absent + register: result + check_mode: yes + + - name: it should not change + assert: + that: + - result is not skipped + - result is not changed + + + - name: When deleting a repository that does not exist + ecs_ecr: + name: '{{ ecr_name }}' + state: absent + register: result + + - name: it should not change + assert: + that: + - result is not changed + + - name: When creating an immutable repository + ecs_ecr: + name: '{{ ecr_name }}' + image_tag_mutability: immutable + register: result + + - name: it should change and create + assert: + that: + - result is changed + - result.created + + - name: it should have been configured as immutable + assert: + that: + - result.repository.imageTagMutability == "IMMUTABLE" + + + - name: When configuring an existing immutable repository to be mutable in check mode + ecs_ecr: + name: '{{ ecr_name }}' + image_tag_mutability: mutable + register: result + check_mode: yes + + - name: it should skip, change and configured mutable + assert: + that: + - result is skipped + - result is changed + - result.repository.imageTagMutability == "MUTABLE" + + - name: When configuring an existing immutable repository to be mutable + ecs_ecr: + name: '{{ ecr_name }}' + image_tag_mutability: mutable + register: result + + - name: it should change and configured mutable + assert: + that: + - result is changed + - result.repository.imageTagMutability == "MUTABLE" + + - name: When configuring an already mutable repository to be mutable + ecs_ecr: + name: '{{ ecr_name }}' + image_tag_mutability: mutable + register: result + + - name: it should not change + assert: + that: + - result is not changed + + - name: enable scan on push in check mode + ecs_ecr: + name: '{{ ecr_name }}' + scan_on_push: yes + check_mode: yes + register: result + + - name: it should change + assert: + that: + - result is skipped + - result is changed + + - name: enable scan on push + ecs_ecr: + name: '{{ ecr_name }}' + scan_on_push: yes + register: result + + - name: it should change + assert: + that: + - result is changed + - result.repository.imageScanningConfiguration.scanOnPush + + - name: verify enable scan on push + ecs_ecr: + name: '{{ ecr_name }}' + scan_on_push: yes + register: result + + - name: it should not change + assert: + that: + - result is not changed + - result.repository.imageScanningConfiguration.scanOnPush + + - name: disable scan on push + ecs_ecr: + name: '{{ ecr_name }}' + scan_on_push: no + register: result + + - name: it should change + assert: + that: + - result is changed + - not result.repository.imageScanningConfiguration.scanOnPush + + - name: When modifying the encryption setting of an existing repository + ecs_ecr: + name: '{{ ecr_name }}' + encryption_configuration: + encryption_type: KMS + kms_key: '{{ kms_test_key.key_arn }}' + register: result + ignore_errors: true + + - name: it should fail + assert: + that: + - result is failed + + - name: delete repository + ecs_ecr: + name: '{{ ecr_name }}' + state: absent + + - name: When creating a repo using KMS encryption + ecs_ecr: + name: '{{ ecr_name }}' + encryption_configuration: + encryption_type: KMS + kms_key: '{{ kms_test_key.key_arn }}' + register: result + + - name: it should create the repo and use KMS encryption + assert: + that: + - result is changed + - result.repository.encryptionConfiguration.encryptionType == "KMS" + + - name: it should use the provided KMS key + assert: + that: + - result.repository.encryptionConfiguration.kmsKey == '{{ kms_test_key.key_arn }}' + + always: + + - name: Delete lingering ECR repository + ecs_ecr: + name: '{{ ecr_name }}' + state: absent + + - name: Delete KMS key + aws_kms: + key_id: '{{ kms_test_key.key_arn }}' + state: absent diff --git a/ansible_collections/community/aws/tests/integration/targets/ecs_ecr/templates/kms_policy.j2 b/ansible_collections/community/aws/tests/integration/targets/ecs_ecr/templates/kms_policy.j2 new file mode 100644 index 000000000..17108e5b3 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ecs_ecr/templates/kms_policy.j2 @@ -0,0 +1,72 @@ +{ + "Id": "key-ansible-test-policy-123", + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "Allow access for root user", + "Effect": "Allow", + "Principal": { + "AWS": "arn:aws:iam::{{ aws_caller_info.account }}:root" + }, + "Action": "kms:*", + "Resource": "*" + }, + { + "Sid": "Allow access for calling user", + "Effect": "Allow", + "Principal": { + "AWS": "{{ aws_caller_info.arn }}" + }, + "Action": [ + "kms:Create*", + "kms:Describe*", + "kms:Enable*", + "kms:List*", + "kms:Put*", + "kms:Update*", + "kms:Revoke*", + "kms:Disable*", + "kms:Get*", + "kms:Delete*", + "kms:TagResource", + "kms:UntagResource", + "kms:ScheduleKeyDeletion", + "kms:CancelKeyDeletion" + ], + "Resource": "*" + }, + { + "Sid": "Allow use of the key", + "Effect": "Allow", + "Principal": { + "AWS": "{{ aws_caller_info.arn }}" + }, + "Action": [ + "kms:Encrypt", + "kms:Decrypt", + "kms:ReEncrypt*", + "kms:GenerateDataKey*", + "kms:DescribeKey" + ], + "Resource": "*" + }, + { + "Sid": "Allow attachment of persistent resources", + "Effect": "Allow", + "Principal": { + "AWS": "{{ aws_caller_info.arn }}" + }, + "Action": [ + "kms:CreateGrant", + "kms:ListGrants", + "kms:RevokeGrant" + ], + "Resource": "*", + "Condition": { + "Bool": { + "kms:GrantIsForAWSResource": "true" + } + } + } + ] +} diff --git a/ansible_collections/community/aws/tests/integration/targets/ecs_tag/aliases b/ansible_collections/community/aws/tests/integration/targets/ecs_tag/aliases new file mode 100644 index 000000000..4413c442e --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ecs_tag/aliases @@ -0,0 +1,6 @@ +# reason: missing-policy +unsupported + +cloud/aws + +ecs_tag diff --git a/ansible_collections/community/aws/tests/integration/targets/ecs_tag/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/ecs_tag/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ecs_tag/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/ecs_tag/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/ecs_tag/tasks/main.yml new file mode 100644 index 000000000..fff9ee27d --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ecs_tag/tasks/main.yml @@ -0,0 +1,355 @@ +- module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key | default(omit) }}' + aws_secret_key: '{{ aws_secret_key | default(omit) }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region | default(omit) }}' + collections: + - amazon.aws + block: + - name: create ecs cluster + ecs_cluster: + name: "{{ resource_prefix }}" + state: present + register: cluster_info + + - name: create ecs_taskdefinition + ecs_taskdefinition: + containers: + - name: my_container + image: ubuntu + memory: 128 + family: "{{ resource_prefix }}" + state: present + register: ecs_taskdefinition_creation + + # even after deleting the cluster and recreating with a different name + # the previous service can prevent the current service from starting + # while it's in a draining state. Check the service info and sleep + # if the service does not report as inactive. + + - name: check if service is still running from a previous task + ecs_service_info: + service: "{{ resource_prefix }}" + cluster: "{{ resource_prefix }}" + details: yes + register: ecs_service_info_results + + - name: delay if the service was not inactive + pause: + seconds: 30 + when: + - ecs_service_info_results.services|length >0 + - ecs_service_info_results.services[0]['status'] != 'INACTIVE' + + - name: create ecs_service + ecs_service: + name: "{{ resource_prefix }}" + cluster: "{{ resource_prefix }}" + task_definition: "{{ resource_prefix }}" + desired_count: 1 + state: present + register: ecs_service_creation + + - name: ecs_service up + assert: + that: + - ecs_service_creation.changed + + # Test tagging cluster resource + + - name: cluster tags - Add tags to cluster + ecs_tag: + cluster_name: "{{resource_prefix}}" + resource: "{{resource_prefix}}" + resource_type: cluster + state: present + tags: + Name: "{{ resource_prefix }}" + another: foobar + register: taglist + + - name: cluster tags - tags should be there + assert: + that: + - taglist.changed == true + - taglist.added_tags.Name == "{{ resource_prefix }}" + - taglist.added_tags.another == "foobar" + + - name: cluster tags - Add tags to cluster again + ecs_tag: + cluster_name: "{{resource_prefix}}" + resource: "{{resource_prefix}}" + resource_type: cluster + state: present + tags: + Name: "{{ resource_prefix }}" + another: foobar + register: taglist + + - name: cluster tags - No change after adding again + assert: + that: + - taglist.changed == false + + - name: cluster tags - remove tag another + ecs_tag: + cluster_name: "{{resource_prefix}}" + resource: "{{resource_prefix}}" + resource_type: cluster + state: absent + tags: + another: + register: taglist + + - name: cluster tags - tag another should be gone + assert: + that: + - taglist.changed == true + - '"another" not in taglist.tags' + + - name: cluster tags - remove tag when not present + ecs_tag: + cluster_name: "{{resource_prefix}}" + resource: "{{resource_prefix}}" + resource_type: cluster + state: absent + tags: + temp: + temp_two: + register: taglist + ignore_errors: yes + + - name: cluster tags - check that there was no fail, but changed is false + assert: + that: + - taglist.failed == false + - taglist.changed == false + + + - name: cluster tags - invalid cluster name + ecs_tag: + cluster_name: "{{resource_prefix}}-foo" + resource: "{{resource_prefix}}-foo" + resource_type: cluster + state: absent + tags: + temp: + temp_two: + register: taglist + ignore_errors: yes + + - name: cluster tags - Make sure invalid clustername is handled + assert: + that: + - taglist.failed == true + - taglist.changed == false + - 'taglist.msg is regex("Failed to find cluster ansible-test-.*-foo")' + + # Test tagging service resource + + - name: services tags - Add name tag + ecs_tag: + cluster_name: "{{resource_prefix}}" + resource: "{{ecs_service_creation.service.serviceName}}" + resource_type: service + state: present + tags: + Name: "service-{{resource_prefix}}" + register: taglist + + - name: service tag - name tags should be there + assert: + that: + - taglist.changed == true + - taglist.added_tags.Name == "service-{{ resource_prefix }}" + - taglist.tags.Name == "service-{{ resource_prefix }}" + + - name: services tags - Add name tag again - see no change + ecs_tag: + cluster_name: "{{resource_prefix}}" + resource: "{{ecs_service_creation.service.serviceName}}" + resource_type: service + state: present + tags: + Name: "service-{{resource_prefix}}" + register: taglist + + - name: service tag - test adding tag twice has no effect + assert: + that: + - taglist.changed == false + - taglist.tags.Name == "service-{{ resource_prefix }}" + + - name: service tags - remove service tags + ecs_tag: + cluster_name: "{{resource_prefix}}" + resource: "{{ecs_service_creation.service.serviceName}}" + resource_type: service + state: absent + tags: + Name: + register: taglist + + - name: service tags - all tags gone + assert: + that: + - taglist.tags|list|length == 0 + - taglist.changed == true + - '"Name" not in taglist.tags' + + + # Test tagging task_definition resource + + - name: task_definition tags - Add name tag + ecs_tag: + cluster_name: "{{resource_prefix}}" + resource: "{{ecs_taskdefinition_creation.taskdefinition.family}}" + resource_type: task_definition + state: present + tags: + Name: "task_definition-{{resource_prefix}}" + register: taglist + + - name: task_definition tag - name tags should be there + assert: + that: + - taglist.changed == true + - taglist.added_tags.Name == "task_definition-{{ resource_prefix }}" + - taglist.tags.Name == "task_definition-{{ resource_prefix }}" + + - name: task_definition tags - Add name tag again - see no change + ecs_tag: + cluster_name: "{{resource_prefix}}" + resource: "{{ecs_taskdefinition_creation.taskdefinition.family}}" + resource_type: task_definition + state: present + tags: + Name: "task_definition-{{resource_prefix}}" + register: taglist + + - name: task_definition tag - test adding tag twice has no effect + assert: + that: + - taglist.changed == false + - taglist.tags.Name == "task_definition-{{ resource_prefix }}" + + - name: task_definition tags - remove task_definition tags + ecs_tag: + cluster_name: "{{resource_prefix}}" + resource: "{{ecs_taskdefinition_creation.taskdefinition.family}}" + resource_type: task_definition + state: absent + tags: + Name: + register: taglist + + - name: task_definition tags - all tags gone + assert: + that: + - taglist.tags|list|length == 0 + - taglist.changed == true + - '"Name" not in taglist.tags' + + # Test tags and tags_propagate with service creation + + - name: create ecs_service with tags + ecs_service: + name: "{{ resource_prefix }}-tags" + cluster: "{{ resource_prefix }}" + task_definition: "{{ resource_prefix }}" + desired_count: 1 + state: present + tags: + Name: foo + "Last Name": bar + register: ecs_service_creation_tags + + - name: ecs_service up + assert: + that: + - ecs_service_creation_tags.changed + + - name: service tags - tags should be there + assert: + that: + - '"Name" in ecs_service_creation_tags.service.tags' + - '"Last Name" in ecs_service_creation_tags.service.tags' + - ecs_service_creation_tags.service.tags.Name == "foo" + - ecs_service_creation_tags.service.tags["Last Name"] == "bar" + + - name: create the same ecs_service with tags + ecs_service: + name: "{{ resource_prefix }}-tags" + cluster: "{{ resource_prefix }}" + task_definition: "{{ resource_prefix }}" + desired_count: 1 + state: present + tags: + test: test + ignore_errors: yes + register: ecs_service_creation_again + + - name: check that creation again with tags failed + assert: + that: + - ecs_service_creation_again is failed + - '"msg" in ecs_service_creation_again' + + always: + - name: scale down ecs service + ecs_service: + name: "{{ resource_prefix }}" + cluster: "{{ resource_prefix }}" + task_definition: "{{ resource_prefix }}" + desired_count: 0 + state: present + ignore_errors: yes + + - name: scale down ecs service + ecs_service: + name: "{{ resource_prefix }}-tags" + cluster: "{{ resource_prefix }}" + task_definition: "{{ resource_prefix }}" + desired_count: 0 + state: present + ignore_errors: yes + + - name: pause to wait for scale down + pause: + seconds: 30 + + - name: remove ecs service + ecs_service: + name: "{{ resource_prefix }}" + cluster: "{{ resource_prefix }}" + task_definition: "{{ resource_prefix }}" + desired_count: 1 + state: absent + ignore_errors: yes + + - name: remove ecs service + ecs_service: + name: "{{ resource_prefix }}-tags" + cluster: "{{ resource_prefix }}" + task_definition: "{{ resource_prefix }}" + desired_count: 1 + state: absent + ignore_errors: yes + + - name: remove ecs task definition + ecs_taskdefinition: + containers: + - name: my_container + image: ubuntu + memory: 128 + family: "{{ resource_prefix }}" + revision: "{{ ecs_taskdefinition_creation.taskdefinition.revision }}" + state: absent + ignore_errors: yes + + - name: remove ecs cluster + ecs_cluster: + name: "{{ resource_prefix }}" + state: absent + ignore_errors: yes diff --git a/ansible_collections/community/aws/tests/integration/targets/efs/aliases b/ansible_collections/community/aws/tests/integration/targets/efs/aliases new file mode 100644 index 000000000..60c7f1ac7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/efs/aliases @@ -0,0 +1,4 @@ +cloud/aws + +efs_info +efs_tag diff --git a/ansible_collections/community/aws/tests/integration/targets/efs/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/efs/defaults/main.yml new file mode 100644 index 000000000..11df61350 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/efs/defaults/main.yml @@ -0,0 +1 @@ +efs_name: "ansible-test-{{ tiny_prefix }}"
\ No newline at end of file diff --git a/ansible_collections/community/aws/tests/integration/targets/efs/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/efs/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/efs/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/efs/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/efs/tasks/main.yml new file mode 100644 index 000000000..d2e9d4bee --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/efs/tasks/main.yml @@ -0,0 +1,553 @@ +--- +- name: 'efs integration tests' + collections: + - amazon.aws + module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + block: + + # ============================================================ + - name: Create VPC for testing + ec2_vpc_net: + name: "{{ efs_name }}-vpc" + cidr_block: 10.22.32.0/23 + tags: + Name: Ansible ec2_instance Testing VPC + tenancy: default + register: testing_vpc + + - name: Create subnet in zone A for testing + ec2_vpc_subnet: + state: present + vpc_id: "{{ testing_vpc.vpc.id }}" + cidr: 10.22.32.0/24 + az: "{{ aws_region }}a" + resource_tags: + Name: "{{ efs_name }}-subnet-a" + register: testing_subnet_a + + - name: Create subnet in zone B for testing + ec2_vpc_subnet: + state: present + vpc_id: "{{ testing_vpc.vpc.id }}" + cidr: 10.22.33.0/24 + az: "{{ aws_region }}b" + resource_tags: + Name: "{{ efs_name }}-subnet-b" + register: testing_subnet_b + + - name: Get default security group id for vpc + ec2_group_info: + filters: + vpc-id: "{{ testing_vpc.vpc.id }}" + register: sg_facts + + - set_fact: + vpc_default_sg_id: "{{sg_facts.security_groups[0].group_id}}" + + + # ============================================================ + - name: Create Efs for testing + efs: + state: present + name: "{{ efs_name }}-test-efs" + tags: + Name: "{{ efs_name }}-test-tag" + Purpose: file-storage + targets: + - subnet_id: "{{testing_subnet_a.subnet.id}}" + - subnet_id: "{{testing_subnet_b.subnet.id}}" + throughput_mode: 'bursting' + register: created_efs + + # ============================================================ + - name: Get all EFS Facts + efs_info: + register: efs_result + + - assert: + that: + - (efs_result.efs | length) >= 1 + + # ============================================================ + - name: Get EFS by creation token + efs_info: + name: "{{ efs_name }}-test-efs" + register: efs_result + + - set_fact: + efs_result_assertions: + - efs_result is not changed + - (efs_result.efs | length) == 1 + - efs_result.efs[0].creation_token == "{{ efs_name }}-test-efs" + - efs_result.efs[0].file_system_id == created_efs.efs.file_system_id + - efs_result.efs[0].number_of_mount_targets == 2 + - (efs_result.efs[0].mount_targets | length) == 2 + - efs_result.efs[0].name == "{{ efs_name }}-test-tag" + - efs_result.efs[0].tags.Name == "{{ efs_name }}-test-tag" + - efs_result.efs[0].tags.Purpose == "file-storage" + - efs_result.efs[0].encrypted == false + - efs_result.efs[0].life_cycle_state == "available" + - efs_result.efs[0].performance_mode == "generalPurpose" + - efs_result.efs[0].throughput_mode == "bursting" + - efs_result.efs[0].mount_targets[0].security_groups[0] == vpc_default_sg_id + - efs_result.efs[0].mount_targets[1].security_groups[0] == vpc_default_sg_id + + - assert: + that: "{{efs_result_assertions}}" + + # ============================================================ + - name: Get EFS by id + efs_info: + id: "{{created_efs.efs.file_system_id}}" + register: efs_result + + - assert: + that: "{{efs_result_assertions}}" + + # ============================================================ + - name: Get EFS by tag + efs_info: + tags: + Name: "{{ efs_name }}-test-tag" + register: efs_result + + - assert: + that: "{{efs_result_assertions}}" + + # ============================================================ + - name: Get EFS by target (subnet_id) + efs_info: + targets: + - "{{testing_subnet_a.subnet.id}}" + register: efs_result + + - assert: + that: "{{efs_result_assertions}}" + + # ============================================================ + - name: Get EFS by target (security_group_id) + efs_info: + targets: + - "{{vpc_default_sg_id}}" + register: efs_result + + - assert: + that: "{{efs_result_assertions}}" + + # ============================================================ + - name: Get EFS by tag and target + efs_info: + tags: + Name: "{{ efs_name }}-test-tag" + targets: + - "{{testing_subnet_a.subnet.id}}" + register: efs_result + + - assert: + that: "{{efs_result_assertions}}" + + # ============================================================ + # Not checking efs_result.efs["throughput_mode"] here as + # Efs with status "life_cycle_state": "updating" might return the previous values + - name: Update Efs to use provisioned throughput_mode + efs: + state: present + name: "{{ efs_name }}-test-efs" + tags: + Name: "{{ efs_name }}-test-tag" + Purpose: file-storage + targets: + - subnet_id: "{{testing_subnet_a.subnet.id}}" + - subnet_id: "{{testing_subnet_b.subnet.id}}" + throughput_mode: 'provisioned' + provisioned_throughput_in_mibps: 5.0 + register: efs_result + + - assert: + that: + - efs_result is changed + + # ============================================================ + - name: Efs same value for provisioned_throughput_in_mibps + efs: + state: present + name: "{{ efs_name }}-test-efs" + tags: + Name: "{{ efs_name }}-test-tag" + Purpose: file-storage + targets: + - subnet_id: "{{testing_subnet_a.subnet.id}}" + - subnet_id: "{{testing_subnet_b.subnet.id}}" + throughput_mode: 'provisioned' + provisioned_throughput_in_mibps: 5.0 + register: efs_result + + - assert: + that: + - efs_result is not changed + - efs_result.efs["throughput_mode"] == "provisioned" + - efs_result.efs["provisioned_throughput_in_mibps"] == 5.0 + + # ============================================================ + - name: Efs new value for provisioned_throughput_in_mibps + efs: + state: present + name: "{{ efs_name }}-test-efs" + tags: + Name: "{{ efs_name }}-test-tag" + Purpose: file-storage + targets: + - subnet_id: "{{testing_subnet_a.subnet.id}}" + - subnet_id: "{{testing_subnet_b.subnet.id}}" + throughput_mode: 'provisioned' + provisioned_throughput_in_mibps: 8.0 + register: efs_result + + - assert: + that: + - efs_result is changed + # Change of provisioned_throughput_in_mibps takes time + - pause: + seconds: 15 + + # ============================================================ + - name: Check new facts with provisioned mode + efs_info: + name: "{{ efs_name }}-test-efs" + register: efs_result + + - set_fact: + efs_result_assertions: + - efs_result is not changed + - efs_result.efs[0].throughput_mode == "provisioned" + - efs_result.efs[0].provisioned_throughput_in_mibps == 8.0 + - (efs_result.efs | length) == 1 + - efs_result.efs[0].creation_token == "{{ efs_name }}-test-efs" + - efs_result.efs[0].file_system_id == created_efs.efs.file_system_id + + - assert: + that: "{{efs_result_assertions}}" + + # ============================================================ + - name: Efs configure IA transition + efs: + state: present + name: "{{ efs_name }}-test-efs" + tags: + Name: "{{ efs_name }}-test-tag" + Purpose: file-storage + targets: + - subnet_id: "{{testing_subnet_a.subnet.id}}" + - subnet_id: "{{testing_subnet_b.subnet.id}}" + transition_to_ia: 60 + register: efs_result + + - assert: + that: + - efs_result is changed + + - name: Efs configure IA transition - idempotency + efs: + state: present + name: "{{ efs_name }}-test-efs" + tags: + Name: "{{ efs_name }}-test-tag" + Purpose: file-storage + targets: + - subnet_id: "{{testing_subnet_a.subnet.id}}" + - subnet_id: "{{testing_subnet_b.subnet.id}}" + transition_to_ia: 60 + register: efs_result + + - assert: + that: + - efs_result is not changed + + # ============================================================ + - name: Efs remove IA transition + efs: + state: present + name: "{{ efs_name }}-test-efs" + tags: + Name: "{{ efs_name }}-test-tag" + Purpose: file-storage + targets: + - subnet_id: "{{testing_subnet_a.subnet.id}}" + - subnet_id: "{{testing_subnet_b.subnet.id}}" + transition_to_ia: None + register: efs_result + + - assert: + that: + - efs_result is changed + + - name: Efs remove IA transition - idempotency + efs: + state: present + name: "{{ efs_name }}-test-efs" + tags: + Name: "{{ efs_name }}-test-tag" + Purpose: file-storage + targets: + - subnet_id: "{{testing_subnet_a.subnet.id}}" + - subnet_id: "{{testing_subnet_b.subnet.id}}" + transition_to_ia: None + register: efs_result + + - assert: + that: + - efs_result is not changed + + # ============================================================ + - name: Query unknown EFS by tag + efs_info: + tags: + Name: "{{ efs_name }}-unknown" + register: efs_result + + - assert: + that: + - efs_result is not changed + - (efs_result.efs | length) == 0 + + - name: Query unknown EFS by target + efs_info: + targets: + - sg-00000000000 + register: efs_result + + - assert: + that: + - efs_result is not changed + - (efs_result.efs | length) == 0 + + # ============================================================ + # efs_tag module tests + - name: Add tags to EFS filesystem in check mode + efs_tag: + state: present + resource: "{{ created_efs.efs.file_system_id }}" + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: "{{ aws_region }}" + tags: + check_mode_tag: 'this tag should not be applied' + check_mode: yes + register: efs_tag_result + + - assert: + that: + - not efs_tag_result.tags.check_mode_tag is defined + + - name: Add tags to EFS filesystem + efs_tag: + state: present + resource: "{{ created_efs.efs.file_system_id }}" + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: "{{ aws_region }}" + tags: + "Title Case": 'Hello Cruel World' + "lowercase spaced": 'hello cruel world' + CamelCase: 'SimpleCamelCase' + Env: IntegrationTests + snake_case: 'simple_snake_case' + register: efs_tag_result + + - assert: + that: + - efs_tag_result.tags.Env is defined + - efs_tag_result.tags.Env is search("IntegrationTests") + - efs_tag_result.tags.Name is defined + - efs_tag_result.tags.Name is search("{{ efs_name }}-test-tag") + - efs_tag_result.tags["CamelCase"] == 'SimpleCamelCase' + - efs_tag_result.tags["Title Case"] == 'Hello Cruel World' + - efs_tag_result.tags["lowercase spaced"] == 'hello cruel world' + - efs_tag_result.tags["snake_case"] == 'simple_snake_case' + - efs_tag_result is changed + + - name: Add/Change tags on EFS filesystem - idempotency + efs_tag: + state: present + resource: "{{ created_efs.efs.file_system_id }}" + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: "{{ aws_region }}" + tags: + Env: IntegrationTests + snake_case: 'simple_snake_case' + register: efs_tag_result + + - assert: + that: + - not efs_tag_result is changed + + - name: Remove specific EFS filesystem tag in check mode + efs_tag: + state: absent + resource: "{{ created_efs.efs.file_system_id }}" + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: "{{ aws_region }}" + tags: + snake_case: 'simple_snake_case' + check_mode: yes + register: efs_tag_result + + - assert: + that: + - efs_tag_result.tags["snake_case"] is defined + - efs_tag_result.tags["snake_case"] == 'simple_snake_case' + + - name: Change tags on EFS filesystem + efs_tag: + state: present + resource: "{{ created_efs.efs.file_system_id }}" + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: "{{ aws_region }}" + tags: + Env: OtherIntegrationTests + register: efs_tag_result + + - assert: + that: + - efs_tag_result.tags.Env is defined + - efs_tag_result.tags.Env is search("OtherIntegrationTests") + - efs_tag_result is changed + + - name: Change tags on EFS filesystem - idempotency + efs_tag: + state: present + resource: "{{ created_efs.efs.file_system_id }}" + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: "{{ aws_region }}" + tags: + Env: OtherIntegrationTests + register: efs_tag_result + + - assert: + that: + - efs_tag_result.tags.Env is defined + - efs_tag_result.tags.Env is search("OtherIntegrationTests") + - not efs_tag_result is changed + + - name: Remove specific EFS filesystem tags + efs_tag: + state: absent + resource: "{{ created_efs.efs.file_system_id }}" + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: "{{ aws_region }}" + tags: + "Title Case": 'Hello Cruel World' + "lowercase spaced": 'hello cruel world' + CamelCase: 'SimpleCamelCase' + snake_case: 'simple_snake_case' + register: efs_tag_result + + - assert: + that: + - efs_tag_result.tags.Env is defined + - efs_tag_result.tags.Env is search("IntegrationTests") + - efs_tag_result.tags.Name is defined + - efs_tag_result.tags.Name is search("{{ efs_name }}-test-tag") + - not efs_tag_result.tags["CamelCase"] is defined + - not efs_tag_result.tags["Title Case"] is defined + - not efs_tag_result.tags["lowercase spaced"] is defined + - not efs_tag_result.tags["snake_case"] is defined + + - name: Remove specific EFS filesystem tag - idempotency + efs_tag: + state: absent + resource: "{{ created_efs.efs.file_system_id }}" + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: "{{ aws_region }}" + tags: + snake_case: 'simple_snake_case' + register: efs_tag_result + + - assert: + that: + - not efs_tag_result is changed + + - name: Remove all tag on EFS filesystem + efs_tag: + state: absent + resource: "{{ created_efs.efs.file_system_id }}" + region: "{{ aws_region }}" + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + tags: {} + purge_tags: true + register: efs_tag_result + + - assert: + that: + - efs_tag_result.tags == {} + + # ============================================================ + always: + - name: Delete EFS used for tests + efs: + state: absent + name: "{{ efs_name }}-test-efs" + tags: + Name: "{{ efs_name }}-test-tag" + Purpose: file-storage + register: removed + until: removed is not failed + ignore_errors: yes + retries: 10 + + - name: Remove test subnet in zone A + ec2_vpc_subnet: + state: absent + vpc_id: "{{ testing_vpc.vpc.id }}" + cidr: 10.22.32.0/24 + az: "{{ aws_region }}a" + resource_tags: + Name: "{{ efs_name }}-subnet-a" + register: removed + until: removed is not failed + ignore_errors: yes + retries: 10 + + - name: Remove test subnet in zone B + ec2_vpc_subnet: + state: absent + vpc_id: "{{ testing_vpc.vpc.id }}" + cidr: 10.22.33.0/24 + az: "{{ aws_region }}b" + resource_tags: + Name: "{{ efs_name }}-subnet-b" + register: removed + until: removed is not failed + ignore_errors: yes + retries: 10 + + - name: remove the VPC + ec2_vpc_net: + name: "{{ efs_name }}-vpc" + cidr_block: 10.22.32.0/23 + state: absent + register: removed + until: removed is not failed + ignore_errors: yes + retries: 10 diff --git a/ansible_collections/community/aws/tests/integration/targets/eks_cluster/aliases b/ansible_collections/community/aws/tests/integration/targets/eks_cluster/aliases new file mode 100644 index 000000000..452fa0630 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/eks_cluster/aliases @@ -0,0 +1,7 @@ +# reason: missing-policy +# We don't have CI or 'unsupported' policy for AWS config +# reason: slow +# Starting up an EKS cluster is a slow process, tests can take 40 minutes +unsupported + +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/eks_cluster/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/eks_cluster/defaults/main.yml new file mode 100644 index 000000000..a7f6a40c6 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/eks_cluster/defaults/main.yml @@ -0,0 +1,34 @@ +eks_cluster_name: "{{ resource_prefix }}" +eks_cluster_short_name: "{{ lookup('password', '/dev/null chars=ascii_lowercase length=5') }}" +eks_subnets: + - zone: a + cidr: 10.0.1.0/24 + - zone: b + cidr: 10.0.2.0/24 + - zone: c + cidr: 10.0.3.0/24 + +eks_security_groups: + - name: "{{ eks_cluster_name }}-control-plane-sg" + description: "EKS Control Plane Security Group" + rules: + - group_name: "{{ eks_cluster_name }}-workers-sg" + group_desc: "EKS Worker Security Group" + ports: 443 + proto: tcp + rules_egress: + - group_name: "{{ eks_cluster_name }}-workers-sg" + group_desc: "EKS Worker Security Group" + from_port: 1025 + to_port: 65535 + proto: tcp + - name: "{{ eks_cluster_name }}-workers-sg" + description: "EKS Worker Security Group" + rules: + - group_name: "{{ eks_cluster_name }}-workers-sg" + proto: tcp + from_port: 1 + to_port: 65535 + - group_name: "{{ eks_cluster_name }}-control-plane-sg" + ports: 10250 + proto: tcp diff --git a/ansible_collections/community/aws/tests/integration/targets/eks_cluster/files/eks-trust-policy.json b/ansible_collections/community/aws/tests/integration/targets/eks_cluster/files/eks-trust-policy.json new file mode 100644 index 000000000..85cfb59dd --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/eks_cluster/files/eks-trust-policy.json @@ -0,0 +1,12 @@ +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Service": "eks.amazonaws.com" + }, + "Action": "sts:AssumeRole" + } + ] +} diff --git a/ansible_collections/community/aws/tests/integration/targets/eks_cluster/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/eks_cluster/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/eks_cluster/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/eks_cluster/tasks/full_test.yml b/ansible_collections/community/aws/tests/integration/targets/eks_cluster/tasks/full_test.yml new file mode 100644 index 000000000..e3aca2863 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/eks_cluster/tasks/full_test.yml @@ -0,0 +1,253 @@ +--- +- block: + + # If us-west-1 does become supported, change this test to use an unsupported region + # or if all regions are supported, delete this test + - name: attempt to use eks in unsupported region + aws_eks_cluster: + name: "{{ eks_cluster_name }}" + state: absent + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token }}" + region: us-west-1 + register: aws_eks_unsupported_region + ignore_errors: yes + + - name: check that aws_eks_cluster did nothing + assert: + that: + - aws_eks_unsupported_region is failed + - '"msg" in aws_eks_unsupported_region' + + - name: delete an as yet non-existent EKS cluster + aws_eks_cluster: + name: "{{ eks_cluster_name }}" + state: absent + register: aws_eks_delete_non_existent + + - name: check that aws_eks_cluster did nothing + assert: + that: + - aws_eks_delete_non_existent is not changed + + - name: ensure IAM instance role exists + iam_role: + name: aws_eks_cluster_role + assume_role_policy_document: "{{ lookup('file','eks-trust-policy.json') }}" + state: present + create_instance_profile: no + managed_policies: + - AmazonEKSServicePolicy + - AmazonEKSClusterPolicy + register: iam_role + + - name: create a VPC to work in + ec2_vpc_net: + cidr_block: 10.0.0.0/16 + state: present + name: '{{ resource_prefix }}_aws_eks' + resource_tags: + Name: '{{ resource_prefix }}_aws_eks' + register: setup_vpc + + - name: create subnets + ec2_vpc_subnet: + az: '{{ aws_region }}{{ item.zone }}' + tags: + Name: '{{ resource_prefix }}_aws_eks-subnet-{{ item.zone }}' + vpc_id: '{{ setup_vpc.vpc.id }}' + cidr: "{{ item.cidr }}" + state: present + register: setup_subnets + with_items: + - "{{ eks_subnets }}" + + - name: create security groups to use for EKS + ec2_group: + name: "{{ item.name }}" + description: "{{ item.description }}" + state: present + rules: "{{ item.rules }}" + rules_egress: "{{ item.rules_egress|default(omit) }}" + vpc_id: '{{ setup_vpc.vpc.id }}' + with_items: "{{ eks_security_groups }}" + register: setup_security_groups + + - name: create EKS cluster + aws_eks_cluster: + name: "{{ eks_cluster_name }}" + security_groups: "{{ eks_security_groups | map(attribute='name') }}" + subnets: "{{ setup_subnets.results | map(attribute='subnet.id') }}" + role_arn: "{{ iam_role.arn }}" + tags: + Name: "{{ resource_prefix }}" + another: foobar + register: eks_create + + - name: check that EKS cluster was created + assert: + that: + - eks_create is changed + - eks_create.name == eks_cluster_name + - eks_create.tags.another == "foobar" + + - name: create EKS cluster with same details but wait for it to become active + aws_eks_cluster: + name: "{{ eks_cluster_name }}" + security_groups: "{{ eks_security_groups | map(attribute='name') }}" + subnets: "{{ setup_subnets.results | map(attribute='subnet.id') }}" + role_arn: "{{ iam_role.arn }}" + wait: yes + register: eks_create + + - name: Check that EKS cluster is active and has CA and endpoint data + assert: + that: + - eks_create is not changed + - eks_create.name == eks_cluster_name + - eks_create.status == "ACTIVE" + - eks_create.certificate_authority.data is defined + - eks_create.certificate_authority.data != "" + - eks_create.endpoint is defined + - eks_create.endpoint != "" + + - name: create EKS cluster with same details but using SG ids + aws_eks_cluster: + name: "{{ eks_cluster_name }}" + security_groups: "{{ setup_security_groups.results | map(attribute='group_id') }}" + subnets: "{{ setup_subnets.results | map(attribute='subnet.id') }}" + role_arn: "{{ iam_role.arn }}" + register: eks_create + + - name: check that EKS cluster did not change + assert: + that: + - eks_create is not changed + - eks_create.name == eks_cluster_name + + - name: remove EKS cluster, waiting until complete + aws_eks_cluster: + name: "{{ eks_cluster_name }}" + state: absent + wait: yes + register: eks_delete + + - name: check that EKS cluster was removed + assert: + that: + - eks_delete is changed + + - name: create EKS cluster with same details but wait for it to become active + aws_eks_cluster: + name: "{{ eks_cluster_name }}" + security_groups: "{{ eks_security_groups | map(attribute='name') }}" + subnets: "{{ setup_subnets.results | map(attribute='subnet.id') }}" + role_arn: "{{ iam_role.arn }}" + wait: yes + register: eks_create + + - name: check that EKS cluster was created + assert: + that: + - eks_create is changed + - eks_create.name == eks_cluster_name + + - name: remove EKS cluster, without waiting this time + aws_eks_cluster: + name: "{{ eks_cluster_name }}" + state: absent + register: eks_delete + + - name: check that EKS cluster remove has started + assert: + that: + - eks_delete is changed + + - name: create EKS cluster with short name + aws_eks_cluster: + name: "{{ eks_cluster_short_name }}" + security_groups: "{{ eks_security_groups | map(attribute='name') }}" + subnets: "{{ setup_subnets.results | map(attribute='subnet.id') }}" + role_arn: "{{ iam_role.arn }}" + register: eks_create + + - name: check that EKS cluster was created with short name + assert: + that: + - eks_create is changed + - eks_create.name == eks_cluster_short_name + - eks_create is not failed + + - name: remove EKS cluster with short name + aws_eks_cluster: + name: "{{ eks_cluster_short_name }}" + state: absent + wait: yes + register: eks_delete + + always: + - name: Announce teardown start + debug: + msg: "***** TESTING COMPLETE. COMMENCE TEARDOWN *****" + + - name: remove EKS cluster + aws_eks_cluster: + name: "{{ eks_cluster_name }}" + state: absent + wait: yes + register: eks_delete + ignore_errors: yes + + - name: remove EKS cluster + aws_eks_cluster: + name: "{{ eks_cluster_short_name }}" + state: absent + wait: yes + register: eks_delete + ignore_errors: yes + + - debug: + msg: "{{ eks_security_groups|reverse|list }}" + + - name: create list of all additional EKS security groups + set_fact: + additional_eks_sg: + - name: "{{ eks_cluster_name }}-workers-sg" + + - name: set all security group rule lists to empty to remove circular dependency + ec2_group: + name: "{{ item.name }}" + description: "{{ item.description }}" + state: present + rules: [] + rules_egress: [] + purge_rules: yes + purge_rules_egress: yes + vpc_id: '{{ setup_vpc.vpc.id }}' + with_items: "{{ eks_security_groups }}" + ignore_errors: yes + + - name: remove security groups + ec2_group: + name: '{{ item.name }}' + state: absent + vpc_id: '{{ setup_vpc.vpc.id }}' + with_items: "{{ eks_security_groups|reverse|list + additional_eks_sg }}" + ignore_errors: yes + + - name: remove setup subnet + ec2_vpc_subnet: + az: '{{ aws_region }}{{ item.zone }}' + vpc_id: '{{ setup_vpc.vpc.id }}' + cidr: "{{ item.cidr}}" + state: absent + with_items: "{{ eks_subnets }}" + ignore_errors: yes + + - name: remove setup VPC + ec2_vpc_net: + cidr_block: 10.0.0.0/16 + state: absent + name: '{{ resource_prefix }}_aws_eks' + ignore_errors: yes diff --git a/ansible_collections/community/aws/tests/integration/targets/eks_cluster/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/eks_cluster/tasks/main.yml new file mode 100644 index 000000000..61aa32cd1 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/eks_cluster/tasks/main.yml @@ -0,0 +1,12 @@ +--- +- name: 'ecs_cluster integration tests' + collections: + - amazon.aws + module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + block: + - include_tasks: full_test.yml diff --git a/ansible_collections/community/aws/tests/integration/targets/eks_fargate_profile/aliases b/ansible_collections/community/aws/tests/integration/targets/eks_fargate_profile/aliases new file mode 100644 index 000000000..f4d138881 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/eks_fargate_profile/aliases @@ -0,0 +1,2 @@ +time=24m +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/eks_fargate_profile/defaults/main.yaml b/ansible_collections/community/aws/tests/integration/targets/eks_fargate_profile/defaults/main.yaml new file mode 100644 index 000000000..005db76ae --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/eks_fargate_profile/defaults/main.yaml @@ -0,0 +1,45 @@ +eks_cluster_name: "{{ resource_prefix }}" +eks_fargate_profile_name_a: fp-template-a +eks_fargate_profile_name_b: fp-template-b + +selectors: + - namespace: "fp-default" + +tags: + foo: bar + env: test + +eks_subnets: + - zone: a + cidr: 10.0.1.0/24 + type: private + tag: internal-elb + - zone: b + cidr: 10.0.2.0/24 + type: public + tag: elb + +eks_security_groups: + - name: "{{ eks_cluster_name }}-control-plane-sg" + description: "EKS Control Plane Security Group" + rules: + - group_name: "{{ eks_cluster_name }}-workers-sg" + group_desc: "EKS Worker Security Group" + ports: 443 + proto: tcp + rules_egress: + - group_name: "{{ eks_cluster_name }}-workers-sg" + group_desc: "EKS Worker Security Group" + from_port: 1025 + to_port: 65535 + proto: tcp + - name: "{{ eks_cluster_name }}-workers-sg" + description: "EKS Worker Security Group" + rules: + - group_name: "{{ eks_cluster_name }}-workers-sg" + proto: tcp + from_port: 1 + to_port: 65535 + - group_name: "{{ eks_cluster_name }}-control-plane-sg" + ports: 10250 + proto: tcp diff --git a/ansible_collections/community/aws/tests/integration/targets/eks_fargate_profile/files/eks-fargate-profile-trust-policy.json b/ansible_collections/community/aws/tests/integration/targets/eks_fargate_profile/files/eks-fargate-profile-trust-policy.json new file mode 100644 index 000000000..eec12ce49 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/eks_fargate_profile/files/eks-fargate-profile-trust-policy.json @@ -0,0 +1,12 @@ +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Service": "eks-fargate-pods.amazonaws.com" + }, + "Action": "sts:AssumeRole" + } + ] +}
\ No newline at end of file diff --git a/ansible_collections/community/aws/tests/integration/targets/eks_fargate_profile/files/eks-trust-policy.json b/ansible_collections/community/aws/tests/integration/targets/eks_fargate_profile/files/eks-trust-policy.json new file mode 100644 index 000000000..85cfb59dd --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/eks_fargate_profile/files/eks-trust-policy.json @@ -0,0 +1,12 @@ +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Service": "eks.amazonaws.com" + }, + "Action": "sts:AssumeRole" + } + ] +} diff --git a/ansible_collections/community/aws/tests/integration/targets/eks_fargate_profile/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/eks_fargate_profile/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/eks_fargate_profile/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/eks_fargate_profile/tasks/cleanup_eks_cluster.yml b/ansible_collections/community/aws/tests/integration/targets/eks_fargate_profile/tasks/cleanup_eks_cluster.yml new file mode 100644 index 000000000..d30761fa3 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/eks_fargate_profile/tasks/cleanup_eks_cluster.yml @@ -0,0 +1,79 @@ +- name: Delete IAM role + iam_role: + name: "{{ iam_role_fargate.role_name }}" + state: absent + ignore_errors: true + +- name: remove EKS cluster + aws_eks_cluster: + name: '{{ eks_cluster_name }}' + state: absent + wait: 'yes' + register: eks_delete + ignore_errors: 'yes' +- name: create list of all additional EKS security groups + set_fact: + additional_eks_sg: + - name: '{{ eks_cluster_name }}-workers-sg' + +- name: set all security group rule lists to empty to remove circular dependency + ec2_group: + name: '{{ item.name }}' + description: '{{ item.description }}' + state: present + rules: [] + rules_egress: [] + purge_rules: 'yes' + purge_rules_egress: 'yes' + vpc_id: '{{ setup_vpc.vpc.id }}' + with_items: '{{ eks_security_groups }}' + ignore_errors: 'yes' + +- name: remove security groups + ec2_group: + name: '{{ item.name }}' + state: absent + vpc_id: '{{ setup_vpc.vpc.id }}' + with_items: '{{ eks_security_groups|reverse|list + additional_eks_sg }}' + ignore_errors: 'yes' + +- name: remove Route Tables + ec2_vpc_route_table: + state: absent + vpc_id: '{{ setup_vpc.vpc.id }}' + route_table_id: '{{ item }}' + lookup: id + ignore_errors: 'yes' + with_items: + - '{{ public_route_table.route_table.route_table_id }}' + - '{{ nat_route_table.route_table.route_table_id }}' + +- name: remove setup Nat Gateway + amazon.aws.ec2_vpc_nat_gateway: + state: absent + nat_gateway_id: '{{ setup_nat_gateway.nat_gateway_id}}' + release_eip: 'yes' + wait: 'yes' + ignore_errors: 'yes' + +- name: remove setup subnet + ec2_vpc_subnet: + az: '{{ aws_region }}{{ item.zone }}' + vpc_id: '{{ setup_vpc.vpc.id }}' + cidr: '{{ item.cidr}}' + state: absent + with_items: '{{ eks_subnets }}' + ignore_errors: 'yes' + +- name: remove Internet Gateway + amazon.aws.ec2_vpc_igw: + state: absent + vpc_id: '{{ setup_vpc.vpc.id}}' + ignore_errors: 'yes' + +- name: remove setup VPC + ec2_vpc_net: + cidr_block: 10.0.0.0/16 + state: absent + name: '{{ resource_prefix }}_aws_eks' + ignore_errors: 'yes' diff --git a/ansible_collections/community/aws/tests/integration/targets/eks_fargate_profile/tasks/create_eks_cluster.yml b/ansible_collections/community/aws/tests/integration/targets/eks_fargate_profile/tasks/create_eks_cluster.yml new file mode 100644 index 000000000..d5affa5b5 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/eks_fargate_profile/tasks/create_eks_cluster.yml @@ -0,0 +1,97 @@ +# Create a EKS Cluster to test Fargate Profile +# This space was a copy by aws_eks_cluster integration test +- name: ensure IAM instance role exists + iam_role: + name: ansible-test-aws_eks_cluster_role + assume_role_policy_document: '{{ lookup(''file'',''eks-trust-policy.json'') }}' + state: present + create_instance_profile: 'no' + managed_policies: + - AmazonEKSServicePolicy + - AmazonEKSClusterPolicy + register: iam_role + +- name: create a VPC to work in + ec2_vpc_net: + cidr_block: 10.0.0.0/16 + state: present + name: '{{ resource_prefix }}_aws_eks' + resource_tags: + Name: '{{ resource_prefix }}_aws_eks' + register: setup_vpc + +- name: create subnets + ec2_vpc_subnet: + az: '{{ aws_region }}{{ item.zone }}' + tags: '{ "Name": "{{ resource_prefix }}_aws_eks-subnet-{{ item.type }}-{{ item.zone }}", "kubernetes.io/role/{{ item.tag }}": "1" }' + vpc_id: '{{ setup_vpc.vpc.id }}' + cidr: '{{ item.cidr }}' + state: present + register: setup_subnets + with_items: + - '{{ eks_subnets }}' + +- name: create Internet Gateway + amazon.aws.ec2_vpc_igw: + vpc_id: '{{ setup_vpc.vpc.id }}' + state: present + tags: + Name: '{{ resource_prefix }}_IGW' + register: setup_igw + +- name: Set up public subnet route table + community.aws.ec2_vpc_route_table: + vpc_id: '{{ setup_vpc.vpc.id }}' + tags: + Name: Public + subnets: '{{ setup_subnets.results|selectattr(''subnet.tags.Name'', ''contains'', ''public'') | map(attribute=''subnet.id'') }}' + routes: + - dest: 0.0.0.0/0 + gateway_id: '{{ setup_igw.gateway_id }}' + register: public_route_table + +- name: create Natgateway + amazon.aws.ec2_vpc_nat_gateway: + if_exist_do_not_create: yes + state: present + subnet_id: '{{ (setup_subnets.results|selectattr(''subnet.tags.Name'', ''contains'', ''public'') | map(attribute=''subnet.id''))[0] }}' + wait: true + tags: + Name: '{{ resource_prefix }}_NAT' + register: setup_nat_gateway + +- name: Set up NAT-protected route table + community.aws.ec2_vpc_route_table: + vpc_id: '{{ setup_vpc.vpc.id }}' + tags: + Name: Internal + subnets: '{{setup_subnets.results|selectattr(''subnet.tags.Name'', ''contains'', ''private'') | map(attribute=''subnet.id'') }}' + routes: + - dest: 0.0.0.0/0 + nat_gateway_id: '{{ setup_nat_gateway.nat_gateway_id }}' + register: nat_route_table + +- name: create security groups to use for EKS + ec2_group: + name: '{{ item.name }}' + description: '{{ item.description }}' + state: present + rules: '{{ item.rules }}' + rules_egress: '{{ item.rules_egress|default(omit) }}' + vpc_id: '{{ setup_vpc.vpc.id }}' + with_items: '{{ eks_security_groups }}' + register: setup_security_groups + +- name: create EKS cluster + aws_eks_cluster: + name: '{{ eks_cluster_name }}' + security_groups: '{{ eks_security_groups | map(attribute=''name'') }}' + subnets: '{{ setup_subnets.results | map(attribute=''subnet.id'') }}' + role_arn: '{{ iam_role.arn }}' + wait: true + register: eks_create + +- name: check that EKS cluster was created + assert: + that: + - eks_create.name == eks_cluster_name
\ No newline at end of file diff --git a/ansible_collections/community/aws/tests/integration/targets/eks_fargate_profile/tasks/full_test.yml b/ansible_collections/community/aws/tests/integration/targets/eks_fargate_profile/tasks/full_test.yml new file mode 100644 index 000000000..b992125b3 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/eks_fargate_profile/tasks/full_test.yml @@ -0,0 +1,429 @@ +# Creating dependencies +- name: create IAM instance role + iam_role: + name: 'ansible-test-aws_eks_fargate_profile' + assume_role_policy_document: '{{ lookup(''file'',''eks-fargate-profile-trust-policy.json'') }}' + state: present + create_instance_profile: no + managed_policies: + - AmazonEKSFargatePodExecutionRolePolicy + register: iam_role_fargate + +- name: Pause a few seconds to ensure IAM role is available to next task + pause: + seconds: 10 + +# Test - Try Create Fargate profile in non existent EKS +- name: attempt to create fargate profile in non existent eks + eks_fargate_profile: + name: '{{ eks_fargate_profile_name_a }}' + state: present + cluster_name: fake_cluster + role_arn: '{{ iam_role_fargate.arn }}' + subnets: >- + {{setup_subnets.results|selectattr('subnet.tags.Name', 'contains', + 'private') | map(attribute='subnet.id') }} + selectors: '{{ selectors }}' + register: aws_eks_non_existent_eks + ignore_errors: 'yes' + +- name: check that eks_fargate_profile did nothing + assert: + that: + - aws_eks_non_existent_eks is failed + +# Test - Try deleting a non-existent fargate profile +- name: delete an as yet non-existent fargate profile + eks_fargate_profile: + name: fake_profile + cluster_name: '{{ eks_cluster_name }}' + state: absent + role_arn: '{{ iam_role_fargate.arn }}' + subnets: >- + {{setup_subnets.results|selectattr('subnet.tags.Name', 'contains', + 'private') | map(attribute='subnet.id') }} + selectors: '{{ selectors }}' + register: eks_fargate_profile_delete_non_existent + ignore_errors: 'yes' + +- name: check that eks_fargate_profile did nothing + assert: + that: + - eks_fargate_profile_delete_non_existent is not changed + +# Test - Try Create Fargate Profile A with wait and public subnet +- name: Try create Fargate Profile with public subnets + eks_fargate_profile: + name: '{{ eks_fargate_profile_name_a }}' + state: present + cluster_name: '{{ eks_cluster_name }}' + role_arn: '{{ iam_role_fargate.arn }}' + subnets: >- + {{setup_subnets.results|selectattr('subnet.tags.Name', 'contains', + 'public') | map(attribute='subnet.id') }} + selectors: '{{ selectors }}' + wait: true + register: eks_fargate_profile_create + ignore_errors: 'yes' + +- name: check that eks_fargate_profile is not created + assert: + that: + - not eks_fargate_profile_create.changed + - eks_fargate_profile_create.msg.endswith("provided in Fargate Profile is not a private subnet") + +# Create Fargate_profile with wait +- name: create Fargate Profile with wait (check mode) + eks_fargate_profile: + name: '{{ eks_fargate_profile_name_a }}' + state: present + cluster_name: '{{ eks_cluster_name }}' + role_arn: '{{ iam_role_fargate.arn }}' + subnets: >- + {{setup_subnets.results|selectattr('subnet.tags.Name', 'contains', + 'private') | map(attribute='subnet.id') }} + selectors: '{{ selectors }}' + wait: true + tags: '{{ tags }}' + register: eks_fargate_profile_create + ignore_errors: 'yes' + check_mode: True + +- name: check that eks_fargate_profile_create is changed (check mode) + assert: + that: + - eks_fargate_profile_create.changed + +- name: create Fargate Profile with wait + eks_fargate_profile: + name: '{{ eks_fargate_profile_name_a }}' + state: present + cluster_name: '{{ eks_cluster_name }}' + role_arn: '{{ iam_role_fargate.arn }}' + subnets: >- + {{setup_subnets.results|selectattr('subnet.tags.Name', 'contains', + 'private') | map(attribute='subnet.id') }} + selectors: '{{ selectors }}' + wait: true + tags: '{{ tags }}' + register: eks_fargate_profile_create + ignore_errors: 'yes' + +- name: check that eks_fargate_profile is created + assert: + that: + - eks_fargate_profile_create.changed + - eks_fargate_profile_create.status == "ACTIVE" + +- name: Try create same Fargate Profile with wait (idempotency)(check mode) + eks_fargate_profile: + name: '{{ eks_fargate_profile_name_a }}' + state: present + cluster_name: '{{ eks_cluster_name }}' + role_arn: '{{ iam_role_fargate.arn }}' + subnets: >- + {{setup_subnets.results|selectattr('subnet.tags.Name', 'contains', + 'private') | map(attribute='subnet.id') }} + selectors: '{{ selectors }}' + wait: true + tags: '{{ tags }}' + register: eks_fargate_profile_create + ignore_errors: 'yes' + check_mode: True + +- name: check that eks_fargate_profile_create is not changed (idempotency)(check mode) + assert: + that: + - not eks_fargate_profile_create.changed + +- name: Try create same Fargate Profile with wait (idempotency) + eks_fargate_profile: + name: '{{ eks_fargate_profile_name_a }}' + state: present + cluster_name: '{{ eks_cluster_name }}' + role_arn: '{{ iam_role_fargate.arn }}' + subnets: >- + {{setup_subnets.results|selectattr('subnet.tags.Name', 'contains', + 'private') | map(attribute='subnet.id') }} + selectors: '{{ selectors }}' + wait: true + tags: '{{ tags }}' + register: eks_fargate_profile_create + ignore_errors: 'yes' + +- name: check that eks_fargate_profile_create is not changed (idempotency) + assert: + that: + - not eks_fargate_profile_create.changed + +# Update tags Fargate_profile +- name: update tags in Fargate Profile a with wait (check mode) + eks_fargate_profile: + name: '{{ eks_fargate_profile_name_a }}' + state: present + cluster_name: '{{ eks_cluster_name }}' + role_arn: '{{ iam_role_fargate.arn }}' + subnets: >- + {{setup_subnets.results|selectattr('subnet.tags.Name', 'contains', + 'private') | map(attribute='subnet.id') }} + selectors: '{{ selectors }}' + wait: true + tags: + env: test + test: foo + register: eks_fargate_profile_update + ignore_errors: 'yes' + check_mode: True + +- name: check that eks_fargate_profile_update is changed (check mode) + assert: + that: + - eks_fargate_profile_update.changed + +- name: update tags in Fargate Profile a with wait + eks_fargate_profile: + name: '{{ eks_fargate_profile_name_a }}' + state: present + cluster_name: '{{ eks_cluster_name }}' + role_arn: '{{ iam_role_fargate.arn }}' + subnets: >- + {{setup_subnets.results|selectattr('subnet.tags.Name', 'contains', + 'private') | map(attribute='subnet.id') }} + selectors: '{{ selectors }}' + wait: true + tags: + env: test + test: foo + register: eks_fargate_profile_update + ignore_errors: 'yes' + +- name: check that eks_fargate_profile_update is changed + assert: + that: + - eks_fargate_profile_update.changed + +- name: Try update tags again in Fargate Profile a with wait (idempotency)(check mode) + eks_fargate_profile: + name: '{{ eks_fargate_profile_name_a }}' + state: present + cluster_name: '{{ eks_cluster_name }}' + role_arn: '{{ iam_role_fargate.arn }}' + subnets: >- + {{setup_subnets.results|selectattr('subnet.tags.Name', 'contains', + 'private') | map(attribute='subnet.id') }} + selectors: '{{ selectors }}' + wait: true + tags: + env: test + test: foo + register: eks_fargate_profile_update + ignore_errors: 'yes' + check_mode: True + +- name: check that eks_fargate_profile_update is not changed (idempotency)(check mode) + assert: + that: + - not eks_fargate_profile_update.changed + +- name: Try update tags again in Fargate Profile a with wait (idempotency) + eks_fargate_profile: + name: '{{ eks_fargate_profile_name_a }}' + state: present + cluster_name: '{{ eks_cluster_name }}' + role_arn: '{{ iam_role_fargate.arn }}' + subnets: >- + {{setup_subnets.results|selectattr('subnet.tags.Name', 'contains', + 'private') | map(attribute='subnet.id') }} + selectors: '{{ selectors }}' + wait: true + tags: + env: test + test: foo + register: eks_fargate_profile_update + ignore_errors: 'yes' + +- name: check that eks_fargate_profile_update is not changed (idempotency) + assert: + that: + - not eks_fargate_profile_update.changed + +# Create Fargate Profile b without wait +- name: create Fargate Profile b without wait (check mode) + eks_fargate_profile: + name: '{{ eks_fargate_profile_name_b }}' + state: present + cluster_name: '{{ eks_cluster_name }}' + role_arn: '{{ iam_role_fargate.arn }}' + subnets: >- + {{setup_subnets.results|selectattr('subnet.tags.Name', 'contains', + 'private') | map(attribute='subnet.id') }} + selectors: '{{ selectors }}' + register: eks_fargate_profile_create_b + ignore_errors: 'yes' + check_mode: True + +- name: check that eks_fargate_profile is created (check mode) + assert: + that: + - eks_fargate_profile_create_b.changed + +- name: create Fargate Profile b without wait + eks_fargate_profile: + name: '{{ eks_fargate_profile_name_b }}' + state: present + cluster_name: '{{ eks_cluster_name }}' + role_arn: '{{ iam_role_fargate.arn }}' + subnets: >- + {{setup_subnets.results|selectattr('subnet.tags.Name', 'contains', + 'private') | map(attribute='subnet.id') }} + selectors: '{{ selectors }}' + register: eks_fargate_profile_create_b + ignore_errors: 'yes' + +- name: check that eks_fargate_profile is created + assert: + that: + - eks_fargate_profile_create_b.changed + - eks_fargate_profile_create_b.status == "CREATING" + +- name: create Fargate Profile b without wait (idempotency)(check mode) + eks_fargate_profile: + name: '{{ eks_fargate_profile_name_b }}' + state: present + cluster_name: '{{ eks_cluster_name }}' + role_arn: '{{ iam_role_fargate.arn }}' + subnets: >- + {{setup_subnets.results|selectattr('subnet.tags.Name', 'contains', + 'private') | map(attribute='subnet.id') }} + selectors: '{{ selectors }}' + register: eks_fargate_profile_create_b + ignore_errors: 'yes' + check_mode: True + +- name: check that eks_fargate_profile_b is not changed (idempotency)(check mode) + assert: + that: + - not eks_fargate_profile_create_b.changed + +- name: create Fargate Profile without wait (idempotency) + eks_fargate_profile: + name: '{{ eks_fargate_profile_name_b }}' + state: present + cluster_name: '{{ eks_cluster_name }}' + role_arn: '{{ iam_role_fargate.arn }}' + subnets: >- + {{setup_subnets.results|selectattr('subnet.tags.Name', 'contains', + 'private') | map(attribute='subnet.id') }} + selectors: '{{ selectors }}' + register: eks_fargate_profile_create_b + ignore_errors: 'yes' + +- name: check that eks_fargate_profile is not changed (idempotency) + assert: + that: + - not eks_fargate_profile_create_b.changed + +# Delete Fargate Profile A without wait (test check_profiles_status function) +- name: delete a fargate profile a (check mode) + eks_fargate_profile: + name: '{{ eks_fargate_profile_name_a }}' + cluster_name: '{{ eks_cluster_name }}' + state: absent + register: eks_fargate_profile_delete + check_mode: True + +- name: check that eks_fargate_profile a is changed (check mode) + assert: + that: + - eks_fargate_profile_delete.changed + +- name: delete a fargate profile + eks_fargate_profile: + name: '{{ eks_fargate_profile_name_a }}' + cluster_name: '{{ eks_cluster_name }}' + state: absent + register: eks_fargate_profile_delete + +- name: check that eks_fargate_profile is deleted + assert: + that: + - eks_fargate_profile_delete.changed + +- name: delete a fargate profile a (idempotency)(check mode) + eks_fargate_profile: + name: '{{ eks_fargate_profile_name_a }}' + cluster_name: '{{ eks_cluster_name }}' + state: absent + register: eks_fargate_profile_delete + check_mode: True + +- name: check that eks_fargate_profile did nothing (idempotency)(check mode) + assert: + that: + - not eks_fargate_profile_delete.changed + +- name: delete a fargate profile a (idempotency) + eks_fargate_profile: + name: '{{ eks_fargate_profile_name_a }}' + cluster_name: '{{ eks_cluster_name }}' + state: absent + register: eks_fargate_profile_delete + +- name: check that eks_fargate_profile did nothing (idempotency) + assert: + that: + - not eks_fargate_profile_delete.changed + +# Delete Fargate Profile b with wait +- name: delete a fargate profile b (check mode) + eks_fargate_profile: + name: '{{ eks_fargate_profile_name_b }}' + cluster_name: '{{ eks_cluster_name }}' + state: absent + wait: true + register: eks_fargate_profile_b_delete + check_mode: True + +- name: check that eks_fargate_profile is deleted (check mode) + assert: + that: + - eks_fargate_profile_b_delete.changed + +- name: delete a fargate profile b + eks_fargate_profile: + name: '{{ eks_fargate_profile_name_b }}' + cluster_name: '{{ eks_cluster_name }}' + state: absent + wait: true + register: eks_fargate_profile_b_delete + +- name: check that eks_fargate_profile is deleted + assert: + that: + - eks_fargate_profile_b_delete.changed + +- name: delete a fargate profile b (idempotency)(check mode) + eks_fargate_profile: + name: '{{ eks_fargate_profile_name_b }}' + cluster_name: '{{ eks_cluster_name }}' + state: absent + wait: true + register: eks_fargate_profile_b_delete + +- name: check that eks_fargate_profile did nothing (idempotency)(check mode) + assert: + that: + - not eks_fargate_profile_b_delete.changed + +- name: delete a fargate profile b (idempotency) + eks_fargate_profile: + name: '{{ eks_fargate_profile_name_b }}' + cluster_name: '{{ eks_cluster_name }}' + state: absent + wait: true + register: eks_fargate_profile_b_delete + +- name: check that eks_fargate_profile did nothing (idempotency) + assert: + that: + - not eks_fargate_profile_b_delete.changed
\ No newline at end of file diff --git a/ansible_collections/community/aws/tests/integration/targets/eks_fargate_profile/tasks/main.yaml b/ansible_collections/community/aws/tests/integration/targets/eks_fargate_profile/tasks/main.yaml new file mode 100644 index 000000000..77298dc81 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/eks_fargate_profile/tasks/main.yaml @@ -0,0 +1,15 @@ +--- +- name: 'eks_cluster integration tests' + collections: + - amazon.aws + module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + block: + - include_tasks: create_eks_cluster.yml + - include_tasks: full_test.yml + always: + - include_tasks: cleanup_eks_cluster.yml
\ No newline at end of file diff --git a/ansible_collections/community/aws/tests/integration/targets/eks_nodegroup/aliases b/ansible_collections/community/aws/tests/integration/targets/eks_nodegroup/aliases new file mode 100644 index 000000000..0b84301d7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/eks_nodegroup/aliases @@ -0,0 +1 @@ +cloud/aws
\ No newline at end of file diff --git a/ansible_collections/community/aws/tests/integration/targets/eks_nodegroup/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/eks_nodegroup/defaults/main.yml new file mode 100644 index 000000000..b473c6bb5 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/eks_nodegroup/defaults/main.yml @@ -0,0 +1,36 @@ +eks_cluster_name: "{{ resource_prefix }}" +eks_nodegroup_name_a: ng-template-a +eks_nodegroup_name_lt: ng-template-lt + +eks_subnets: + - zone: a + cidr: 10.0.1.0/24 + type: private + - zone: b + cidr: 10.0.2.0/24 + type: public + +eks_security_groups: + - name: "{{ eks_cluster_name }}-control-plane-sg" + description: "EKS Control Plane Security Group" + rules: + - group_name: "{{ eks_cluster_name }}-workers-sg" + group_desc: "EKS Worker Security Group" + ports: 443 + proto: tcp + rules_egress: + - group_name: "{{ eks_cluster_name }}-workers-sg" + group_desc: "EKS Worker Security Group" + from_port: 1025 + to_port: 65535 + proto: tcp + - name: "{{ eks_cluster_name }}-workers-sg" + description: "EKS Worker Security Group" + rules: + - group_name: "{{ eks_cluster_name }}-workers-sg" + proto: tcp + from_port: 1 + to_port: 65535 + - group_name: "{{ eks_cluster_name }}-control-plane-sg" + ports: 10250 + proto: tcp
\ No newline at end of file diff --git a/ansible_collections/community/aws/tests/integration/targets/eks_nodegroup/files/eks-nodegroup-trust-policy.json b/ansible_collections/community/aws/tests/integration/targets/eks_nodegroup/files/eks-nodegroup-trust-policy.json new file mode 100644 index 000000000..fa43376a6 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/eks_nodegroup/files/eks-nodegroup-trust-policy.json @@ -0,0 +1,12 @@ +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Service": "ec2.amazonaws.com" + }, + "Action": "sts:AssumeRole" + } + ] +} diff --git a/ansible_collections/community/aws/tests/integration/targets/eks_nodegroup/files/eks-trust-policy.json b/ansible_collections/community/aws/tests/integration/targets/eks_nodegroup/files/eks-trust-policy.json new file mode 100644 index 000000000..85cfb59dd --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/eks_nodegroup/files/eks-trust-policy.json @@ -0,0 +1,12 @@ +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Service": "eks.amazonaws.com" + }, + "Action": "sts:AssumeRole" + } + ] +} diff --git a/ansible_collections/community/aws/tests/integration/targets/eks_nodegroup/tasks/cleanup.yml b/ansible_collections/community/aws/tests/integration/targets/eks_nodegroup/tasks/cleanup.yml new file mode 100644 index 000000000..ff841f0f5 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/eks_nodegroup/tasks/cleanup.yml @@ -0,0 +1,83 @@ +- name: remove launch template + ec2_launch_template: + name: "{{ resource_prefix }}-simple" + state: absent + ignore_errors: yes + +- name: remove EKS cluster + aws_eks_cluster: + name: '{{ eks_cluster_name }}' + state: absent + wait: 'yes' + register: eks_delete + ignore_errors: 'yes' +- name: create list of all additional EKS security groups + set_fact: + additional_eks_sg: + - name: '{{ eks_cluster_name }}-workers-sg' + +- name: set all security group rule lists to empty to remove circular dependency + ec2_group: + name: '{{ item.name }}' + description: '{{ item.description }}' + state: present + rules: [] + rules_egress: [] + purge_rules: 'yes' + purge_rules_egress: 'yes' + vpc_id: '{{ setup_vpc.vpc.id }}' + with_items: '{{ eks_security_groups }}' + ignore_errors: 'yes' + +- name: remove security groups + ec2_group: + name: '{{ item.name }}' + state: absent + vpc_id: '{{ setup_vpc.vpc.id }}' + with_items: '{{ eks_security_groups|reverse|list + additional_eks_sg }}' + ignore_errors: 'yes' + +- name: Delete securitygroup for node access + amazon.aws.ec2_security_group: + name: 'ansible-test-eks_nodegroup' + description: "SSH access" + vpc_id: '{{ setup_vpc.vpc.id }}' + rules: [] + state: absent + +- name: Delete Keypair for Access to Nodegroup nodes + amazon.aws.ec2_key: + name: "ansible-test-eks_nodegroup" + state: absent + +- name: remove Route Tables + ec2_vpc_route_table: + state: absent + vpc_id: '{{ setup_vpc.vpc.id }}' + route_table_id: '{{ item }}' + lookup: id + with_items: + - '{{ public_route_table.route_table.route_table_id }}' + ignore_errors: 'yes' + +- name: remove setup subnet + ec2_vpc_subnet: + az: '{{ aws_region }}{{ item.zone }}' + vpc_id: '{{ setup_vpc.vpc.id }}' + cidr: '{{ item.cidr}}' + state: absent + with_items: '{{ eks_subnets }}' + ignore_errors: 'yes' + +- name: remove Internet Gateway + amazon.aws.ec2_vpc_igw: + state: absent + vpc_id: '{{ setup_vpc.vpc.id}}' + ignore_errors: 'yes' + +- name: remove setup VPC + ec2_vpc_net: + cidr_block: 10.0.0.0/16 + state: absent + name: '{{ resource_prefix }}_aws_eks' + ignore_errors: 'yes'
\ No newline at end of file diff --git a/ansible_collections/community/aws/tests/integration/targets/eks_nodegroup/tasks/dependecies.yml b/ansible_collections/community/aws/tests/integration/targets/eks_nodegroup/tasks/dependecies.yml new file mode 100644 index 000000000..dd6efd27a --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/eks_nodegroup/tasks/dependecies.yml @@ -0,0 +1,125 @@ +# Create a EKS Cluster to test Nodegroup +# This space was a copy by aws_eks_cluster integration test +- name: ensure IAM instance role exists + iam_role: + name: ansible-test-eks_cluster_role + assume_role_policy_document: '{{ lookup(''file'',''eks-trust-policy.json'') }}' + state: present + create_instance_profile: 'no' + managed_policies: + - AmazonEKSServicePolicy + - AmazonEKSClusterPolicy + register: iam_role + +- name: create a VPC to work in + ec2_vpc_net: + cidr_block: 10.0.0.0/16 + state: present + name: '{{ resource_prefix }}_aws_eks' + resource_tags: + Name: '{{ resource_prefix }}_aws_eks' + register: setup_vpc + +- name: create subnets + ec2_vpc_subnet: + az: '{{ aws_region }}{{ item.zone }}' + tags: '{ "Name": "{{ resource_prefix }}_aws_eks-subnet-{{ item.type }}-{{ item.zone }}" }' + vpc_id: '{{ setup_vpc.vpc.id }}' + cidr: '{{ item.cidr }}' + map_public: 'yes' + state: present + register: setup_subnets + with_items: + - '{{ eks_subnets }}' + +- name: create Internet Gateway + amazon.aws.ec2_vpc_igw: + vpc_id: '{{ setup_vpc.vpc.id }}' + state: present + tags: + Name: '{{ resource_prefix }}_IGW' + register: setup_igw + +- name: Set up public subnet route table + community.aws.ec2_vpc_route_table: + vpc_id: '{{ setup_vpc.vpc.id }}' + tags: + Name: EKS + subnets: '{{ setup_subnets.results | map(attribute=''subnet.id'') }}' + routes: + - dest: 0.0.0.0/0 + gateway_id: '{{ setup_igw.gateway_id }}' + register: public_route_table + +- name: create security groups to use for EKS + ec2_group: + name: '{{ item.name }}' + description: '{{ item.description }}' + state: present + rules: '{{ item.rules }}' + rules_egress: '{{ item.rules_egress|default(omit) }}' + vpc_id: '{{ setup_vpc.vpc.id }}' + with_items: '{{ eks_security_groups }}' + register: setup_security_groups + +- name: create EKS cluster + aws_eks_cluster: + name: '{{ eks_cluster_name }}' + security_groups: '{{ eks_security_groups | map(attribute=''name'') }}' + subnets: '{{ setup_subnets.results | map(attribute=''subnet.id'') }}' + role_arn: '{{ iam_role.arn }}' + wait: true + register: eks_create + +- name: check that EKS cluster was created + assert: + that: + - eks_create.name == eks_cluster_name + +# Dependecies to eks nodegroup +- name: create IAM instance role + iam_role: + name: 'ansible-test-eks_nodegroup' + assume_role_policy_document: '{{ lookup(''file'',''eks-nodegroup-trust-policy.json'') }}' + state: present + create_instance_profile: no + managed_policies: + - AmazonEKSWorkerNodePolicy + - AmazonEC2ContainerRegistryReadOnly + - AmazonEKS_CNI_Policy + register: iam_role_eks_nodegroup + +- name: Pause a few seconds to ensure IAM role is available to next task + pause: + seconds: 10 + +# Dependecies to test eks nodegroup with launch_template +- name: create instance template + ec2_launch_template: + name: "{{ resource_prefix }}-simple" + instance_type: t3.nano + register: lt_default + +- name: update simple instance template + ec2_launch_template: + name: "{{ resource_prefix }}-simple" + default_version: 1 + instance_type: t3.micro + register: lt + +- name: Create securitygroup for node access + amazon.aws.ec2_security_group: + name: 'ansible-test-eks_nodegroup' + description: "SSH access" + vpc_id: '{{ setup_vpc.vpc.id }}' + rules: + - proto: tcp + ports: + - 22 + cidr_ip: 0.0.0.0/0 + register: securitygroup_eks_nodegroup + +- name: Create Keypair for Access to Nodegroup nodes + amazon.aws.ec2_key: + name: "ansible-test-eks_nodegroup" + register: ec2_key_eks_nodegroup diff --git a/ansible_collections/community/aws/tests/integration/targets/eks_nodegroup/tasks/full_test.yml b/ansible_collections/community/aws/tests/integration/targets/eks_nodegroup/tasks/full_test.yml new file mode 100644 index 000000000..dcb35d2d1 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/eks_nodegroup/tasks/full_test.yml @@ -0,0 +1,586 @@ +# Test - Try Create Nodegroup in non existent EKS +- name: Test - attempt to create Nodegroup in non existent eks + eks_nodegroup: + name: '{{ eks_nodegroup_name_a }}' + state: present + cluster_name: fake_cluster + node_role: '{{ iam_role_eks_nodegroup.arn }}' + subnets: '{{ setup_subnets.results | map(attribute=''subnet.id'') }}' + scaling_config: + min_size: 1 + max_size: 2 + desired_size: 1 + disk_size: 20 + instance_types: 't3.micro' + ami_type: 'AL2_x86_64' + update_config: + max_unavailable_percentage: 50 + labels: + 'test': 'test' + taints: + - key: 'test' + value: 'test' + effect: 'NO_SCHEDULE' + capacity_type: 'on_demand' + register: eks_nodegroup_result + ignore_errors: 'yes' + +- name: check that eks_nodegroup did nothing + assert: + that: + - eks_nodegroup_result is failed + - '"msg" in eks_nodegroup_result' + +# Test - Try Create Nodegroup with parameters conflict + +- name: Test - attempt to create Nodegroup with parameters conflict + eks_nodegroup: + name: '{{ eks_nodegroup_name_a }}' + state: present + cluster_name: '{{ eks_cluster_name }}' + node_role: '{{ iam_role_eks_nodegroup.arn }}' + subnets: '{{ setup_subnets.results | map(attribute=''subnet.id'') }}' + wait: True + launch_template: + id: 'lt-0824c69cafa69ac81' + disk_size: 30 + register: eks_nodegroup_result + ignore_errors: 'yes' + +- name: check that eks_nodegroup did nothing + assert: + that: + - eks_nodegroup_result is failed + +############################################### +## CREATE NODEGROUP + +- name: create nodegroup (check mode) + eks_nodegroup: + name: '{{ eks_nodegroup_name_a }}' + state: present + cluster_name: '{{ eks_cluster_name }}' + node_role: '{{ iam_role_eks_nodegroup.arn }}' + subnets: '{{ setup_subnets.results | map(attribute=''subnet.id'') }}' + scaling_config: + min_size: 1 + max_size: 3 + desired_size: 2 + disk_size: 30 + instance_types: ['t3.small'] + ami_type: 'AL2_x86_64' + update_config: + max_unavailable_percentage: 50 + labels: + 'env': 'test' + taints: + - key: 'env' + value: 'test' + effect: 'PREFER_NO_SCHEDULE' + capacity_type: 'SPOT' + tags: + 'foo': 'bar' + remote_access: + ec2_ssh_key: "{{ ec2_key_eks_nodegroup.key.name }}" + source_sg: + - "{{ securitygroup_eks_nodegroup.group_id }}" + wait: True + register: eks_nodegroup_result + check_mode: True + +- name: check that eks_nodegroup is created (check mode) + assert: + that: + - eks_nodegroup_result.changed + +- name: create nodegroup + eks_nodegroup: + name: '{{ eks_nodegroup_name_a }}' + state: present + cluster_name: '{{ eks_cluster_name }}' + node_role: '{{ iam_role_eks_nodegroup.arn }}' + subnets: '{{ setup_subnets.results | map(attribute=''subnet.id'') }}' + scaling_config: + min_size: 1 + max_size: 3 + desired_size: 2 + disk_size: 30 + instance_types: ['t3.small'] + ami_type: 'AL2_x86_64' + update_config: + max_unavailable_percentage: 50 + labels: + 'env': 'test' + taints: + - key: 'env' + value: 'test' + effect: 'PREFER_NO_SCHEDULE' + capacity_type: 'SPOT' + tags: + 'foo': 'bar' + remote_access: + ec2_ssh_key: "{{ ec2_key_eks_nodegroup.key.name }}" + source_sg: + - "{{ securitygroup_eks_nodegroup.group_id }}" + wait: True + register: eks_nodegroup_result + +- name: check that eks_nodegroup is created + assert: + that: + - eks_nodegroup_result.changed + +- name: create nodegroup (idempotency)(check mode) + eks_nodegroup: + name: '{{ eks_nodegroup_name_a }}' + state: present + cluster_name: '{{ eks_cluster_name }}' + node_role: '{{ iam_role_eks_nodegroup.arn }}' + subnets: '{{ setup_subnets.results | map(attribute=''subnet.id'') }}' + scaling_config: + min_size: 1 + max_size: 3 + desired_size: 2 + disk_size: 30 + instance_types: ['t3.small'] + ami_type: 'AL2_x86_64' + update_config: + max_unavailable_percentage: 50 + labels: + 'env': 'test' + taints: + - key: 'env' + value: 'test' + effect: 'PREFER_NO_SCHEDULE' + capacity_type: 'SPOT' + tags: + 'foo': 'bar' + remote_access: + ec2_ssh_key: "{{ ec2_key_eks_nodegroup.key.name }}" + source_sg: + - "{{ securitygroup_eks_nodegroup.group_id }}" + wait: True + register: eks_nodegroup_result + check_mode: True + +- name: check that eks_nodegroup is not changed (idempotency)(check mode) + assert: + that: + - not eks_nodegroup_result.changed + +- name: create nodegroup (idempotency) + eks_nodegroup: + name: '{{ eks_nodegroup_name_a }}' + state: present + cluster_name: '{{ eks_cluster_name }}' + node_role: '{{ iam_role_eks_nodegroup.arn }}' + subnets: '{{ setup_subnets.results | map(attribute=''subnet.id'') }}' + scaling_config: + min_size: 1 + max_size: 3 + desired_size: 2 + disk_size: 30 + instance_types: ['t3.small'] + ami_type: 'AL2_x86_64' + update_config: + max_unavailable_percentage: 50 + labels: + 'env': 'test' + taints: + - key: 'env' + value: 'test' + effect: 'PREFER_NO_SCHEDULE' + capacity_type: 'SPOT' + tags: + 'foo': 'bar' + remote_access: + ec2_ssh_key: "{{ ec2_key_eks_nodegroup.key.name }}" + source_sg: + - "{{ securitygroup_eks_nodegroup.group_id }}" + wait: True + register: eks_nodegroup_result + check_mode: True + +- name: check that eks_nodegroup is not changed (idempotency) + assert: + that: + - not eks_nodegroup_result.changed + +######################################################### +## TRY UPDATE PARAMETERS + +- name: Test - attempt to update Nodegroup with not permited parameter (disk_size) + eks_nodegroup: + name: '{{ eks_nodegroup_name_a }}' + state: present + cluster_name: '{{ eks_cluster_name }}' + node_role: '{{ iam_role_eks_nodegroup.arn }}' + subnets: '{{ setup_subnets.results | map(attribute=''subnet.id'') }}' + wait: True + disk_size: 40 + ignore_errors: 'yes' + register: eks_nodegroup_result + +- name: check that eks_nodegroup did nothing + assert: + that: + - eks_nodegroup_result is failed + +- name: Test - attempt to update Nodegroup with not permited parameter (instance_types) + eks_nodegroup: + name: '{{ eks_nodegroup_name_a }}' + state: present + cluster_name: '{{ eks_cluster_name }}' + node_role: '{{ iam_role_eks_nodegroup.arn }}' + subnets: '{{ setup_subnets.results | map(attribute=''subnet.id'') }}' + wait: True + instance_types: ['t3.small'] + ignore_errors: 'yes' + register: eks_nodegroup_result + +- name: check that eks_nodegroup did nothing + assert: + that: + - eks_nodegroup_result is failed + +######################################################### +## UPDATE NODEGROUP + +- name: update nodegroup (check mode) + eks_nodegroup: + name: '{{ eks_nodegroup_name_a }}' + state: present + cluster_name: '{{ eks_cluster_name }}' + node_role: '{{ iam_role_eks_nodegroup.arn }}' + subnets: '{{ setup_subnets.results | map(attribute=''subnet.id'') }}' + scaling_config: + min_size: 1 + max_size: 4 + desired_size: 2 + disk_size: 30 + instance_types: ['t3.small'] + ami_type: 'AL2_x86_64' + update_config: + max_unavailable_percentage: 50 + labels: + 'env': 'changeit' + taints: + - key: 'env' + value: 'test' + effect: 'PREFER_NO_SCHEDULE' + capacity_type: 'SPOT' + tags: + 'foo': 'bar' + remote_access: + ec2_ssh_key: "{{ ec2_key_eks_nodegroup.key.name }}" + source_sg: + - "{{ securitygroup_eks_nodegroup.group_id }}" + wait: True + register: eks_nodegroup_result + check_mode: True + +- name: check that eks_nodegroup is updated (check mode) + assert: + that: + - eks_nodegroup_result.changed + +- name: update nodegroup + eks_nodegroup: + name: '{{ eks_nodegroup_name_a }}' + state: present + cluster_name: '{{ eks_cluster_name }}' + node_role: '{{ iam_role_eks_nodegroup.arn }}' + subnets: '{{ setup_subnets.results | map(attribute=''subnet.id'') }}' + scaling_config: + min_size: 1 + max_size: 4 + desired_size: 2 + disk_size: 30 + instance_types: ['t3.small'] + ami_type: 'AL2_x86_64' + update_config: + max_unavailable_percentage: 50 + labels: + 'env': 'changeit' + taints: + - key: 'env' + value: 'test' + effect: 'PREFER_NO_SCHEDULE' + capacity_type: 'SPOT' + tags: + 'foo': 'bar' + remote_access: + ec2_ssh_key: "{{ ec2_key_eks_nodegroup.key.name }}" + source_sg: + - "{{ securitygroup_eks_nodegroup.group_id }}" + wait: True + register: eks_nodegroup_result + +- name: check that eks_nodegroup is updated + assert: + that: + - eks_nodegroup_result.changed + +- name: update nodegroup (idempotency)(check mode) + eks_nodegroup: + name: '{{ eks_nodegroup_name_a }}' + state: present + cluster_name: '{{ eks_cluster_name }}' + node_role: '{{ iam_role_eks_nodegroup.arn }}' + subnets: '{{ setup_subnets.results | map(attribute=''subnet.id'') }}' + scaling_config: + min_size: 1 + max_size: 4 + desired_size: 2 + disk_size: 30 + instance_types: ['t3.small'] + ami_type: 'AL2_x86_64' + update_config: + max_unavailable_percentage: 50 + labels: + 'env': 'changeit' + taints: + - key: 'env' + value: 'test' + effect: 'PREFER_NO_SCHEDULE' + capacity_type: 'SPOT' + tags: + 'foo': 'bar' + remote_access: + ec2_ssh_key: "{{ ec2_key_eks_nodegroup.key.name }}" + source_sg: + - "{{ securitygroup_eks_nodegroup.group_id }}" + wait: True + register: eks_nodegroup_result + check_mode: True + +- name: check that eks_nodegroup is not changed (idempotency)(check mode) + assert: + that: + - not eks_nodegroup_result.changed + +- name: update nodegroup (idempotency) + eks_nodegroup: + name: '{{ eks_nodegroup_name_a }}' + state: present + cluster_name: '{{ eks_cluster_name }}' + node_role: '{{ iam_role_eks_nodegroup.arn }}' + subnets: '{{ setup_subnets.results | map(attribute=''subnet.id'') }}' + scaling_config: + min_size: 1 + max_size: 4 + desired_size: 2 + disk_size: 30 + instance_types: ['t3.small'] + ami_type: 'AL2_x86_64' + update_config: + max_unavailable_percentage: 50 + labels: + 'env': 'changeit' + taints: + - key: 'env' + value: 'test' + effect: 'PREFER_NO_SCHEDULE' + capacity_type: 'SPOT' + tags: + 'foo': 'bar' + remote_access: + ec2_ssh_key: "{{ ec2_key_eks_nodegroup.key.name }}" + source_sg: + - "{{ securitygroup_eks_nodegroup.group_id }}" + wait: True + register: eks_nodegroup_result + +- name: check that eks_nodegroup is not changed (idempotency) + assert: + that: + - not eks_nodegroup_result.changed + + +######################################################### +## DELETE NODEGROUP + +- name: delete nodegroup (check mode) + eks_nodegroup: + name: '{{ eks_nodegroup_name_a }}' + state: absent + cluster_name: '{{ eks_cluster_name }}' + node_role: '{{ iam_role_eks_nodegroup.arn }}' + register: eks_nodegroup_result + check_mode: True + +- name: check that eks_nodegroup is delete (check mode) + assert: + that: + - eks_nodegroup_result.changed + +- name: delete nodegroup + eks_nodegroup: + name: '{{ eks_nodegroup_name_a }}' + state: absent + cluster_name: '{{ eks_cluster_name }}' + register: eks_nodegroup_result + +- name: check that eks_nodegroup is deleted + assert: + that: + - eks_nodegroup_result.changed + +- name: delete nodegroup (idempotency)(check mode) + eks_nodegroup: + name: '{{ eks_nodegroup_name_a }}' + state: absent + cluster_name: '{{ eks_cluster_name }}' + register: eks_nodegroup_result + check_mode: True + +- name: check that eks_nodegroup is not changed (idempotency)(check mode) + assert: + that: + - eks_nodegroup_result is not changed + +- name: delete nodegroup (idempotency) + eks_nodegroup: + name: '{{ eks_nodegroup_name_a }}' + state: absent + cluster_name: '{{ eks_cluster_name }}' + register: eks_nodegroup_result + check_mode: True + +- name: check that eks_nodegroup is not changed (idempotency) + assert: + that: + - eks_nodegroup_result is not changed + +######################################################### +## CREATE WITH LAUCH_TEMPLATE + +- name: create nodegroup with Lauch Template (check mode) + eks_nodegroup: + name: '{{ eks_nodegroup_name_lt }}' + state: present + cluster_name: '{{ eks_cluster_name }}' + node_role: '{{ iam_role_eks_nodegroup.arn }}' + subnets: '{{ setup_subnets.results | map(attribute=''subnet.id'') }}' + launch_template: + name: '{{ lt.template.launch_template_name }}' + wait: True + register: eks_nodegroup_result + check_mode: True + +- name: check that eks_nodegroup is created (check mode) + assert: + that: + - eks_nodegroup_result.changed + +- name: create nodegroup with Lauch Template + eks_nodegroup: + name: '{{ eks_nodegroup_name_lt }}' + state: present + cluster_name: '{{ eks_cluster_name }}' + node_role: '{{ iam_role_eks_nodegroup.arn }}' + subnets: '{{ setup_subnets.results | map(attribute=''subnet.id'') }}' + launch_template: + name: '{{ lt.template.launch_template_name }}' + wait: True + register: eks_nodegroup_result + +- name: check that eks_nodegroup is created + assert: + that: + - eks_nodegroup_result.changed + +- name: create nodegroup with Lauch Template (idempotency)(check mode) + eks_nodegroup: + name: '{{ eks_nodegroup_name_lt }}' + state: present + cluster_name: '{{ eks_cluster_name }}' + node_role: '{{ iam_role_eks_nodegroup.arn }}' + subnets: '{{ setup_subnets.results | map(attribute=''subnet.id'') }}' + launch_template: + name: '{{ lt.template.launch_template_name }}' + wait: True + register: eks_nodegroup_result + check_mode: True + +- name: check that eks_nodegroup is not changed (idempotency)(check mode) + assert: + that: + - not eks_nodegroup_result.changed + +- name: create nodegroup with Lauch Template (idempotency) + eks_nodegroup: + name: '{{ eks_nodegroup_name_lt }}' + state: present + cluster_name: '{{ eks_cluster_name }}' + node_role: '{{ iam_role_eks_nodegroup.arn }}' + subnets: '{{ setup_subnets.results | map(attribute=''subnet.id'') }}' + launch_template: + name: '{{ lt.template.launch_template_name }}' + wait: True + register: eks_nodegroup_result + check_mode: True + +- name: check that eks_nodegroup is not changed (idempotency) + assert: + that: + - not eks_nodegroup_result.changed + +######################################################### +## DELETE NODEGROUP + +- name: delete launch_template nodegroup (check mode) + eks_nodegroup: + name: '{{ eks_nodegroup_name_lt }}' + state: absent + cluster_name: '{{ eks_cluster_name }}' + node_role: '{{ iam_role_eks_nodegroup.arn }}' + wait: True + register: eks_nodegroup_result + check_mode: True + +- name: check that eks_nodegroup is delete (check mode) + assert: + that: + - eks_nodegroup_result.changed + +- name: delete launch_template nodegroup + eks_nodegroup: + name: '{{ eks_nodegroup_name_lt }}' + state: absent + cluster_name: '{{ eks_cluster_name }}' + wait: True + register: eks_nodegroup_result + +- name: check that eks_nodegroup is deleted + assert: + that: + - eks_nodegroup_result.changed + +- name: delete launch_template nodegroup (idempotency)(check mode) + eks_nodegroup: + name: '{{ eks_nodegroup_name_lt }}' + state: absent + cluster_name: '{{ eks_cluster_name }}' + wait: True + register: eks_nodegroup_result + check_mode: True + +- name: check that eks_nodegroup is not changed (idempotency)(check mode) + assert: + that: + - eks_nodegroup_result is not changed + +- name: delete launch_template nodegroup (idempotency) + eks_nodegroup: + name: '{{ eks_nodegroup_name_lt }}' + state: absent + cluster_name: '{{ eks_cluster_name }}' + wait: True + register: eks_nodegroup_result + check_mode: True + +- name: check that eks_nodegroup is not changed (idempotency) + assert: + that: + - eks_nodegroup_result is not changed
\ No newline at end of file diff --git a/ansible_collections/community/aws/tests/integration/targets/eks_nodegroup/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/eks_nodegroup/tasks/main.yml new file mode 100644 index 000000000..9f896bec6 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/eks_nodegroup/tasks/main.yml @@ -0,0 +1,16 @@ +--- +- name: 'eks_nodegroup integration tests' + collections: + - amazon.aws + - amozon.community + module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + block: + - include_tasks: dependecies.yml + - include_tasks: full_test.yml + always: + - include_tasks: cleanup.yml
\ No newline at end of file diff --git a/ansible_collections/community/aws/tests/integration/targets/elasticache/aliases b/ansible_collections/community/aws/tests/integration/targets/elasticache/aliases new file mode 100644 index 000000000..2d3ad74d7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elasticache/aliases @@ -0,0 +1,6 @@ +# Sometimes hit AWS capacity issues - InsufficientCacheClusterCapacity +# https://docs.aws.amazon.com/AmazonElastiCache/latest/red-ug/ErrorMessages.html#ErrorMessages.INSUFFICIENT_CACHE_CLUSTER_CAPACITY +unstable + +cloud/aws +elasticache_info diff --git a/ansible_collections/community/aws/tests/integration/targets/elasticache/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/elasticache/defaults/main.yml new file mode 100644 index 000000000..1e1f60c90 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elasticache/defaults/main.yml @@ -0,0 +1,10 @@ +--- + +vpc_name: "{{ resource_prefix }}-elasticache-test-vpc" +vpc_seed: '{{ resource_prefix }}' +vpc_cidr_prefix: '10.{{ 256 | random(seed=vpc_seed) }}' + +elasticache_redis_sg_name: "{{ resource_prefix }}-elasticache-test-redis-sg" +elasticache_redis_test_name: "{{ resource_prefix }}-redis-test" +elasticache_subnet_group_name: "{{ resource_prefix }}-elasticache-test-vpc-subnet-group" +elasticache_redis_port: 6379 diff --git a/ansible_collections/community/aws/tests/integration/targets/elasticache/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/elasticache/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elasticache/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/elasticache/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/elasticache/tasks/main.yml new file mode 100644 index 000000000..31ae3d9cf --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elasticache/tasks/main.yml @@ -0,0 +1,209 @@ +--- + +- name: Integration testing for the elasticache module + module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + collections: + - amazon.aws + block: + # == Dependency setup == + + - name: Create VPC to launch Elasticache instances into + ec2_vpc_net: + name: "{{ vpc_name }}" + cidr_block: "{{ vpc_cidr_prefix }}.0.0/16" + state: present + register: elasticache_vpc + + - name: Create subnet 1 in this VPC to launch Elasticache instances into + ec2_vpc_subnet: + vpc_id: "{{ elasticache_vpc.vpc.id }}" + cidr: "{{ vpc_cidr_prefix }}.1.0/24" + state: present + register: elasticache_vpc_subnet_1 + + - name: Create subnet 2 in this VPC to launch Elasticache instances into + ec2_vpc_subnet: + vpc_id: "{{ elasticache_vpc.vpc.id }}" + cidr: "{{ vpc_cidr_prefix }}.2.0/24" + state: present + register: elasticache_vpc_subnet_2 + + - name: Create Elasticache Subnet Group (grouping two subnets together) + elasticache_subnet_group: + name: "{{ elasticache_subnet_group_name }}" + description: Subnet group grouping together both VPC subnets for Elasticache Test setup + subnets: + - "{{ elasticache_vpc_subnet_1.subnet.id }}" + - "{{ elasticache_vpc_subnet_2.subnet.id }}" + state: present + + # == Actual testing of the elasticache module == + + - name: Create Redis Server on Elasticache in VPC subnets + elasticache: + name: "{{ elasticache_redis_test_name }}" + engine: redis + node_type: cache.t3.micro + cache_port: "{{ elasticache_redis_port }}" + cache_subnet_group: "{{ elasticache_subnet_group_name }}" + num_nodes: 1 + state: present + register: elasticache_redis + + - name: Assert that task worked + assert: + that: + - elasticache_redis is changed + - elasticache_redis.elasticache.data is defined + - elasticache_redis.elasticache.name == "{{ elasticache_redis_test_name }}" + - elasticache_redis.elasticache.data.CacheSubnetGroupName == "{{ elasticache_subnet_group_name }}" + + - name: Add security group for Redis access in Elasticache + ec2_group: + name: "{{ elasticache_redis_sg_name }}" + description: Allow access to Elasticache Redis for testing EC module + vpc_id: "{{ elasticache_vpc.vpc.id }}" + rules: + - proto: tcp + from_port: "{{ elasticache_redis_port }}" + to_port: "{{ elasticache_redis_port }}" + cidr_ip: 10.31.0.0/16 + register: elasticache_redis_sg + + - name: Update Redis Elasticache config with security group (to if changes to existing setup work) + elasticache: + name: "{{ elasticache_redis.elasticache.name }}" + engine: redis + node_type: cache.t3.micro + num_nodes: 1 + cache_port: "{{ elasticache_redis_port }}" + cache_subnet_group: "{{ elasticache_subnet_group_name }}" + security_group_ids: "{{ elasticache_redis_sg.group_id }}" + state: present + register: elasticache_redis_new + + - name: Assert that task worked + assert: + that: + - elasticache_redis_new is changed + - elasticache_redis_new.elasticache.data is defined + - elasticache_redis_new.elasticache.data.Engine == "redis" + - elasticache_redis_new.elasticache.data.SecurityGroups.0.SecurityGroupId == elasticache_redis_sg.group_id + + - name: Describe all Elasticache clusters + elasticache_info: {} + register: elasticache_info + + - assert: + that: + - '"elasticache_clusters" in elasticache_info' + - elasticache_info.elasticache_clusters | length >= 1 + + - name: Describe Elasticache Redis cluster + elasticache_info: + name: "{{ elasticache_redis.elasticache.name }}" + register: elasticache_info + + - assert: + that: + - '"elasticache_clusters" in elasticache_info' + - elasticache_info.elasticache_clusters | length == 1 + - '"arn" in elasticache_info.elasticache_clusters[0]' + - '"at_rest_encryption_enabled" in elasticache_info.elasticache_clusters[0]' + - '"auth_token_enabled" in elasticache_info.elasticache_clusters[0]' + - '"auto_minor_version_upgrade" in elasticache_info.elasticache_clusters[0]' + - '"cache_cluster_create_time" in elasticache_info.elasticache_clusters[0]' + - '"cache_cluster_id" in elasticache_info.elasticache_clusters[0]' + - '"cache_cluster_status" in elasticache_info.elasticache_clusters[0]' + - '"cache_node_type" in elasticache_info.elasticache_clusters[0]' + - '"cache_nodes" in elasticache_info.elasticache_clusters[0]' + - '"cache_parameter_group" in elasticache_info.elasticache_clusters[0]' + - '"cache_security_groups" in elasticache_info.elasticache_clusters[0]' + - '"cache_subnet_group_name" in elasticache_info.elasticache_clusters[0]' + - '"client_download_landing_page" in elasticache_info.elasticache_clusters[0]' + - '"engine" in elasticache_info.elasticache_clusters[0]' + - '"engine_version" in elasticache_info.elasticache_clusters[0]' + - '"num_cache_nodes" in elasticache_info.elasticache_clusters[0]' + - '"pending_modified_values" in elasticache_info.elasticache_clusters[0]' + - '"preferred_availability_zone" in elasticache_info.elasticache_clusters[0]' + - '"preferred_maintenance_window" in elasticache_info.elasticache_clusters[0]' + - '"security_groups" in elasticache_info.elasticache_clusters[0]' + - '"snapshot_retention_limit" in elasticache_info.elasticache_clusters[0]' + - '"snapshot_window" in elasticache_info.elasticache_clusters[0]' + - '"tags" in elasticache_info.elasticache_clusters[0]' + - '"transit_encryption_enabled" in elasticache_info.elasticache_clusters[0]' + - elasticache_info.elasticache_clusters[0].arn.startswith("arn:aws") + - elasticache_info.elasticache_clusters[0].at_rest_encryption_enabled == False + - elasticache_info.elasticache_clusters[0].auth_token_enabled == False + - elasticache_info.elasticache_clusters[0].auto_minor_version_upgrade == True + - elasticache_info.elasticache_clusters[0].cache_cluster_status == "available" + - elasticache_info.elasticache_clusters[0].cache_node_type == "cache.t3.micro" + - elasticache_info.elasticache_clusters[0].cache_subnet_group_name == elasticache_subnet_group_name + - elasticache_info.elasticache_clusters[0].engine == "redis" + - elasticache_info.elasticache_clusters[0].num_cache_nodes == 1 + - elasticache_info.elasticache_clusters[0].snapshot_retention_limit == 0 + - elasticache_info.elasticache_clusters[0].transit_encryption_enabled == False + - elasticache_info.elasticache_clusters[0].cache_nodes | length == 1 + - '"cache_node_create_time" in elasticache_info.elasticache_clusters[0].cache_nodes[0]' + - '"cache_node_id" in elasticache_info.elasticache_clusters[0].cache_nodes[0]' + - '"cache_node_status" in elasticache_info.elasticache_clusters[0].cache_nodes[0]' + - '"customer_availability_zone" in elasticache_info.elasticache_clusters[0].cache_nodes[0]' + - '"endpoint" in elasticache_info.elasticache_clusters[0].cache_nodes[0]' + - '"parameter_group_status" in elasticache_info.elasticache_clusters[0].cache_nodes[0]' + - elasticache_info.elasticache_clusters[0].cache_nodes[0].cache_node_status == "available" + - elasticache_info.elasticache_clusters[0].cache_nodes[0].parameter_group_status == "in-sync" + - '"address" in elasticache_info.elasticache_clusters[0].cache_nodes[0].endpoint' + - '"port" in elasticache_info.elasticache_clusters[0].cache_nodes[0].endpoint' + - '"cache_node_ids_to_reboot" in elasticache_info.elasticache_clusters[0].cache_parameter_group' + - '"cache_parameter_group_name" in elasticache_info.elasticache_clusters[0].cache_parameter_group' + - '"parameter_apply_status" in elasticache_info.elasticache_clusters[0].cache_parameter_group' + - elasticache_info.elasticache_clusters[0].cache_parameter_group.cache_node_ids_to_reboot | length == 0 + - elasticache_info.elasticache_clusters[0].cache_parameter_group.parameter_apply_status == "in-sync" + - elasticache_info.elasticache_clusters[0].security_groups | length == 1 + - '"security_group_id" in elasticache_info.elasticache_clusters[0].security_groups[0]' + - '"status" in elasticache_info.elasticache_clusters[0].security_groups[0]' + - elasticache_info.elasticache_clusters[0].security_groups[0].security_group_id == elasticache_redis_sg.group_id + - elasticache_info.elasticache_clusters[0].security_groups[0].status == "active" + + always: + + # == Cleanup == + + - name: Make sure test Redis is deleted again from Elasticache + elasticache: + name: "{{ elasticache_redis_test_name }}" + engine: redis + state: absent + + - name: Make sure Elasticache Subnet group is deleted again + elasticache_subnet_group: + name: "{{ elasticache_subnet_group_name }}" + state: absent + + - name: Make sure Redis Security Group is deleted again + ec2_group: + name: "{{ elasticache_redis_sg_name }}" + state: absent + + - name: Make sure VPC subnet 1 is deleted again + ec2_vpc_subnet: + vpc_id: "{{ elasticache_vpc.vpc.id }}" + cidr: "{{ vpc_cidr_prefix }}.1.0/24" + state: absent + + - name: Make sure VPC subnet 2 is deleted again + ec2_vpc_subnet: + vpc_id: "{{ elasticache_vpc.vpc.id }}" + cidr: "{{ vpc_cidr_prefix }}.2.0/24" + state: absent + + - name: Make sure VPC is deleted again (only works if subnets were deleted) + ec2_vpc_net: + name: "{{ vpc_name }}" + cidr_block: "{{ vpc_cidr_prefix }}.0.0/16" + state: absent diff --git a/ansible_collections/community/aws/tests/integration/targets/elasticache_subnet_group/aliases b/ansible_collections/community/aws/tests/integration/targets/elasticache_subnet_group/aliases new file mode 100644 index 000000000..4ef4b2067 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elasticache_subnet_group/aliases @@ -0,0 +1 @@ +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/elasticache_subnet_group/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/elasticache_subnet_group/defaults/main.yml new file mode 100644 index 000000000..ea8921880 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elasticache_subnet_group/defaults/main.yml @@ -0,0 +1,42 @@ +--- +availability_zone: '{{ ec2_availability_zone_names[0] }}' + +vpc_name: '{{ resource_prefix }}' +subnet_name_a: '{{ resource_prefix }}-a' +subnet_name_b: '{{ resource_prefix }}-b' +subnet_name_c: '{{ resource_prefix }}-c' +subnet_name_d: '{{ resource_prefix }}-d' + +vpc_cidr: '10.{{ 256 | random(seed=resource_prefix) }}.0.0/16' +subnet_cidr_a: '10.{{ 256 | random(seed=resource_prefix) }}.1.0/24' +subnet_cidr_b: '10.{{ 256 | random(seed=resource_prefix) }}.2.0/24' +subnet_cidr_c: '10.{{ 256 | random(seed=resource_prefix) }}.3.0/24' +subnet_cidr_d: '10.{{ 256 | random(seed=resource_prefix) }}.4.0/24' + +subnet_zone_a: '{{ ec2_availability_zone_names[0] }}' +subnet_zone_b: '{{ ec2_availability_zone_names[1] }}' +subnet_zone_c: '{{ ec2_availability_zone_names[0] }}' +subnet_zone_d: '{{ ec2_availability_zone_names[1] }}' + +group_name: '{{ resource_prefix }}' +description_default: 'Subnet Description' +description_updated: 'updated subnet description' + +# Tagging not currently supported, planned with boto3 upgrade +tags_default: + snake_case_key: snake_case_value + camelCaseKey: camelCaseValue + PascalCaseKey: PascalCaseValue + 'key with spaces': value with spaces + 'Upper With Spaces': Upper With Spaces + +partial_tags: + snake_case_key: snake_case_value + camelCaseKey: camelCaseValue + +updated_tags: + updated_snake_case_key: updated_snake_case_value + updatedCamelCaseKey: updatedCamelCaseValue + UpdatedPascalCaseKey: UpdatedPascalCaseValue + 'updated key with spaces': updated value with spaces + 'updated Upper With Spaces': Updated Upper With Spaces diff --git a/ansible_collections/community/aws/tests/integration/targets/elasticache_subnet_group/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/elasticache_subnet_group/meta/main.yml new file mode 100644 index 000000000..1471b11f6 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elasticache_subnet_group/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - setup_ec2_facts diff --git a/ansible_collections/community/aws/tests/integration/targets/elasticache_subnet_group/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/elasticache_subnet_group/tasks/main.yml new file mode 100644 index 000000000..5814f9dc9 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elasticache_subnet_group/tasks/main.yml @@ -0,0 +1,681 @@ +--- +# elasticache_subnet_group integration tests +# +# Current module limitations: +# - check_mode not supported +# - Tagging not supported +# - Returned values *very* limited (almost none) +# +- module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + block: + + # ============================================================ + + - name: Create Subnet Group with no subnets - check_mode + elasticache_subnet_group: + state: present + name: '{{ group_name }}' + check_mode: True + register: create_group + ignore_errors: True + + - name: Check result - Create Subnet Group with no subnets - check_mode + assert: + that: + - create_group is failed + # Check we caught the issue before trying to create + - '"CreateCacheSubnetGroup" not in create_group.resource_actions' + # Check that we don't refer to the boto3 parameter + - '"SubnetIds" not in create_group.msg' + # Loosely check the message + - '"subnet" in create_group.msg' + - '"At least" in create_group.msg' + + - name: Create Subnet Group with no subnets + elasticache_subnet_group: + state: present + name: '{{ group_name }}' + register: create_group + ignore_errors: True + + - name: Check result - Create Subnet Group with no subnets + assert: + that: + - create_group is failed + # Check we caught the issue before trying to create + - '"CreateCacheSubnetGroup" not in create_group.resource_actions' + # Check that we don't refer to the boto3 parameter + - '"SubnetIds" not in create_group.msg' + # Loosely check the message + - '"subnet" in create_group.msg' + - '"At least" in create_group.msg' + + # ============================================================ + # Setup infra needed for tests + - name: create a VPC + ec2_vpc_net: + state: present + name: '{{ vpc_name }}' + cidr_block: '{{ vpc_cidr }}' + tags: + TestPrefix: '{{ resource_prefix }}' + register: vpc_result + + - name: create subnets + ec2_vpc_subnet: + state: present + cidr: '{{ item.cidr }}' + az: '{{ item.zone }}' + vpc_id: '{{ vpc_result.vpc.id }}' + tags: + Name: '{{ item.name }}' + TestPrefix: '{{ resource_prefix }}' + register: vpc_subnet_create + loop: + - name: '{{ subnet_name_a }}' + cidr: '{{ subnet_cidr_a }}' + zone: '{{ subnet_zone_a }}' + - name: '{{ subnet_name_b }}' + cidr: '{{ subnet_cidr_b }}' + zone: '{{ subnet_zone_b }}' + - name: '{{ subnet_name_c }}' + cidr: '{{ subnet_cidr_c }}' + zone: '{{ subnet_zone_c }}' + - name: '{{ subnet_name_d }}' + cidr: '{{ subnet_cidr_d }}' + zone: '{{ subnet_zone_d }}' + + - name: Store IDs of subnets and VPC + set_fact: + vpc_id: '{{ vpc_result.vpc.id }}' + subnet_id_a: '{{ vpc_subnet_create.results[0].subnet.id }}' + subnet_id_b: '{{ vpc_subnet_create.results[1].subnet.id }}' + subnet_id_c: '{{ vpc_subnet_create.results[2].subnet.id }}' + subnet_id_d: '{{ vpc_subnet_create.results[3].subnet.id }}' + + # ============================================================ + + - name: Create Subnet Group - check_mode + elasticache_subnet_group: + state: present + name: '{{ group_name }}' + description: '{{ description_default }}' + subnets: + - '{{ subnet_id_a }}' + - '{{ subnet_id_b }}' + check_mode: True + register: create_group + + - name: Check result - Create Subnet Group - check_mode + assert: + that: + - create_group is successful + - create_group is changed + + - name: Create Subnet Group + elasticache_subnet_group: + state: present + name: '{{ group_name }}' + description: '{{ description_default }}' + subnets: + - '{{ subnet_id_a }}' + - '{{ subnet_id_b }}' + register: create_group + + - name: Check result - Create Subnet Group + assert: + that: + - create_group is successful + - create_group is changed + - '"cache_subnet_group" in create_group' + - '"arn" in create_group.cache_subnet_group' + - '"description" in create_group.cache_subnet_group' + - '"name" in create_group.cache_subnet_group' + - '"subnet_ids" in create_group.cache_subnet_group' + - '"vpc_id" in create_group.cache_subnet_group' + - create_group.cache_subnet_group.description == description_default + - create_group.cache_subnet_group.name == group_name + - subnet_id_a in create_group.cache_subnet_group.subnet_ids + - subnet_id_b in create_group.cache_subnet_group.subnet_ids + - subnet_id_c not in create_group.cache_subnet_group.subnet_ids + - subnet_id_d not in create_group.cache_subnet_group.subnet_ids + - create_group.cache_subnet_group.vpc_id == vpc_id + - create_group.cache_subnet_group.arn.startswith('arn:') + - create_group.cache_subnet_group.arn.endswith(group_name) + + - name: Create Subnet Group - idempotency - check_mode + elasticache_subnet_group: + state: present + name: '{{ group_name }}' + description: '{{ description_default }}' + subnets: + - '{{ subnet_id_a }}' + - '{{ subnet_id_b }}' + check_mode: True + register: create_group + + - name: Check result - Create Subnet Group - idempotency - check_mode + assert: + that: + - create_group is successful + - create_group is not changed + + - name: Create Subnet Group - idempotency + elasticache_subnet_group: + state: present + name: '{{ group_name }}' + description: '{{ description_default }}' + subnets: + - '{{ subnet_id_a }}' + - '{{ subnet_id_b }}' + register: create_group + + - name: Check result - Create Subnet Group - idempotency + assert: + that: + - create_group is successful + - create_group is not changed + - '"cache_subnet_group" in create_group' + - '"arn" in create_group.cache_subnet_group' + - '"description" in create_group.cache_subnet_group' + - '"name" in create_group.cache_subnet_group' + - '"subnet_ids" in create_group.cache_subnet_group' + - '"vpc_id" in create_group.cache_subnet_group' + - create_group.cache_subnet_group.description == description_default + - create_group.cache_subnet_group.name == group_name + - subnet_id_a in create_group.cache_subnet_group.subnet_ids + - subnet_id_b in create_group.cache_subnet_group.subnet_ids + - subnet_id_c not in create_group.cache_subnet_group.subnet_ids + - subnet_id_d not in create_group.cache_subnet_group.subnet_ids + - create_group.cache_subnet_group.vpc_id == vpc_id + - create_group.cache_subnet_group.arn.startswith('arn:') + - create_group.cache_subnet_group.arn.endswith(group_name) + + # ============================================================ + + - name: Update Subnet Group Description - check_mode + elasticache_subnet_group: + state: present + name: '{{ group_name }}' + description: '{{ description_updated }}' + ## No longer mandatory + # subnets: + # - '{{ subnet_id_a }}' + # - '{{ subnet_id_b }}' + check_mode: True + register: update_description + + - name: Check result - Update Subnet Group Description - check_mode + assert: + that: + - update_description is successful + - update_description is changed + + - name: Update Subnet Group Description + elasticache_subnet_group: + state: present + name: '{{ group_name }}' + description: '{{ description_updated }}' + ## No longer mandatory + # subnets: + # - '{{ subnet_id_a }}' + # - '{{ subnet_id_b }}' + register: update_description + + - name: Check result - Update Subnet Group Description + assert: + that: + - update_description is successful + - update_description is changed + - '"cache_subnet_group" in update_description' + - '"arn" in update_description.cache_subnet_group' + - '"description" in update_description.cache_subnet_group' + - '"name" in update_description.cache_subnet_group' + - '"subnet_ids" in update_description.cache_subnet_group' + - '"vpc_id" in update_description.cache_subnet_group' + - update_description.cache_subnet_group.description == description_updated + - update_description.cache_subnet_group.name == group_name + - subnet_id_a in update_description.cache_subnet_group.subnet_ids + - subnet_id_b in update_description.cache_subnet_group.subnet_ids + - subnet_id_c not in update_description.cache_subnet_group.subnet_ids + - subnet_id_d not in update_description.cache_subnet_group.subnet_ids + - update_description.cache_subnet_group.vpc_id == vpc_id + - update_description.cache_subnet_group.arn.startswith('arn:') + - update_description.cache_subnet_group.arn.endswith(group_name) + + - name: Update Subnet Group Description - idempotency - check_mode + elasticache_subnet_group: + state: present + name: '{{ group_name }}' + description: '{{ description_updated }}' + ## No longer mandatory + # subnets: + # - '{{ subnet_id_a }}' + # - '{{ subnet_id_b }}' + check_mode: True + register: update_description + + - name: Check result - Update Subnet Group Description - idempotency - check_mode + assert: + that: + - update_description is successful + - update_description is not changed + + - name: Update Subnet Group Description - idempotency + elasticache_subnet_group: + state: present + name: '{{ group_name }}' + description: '{{ description_updated }}' + ## No longer mandatory + # subnets: + # - '{{ subnet_id_a }}' + # - '{{ subnet_id_b }}' + register: update_description + + - name: Check result - Update Subnet Group Description - idempotency + assert: + that: + - update_description is successful + - update_description is not changed + - '"cache_subnet_group" in update_description' + - '"arn" in update_description.cache_subnet_group' + - '"description" in update_description.cache_subnet_group' + - '"name" in update_description.cache_subnet_group' + - '"subnet_ids" in update_description.cache_subnet_group' + - '"vpc_id" in update_description.cache_subnet_group' + - update_description.cache_subnet_group.description == description_updated + - update_description.cache_subnet_group.name == group_name + - subnet_id_a in update_description.cache_subnet_group.subnet_ids + - subnet_id_b in update_description.cache_subnet_group.subnet_ids + - subnet_id_c not in update_description.cache_subnet_group.subnet_ids + - subnet_id_d not in update_description.cache_subnet_group.subnet_ids + - update_description.cache_subnet_group.vpc_id == vpc_id + - update_description.cache_subnet_group.arn.startswith('arn:') + - update_description.cache_subnet_group.arn.endswith(group_name) + + # ============================================================ + + - name: Update Subnet Group subnets - check_mode + elasticache_subnet_group: + state: present + name: '{{ group_name }}' + ## No longer mandatory + # description: '{{ description_updated }}' + subnets: + - '{{ subnet_id_c }}' + - '{{ subnet_id_d }}' + check_mode: True + register: update_subnets + + - name: Check result - Update Subnet Group subnets - check_mode + assert: + that: + - update_subnets is successful + - update_subnets is changed + + - name: Update Subnet Group subnets + elasticache_subnet_group: + state: present + name: '{{ group_name }}' + ## No longer mandatory + # description: '{{ description_updated }}' + subnets: + - '{{ subnet_id_c }}' + - '{{ subnet_id_d }}' + register: update_subnets + + - name: Check result - Update Subnet Group subnets + assert: + that: + - update_subnets is successful + - update_subnets is changed + - '"cache_subnet_group" in update_subnets' + - '"arn" in update_subnets.cache_subnet_group' + - '"description" in update_subnets.cache_subnet_group' + - '"name" in update_subnets.cache_subnet_group' + - '"subnet_ids" in update_subnets.cache_subnet_group' + - '"vpc_id" in update_subnets.cache_subnet_group' + - update_subnets.cache_subnet_group.description == description_updated + - update_subnets.cache_subnet_group.name == group_name + - subnet_id_a not in update_subnets.cache_subnet_group.subnet_ids + - subnet_id_b not in update_subnets.cache_subnet_group.subnet_ids + - subnet_id_c in update_subnets.cache_subnet_group.subnet_ids + - subnet_id_d in update_subnets.cache_subnet_group.subnet_ids + - update_subnets.cache_subnet_group.vpc_id == vpc_id + - update_subnets.cache_subnet_group.arn.startswith('arn:') + - update_subnets.cache_subnet_group.arn.endswith(group_name) + + - name: Update Subnet Group subnets - idempotency - check_mode + elasticache_subnet_group: + state: present + name: '{{ group_name }}' + ## No longer mandatory + # description: '{{ description_updated }}' + subnets: + - '{{ subnet_id_c }}' + - '{{ subnet_id_d }}' + check_mode: True + register: update_subnets + + - name: Check result - Update Subnet Group subnets - idempotency - check_mode + assert: + that: + - update_subnets is successful + - update_subnets is not changed + + - name: Update Subnet Group subnets - idempotency + elasticache_subnet_group: + state: present + name: '{{ group_name }}' + ## No longer mandatory + # description: '{{ description_updated }}' + subnets: + - '{{ subnet_id_c }}' + - '{{ subnet_id_d }}' + register: update_subnets + + - name: Check result - Update Subnet Group subnets - idempotency + assert: + that: + - update_subnets is successful + - update_subnets is not changed + - '"cache_subnet_group" in update_subnets' + - '"arn" in update_subnets.cache_subnet_group' + - '"description" in update_subnets.cache_subnet_group' + - '"name" in update_subnets.cache_subnet_group' + - '"subnet_ids" in update_subnets.cache_subnet_group' + - '"vpc_id" in update_subnets.cache_subnet_group' + - update_subnets.cache_subnet_group.description == description_updated + - update_subnets.cache_subnet_group.name == group_name + - subnet_id_a not in update_subnets.cache_subnet_group.subnet_ids + - subnet_id_b not in update_subnets.cache_subnet_group.subnet_ids + - subnet_id_c in update_subnets.cache_subnet_group.subnet_ids + - subnet_id_d in update_subnets.cache_subnet_group.subnet_ids + - update_subnets.cache_subnet_group.vpc_id == vpc_id + - update_subnets.cache_subnet_group.arn.startswith('arn:') + - update_subnets.cache_subnet_group.arn.endswith(group_name) + + # ============================================================ + + - name: Delete Subnet Group - check_mode + elasticache_subnet_group: + state: absent + name: '{{ group_name }}' + check_mode: True + register: delete_group + + - name: Check result - Delete Subnet Group - check_mode + assert: + that: + - delete_group is changed + + - name: Delete Subnet Group + elasticache_subnet_group: + state: absent + name: '{{ group_name }}' + register: delete_group + + - name: Check result - Delete Subnet Group + assert: + that: + - delete_group is changed + + - name: Delete Subnet Group - idempotency - check_mode + elasticache_subnet_group: + state: absent + name: '{{ group_name }}' + check_mode: True + register: delete_group + + - name: Check result - Delete Subnet Group - idempotency - check_mode + assert: + that: + - delete_group is not changed + + - name: Delete Subnet Group - idempotency + elasticache_subnet_group: + state: absent + name: '{{ group_name }}' + register: delete_group + + - name: Check result - Delete Subnet Group - idempotency + assert: + that: + - delete_group is not changed + + # ============================================================ + + - name: Create minimal Subnet Group - check_mode + elasticache_subnet_group: + state: present + name: '{{ group_name }}' + subnets: + - '{{ subnet_id_a }}' + check_mode: True + register: create_group + + - name: Check result - Create minimal Subnet Group - check_mode + assert: + that: + - create_group is successful + - create_group is changed + + - name: Create minimal Subnet Group + elasticache_subnet_group: + state: present + name: '{{ group_name }}' + subnets: + - '{{ subnet_id_a }}' + register: create_group + + - name: Check result - Create minimal Subnet Group + assert: + that: + - create_group is successful + - create_group is changed + - '"cache_subnet_group" in create_group' + - '"arn" in create_group.cache_subnet_group' + - '"description" in create_group.cache_subnet_group' + - '"name" in create_group.cache_subnet_group' + - '"subnet_ids" in create_group.cache_subnet_group' + - '"vpc_id" in create_group.cache_subnet_group' + - create_group.cache_subnet_group.description == group_name + - create_group.cache_subnet_group.name == group_name + - subnet_id_a in create_group.cache_subnet_group.subnet_ids + - subnet_id_b not in create_group.cache_subnet_group.subnet_ids + - subnet_id_c not in create_group.cache_subnet_group.subnet_ids + - subnet_id_d not in create_group.cache_subnet_group.subnet_ids + - create_group.cache_subnet_group.vpc_id == vpc_id + - create_group.cache_subnet_group.arn.startswith('arn:') + - create_group.cache_subnet_group.arn.endswith(group_name) + + - name: Create minimal Subnet Group - idempotency - check_mode + elasticache_subnet_group: + state: present + name: '{{ group_name }}' + subnets: + - '{{ subnet_id_a }}' + check_mode: True + register: create_group + + - name: Check result - Create minimal Subnet Group - idempotency - check_mode + assert: + that: + - create_group is successful + - create_group is not changed + + - name: Create minimal Subnet Group - idempotency + elasticache_subnet_group: + state: present + name: '{{ group_name }}' + subnets: + - '{{ subnet_id_a }}' + register: create_group + + - name: Check result - Create minimal Subnet Group - idempotency + assert: + that: + - create_group is successful + - create_group is not changed + - '"cache_subnet_group" in create_group' + - '"arn" in create_group.cache_subnet_group' + - '"description" in create_group.cache_subnet_group' + - '"name" in create_group.cache_subnet_group' + - '"subnet_ids" in create_group.cache_subnet_group' + - '"vpc_id" in create_group.cache_subnet_group' + - create_group.cache_subnet_group.description == group_name + - create_group.cache_subnet_group.name == group_name + - subnet_id_a in create_group.cache_subnet_group.subnet_ids + - subnet_id_b not in create_group.cache_subnet_group.subnet_ids + - subnet_id_c not in create_group.cache_subnet_group.subnet_ids + - subnet_id_d not in create_group.cache_subnet_group.subnet_ids + - create_group.cache_subnet_group.vpc_id == vpc_id + - create_group.cache_subnet_group.arn.startswith('arn:') + - create_group.cache_subnet_group.arn.endswith(group_name) + + # ============================================================ + + - name: Full Update Subnet Group - check_mode + elasticache_subnet_group: + state: present + name: '{{ group_name }}' + description: '{{ description_updated }}' + subnets: + - '{{ subnet_id_a }}' + - '{{ subnet_id_b }}' + check_mode: True + register: update_complex + + - name: Check result - Full Update Subnet Group - check_mode + assert: + that: + - update_complex is successful + - update_complex is changed + + - name: Update Subnet Group + elasticache_subnet_group: + state: present + name: '{{ group_name }}' + description: '{{ description_updated }}' + subnets: + - '{{ subnet_id_a }}' + - '{{ subnet_id_b }}' + register: update_complex + + - name: Check result - Full Update Subnet Group + assert: + that: + - update_complex is successful + - update_complex is changed + - '"cache_subnet_group" in update_complex' + - '"arn" in update_complex.cache_subnet_group' + - '"description" in update_complex.cache_subnet_group' + - '"name" in update_complex.cache_subnet_group' + - '"subnet_ids" in update_complex.cache_subnet_group' + - '"vpc_id" in update_complex.cache_subnet_group' + - update_complex.cache_subnet_group.description == description_updated + - update_complex.cache_subnet_group.name == group_name + - subnet_id_a in update_complex.cache_subnet_group.subnet_ids + - subnet_id_b in update_complex.cache_subnet_group.subnet_ids + - subnet_id_c not in update_complex.cache_subnet_group.subnet_ids + - subnet_id_d not in update_complex.cache_subnet_group.subnet_ids + - update_complex.cache_subnet_group.vpc_id == vpc_id + - update_complex.cache_subnet_group.arn.startswith('arn:') + - update_complex.cache_subnet_group.arn.endswith(group_name) + + - name: Full Update Subnet Group - idempotency - check_mode + elasticache_subnet_group: + state: present + name: '{{ group_name }}' + description: '{{ description_updated }}' + subnets: + - '{{ subnet_id_a }}' + - '{{ subnet_id_b }}' + check_mode: True + register: update_complex + + - name: Check result - Full Update Subnet Group - idempotency - check_mode + assert: + that: + - update_complex is successful + - update_complex is not changed + + - name: Full Update Subnet Group - idempotency + elasticache_subnet_group: + state: present + name: '{{ group_name }}' + description: '{{ description_updated }}' + subnets: + - '{{ subnet_id_a }}' + - '{{ subnet_id_b }}' + register: update_complex + + - name: Check result - Full Update Subnet Group - idempotency + assert: + that: + - update_complex is successful + - update_complex is not changed + - '"cache_subnet_group" in update_complex' + - '"arn" in update_complex.cache_subnet_group' + - '"description" in update_complex.cache_subnet_group' + - '"name" in update_complex.cache_subnet_group' + - '"subnet_ids" in update_complex.cache_subnet_group' + - '"vpc_id" in update_complex.cache_subnet_group' + - update_complex.cache_subnet_group.description == description_updated + - update_complex.cache_subnet_group.name == group_name + - subnet_id_a in update_complex.cache_subnet_group.subnet_ids + - subnet_id_b in update_complex.cache_subnet_group.subnet_ids + - subnet_id_c not in update_complex.cache_subnet_group.subnet_ids + - subnet_id_d not in update_complex.cache_subnet_group.subnet_ids + - update_complex.cache_subnet_group.vpc_id == vpc_id + - update_complex.cache_subnet_group.arn.startswith('arn:') + - update_complex.cache_subnet_group.arn.endswith(group_name) + + # ============================================================ + + - name: Delete Subnet Group + elasticache_subnet_group: + state: absent + name: '{{ group_name }}' + register: delete_group + + - name: Check result - Delete Subnet Group + assert: + that: + - delete_group is changed + + always: + + ################################################ + # TEARDOWN STARTS HERE + ################################################ + + - name: Delete Subnet Group + elasticache_subnet_group: + state: absent + name: '{{ group_name }}' + ignore_errors: True + + - name: tidy up subnet + ec2_vpc_subnet: + state: absent + cidr: '{{ item }}' + vpc_id: '{{ vpc_result.vpc.id }}' + loop: + - '{{ subnet_cidr_a }}' + - '{{ subnet_cidr_b }}' + - '{{ subnet_cidr_c }}' + - '{{ subnet_cidr_d }}' + ignore_errors: True + + - name: tidy up VPC + ec2_vpc_net: + state: absent + name: '{{ vpc_name }}' + cidr_block: '{{ vpc_cidr }}' + ignore_errors: True diff --git a/ansible_collections/community/aws/tests/integration/targets/elasticbeanstalk_app/aliases b/ansible_collections/community/aws/tests/integration/targets/elasticbeanstalk_app/aliases new file mode 100644 index 000000000..4ef4b2067 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elasticbeanstalk_app/aliases @@ -0,0 +1 @@ +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/elasticbeanstalk_app/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/elasticbeanstalk_app/defaults/main.yml new file mode 100644 index 000000000..3f38e1a85 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elasticbeanstalk_app/defaults/main.yml @@ -0,0 +1,5 @@ +--- +# defaults file for aws_elasticbeanstalk_app +app_name: '{{ resource_prefix }}_eb_ansible_test' +description: 'eb_ansible_test app description' +alternate_description: 'eb_ansible_test app alternate_description' diff --git a/ansible_collections/community/aws/tests/integration/targets/elasticbeanstalk_app/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/elasticbeanstalk_app/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elasticbeanstalk_app/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/elasticbeanstalk_app/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/elasticbeanstalk_app/tasks/main.yml new file mode 100644 index 000000000..d90a7ce8d --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elasticbeanstalk_app/tasks/main.yml @@ -0,0 +1,145 @@ +--- +- name: 'aws_elasticbeanstalk_app integration tests' + collections: + - amazon.aws + module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + block: + + # ============================================================ + - name: test with no parameters + aws_elasticbeanstalk_app: + register: result + ignore_errors: true + + - name: assert failure when called with no parameters + assert: + that: + - 'result.failed' + + # ============================================================ + - name: test create app + aws_elasticbeanstalk_app: + app_name: "{{ app_name }}" + description: "{{ description }}" + state: present + register: result + + - name: assert changed is True + assert: + that: + - result.changed == True + + # ============================================================ + - name: test create when app already exists + aws_elasticbeanstalk_app: + app_name: "{{ app_name }}" + description: "{{ description }}" + state: present + register: result + + - name: assert changed is False since the app already exists + assert: + that: + - result.changed == False + + # ============================================================ + - name: make an update to an existing app + aws_elasticbeanstalk_app: + app_name: "{{ app_name }}" + description: "{{ alternate_description }}" + state: present + register: result + + - name: assert changed is True + assert: + that: + - result.changed == True + +# # ============================================================ +# - name: fail deleting an app that has environments that exist +# aws_elasticbeanstalk_app: +# app_name: "non_app" +# state: absent +# register: result +# ignore_errors: true +# +# - name: assert deleteing app with running environments fail +# assert: +# that: +# - result.changed == False + +# # ============================================================ +# - name: deleting an app that has environments that exist with terminate_by_force True +# aws_elasticbeanstalk_app: +# app_name: "non_app" +# state: absent +# terminate_by_force: True +# register: result +# +# - name: assert deleteing app with running environments with terminate_by_force True +# assert: +# that: +# - result.changed == True +# + # ============================================================ +# - name: retrieve a list of apps +# aws_elasticbeanstalk_app_facts: +# register: result + +# - name: assert changed is True +# assert: +# that: +# - result is success + +# # ============================================================ +# - name: deleting an app that has environments that exist with terminate_by_force True +# aws_elasticbeanstalk_app: +# app_name: "non_app" +# state: absent +# terminate_by_force: True +# register: result +# +# - name: assert deleteing app with running environments with terminate_by_force True +# assert: +# that: +# - result.changed == True +# + # ============================================================ + - name: delete non existent app + aws_elasticbeanstalk_app: + app_name: "non_app" + state: absent + register: result + ignore_errors: true + + - name: assert deleteing non existant app fails + assert: + that: + - result.changed == False + - 'result.output.startswith("Application not found")' + + # ============================================================ + - name: delete existing app + aws_elasticbeanstalk_app: + app_name: "{{ app_name }}" + state: absent + register: result + + - name: assert changed is True + assert: + that: + - result.changed == True + + # ============================================================ + + always: + + - name: delete existing app + aws_elasticbeanstalk_app: + app_name: "{{ app_name }}" + state: absent diff --git a/ansible_collections/community/aws/tests/integration/targets/elb_classic_lb_info/aliases b/ansible_collections/community/aws/tests/integration/targets/elb_classic_lb_info/aliases new file mode 100644 index 000000000..4ef4b2067 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elb_classic_lb_info/aliases @@ -0,0 +1 @@ +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/elb_classic_lb_info/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/elb_classic_lb_info/defaults/main.yml new file mode 100644 index 000000000..bd059e26e --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elb_classic_lb_info/defaults/main.yml @@ -0,0 +1,3 @@ +--- +# defaults file for test_ec2_eip +elb_name: 'ansible-test-{{ tiny_prefix }}-ecli' diff --git a/ansible_collections/community/aws/tests/integration/targets/elb_classic_lb_info/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/elb_classic_lb_info/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elb_classic_lb_info/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/elb_classic_lb_info/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/elb_classic_lb_info/tasks/main.yml new file mode 100644 index 000000000..e4cd8144b --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elb_classic_lb_info/tasks/main.yml @@ -0,0 +1,311 @@ +--- +# __Test Info__ +# Create a self signed cert and upload it to AWS +# http://www.akadia.com/services/ssh_test_certificate.html +# http://docs.aws.amazon.com/ElasticLoadBalancing/latest/DeveloperGuide/ssl-server-cert.html + +# __Test Outline__ +# +# __elb_classic_lb__ +# create test elb with listeners and certificate +# change AZ's +# change listeners +# remove listeners +# remove elb + +# __elb_classic_lb_info_ +# get nonexistent load balancer + +- module_defaults: + group/aws: + region: "{{ ec2_region }}" + ec2_access_key: "{{ ec2_access_key }}" + ec2_secret_key: "{{ ec2_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + block: + + # ============================================================ + # create test elb with listeners, certificate, and health check + + - name: Create ELB + elb_classic_lb: + name: "{{ elb_name }}" + state: present + zones: + - "{{ ec2_region }}a" + - "{{ ec2_region }}b" + listeners: + - protocol: http + load_balancer_port: 80 + instance_port: 80 + - protocol: http + load_balancer_port: 8080 + instance_port: 8080 + health_check: + ping_protocol: http + ping_port: 80 + ping_path: "/index.html" + response_timeout: 5 + interval: 30 + unhealthy_threshold: 2 + healthy_threshold: 10 + register: create + + - assert: + that: + - create is changed + # We rely on these for the info test, make sure they're what we expect + - '"{{ ec2_region }}a" in create.elb.zones' + - '"{{ ec2_region }}b" in create.elb.zones' + - create.elb.health_check.healthy_threshold == 10 + - create.elb.health_check.interval == 30 + - create.elb.health_check.target == "HTTP:80/index.html" + - create.elb.health_check.timeout == 5 + - create.elb.health_check.unhealthy_threshold == 2 + - '[80, 80, "HTTP", "HTTP"] in create.elb.listeners' + - '[8080, 8080, "HTTP", "HTTP"] in create.elb.listeners' + + - name: Get ELB info + elb_classic_lb_info: + names: "{{ elb_name }}" + register: info + + - assert: + that: + - info.elbs|length == 1 + - elb.availability_zones|length == 2 + - '"{{ ec2_region }}a" in elb.availability_zones' + - '"{{ ec2_region }}b" in elb.availability_zones' + - elb.health_check.healthy_threshold == 10 + - elb.health_check.interval == 30 + - elb.health_check.target == "HTTP:80/index.html" + - elb.health_check.timeout == 5 + - elb.health_check.unhealthy_threshold == 2 + - '{"instance_port": 80, "instance_protocol": "HTTP", "load_balancer_port": 80, "protocol": "HTTP"} == listeners[0]' + - '{"instance_port": 8080, "instance_protocol": "HTTP", "load_balancer_port": 8080, "protocol": "HTTP"} == listeners[1]' + vars: + elb: "{{ info.elbs[0] }}" + listeners: "{{ elb.listener_descriptions|map(attribute='listener')|sort(attribute='load_balancer_port') }}" + + # ============================================================ + + # check ports, would be cool, but we are at the mercy of AWS + # to start things in a timely manner + + #- name: check to make sure 80 is listening + # wait_for: host={{ info.elb.dns_name }} port=80 timeout=600 + # register: result + + #- name: assert can connect to port# + # assert: 'result.state == "started"' + + #- name: check to make sure 443 is listening + # wait_for: host={{ info.elb.dns_name }} port=443 timeout=600 + # register: result + + #- name: assert can connect to port# + # assert: 'result.state == "started"' + + # ============================================================ + + # Change AZ's + + - name: Change AZ's + elb_classic_lb: + name: "{{ elb_name }}" + state: present + zones: + - "{{ ec2_region }}c" + listeners: + - protocol: http + load_balancer_port: 80 + instance_port: 80 + purge_zones: yes + health_check: + ping_protocol: http + ping_port: 80 + ping_path: "/index.html" + response_timeout: 5 + interval: 30 + unhealthy_threshold: 2 + healthy_threshold: 10 + register: update_az + + - assert: + that: + - update_az is changed + - update_az.elb.zones[0] == "{{ ec2_region }}c" + + - name: Get ELB info after changing AZ's + elb_classic_lb_info: + names: "{{ elb_name }}" + register: info + + - assert: + that: + - elb.availability_zones|length == 1 + - '"{{ ec2_region }}c" in elb.availability_zones[0]' + vars: + elb: "{{ info.elbs[0] }}" + + # ============================================================ + + # Update AZ's + + - name: Update AZ's + elb_classic_lb: + name: "{{ elb_name }}" + state: present + zones: + - "{{ ec2_region }}a" + - "{{ ec2_region }}b" + - "{{ ec2_region }}c" + listeners: + - protocol: http + load_balancer_port: 80 + instance_port: 80 + purge_zones: yes + register: update_az + + - assert: + that: + - update_az is changed + - '"{{ ec2_region }}a" in update_az.elb.zones' + - '"{{ ec2_region }}b" in update_az.elb.zones' + - '"{{ ec2_region }}c" in update_az.elb.zones' + + - name: Get ELB info after updating AZ's + elb_classic_lb_info: + names: "{{ elb_name }}" + register: info + + - assert: + that: + - elb.availability_zones|length == 3 + - '"{{ ec2_region }}a" in elb.availability_zones' + - '"{{ ec2_region }}b" in elb.availability_zones' + - '"{{ ec2_region }}c" in elb.availability_zones' + vars: + elb: "{{ info.elbs[0] }}" + + # ============================================================ + + # Purge Listeners + + - name: Purge Listeners + elb_classic_lb: + name: "{{ elb_name }}" + state: present + zones: + - "{{ ec2_region }}a" + - "{{ ec2_region }}b" + - "{{ ec2_region }}c" + listeners: + - protocol: http + load_balancer_port: 80 + instance_port: 81 + purge_listeners: yes + register: purge_listeners + + - assert: + that: + - purge_listeners is changed + - '[80, 81, "HTTP", "HTTP"] in purge_listeners.elb.listeners' + - purge_listeners.elb.listeners|length == 1 + + - name: Get ELB info after purging listeners + elb_classic_lb_info: + names: "{{ elb_name }}" + register: info + + - assert: + that: + - elb.listener_descriptions|length == 1 + - '{"instance_port": 81, "instance_protocol": "HTTP", "load_balancer_port": 80, "protocol": "HTTP"} == elb.listener_descriptions[0].listener' + vars: + elb: "{{ info.elbs[0] }}" + + + # ============================================================ + + # add Listeners + + - name: Add Listeners + elb_classic_lb: + name: "{{ elb_name }}" + state: present + zones: + - "{{ ec2_region }}a" + - "{{ ec2_region }}b" + - "{{ ec2_region }}c" + listeners: + - protocol: http + load_balancer_port: 8081 + instance_port: 8081 + purge_listeners: no + register: update_listeners + + - assert: + that: + - update_listeners is changed + - '[80, 81, "HTTP", "HTTP"] in update_listeners.elb.listeners' + - '[8081, 8081, "HTTP", "HTTP"] in update_listeners.elb.listeners' + - update_listeners.elb.listeners|length == 2 + + - name: Get ELB info after adding listeners + elb_classic_lb_info: + names: "{{ elb_name }}" + register: info + + - assert: + that: + - elb.listener_descriptions|length == 2 + - '{"instance_port": 81, "instance_protocol": "HTTP", "load_balancer_port": 80, "protocol": "HTTP"} == listeners[0]' + - '{"instance_port": 8081, "instance_protocol": "HTTP", "load_balancer_port": 8081, "protocol": "HTTP"} == listeners[1]' + vars: + elb: "{{ info.elbs[0] }}" + listeners: "{{ elb.listener_descriptions|map(attribute='listener')|sort(attribute='load_balancer_port') }}" + + # ============================================================ + + # Test getting nonexistent load balancer + - name: get nonexistent load balancer + elb_classic_lb_info: + names: "invalid-elb" + register: info + + - assert: + that: + - info.elbs|length==0 + + # Test getting a valid and nonexistent load balancer + - name: get nonexistent load balancer + elb_classic_lb_info: + names: ["{{ elb_name }}", "invalid-elb"] + register: info + + - assert: + that: + - info.elbs|length==1 + - info.elbs[0].load_balancer_name == elb_name + + # ============================================================ + + - name: get all load balancers + elb_classic_lb_info: + names: "{{ omit }}" + register: info + + - assert: + that: + - info.elbs|length>0 + + always: + + # ============================================================ + - name: remove the test load balancer completely + elb_classic_lb: + name: "{{ elb_name }}" + state: absent + register: result + ignore_errors: true diff --git a/ansible_collections/community/aws/tests/integration/targets/elb_classic_lb_info/vars/main.yml b/ansible_collections/community/aws/tests/integration/targets/elb_classic_lb_info/vars/main.yml new file mode 100644 index 000000000..79194af1e --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elb_classic_lb_info/vars/main.yml @@ -0,0 +1,2 @@ +--- +# vars file for test_ec2_elb_lb diff --git a/ansible_collections/community/aws/tests/integration/targets/elb_instance/aliases b/ansible_collections/community/aws/tests/integration/targets/elb_instance/aliases new file mode 100644 index 000000000..7f51a8836 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elb_instance/aliases @@ -0,0 +1,4 @@ +time=8m +cloud/aws + +elb_classic_lb_info diff --git a/ansible_collections/community/aws/tests/integration/targets/elb_instance/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/elb_instance/defaults/main.yml new file mode 100644 index 000000000..25817c110 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elb_instance/defaults/main.yml @@ -0,0 +1,28 @@ +--- +# defaults file for elb_instance + +elb_name_1: 'ansible-test-{{ tiny_prefix }}-1' +elb_name_2: 'ansible-test-{{ tiny_prefix }}-2' + +vpc_cidr: '10.{{ 256 | random(seed=resource_prefix) }}.0.0/16' +subnet_cidr_1: '10.{{ 256 | random(seed=resource_prefix) }}.1.0/24' +subnet_cidr_2: '10.{{ 256 | random(seed=resource_prefix) }}.2.0/24' + +instance_name_1: 'ansible-test-{{ tiny_prefix }}-elb-instance-1' +instance_name_2: 'ansible-test-{{ tiny_prefix }}-elb-instance-2' +lc_name: 'ansible-test-{{ tiny_prefix }}-elb-instance' +asg_name: 'ansible-test-{{ tiny_prefix }}-elb-instance' +vpc_name: 'ansible-test-{{ tiny_prefix }}-elb-instance' +subnet_name_1: 'ansible-test-{{ tiny_prefix }}-elb-instance-1' +subnet_name_2: 'ansible-test-{{ tiny_prefix }}-elb-instance-2' +sg_name_1: 'ansible-test-{{ tiny_prefix }}-elb-instance-1' +sg_name_2: 'ansible-test-{{ tiny_prefix }}-elb-instance-2' + +availability_zone_a: '{{ ec2_availability_zone_names[0] }}' +availability_zone_b: '{{ ec2_availability_zone_names[1] }}' + +elb_listeners: + - protocol: tcp + load_balancer_port: 80 + instance_port: 80 + instance_protocol: tcp diff --git a/ansible_collections/community/aws/tests/integration/targets/elb_instance/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/elb_instance/meta/main.yml new file mode 100644 index 000000000..1471b11f6 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elb_instance/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - setup_ec2_facts diff --git a/ansible_collections/community/aws/tests/integration/targets/elb_instance/tasks/cleanup_elbs.yml b/ansible_collections/community/aws/tests/integration/targets/elb_instance/tasks/cleanup_elbs.yml new file mode 100644 index 000000000..92f7e31ae --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elb_instance/tasks/cleanup_elbs.yml @@ -0,0 +1,16 @@ +--- +- name: remove the public load balancer + elb_classic_lb: + name: "{{ elb_name_1 }}" + state: absent + wait: true + register: result + ignore_errors: true + +- name: remove the private load balancer + elb_classic_lb: + name: "{{ elb_name_2 }}" + state: absent + wait: true + register: result + ignore_errors: true diff --git a/ansible_collections/community/aws/tests/integration/targets/elb_instance/tasks/cleanup_instances.yml b/ansible_collections/community/aws/tests/integration/targets/elb_instance/tasks/cleanup_instances.yml new file mode 100644 index 000000000..7ae91ac00 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elb_instance/tasks/cleanup_instances.yml @@ -0,0 +1,23 @@ +--- +- name: Delete instance + ec2_instance: + instance_ids: + - '{{ instance_a }}' + - '{{ instance_b }}' + state: absent + wait: true + ignore_errors: true + +- name: Delete ASG + ec2_asg: + name: '{{ asg_name }}' + state: absent + ignore_errors: true + register: ec2_asg_a + +- name: Delete Launch Template + ec2_lc: + name: '{{ lc_name }}' + state: absent + ignore_errors: true + register: ec2_lc_a diff --git a/ansible_collections/community/aws/tests/integration/targets/elb_instance/tasks/cleanup_vpc.yml b/ansible_collections/community/aws/tests/integration/targets/elb_instance/tasks/cleanup_vpc.yml new file mode 100644 index 000000000..9abeb74a2 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elb_instance/tasks/cleanup_vpc.yml @@ -0,0 +1,26 @@ +--- +- name: delete security groups + ec2_group: + name: '{{ item }}' + state: absent + ignore_errors: true + loop: + - '{{ sg_name_1 }}' + - '{{ sg_name_2 }}' + +- name: delete subnets + ec2_vpc_subnet: + vpc_id: '{{ setup_vpc.vpc.id }}' + cidr: '{{ item }}' + state: absent + ignore_errors: true + loop: + - '{{ subnet_cidr_1 }}' + - '{{ subnet_cidr_2 }}' + +- name: delete VPC + ec2_vpc_net: + cidr_block: '{{ vpc_cidr }}' + state: absent + name: '{{ vpc_name }}' + ignore_errors: true diff --git a/ansible_collections/community/aws/tests/integration/targets/elb_instance/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/elb_instance/tasks/main.yml new file mode 100644 index 000000000..247b6f6b6 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elb_instance/tasks/main.yml @@ -0,0 +1,29 @@ +--- +- module_defaults: + group/aws: + region: "{{ aws_region }}" + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + collections: + - community.aws + - amazon.aws + block: + # ============================================================ + + - include_tasks: setup_vpc.yml + - include_tasks: setup_elbs.yml + - include_tasks: setup_instances.yml + + # ============================================================ + + - include_tasks: manage_instances.yml + - include_tasks: manage_asgs.yml + + always: + + # ============================================================ + + - include_tasks: cleanup_elbs.yml + - include_tasks: cleanup_instances.yml + - include_tasks: cleanup_vpc.yml diff --git a/ansible_collections/community/aws/tests/integration/targets/elb_instance/tasks/manage_asgs.yml b/ansible_collections/community/aws/tests/integration/targets/elb_instance/tasks/manage_asgs.yml new file mode 100644 index 000000000..f0e9db601 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elb_instance/tasks/manage_asgs.yml @@ -0,0 +1,111 @@ +--- +- name: Get ASG info + ec2_asg_info: + name: "{{ asg_name }}$" + register: asg_info + +- name: Store Instance ID from ASG + set_fact: + instance_asg: '{{ asg_info.results[0].instances[0].instance_id }}' + +- name: 'Remove an instance from ELB (check_mode)' + elb_instance: + instance_id: '{{ instance_asg }}' + state: 'absent' + wait_timeout: 60 + register: remove_instance + check_mode: true + +- elb_classic_lb_info: + names: '{{ elb_name_1 }}' + register: elb_info_1 +- elb_classic_lb_info: + names: '{{ elb_name_2 }}' + register: elb_info_2 + +- assert: + that: + - remove_instance is successful + - remove_instance is changed + - '"updated_elbs" in remove_instance' + - elb_name_1 in remove_instance.updated_elbs + - elb_name_2 in remove_instance.updated_elbs + # Check the real state didn't change + - instance_asg in elb_info_1.elbs[0].instances_inservice + - instance_asg in elb_info_2.elbs[0].instances_inservice + +- name: 'Remove an instance from ELB' + elb_instance: + instance_id: '{{ instance_asg }}' + state: 'absent' + wait_timeout: 60 + register: remove_instance + +- elb_classic_lb_info: + names: '{{ elb_name_1 }}' + register: elb_info_1 +- elb_classic_lb_info: + names: '{{ elb_name_2 }}' + register: elb_info_2 + +- assert: + that: + - remove_instance is successful + - remove_instance is changed + - '"updated_elbs" in remove_instance' + - elb_name_1 in remove_instance.updated_elbs + - elb_name_2 in remove_instance.updated_elbs + # Check the real state + - instance_asg not in elb_info_1.elbs[0].instances_inservice + - instance_asg not in elb_info_2.elbs[0].instances_inservice + +- name: 'Remove an instance from ELB - idempotency (check_mode)' + elb_instance: + instance_id: '{{ instance_asg }}' + state: 'absent' + wait_timeout: 60 + register: remove_instance + check_mode: true + +- elb_classic_lb_info: + names: '{{ elb_name_1 }}' + register: elb_info_1 +- elb_classic_lb_info: + names: '{{ elb_name_2 }}' + register: elb_info_2 + +- assert: + that: + - remove_instance is successful + - remove_instance is not changed + - '"updated_elbs" in remove_instance' + - elb_name_1 not in remove_instance.updated_elbs + - elb_name_2 not in remove_instance.updated_elbs + # Check the real state + - instance_asg not in elb_info_1.elbs[0].instances_inservice + - instance_asg not in elb_info_2.elbs[0].instances_inservice + +- name: 'Remove an instance from ELB - idempotency' + elb_instance: + instance_id: '{{ instance_asg }}' + state: 'absent' + wait_timeout: 60 + register: remove_instance + +- elb_classic_lb_info: + names: '{{ elb_name_1 }}' + register: elb_info_1 +- elb_classic_lb_info: + names: '{{ elb_name_2 }}' + register: elb_info_2 + +- assert: + that: + - remove_instance is successful + - remove_instance is not changed + - '"updated_elbs" in remove_instance' + - elb_name_1 not in remove_instance.updated_elbs + - elb_name_2 not in remove_instance.updated_elbs + # Check the real state + - instance_asg not in elb_info_1.elbs[0].instances_inservice + - instance_asg not in elb_info_2.elbs[0].instances_inservice diff --git a/ansible_collections/community/aws/tests/integration/targets/elb_instance/tasks/manage_instances.yml b/ansible_collections/community/aws/tests/integration/targets/elb_instance/tasks/manage_instances.yml new file mode 100644 index 000000000..6e617de40 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elb_instance/tasks/manage_instances.yml @@ -0,0 +1,481 @@ +--- +- name: 'Wait for instances to be OK' + ec2_instance: + instance_ids: + - '{{ instance_a }}' + - '{{ instance_b }}' + state: 'started' + +- name: 'Add an instance to two ELBs (check_mode)' + elb_instance: + instance_id: '{{ instance_a }}' + state: 'present' + ec2_elbs: + - '{{ elb_name_1 }}' + - '{{ elb_name_2 }}' + wait_timeout: 60 + register: add_instance + check_mode: true + +- elb_classic_lb_info: + names: '{{ elb_name_1 }}' + register: elb_info_1 +- elb_classic_lb_info: + names: '{{ elb_name_2 }}' + register: elb_info_2 + +- assert: + that: + - add_instance is successful + - add_instance is changed + - '"updated_elbs" in add_instance' + - elb_name_1 in add_instance.updated_elbs + - elb_name_2 in add_instance.updated_elbs + # Check the real state didn't change + - instance_a not in elb_info_1.elbs[0].instances_inservice + - instance_a not in elb_info_2.elbs[0].instances_inservice + +- name: 'Add an instance to two ELBs' + elb_instance: + instance_id: '{{ instance_a }}' + state: 'present' + ec2_elbs: + - '{{ elb_name_1 }}' + - '{{ elb_name_2 }}' + wait_timeout: 60 + register: add_instance + +- elb_classic_lb_info: + names: '{{ elb_name_1 }}' + register: elb_info_1 +- elb_classic_lb_info: + names: '{{ elb_name_2 }}' + register: elb_info_2 + +- assert: + that: + - add_instance is successful + - add_instance is changed + - '"updated_elbs" in add_instance' + - elb_name_1 in add_instance.updated_elbs + - elb_name_2 in add_instance.updated_elbs + # Check the real state + - instance_a in elb_info_1.elbs[0].instances_inservice + - instance_a in elb_info_2.elbs[0].instances_inservice + +- name: 'Add an instance to two ELBs - idempotency (check_mode)' + elb_instance: + instance_id: '{{ instance_a }}' + state: 'present' + ec2_elbs: + - '{{ elb_name_1 }}' + - '{{ elb_name_2 }}' + wait_timeout: 60 + register: add_instance + check_mode: true + +- elb_classic_lb_info: + names: '{{ elb_name_1 }}' + register: elb_info_1 +- elb_classic_lb_info: + names: '{{ elb_name_2 }}' + register: elb_info_2 + +- assert: + that: + - add_instance is successful + - add_instance is not changed + - '"updated_elbs" in add_instance' + - elb_name_1 not in add_instance.updated_elbs + - elb_name_2 not in add_instance.updated_elbs + # Check the real state didn't change + - instance_a in elb_info_1.elbs[0].instances_inservice + - instance_a in elb_info_2.elbs[0].instances_inservice + +- name: 'Add an instance to two ELBs - idempotency' + elb_instance: + instance_id: '{{ instance_a }}' + state: 'present' + ec2_elbs: + - '{{ elb_name_1 }}' + - '{{ elb_name_2 }}' + wait_timeout: 60 + register: add_instance + +- elb_classic_lb_info: + names: '{{ elb_name_1 }}' + register: elb_info_1 +- elb_classic_lb_info: + names: '{{ elb_name_2 }}' + register: elb_info_2 + +- assert: + that: + - add_instance is successful + - add_instance is not changed + - '"updated_elbs" in add_instance' + - elb_name_1 not in add_instance.updated_elbs + - elb_name_2 not in add_instance.updated_elbs + # Check the real state didn't change + - instance_a in elb_info_1.elbs[0].instances_inservice + - instance_a in elb_info_2.elbs[0].instances_inservice + +############################################################################### + +- name: 'Add an instance to an ELB in wrong AZ (check_mode)' + elb_instance: + instance_id: '{{ instance_b }}' + state: 'present' + ec2_elbs: + - '{{ elb_name_1 }}' + wait_timeout: 60 + register: add_instance + check_mode: true + ignore_errors: true + +- elb_classic_lb_info: + names: '{{ elb_name_1 }}' + register: elb_info_1 + +- assert: + that: + # - add_instance is failed + # Check the real state didn't change + - instance_b not in elb_info_1.elbs[0].instances_inservice + +- name: 'Add an instance to an ELB in wrong AZ' + elb_instance: + instance_id: '{{ instance_b }}' + state: 'present' + ec2_elbs: + - '{{ elb_name_1 }}' + wait_timeout: 60 + register: add_instance + ignore_errors: true + +- elb_classic_lb_info: + names: '{{ elb_name_1 }}' + register: elb_info_1 + +- assert: + that: + - add_instance is failed + - instance_b not in elb_info_1.elbs[0].instances_inservice + +################################################################################ +# Currently doesn't support adding an AZ if you're not using the default VPC +# +# - name: 'Add an instance to an ELB with enable_availability_zone (check_mode)' +# elb_instance: +# instance_id: '{{ instance_b }}' +# state: 'present' +# ec2_elbs: +# - '{{ elb_name_1 }}' +# wait_timeout: 60 +# register: add_instance +# check_mode: true +# ignore_errors: true +# +# - elb_classic_lb_info: +# names: '{{ elb_name_1 }}' +# register: elb_info_1 +# +# - assert: +# that: +# - add_instance is successful +# # - add_instance is changed +# +# - name: 'Add an instance to an ELB with enable_availability_zone' +# elb_instance: +# instance_id: '{{ instance_b }}' +# state: 'present' +# ec2_elbs: +# - '{{ elb_name_1 }}' +# wait_timeout: 60 +# register: add_instance +# ignore_errors: true +# +# - elb_classic_lb_info: +# names: '{{ elb_name_1 }}' +# register: elb_info_1 +# +# - assert: +# that: +# - add_instance is successful +# - add_instance is changed +# +# - name: 'Add an instance to an ELB with enable_availability_zone - idempotency (check_mode)' +# elb_instance: +# instance_id: '{{ instance_b }}' +# state: 'present' +# ec2_elbs: +# - '{{ elb_name_1 }}' +# wait_timeout: 60 +# register: add_instance +# check_mode: true +# ignore_errors: true +# +# - elb_classic_lb_info: +# names: '{{ elb_name_2 }}' +# register: elb_info_1 +# +# - assert: +# that: +# - add_instance is successful +# - add_instance is not changed +# +# - name: 'Add an instance to an ELB with enable_availability_zone - idempotency' +# elb_instance: +# instance_id: '{{ instance_b }}' +# state: 'present' +# ec2_elbs: +# - '{{ elb_name_1 }}' +# wait_timeout: 60 +# register: add_instance +# ignore_errors: true +# +# - elb_classic_lb_info: +# names: '{{ elb_name_1 }}' +# register: elb_info_1 +# +# - assert: +# that: +# - add_instance is successful +# - add_instance is not changed +# +################################################################################ + +- name: 'Remove an instance from two ELBs (check_mode)' + elb_instance: + instance_id: '{{ instance_a }}' + state: 'absent' + ec2_elbs: + - '{{ elb_name_1 }}' + - '{{ elb_name_2 }}' + wait_timeout: 60 + register: remove_instance + check_mode: true + +- elb_classic_lb_info: + names: '{{ elb_name_1 }}' + register: elb_info_1 +- elb_classic_lb_info: + names: '{{ elb_name_2 }}' + register: elb_info_2 + +- assert: + that: + - remove_instance is successful + - remove_instance is changed + - '"updated_elbs" in remove_instance' + - elb_name_1 in remove_instance.updated_elbs + - elb_name_2 in remove_instance.updated_elbs + # Check the real state didn't change + - instance_a in elb_info_1.elbs[0].instances_inservice + - instance_a in elb_info_2.elbs[0].instances_inservice + +- name: 'Remove an instance from two ELBs' + elb_instance: + instance_id: '{{ instance_a }}' + state: 'absent' + ec2_elbs: + - '{{ elb_name_1 }}' + - '{{ elb_name_2 }}' + wait_timeout: 60 + register: remove_instance + +- elb_classic_lb_info: + names: '{{ elb_name_1 }}' + register: elb_info_1 +- elb_classic_lb_info: + names: '{{ elb_name_2 }}' + register: elb_info_2 + +- assert: + that: + - remove_instance is successful + - remove_instance is changed + - '"updated_elbs" in remove_instance' + - elb_name_1 in remove_instance.updated_elbs + - elb_name_2 in remove_instance.updated_elbs + # Check the real state + - instance_a not in elb_info_1.elbs[0].instances_inservice + - instance_a not in elb_info_2.elbs[0].instances_inservice + +- name: 'Remove an instance from two ELBs - idempotency (check_mode)' + elb_instance: + instance_id: '{{ instance_a }}' + state: 'absent' + ec2_elbs: + - '{{ elb_name_1 }}' + - '{{ elb_name_2 }}' + wait_timeout: 60 + register: remove_instance + check_mode: true + +- elb_classic_lb_info: + names: '{{ elb_name_1 }}' + register: elb_info_1 +- elb_classic_lb_info: + names: '{{ elb_name_2 }}' + register: elb_info_2 + +- assert: + that: + - remove_instance is successful + - remove_instance is not changed + - '"updated_elbs" in remove_instance' + - elb_name_1 not in remove_instance.updated_elbs + - elb_name_2 not in remove_instance.updated_elbs + # Check the real state didn't change + - instance_a not in elb_info_1.elbs[0].instances_inservice + - instance_a not in elb_info_2.elbs[0].instances_inservice + +- name: 'Remove an instance from two ELBs - idempotency' + elb_instance: + instance_id: '{{ instance_a }}' + state: 'absent' + ec2_elbs: + - '{{ elb_name_1 }}' + - '{{ elb_name_2 }}' + wait_timeout: 60 + register: remove_instance + +- elb_classic_lb_info: + names: '{{ elb_name_1 }}' + register: elb_info_1 +- elb_classic_lb_info: + names: '{{ elb_name_2 }}' + register: elb_info_2 + +- assert: + that: + - remove_instance is successful + - remove_instance is not changed + - '"updated_elbs" in remove_instance' + - elb_name_1 not in remove_instance.updated_elbs + - elb_name_2 not in remove_instance.updated_elbs + # Check the real state didn't change + - instance_a not in elb_info_1.elbs[0].instances_inservice + - instance_a not in elb_info_2.elbs[0].instances_inservice + +################################################################################ + +- name: 'Add second instance to one ELB' + elb_instance: + instance_id: '{{ instance_b }}' + state: 'present' + ec2_elbs: + - '{{ elb_name_2 }}' + wait_timeout: 60 + register: add_instance + +- assert: + that: + - add_instance is successful + - add_instance is changed + - '"updated_elbs" in add_instance' + - elb_name_1 not in add_instance.updated_elbs + - elb_name_2 in add_instance.updated_elbs + +- name: 'Remove an instance without specifying ELBs (check_mode)' + elb_instance: + instance_id: '{{ instance_b }}' + state: 'absent' + wait_timeout: 60 + register: remove_instance + check_mode: true + +- elb_classic_lb_info: + names: '{{ elb_name_1 }}' + register: elb_info_1 +- elb_classic_lb_info: + names: '{{ elb_name_2 }}' + register: elb_info_2 + +- assert: + that: + - remove_instance is successful + - remove_instance is changed + - '"updated_elbs" in remove_instance' + - elb_name_1 not in remove_instance.updated_elbs + - elb_name_2 in remove_instance.updated_elbs + # Check the real state didn't change + - instance_b not in elb_info_1.elbs[0].instances_inservice + - instance_b in elb_info_2.elbs[0].instances_inservice + +- name: 'Remove an instance without specifying ELBs' + elb_instance: + instance_id: '{{ instance_b }}' + state: 'absent' + wait_timeout: 60 + register: remove_instance + +- elb_classic_lb_info: + names: '{{ elb_name_1 }}' + register: elb_info_1 +- elb_classic_lb_info: + names: '{{ elb_name_2 }}' + register: elb_info_2 + +- assert: + that: + - remove_instance is successful + - remove_instance is changed + - '"updated_elbs" in remove_instance' + - elb_name_1 not in remove_instance.updated_elbs + - elb_name_2 in remove_instance.updated_elbs + # Check the real state + - instance_b not in elb_info_1.elbs[0].instances_inservice + - instance_b not in elb_info_2.elbs[0].instances_inservice + +- name: 'Remove an instance without specifying ELBs - idempotency (check_mode)' + elb_instance: + instance_id: '{{ instance_b }}' + state: 'absent' + wait_timeout: 60 + register: remove_instance + check_mode: true + +- elb_classic_lb_info: + names: '{{ elb_name_1 }}' + register: elb_info_1 +- elb_classic_lb_info: + names: '{{ elb_name_2 }}' + register: elb_info_2 + +- assert: + that: + - remove_instance is successful + - remove_instance is not changed + - '"updated_elbs" in remove_instance' + - elb_name_1 not in remove_instance.updated_elbs + - elb_name_2 not in remove_instance.updated_elbs + # Check the real state didn't change + - instance_b not in elb_info_1.elbs[0].instances_inservice + - instance_b not in elb_info_2.elbs[0].instances_inservice + +- name: 'Remove an instance without specifying ELBs - idempotency' + elb_instance: + instance_id: '{{ instance_b }}' + state: 'absent' + wait_timeout: 60 + register: remove_instance + +- elb_classic_lb_info: + names: '{{ elb_name_1 }}' + register: elb_info_1 +- elb_classic_lb_info: + names: '{{ elb_name_2 }}' + register: elb_info_2 + +- assert: + that: + - remove_instance is successful + - remove_instance is not changed + - '"updated_elbs" in remove_instance' + - elb_name_1 not in remove_instance.updated_elbs + - elb_name_2 not in remove_instance.updated_elbs + # Check the real state didn't change + - instance_b not in elb_info_1.elbs[0].instances_inservice + - instance_b not in elb_info_2.elbs[0].instances_inservice diff --git a/ansible_collections/community/aws/tests/integration/targets/elb_instance/tasks/setup_elbs.yml b/ansible_collections/community/aws/tests/integration/targets/elb_instance/tasks/setup_elbs.yml new file mode 100644 index 000000000..2e820f820 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elb_instance/tasks/setup_elbs.yml @@ -0,0 +1,35 @@ +--- + +- name: Create a private load balancer with 1 AZ enabled + elb_classic_lb: + name: "{{ elb_name_1 }}" + state: present + scheme: 'internal' + subnets: ['{{ subnet_a }}'] + listeners: '{{ elb_listeners }}' + wait: true + health_check: + ping_protocol: TCP + ping_port: 22 + healthy_threshold: 2 + unhealthy_threshold: 2 + interval: 5 + timeout: 2 + register: result + +- name: Create a private load balancer with 2 AZs enabled + elb_classic_lb: + name: "{{ elb_name_2 }}" + state: present + scheme: 'internal' + subnets: ['{{ subnet_a }}', '{{ subnet_b }}'] + listeners: '{{ elb_listeners }}' + wait: true + health_check: + ping_protocol: TCP + ping_port: 22 + healthy_threshold: 2 + unhealthy_threshold: 2 + interval: 5 + timeout: 2 + register: result diff --git a/ansible_collections/community/aws/tests/integration/targets/elb_instance/tasks/setup_instances.yml b/ansible_collections/community/aws/tests/integration/targets/elb_instance/tasks/setup_instances.yml new file mode 100644 index 000000000..b89b38d20 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elb_instance/tasks/setup_instances.yml @@ -0,0 +1,52 @@ +--- +- name: Create instance a + ec2_instance: + name: "{{ instance_name_1 }}" + image_id: "{{ ec2_ami_id }}" + vpc_subnet_id: "{{ subnet_a }}" + instance_type: t3.micro + wait: false + security_group: "{{ sg_a }}" + register: ec2_instance_a + +- name: Create instance b + ec2_instance: + name: "{{ instance_name_2 }}" + image_id: "{{ ec2_ami_id }}" + vpc_subnet_id: "{{ subnet_b }}" + instance_type: t3.micro + wait: false + security_group: "{{ sg_a }}" + register: ec2_instance_b + +- name: store the Instance IDs + set_fact: + instance_a: "{{ ec2_instance_a.instance_ids[0] }}" + instance_b: "{{ ec2_instance_b.instance_ids[0] }}" + +- name: Create a Launch Template + ec2_lc: + name: "{{ lc_name }}" + image_id: "{{ ec2_ami_id }}" + security_groups: "{{ sg_a }}" + instance_type: t3.micro + assign_public_ip: no + register: ec2_lc_a + +- name: Create an ASG + ec2_asg: + name: "{{ asg_name }}" + load_balancers: + - "{{ elb_name_1 }}" + - "{{ elb_name_2 }}" + launch_config_name: "{{ lc_name }}" + availability_zones: + - "{{ availability_zone_a }}" + min_size: 0 + max_size: 1 + desired_capacity: 1 + wait_timeout: 600 + health_check_period: 60 + vpc_zone_identifier: + - "{{ subnet_a }}" + register: ec2_asg_a diff --git a/ansible_collections/community/aws/tests/integration/targets/elb_instance/tasks/setup_vpc.yml b/ansible_collections/community/aws/tests/integration/targets/elb_instance/tasks/setup_vpc.yml new file mode 100644 index 000000000..26fafa41c --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elb_instance/tasks/setup_vpc.yml @@ -0,0 +1,65 @@ +--- +# SETUP: vpc, subnet, security group +- name: create a VPC to work in + ec2_vpc_net: + cidr_block: '{{ vpc_cidr }}' + state: present + name: '{{ vpc_name }}' + resource_tags: + Name: '{{ vpc_name }}' + register: setup_vpc + +- name: create a subnet + ec2_vpc_subnet: + az: '{{ availability_zone_a }}' + tags: '{{ resource_prefix }}' + vpc_id: '{{ setup_vpc.vpc.id }}' + cidr: '{{ subnet_cidr_1 }}' + state: present + resource_tags: + Name: '{{ subnet_name_1 }}' + register: setup_subnet_1 + +- name: create a subnet + ec2_vpc_subnet: + az: '{{ availability_zone_b }}' + tags: '{{ resource_prefix }}' + vpc_id: '{{ setup_vpc.vpc.id }}' + cidr: '{{ subnet_cidr_2 }}' + state: present + resource_tags: + Name: '{{ subnet_name_2 }}' + register: setup_subnet_2 + +- name: create a security group + ec2_group: + name: '{{ sg_name_1 }}' + description: 'created by Ansible integration tests' + state: present + vpc_id: '{{ setup_vpc.vpc.id }}' + rules: + - proto: tcp + from_port: 22 + to_port: 22 + cidr_ip: '{{ vpc_cidr }}' + register: setup_sg_1 + +- name: create a security group + ec2_group: + name: '{{ sg_name_2 }}' + description: 'created by Ansible integration tests' + state: present + vpc_id: '{{ setup_vpc.vpc.id }}' + rules: + - proto: tcp + from_port: 22 + to_port: 22 + cidr_ip: '{{ vpc_cidr }}' + register: setup_sg_2 + +- name: store the IDs + set_fact: + subnet_a: "{{ setup_subnet_1.subnet.id }}" + subnet_b: "{{ setup_subnet_2.subnet.id }}" + sg_a: "{{ setup_sg_1.group_id }}" + sg_b: "{{ setup_sg_2.group_id }}" diff --git a/ansible_collections/community/aws/tests/integration/targets/elb_instance/vars/main.yml b/ansible_collections/community/aws/tests/integration/targets/elb_instance/vars/main.yml new file mode 100644 index 000000000..ed97d539c --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elb_instance/vars/main.yml @@ -0,0 +1 @@ +--- diff --git a/ansible_collections/community/aws/tests/integration/targets/elb_network_lb/aliases b/ansible_collections/community/aws/tests/integration/targets/elb_network_lb/aliases new file mode 100644 index 000000000..04b4c46b5 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elb_network_lb/aliases @@ -0,0 +1,2 @@ +time=10m +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/elb_network_lb/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/elb_network_lb/defaults/main.yml new file mode 100644 index 000000000..83e944041 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elb_network_lb/defaults/main.yml @@ -0,0 +1,7 @@ +--- +# load balancer and target group names have to be less than 32 characters +# the 8 digit identifier at the end of resource_prefix helps determine during which test something +# was created and allows tests to be run in parallel +nlb_name: "nlb-{{ tiny_prefix }}" +tg_name: "nlb-{{ tiny_prefix }}" +tg_tcpudp_name: "nlb-tcp-udp-{{ tiny_prefix }}" diff --git a/ansible_collections/community/aws/tests/integration/targets/elb_network_lb/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/elb_network_lb/meta/main.yml new file mode 100644 index 000000000..fd89b0e4f --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elb_network_lb/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - setup_ec2_facts + - setup_remote_tmp_dir diff --git a/ansible_collections/community/aws/tests/integration/targets/elb_network_lb/tasks/generate-certs.yml b/ansible_collections/community/aws/tests/integration/targets/elb_network_lb/tasks/generate-certs.yml new file mode 100644 index 000000000..a79289a95 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elb_network_lb/tasks/generate-certs.yml @@ -0,0 +1,52 @@ +################################################ +# Setup SSL certs to store in IAM +################################################ +- name: 'Generate SSL Keys' + community.crypto.openssl_privatekey: + path: '{{ remote_tmp_dir }}/{{ item }}-key.pem' + size: 4096 + loop: + - 'ca' + - 'cert1' + +- name: 'Generate CSRs' + community.crypto.openssl_csr: + path: '{{ remote_tmp_dir }}/{{ item }}.csr' + privatekey_path: '{{ remote_tmp_dir }}/{{ item }}-key.pem' + common_name: '{{ item }}.ansible.test' + subject_alt_name: 'DNS:{{ item }}.ansible.test' + basic_constraints: + - 'CA:TRUE' + loop: + - 'ca' + - 'cert1' + +- name: 'Self-sign the "root"' + community.crypto.x509_certificate: + provider: selfsigned + path: '{{ remote_tmp_dir }}/ca.pem' + privatekey_path: '{{ remote_tmp_dir }}/ca-key.pem' + csr_path: '{{ remote_tmp_dir }}/ca.csr' + +- name: 'Sign the cert' + community.crypto.x509_certificate: + provider: ownca + path: '{{ remote_tmp_dir }}/cert1.pem' + csr_path: '{{ remote_tmp_dir }}/cert1.csr' + ownca_path: '{{ remote_tmp_dir }}/ca.pem' + ownca_privatekey_path: '{{ remote_tmp_dir }}/ca-key.pem' + +- set_fact: + path_ca_cert: '{{ remote_tmp_dir }}/ca.pem' + path_ca_key: '{{ remote_tmp_dir }}/ca-key.pem' + path_cert_pem: '{{ remote_tmp_dir }}/cert1.pem' + path_cert_key: '{{ remote_tmp_dir }}/cert1-key.pem' + + +- name: create certificate + iam_server_certificate: + name: 'ansible-test-nlb-{{ tiny_prefix }}' + state: present + cert: "{{ lookup('file', path_cert_pem) }}" + key: "{{ lookup('file', path_cert_key) }}" + register: cert diff --git a/ansible_collections/community/aws/tests/integration/targets/elb_network_lb/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/elb_network_lb/tasks/main.yml new file mode 100644 index 000000000..cf0a13ec4 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elb_network_lb/tasks/main.yml @@ -0,0 +1,246 @@ +--- +- name: 'elb_network_lb integration tests' + collections: + - amazon.aws + module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + block: + + - include_tasks: generate-certs.yml + + - name: create VPC + ec2_vpc_net: + cidr_block: 10.228.228.0/22 + name: "{{ resource_prefix }}_vpc" + ipv6_cidr: true + state: present + register: vpc + + - name: create internet gateway + ec2_vpc_igw: + vpc_id: "{{ vpc.vpc.id }}" + state: present + tags: + Name: "{{ resource_prefix }}" + register: igw + + - name: create private subnets + ec2_vpc_subnet: + cidr: "{{ item.cidr }}" + az: "{{ aws_region}}{{ item.az }}" + vpc_id: "{{ vpc.vpc.id }}" + state: present + tags: + Created_By: "{{ resource_prefix }}" + Public: "{{ item.public }}" + with_items: + - cidr: 10.228.230.0/24 + az: "a" + public: False + - cidr: 10.228.231.0/24 + az: "b" + public: False + register: subnets + + - name: create public subnets with ipv6 + ec2_vpc_subnet: + cidr: '{{ item.cidr }}' + az: '{{ aws_region}}{{ item.az }}' + vpc_id: '{{ vpc.vpc.id }}' + state: present + ipv6_cidr: '{{ item.vpc_ipv6_cidr }}' + tags: + Public: '{{ item.public|string }}' + Name: '{{ item.public|ternary(''public'', ''private'') }}-{{ item.az }}' + Created_By: "{{ resource_prefix }}" + with_items: + - cidr: 10.228.228.0/24 + az: "a" + public: True + vpc_ipv6_cidr: "{{ vpc.vpc.ipv6_cidr_block_association_set[0].ipv6_cidr_block | replace('0::/56','0::/64') }}" + - cidr: 10.228.229.0/24 + az: "b" + public: True + vpc_ipv6_cidr: "{{ vpc.vpc.ipv6_cidr_block_association_set[0].ipv6_cidr_block | replace('0::/56','1::/64') }}" + + - ec2_vpc_subnet_info: + filters: + vpc-id: "{{ vpc.vpc.id }}" + register: vpc_subnets + + - name: create list of subnet ids + set_fact: + nlb_subnets: "{{ vpc_subnets|community.general.json_query('subnets[?tags.Public == `True`].id') }}" + private_subnets: "{{ vpc_subnets|community.general.json_query('subnets[?tags.Public != `True`].id') }}" + + - name: create a route table + ec2_vpc_route_table: + vpc_id: "{{ vpc.vpc.id }}" + tags: + Name: igw-route + Created: "{{ resource_prefix }}" + subnets: "{{ nlb_subnets + private_subnets }}" + routes: + - dest: 0.0.0.0/0 + gateway_id: "{{ igw.gateway_id }}" + register: route_table + + - ec2_group: + name: "{{ resource_prefix }}" + description: "security group for Ansible NLB integration tests" + state: present + vpc_id: "{{ vpc.vpc.id }}" + rules: + - proto: tcp + from_port: 1 + to_port: 65535 + cidr_ip: 0.0.0.0/0 + - proto: all + ports: 80 + cidr_ip: 10.228.228.0/22 + register: sec_group + + - name: create a target group for testing + elb_target_group: + name: "{{ tg_name }}" + protocol: tcp + port: 80 + vpc_id: "{{ vpc.vpc.id }}" + state: present + register: tg + + - name: create a target group for testing tcp_udp protocols + elb_target_group: + name: "{{ tg_tcpudp_name }}" + protocol: tcp_udp + port: 80 + vpc_id: "{{ vpc.vpc.id }}" + state: present + register: tg_tcpudp + + - include_tasks: test_nlb_bad_listener_options.yml + - include_tasks: test_nlb_ip_address_type_option.yml + - include_tasks: test_nlb_tags.yml + - include_tasks: test_creating_nlb.yml + - include_tasks: test_nlb_with_asg.yml + - include_tasks: test_modifying_nlb_listeners.yml + - include_tasks: test_deleting_nlb.yml + + always: + + - name: destroy NLB + elb_network_lb: + name: "{{ nlb_name }}" + state: absent + wait: yes + wait_timeout: 600 + ignore_errors: yes + + - name: destroy target group if it was created + elb_target_group: + name: "{{ tg_name }}" + protocol: tcp + port: 80 + vpc_id: "{{ vpc.vpc.id }}" + state: absent + wait: yes + wait_timeout: 600 + register: remove_tg + retries: 5 + delay: 3 + until: remove_tg is success + when: tg is defined + ignore_errors: yes + + - name: destroy tcp_udp target group if it was created + elb_target_group: + name: "{{ tg_tcpudp_name }}" + protocol: tcp_udp + port: 80 + vpc_id: "{{ vpc.vpc.id }}" + state: absent + wait: yes + wait_timeout: 600 + register: remove_tg + retries: 5 + delay: 3 + until: remove_tg is success + when: tg_tcpudp is defined + ignore_errors: yes + + - name: destroy sec group + ec2_group: + name: "{{ sec_group.group_name }}" + description: "security group for Ansible NLB integration tests" + state: absent + vpc_id: "{{ vpc.vpc.id }}" + register: remove_sg + retries: 10 + delay: 5 + until: remove_sg is success + ignore_errors: yes + + - name: remove route table + ec2_vpc_route_table: + vpc_id: "{{ vpc.vpc.id }}" + route_table_id: "{{ route_table.route_table.route_table_id }}" + lookup: id + state: absent + register: remove_rt + retries: 10 + delay: 5 + until: remove_rt is success + ignore_errors: yes + + - name: destroy subnets + ec2_vpc_subnet: + cidr: "{{ item.cidr }}" + vpc_id: "{{ vpc.vpc.id }}" + state: absent + register: remove_subnet + retries: 10 + delay: 5 + until: remove_subnet is success + with_items: + - cidr: 10.228.228.0/24 + - cidr: 10.228.229.0/24 + - cidr: 10.228.230.0/24 + - cidr: 10.228.231.0/24 + ignore_errors: yes + + - name: destroy internet gateway + ec2_vpc_igw: + vpc_id: "{{ vpc.vpc.id }}" + tags: + Name: "{{ resource_prefix }}" + state: absent + register: remove_igw + retries: 10 + delay: 5 + until: remove_igw is success + ignore_errors: yes + + - name: destroy VPC + ec2_vpc_net: + cidr_block: 10.228.228.0/22 + name: "{{ resource_prefix }}_vpc" + state: absent + register: remove_vpc + retries: 10 + delay: 5 + until: remove_vpc is success + ignore_errors: yes + + - name: destroy certificate + iam_server_certificate: + name: 'ansible-test-nlb-{{ tiny_prefix }}' + state: absent + register: remove_cert + ignore_errors: yes + retries: 10 + delay: 5 + until: remove_cert is success diff --git a/ansible_collections/community/aws/tests/integration/targets/elb_network_lb/tasks/test_creating_nlb.yml b/ansible_collections/community/aws/tests/integration/targets/elb_network_lb/tasks/test_creating_nlb.yml new file mode 100644 index 000000000..743857add --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elb_network_lb/tasks/test_creating_nlb.yml @@ -0,0 +1,71 @@ +- block: + + - name: create NLB with listeners + elb_network_lb: + name: "{{ nlb_name }}" + subnets: "{{ nlb_subnets }}" + state: present + listeners: + - Protocol: TCP + Port: 80 + DefaultActions: + - Type: forward + TargetGroupName: "{{ tg_name }}" + - Protocol: TLS + Port: 443 + Certificates: + - CertificateArn: "{{ cert.arn }}" + DefaultActions: + - Type: forward + TargetGroupName: "{{ tg_name }}" + - Protocol: UDP + Port: 13 + DefaultActions: + - Type: forward + TargetGroupName: "{{ tg_tcpudp_name }}" + - Protocol: TCP_UDP + Port: 17 + DefaultActions: + - Type: forward + TargetGroupName: "{{ tg_tcpudp_name }}" + register: nlb + + - assert: + that: + - nlb.changed + - nlb.listeners|length == 4 + + - name: test idempotence creating NLB with listeners + elb_network_lb: + name: "{{ nlb_name }}" + subnets: "{{ nlb_subnets }}" + state: present + listeners: + - Protocol: TCP + Port: 80 + DefaultActions: + - Type: forward + TargetGroupName: "{{ tg_name }}" + - Protocol: TLS + Port: 443 + Certificates: + - CertificateArn: "{{ cert.arn }}" + DefaultActions: + - Type: forward + TargetGroupName: "{{ tg_name }}" + - Protocol: UDP + Port: 13 + DefaultActions: + - Type: forward + TargetGroupName: "{{ tg_tcpudp_name }}" + - Protocol: TCP_UDP + Port: 17 + DefaultActions: + - Type: forward + TargetGroupName: "{{ tg_tcpudp_name }}" + register: nlb + + - assert: + that: + - not nlb.changed + - nlb.listeners|length == 4 diff --git a/ansible_collections/community/aws/tests/integration/targets/elb_network_lb/tasks/test_deleting_nlb.yml b/ansible_collections/community/aws/tests/integration/targets/elb_network_lb/tasks/test_deleting_nlb.yml new file mode 100644 index 000000000..65c5a2198 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elb_network_lb/tasks/test_deleting_nlb.yml @@ -0,0 +1,39 @@ +- block: + + - name: destroy NLB with listener + elb_network_lb: + name: "{{ nlb_name }}" + subnets: "{{ nlb_subnets }}" + state: absent + listeners: + - Protocol: TCP + Port: 80 + DefaultActions: + - Type: forward + TargetGroupName: "{{ tg_name }}" + wait: yes + wait_timeout: 300 + register: nlb + + - assert: + that: + - nlb.changed + + - name: test idempotence + elb_network_lb: + name: "{{ nlb_name }}" + subnets: "{{ nlb_subnets }}" + state: absent + listeners: + - Protocol: TCP + Port: 80 + DefaultActions: + - Type: forward + TargetGroupName: "{{ tg_name }}" + wait: yes + wait_timeout: 300 + register: nlb + + - assert: + that: + - not nlb.changed diff --git a/ansible_collections/community/aws/tests/integration/targets/elb_network_lb/tasks/test_modifying_nlb_listeners.yml b/ansible_collections/community/aws/tests/integration/targets/elb_network_lb/tasks/test_modifying_nlb_listeners.yml new file mode 100644 index 000000000..9877e3f1b --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elb_network_lb/tasks/test_modifying_nlb_listeners.yml @@ -0,0 +1,75 @@ +- block: + + - name: add a listener + elb_network_lb: + name: "{{ nlb_name }}" + subnets: "{{ nlb_subnets }}" + state: present + listeners: + - Protocol: TCP + Port: 80 + DefaultActions: + - Type: forward + TargetGroupName: "{{ tg_name }}" + - Protocol: TCP + Port: 443 + DefaultActions: + - Type: forward + TargetGroupName: "{{ tg_name }}" + register: nlb + + - assert: + that: + - nlb.changed + - nlb.listeners|length == 2 + + - name: test an omitted listener will not be removed without purge_listeners + elb_network_lb: + name: "{{ nlb_name }}" + subnets: "{{ nlb_subnets }}" + state: present + purge_listeners: false + listeners: + - Protocol: TCP + Port: 80 + DefaultActions: + - Type: forward + TargetGroupName: "{{ tg_name }}" + register: nlb + + - assert: + that: + - not nlb.changed + - nlb.listeners|length == 2 + + - name: remove the rule + elb_network_lb: + name: "{{ nlb_name }}" + subnets: "{{ nlb_subnets }}" + state: present + purge_listeners: true + listeners: + - Protocol: TCP + Port: 80 + DefaultActions: + - Type: forward + TargetGroupName: "{{ tg_name }}" + register: nlb + + - assert: + that: + - nlb.changed + - nlb.listeners|length == 1 + + - name: remove listener from NLB + elb_network_lb: + name: "{{ nlb_name }}" + subnets: "{{ nlb_subnets }}" + state: present + listeners: [] + register: nlb + + - assert: + that: + - nlb.changed + - not nlb.listeners diff --git a/ansible_collections/community/aws/tests/integration/targets/elb_network_lb/tasks/test_nlb_bad_listener_options.yml b/ansible_collections/community/aws/tests/integration/targets/elb_network_lb/tasks/test_nlb_bad_listener_options.yml new file mode 100644 index 000000000..1648acd08 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elb_network_lb/tasks/test_nlb_bad_listener_options.yml @@ -0,0 +1,60 @@ +- block: + + - name: test creating an NLB with invalid listener options + elb_network_lb: + name: "{{ nlb_name }}" + subnets: "{{ nlb_subnets }}" + #security_groups: "{{ sec_group.group_id }}" + state: present + listeners: + - Protocol: TCP + Port: 80 + Certificates: {'CertificateArn': 'test', 'IsDefault': 'True'} + DefaultActions: + - Type: forward + TargetGroupName: "{{ tg_name }}" + ignore_errors: yes + register: nlb + + - assert: + that: + - nlb is failed + - "'unable to convert to list' in nlb.msg" + + - name: test creating an NLB without providing required listener options + elb_network_lb: + name: "{{ nlb_name }}" + subnets: "{{ nlb_subnets }}" + #security_groups: "{{ sec_group.group_id }}" + state: present + listeners: + - Port: 80 + ignore_errors: yes + register: nlb + + - assert: + that: + - nlb is failed + - '"missing required arguments" in nlb.msg' + - '"Protocol" in nlb.msg' + - '"DefaultActions" in nlb.msg' + + - name: test creating an NLB providing an invalid listener option type + elb_network_lb: + name: "{{ nlb_name }}" + subnets: "{{ nlb_subnets }}" + #security_groups: "{{ sec_group.group_id }}" + state: present + listeners: + - Protocol: TCP + Port: "bad type" + DefaultActions: + - Type: forward + TargetGroupName: "{{ tg_name }}" + ignore_errors: yes + register: nlb + + - assert: + that: + - nlb is failed + - "'unable to convert to int' in nlb.msg" diff --git a/ansible_collections/community/aws/tests/integration/targets/elb_network_lb/tasks/test_nlb_ip_address_type_option.yml b/ansible_collections/community/aws/tests/integration/targets/elb_network_lb/tasks/test_nlb_ip_address_type_option.yml new file mode 100644 index 000000000..0399e58b1 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elb_network_lb/tasks/test_nlb_ip_address_type_option.yml @@ -0,0 +1,92 @@ +- block: + - name: set NLB name for ipv6 + set_fact: + nlb_name_ipv6: "{{ nlb_name ~ 'ipv6' }}" + + - name: Create NLB with invalid ip address type + elb_network_lb: + name: "{{ nlb_name_ipv6 }}" + subnets: "{{ nlb_subnets }}" + state: present + listeners: + - Protocol: TCP + Port: 80 + DefaultActions: + - Type: forward + TargetGroupName: "{{ tg_name }}" + ip_address_type: "ipv6" + ignore_errors: true + register: nlb + + - assert: + that: + - nlb is failed + + - name: Create NLB with dualstack ip address type + elb_network_lb: + name: "{{ nlb_name_ipv6 }}" + subnets: "{{ nlb_subnets }}" + state: present + listeners: + - Protocol: TCP + Port: 80 + DefaultActions: + - Type: forward + TargetGroupName: "{{ tg_name }}" + ip_address_type: "dualstack" + wait: true + register: nlb + + - assert: + that: + - nlb.ip_address_type == "dualstack" + + - name: Update NLB with ipv4 address type + elb_network_lb: + name: "{{ nlb_name_ipv6 }}" + subnets: "{{ nlb_subnets }}" + state: present + listeners: + - Protocol: TCP + Port: 80 + DefaultActions: + - Type: forward + TargetGroupName: "{{ tg_name }}" + ip_address_type: "ipv4" + wait: true + register: nlb + + - assert: + that: + - nlb.changed + - nlb.ip_address_type == "ipv4" + + - name: Test idempotence when updating NLB + elb_network_lb: + name: "{{ nlb_name_ipv6 }}" + subnets: "{{ nlb_subnets }}" + state: present + listeners: + - Protocol: TCP + Port: 80 + DefaultActions: + - Type: forward + TargetGroupName: "{{ tg_name }}" + ip_address_type: "ipv4" + wait: true + register: nlb + + - assert: + that: + - not nlb.changed + - nlb.ip_address_type == "ipv4" + + always: + # Cleanup + - name: destroy NLB if created + elb_network_lb: + name: '{{ nlb_name_ipv6 }}' + state: absent + wait: true + wait_timeout: 600 + ignore_errors: true diff --git a/ansible_collections/community/aws/tests/integration/targets/elb_network_lb/tasks/test_nlb_tags.yml b/ansible_collections/community/aws/tests/integration/targets/elb_network_lb/tasks/test_nlb_tags.yml new file mode 100644 index 000000000..b55a0777f --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elb_network_lb/tasks/test_nlb_tags.yml @@ -0,0 +1,85 @@ +- block: + + - name: create NLB with no listeners + elb_network_lb: + name: "{{ nlb_name }}" + subnets: "{{ nlb_subnets }}" + state: present + register: nlb + + - assert: + that: + - nlb.changed + + - name: re-create NLB with no listeners + elb_network_lb: + name: "{{ nlb_name }}" + subnets: "{{ nlb_subnets }}" + state: present + register: nlb + + - assert: + that: + - not nlb.changed + + - name: add tags to NLB + elb_network_lb: + name: "{{ nlb_name }}" + subnets: "{{ nlb_subnets }}" + state: present + tags: + created_by: "NLB test {{ resource_prefix }}" + register: nlb + + - assert: + that: + - nlb.changed + - 'nlb.tags.created_by == "NLB test {{ resource_prefix }}"' + + - name: test tags are not removed if unspecified + elb_network_lb: + name: "{{ nlb_name }}" + subnets: "{{ nlb_subnets }}" + state: present + register: nlb + + - assert: + that: + - not nlb.changed + - 'nlb.tags.created_by == "NLB test {{ resource_prefix }}"' + + - name: remove tags from NLB + elb_network_lb: + name: "{{ nlb_name }}" + subnets: "{{ nlb_subnets }}" + state: present + tags: {} + register: nlb + + - assert: + that: + - nlb.changed + - not nlb.tags + + - name: test idempotence + elb_network_lb: + name: "{{ nlb_name }}" + subnets: "{{ nlb_subnets }}" + state: present + tags: {} + register: nlb + + - assert: + that: + - not nlb.changed + - not nlb.tags + + - name: destroy NLB with no listeners + elb_network_lb: + name: "{{ nlb_name }}" + state: absent + register: nlb + + - assert: + that: + - nlb.changed diff --git a/ansible_collections/community/aws/tests/integration/targets/elb_network_lb/tasks/test_nlb_with_asg.yml b/ansible_collections/community/aws/tests/integration/targets/elb_network_lb/tasks/test_nlb_with_asg.yml new file mode 100644 index 000000000..06fab22b5 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elb_network_lb/tasks/test_nlb_with_asg.yml @@ -0,0 +1,62 @@ +- block: + + # create instances + - ec2_asg: + state: absent + name: "{{ resource_prefix }}-webservers" + wait_timeout: 900 + + - ec2_lc: + name: "{{ resource_prefix }}-web-lcfg" + state: absent + + - name: Create launch config for testing + ec2_lc: + name: "{{ resource_prefix }}-web-lcfg" + assign_public_ip: true + image_id: "{{ ec2_ami_id }}" + security_groups: "{{ sec_group.group_id }}" + instance_type: t2.micro + user_data: | + #!/bin/bash + set -x + yum update -y --nogpgcheck + yum install -y --nogpgcheck httpd + echo "Hello Ansiblings!" >> /var/www/html/index.html + service httpd start + volumes: + - device_name: /dev/xvda + volume_size: 10 + volume_type: gp2 + delete_on_termination: true + + - name: Create autoscaling group for app server fleet + ec2_asg: + name: "{{ resource_prefix }}-webservers" + vpc_zone_identifier: "{{ nlb_subnets }}" + launch_config_name: "{{ resource_prefix }}-web-lcfg" + termination_policies: + - OldestLaunchConfiguration + - Default + health_check_period: 600 + health_check_type: EC2 + replace_all_instances: true + min_size: 0 + max_size: 2 + desired_capacity: 1 + wait_for_instances: true + target_group_arns: + - "{{ tg.target_group_arn }}" + + always: + + - ec2_asg: + state: absent + name: "{{ resource_prefix }}-webservers" + wait_timeout: 900 + ignore_errors: yes + + - ec2_lc: + name: "{{ resource_prefix }}-web-lcfg" + state: absent + ignore_errors: yes diff --git a/ansible_collections/community/aws/tests/integration/targets/elb_target/aliases b/ansible_collections/community/aws/tests/integration/targets/elb_target/aliases new file mode 100644 index 000000000..8ac98fca7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elb_target/aliases @@ -0,0 +1,5 @@ +cloud/aws + +slow +elb_target_group +elb_target_group_info diff --git a/ansible_collections/community/aws/tests/integration/targets/elb_target/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/elb_target/defaults/main.yml new file mode 100644 index 000000000..5c21c956c --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elb_target/defaults/main.yml @@ -0,0 +1,15 @@ +unique_id: "ansible-test-{{ tiny_prefix }}" + +# Defaults used by the lambda based test + +lambda_role_name: '{{ unique_id }}-elb-target' +lambda_name: '{{ unique_id }}-elb-target' +elb_target_group_name: "{{ unique_id }}-elb" + +# Defaults used by the EC2 based test +tg_name: "{{ unique_id }}-tg" +tg_used_name: "{{ unique_id }}-tgu" +tg_tcpudp_name: "{{ unique_id }}-udp" +lb_name: "{{ unique_id }}-lb" +healthy_state: + state: 'healthy' diff --git a/ansible_collections/community/aws/tests/integration/targets/elb_target/files/ansible_lambda_target.py b/ansible_collections/community/aws/tests/integration/targets/elb_target/files/ansible_lambda_target.py new file mode 100644 index 000000000..3ea22472e --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elb_target/files/ansible_lambda_target.py @@ -0,0 +1,10 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type +import json + + +def lambda_handler(event, context): + return { + 'statusCode': 200, + 'body': json.dumps('Hello from Lambda!') + } diff --git a/ansible_collections/community/aws/tests/integration/targets/elb_target/files/assume-role.json b/ansible_collections/community/aws/tests/integration/targets/elb_target/files/assume-role.json new file mode 100644 index 000000000..06456f799 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elb_target/files/assume-role.json @@ -0,0 +1,8 @@ +{ + "Version": "2012-10-17", + "Statement": { + "Effect": "Allow", + "Principal": { "Service": "lambda.amazonaws.com" }, + "Action": "sts:AssumeRole" + } +} diff --git a/ansible_collections/community/aws/tests/integration/targets/elb_target/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/elb_target/meta/main.yml new file mode 100644 index 000000000..1471b11f6 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elb_target/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - setup_ec2_facts diff --git a/ansible_collections/community/aws/tests/integration/targets/elb_target/tasks/alb_target.yml b/ansible_collections/community/aws/tests/integration/targets/elb_target/tasks/alb_target.yml new file mode 100644 index 000000000..d3638a63c --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elb_target/tasks/alb_target.yml @@ -0,0 +1,205 @@ +--- +- name: test elb_target_group with target_type = alb + block: + - name: set up testing VPC + ec2_vpc_net: + name: "{{ resource_prefix }}-vpc" + state: present + cidr_block: 20.0.0.0/16 + tags: + Name: "{{ resource_prefix }}-vpc" + Description: "Created by ansible-test" + register: vpc + + - name: set up testing internet gateway + ec2_vpc_igw: + vpc_id: "{{ vpc.vpc.id }}" + state: present + register: igw + + - name: set up testing subnet + ec2_vpc_subnet: + state: present + vpc_id: "{{ vpc.vpc.id }}" + cidr: 20.0.0.0/18 + az: "{{ aws_region }}a" + resource_tags: + Name: "{{ resource_prefix }}-subnet" + register: subnet_1 + + - name: set up testing subnet + ec2_vpc_subnet: + state: present + vpc_id: "{{ vpc.vpc.id }}" + cidr: 20.0.64.0/18 + az: "{{ aws_region }}b" + resource_tags: + Name: "{{ resource_prefix }}-subnet" + register: subnet_2 + + - name: create routing rules + ec2_vpc_route_table: + vpc_id: "{{ vpc.vpc.id }}" + tags: + created: "{{ resource_prefix }}-route" + routes: + - dest: 0.0.0.0/0 + gateway_id: "{{ igw.gateway_id }}" + subnets: + - "{{ subnet_1.subnet.id }}" + - "{{ subnet_2.subnet.id }}" + register: route_table + + - name: create testing security group + ec2_group: + name: "{{ resource_prefix }}-sg" + description: a security group for ansible tests + vpc_id: "{{ vpc.vpc.id }}" + rules: + - proto: tcp + from_port: 80 + to_port: 80 + cidr_ip: 0.0.0.0/0 + - proto: tcp + from_port: 22 + to_port: 22 + cidr_ip: 0.0.0.0/0 + register: sg + + - name: set up testing target group for NLB (type=alb) + elb_target_group: + name: "{{ elb_target_group_name }}" + target_type: alb + state: present + protocol: TCP + port: 80 + vpc_id: "{{ vpc.vpc.id }}" + register: elb_target_group + + - name: assert target group was created successfully + assert: + that: + - elb_target_group.changed + - elb_target_group.target_group_name == elb_target_group_name + - elb_target_group.target_type == 'alb' + - elb_target_group.vpc_id == vpc.vpc.id + - elb_target_group.port == 80 + - elb_target_group.protocol == 'TCP' + - elb_target_group.load_balancer_arns | length == 0 + + - name: create a network load balancer and attach to target group + elb_network_lb: + name: "{{ lb_name }}-nlb" + subnets: + - "{{ subnet_1.subnet.id }}" + - "{{ subnet_2.subnet.id }}" + listeners: + - Protocol: TCP + Port: 80 + DefaultActions: + - Type: forward + TargetGroupName: "{{ elb_target_group_name }}" + state: present + register: nlb + + - name: assert NLB was created successfully and attached to target group + assert: + that: + - nlb is changed + - nlb.listeners | length == 1 + - nlb.listeners[0].default_actions[0].forward_config.target_groups[0].target_group_arn == elb_target_group.target_group_arn + + - name: get target group info + elb_target_group_info: + load_balancer_arn: "{{ nlb.load_balancer_arn }}" + register: tg_info + + - name: assert target group's target is nlb + assert: + that: + - tg_info.target_groups[0].target_group_name == elb_target_group_name + - tg_info.target_groups[0].target_type == 'alb' + - tg_info.target_groups[0].load_balancer_arns | length == 1 + - tg_info.target_groups[0].load_balancer_arns[0] == nlb.load_balancer_arn + + always: + - name: remove network load balancer + elb_network_lb: + name: "{{ lb_name }}-nlb" + state: absent + wait: true + wait_timeout: 600 + register: removed + retries: 10 + until: removed is not failed + ignore_errors: true + + - name: remove elb target group + elb_target_group: + name: "{{ elb_target_group_name }}" + target_type: alb + state: absent + protocol: HTTP + port: 80 + vpc_id: "{{ vpc.vpc.id }}" + ignore_errors: true + + - name: remove routing rules + ec2_vpc_route_table: + state: absent + lookup: id + route_table_id: "{{ route_table.route_table.id }}" + register: removed + retries: 5 + until: removed is not failed + ignore_errors: true + + - name: remove testing subnet + ec2_vpc_subnet: + state: absent + vpc_id: "{{ vpc.vpc.id }}" + cidr: 20.0.0.0/18 + az: "{{ aws_region }}a" + register: removed + retries: 10 + until: removed is not failed + ignore_errors: true + + - name: remove testing subnet + ec2_vpc_subnet: + state: absent + vpc_id: "{{ vpc.vpc.id }}" + cidr: 20.0.64.0/18 + az: "{{ aws_region }}b" + register: removed + retries: 10 + until: removed is not failed + ignore_errors: true + + - name: remove testing security group + ec2_group: + state: absent + name: "{{ resource_prefix }}-sg" + register: removed + retries: 10 + until: removed is not failed + ignore_errors: true + + - name: remove testing internet gateway + ec2_vpc_igw: + vpc_id: "{{ vpc.vpc.id }}" + state: absent + register: removed + retries: 2 + until: removed is not failed + ignore_errors: true + + - name: remove testing VPC + ec2_vpc_net: + name: "{{ resource_prefix }}-vpc" + cidr_block: 20.0.0.0/16 + state: absent + register: removed + retries: 2 + until: removed is not failed + ignore_errors: true
\ No newline at end of file diff --git a/ansible_collections/community/aws/tests/integration/targets/elb_target/tasks/ec2_target.yml b/ansible_collections/community/aws/tests/integration/targets/elb_target/tasks/ec2_target.yml new file mode 100644 index 000000000..611aca26f --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elb_target/tasks/ec2_target.yml @@ -0,0 +1,633 @@ +--- +- name: set up ec2 based test prerequisites + block: + # ============================================================ + + - name: + debug: msg="********** Setting up elb_target EC2 test dependencies **********" + + # ============================================================ + + - name: set up testing VPC + ec2_vpc_net: + name: "{{ resource_prefix }}-vpc" + state: present + cidr_block: 20.0.0.0/16 + tags: + Name: "{{ resource_prefix }}-vpc" + Description: "Created by ansible-test" + register: vpc + + - name: set up testing internet gateway + ec2_vpc_igw: + vpc_id: "{{ vpc.vpc.id }}" + state: present + register: igw + + - name: set up testing subnet + ec2_vpc_subnet: + state: present + vpc_id: "{{ vpc.vpc.id }}" + cidr: 20.0.0.0/18 + az: "{{ aws_region }}a" + resource_tags: + Name: "{{ resource_prefix }}-subnet" + register: subnet_1 + + - name: set up testing subnet + ec2_vpc_subnet: + state: present + vpc_id: "{{ vpc.vpc.id }}" + cidr: 20.0.64.0/18 + az: "{{ aws_region }}b" + resource_tags: + Name: "{{ resource_prefix }}-subnet" + register: subnet_2 + + - name: create routing rules + ec2_vpc_route_table: + vpc_id: "{{ vpc.vpc.id }}" + tags: + created: "{{ resource_prefix }}-route" + routes: + - dest: 0.0.0.0/0 + gateway_id: "{{ igw.gateway_id }}" + subnets: + - "{{ subnet_1.subnet.id }}" + - "{{ subnet_2.subnet.id }}" + register: route_table + + - name: create testing security group + ec2_group: + name: "{{ resource_prefix }}-sg" + description: a security group for ansible tests + vpc_id: "{{ vpc.vpc.id }}" + rules: + - proto: tcp + from_port: 80 + to_port: 80 + cidr_ip: 0.0.0.0/0 + - proto: tcp + from_port: 22 + to_port: 22 + cidr_ip: 0.0.0.0/0 + register: sg + + - name: set up testing target group (type=instance) + elb_target_group: + name: "{{ tg_name }}" + health_check_port: 80 + protocol: http + port: 80 + vpc_id: "{{ vpc.vpc.id }}" + state: present + target_type: instance + tags: + Description: "Created by {{ resource_prefix }}" + + - name: set up testing target group (type=instance) with UDP protocol + elb_target_group: + name: "{{ tg_tcpudp_name }}" + protocol: udp + port: 53 + vpc_id: "{{ vpc.vpc.id }}" + state: present + target_type: instance + tags: + Protocol: "UDP" + Description: "Created by {{ resource_prefix }}" + + - name: set up testing target group for ALB (type=instance) + elb_target_group: + name: "{{ tg_used_name }}" + health_check_port: 80 + protocol: http + port: 80 + vpc_id: "{{ vpc.vpc.id }}" + state: present + target_type: instance + tags: + Description: "Created by {{ resource_prefix }}" + register: result + + - name: set up testing target group for ALB (type=instance) + assert: + that: + - result.changed + - result.target_group_name == tg_used_name + - result.target_type == 'instance' + - result.vpc_id == vpc.vpc.id + - result.port == 80 + - result.load_balancing_algorithm_type == 'round_robin' # default + - '"health_check_port" in result' + - '"tags" in result' + + - name: set up testing target group for NLB (type=instance) + elb_target_group: + name: "{{ tg_name }}-nlb" + health_check_port: 80 + protocol: tcp + port: 80 + vpc_id: "{{ vpc.vpc.id }}" + state: present + target_type: instance + deregistration_delay_timeout: 60 + deregistration_connection_termination: yes + tags: + Description: "Created by {{ resource_prefix }}" + register: result + + - name: set up testing target group for NLB (type=instance) + assert: + that: + - result.changed + - '"health_check_port" in result' + - result.port == 80 + - '"health_check_protocol" in result' + - result.health_check_protocol == 'TCP' + - '"tags" in result' + - '"target_group_arn" in result' + - result.target_group_name == "{{ tg_name }}-nlb" + - result.target_type == 'instance' + - result.deregistration_delay_timeout_seconds == '60' + - result.deregistration_delay_connection_termination_enabled + - result.vpc_id == vpc.vpc.id + + - name: set up ec2 instance to use as a target + amazon.aws.ec2_instance: + name: "{{ resource_prefix }}-inst" + security_group: "{{ sg.group_id }}" + instance_type: t3.micro + image_id: "{{ ec2_ami_id }}" + vpc_subnet_id: "{{ subnet_2.subnet.id }}" + network: + assign_public_ip: true + volumes: [] + wait: true + ebs_optimized: false + tags: + Name: "{{ resource_prefix }}-inst" + user_data: | + #!/bin/bash + sudo nohup python3 -m http.server 80 & + register: ec2 + + - set_fact: + instance_id: "{{ ec2.instances[0].instance_id }}" + + - name: create an application load balancer + elb_application_lb: + name: "{{ lb_name }}" + security_groups: + - "{{ sg.group_id }}" + subnets: + - "{{ subnet_1.subnet.id }}" + - "{{ subnet_2.subnet.id }}" + listeners: + - Protocol: HTTP + Port: 80 + DefaultActions: + - Type: forward + TargetGroupName: "{{ tg_used_name }}" + state: present + + - name: create a network load balancer + elb_network_lb: + name: "{{ lb_name }}-nlb" + subnets: + - "{{ subnet_1.subnet.id }}" + - "{{ subnet_2.subnet.id }}" + listeners: + - Protocol: TCP + Port: 80 + DefaultActions: + - Type: forward + TargetGroupName: "{{ tg_name }}-nlb" + state: present + register: result + + - name: create a netwok load balancer + assert: + that: + - result.changed + - '"created_time" in result' + - '"load_balancer_arn" in result' + - '"tags" in result' + - result.type == 'network' + - result.vpc_id == '{{ vpc.vpc.id }}' + + - name: modify up testing target group for NLB (preserve_client_ip_enabled=false) + elb_target_group: + name: "{{ tg_name }}-nlb" + health_check_port: 80 + protocol: tcp + port: 80 + vpc_id: "{{ vpc.vpc.id }}" + state: present + target_type: instance + modify_targets: true + preserve_client_ip_enabled: false + tags: + Description: "Created by {{ resource_prefix }}" + register: result + + - name: modify up testing target group for NLB (preserve_client_ip_enabled=false) + assert: + that: + - result.changed + - result.preserve_client_ip_enabled == 'false' + - result.proxy_protocol_v2_enabled == 'false' + + - name: modify up testing target group for NLB (proxy_protocol_v2_enabled=true) + elb_target_group: + name: "{{ tg_name }}-nlb" + health_check_port: 80 + protocol: tcp + port: 80 + vpc_id: "{{ vpc.vpc.id }}" + state: present + target_type: instance + modify_targets: true + proxy_protocol_v2_enabled: true + tags: + Description: "Created by {{ resource_prefix }}" + register: result + + - name: modify up testing target group for NLB (proxy_protocol_v2_enabled=true) + assert: + that: + - result.changed + - result.proxy_protocol_v2_enabled == 'true' + - result.preserve_client_ip_enabled == 'false' + + - name: (idempotence) modify up testing target group for NLB (preserve_client_ip_enabled=false and proxy_protocol_v2_enabled=true) + elb_target_group: + name: "{{ tg_name }}-nlb" + health_check_port: 80 + protocol: tcp + port: 80 + vpc_id: "{{ vpc.vpc.id }}" + state: present + target_type: instance + modify_targets: true + preserve_client_ip_enabled: false + proxy_protocol_v2_enabled: true + tags: + Description: "Created by {{ resource_prefix }}" + register: result + + - name: (idempotence) modify up testing target group for NLB (preserve_client_ip_enabled=false and proxy_protocol_v2_enabled=true) + assert: + that: + - not result.changed + - result.proxy_protocol_v2_enabled == 'true' + - result.preserve_client_ip_enabled == 'false' + + # Modify target group atrributes ============================= + # TODO: Add check_mode support + + - name: update target group atrributes + elb_target_group: + name: "{{ tg_used_name }}" + health_check_port: 80 + protocol: http + port: 80 + vpc_id: "{{ vpc.vpc.id }}" + load_balancing_algorithm_type: least_outstanding_requests + state: present + target_type: instance + tags: + Description: "Created by {{ resource_prefix }}" + register: result + + - name: update target group atrributes + assert: + that: + - result.changed + - result.target_group_name == tg_used_name + - result.load_balancing_algorithm_type == 'least_outstanding_requests' + + - name: update target group atrributes - idempotence + elb_target_group: + name: "{{ tg_used_name }}" + health_check_port: 80 + protocol: http + port: 80 + vpc_id: "{{ vpc.vpc.id }}" + load_balancing_algorithm_type: least_outstanding_requests + state: present + target_type: instance + tags: + Description: "Created by {{ resource_prefix }}" + register: result + + - name: update target group atrributes - idempotence + assert: + that: + - not result.changed + - result.load_balancing_algorithm_type == 'least_outstanding_requests' + + # ============================================================ + + - name: + debug: msg="********** Running elb_target integration tests **********" + + # ============================================================ + + - name: register an instance to unused target group + elb_target: + target_group_name: "{{ tg_name }}" + target_id: "{{ instance_id }}" + state: present + register: result + + - name: target is registered + assert: + that: + - result.changed + - result.target_group_arn + - result.target_health_descriptions.target.id == instance_id + + # ============================================================ + + - name: test idempotence + elb_target: + target_group_name: "{{ tg_name }}" + target_id: "{{ instance_id }}" + state: present + register: result + + - name: target was already registered + assert: + that: + - not result.changed + + # ============================================================ + + - name: remove an unused target + elb_target: + target_group_name: "{{ tg_name }}" + target_id: "{{ instance_id }}" + state: absent + deregister_unused: true + register: result + + - name: target group was deleted + assert: + that: + - result.changed + - not result.target_health_descriptions + + # ============================================================ + + - name: register an instance to used target group and wait until healthy + elb_target: + target_group_name: "{{ tg_used_name }}" + target_id: "{{ instance_id }}" + state: present + target_status: healthy + target_status_timeout: 400 + register: result + + - name: target is registered + assert: + that: + - result.changed + - result.target_group_arn + - result.target_health_descriptions.target.id == instance_id + - result.target_health_descriptions.target_health == healthy_state + + # ============================================================ + + - name: remove a target from used target group + elb_target: + target_group_name: "{{ tg_used_name }}" + target_id: "{{ instance_id }}" + state: absent + target_status: unused + target_status_timeout: 400 + register: result + + - name: target was deregistered + assert: + that: + - result.changed + + # ============================================================ + + - name: test idempotence + elb_target: + target_group_name: "{{ tg_used_name }}" + target_id: "{{ instance_id }}" + state: absent + register: result + + - name: target was already deregistered + assert: + that: + - not result.changed + + # ============================================================ + + - name: register an instance to used target group and wait until healthy again to test deregistering differently + elb_target: + target_group_name: "{{ tg_used_name }}" + target_id: "{{ instance_id }}" + state: present + target_status: healthy + target_status_timeout: 400 + register: result + + - name: target is registered + assert: + that: + - result.changed + - result.target_group_arn + - result.target_health_descriptions.target.id == instance_id + - result.target_health_descriptions.target_health == healthy_state + + - name: start deregisteration but don't wait + elb_target: + target_group_name: "{{ tg_used_name }}" + target_id: "{{ instance_id }}" + state: absent + register: result + + - name: target is starting to deregister + assert: + that: + - result.changed + - result.target_health_descriptions.target_health.reason == "Target.DeregistrationInProgress" + + - name: now wait for target to finish deregistering + elb_target: + target_group_name: "{{ tg_used_name }}" + target_id: "{{ instance_id }}" + state: absent + target_status: unused + target_status_timeout: 400 + register: result + + - name: target was deregistered already and now has finished + assert: + that: + - not result.changed + - not result.target_health_descriptions + + - name: create ip target group + elb_target_group: + name: "{{ tg_name }}-ip" + health_check_port: 443 + protocol: tcp + port: 443 + vpc_id: "{{ vpc.vpc.id }}" + state: present + target_type: ip + register: result + + - name: ip target group must be created + assert: + that: + - result.changed + - result.target_type == 'ip' + + - name: "mobify ip target group with AvailabilityZone: all" + elb_target_group: + name: "{{ tg_name }}-ip" + health_check_port: 443 + protocol: tcp + port: 443 + vpc_id: "{{ vpc.vpc.id }}" + state: present + target_type: ip + wait: false + modify_targets: true + targets: + - Id: 192.168.178.32 + Port: 443 + AvailabilityZone: all + register: result + + - name: ip target group must be modified + assert: + that: + - result.changed + - result.load_balancing_cross_zone_enabled == 'use_load_balancer_configuration' + + # ============================================================ + + always: + - name: + debug: msg="********** Tearing down elb_target test dependencies **********" + + - name: remove ec2 instance + amazon.aws.ec2_instance: + instance_ids: + - "{{ instance_id }}" + state: absent + wait: True + register: removed + retries: 10 + until: removed is not failed + ignore_errors: true + + - name: remove application load balancer + elb_application_lb: + name: "{{ lb_name }}" + state: absent + wait: true + wait_timeout: 600 + register: removed + retries: 10 + until: removed is not failed + ignore_errors: true + + - name: remove network load balancer + elb_network_lb: + name: "{{ lb_name }}-nlb" + state: absent + wait: true + wait_timeout: 600 + register: removed + retries: 10 + until: removed is not failed + ignore_errors: true + + - name: remove testing target groups + elb_target_group: + name: "{{ item }}" + state: absent + wait: true + wait_timeout: 600 + register: removed + retries: 10 + until: removed is not failed + with_items: + - "{{ tg_name }}" + - "{{ tg_used_name }}" + - "{{ tg_tcpudp_name }}" + - "{{ tg_name }}-nlb" + - "{{ tg_name }}-ip" + ignore_errors: true + + - name: remove routing rules + ec2_vpc_route_table: + state: absent + lookup: id + route_table_id: "{{ route_table.route_table.id }}" + register: removed + retries: 10 + until: removed is not failed + ignore_errors: true + + - name: remove testing subnet + ec2_vpc_subnet: + state: absent + vpc_id: "{{ vpc.vpc.id }}" + cidr: 20.0.0.0/18 + az: "{{ aws_region }}a" + register: removed + retries: 15 + until: removed is not failed + ignore_errors: true + + - name: remove testing subnet + ec2_vpc_subnet: + state: absent + vpc_id: "{{ vpc.vpc.id }}" + cidr: 20.0.64.0/18 + az: "{{ aws_region }}b" + register: removed + retries: 15 + until: removed is not failed + ignore_errors: true + + - name: remove testing security group + ec2_group: + state: absent + name: "{{ resource_prefix }}-sg" + register: removed + retries: 15 + until: removed is not failed + ignore_errors: true + + - name: remove testing internet gateway + ec2_vpc_igw: + vpc_id: "{{ vpc.vpc.id }}" + state: absent + register: removed + retries: 10 + until: removed is not failed + ignore_errors: true + + - name: remove testing VPC + ec2_vpc_net: + name: "{{ resource_prefix }}-vpc" + cidr_block: 20.0.0.0/16 + state: absent + register: removed + retries: 10 + until: removed is not failed + ignore_errors: true + + # ============================================================ diff --git a/ansible_collections/community/aws/tests/integration/targets/elb_target/tasks/etg_protocol_version.yml b/ansible_collections/community/aws/tests/integration/targets/elb_target/tasks/etg_protocol_version.yml new file mode 100644 index 000000000..7d4177131 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elb_target/tasks/etg_protocol_version.yml @@ -0,0 +1,77 @@ +--- +- name: Run elb_target_group protocol_version tests + block: + + # ===================================================================== + - name: set up testing VPC + ec2_vpc_net: + name: "{{ resource_prefix }}-etg-protocol-version-vpc" + state: present + cidr_block: 20.0.0.0/16 + tags: + Name: "{{ resource_prefix }}-etg-protocol-version-vpc" + Description: "Created by ansible-test" + register: vpc + + # ===================================================================== + - name: Create a target group with protocol_version 'GRPC' + community.aws.elb_target_group: + name: "{{ elb_target_group_name }}" + protocol: http + port: 80 + vpc_id: "{{ vpc.vpc.id }}" + protocol_version: GRPC + state: present + register: create_result + + - assert: + that: + - create_result is changed + - create_result is not failed + - create_result.protocol_version == "GRPC" + - create_result.protocol == "HTTP" + - create_result.port == 80 + + - name: Create a target group with protocol_version 'GRPC' (idempotence) + community.aws.elb_target_group: + name: "{{ elb_target_group_name }}" + protocol: http + port: 80 + vpc_id: "{{ vpc.vpc.id }}" + protocol_version: GRPC + state: present + register: create_result + + - assert: + that: + - create_result is not changed + - create_result is not failed + - create_result.protocol_version == "GRPC" + - create_result.protocol == "HTTP" + - create_result.port == 80 + + # ===================================================================== + + always: + - name: Remove elb target group + elb_target_group: + name: "{{ elb_target_group_name }}" + state: absent + ignore_errors: true + register: deletion_result + + - name: remove testing VPC + ec2_vpc_net: + name: "{{ resource_prefix }}-etg-protocol-version-vpc" + cidr_block: 20.0.0.0/16 + state: absent + register: removed + retries: 10 + until: removed is not failed + ignore_errors: true + + - name: Ensure elb_target_group deletion + assert: + that: + - deletion_result is changed + - deletion_result is not failed diff --git a/ansible_collections/community/aws/tests/integration/targets/elb_target/tasks/lambda_target.yml b/ansible_collections/community/aws/tests/integration/targets/elb_target/tasks/lambda_target.yml new file mode 100644 index 000000000..abc4cc5d0 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elb_target/tasks/lambda_target.yml @@ -0,0 +1,117 @@ +- name: set up lambda as elb_target + block: + - name: create zip to deploy lambda code + archive: + format: zip + path: "{{ role_path }}/files/ansible_lambda_target.py" + dest: "/tmp/lambda.zip" + + - name: create or update service-role for lambda + iam_role: + name: "{{ lambda_role_name }}" + assume_role_policy_document: '{{ lookup("file", role_path + "/files/assume-role.json") }}' + managed_policy: + - "arn:aws:iam::aws:policy/AWSXrayWriteOnlyAccess" + register: ROLE_ARN + + - name: when it is too fast, the role is not usable. + pause: + seconds: 10 + + - name: deploy lambda.zip to ansible_lambda_target function + lambda: + name: "{{ lambda_name }}" + state: present + zip_file: /tmp/lambda.zip + runtime: python3.7 + role: "{{ ROLE_ARN.arn }}" + handler: ansible_lambda_target.lambda_handler + timeout: 30 + register: lambda_function + retries: 3 + delay: 15 + until: lambda_function.changed + + - name: create empty target group + elb_target_group: + name: "{{ elb_target_group_name }}" + target_type: lambda + state: present + modify_targets: false + register: elb_target_group + + - name: tg is created, state must be changed + assert: + that: + - elb_target_group.changed + + - name: allow elb to invoke the lambda function + lambda_policy: + state: present + function_name: "{{ lambda_name }}" + version: "{{ lambda_function.configuration.version }}" + statement_id: elb1 + action: lambda:InvokeFunction + principal: elasticloadbalancing.amazonaws.com + source_arn: "{{ elb_target_group.target_group_arn }}" + + - name: add lambda to elb target + elb_target_group: + name: "{{ elb_target_group_name }}" + target_type: lambda + state: present + targets: + - Id: "{{ lambda_function.configuration.function_arn }}" + register: elb_target_group + + - name: target is updated, state must be changed + assert: + that: + - elb_target_group.changed + + - name: re-add lambda to elb target (idempotency) + elb_target_group: + name: "{{ elb_target_group_name }}" + target_type: lambda + state: present + targets: + - Id: "{{ lambda_function.configuration.function_arn }}" + register: elb_target_group + + - name: target is still the same, state must not be changed (idempotency) + assert: + that: + - not elb_target_group.changed + + - name: remove lambda target from target group + elb_target_group: + name: "{{ elb_target_group_name }}" + target_type: lambda + state: absent + targets: [] + register: elb_target_group + + - name: remove lambda target from target group + assert: + that: + - elb_target_group.changed + + always: + - name: remove elb target group + elb_target_group: + name: "{{ elb_target_group_name }}" + target_type: lambda + state: absent + ignore_errors: true + + - name: remove lambda function + lambda: + name: "{{ lambda_name }}" + state: absent + ignore_errors: true + + - name: remove iam role for lambda + iam_role: + name: "{{ lambda_role_name }}" + state: absent + ignore_errors: true diff --git a/ansible_collections/community/aws/tests/integration/targets/elb_target/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/elb_target/tasks/main.yml new file mode 100644 index 000000000..e99118c64 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elb_target/tasks/main.yml @@ -0,0 +1,16 @@ +--- +- name: set up elb_target test prerequisites + module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + collections: + - community.general + - amazon.aws + block: + - include_tasks: etg_protocol_version.yml + - include_tasks: ec2_target.yml + - include_tasks: lambda_target.yml + - include_tasks: alb_target.yml diff --git a/ansible_collections/community/aws/tests/integration/targets/elb_target_info/aliases b/ansible_collections/community/aws/tests/integration/targets/elb_target_info/aliases new file mode 100644 index 000000000..e42802725 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elb_target_info/aliases @@ -0,0 +1,4 @@ +# reason: missing-policy +unsupported + +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/elb_target_info/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/elb_target_info/defaults/main.yml new file mode 100644 index 000000000..3922dc360 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elb_target_info/defaults/main.yml @@ -0,0 +1,3 @@ +--- +tg_name: "ansible-test-{{ resource_prefix | regex_search('([0-9]+)$') }}-tg" +lb_name: "ansible-test-{{ resource_prefix | regex_search('([0-9]+)$') }}-lb" diff --git a/ansible_collections/community/aws/tests/integration/targets/elb_target_info/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/elb_target_info/meta/main.yml new file mode 100644 index 000000000..1471b11f6 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elb_target_info/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - setup_ec2_facts diff --git a/ansible_collections/community/aws/tests/integration/targets/elb_target_info/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/elb_target_info/tasks/main.yml new file mode 100644 index 000000000..fc11cdbcd --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/elb_target_info/tasks/main.yml @@ -0,0 +1,487 @@ +--- + - name: set up elb_target_info test prerequisites + module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + collections: + - amazon.aws + + block: + + # ============================================================ + + - name: + debug: msg="********** Setting up elb_target_info test dependencies **********" + + - name: set up testing VPC + ec2_vpc_net: + name: "{{ resource_prefix }}-vpc" + state: present + cidr_block: 20.0.0.0/16 + tags: + Name: "{{ resource_prefix }}-vpc" + Description: "Created by ansible-test" + register: vpc + + - name: set up testing internet gateway + ec2_vpc_igw: + vpc_id: "{{ vpc.vpc.id }}" + state: present + register: igw + + - name: set up testing subnet + ec2_vpc_subnet: + state: present + vpc_id: "{{ vpc.vpc.id }}" + cidr: 20.0.0.0/18 + az: "{{ aws_region }}a" + resource_tags: + Name: "{{ resource_prefix }}-subnet" + register: subnet_1 + + - name: set up testing subnet + ec2_vpc_subnet: + state: present + vpc_id: "{{ vpc.vpc.id }}" + cidr: 20.0.64.0/18 + az: "{{ aws_region }}b" + resource_tags: + Name: "{{ resource_prefix }}-subnet" + register: subnet_2 + + - name: create routing rules + ec2_vpc_route_table: + vpc_id: "{{ vpc.vpc.id }}" + tags: + created: "{{ resource_prefix }}-route" + routes: + - dest: 0.0.0.0/0 + gateway_id: "{{ igw.gateway_id }}" + subnets: + - "{{ subnet_1.subnet.id }}" + - "{{ subnet_2.subnet.id }}" + register: route_table + + - name: create testing security group + ec2_group: + name: "{{ resource_prefix }}-sg" + description: a security group for ansible tests + vpc_id: "{{ vpc.vpc.id }}" + rules: + - proto: tcp + from_port: 80 + to_port: 80 + cidr_ip: 0.0.0.0/0 + - proto: tcp + from_port: 22 + to_port: 22 + cidr_ip: 0.0.0.0/0 + register: sg + + - name: set up testing target group (type=instance) + register: alb_target_group + elb_target_group: + name: "{{ tg_name }}-inst" + health_check_port: 80 + protocol: http + port: 80 + vpc_id: '{{ vpc.vpc.id }}' + state: present + target_type: instance + # set this to 30 to test polling for changes, instead of having everything go out immediately + deregistration_delay_timeout: 30 + tags: + Description: "Created by {{ resource_prefix }}" + + - name: set up testing target group (type=ip) + register: nlb_target_group + elb_target_group: + name: "{{ tg_name }}-ip" + health_check_port: 80 + protocol: tcp + port: 80 + vpc_id: '{{ vpc.vpc.id }}' + state: present + # set this to 30 to test polling for changes, instead of having everything go out immediately + deregistration_delay_timeout: 30 + target_type: ip + tags: + Description: "Created by {{ resource_prefix }}" + + - name: set up testing target group which will not be associated with any load balancers + register: idle_target_group + elb_target_group: + name: "{{ tg_name }}-idle" + health_check_port: 80 + protocol: tcp + port: 80 + vpc_id: '{{ vpc.vpc.id }}' + state: present + target_type: instance + tags: + Description: "Created by {{ resource_prefix }}" + + - name: set up ec2 instance to use as a target + ec2_instance: + name: "{{ resource_prefix }}-inst" + security_group: "{{ sg.group_id }}" + instance_type: t3.micro + image_id: "{{ ec2_ami_id }}" + vpc_subnet_id: "{{ subnet_2.subnet.id }}" + network: + assign_public_ip: true + volumes: [] + wait: true + ebs_optimized: false + user_data: | + #cloud-config + package_upgrade: true + package_update: true + packages: + - httpd + runcmd: + - "service httpd start" + - echo "HELLO ANSIBLE" > /var/www/html/index.html + register: ec2 + + - set_fact: + instance_id: "{{ ec2.instances[0].instance_id }}" + + - name: create an application load balancer + elb_application_lb: + name: "{{ lb_name }}-alb" + security_groups: + - "{{ sg.group_id }}" + subnets: + - "{{ subnet_1.subnet.id }}" + - "{{ subnet_2.subnet.id }}" + listeners: + - Protocol: HTTP + Port: 80 + DefaultActions: + - Type: forward + TargetGroupName: "{{ tg_name }}-inst" + state: present + + + - name: create a network load balancer + elb_network_lb: + name: "{{ lb_name }}-nlb" + subnets: + - "{{ subnet_1.subnet.id }}" + - "{{ subnet_2.subnet.id }}" + listeners: + - Protocol: TCP + Port: 80 + DefaultActions: + - Type: forward + TargetGroupName: "{{ tg_name }}-ip" + state: present + + - name: register with the ALB + elb_target: + target_group_name: "{{ tg_name }}-inst" + target_id: "{{ instance_id }}" + state: present + target_status: "initial" + + - name: register with the NLB IP target group + elb_target: + target_group_name: "{{ tg_name }}-ip" + target_id: "{{ ec2.instances[0].private_ip_address }}" + state: present + target_status: "initial" + + # ============================================================ + + - debug: msg="********** Running elb_target_info integration tests **********" + + # ============================================================ + - name: gather facts + elb_target_info: + instance_id: "{{ instance_id }}" + register: target_facts + + - assert: + that: + - "{{ alb_target_group.target_group_arn in (target_facts.instance_target_groups | map(attribute='target_group_arn')) }}" + - "{{ nlb_target_group.target_group_arn in (target_facts.instance_target_groups | map(attribute='target_group_arn')) }}" + - "{{ idle_target_group.target_group_arn not in (target_facts.instance_target_groups | map(attribute='target_group_arn')) }}" + - (target_facts.instance_target_groups | length) == 2 + msg: "target facts showed the target in the right target groups" + + + - name: register with unused target group + elb_target: + target_group_name: "{{ tg_name }}-idle" + target_id: "{{ instance_id }}" + state: present + target_status: "unused" + + - name: gather facts again, including the idle group + elb_target_info: + instance_id: "{{ instance_id }}" + register: target_facts + + - assert: + that: + - "{{ alb_target_group.target_group_arn in (target_facts.instance_target_groups | map(attribute='target_group_arn')) }}" + - "{{ nlb_target_group.target_group_arn in (target_facts.instance_target_groups | map(attribute='target_group_arn')) }}" + - "{{ idle_target_group.target_group_arn in (target_facts.instance_target_groups | map(attribute='target_group_arn')) }}" + - (target_facts.instance_target_groups | length) == 3 + msg: "target facts reflected the addition of the target to the idle group" + + - name: gather facts again, this time excluding the idle group + elb_target_info: + instance_id: "{{ instance_id }}" + get_unused_target_groups: false + register: target_facts + + - assert: + that: + - "{{ alb_target_group.target_group_arn in (target_facts.instance_target_groups | map(attribute='target_group_arn')) }}" + - "{{ nlb_target_group.target_group_arn in (target_facts.instance_target_groups | map(attribute='target_group_arn')) }}" + - "{{ idle_target_group.target_group_arn not in (target_facts.instance_target_groups | map(attribute='target_group_arn')) }}" + - (target_facts.instance_target_groups | length) == 2 + msg: "target_facts.instance_target_groups did not gather unused target groups when variable was set" + + - name: register twice in the same target group + elb_target: + target_group_name: "{{ tg_name }}-ip" + target_port: 22 + target_id: "{{ ec2.instances[0].private_ip_address }}" + state: present + target_status: "healthy" + target_status_timeout: 400 + + - name: gather facts + elb_target_info: + instance_id: "{{ instance_id }}" + get_unused_target_groups: false + register: target_facts + + - assert: + that: + - alb_target_group.target_group_arn in (target_facts.instance_target_groups | map(attribute='target_group_arn')) + - nlb_target_group.target_group_arn in (target_facts.instance_target_groups | map(attribute='target_group_arn')) + - (target_facts.instance_target_groups | length) == 2 + - (target_facts.instance_target_groups | + selectattr('target_group_arn', 'equalto', nlb_target_group.target_group_arn) | + map(attribute='targets') | + flatten | + list | + length) == 2 + msg: "registering a target twice didn't affect the overall target group count, increased target count" + + - set_fact: + original_target_groups: "{{ target_facts.instance_target_groups }}" + + - name: Deregister instance from all target groups + elb_target: + target_group_arn: "{{ item.0.target_group_arn }}" + target_port: "{{ item.1.target_port }}" + target_az: "{{ item.1.target_az }}" + target_id: "{{ item.1.target_id }}" + state: absent + target_status: "draining" + with_subelements: + - "{{ original_target_groups }}" + - "targets" + + - name: wait for all targets to deregister simultaneously + elb_target_info: + get_unused_target_groups: false + instance_id: "{{ instance_id }}" + register: target_facts + until: (target_facts.instance_target_groups | length) == 0 + retries: 60 + delay: 10 + + - name: reregister in elbv2s + elb_target: + target_group_arn: "{{ item.0.target_group_arn }}" + target_port: "{{ item.1.target_port }}" + target_az: "{{ item.1.target_az }}" + target_id: "{{ item.1.target_id }}" + state: present + target_status: "initial" + with_subelements: + - "{{ original_target_groups }}" + - "targets" + + # wait until all groups associated with this instance are 'healthy' or + # 'unused' + - name: wait for registration + elb_target_info: + get_unused_target_groups: false + instance_id: "{{ instance_id }}" + register: target_facts + until: > + (target_facts.instance_target_groups | + map(attribute='targets') | + flatten | + map(attribute='target_health') | + rejectattr('state', 'equalto', 'healthy') | + rejectattr('state', 'equalto', 'unused') | + list | + length) == 0 + retries: 61 + delay: 10 + + - assert: + that: + - alb_target_group.target_group_arn in (target_facts.instance_target_groups | map(attribute='target_group_arn')) + - nlb_target_group.target_group_arn in (target_facts.instance_target_groups | map(attribute='target_group_arn')) + - (target_facts.instance_target_groups | length) == 2 + - (target_facts.instance_target_groups | + selectattr('target_group_arn', 'equalto', nlb_target_group.target_group_arn) | + map(attribute='targets') | + flatten | + list | + length) == 2 + msg: "reregistration completed successfully" + + always: + + - name: + debug: msg="********** Tearing down elb_target_info test dependencies **********" + + - name: remove ec2 instance + ec2_instance: + name: "{{ resource_prefix }}-inst" + state: absent + wait: True + ignore_errors: true + + - name: remove application load balancer + elb_application_lb: + name: "{{ lb_name }}-alb" + security_groups: + - "{{ sg.group_id }}" + subnets: + - "{{ subnet_1.subnet.id }}" + - "{{ subnet_2.subnet.id }}" + listeners: + - Protocol: HTTP + Port: 80 + DefaultActions: + - Type: forward + TargetGroupName: "{{ tg_name }}-inst" + state: absent + wait: true + wait_timeout: 200 + register: removed + retries: 10 + until: removed is not failed + ignore_errors: true + + - name: remove NLB + ignore_errors: true + elb_network_lb: + name: "{{ lb_name }}-nlb" + state: absent + + - name: remove testing target groups + elb_target_group: + name: "{{ item }}" + health_check_port: 80 + protocol: http + port: 80 + vpc_id: '{{ vpc.vpc.id }}' + state: absent + target_type: instance + tags: + Description: "Created by {{ resource_prefix }}" + wait: true + wait_timeout: 200 + register: removed + retries: 10 + until: removed is not failed + with_items: + - "{{ tg_name }}-idle" + - "{{ tg_name }}-ip" + - "{{ tg_name }}-inst" + ignore_errors: true + + - name: remove testing security group + ec2_group: + state: absent + name: "{{ resource_prefix }}-sg" + description: a security group for ansible tests + vpc_id: "{{ vpc.vpc.id }}" + rules: + - proto: tcp + from_port: 80 + to_port: 80 + cidr_ip: 0.0.0.0/0 + - proto: tcp + from_port: 22 + to_port: 22 + cidr_ip: 0.0.0.0/0 + register: removed + retries: 10 + until: removed is not failed + ignore_errors: true + + - name: remove routing rules + ec2_vpc_route_table: + state: absent + lookup: id + route_table_id: "{{ route_table.route_table.id }}" + register: removed + retries: 10 + until: removed is not failed + ignore_errors: true + + - name: remove testing subnet + ec2_vpc_subnet: + state: absent + vpc_id: "{{ vpc.vpc.id }}" + cidr: 20.0.0.0/18 + az: "{{ aws_region }}a" + resource_tags: + Name: "{{ resource_prefix }}-subnet" + register: removed + retries: 10 + until: removed is not failed + ignore_errors: true + + - name: remove testing subnet + ec2_vpc_subnet: + state: absent + vpc_id: "{{ vpc.vpc.id }}" + cidr: 20.0.64.0/18 + az: "{{ aws_region }}b" + resource_tags: + Name: "{{ resource_prefix }}-subnet" + register: removed + retries: 10 + until: removed is not failed + ignore_errors: true + + - name: remove testing internet gateway + ec2_vpc_igw: + vpc_id: "{{ vpc.vpc.id }}" + state: absent + register: removed + retries: 10 + until: removed is not failed + ignore_errors: true + + - name: remove testing VPC + ec2_vpc_net: + name: "{{ resource_prefix }}-vpc" + state: absent + cidr_block: 20.0.0.0/16 + tags: + Name: "{{ resource_prefix }}-vpc" + Description: "Created by ansible-test" + register: removed + retries: 10 + until: removed is not failed + ignore_errors: True + + # ============================================================ diff --git a/ansible_collections/community/aws/tests/integration/targets/glue_connection/aliases b/ansible_collections/community/aws/tests/integration/targets/glue_connection/aliases new file mode 100644 index 000000000..4ef4b2067 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/glue_connection/aliases @@ -0,0 +1 @@ +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/glue_connection/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/glue_connection/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/glue_connection/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/glue_connection/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/glue_connection/tasks/main.yml new file mode 100644 index 000000000..837f9bd17 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/glue_connection/tasks/main.yml @@ -0,0 +1,13 @@ +--- +- name: aws_glue_connection integration tests + collections: + - amazon.aws + module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + block: + - include_tasks: test_connection_network.yml + - include_tasks: test_connection_jdbc.yml diff --git a/ansible_collections/community/aws/tests/integration/targets/glue_connection/tasks/test_connection_jdbc.yml b/ansible_collections/community/aws/tests/integration/targets/glue_connection/tasks/test_connection_jdbc.yml new file mode 100644 index 000000000..966d8156f --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/glue_connection/tasks/test_connection_jdbc.yml @@ -0,0 +1,74 @@ +--- +- name: 'aws_glue_connection integration tests (JDBC)' + block: + + # TODO: description, match_criteria, security_groups, and subnet_id are unused module options + + - name: create glue connection + aws_glue_connection: + name: "{{ resource_prefix }}" + connection_properties: + JDBC_CONNECTION_URL: "jdbc:mysql://mydb:3306/{{ resource_prefix }}" + USERNAME: my-username + PASSWORD: my-password + state: present + register: result + + - assert: + that: + - result.changed + + - name: test idempotence creating glue connection + aws_glue_connection: + name: "{{ resource_prefix }}" + connection_properties: + JDBC_CONNECTION_URL: "jdbc:mysql://mydb:3306/{{ resource_prefix }}" + USERNAME: my-username + PASSWORD: my-password + state: present + register: result + + - assert: + that: + - not result.changed + + - name: test updating JDBC connection url + aws_glue_connection: + name: "{{ resource_prefix }}" + connection_properties: + JDBC_CONNECTION_URL: "jdbc:mysql://mydb:3306/{{ resource_prefix }}-updated" + USERNAME: my-username + PASSWORD: my-password + state: present + register: result + + - assert: + that: + - result.changed + + - name: delete glue connection + aws_glue_connection: + name: "{{ resource_prefix }}" + state: absent + register: result + + - assert: + that: + - result.changed + + - name: test idempotence removing glue connection + aws_glue_connection: + name: "{{ resource_prefix }}" + state: absent + register: result + + - assert: + that: + - not result.changed + + always: + + - name: delete glue connection + aws_glue_connection: + name: "{{ resource_prefix }}" + state: absent diff --git a/ansible_collections/community/aws/tests/integration/targets/glue_connection/tasks/test_connection_network.yml b/ansible_collections/community/aws/tests/integration/targets/glue_connection/tasks/test_connection_network.yml new file mode 100644 index 000000000..230015585 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/glue_connection/tasks/test_connection_network.yml @@ -0,0 +1,337 @@ +--- +- name: aws_glue_connection integration tests (network) + block: + - name: Install AWS CLI + pip: + name: awscli + state: present + + - name: Create VPC + ec2_vpc_net: + name: "{{ resource_prefix }}-vpc" + cidr_block: 10.22.32.0/23 + tags: + Name: Ansible ec2_instance Testing VPC + tenancy: default + register: glue_vpc + + - name: Create default subnet in zone A + ec2_vpc_subnet: + az: "{{ aws_region }}a" + cidr: 10.22.32.0/24 + vpc_id: "{{ glue_vpc.vpc.id }}" + resource_tags: + Name: "{{ resource_prefix }}-subnet-a" + state: present + register: glue_subnet_a + + - name: Create security group 1 + ec2_group: + name: "{{ resource_prefix }}-sg-glue-1" + description: A security group for Ansible tests + vpc_id: "{{ glue_vpc.vpc.id }}" + rules: + - proto: -1 + ports: -1 + group_name: "{{ resource_prefix }}-sg-glue-1" + rule_desc: Connections from Glue + + - name: Create security group 2 + ec2_group: + name: "{{ resource_prefix }}-sg-glue-2" + description: A security group for Ansible tests + vpc_id: "{{ glue_vpc.vpc.id }}" + rules: + - proto: -1 + ports: -1 + group_name: "{{ resource_prefix }}-sg-glue-2" + rule_desc: Connections from Glue + + - name: Create Glue connection (check mode) + aws_glue_connection: + name: "{{ resource_prefix }}" + availability_zone: "{{ aws_region }}a" + connection_properties: + JDBC_ENFORCE_SSL: "false" + connection_type: NETWORK + description: Test connection + security_groups: + - "{{ resource_prefix }}-sg-glue-1" + subnet_id: "{{ glue_subnet_a.subnet.id }}" + state: present + check_mode: true + register: glue_connection_check + + - name: Verity that Glue connection was not created in check mode + assert: + that: + - glue_connection_check.changed + - glue_connection_check.description is not defined + + - name: Create Glue connection + aws_glue_connection: + name: "{{ resource_prefix }}" + availability_zone: "{{ aws_region }}a" + connection_properties: + JDBC_ENFORCE_SSL: "false" + connection_type: NETWORK + description: Test connection + security_groups: + - "{{ resource_prefix }}-sg-glue-1" + subnet_id: "{{ glue_subnet_a.subnet.id }}" + state: present + register: glue_connection + + - name: Get info on Glue connection + command: "aws glue get-connection --name {{ resource_prefix }}" + environment: + AWS_ACCESS_KEY_ID: "{{ aws_access_key }}" + AWS_SECRET_ACCESS_KEY: "{{ aws_secret_key }}" + AWS_SESSION_TOKEN: "{{ security_token | default('') }}" + AWS_DEFAULT_REGION: "{{ aws_region }}" + register: connection_info_query + + - name: Convert it to an object + set_fact: + connection_info: "{{ connection_info_query.stdout | from_json }}" + + - name: Verity that Glue connection was created + assert: + that: + - glue_connection.changed + - glue_connection.name == connection_info["Connection"]["Name"] + - glue_connection.description == connection_info["Connection"]["Description"] + - glue_connection.raw_connection_properties == connection_info["Connection"]["ConnectionProperties"] + - glue_connection.connection_type == connection_info["Connection"]["ConnectionType"] + - glue_connection.physical_connection_requirements.subnet_id == connection_info["Connection"]["PhysicalConnectionRequirements"]["SubnetId"] + - glue_connection.physical_connection_requirements.security_group_id_list == connection_info["Connection"]["PhysicalConnectionRequirements"]["SecurityGroupIdList"] + - glue_connection.physical_connection_requirements.availability_zone == connection_info["Connection"]["PhysicalConnectionRequirements"]["AvailabilityZone"] + - glue_connection.raw_connection_properties == connection_info["Connection"]["ConnectionProperties"] + + - name: Create Glue connection (idempotent) (check mode) + aws_glue_connection: + name: "{{ resource_prefix }}" + availability_zone: "{{ aws_region }}a" + connection_properties: + JDBC_ENFORCE_SSL: "false" + connection_type: NETWORK + description: Test connection + security_groups: + - "{{ resource_prefix }}-sg-glue-1" + subnet_id: "{{ glue_subnet_a.subnet.id }}" + state: present + check_mode: true + register: glue_connection_idempotent_check + + - name: Get info on Glue connection + command: "aws glue get-connection --name {{ resource_prefix }}" + environment: + AWS_ACCESS_KEY_ID: "{{ aws_access_key }}" + AWS_SECRET_ACCESS_KEY: "{{ aws_secret_key }}" + AWS_SESSION_TOKEN: "{{ security_token | default('') }}" + AWS_DEFAULT_REGION: "{{ aws_region }}" + register: connection_info_query_idempotent_check + + - name: Convert it to an object + set_fact: + connection_info_idempotent_check: "{{ connection_info_query_idempotent_check.stdout | from_json }}" + + - name: Verity that Glue connection was not modified in check mode + assert: + that: + - not glue_connection_idempotent_check.changed + - connection_info_idempotent_check["Connection"]["Name"] == connection_info["Connection"]["Name"] + - connection_info_idempotent_check["Connection"]["Description"] == connection_info["Connection"]["Description"] + - connection_info_idempotent_check["Connection"]["ConnectionProperties"] == connection_info["Connection"]["ConnectionProperties"] + - connection_info_idempotent_check["Connection"]["ConnectionType"] == connection_info["Connection"]["ConnectionType"] + - connection_info_idempotent_check["Connection"]["PhysicalConnectionRequirements"]["SubnetId"] == connection_info["Connection"]["PhysicalConnectionRequirements"]["SubnetId"] + - connection_info_idempotent_check["Connection"]["PhysicalConnectionRequirements"]["SecurityGroupIdList"]== connection_info["Connection"]["PhysicalConnectionRequirements"]["SecurityGroupIdList"] + - connection_info_idempotent_check["Connection"]["PhysicalConnectionRequirements"]["AvailabilityZone"] == connection_info["Connection"]["PhysicalConnectionRequirements"]["AvailabilityZone"] + + - name: Create Glue connection (idempotent) + aws_glue_connection: + name: "{{ resource_prefix }}" + availability_zone: "{{ aws_region }}a" + connection_properties: + JDBC_ENFORCE_SSL: "false" + connection_type: NETWORK + description: Test connection + security_groups: + - "{{ resource_prefix }}-sg-glue-1" + subnet_id: "{{ glue_subnet_a.subnet.id }}" + state: present + register: glue_connection_idempotent + + - name: Get info on Glue connection + command: "aws glue get-connection --name {{ resource_prefix }}" + environment: + AWS_ACCESS_KEY_ID: "{{ aws_access_key }}" + AWS_SECRET_ACCESS_KEY: "{{ aws_secret_key }}" + AWS_SESSION_TOKEN: "{{ security_token | default('') }}" + AWS_DEFAULT_REGION: "{{ aws_region }}" + register: connection_info_query_idempotent + + - name: Convert it to an object + set_fact: + connection_info_idempotent: "{{ connection_info_query_idempotent.stdout | from_json }}" + + - name: Verity that Glue connection was not modified + assert: + that: + - not glue_connection_idempotent.changed + - connection_info_idempotent["Connection"]["Name"] == connection_info["Connection"]["Name"] + - connection_info_idempotent["Connection"]["Description"] == connection_info["Connection"]["Description"] + - connection_info_idempotent["Connection"]["ConnectionProperties"] == connection_info["Connection"]["ConnectionProperties"] + - connection_info_idempotent["Connection"]["ConnectionType"] == connection_info["Connection"]["ConnectionType"] + - connection_info_idempotent["Connection"]["PhysicalConnectionRequirements"]["SubnetId"] == connection_info["Connection"]["PhysicalConnectionRequirements"]["SubnetId"] + - connection_info_idempotent["Connection"]["PhysicalConnectionRequirements"]["SecurityGroupIdList"]== connection_info["Connection"]["PhysicalConnectionRequirements"]["SecurityGroupIdList"] + - connection_info_idempotent["Connection"]["PhysicalConnectionRequirements"]["AvailabilityZone"] == connection_info["Connection"]["PhysicalConnectionRequirements"]["AvailabilityZone"] + + - name: Update Glue connection (check mode) + aws_glue_connection: + name: "{{ resource_prefix }}" + availability_zone: "{{ aws_region }}a" + connection_properties: + JDBC_ENFORCE_SSL: "false" + connection_type: NETWORK + description: Test connection modified + security_groups: + - "{{ resource_prefix }}-sg-glue-2" + subnet_id: "{{ glue_subnet_a.subnet.id }}" + state: present + check_mode: true + register: glue_connection_update_check + + - name: Get info on Glue connection + command: "aws glue get-connection --name {{ resource_prefix }}" + environment: + AWS_ACCESS_KEY_ID: "{{ aws_access_key }}" + AWS_SECRET_ACCESS_KEY: "{{ aws_secret_key }}" + AWS_SESSION_TOKEN: "{{ security_token | default('') }}" + AWS_DEFAULT_REGION: "{{ aws_region }}" + register: connection_info_query_update_check + + - name: Convert it to an object + set_fact: + connection_info_update_check: "{{ connection_info_query_update_check.stdout | from_json }}" + + - name: Verity that Glue connection was not modified in check mode + assert: + that: + - glue_connection_update_check.changed + - glue_connection_update_check.name == connection_info_update_check["Connection"]["Name"] + - glue_connection_update_check.description == connection_info_update_check["Connection"]["Description"] + - glue_connection_update_check.raw_connection_properties == connection_info_update_check["Connection"]["ConnectionProperties"] + - glue_connection_update_check.connection_type == connection_info_update_check["Connection"]["ConnectionType"] + - glue_connection_update_check.physical_connection_requirements.subnet_id == connection_info_update_check["Connection"]["PhysicalConnectionRequirements"]["SubnetId"] + - glue_connection_update_check.physical_connection_requirements.security_group_id_list == connection_info_update_check["Connection"]["PhysicalConnectionRequirements"]["SecurityGroupIdList"] + - glue_connection_update_check.physical_connection_requirements.availability_zone == connection_info_update_check["Connection"]["PhysicalConnectionRequirements"]["AvailabilityZone"] + - glue_connection_update_check.raw_connection_properties == connection_info_update_check["Connection"]["ConnectionProperties"] + + - name: Update Glue connection + aws_glue_connection: + name: "{{ resource_prefix }}" + availability_zone: "{{ aws_region }}a" + connection_properties: + JDBC_ENFORCE_SSL: "false" + connection_type: NETWORK + description: Test connection modified + security_groups: + - "{{ resource_prefix }}-sg-glue-2" + subnet_id: "{{ glue_subnet_a.subnet.id }}" + state: present + register: glue_connection_update + + - name: Get info on Glue connection + command: "aws glue get-connection --name {{ resource_prefix }}" + environment: + AWS_ACCESS_KEY_ID: "{{ aws_access_key }}" + AWS_SECRET_ACCESS_KEY: "{{ aws_secret_key }}" + AWS_SESSION_TOKEN: "{{ security_token | default('') }}" + AWS_DEFAULT_REGION: "{{ aws_region }}" + register: connection_info_query_update + + - name: Convert it to an object + set_fact: + connection_info_update: "{{ connection_info_query_update.stdout | from_json }}" + + - name: Verity that Glue connection was modified + assert: + that: + - glue_connection_update.changed + - glue_connection_update.name == connection_info_update["Connection"]["Name"] + - glue_connection_update.description == connection_info_update["Connection"]["Description"] + - glue_connection_update.raw_connection_properties == connection_info_update["Connection"]["ConnectionProperties"] + - glue_connection_update.connection_type == connection_info_update["Connection"]["ConnectionType"] + - glue_connection_update.physical_connection_requirements.subnet_id == connection_info_update["Connection"]["PhysicalConnectionRequirements"]["SubnetId"] + - glue_connection_update.physical_connection_requirements.security_group_id_list == connection_info_update["Connection"]["PhysicalConnectionRequirements"]["SecurityGroupIdList"] + - glue_connection_update.physical_connection_requirements.availability_zone == connection_info_update["Connection"]["PhysicalConnectionRequirements"]["AvailabilityZone"] + - glue_connection_update.raw_connection_properties == connection_info_update["Connection"]["ConnectionProperties"] + + - name: Delete Glue connection (check mode) + aws_glue_connection: + name: "{{ resource_prefix }}" + state: absent + check_mode: true + register: glue_connection_delete_check + + - name: Get info on Glue connection + command: "aws glue get-connection --name {{ resource_prefix }}" + environment: + AWS_ACCESS_KEY_ID: "{{ aws_access_key }}" + AWS_SECRET_ACCESS_KEY: "{{ aws_secret_key }}" + AWS_SESSION_TOKEN: "{{ security_token | default('') }}" + AWS_DEFAULT_REGION: "{{ aws_region }}" + register: connection_info_query_delete_check + + - name: Convert it to an object + set_fact: + connection_info_delete_check: "{{ connection_info_query_delete_check.stdout | from_json }}" + + - name: Verity that Glue connection was not deleted in check mode + assert: + that: + - glue_connection_delete_check.changed + - connection_info["Connection"]["Name"] == connection_info_delete_check["Connection"]["Name"] + + - name: Delete Glue connection + aws_glue_connection: + name: "{{ resource_prefix }}" + state: absent + register: glue_connection_delete + + - name: Verity that Glue connection was deleted + assert: + that: + - glue_connection_delete.changed + + always: + - name: Delete Glue connection + aws_glue_connection: + name: "{{ resource_prefix }}" + state: absent + ignore_errors: true + - name: Delete security group 1 + ec2_group: + name: "{{ resource_prefix }}-sg-glue-1" + state: absent + ignore_errors: true + - name: Delete security group 2 + ec2_group: + name: "{{ resource_prefix }}-sg-glue-2" + state: absent + ignore_errors: true + - name: Delete default subnet in zone A + ec2_vpc_subnet: + az: "{{ aws_region }}a" + cidr: 10.22.32.0/24 + vpc_id: "{{ glue_vpc.vpc.id }}" + state: absent + register: glue_subnet_a + ignore_errors: true + - name: Delete VPC + ec2_vpc_net: + name: "{{ resource_prefix }}-vpc" + cidr_block: 10.22.32.0/23 + state: absent + ignore_errors: true diff --git a/ansible_collections/community/aws/tests/integration/targets/glue_crawler/aliases b/ansible_collections/community/aws/tests/integration/targets/glue_crawler/aliases new file mode 100644 index 000000000..4ef4b2067 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/glue_crawler/aliases @@ -0,0 +1 @@ +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/glue_crawler/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/glue_crawler/defaults/main.yml new file mode 100644 index 000000000..e5d95891f --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/glue_crawler/defaults/main.yml @@ -0,0 +1,11 @@ +--- +glue_crawler_name: "{{ resource_prefix }}" +glue_crawler_description: Test crawler +glue_crawler_s3_path: "s3://test-s3-bucket-glue/prefix/folder/" +# IAM role names have to be less than 64 characters +# The 8 digit identifier at the end of resource_prefix helps determine during +# which test something was created and allows tests to be run in parallel +# Shippable resource_prefixes are in the format shippable-123456-123, so in those cases +# we need both sets of digits to keep the resource name unique +unique_id: "{{ resource_prefix | regex_search('(\\d+-?)(\\d+)$') }}" +glue_crawler_role_name: "ansible-test-{{ unique_id }}-glue-crawler" diff --git a/ansible_collections/community/aws/tests/integration/targets/glue_crawler/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/glue_crawler/meta/main.yml new file mode 100644 index 000000000..1810d4bec --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/glue_crawler/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - setup_remote_tmp_dir diff --git a/ansible_collections/community/aws/tests/integration/targets/glue_crawler/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/glue_crawler/tasks/main.yml new file mode 100644 index 000000000..b96968195 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/glue_crawler/tasks/main.yml @@ -0,0 +1,339 @@ +--- +- name: aws_glue_crawler integration tests + collections: + - amazon.aws + module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + block: + + - name: Create minimal Glue crawler role + iam_role: + name: "{{ glue_crawler_role_name }}" + assume_role_policy_document: + Version: "2008-10-17" + Statement: + - Action: "sts:AssumeRole" + Effect: Allow + Principal: + Service: glue.amazonaws.com + create_instance_profile: false + managed_policies: + - "arn:aws:iam::aws:policy/AWSXrayWriteOnlyAccess" + + - name: Pause for role creation to finish + pause: + seconds: 10 + + - name: Create Glue crawler (check mode) + aws_glue_crawler: + name: "{{ glue_crawler_name }}" + database_name: my_database + description: "{{ glue_crawler_description }}" + role: "{{ glue_crawler_role_name }}" + recrawl_policy: + recrawl_behavior: CRAWL_EVERYTHING + schema_change_policy: + delete_behavior: DELETE_FROM_DATABASE + update_behavior: UPDATE_IN_DATABASE + targets: + S3Targets: + - Path: "{{ glue_crawler_s3_path }}" + tags: + Environment: Test + Product: Glue + state: present + check_mode: true + register: glue_crawler_check + + - name: Verity that Glue crawler was not created in check mode + assert: + that: + - glue_crawler_check.changed + - glue_crawler_check.description is not defined + + - name: Create Glue crawler + aws_glue_crawler: + name: "{{ glue_crawler_name }}" + database_name: my_database + description: "{{ glue_crawler_description }}" + role: "{{ glue_crawler_role_name }}" + recrawl_policy: + recrawl_behavior: CRAWL_EVERYTHING + schema_change_policy: + delete_behavior: DELETE_FROM_DATABASE + update_behavior: UPDATE_IN_DATABASE + targets: + S3Targets: + - Path: "{{ glue_crawler_s3_path }}" + tags: + Environment: Test + Product: Glue + state: present + register: glue_crawler + + - name: Get info on Glue crawler + command: "aws glue get-crawler --name {{ glue_crawler_name }}" + environment: + AWS_ACCESS_KEY_ID: "{{ aws_access_key }}" + AWS_SECRET_ACCESS_KEY: "{{ aws_secret_key }}" + AWS_SESSION_TOKEN: "{{ security_token | default('') }}" + AWS_DEFAULT_REGION: "{{ aws_region }}" + register: crawler_info_query + no_log: True + + - name: Convert it to an object + set_fact: + crawler_info: "{{ crawler_info_query.stdout | from_json }}" + + - name: Verity that Glue crawler was created + assert: + that: + - glue_crawler.changed + - glue_crawler.name == crawler_info["Crawler"]["Name"] + - glue_crawler.database_name == crawler_info["Crawler"]["DatabaseName"] + - glue_crawler.description == crawler_info["Crawler"]["Description"] + - glue_crawler.recrawl_policy == crawler_info["Crawler"]["RecrawlPolicy"] + - glue_crawler.role == crawler_info["Crawler"]["Role"] + - glue_crawler.schema_change_policy == crawler_info["Crawler"]["SchemaChangePolicy"] + - glue_crawler.targets.S3Targets == crawler_info["Crawler"]["Targets"]["S3Targets"] + + - name: Create Glue crawler (idempotent) (check mode) + aws_glue_crawler: + name: "{{ glue_crawler_name }}" + database_name: my_database + description: "{{ glue_crawler_description }}" + role: "{{ glue_crawler_role_name }}" + recrawl_policy: + recrawl_behavior: CRAWL_EVERYTHING + schema_change_policy: + delete_behavior: DELETE_FROM_DATABASE + update_behavior: UPDATE_IN_DATABASE + targets: + S3Targets: + - Path: "{{ glue_crawler_s3_path }}" + tags: + Environment: Test + Product: Glue + state: present + check_mode: true + register: glue_crawler_idempotent_check + + - name: Get info on Glue crawler + command: "aws glue get-crawler --name {{ glue_crawler_name }}" + environment: + AWS_ACCESS_KEY_ID: "{{ aws_access_key }}" + AWS_SECRET_ACCESS_KEY: "{{ aws_secret_key }}" + AWS_SESSION_TOKEN: "{{ security_token | default('') }}" + AWS_DEFAULT_REGION: "{{ aws_region }}" + register: crawler_info_query_idempotent_check + no_log: True + + - name: Convert it to an object + set_fact: + crawler_info_idempotent_check: "{{ crawler_info_query_idempotent_check.stdout | from_json }}" + + - name: Verity that Glue crawler was not modified in check mode + assert: + that: + - glue_crawler_idempotent_check is not changed + - crawler_info["Crawler"]["Name"] == crawler_info_idempotent_check["Crawler"]["Name"] + - crawler_info["Crawler"]["DatabaseName"] == crawler_info_idempotent_check["Crawler"]["DatabaseName"] + - crawler_info["Crawler"]["Description"] == crawler_info_idempotent_check["Crawler"]["Description"] + - crawler_info["Crawler"]["RecrawlPolicy"] == crawler_info_idempotent_check["Crawler"]["RecrawlPolicy"] + - crawler_info["Crawler"]["Role"] == crawler_info_idempotent_check["Crawler"]["Role"] + - crawler_info["Crawler"]["SchemaChangePolicy"] == crawler_info_idempotent_check["Crawler"]["SchemaChangePolicy"] + - crawler_info["Crawler"]["Targets"]["S3Targets"] == crawler_info_idempotent_check["Crawler"]["Targets"]["S3Targets"] + + - name: Create Glue crawler (idempotent) + aws_glue_crawler: + name: "{{ glue_crawler_name }}" + database_name: my_database + description: "{{ glue_crawler_description }}" + role: "{{ glue_crawler_role_name }}" + recrawl_policy: + recrawl_behavior: CRAWL_EVERYTHING + schema_change_policy: + delete_behavior: DELETE_FROM_DATABASE + update_behavior: UPDATE_IN_DATABASE + targets: + S3Targets: + - Path: "{{ glue_crawler_s3_path }}" + tags: + Environment: Test + Product: Glue + state: present + register: glue_crawler_idempotent + + - name: Get info on Glue crawler + command: "aws glue get-crawler --name {{ glue_crawler_name }}" + environment: + AWS_ACCESS_KEY_ID: "{{ aws_access_key }}" + AWS_SECRET_ACCESS_KEY: "{{ aws_secret_key }}" + AWS_SESSION_TOKEN: "{{ security_token | default('') }}" + AWS_DEFAULT_REGION: "{{ aws_region }}" + register: crawler_info_query_idempotent + no_log: True + + - name: Convert it to an object + set_fact: + crawler_info_idempotent: "{{ crawler_info_query_idempotent.stdout | from_json }}" + + - name: Verity that Glue crawler was not modified + assert: + that: + - glue_crawler_idempotent is not changed + - crawler_info["Crawler"]["Name"] == crawler_info_idempotent["Crawler"]["Name"] + - crawler_info["Crawler"]["DatabaseName"] == crawler_info_idempotent["Crawler"]["DatabaseName"] + - crawler_info["Crawler"]["Description"] == crawler_info_idempotent["Crawler"]["Description"] + - crawler_info["Crawler"]["RecrawlPolicy"] == crawler_info_idempotent["Crawler"]["RecrawlPolicy"] + - crawler_info["Crawler"]["Role"] == crawler_info_idempotent["Crawler"]["Role"] + - crawler_info["Crawler"]["SchemaChangePolicy"] == crawler_info_idempotent["Crawler"]["SchemaChangePolicy"] + - crawler_info["Crawler"]["Targets"]["S3Targets"] == crawler_info_idempotent["Crawler"]["Targets"]["S3Targets"] + + - name: Update Glue crawler (check mode) + aws_glue_crawler: + name: "{{ glue_crawler_name }}" + database_name: my_database_2 + description: "{{ glue_crawler_description }}" + role: "{{ glue_crawler_role_name }}" + recrawl_policy: + recrawl_behavior: CRAWL_EVERYTHING + schema_change_policy: + delete_behavior: DELETE_FROM_DATABASE + update_behavior: UPDATE_IN_DATABASE + targets: + S3Targets: + - Path: "{{ glue_crawler_s3_path }}" + tags: + Environment: Test + Product: Glue + state: present + check_mode: true + register: glue_crawler_update_check + + - name: Get info on Glue crawler + command: "aws glue get-crawler --name {{ glue_crawler_name }}" + environment: + AWS_ACCESS_KEY_ID: "{{ aws_access_key }}" + AWS_SECRET_ACCESS_KEY: "{{ aws_secret_key }}" + AWS_SESSION_TOKEN: "{{ security_token | default('') }}" + AWS_DEFAULT_REGION: "{{ aws_region }}" + register: crawler_info_query_update_check + no_log: True + + - name: Convert it to an object + set_fact: + crawler_info_update_check: "{{ crawler_info_query_update_check.stdout | from_json }}" + + - name: Verity that Glue crawler was not modified in check mode + assert: + that: + - glue_crawler_update_check is changed + - glue_crawler_update_check.name == crawler_info_update_check["Crawler"]["Name"] + - glue_crawler_update_check.database_name == crawler_info_update_check["Crawler"]["DatabaseName"] + - glue_crawler_update_check.description == crawler_info_update_check["Crawler"]["Description"] + - glue_crawler_update_check.recrawl_policy == crawler_info_update_check["Crawler"]["RecrawlPolicy"] + - glue_crawler_update_check.role == crawler_info_update_check["Crawler"]["Role"] + - glue_crawler_update_check.schema_change_policy == crawler_info_update_check["Crawler"]["SchemaChangePolicy"] + - glue_crawler_update_check.targets.S3Targets == crawler_info_update_check["Crawler"]["Targets"]["S3Targets"] + + - name: Update Glue crawler + aws_glue_crawler: + name: "{{ glue_crawler_name }}" + database_name: my_database_2 + description: "{{ glue_crawler_description }}" + role: "{{ glue_crawler_role_name }}" + recrawl_policy: + recrawl_behavior: CRAWL_EVERYTHING + schema_change_policy: + delete_behavior: DELETE_FROM_DATABASE + update_behavior: UPDATE_IN_DATABASE + targets: + S3Targets: + - Path: "{{ glue_crawler_s3_path }}" + tags: + Environment: Test + Product: Glue + state: present + register: glue_crawler_update + + - name: Get info on Glue crawler + command: "aws glue get-crawler --name {{ glue_crawler_name }}" + environment: + AWS_ACCESS_KEY_ID: "{{ aws_access_key }}" + AWS_SECRET_ACCESS_KEY: "{{ aws_secret_key }}" + AWS_SESSION_TOKEN: "{{ security_token | default('') }}" + AWS_DEFAULT_REGION: "{{ aws_region }}" + register: crawler_info_query_update + no_log: True + + - name: Convert it to an object + set_fact: + crawler_info_update: "{{ crawler_info_query_update.stdout | from_json }}" + + - name: Verity that Glue crawler was modified + assert: + that: + - glue_crawler_update.changed + - glue_crawler_update.name == crawler_info_update["Crawler"]["Name"] + - glue_crawler_update.database_name == crawler_info_update["Crawler"]["DatabaseName"] + - glue_crawler_update.description == crawler_info_update["Crawler"]["Description"] + - glue_crawler_update.recrawl_policy == crawler_info_update["Crawler"]["RecrawlPolicy"] + - glue_crawler_update.role == crawler_info_update["Crawler"]["Role"] + - glue_crawler_update.schema_change_policy == crawler_info_update["Crawler"]["SchemaChangePolicy"] + - glue_crawler_update.targets.S3Targets == crawler_info_update["Crawler"]["Targets"]["S3Targets"] + + - name: Delete Glue crawler (check mode) + aws_glue_crawler: + name: "{{ glue_crawler_name }}" + state: absent + check_mode: true + register: glue_crawler_delete_check + + - name: Get info on Glue crawler + command: "aws glue get-crawler --name {{ glue_crawler_name }}" + environment: + AWS_ACCESS_KEY_ID: "{{ aws_access_key }}" + AWS_SECRET_ACCESS_KEY: "{{ aws_secret_key }}" + AWS_SESSION_TOKEN: "{{ security_token | default('') }}" + AWS_DEFAULT_REGION: "{{ aws_region }}" + register: crawler_info_query_delete_check + no_log: True + + - name: Convert it to an object + set_fact: + crawler_info_delete_check: "{{ crawler_info_query_delete_check.stdout | from_json }}" + + - name: Verity that Glue crawler was not deleted in check mode + assert: + that: + - glue_crawler_delete_check.changed + - crawler_info["Crawler"]["Name"] == crawler_info_delete_check["Crawler"]["Name"] + + - name: Delete Glue crawler + aws_glue_crawler: + name: "{{ glue_crawler_name }}" + state: absent + register: glue_crawler_delete + + - name: Verity that Glue crawler was deleted + assert: + that: + - glue_crawler_delete.changed + + always: + - name: Delete Glue crawler + aws_glue_crawler: + name: "{{ glue_crawler_name }}" + state: absent + ignore_errors: true + + - name: Delete Glue crawler role + iam_role: + name: "{{ glue_crawler_role_name }}" + state: absent + ignore_errors: true diff --git a/ansible_collections/community/aws/tests/integration/targets/glue_job/aliases b/ansible_collections/community/aws/tests/integration/targets/glue_job/aliases new file mode 100644 index 000000000..4ef4b2067 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/glue_job/aliases @@ -0,0 +1 @@ +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/glue_job/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/glue_job/defaults/main.yml new file mode 100644 index 000000000..19c44066b --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/glue_job/defaults/main.yml @@ -0,0 +1,12 @@ +--- +glue_job_name: "{{ resource_prefix }}" +glue_job_description: Test job +glue_job_command_script_location: "s3://test-s3-bucket-glue/job.py" +glue_job_temp_dir: "s3://test-s3-bucket-glue/temp/" +# IAM role names have to be less than 64 characters +# The 8 digit identifier at the end of resource_prefix helps determine during +# which test something was created and allows tests to be run in parallel +# Shippable resource_prefixes are in the format shippable-123456-123, so in those cases +# we need both sets of digits to keep the resource name unique +unique_id: "{{ resource_prefix | regex_search('(\\d+-?)(\\d+)$') }}" +glue_job_role_name: "ansible-test-{{ unique_id }}-glue-job" diff --git a/ansible_collections/community/aws/tests/integration/targets/glue_job/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/glue_job/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/glue_job/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/glue_job/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/glue_job/tasks/main.yml new file mode 100644 index 000000000..307a9befb --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/glue_job/tasks/main.yml @@ -0,0 +1,304 @@ +--- +- name: aws_glue_job integration tests + collections: + - amazon.aws + module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + block: + # AWS CLI is needed until there's a module to get info about Glue jobs + - name: Install AWS CLI + pip: + name: awscli + state: present + + - name: Create minimal Glue job role + iam_role: + name: "{{ glue_job_role_name }}" + assume_role_policy_document: + Version: "2008-10-17" + Statement: + - Action: "sts:AssumeRole" + Effect: Allow + Principal: + Service: glue.amazonaws.com + create_instance_profile: false + managed_policies: + - "arn:aws:iam::aws:policy/AWSXrayWriteOnlyAccess" + + - name: Create Glue job (check mode) + aws_glue_job: + name: "{{ glue_job_name }}" + command_python_version: 3 + command_script_location: "{{ glue_job_command_script_location }}" + default_arguments: + "--TempDir": "{{ glue_job_temp_dir }}" + description: "{{ glue_job_description }}" + glue_version: "2.0" + role: "{{ glue_job_role_name }}" + tags: + Environment: Test + Product: Glue + state: present + check_mode: true + register: glue_job_check + + - name: Verity that Glue job was not created in check mode + assert: + that: + - glue_job_check.changed + - glue_job_check.description is not defined + + - name: Create Glue job + aws_glue_job: + name: "{{ glue_job_name }}" + command_python_version: 3 + command_script_location: "{{ glue_job_command_script_location }}" + default_arguments: + "--TempDir": "{{ glue_job_temp_dir }}" + description: "{{ glue_job_description }}" + glue_version: "2.0" + role: "{{ glue_job_role_name }}" + tags: + Environment: Test + Product: Glue + state: present + register: glue_job + + - name: Get info on Glue job + command: "aws glue get-job --job-name {{ glue_job_name }}" + environment: + AWS_ACCESS_KEY_ID: "{{ aws_access_key }}" + AWS_SECRET_ACCESS_KEY: "{{ aws_secret_key }}" + AWS_SESSION_TOKEN: "{{ security_token | default('') }}" + AWS_DEFAULT_REGION: "{{ aws_region }}" + register: job_info_query + + - name: Convert it to an object + set_fact: + job_info: "{{ job_info_query.stdout | from_json }}" + + - name: Verity that Glue job was created + assert: + that: + - glue_job.changed + - glue_job.command.python_version == job_info["Job"]["Command"]["PythonVersion"] + - glue_job.command.script_location == job_info["Job"]["Command"]["ScriptLocation"] + - glue_job.default_arguments == job_info["Job"]["DefaultArguments"] + - glue_job.description == job_info["Job"]["Description"] + - glue_job.glue_version == job_info["Job"]["GlueVersion"] + - glue_job.role == job_info["Job"]["Role"] + + - name: Create Glue job (idempotent) (check mode) + aws_glue_job: + name: "{{ glue_job_name }}" + command_python_version: 3 + command_script_location: "{{ glue_job_command_script_location }}" + default_arguments: + "--TempDir": "{{ glue_job_temp_dir }}" + description: "{{ glue_job_description }}" + glue_version: "2.0" + role: "{{ glue_job_role_name }}" + tags: + Environment: Test + Product: Glue + state: present + check_mode: true + register: glue_job_idempotent_check + + - name: Get info on Glue job + command: "aws glue get-job --job-name {{ glue_job_name }}" + environment: + AWS_ACCESS_KEY_ID: "{{ aws_access_key }}" + AWS_SECRET_ACCESS_KEY: "{{ aws_secret_key }}" + AWS_SESSION_TOKEN: "{{ security_token | default('') }}" + AWS_DEFAULT_REGION: "{{ aws_region }}" + register: job_info_query_idempotent_check + + - name: Convert it to an object + set_fact: + job_info_idempotent_check: "{{ job_info_query_idempotent_check.stdout | from_json }}" + + - name: Verity that Glue job was not modified in check mode + assert: + that: + - not glue_job_idempotent_check.changed + - job_info["Job"]["Name"] == job_info_idempotent_check["Job"]["Name"] + - job_info["Job"]["Command"]["PythonVersion"] == job_info_idempotent_check["Job"]["Command"]["PythonVersion"] + - job_info["Job"]["Command"]["ScriptLocation"] == job_info_idempotent_check["Job"]["Command"]["ScriptLocation"] + - job_info["Job"]["DefaultArguments"] == job_info_idempotent_check["Job"]["DefaultArguments"] + - job_info["Job"]["Description"] == job_info_idempotent_check["Job"]["Description"] + - job_info["Job"]["GlueVersion"] == job_info_idempotent_check["Job"]["GlueVersion"] + - job_info["Job"]["Role"] == job_info_idempotent_check["Job"]["Role"] + + - name: Create Glue job (idempotent) + aws_glue_job: + name: "{{ glue_job_name }}" + command_python_version: 3 + command_script_location: "{{ glue_job_command_script_location }}" + default_arguments: + "--TempDir": "{{ glue_job_temp_dir }}" + description: "{{ glue_job_description }}" + glue_version: "2.0" + role: "{{ glue_job_role_name }}" + tags: + Environment: Test + Product: Glue + state: present + register: glue_job_idempotent + + - name: Get info on Glue job + command: "aws glue get-job --job-name {{ glue_job_name }}" + environment: + AWS_ACCESS_KEY_ID: "{{ aws_access_key }}" + AWS_SECRET_ACCESS_KEY: "{{ aws_secret_key }}" + AWS_SESSION_TOKEN: "{{ security_token | default('') }}" + AWS_DEFAULT_REGION: "{{ aws_region }}" + register: job_info_query_idempotent + + - name: Convert it to an object + set_fact: + job_info_idempotent: "{{ job_info_query_idempotent.stdout | from_json }}" + + - name: Verity that Glue job was not modified + assert: + that: + - not glue_job_idempotent.changed + - job_info["Job"]["Name"] == job_info_idempotent["Job"]["Name"] + - job_info["Job"]["Command"]["PythonVersion"] == job_info_idempotent["Job"]["Command"]["PythonVersion"] + - job_info["Job"]["Command"]["ScriptLocation"] == job_info_idempotent["Job"]["Command"]["ScriptLocation"] + - job_info["Job"]["DefaultArguments"] == job_info_idempotent["Job"]["DefaultArguments"] + - job_info["Job"]["Description"] == job_info_idempotent["Job"]["Description"] + - job_info["Job"]["GlueVersion"] == job_info_idempotent["Job"]["GlueVersion"] + - job_info["Job"]["Role"] == job_info_idempotent["Job"]["Role"] + + - name: Update Glue job (check mode) + aws_glue_job: + name: "{{ glue_job_name }}" + command_python_version: 2 + command_script_location: "{{ glue_job_command_script_location }}" + default_arguments: + "--TempDir": "{{ glue_job_temp_dir }}subfolder/" + description: "{{ glue_job_description }}" + glue_version: "0.9" + role: "{{ glue_job_role_name }}" + tags: + Environment: Test + state: present + check_mode: true + register: glue_job_update_check + + - name: Get info on Glue job + command: "aws glue get-job --job-name {{ glue_job_name }}" + environment: + AWS_ACCESS_KEY_ID: "{{ aws_access_key }}" + AWS_SECRET_ACCESS_KEY: "{{ aws_secret_key }}" + AWS_SESSION_TOKEN: "{{ security_token | default('') }}" + AWS_DEFAULT_REGION: "{{ aws_region }}" + register: job_info_query_update_check + + - name: Convert it to an object + set_fact: + job_info_update_check: "{{ job_info_query_update_check.stdout | from_json }}" + + - name: Verity that Glue job was not modified in check mode + assert: + that: + - glue_job_update_check.changed + - glue_job_update_check.command.python_version == job_info_update_check["Job"]["Command"]["PythonVersion"] + - glue_job_update_check.command.script_location == job_info_update_check["Job"]["Command"]["ScriptLocation"] + - glue_job_update_check.default_arguments == job_info_update_check["Job"]["DefaultArguments"] + - glue_job_update_check.description == job_info_update_check["Job"]["Description"] + - glue_job_update_check.glue_version == job_info_update_check["Job"]["GlueVersion"] + - glue_job_update_check.role == job_info_update_check["Job"]["Role"] + + - name: Update Glue job + aws_glue_job: + name: "{{ glue_job_name }}" + command_python_version: 2 + command_script_location: "{{ glue_job_command_script_location }}" + default_arguments: + "--TempDir": "{{ glue_job_temp_dir }}subfolder/" + description: "{{ glue_job_description }}" + glue_version: "0.9" + role: "{{ glue_job_role_name }}" + tags: + Environment: Test + state: present + register: glue_job_update + + - name: Get info on Glue job + command: "aws glue get-job --job-name {{ glue_job_name }}" + environment: + AWS_ACCESS_KEY_ID: "{{ aws_access_key }}" + AWS_SECRET_ACCESS_KEY: "{{ aws_secret_key }}" + AWS_SESSION_TOKEN: "{{ security_token | default('') }}" + AWS_DEFAULT_REGION: "{{ aws_region }}" + register: job_info_query_update + + - name: Convert it to an object + set_fact: + job_info_update: "{{ job_info_query_update.stdout | from_json }}" + + - name: Verity that Glue job was modified + assert: + that: + - glue_job_update.changed + - glue_job_update.command.python_version == job_info_update["Job"]["Command"]["PythonVersion"] + - glue_job_update.command.script_location == job_info_update["Job"]["Command"]["ScriptLocation"] + - glue_job_update.default_arguments == job_info_update["Job"]["DefaultArguments"] + - glue_job_update.description == job_info_update["Job"]["Description"] + - glue_job_update.glue_version == job_info_update["Job"]["GlueVersion"] + - glue_job_update.role == job_info_update["Job"]["Role"] + + - name: Delete Glue job (check mode) + aws_glue_job: + name: "{{ glue_job_name }}" + state: absent + check_mode: true + register: glue_job_delete_check + + - name: Get info on Glue job + command: "aws glue get-job --job-name {{ glue_job_name }}" + environment: + AWS_ACCESS_KEY_ID: "{{ aws_access_key }}" + AWS_SECRET_ACCESS_KEY: "{{ aws_secret_key }}" + AWS_SESSION_TOKEN: "{{ security_token | default('') }}" + AWS_DEFAULT_REGION: "{{ aws_region }}" + register: job_info_query_delete_check + + - name: Convert it to an object + set_fact: + job_info_delete_check: "{{ job_info_query_delete_check.stdout | from_json }}" + + - name: Verity that Glue job was not deleted in check mode + assert: + that: + - glue_job_delete_check.changed + - job_info["Job"]["Name"] == job_info_delete_check["Job"]["Name"] + + - name: Delete Glue job + aws_glue_job: + name: "{{ glue_job_name }}" + state: absent + register: glue_job_delete + + - name: Verity that Glue job was deleted + assert: + that: + - glue_job_delete.changed + + always: + - name: Delete Glue job + aws_glue_job: + name: "{{ glue_job_name }}" + state: absent + ignore_errors: true + - name: Delete Glue job role + iam_role: + name: "{{ glue_job_role_name }}" + state: absent + ignore_errors: true diff --git a/ansible_collections/community/aws/tests/integration/targets/iam_access_key/aliases b/ansible_collections/community/aws/tests/integration/targets/iam_access_key/aliases new file mode 100644 index 000000000..ffceccfcc --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/iam_access_key/aliases @@ -0,0 +1,9 @@ +# reason: missing-policy +# It should be possible to test iam_user by limiting which policies can be +# attached to the users. +# Careful review is needed prior to adding this to the main CI. +unsupported + +cloud/aws + +iam_access_key_info diff --git a/ansible_collections/community/aws/tests/integration/targets/iam_access_key/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/iam_access_key/defaults/main.yml new file mode 100644 index 000000000..eaaa3523e --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/iam_access_key/defaults/main.yml @@ -0,0 +1,2 @@ +--- +test_user: '{{ resource_prefix }}' diff --git a/ansible_collections/community/aws/tests/integration/targets/iam_access_key/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/iam_access_key/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/iam_access_key/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/iam_access_key/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/iam_access_key/tasks/main.yml new file mode 100644 index 000000000..a7fcc633c --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/iam_access_key/tasks/main.yml @@ -0,0 +1,808 @@ +--- +- name: AWS AuthN details + module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + collections: + - amazon.aws + - community.aws + block: + # ================================================================================== + # Preparation + # ================================================================================== + # We create an IAM user with no attached permissions. The *only* thing the + # user will be able to do is call sts.get_caller_identity + # https://docs.aws.amazon.com/STS/latest/APIReference/API_GetCallerIdentity.html + - name: Create test user + iam_user: + name: '{{ test_user }}' + state: present + register: iam_user + + - assert: + that: + - iam_user is successful + - iam_user is changed + + # ================================================================================== + + - name: Fetch IAM key info (no keys) + iam_access_key_info: + user_name: '{{ test_user }}' + register: access_key_info + + - assert: + that: + - access_key_info is successful + - '"access_keys" in access_key_info' + - access_key_info.access_keys | length == 0 + + # ================================================================================== + + - name: Create a key (check_mode) + iam_access_key: + user_name: '{{ test_user }}' + state: present + register: create_key_1 + check_mode: true + + - assert: + that: + - create_key_1 is successful + - create_key_1 is changed + + - name: Create a key + iam_access_key: + user_name: '{{ test_user }}' + state: present + register: create_key_1 + + - assert: + that: + - create_key_1 is successful + - create_key_1 is changed + - '"access_key" in create_key_1' + - '"secret_access_key" in create_key_1' + - '"deleted_access_key_id" not in create_key_1' + - '"access_key_id" in create_key_1.access_key' + - '"create_date" in create_key_1.access_key' + - '"user_name" in create_key_1.access_key' + - '"status" in create_key_1.access_key' + - create_key_1.access_key.user_name == test_user + - create_key_1.access_key.status == 'Active' + + - name: Fetch IAM key info (1 key) + iam_access_key_info: + user_name: '{{ test_user }}' + register: access_key_info + + - assert: + that: + - access_key_info is successful + - '"access_keys" in access_key_info' + - access_key_info.access_keys | length == 1 + - '"access_key_id" in access_key_1' + - '"create_date" in access_key_1' + - '"user_name" in access_key_1' + - '"status" in access_key_1' + - access_key_1.user_name == test_user + - access_key_1.access_key_id == create_key_1.access_key.access_key_id + - access_key_1.create_date == create_key_1.access_key.create_date + - access_key_1.status == 'Active' + vars: + access_key_1: '{{ access_key_info.access_keys[0] }}' + + # ================================================================================== + + - name: Create a second key (check_mode) + iam_access_key: + user_name: '{{ test_user }}' + state: present + register: create_key_2 + check_mode: true + + - assert: + that: + - create_key_2 is successful + - create_key_2 is changed + + - name: Create a second key + iam_access_key: + user_name: '{{ test_user }}' + state: present + register: create_key_2 + + - assert: + that: + - create_key_2 is successful + - create_key_2 is changed + - '"access_key" in create_key_2' + - '"secret_access_key" in create_key_2' + - '"deleted_access_key_id" not in create_key_2' + - '"access_key_id" in create_key_2.access_key' + - '"create_date" in create_key_2.access_key' + - '"user_name" in create_key_2.access_key' + - '"status" in create_key_2.access_key' + - create_key_2.access_key.user_name == test_user + - create_key_2.access_key.status == 'Active' + + - name: Fetch IAM key info (2 keys) + iam_access_key_info: + user_name: '{{ test_user }}' + register: access_key_info + + - assert: + that: + - access_key_info is successful + - '"access_keys" in access_key_info' + - access_key_info.access_keys | length == 2 + - '"access_key_id" in access_key_1' + - '"create_date" in access_key_1' + - '"user_name" in access_key_1' + - '"status" in access_key_1' + - access_key_1.user_name == test_user + - access_key_1.access_key_id == create_key_1.access_key.access_key_id + - access_key_1.create_date == create_key_1.access_key.create_date + - access_key_1.status == 'Active' + - '"access_key_id" in access_key_2' + - '"create_date" in access_key_2' + - '"user_name" in access_key_2' + - '"status" in access_key_2' + - access_key_2.user_name == test_user + - access_key_2.access_key_id == create_key_2.access_key.access_key_id + - access_key_2.create_date == create_key_2.access_key.create_date + - access_key_2.status == 'Active' + vars: + access_key_1: '{{ access_key_info.access_keys[0] }}' + access_key_2: '{{ access_key_info.access_keys[1] }}' + + # ================================================================================== + + # We don't block the attempt to create a third access key - should AWS change + # the limits this will "JustWork". + + # - name: Create a third key (check_mode) + # iam_access_key: + # user_name: '{{ test_user }}' + # state: present + # register: create_key_3 + # ignore_errors: True + # check_mode: true + + # - assert: + # that: + # - create_key_3 is successful + # - create_key_3 is changed + + - name: Create a third key without rotation + iam_access_key: + user_name: '{{ test_user }}' + state: present + register: create_key_3 + ignore_errors: True + + - assert: + that: + # If Amazon update the limits we may need to change the expectation here. + - create_key_3 is failed + + - name: Fetch IAM key info (2 keys - not changed) + iam_access_key_info: + user_name: '{{ test_user }}' + register: access_key_info + + - assert: + that: + - access_key_info is successful + - '"access_keys" in access_key_info' + - access_key_info.access_keys | length == 2 + - '"access_key_id" in access_key_1' + - '"create_date" in access_key_1' + - '"user_name" in access_key_1' + - '"status" in access_key_1' + - access_key_1.user_name == test_user + - access_key_1.access_key_id == create_key_1.access_key.access_key_id + - access_key_1.create_date == create_key_1.access_key.create_date + - access_key_1.status == 'Active' + - '"access_key_id" in access_key_2' + - '"create_date" in access_key_2' + - '"user_name" in access_key_2' + - '"status" in access_key_2' + - access_key_2.user_name == test_user + - access_key_2.access_key_id == create_key_2.access_key.access_key_id + - access_key_2.create_date == create_key_2.access_key.create_date + - access_key_2.status == 'Active' + vars: + access_key_1: '{{ access_key_info.access_keys[0] }}' + access_key_2: '{{ access_key_info.access_keys[1] }}' + + # ================================================================================== + + - name: Create a third key - rotation enabled (check_mode) + iam_access_key: + user_name: '{{ test_user }}' + state: present + rotate_keys: true + register: create_key_3 + check_mode: true + + - assert: + that: + - create_key_3 is successful + - create_key_3 is changed + - '"deleted_access_key_id" in create_key_3' + - create_key_3.deleted_access_key_id == create_key_1.access_key.access_key_id + + - name: Create a second key + iam_access_key: + user_name: '{{ test_user }}' + state: present + rotate_keys: true + register: create_key_3 + + - assert: + that: + - create_key_3 is successful + - create_key_3 is changed + - '"access_key" in create_key_3' + - '"secret_access_key" in create_key_3' + - '"deleted_access_key_id" in create_key_3' + - create_key_3.deleted_access_key_id == create_key_1.access_key.access_key_id + - '"access_key_id" in create_key_3.access_key' + - '"create_date" in create_key_3.access_key' + - '"user_name" in create_key_3.access_key' + - '"status" in create_key_3.access_key' + - create_key_3.access_key.user_name == test_user + - create_key_3.access_key.status == 'Active' + + - name: Fetch IAM key info (2 keys - oldest rotated) + iam_access_key_info: + user_name: '{{ test_user }}' + register: access_key_info + + - assert: + that: + - access_key_info is successful + - '"access_keys" in access_key_info' + - access_key_info.access_keys | length == 2 + - '"access_key_id" in access_key_1' + - '"create_date" in access_key_1' + - '"user_name" in access_key_1' + - '"status" in access_key_1' + - access_key_1.user_name == test_user + - access_key_1.access_key_id == create_key_2.access_key.access_key_id + - access_key_1.create_date == create_key_2.access_key.create_date + - access_key_1.status == 'Active' + - '"access_key_id" in access_key_2' + - '"create_date" in access_key_2' + - '"user_name" in access_key_2' + - '"status" in access_key_2' + - access_key_2.user_name == test_user + - access_key_2.access_key_id == create_key_3.access_key.access_key_id + - access_key_2.create_date == create_key_3.access_key.create_date + - access_key_2.status == 'Active' + vars: + access_key_1: '{{ access_key_info.access_keys[0] }}' + access_key_2: '{{ access_key_info.access_keys[1] }}' + + # ================================================================================== + + - name: Disable third key (check_mode) + iam_access_key: + user_name: '{{ test_user }}' + id: '{{ create_key_3.access_key.access_key_id }}' + enabled: False + register: disable_key + check_mode: true + + - assert: + that: + - disable_key is successful + - disable_key is changed + + - name: Disable third key + iam_access_key: + user_name: '{{ test_user }}' + id: '{{ create_key_3.access_key.access_key_id }}' + enabled: False + register: disable_key + + - assert: + that: + - disable_key is successful + - disable_key is changed + - '"access_key" in disable_key' + - '"secret_access_key" not in disable_key' + - '"deleted_access_key_id" not in disable_key' + - '"access_key_id" in disable_key.access_key' + - '"create_date" in disable_key.access_key' + - '"user_name" in disable_key.access_key' + - '"status" in disable_key.access_key' + - disable_key.access_key.user_name == test_user + - disable_key.access_key.status == 'Inactive' + + - name: Disable third key - idempotency (check_mode) + iam_access_key: + user_name: '{{ test_user }}' + id: '{{ create_key_3.access_key.access_key_id }}' + enabled: False + register: disable_key + check_mode: true + + - assert: + that: + - disable_key is successful + - disable_key is not changed + + - name: Disable third key - idempotency + iam_access_key: + user_name: '{{ test_user }}' + id: '{{ create_key_3.access_key.access_key_id }}' + enabled: False + register: disable_key + + - assert: + that: + - disable_key is successful + - disable_key is not changed + - '"access_key" in disable_key' + - '"secret_access_key" not in disable_key' + - '"deleted_access_key_id" not in disable_key' + - '"access_key_id" in disable_key.access_key' + - '"create_date" in disable_key.access_key' + - '"user_name" in disable_key.access_key' + - '"status" in disable_key.access_key' + - disable_key.access_key.user_name == test_user + - disable_key.access_key.status == 'Inactive' + + - name: Fetch IAM key info (2 keys - 1 disabled) + iam_access_key_info: + user_name: '{{ test_user }}' + register: access_key_info + + - assert: + that: + - access_key_info is successful + - '"access_keys" in access_key_info' + - access_key_info.access_keys | length == 2 + - '"access_key_id" in access_key_1' + - '"create_date" in access_key_1' + - '"user_name" in access_key_1' + - '"status" in access_key_1' + - access_key_1.user_name == test_user + - access_key_1.access_key_id == create_key_2.access_key.access_key_id + - access_key_1.create_date == create_key_2.access_key.create_date + - access_key_1.status == 'Active' + - '"access_key_id" in access_key_2' + - '"create_date" in access_key_2' + - '"user_name" in access_key_2' + - '"status" in access_key_2' + - access_key_2.user_name == test_user + - access_key_2.access_key_id == create_key_3.access_key.access_key_id + - access_key_2.create_date == create_key_3.access_key.create_date + - access_key_2.status == 'Inactive' + vars: + access_key_1: '{{ access_key_info.access_keys[0] }}' + access_key_2: '{{ access_key_info.access_keys[1] }}' + + # ================================================================================== + + - name: Touch third key - no change (check_mode) + iam_access_key: + user_name: '{{ test_user }}' + id: '{{ create_key_3.access_key.access_key_id }}' + register: touch_key + check_mode: true + + - assert: + that: + - touch_key is successful + - touch_key is not changed + + - name: Touch third key - no change + iam_access_key: + user_name: '{{ test_user }}' + id: '{{ create_key_3.access_key.access_key_id }}' + register: touch_key + + - assert: + that: + - touch_key is successful + - touch_key is not changed + - '"access_key" in touch_key' + - '"secret_access_key" not in touch_key' + - '"deleted_access_key_id" not in touch_key' + - '"access_key_id" in touch_key.access_key' + - '"create_date" in touch_key.access_key' + - '"user_name" in touch_key.access_key' + - '"status" in touch_key.access_key' + - touch_key.access_key.user_name == test_user + - touch_key.access_key.status == 'Inactive' + + # ================================================================================== + + - name: Enable third key (check_mode) + iam_access_key: + user_name: '{{ test_user }}' + id: '{{ create_key_3.access_key.access_key_id }}' + enabled: True + register: enable_key + check_mode: true + + - assert: + that: + - enable_key is successful + - enable_key is changed + + - name: Enable third key + iam_access_key: + user_name: '{{ test_user }}' + id: '{{ create_key_3.access_key.access_key_id }}' + enabled: True + register: enable_key + + - assert: + that: + - enable_key is successful + - enable_key is changed + - '"access_key" in enable_key' + - '"secret_access_key" not in enable_key' + - '"deleted_access_key_id" not in enable_key' + - '"access_key_id" in enable_key.access_key' + - '"create_date" in enable_key.access_key' + - '"user_name" in enable_key.access_key' + - '"status" in enable_key.access_key' + - enable_key.access_key.user_name == test_user + - enable_key.access_key.status == 'Active' + + - name: Enable third key - idempotency (check_mode) + iam_access_key: + user_name: '{{ test_user }}' + id: '{{ create_key_3.access_key.access_key_id }}' + enabled: True + register: enable_key + check_mode: true + + - assert: + that: + - enable_key is successful + - enable_key is not changed + + - name: Enable third key - idempotency + iam_access_key: + user_name: '{{ test_user }}' + id: '{{ create_key_3.access_key.access_key_id }}' + enabled: True + register: enable_key + + - assert: + that: + - enable_key is successful + - enable_key is not changed + - '"access_key" in enable_key' + - '"secret_access_key" not in enable_key' + - '"deleted_access_key_id" not in enable_key' + - '"access_key_id" in enable_key.access_key' + - '"create_date" in enable_key.access_key' + - '"user_name" in enable_key.access_key' + - '"status" in enable_key.access_key' + - enable_key.access_key.user_name == test_user + - enable_key.access_key.status == 'Active' + + # ================================================================================== + + - name: Touch third key again - no change (check_mode) + iam_access_key: + user_name: '{{ test_user }}' + id: '{{ create_key_3.access_key.access_key_id }}' + register: touch_key + check_mode: true + + - assert: + that: + - touch_key is successful + - touch_key is not changed + + - name: Touch third key again - no change + iam_access_key: + user_name: '{{ test_user }}' + id: '{{ create_key_3.access_key.access_key_id }}' + register: touch_key + + - assert: + that: + - touch_key is successful + - touch_key is not changed + - '"access_key" in touch_key' + - '"secret_access_key" not in touch_key' + - '"deleted_access_key_id" not in touch_key' + - '"access_key_id" in touch_key.access_key' + - '"create_date" in touch_key.access_key' + - '"user_name" in touch_key.access_key' + - '"status" in touch_key.access_key' + - touch_key.access_key.user_name == test_user + - touch_key.access_key.status == 'Active' + + # ================================================================================== + + - name: Re-Disable third key + iam_access_key: + user_name: '{{ test_user }}' + id: '{{ create_key_3.access_key.access_key_id }}' + enabled: False + register: redisable_key + + - assert: + that: + - redisable_key is successful + - redisable_key is changed + - redisable_key.access_key.status == 'Inactive' + + - pause: + seconds: 10 + + # ================================================================================== + + - name: Test GetCallerIdentity - Key 2 + aws_caller_info: + aws_access_key: "{{ create_key_2.access_key.access_key_id }}" + aws_secret_key: "{{ create_key_2.secret_access_key }}" + security_token: "{{ omit }}" + register: caller_identity_2 + + - assert: + that: + - caller_identity_2 is successful + - caller_identity_2.arn == iam_user.iam_user.user.arn + + - name: Test GetCallerIdentity - Key 1 (gone) + aws_caller_info: + aws_access_key: "{{ create_key_1.access_key.access_key_id }}" + aws_secret_key: "{{ create_key_1.secret_access_key }}" + security_token: "{{ omit }}" + register: caller_identity_1 + ignore_errors: true + + - assert: + that: + - caller_identity_1 is failed + - caller_identity_1.error.code == 'InvalidClientTokenId' + + - name: Test GetCallerIdentity - Key 3 (disabled) + aws_caller_info: + aws_access_key: "{{ create_key_3.access_key.access_key_id }}" + aws_secret_key: "{{ create_key_3.secret_access_key }}" + security_token: "{{ omit }}" + register: caller_identity_3 + ignore_errors: true + + - assert: + that: + - caller_identity_3 is failed + - caller_identity_3.error.code == 'InvalidClientTokenId' + + # ================================================================================== + + - name: Delete active key (check_mode) + iam_access_key: + user_name: '{{ test_user }}' + id: '{{ create_key_2.access_key.access_key_id }}' + state: absent + register: delete_active_key + check_mode: true + + - assert: + that: + - delete_active_key is successful + - delete_active_key is changed + + - name: Delete active key + iam_access_key: + user_name: '{{ test_user }}' + id: '{{ create_key_2.access_key.access_key_id }}' + state: absent + register: delete_active_key + + - assert: + that: + - delete_active_key is successful + - delete_active_key is changed + + - name: Delete active key - idempotency (check_mode) + iam_access_key: + user_name: '{{ test_user }}' + id: '{{ create_key_2.access_key.access_key_id }}' + state: absent + register: delete_active_key + check_mode: true + + - assert: + that: + - delete_active_key is successful + - delete_active_key is not changed + + - name: Delete active key - idempotency + iam_access_key: + user_name: '{{ test_user }}' + id: '{{ create_key_2.access_key.access_key_id }}' + state: absent + register: delete_active_key + + - assert: + that: + - delete_active_key is successful + - delete_active_key is not changed + + # ================================================================================== + + - name: Delete inactive key (check_mode) + iam_access_key: + user_name: '{{ test_user }}' + id: '{{ create_key_3.access_key.access_key_id }}' + state: absent + register: delete_inactive_key + check_mode: true + + - assert: + that: + - delete_inactive_key is successful + - delete_inactive_key is changed + + - name: Delete inactive key + iam_access_key: + user_name: '{{ test_user }}' + id: '{{ create_key_3.access_key.access_key_id }}' + state: absent + register: delete_inactive_key + + - assert: + that: + - delete_inactive_key is successful + - delete_inactive_key is changed + + - name: Delete inactive key - idempotency (check_mode) + iam_access_key: + user_name: '{{ test_user }}' + id: '{{ create_key_3.access_key.access_key_id }}' + state: absent + register: delete_inactive_key + check_mode: true + + - assert: + that: + - delete_inactive_key is successful + - delete_inactive_key is not changed + + - name: Delete inactive key - idempotency + iam_access_key: + user_name: '{{ test_user }}' + id: '{{ create_key_3.access_key.access_key_id }}' + state: absent + register: delete_inactive_key + + - assert: + that: + - delete_inactive_key is successful + - delete_inactive_key is not changed + + # ================================================================================== + + - name: Fetch IAM key info (no keys) + iam_access_key_info: + user_name: '{{ test_user }}' + register: access_key_info + + - assert: + that: + - access_key_info is successful + - '"access_keys" in access_key_info' + - access_key_info.access_keys | length == 0 + + # ================================================================================== + + - name: Create an inactive key (check_mode) + iam_access_key: + user_name: '{{ test_user }}' + state: present + enabled: false + register: create_key_4 + check_mode: true + + - assert: + that: + - create_key_4 is successful + - create_key_4 is changed + + - name: Create a key + iam_access_key: + user_name: '{{ test_user }}' + state: present + enabled: false + register: create_key_4 + + - assert: + that: + - create_key_4 is successful + - create_key_4 is changed + - '"access_key" in create_key_4' + - '"secret_access_key" in create_key_4' + - '"deleted_access_key_id" not in create_key_4' + - '"access_key_id" in create_key_4.access_key' + - '"create_date" in create_key_4.access_key' + - '"user_name" in create_key_4.access_key' + - '"status" in create_key_4.access_key' + - create_key_4.access_key.user_name == test_user + - create_key_4.access_key.status == 'Inactive' + + - name: Fetch IAM key info (1 inactive key) + iam_access_key_info: + user_name: '{{ test_user }}' + register: access_key_info + + - assert: + that: + - access_key_info is successful + - '"access_keys" in access_key_info' + - access_key_info.access_keys | length == 1 + - '"access_key_id" in access_key_1' + - '"create_date" in access_key_1' + - '"user_name" in access_key_1' + - '"status" in access_key_1' + - access_key_1.user_name == test_user + - access_key_1.access_key_id == create_key_4.access_key.access_key_id + - access_key_1.create_date == create_key_4.access_key.create_date + - access_key_1.status == 'Inactive' + vars: + access_key_1: '{{ access_key_info.access_keys[0] }}' + + # We already tested the idempotency of disabling keys, use this to verify that + # the key is disabled + - name: Disable new key + iam_access_key: + user_name: '{{ test_user }}' + id: '{{ create_key_4.access_key.access_key_id }}' + enabled: False + register: disable_new_key + + - assert: + that: + - disable_new_key is successful + - disable_new_key is not changed + - '"access_key" in disable_new_key' + + # ================================================================================== + # Cleanup + + - name: Delete new key + iam_access_key: + user_name: '{{ test_user }}' + id: '{{ create_key_4.access_key.access_key_id }}' + state: absent + register: delete_new_key + + - assert: + that: + - delete_new_key is successful + - delete_new_key is changed + + - name: Remove test user + iam_user: + name: '{{ test_user }}' + state: absent + register: delete_user + + - assert: + that: + - delete_user is successful + - delete_user is changed + + always: + + - name: Remove test user + iam_user: + name: '{{ test_user }}' + state: absent + ignore_errors: yes diff --git a/ansible_collections/community/aws/tests/integration/targets/iam_group/aliases b/ansible_collections/community/aws/tests/integration/targets/iam_group/aliases new file mode 100644 index 000000000..2da398045 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/iam_group/aliases @@ -0,0 +1,7 @@ +# reason: missing-policy +# It should be possible to test iam_groups by limiting which policies can be +# attached to the groups as well as which users can be added to the groups. +# Careful review is needed prior to adding this to the main CI. +unsupported + +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/iam_group/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/iam_group/defaults/main.yml new file mode 100644 index 000000000..f5112b1a4 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/iam_group/defaults/main.yml @@ -0,0 +1,3 @@ +--- +test_user: '{{ resource_prefix }}-user' +test_group: '{{ resource_prefix }}-group' diff --git a/ansible_collections/community/aws/tests/integration/targets/iam_group/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/iam_group/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/iam_group/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/iam_group/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/iam_group/tasks/main.yml new file mode 100644 index 000000000..65b441827 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/iam_group/tasks/main.yml @@ -0,0 +1,127 @@ +--- +- name: set up aws connection info + module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + collections: + - amazon.aws + block: + - name: ensure ansible user exists + iam_user: + name: '{{ test_user }}' + state: present + + - name: ensure group exists + iam_group: + name: '{{ test_group }}' + users: + - '{{ test_user }}' + state: present + register: iam_group + + - assert: + that: + - iam_group.iam_group.users + - iam_group is changed + + - name: add non existent user to group + iam_group: + name: '{{ test_group }}' + users: + - '{{ test_user }}' + - NonExistentUser + state: present + ignore_errors: yes + register: iam_group + + - name: assert that adding non existent user to group fails with helpful message + assert: + that: + - iam_group is failed + - iam_group.msg.startswith("Couldn't add user NonExistentUser to group {{ test_group }}") + + - name: remove a user + iam_group: + name: '{{ test_group }}' + purge_users: True + users: [] + state: present + register: iam_group + + - assert: + that: + - iam_group is changed + - not iam_group.iam_group.users + + - name: re-remove a user (no change) + iam_group: + name: '{{ test_group }}' + purge_users: True + users: [] + state: present + register: iam_group + + - assert: + that: + - iam_group is not changed + - not iam_group.iam_group.users + + - name: Add the user again + iam_group: + name: '{{ test_group }}' + users: + - '{{ test_user }}' + state: present + register: iam_group + + - assert: + that: + - iam_group is changed + - iam_group.iam_group.users + + - name: Re-add the user + iam_group: + name: '{{ test_group }}' + users: + - '{{ test_user }}' + state: present + register: iam_group + + - assert: + that: + - iam_group is not changed + - iam_group.iam_group.users + + - name: remove group + iam_group: + name: '{{ test_group }}' + state: absent + register: iam_group + + - assert: + that: + - iam_group is changed + + - name: re-remove group + iam_group: + name: '{{ test_group }}' + state: absent + register: iam_group + + - assert: + that: + - iam_group is not changed + + always: + - name: remove group + iam_group: + name: '{{ test_group }}' + state: absent + + - name: remove ansible user + iam_user: + name: '{{ test_user }}' + state: absent diff --git a/ansible_collections/community/aws/tests/integration/targets/iam_managed_policy/aliases b/ansible_collections/community/aws/tests/integration/targets/iam_managed_policy/aliases new file mode 100644 index 000000000..839bd014b --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/iam_managed_policy/aliases @@ -0,0 +1,6 @@ +# reason: missing-policy +# It's not possible to control what permissions are granted to a policy. +# This makes securely testing iam_policy very difficult +unsupported + +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/iam_managed_policy/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/iam_managed_policy/defaults/main.yml new file mode 100644 index 000000000..a6edcacef --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/iam_managed_policy/defaults/main.yml @@ -0,0 +1,2 @@ +--- +policy_name: "{{ resource_prefix }}-policy" diff --git a/ansible_collections/community/aws/tests/integration/targets/iam_managed_policy/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/iam_managed_policy/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/iam_managed_policy/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/iam_managed_policy/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/iam_managed_policy/tasks/main.yml new file mode 100644 index 000000000..f17b7cad0 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/iam_managed_policy/tasks/main.yml @@ -0,0 +1,160 @@ +--- +- name: "Run integration tests for IAM managed policy" + module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + collections: + - amazon.aws + block: + ## Test policy creation + - name: Create IAM managed policy - check mode + iam_managed_policy: + policy_name: "{{ policy_name }}" + policy: + Version: "2012-10-17" + Statement: + - Effect: "Deny" + Action: "logs:CreateLogGroup" + Resource: "*" + state: present + register: result + check_mode: yes + + - name: Create IAM managed policy - check mode + assert: + that: + - result.changed + + - name: Create IAM managed policy + iam_managed_policy: + policy_name: "{{ policy_name }}" + policy: + Version: "2012-10-17" + Statement: + - Effect: "Deny" + Action: "logs:CreateLogGroup" + Resource: "*" + state: present + register: result + + - name: Create IAM managed policy + assert: + that: + - result.changed + - result.policy.policy_name == policy_name + + - name: Create IAM managed policy - idempotency check + iam_managed_policy: + policy_name: "{{ policy_name }}" + policy: + Version: "2012-10-17" + Statement: + - Effect: "Deny" + Action: "logs:CreateLogGroup" + Resource: "*" + state: present + register: result + + - name: Create IAM managed policy - idempotency check + assert: + that: + - not result.changed + + ## Test policy update + - name: Update IAM managed policy - check mode + iam_managed_policy: + policy_name: "{{ policy_name }}" + policy: + Version: "2012-10-17" + Statement: + - Effect: "Deny" + Action: "logs:Describe*" + Resource: "*" + state: present + register: result + check_mode: yes + + - name: Update IAM managed policy - check mode + assert: + that: + - result.changed + + - name: Update IAM managed policy + iam_managed_policy: + policy_name: "{{ policy_name }}" + policy: + Version: "2012-10-17" + Statement: + - Effect: "Deny" + Action: "logs:Describe*" + Resource: "*" + state: present + register: result + + - name: Update IAM managed policy + assert: + that: + - result.changed + - result.policy.policy_name == policy_name + + - name: Update IAM managed policy - idempotency check + iam_managed_policy: + policy_name: "{{ policy_name }}" + policy: + Version: "2012-10-17" + Statement: + - Effect: "Deny" + Action: "logs:Describe*" + Resource: "*" + state: present + register: result + + - name: Update IAM managed policy - idempotency check + assert: + that: + - not result.changed + + ## Test policy deletion + - name: Delete IAM managed policy - check mode + iam_managed_policy: + policy_name: "{{ policy_name }}" + state: absent + register: result + check_mode: yes + + - name: Delete IAM managed policy - check mode + assert: + that: + - result.changed + + - name: Delete IAM managed policy + iam_managed_policy: + policy_name: "{{ policy_name }}" + state: absent + register: result + + - name: Delete IAM managed policy + assert: + that: + - result.changed + + - name: Delete IAM managed policy - idempotency check + iam_managed_policy: + policy_name: "{{ policy_name }}" + state: absent + register: result + + - name: Delete IAM managed policy - idempotency check + assert: + that: + - not result.changed + + always: + - name: Delete IAM managed policy + iam_managed_policy: + policy_name: "{{ policy_name }}" + state: absent + ignore_errors: yes diff --git a/ansible_collections/community/aws/tests/integration/targets/iam_password_policy/aliases b/ansible_collections/community/aws/tests/integration/targets/iam_password_policy/aliases new file mode 100644 index 000000000..140a2f2dc --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/iam_password_policy/aliases @@ -0,0 +1,8 @@ +# reason: missing-policy +# IAM Password Policies configure account-wide settings, this makes then +# difficult to safely test +# reason: serial +# Only one password policy can be configured per account +unsupported + +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/iam_password_policy/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/iam_password_policy/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/iam_password_policy/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/iam_password_policy/tasks/main.yaml b/ansible_collections/community/aws/tests/integration/targets/iam_password_policy/tasks/main.yaml new file mode 100644 index 000000000..7b773eac8 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/iam_password_policy/tasks/main.yaml @@ -0,0 +1,107 @@ +- module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + collections: + - amazon.aws + block: + - name: set iam password policy + iam_password_policy: + state: present + min_pw_length: 8 + require_symbols: false + require_numbers: true + require_uppercase: true + require_lowercase: true + allow_pw_change: true + pw_max_age: 60 + pw_reuse_prevent: 5 + pw_expire: false + register: result + + - name: assert that changes were made + assert: + that: + - result.changed + + - name: verify iam password policy has been created + iam_password_policy: + state: present + min_pw_length: 8 + require_symbols: false + require_numbers: true + require_uppercase: true + require_lowercase: true + allow_pw_change: true + pw_max_age: 60 + pw_reuse_prevent: 5 + pw_expire: false + register: result + + - name: assert that no changes were made + assert: + that: + - not result.changed + + - name: update iam password policy with different settings + iam_password_policy: + state: present + min_pw_length: 15 + require_symbols: true + require_numbers: true + require_uppercase: true + require_lowercase: true + allow_pw_change: true + pw_max_age: 30 + pw_reuse_prevent: 10 + pw_expire: true + register: result + + - name: assert that updates were made + assert: + that: + - result.changed + + # Test for regression of #59102 + - name: update iam password policy without expiry + iam_password_policy: + state: present + min_pw_length: 15 + require_symbols: true + require_numbers: true + require_uppercase: true + require_lowercase: true + allow_pw_change: true + register: result + + - name: assert that changes were made + assert: + that: + - result.changed + + - name: remove iam password policy + iam_password_policy: + state: absent + register: result + + - name: assert password policy has been removed + assert: + that: + - result.changed + + - name: verify password policy has been removed + iam_password_policy: + state: absent + register: result + + - name: assert no changes were made + assert: + that: + - not result.changed + always: + - name: remove iam password policy + iam_password_policy: + state: absent + register: result diff --git a/ansible_collections/community/aws/tests/integration/targets/iam_role/aliases b/ansible_collections/community/aws/tests/integration/targets/iam_role/aliases new file mode 100644 index 000000000..483c86115 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/iam_role/aliases @@ -0,0 +1,9 @@ +# reason: missing-policy +# It should be possible to test iam_role by limiting which policies can be +# attached to the roles. +# Careful review is needed prior to adding this to the main CI. +unsupported + +cloud/aws + +iam_role_info diff --git a/ansible_collections/community/aws/tests/integration/targets/iam_role/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/iam_role/defaults/main.yml new file mode 100644 index 000000000..d496c4216 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/iam_role/defaults/main.yml @@ -0,0 +1,6 @@ +--- +test_role: '{{ resource_prefix }}-role' +test_path: '/{{ resource_prefix }}/' +safe_managed_policy: 'AWSDenyAll' +custom_policy_name: '{{ resource_prefix }}-denyall' +boundary_policy: 'arn:aws:iam::aws:policy/AWSDenyAll' diff --git a/ansible_collections/community/aws/tests/integration/targets/iam_role/files/deny-all-a.json b/ansible_collections/community/aws/tests/integration/targets/iam_role/files/deny-all-a.json new file mode 100644 index 000000000..ae62fd197 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/iam_role/files/deny-all-a.json @@ -0,0 +1,13 @@ +{ + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "*" + ], + "Effect": "Deny", + "Resource": "*", + "Sid": "DenyA" + } + ] +} diff --git a/ansible_collections/community/aws/tests/integration/targets/iam_role/files/deny-all-b.json b/ansible_collections/community/aws/tests/integration/targets/iam_role/files/deny-all-b.json new file mode 100644 index 000000000..3a4704a46 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/iam_role/files/deny-all-b.json @@ -0,0 +1,13 @@ +{ + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "*" + ], + "Effect": "Deny", + "Resource": "*", + "Sid": "DenyB" + } + ] +} diff --git a/ansible_collections/community/aws/tests/integration/targets/iam_role/files/deny-all.json b/ansible_collections/community/aws/tests/integration/targets/iam_role/files/deny-all.json new file mode 100644 index 000000000..3d324b9b9 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/iam_role/files/deny-all.json @@ -0,0 +1,12 @@ +{ + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "*" + ], + "Effect": "Deny", + "Resource": "*" + } + ] +} diff --git a/ansible_collections/community/aws/tests/integration/targets/iam_role/files/deny-assume.json b/ansible_collections/community/aws/tests/integration/targets/iam_role/files/deny-assume.json new file mode 100644 index 000000000..73e877158 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/iam_role/files/deny-assume.json @@ -0,0 +1,10 @@ +{ + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Principal": { "Service": "ec2.amazonaws.com" }, + "Effect": "Deny" + } + ] +} diff --git a/ansible_collections/community/aws/tests/integration/targets/iam_role/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/iam_role/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/iam_role/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/iam_role/tasks/boundary_policy.yml b/ansible_collections/community/aws/tests/integration/targets/iam_role/tasks/boundary_policy.yml new file mode 100644 index 000000000..89a983f15 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/iam_role/tasks/boundary_policy.yml @@ -0,0 +1,94 @@ +--- +- name: "Create minimal role with no boundary policy" + iam_role: + name: "{{ test_role }}" + create_instance_profile: no + register: iam_role + +- assert: + that: + - iam_role is changed + - iam_role.iam_role.role_name == test_role + +- name: "Configure Boundary Policy (CHECK MODE)" + iam_role: + name: "{{ test_role }}" + create_instance_profile: no + boundary: "{{ boundary_policy }}" + check_mode: yes + register: iam_role + +- assert: + that: + - iam_role is changed + +- name: "Configure Boundary Policy" + iam_role: + name: "{{ test_role }}" + create_instance_profile: no + boundary: "{{ boundary_policy }}" + register: iam_role + +- assert: + that: + - iam_role is changed + - iam_role.iam_role.role_name == test_role + +- name: "Configure Boundary Policy (no change) - check mode" + iam_role: + name: "{{ test_role }}" + create_instance_profile: no + boundary: "{{ boundary_policy }}" + register: iam_role + check_mode: yes + +- assert: + that: + - iam_role is not changed + +- name: "Configure Boundary Policy (no change)" + iam_role: + name: "{{ test_role }}" + create_instance_profile: no + boundary: "{{ boundary_policy }}" + register: iam_role + +- assert: + that: + - iam_role is not changed + - iam_role.iam_role.role_name == test_role + +- name: "iam_role_info after adding boundary policy" + iam_role_info: + name: "{{ test_role }}" + register: role_info + +- assert: + that: + - role_info is succeeded + - role_info.iam_roles | length == 1 + - 'role_info.iam_roles[0].arn.startswith("arn")' + - 'role_info.iam_roles[0].arn.endswith("role/" + test_role )' + - '"assume_role_policy_document" in role_info.iam_roles[0]' + - '"create_date" in role_info.iam_roles[0]' + - '"description" not in role_info.iam_roles[0]' + - role_info.iam_roles[0].inline_policies | length == 0 + - role_info.iam_roles[0].instance_profiles | length == 0 + - role_info.iam_roles[0].managed_policies | length == 0 + - role_info.iam_roles[0].max_session_duration == 3600 + - role_info.iam_roles[0].path == '/' + - role_info.iam_roles[0].permissions_boundary.permissions_boundary_arn == boundary_policy + - role_info.iam_roles[0].permissions_boundary.permissions_boundary_type == 'Policy' + - role_info.iam_roles[0].role_id == iam_role.iam_role.role_id + - role_info.iam_roles[0].role_name == test_role + +- name: "Remove IAM Role" + iam_role: + state: absent + name: "{{ test_role }}" + delete_instance_profile: yes + register: iam_role + +- assert: + that: + - iam_role is changed
\ No newline at end of file diff --git a/ansible_collections/community/aws/tests/integration/targets/iam_role/tasks/complex_role_creation.yml b/ansible_collections/community/aws/tests/integration/targets/iam_role/tasks/complex_role_creation.yml new file mode 100644 index 000000000..c23234ebf --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/iam_role/tasks/complex_role_creation.yml @@ -0,0 +1,131 @@ +--- +- name: "Complex IAM Role (CHECK MODE)" + iam_role: + name: "{{ test_role }}" + assume_role_policy_document: '{{ lookup("file", "deny-assume.json") }}' + boundary: "{{ boundary_policy }}" + create_instance_profile: no + description: "Ansible Test Role {{ resource_prefix }}" + managed_policy: + - "{{ safe_managed_policy }}" + - "{{ custom_policy_name }}" + max_session_duration: 43200 + path: "{{ test_path }}" + tags: + TagA: "ValueA" + check_mode: yes + register: iam_role + +- assert: + that: + - iam_role is changed + +- name: "iam_role_info after Complex Role creation in check_mode" + iam_role_info: + name: "{{ test_role }}" + register: role_info +- assert: + that: + - role_info is succeeded + - role_info.iam_roles | length == 0 + +- name: "Complex IAM Role" + iam_role: + name: "{{ test_role }}" + assume_role_policy_document: '{{ lookup("file", "deny-assume.json") }}' + boundary: "{{ boundary_policy }}" + create_instance_profile: no + description: "Ansible Test Role {{ resource_prefix }}" + managed_policy: + - "{{ safe_managed_policy }}" + - "{{ custom_policy_name }}" + max_session_duration: 43200 + path: "{{ test_path }}" + tags: + TagA: "ValueA" + register: iam_role + +- assert: + that: + - iam_role is changed + - iam_role.iam_role.role_name == test_role + - 'iam_role.iam_role.arn.startswith("arn")' + - 'iam_role.iam_role.arn.endswith("role" + test_path + test_role )' + # Would be nice to test the contents... + - '"assume_role_policy_document" in iam_role.iam_role' + - iam_role.iam_role.attached_policies | length == 2 + - iam_role.iam_role.max_session_duration == 43200 + - iam_role.iam_role.path == test_path + - iam_role.iam_role.role_name == test_role + - '"create_date" in iam_role.iam_role' + - '"role_id" in iam_role.iam_role' + +- name: "Complex IAM role (no change) - check mode" + iam_role: + name: "{{ test_role }}" + assume_role_policy_document: '{{ lookup("file", "deny-assume.json") }}' + boundary: "{{ boundary_policy }}" + create_instance_profile: no + description: "Ansible Test Role {{ resource_prefix }}" + managed_policy: + - "{{ safe_managed_policy }}" + - "{{ custom_policy_name }}" + max_session_duration: 43200 + path: "{{ test_path }}" + tags: + TagA: "ValueA" + register: iam_role + check_mode: yes + +- assert: + that: + - iam_role is not changed + +- name: "Complex IAM role (no change)" + iam_role: + name: "{{ test_role }}" + assume_role_policy_document: '{{ lookup("file", "deny-assume.json") }}' + boundary: "{{ boundary_policy }}" + create_instance_profile: no + description: "Ansible Test Role {{ resource_prefix }}" + managed_policy: + - "{{ safe_managed_policy }}" + - "{{ custom_policy_name }}" + max_session_duration: 43200 + path: "{{ test_path }}" + tags: + TagA: "ValueA" + register: iam_role + +- assert: + that: + - iam_role is not changed + - iam_role.iam_role.role_name == test_role + +- name: "iam_role_info after Role creation" + iam_role_info: + name: "{{ test_role }}" + register: role_info + +- assert: + that: + - role_info is succeeded + - role_info.iam_roles | length == 1 + - 'role_info.iam_roles[0].arn.startswith("arn")' + - 'role_info.iam_roles[0].arn.endswith("role" + test_path + test_role )' + - '"assume_role_policy_document" in role_info.iam_roles[0]' + - '"create_date" in role_info.iam_roles[0]' + - 'role_info.iam_roles[0].description == "Ansible Test Role {{ resource_prefix }}"' + - role_info.iam_roles[0].inline_policies | length == 0 + - role_info.iam_roles[0].instance_profiles | length == 0 + - role_info.iam_roles[0].managed_policies | length == 2 + - safe_managed_policy in ( role_info | community.general.json_query("iam_roles[*].managed_policies[*].policy_name") | list | flatten ) + - custom_policy_name in ( role_info | community.general.json_query("iam_roles[*].managed_policies[*].policy_name") | list | flatten ) + - role_info.iam_roles[0].max_session_duration == 43200 + - role_info.iam_roles[0].path == test_path + - role_info.iam_roles[0].permissions_boundary.permissions_boundary_arn == boundary_policy + - role_info.iam_roles[0].permissions_boundary.permissions_boundary_type == 'Policy' + - role_info.iam_roles[0].role_id == iam_role.iam_role.role_id + - role_info.iam_roles[0].role_name == test_role + - '"TagA" in role_info.iam_roles[0].tags' + - role_info.iam_roles[0].tags.TagA == "ValueA" diff --git a/ansible_collections/community/aws/tests/integration/targets/iam_role/tasks/creation_deletion.yml b/ansible_collections/community/aws/tests/integration/targets/iam_role/tasks/creation_deletion.yml new file mode 100644 index 000000000..0579a6d34 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/iam_role/tasks/creation_deletion.yml @@ -0,0 +1,404 @@ +--- +- name: Try running some rapid fire create/delete tests + block: + - name: "Minimal IAM Role without instance profile (rapid)" + iam_role: + name: "{{ test_role }}" + create_instance_profile: no + register: iam_role + + - name: "Minimal IAM Role without instance profile (rapid)" + iam_role: + name: "{{ test_role }}" + create_instance_profile: no + register: iam_role_again + + - assert: + that: + - iam_role is changed + - iam_role_again is not changed + + - name: "Remove IAM Role (rapid)" + iam_role: + state: absent + name: "{{ test_role }}" + register: iam_role + + - name: "Remove IAM Role (rapid)" + iam_role: + state: absent + name: "{{ test_role }}" + register: iam_role_again + + - assert: + that: + - iam_role is changed + - iam_role_again is not changed + + - name: "Minimal IAM Role without instance profile (rapid)" + iam_role: + name: "{{ test_role }}" + create_instance_profile: no + register: iam_role + + - name: "Remove IAM Role (rapid)" + iam_role: + state: absent + name: "{{ test_role }}" + + register: iam_role_again + - assert: + that: + - iam_role is changed + - iam_role_again is changed + +# =================================================================== +# Role Creation +# (without Instance profile) +- name: "iam_role_info before Role creation (no args)" + iam_role_info: + register: role_info + +- assert: + that: + - role_info is succeeded + +- name: "iam_role_info before Role creation (search for test role)" + iam_role_info: + name: "{{ test_role }}" + register: role_info + +- assert: + that: + - role_info is succeeded + - role_info.iam_roles | length == 0 + +- name: "Minimal IAM Role (CHECK MODE)" + iam_role: + name: "{{ test_role }}" + create_instance_profile: no + register: iam_role + check_mode: yes + +- assert: + that: + - iam_role is changed + +- name: "iam_role_info after Role creation in check_mode" + iam_role_info: + name: "{{ test_role }}" + register: role_info +- assert: + that: + - role_info is succeeded + - role_info.iam_roles | length == 0 + +- name: "Minimal IAM Role without instance profile" + iam_role: + name: "{{ test_role }}" + create_instance_profile: no + register: iam_role + +- assert: + that: + - iam_role is changed + - iam_role.iam_role.role_name == test_role + - 'iam_role.iam_role.arn.startswith("arn")' + - 'iam_role.iam_role.arn.endswith("role/" + test_role )' + - '"assume_role_policy_document" in iam_role.iam_role' + - '"assume_role_policy_document_raw" in iam_role.iam_role' + - iam_role.iam_role.assume_role_policy_document_raw == assume_deny_policy + - iam_role.iam_role.attached_policies | length == 0 + - iam_role.iam_role.max_session_duration == 3600 + - iam_role.iam_role.path == '/' + - iam_role.iam_role.role_name == test_role + - '"create_date" in iam_role.iam_role' + - '"role_id" in iam_role.iam_role' + +- name: "Minimal IAM Role without instance profile (no change) - check mode" + iam_role: + name: "{{ test_role }}" + create_instance_profile: no + register: iam_role + check_mode: yes + +- assert: + that: + - iam_role is not changed + +- name: "Minimal IAM Role without instance profile (no change)" + iam_role: + name: "{{ test_role }}" + create_instance_profile: no + register: iam_role + +- assert: + that: + - iam_role is not changed + - iam_role.iam_role.role_name == test_role + +- name: "iam_role_info after Role creation" + iam_role_info: + name: "{{ test_role }}" + register: role_info + +- assert: + that: + - role_info is succeeded + - role_info.iam_roles | length == 1 + - 'role_info.iam_roles[0].arn.startswith("arn")' + - 'role_info.iam_roles[0].arn.endswith("role/" + test_role )' + - '"assume_role_policy_document" in role_info.iam_roles[0]' + - '"assume_role_policy_document_raw" in role_info.iam_roles[0]' + - '"create_date" in role_info.iam_roles[0]' + - '"description" not in role_info.iam_roles[0]' + - role_info.iam_roles[0].assume_role_policy_document_raw == assume_deny_policy + - role_info.iam_roles[0].inline_policies | length == 0 + - role_info.iam_roles[0].instance_profiles | length == 0 + - role_info.iam_roles[0].managed_policies | length == 0 + - role_info.iam_roles[0].max_session_duration == 3600 + - role_info.iam_roles[0].path == '/' + - '"permissions_boundary" not in role_info.iam_roles[0]' + - role_info.iam_roles[0].role_id == iam_role.iam_role.role_id + - role_info.iam_roles[0].role_name == test_role + - role_info.iam_roles[0].tags | length == 0 + +- name: "Remove IAM Role" + iam_role: + state: absent + name: "{{ test_role }}" + delete_instance_profile: yes + register: iam_role + +- assert: + that: + - iam_role is changed + +- name: "iam_role_info after Role deletion" + iam_role_info: + name: "{{ test_role }}" + register: role_info + +- assert: + that: + - role_info is succeeded + - role_info.iam_roles | length == 0 + +# ------------------------------------------------------------------------------------------ + +# (with path) +- name: "Minimal IAM Role with path (CHECK MODE)" + iam_role: + name: "{{ test_role }}" + path: "{{ test_path }}" + register: iam_role + check_mode: yes + +- assert: + that: + - iam_role is changed + +- name: "Minimal IAM Role with path" + iam_role: + name: "{{ test_role }}" + path: "{{ test_path }}" + register: iam_role + +- assert: + that: + - iam_role is changed + - iam_role.iam_role.role_name == test_role + - 'iam_role.iam_role.arn.startswith("arn")' + - 'iam_role.iam_role.arn.endswith("role" + test_path + test_role )' + # Would be nice to test the contents... + - '"assume_role_policy_document" in iam_role.iam_role' + - iam_role.iam_role.attached_policies | length == 0 + - iam_role.iam_role.max_session_duration == 3600 + - iam_role.iam_role.path == '{{ test_path }}' + - iam_role.iam_role.role_name == test_role + - '"create_date" in iam_role.iam_role' + - '"role_id" in iam_role.iam_role' + +- name: "Minimal IAM Role with path (no change) - check mode" + iam_role: + name: "{{ test_role }}" + path: "{{ test_path }}" + register: iam_role + check_mode: yes + +- assert: + that: + - iam_role is not changed + +- name: "Minimal IAM Role with path (no change)" + iam_role: + name: "{{ test_role }}" + path: "{{ test_path }}" + register: iam_role + +- assert: + that: + - iam_role is not changed + - iam_role.iam_role.role_name == test_role + +- name: "iam_role_info after Role creation" + iam_role_info: + name: "{{ test_role }}" + register: role_info + +- assert: + that: + - role_info is succeeded + - role_info.iam_roles | length == 1 + - 'role_info.iam_roles[0].arn.startswith("arn")' + - 'role_info.iam_roles[0].arn.endswith("role" + test_path + test_role )' + - '"assume_role_policy_document" in role_info.iam_roles[0]' + - '"create_date" in role_info.iam_roles[0]' + - '"description" not in role_info.iam_roles[0]' + - role_info.iam_roles[0].inline_policies | length == 0 + - role_info.iam_roles[0].instance_profiles | length == 1 + - role_info.iam_roles[0].instance_profiles[0].instance_profile_name == test_role + - 'role_info.iam_roles[0].instance_profiles[0].arn.startswith("arn")' + - 'role_info.iam_roles[0].instance_profiles[0].arn.endswith("instance-profile" + test_path + test_role)' + - role_info.iam_roles[0].managed_policies | length == 0 + - role_info.iam_roles[0].max_session_duration == 3600 + - role_info.iam_roles[0].path == '{{ test_path }}' + - '"permissions_boundary" not in role_info.iam_roles[0]' + - role_info.iam_roles[0].role_id == iam_role.iam_role.role_id + - role_info.iam_roles[0].role_name == test_role + - role_info.iam_roles[0].tags | length == 0 + +- name: "iam_role_info after Role creation (searching a path)" + iam_role_info: + path_prefix: "{{ test_path }}" + register: role_info + +- assert: + that: + - role_info is succeeded + - role_info.iam_roles | length == 1 + - 'role_info.iam_roles[0].arn.startswith("arn")' + - 'role_info.iam_roles[0].arn.endswith("role" + test_path + test_role )' + - '"assume_role_policy_document" in role_info.iam_roles[0]' + - '"create_date" in role_info.iam_roles[0]' + - '"description" not in role_info.iam_roles[0]' + - role_info.iam_roles[0].inline_policies | length == 0 + - role_info.iam_roles[0].instance_profiles | length == 1 + - role_info.iam_roles[0].instance_profiles[0].instance_profile_name == test_role + - 'role_info.iam_roles[0].instance_profiles[0].arn.startswith("arn")' + - 'role_info.iam_roles[0].instance_profiles[0].arn.endswith("instance-profile" + test_path + test_role)' + - role_info.iam_roles[0].managed_policies | length == 0 + - role_info.iam_roles[0].max_session_duration == 3600 + - '"permissions_boundary" not in role_info.iam_roles[0]' + - role_info.iam_roles[0].path == '{{ test_path }}' + - role_info.iam_roles[0].role_id == iam_role.iam_role.role_id + - role_info.iam_roles[0].role_name == test_role + - role_info.iam_roles[0].tags | length == 0 + +- name: "Remove IAM Role" + iam_role: + state: absent + name: "{{ test_role }}" + path: "{{ test_path }}" + # If we don't delete the existing profile it'll be reused (with the path) + # by the test below. + delete_instance_profile: yes + register: iam_role + +- assert: + that: + - iam_role is changed + +- name: "iam_role_info after Role deletion" + iam_role_info: + name: "{{ test_role }}" + register: role_info + +- assert: + that: + - role_info is succeeded + - role_info.iam_roles | length == 0 + +# ------------------------------------------------------------------------------------------ + +# (with Instance profile) +- name: "Minimal IAM Role with instance profile - check mode" + iam_role: + name: "{{ test_role }}" + create_instance_profile: yes + register: iam_role + check_mode: yes + +- assert: + that: + - iam_role is changed + +- name: "Minimal IAM Role with instance profile" + iam_role: + name: "{{ test_role }}" + create_instance_profile: yes + register: iam_role + +- assert: + that: + - iam_role is changed + - iam_role.iam_role.role_name == test_role + - 'iam_role.iam_role.arn.startswith("arn")' + - 'iam_role.iam_role.arn.endswith("role/" + test_role )' + # Would be nice to test the contents... + - '"assume_role_policy_document" in iam_role.iam_role' + - iam_role.iam_role.attached_policies | length == 0 + - iam_role.iam_role.max_session_duration == 3600 + - iam_role.iam_role.path == '/' + - iam_role.iam_role.role_name == test_role + - '"create_date" in iam_role.iam_role' + - '"role_id" in iam_role.iam_role' + +- name: "Minimal IAM Role wth instance profile (no change) - check mode" + iam_role: + name: "{{ test_role }}" + create_instance_profile: yes + register: iam_role + check_mode: yes + +- assert: + that: + - iam_role is not changed + +- name: "Minimal IAM Role wth instance profile (no change)" + iam_role: + name: "{{ test_role }}" + create_instance_profile: yes + register: iam_role + +- assert: + that: + - iam_role is not changed + - iam_role.iam_role.role_name == test_role + +- name: "iam_role_info after Role creation" + iam_role_info: + name: "{{ test_role }}" + register: role_info + +- assert: + that: + - role_info is succeeded + - role_info.iam_roles | length == 1 + - 'role_info.iam_roles[0].arn.startswith("arn")' + - 'role_info.iam_roles[0].arn.endswith("role/" + test_role )' + - '"assume_role_policy_document" in role_info.iam_roles[0]' + - '"create_date" in role_info.iam_roles[0]' + - '"description" not in role_info.iam_roles[0]' + - role_info.iam_roles[0].inline_policies | length == 0 + - role_info.iam_roles[0].instance_profiles | length == 1 + - role_info.iam_roles[0].instance_profiles[0].instance_profile_name == test_role + - 'role_info.iam_roles[0].instance_profiles[0].arn.startswith("arn")' + - 'role_info.iam_roles[0].instance_profiles[0].arn.endswith("instance-profile/" + test_role)' + - role_info.iam_roles[0].managed_policies | length == 0 + - role_info.iam_roles[0].max_session_duration == 3600 + - role_info.iam_roles[0].path == '/' + - '"permissions_boundary" not in role_info.iam_roles[0]' + - role_info.iam_roles[0].role_id == iam_role.iam_role.role_id + - role_info.iam_roles[0].role_name == test_role + - role_info.iam_roles[0].tags | length == 0 diff --git a/ansible_collections/community/aws/tests/integration/targets/iam_role/tasks/description_update.yml b/ansible_collections/community/aws/tests/integration/targets/iam_role/tasks/description_update.yml new file mode 100644 index 000000000..85f5e1f56 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/iam_role/tasks/description_update.yml @@ -0,0 +1,148 @@ +--- +- name: "Add Description (CHECK MODE)" + iam_role: + name: "{{ test_role }}" + description: "Ansible Test Role {{ resource_prefix }}" + check_mode: yes + register: iam_role + +- assert: + that: + - iam_role is changed + +- name: "Add Description" + iam_role: + name: "{{ test_role }}" + description: "Ansible Test Role {{ resource_prefix }}" + register: iam_role + +- assert: + that: + - iam_role is changed + - iam_role.iam_role.role_name == test_role + - iam_role.iam_role.description == 'Ansible Test Role {{ resource_prefix }}' + +- name: "Add Description (no change) - check mode" + iam_role: + name: "{{ test_role }}" + description: "Ansible Test Role {{ resource_prefix }}" + register: iam_role + check_mode: yes + +- assert: + that: + - iam_role is not changed + +- name: "Add Description (no change)" + iam_role: + name: "{{ test_role }}" + description: "Ansible Test Role {{ resource_prefix }}" + register: iam_role + +- assert: + that: + - iam_role is not changed + - iam_role.iam_role.role_name == test_role + - iam_role.iam_role.description == 'Ansible Test Role {{ resource_prefix }}' + +- name: "iam_role_info after adding Description" + iam_role_info: + name: "{{ test_role }}" + register: role_info + +- assert: + that: + - role_info is succeeded + - role_info.iam_roles | length == 1 + - 'role_info.iam_roles[0].arn.startswith("arn")' + - 'role_info.iam_roles[0].arn.endswith("role/" + test_role )' + - '"assume_role_policy_document" in role_info.iam_roles[0]' + - '"create_date" in role_info.iam_roles[0]' + - 'role_info.iam_roles[0].description == "Ansible Test Role {{ resource_prefix }}"' + - role_info.iam_roles[0].inline_policies | length == 0 + - role_info.iam_roles[0].instance_profiles | length == 1 + - role_info.iam_roles[0].instance_profiles[0].instance_profile_name == test_role + - 'role_info.iam_roles[0].instance_profiles[0].arn.startswith("arn")' + - 'role_info.iam_roles[0].instance_profiles[0].arn.endswith("instance-profile/" + test_role)' + - role_info.iam_roles[0].managed_policies | length == 0 + - role_info.iam_roles[0].max_session_duration == 43200 + - role_info.iam_roles[0].path == '/' + - '"permissions_boundary" not in role_info.iam_roles[0]' + - role_info.iam_roles[0].role_id == iam_role.iam_role.role_id + - role_info.iam_roles[0].role_name == test_role + - role_info.iam_roles[0].tags | length == 0 + +# ------------------------------------------------------------------------------------------ + +- name: "Update Description (CHECK MODE)" + iam_role: + name: "{{ test_role }}" + description: "Ansible Test Role (updated) {{ resource_prefix }}" + check_mode: yes + register: iam_role + +- assert: + that: + - iam_role is changed + +- name: "Update Description" + iam_role: + name: "{{ test_role }}" + description: "Ansible Test Role (updated) {{ resource_prefix }}" + register: iam_role + +- assert: + that: + - iam_role is changed + - iam_role.iam_role.role_name == test_role + - iam_role.iam_role.description == 'Ansible Test Role (updated) {{ resource_prefix }}' + +- name: "Update Description (no change) - check mode" + iam_role: + name: "{{ test_role }}" + description: "Ansible Test Role (updated) {{ resource_prefix }}" + register: iam_role + check_mode: yes + +- assert: + that: + - iam_role is not changed + +- name: "Update Description (no change)" + iam_role: + name: "{{ test_role }}" + description: "Ansible Test Role (updated) {{ resource_prefix }}" + register: iam_role + +- assert: + that: + - iam_role is not changed + - iam_role.iam_role.role_name == test_role + - iam_role.iam_role.description == 'Ansible Test Role (updated) {{ resource_prefix }}' + +- name: "iam_role_info after updating Description" + iam_role_info: + name: "{{ test_role }}" + register: role_info + +- assert: + that: + - role_info is succeeded + - role_info.iam_roles | length == 1 + - 'role_info.iam_roles[0].arn.startswith("arn")' + - 'role_info.iam_roles[0].arn.endswith("role/" + test_role )' + - '"assume_role_policy_document" in role_info.iam_roles[0]' + - '"create_date" in role_info.iam_roles[0]' + - 'role_info.iam_roles[0].description == "Ansible Test Role (updated) {{ resource_prefix }}"' + - role_info.iam_roles[0].inline_policies | length == 0 + - role_info.iam_roles[0].instance_profiles | length == 1 + - role_info.iam_roles[0].instance_profiles[0].instance_profile_name == test_role + - 'role_info.iam_roles[0].instance_profiles[0].arn.startswith("arn")' + - 'role_info.iam_roles[0].instance_profiles[0].arn.endswith("instance-profile/" + test_role)' + - role_info.iam_roles[0].managed_policies | length == 0 + - role_info.iam_roles[0].max_session_duration == 43200 + - role_info.iam_roles[0].path == '/' + - '"permissions_boundary" not in role_info.iam_roles[0]' + - role_info.iam_roles[0].role_id == iam_role.iam_role.role_id + - role_info.iam_roles[0].role_name == test_role + - role_info.iam_roles[0].tags | length == 0 diff --git a/ansible_collections/community/aws/tests/integration/targets/iam_role/tasks/inline_policy_update.yml b/ansible_collections/community/aws/tests/integration/targets/iam_role/tasks/inline_policy_update.yml new file mode 100644 index 000000000..d364d87d7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/iam_role/tasks/inline_policy_update.yml @@ -0,0 +1,48 @@ +--- +- name: "Attach inline policy a" + iam_policy: + state: present + iam_type: "role" + iam_name: "{{ test_role }}" + policy_name: "inline-policy-a" + policy_json: '{{ lookup("file", "deny-all-a.json") }}' + +- name: "Attach inline policy b" + iam_policy: + state: present + iam_type: "role" + iam_name: "{{ test_role }}" + policy_name: "inline-policy-b" + policy_json: '{{ lookup("file", "deny-all-b.json") }}' + +- name: "iam_role_info after attaching inline policies (using iam_policy)" + iam_role_info: + name: "{{ test_role }}" + register: role_info +- assert: + that: + - role_info is succeeded + - role_info.iam_roles | length == 1 + - 'role_info.iam_roles[0].arn.startswith("arn")' + - 'role_info.iam_roles[0].arn.endswith("role/" + test_role )' + - '"assume_role_policy_document" in role_info.iam_roles[0]' + - '"create_date" in role_info.iam_roles[0]' + - 'role_info.iam_roles[0].description == "Ansible Test Role (updated) {{ resource_prefix }}"' + - role_info.iam_roles[0].inline_policies | length == 2 + - '"inline-policy-a" in role_info.iam_roles[0].inline_policies' + - '"inline-policy-b" in role_info.iam_roles[0].inline_policies' + - role_info.iam_roles[0].instance_profiles | length == 1 + - role_info.iam_roles[0].instance_profiles[0].instance_profile_name == test_role + - 'role_info.iam_roles[0].instance_profiles[0].arn.startswith("arn")' + - 'role_info.iam_roles[0].instance_profiles[0].arn.endswith("instance-profile/" + test_role)' + - role_info.iam_roles[0].managed_policies | length == 1 + - safe_managed_policy not in ( role_info | community.general.json_query("iam_roles[*].managed_policies[*].policy_name") | list | flatten ) + - custom_policy_name in ( role_info | community.general.json_query("iam_roles[*].managed_policies[*].policy_name") | list | flatten ) + - role_info.iam_roles[0].max_session_duration == 43200 + - role_info.iam_roles[0].path == '/' + - '"permissions_boundary" not in role_info.iam_roles[0]' + - role_info.iam_roles[0].role_id == iam_role.iam_role.role_id + - role_info.iam_roles[0].role_name == test_role + - role_info.iam_roles[0].tags | length == 1 + - '"TagB" in role_info.iam_roles[0].tags' + - role_info.iam_roles[0].tags.TagB == "ValueB" diff --git a/ansible_collections/community/aws/tests/integration/targets/iam_role/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/iam_role/tasks/main.yml new file mode 100644 index 000000000..ae47ada1a --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/iam_role/tasks/main.yml @@ -0,0 +1,119 @@ +--- +# Tests for iam_role and iam_role_info +# +# Tests: +# - Minimal Role creation +# - Role deletion +# - Fetching a specific role +# - Creating roles w/ and w/o instance profiles +# - Creating roles w/ a path +# - Updating Max Session Duration +# - Updating Description +# - Managing list of managed policies +# - Managing list of inline policies (for testing _info) +# - Managing boundary policy +# +# Notes: +# - Only tests *documented* return values ( RESULT.iam_role ) +# - There are some known timing issues with boto3 returning before actions +# complete in the case of problems with "changed" status it's worth enabling +# the standard_pauses and paranoid_pauses options as a first step in debugging + + +- name: "Setup AWS connection info" + module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + iam_role: + assume_role_policy_document: '{{ lookup("file", "deny-assume.json") }}' + collections: + - amazon.aws + - community.general + block: + - set_fact: + assume_deny_policy: '{{ lookup("file", "deny-assume.json") | from_json }}' + # =================================================================== + # Parameter Checks + - include_tasks: parameter_checks.yml + + # =================================================================== + # Supplemental resource pre-creation + - name: "Create Safe IAM Managed Policy" + iam_managed_policy: + state: present + policy_name: "{{ custom_policy_name }}" + policy_description: "A safe (deny-all) managed policy" + policy: "{{ lookup('file', 'deny-all.json') }}" + register: create_managed_policy + + - assert: + that: + - create_managed_policy is succeeded + + # =================================================================== + # Rapid Role Creation and deletion + - include_tasks: creation_deletion.yml + + # =================================================================== + # Max Session Duration Manipulation + - include_tasks: max_session_update.yml + + # =================================================================== + # Description Manipulation + - include_tasks: description_update.yml + + # =================================================================== + # Tag Manipulation + - include_tasks: tags_update.yml + + # =================================================================== + # Policy Manipulation + - include_tasks: policy_update.yml + + # =================================================================== + # Inline Policy (test _info behavior) + - include_tasks: inline_policy_update.yml + + # =================================================================== + # Role Removal + - include_tasks: role_removal.yml + + # =================================================================== + # Boundary Policy (requires create_instance_profile: no) + - include_tasks: boundary_policy.yml + + # =================================================================== + # Complex role Creation + - include_tasks: complex_role_creation.yml + + always: + # =================================================================== + # Cleanup + + - name: "Remove IAM Role" + iam_role: + state: absent + name: "{{ test_role }}" + delete_instance_profile: yes + ignore_errors: true + + - name: "Remove IAM Role (with path)" + iam_role: + state: absent + name: "{{ test_role }}" + path: "{{ test_path }}" + delete_instance_profile: yes + ignore_errors: true + + - name: "iam_role_info after Role deletion" + iam_role_info: + name: "{{ test_role }}" + ignore_errors: true + + - name: "Remove test managed policy" + iam_managed_policy: + state: absent + policy_name: "{{ custom_policy_name }}" diff --git a/ansible_collections/community/aws/tests/integration/targets/iam_role/tasks/max_session_update.yml b/ansible_collections/community/aws/tests/integration/targets/iam_role/tasks/max_session_update.yml new file mode 100644 index 000000000..8ad3641be --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/iam_role/tasks/max_session_update.yml @@ -0,0 +1,71 @@ +--- +- name: "Update Max Session Duration (CHECK MODE)" + iam_role: + name: "{{ test_role }}" + max_session_duration: 43200 + check_mode: yes + register: iam_role + +- assert: + that: + - iam_role is changed + +- name: "Update Max Session Duration" + iam_role: + name: "{{ test_role }}" + max_session_duration: 43200 + register: iam_role + +- assert: + that: + - iam_role is changed + - iam_role.iam_role.role_name == test_role + - iam_role.iam_role.max_session_duration == 43200 + +- name: "Update Max Session Duration (no change)" + iam_role: + name: "{{ test_role }}" + max_session_duration: 43200 + register: iam_role + +- assert: + that: + - iam_role is not changed + +- name: "Update Max Session Duration (no change) - check mode" + iam_role: + name: "{{ test_role }}" + max_session_duration: 43200 + register: iam_role + check_mode: yes + +- assert: + that: + - iam_role is not changed + +- name: "iam_role_info after updating Max Session Duration" + iam_role_info: + name: "{{ test_role }}" + register: role_info + +- assert: + that: + - role_info is succeeded + - role_info.iam_roles | length == 1 + - 'role_info.iam_roles[0].arn.startswith("arn")' + - 'role_info.iam_roles[0].arn.endswith("role/" + test_role )' + - '"assume_role_policy_document" in role_info.iam_roles[0]' + - '"create_date" in role_info.iam_roles[0]' + - '"description" not in role_info.iam_roles[0]' + - role_info.iam_roles[0].inline_policies | length == 0 + - role_info.iam_roles[0].instance_profiles | length == 1 + - role_info.iam_roles[0].instance_profiles[0].instance_profile_name == test_role + - 'role_info.iam_roles[0].instance_profiles[0].arn.startswith("arn")' + - 'role_info.iam_roles[0].instance_profiles[0].arn.endswith("instance-profile/" + test_role)' + - role_info.iam_roles[0].managed_policies | length == 0 + - role_info.iam_roles[0].max_session_duration == 43200 + - role_info.iam_roles[0].path == '/' + - '"permissions_boundary" not in role_info.iam_roles[0]' + - role_info.iam_roles[0].role_id == iam_role.iam_role.role_id + - role_info.iam_roles[0].role_name == test_role + - role_info.iam_roles[0].tags | length == 0 diff --git a/ansible_collections/community/aws/tests/integration/targets/iam_role/tasks/parameter_checks.yml b/ansible_collections/community/aws/tests/integration/targets/iam_role/tasks/parameter_checks.yml new file mode 100644 index 000000000..57df5436a --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/iam_role/tasks/parameter_checks.yml @@ -0,0 +1,90 @@ +--- +# Parameter Checks +- name: "Friendly message when creating an instance profile and adding a boundary profile" + iam_role: + name: "{{ test_role }}" + boundary: "{{ boundary_policy }}" + register: iam_role + ignore_errors: yes + +- assert: + that: + - iam_role is failed + - '"boundary policy" in iam_role.msg' + - '"create_instance_profile" in iam_role.msg' + - '"false" in iam_role.msg' + +- name: "Friendly message when boundary profile is not an ARN" + iam_role: + name: "{{ test_role }}" + boundary: "AWSDenyAll" + create_instance_profile: no + register: iam_role + ignore_errors: yes + +- assert: + that: + - iam_role is failed + - '"Boundary policy" in iam_role.msg' + - '"ARN" in iam_role.msg' + +- name: 'Friendly message when "present" without assume_role_policy_document' + module_defaults: { iam_role: {} } + iam_role: + name: "{{ test_role }}" + register: iam_role + ignore_errors: yes + +- assert: + that: + - iam_role is failed + - 'iam_role.msg.startswith("state is present but all of the following are missing")' + - '"assume_role_policy_document" in iam_role.msg' + +- name: "Maximum Session Duration needs to be between 1 and 12 hours" + iam_role: + name: "{{ test_role }}" + max_session_duration: 3599 + register: iam_role + ignore_errors: yes + +- assert: + that: + - iam_role is failed + - '"max_session_duration must be between" in iam_role.msg' + +- name: "Maximum Session Duration needs to be between 1 and 12 hours" + iam_role: + name: "{{ test_role }}" + max_session_duration: 43201 + register: iam_role + ignore_errors: yes + +- assert: + that: + - iam_role is failed + - '"max_session_duration must be between" in iam_role.msg' + +- name: "Role Paths must start with /" + iam_role: + name: "{{ test_role }}" + path: "test/" + register: iam_role + ignore_errors: yes + +- assert: + that: + - iam_role is failed + - '"path must begin and end with /" in iam_role.msg' + +- name: "Role Paths must end with /" + iam_role: + name: "{{ test_role }}" + path: "/test" + register: iam_role + ignore_errors: yes + +- assert: + that: + - iam_role is failed + - '"path must begin and end with /" in iam_role.msg' diff --git a/ansible_collections/community/aws/tests/integration/targets/iam_role/tasks/policy_update.yml b/ansible_collections/community/aws/tests/integration/targets/iam_role/tasks/policy_update.yml new file mode 100644 index 000000000..a822edf74 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/iam_role/tasks/policy_update.yml @@ -0,0 +1,250 @@ +--- +- name: "Add Managed Policy (CHECK MODE)" + iam_role: + name: "{{ test_role }}" + purge_policies: no + managed_policy: + - "{{ safe_managed_policy }}" + check_mode: yes + register: iam_role + +- assert: + that: + - iam_role is changed + +- name: "Add Managed Policy" + iam_role: + name: "{{ test_role }}" + purge_policies: no + managed_policy: + - "{{ safe_managed_policy }}" + register: iam_role + +- assert: + that: + - iam_role is changed + - iam_role.iam_role.role_name == test_role + +- name: "Add Managed Policy (no change) - check mode" + iam_role: + name: "{{ test_role }}" + purge_policies: no + managed_policy: + - "{{ safe_managed_policy }}" + register: iam_role + check_mode: yes + +- assert: + that: + - iam_role is not changed + +- name: "Add Managed Policy (no change)" + iam_role: + name: "{{ test_role }}" + purge_policies: no + managed_policy: + - "{{ safe_managed_policy }}" + register: iam_role + +- assert: + that: + - iam_role is not changed + - iam_role.iam_role.role_name == test_role + +- name: "iam_role_info after adding Managed Policy" + iam_role_info: + name: "{{ test_role }}" + register: role_info + +- assert: + that: + - role_info is succeeded + - role_info.iam_roles | length == 1 + - 'role_info.iam_roles[0].arn.startswith("arn")' + - 'role_info.iam_roles[0].arn.endswith("role/" + test_role )' + - '"assume_role_policy_document" in role_info.iam_roles[0]' + - '"create_date" in role_info.iam_roles[0]' + - 'role_info.iam_roles[0].description == "Ansible Test Role (updated) {{ resource_prefix }}"' + - role_info.iam_roles[0].inline_policies | length == 0 + - role_info.iam_roles[0].instance_profiles | length == 1 + - role_info.iam_roles[0].instance_profiles[0].instance_profile_name == test_role + - 'role_info.iam_roles[0].instance_profiles[0].arn.startswith("arn")' + - 'role_info.iam_roles[0].instance_profiles[0].arn.endswith("instance-profile/" + test_role)' + - role_info.iam_roles[0].managed_policies | length == 1 + - safe_managed_policy in ( role_info | community.general.json_query("iam_roles[*].managed_policies[*].policy_name") | list | flatten ) + - custom_policy_name not in ( role_info | community.general.json_query("iam_roles[*].managed_policies[*].policy_name") | list | flatten ) + - role_info.iam_roles[0].max_session_duration == 43200 + - role_info.iam_roles[0].path == '/' + - '"permissions_boundary" not in role_info.iam_roles[0]' + - role_info.iam_roles[0].role_id == iam_role.iam_role.role_id + - role_info.iam_roles[0].role_name == test_role + - role_info.iam_roles[0].tags | length == 1 + - '"TagB" in role_info.iam_roles[0].tags' + - role_info.iam_roles[0].tags.TagB == "ValueB" + +# ------------------------------------------------------------------------------------------ + +- name: "Update Managed Policy without purge (CHECK MODE)" + iam_role: + name: "{{ test_role }}" + purge_policies: no + managed_policy: + - "{{ custom_policy_name }}" + check_mode: yes + register: iam_role + +- assert: + that: + - iam_role is changed + +- name: "Update Managed Policy without purge" + iam_role: + name: "{{ test_role }}" + purge_policies: no + managed_policy: + - "{{ custom_policy_name }}" + register: iam_role + +- assert: + that: + - iam_role is changed + - iam_role.iam_role.role_name == test_role + +- name: "Update Managed Policy without purge (no change) - check mode" + iam_role: + name: "{{ test_role }}" + purge_policies: no + managed_policy: + - "{{ custom_policy_name }}" + register: iam_role + check_mode: yes + +- assert: + that: + - iam_role is not changed + +- name: "Update Managed Policy without purge (no change)" + iam_role: + name: "{{ test_role }}" + purge_policies: no + managed_policy: + - "{{ custom_policy_name }}" + register: iam_role + +- assert: + that: + - iam_role is not changed + - iam_role.iam_role.role_name == test_role + +- name: "iam_role_info after updating Managed Policy without purge" + iam_role_info: + name: "{{ test_role }}" + register: role_info + +- assert: + that: + - role_info is succeeded + - role_info.iam_roles | length == 1 + - 'role_info.iam_roles[0].arn.startswith("arn")' + - 'role_info.iam_roles[0].arn.endswith("role/" + test_role )' + - '"assume_role_policy_document" in role_info.iam_roles[0]' + - '"create_date" in role_info.iam_roles[0]' + - 'role_info.iam_roles[0].description == "Ansible Test Role (updated) {{ resource_prefix }}"' + - role_info.iam_roles[0].inline_policies | length == 0 + - role_info.iam_roles[0].instance_profiles | length == 1 + - role_info.iam_roles[0].instance_profiles[0].instance_profile_name == test_role + - 'role_info.iam_roles[0].instance_profiles[0].arn.startswith("arn")' + - 'role_info.iam_roles[0].instance_profiles[0].arn.endswith("instance-profile/" + test_role)' + - role_info.iam_roles[0].managed_policies | length == 2 + - safe_managed_policy in ( role_info | community.general.json_query("iam_roles[*].managed_policies[*].policy_name") | list | flatten ) + - custom_policy_name in ( role_info | community.general.json_query("iam_roles[*].managed_policies[*].policy_name") | list | flatten ) + - role_info.iam_roles[0].max_session_duration == 43200 + - role_info.iam_roles[0].path == '/' + - '"permissions_boundary" not in role_info.iam_roles[0]' + - role_info.iam_roles[0].role_id == iam_role.iam_role.role_id + - role_info.iam_roles[0].role_name == test_role + - role_info.iam_roles[0].tags | length == 1 + - '"TagB" in role_info.iam_roles[0].tags' + - role_info.iam_roles[0].tags.TagB == "ValueB" + +# ------------------------------------------------------------------------------------------ + +# Managed Policies are purged by default +- name: "Update Managed Policy with purge (CHECK MODE)" + iam_role: + name: "{{ test_role }}" + managed_policy: + - "{{ custom_policy_name }}" + check_mode: yes + register: iam_role + +- assert: + that: + - iam_role is changed + +- name: "Update Managed Policy with purge" + iam_role: + name: "{{ test_role }}" + managed_policy: + - "{{ custom_policy_name }}" + register: iam_role + +- assert: + that: + - iam_role is changed + - iam_role.iam_role.role_name == test_role + +- name: "Update Managed Policy with purge (no change) - check mode" + iam_role: + name: "{{ test_role }}" + managed_policy: + - "{{ custom_policy_name }}" + register: iam_role + check_mode: yes + +- assert: + that: + - iam_role is not changed + +- name: "Update Managed Policy with purge (no change)" + iam_role: + name: "{{ test_role }}" + managed_policy: + - "{{ custom_policy_name }}" + register: iam_role + +- assert: + that: + - iam_role is not changed + - iam_role.iam_role.role_name == test_role + +- name: "iam_role_info after updating Managed Policy with purge" + iam_role_info: + name: "{{ test_role }}" + register: role_info + +- assert: + that: + - role_info is succeeded + - role_info.iam_roles | length == 1 + - 'role_info.iam_roles[0].arn.startswith("arn")' + - 'role_info.iam_roles[0].arn.endswith("role/" + test_role )' + - '"assume_role_policy_document" in role_info.iam_roles[0]' + - '"create_date" in role_info.iam_roles[0]' + - 'role_info.iam_roles[0].description == "Ansible Test Role (updated) {{ resource_prefix }}"' + - role_info.iam_roles[0].inline_policies | length == 0 + - role_info.iam_roles[0].instance_profiles | length == 1 + - role_info.iam_roles[0].instance_profiles[0].instance_profile_name == test_role + - 'role_info.iam_roles[0].instance_profiles[0].arn.startswith("arn")' + - 'role_info.iam_roles[0].instance_profiles[0].arn.endswith("instance-profile/" + test_role)' + - role_info.iam_roles[0].managed_policies | length == 1 + - safe_managed_policy not in ( role_info | community.general.json_query("iam_roles[*].managed_policies[*].policy_name") | list | flatten ) + - custom_policy_name in ( role_info | community.general.json_query("iam_roles[*].managed_policies[*].policy_name") | list | flatten ) + - role_info.iam_roles[0].max_session_duration == 43200 + - role_info.iam_roles[0].path == '/' + - '"permissions_boundary" not in role_info.iam_roles[0]' + - role_info.iam_roles[0].role_id == iam_role.iam_role.role_id + - role_info.iam_roles[0].role_name == test_role + - role_info.iam_roles[0].tags | length == 1 + - '"TagB" in role_info.iam_roles[0].tags' + - role_info.iam_roles[0].tags.TagB == "ValueB" diff --git a/ansible_collections/community/aws/tests/integration/targets/iam_role/tasks/role_removal.yml b/ansible_collections/community/aws/tests/integration/targets/iam_role/tasks/role_removal.yml new file mode 100644 index 000000000..ebcfd5453 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/iam_role/tasks/role_removal.yml @@ -0,0 +1,65 @@ +--- +- name: "Remove IAM Role (CHECK MODE)" + iam_role: + state: absent + name: "{{ test_role }}" + delete_instance_profile: yes + check_mode: yes + register: iam_role + +- assert: + that: + - iam_role is changed + +- name: "iam_role_info after deleting role in check mode" + iam_role_info: + name: "{{ test_role }}" + register: role_info + +- assert: + that: + - role_info is succeeded + - role_info.iam_roles | length == 1 + +- name: "Remove IAM Role" + iam_role: + state: absent + name: "{{ test_role }}" + delete_instance_profile: yes + register: iam_role + +- assert: + that: + - iam_role is changed + +- name: "iam_role_info after deleting role" + iam_role_info: + name: "{{ test_role }}" + register: role_info +- assert: + that: + - role_info is succeeded + - role_info.iam_roles | length == 0 + +- name: "Remove IAM Role (should be gone already) - check mode" + iam_role: + state: absent + name: "{{ test_role }}" + delete_instance_profile: yes + register: iam_role + check_mode: yes + +- assert: + that: + - iam_role is not changed + +- name: "Remove IAM Role (should be gone already)" + iam_role: + state: absent + name: "{{ test_role }}" + delete_instance_profile: yes + register: iam_role + +- assert: + that: + - iam_role is not changed diff --git a/ansible_collections/community/aws/tests/integration/targets/iam_role/tasks/tags_update.yml b/ansible_collections/community/aws/tests/integration/targets/iam_role/tasks/tags_update.yml new file mode 100644 index 000000000..5eadd9fdf --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/iam_role/tasks/tags_update.yml @@ -0,0 +1,341 @@ +--- +- name: "Add Tag (CHECK MODE)" + iam_role: + name: "{{ test_role }}" + tags: + TagA: ValueA + check_mode: yes + register: iam_role + +- assert: + that: + - iam_role is changed + +- name: "Add Tag" + iam_role: + name: "{{ test_role }}" + tags: + TagA: ValueA + register: iam_role + +- assert: + that: + - iam_role is changed + - iam_role.iam_role.role_name == test_role + - iam_role.iam_role.tags | length == 1 + - '"TagA" in iam_role.iam_role.tags' + - iam_role.iam_role.tags.TagA == "ValueA" + +- name: "Add Tag (no change) - check mode" + iam_role: + name: "{{ test_role }}" + tags: + TagA: ValueA + register: iam_role + check_mode: yes + +- assert: + that: + - iam_role is not changed + +- name: "Add Tag (no change)" + iam_role: + name: "{{ test_role }}" + tags: + TagA: ValueA + register: iam_role + +- assert: + that: + - iam_role is not changed + - iam_role.iam_role.role_name == test_role + - '"TagA" in iam_role.iam_role.tags' + - iam_role.iam_role.tags.TagA == "ValueA" + +- name: "iam_role_info after adding Tags" + iam_role_info: + name: "{{ test_role }}" + register: role_info + +- assert: + that: + - role_info is succeeded + - role_info.iam_roles | length == 1 + - 'role_info.iam_roles[0].arn.startswith("arn")' + - 'role_info.iam_roles[0].arn.endswith("role/" + test_role )' + - '"assume_role_policy_document" in role_info.iam_roles[0]' + - '"create_date" in role_info.iam_roles[0]' + - 'role_info.iam_roles[0].description == "Ansible Test Role (updated) {{ resource_prefix }}"' + - role_info.iam_roles[0].inline_policies | length == 0 + - role_info.iam_roles[0].instance_profiles | length == 1 + - role_info.iam_roles[0].instance_profiles[0].instance_profile_name == test_role + - 'role_info.iam_roles[0].instance_profiles[0].arn.startswith("arn")' + - 'role_info.iam_roles[0].instance_profiles[0].arn.endswith("instance-profile/" + test_role)' + - role_info.iam_roles[0].managed_policies | length == 0 + - role_info.iam_roles[0].max_session_duration == 43200 + - role_info.iam_roles[0].path == '/' + - '"permissions_boundary" not in role_info.iam_roles[0]' + - role_info.iam_roles[0].role_id == iam_role.iam_role.role_id + - role_info.iam_roles[0].role_name == test_role + - role_info.iam_roles[0].tags | length == 1 + - '"TagA" in role_info.iam_roles[0].tags' + - role_info.iam_roles[0].tags.TagA == "ValueA" + +# ------------------------------------------------------------------------------------------ + +- name: "Update Tag (CHECK MODE)" + iam_role: + name: "{{ test_role }}" + tags: + TagA: AValue + check_mode: yes + register: iam_role + +- assert: + that: + - iam_role is changed + +- name: "Update Tag" + iam_role: + name: "{{ test_role }}" + tags: + TagA: AValue + register: iam_role + +- assert: + that: + - iam_role is changed + - iam_role.iam_role.role_name == test_role + - '"TagA" in iam_role.iam_role.tags' + - iam_role.iam_role.tags.TagA == "AValue" + +- name: "Update Tag (no change) - check mode" + iam_role: + name: "{{ test_role }}" + tags: + TagA: AValue + register: iam_role + check_mode: yes + +- assert: + that: + - iam_role is not changed + +- name: "Update Tag (no change)" + iam_role: + name: "{{ test_role }}" + tags: + TagA: AValue + register: iam_role + +- assert: + that: + - iam_role is not changed + - iam_role.iam_role.role_name == test_role + - '"TagA" in iam_role.iam_role.tags' + - iam_role.iam_role.tags.TagA == "AValue" + +- name: "iam_role_info after updating Tag" + iam_role_info: + name: "{{ test_role }}" + register: role_info + +- assert: + that: + - role_info is succeeded + - role_info.iam_roles | length == 1 + - 'role_info.iam_roles[0].arn.startswith("arn")' + - 'role_info.iam_roles[0].arn.endswith("role/" + test_role )' + - '"assume_role_policy_document" in role_info.iam_roles[0]' + - '"create_date" in role_info.iam_roles[0]' + - 'role_info.iam_roles[0].description == "Ansible Test Role (updated) {{ resource_prefix }}"' + - role_info.iam_roles[0].inline_policies | length == 0 + - role_info.iam_roles[0].instance_profiles | length == 1 + - role_info.iam_roles[0].instance_profiles[0].instance_profile_name == test_role + - 'role_info.iam_roles[0].instance_profiles[0].arn.startswith("arn")' + - 'role_info.iam_roles[0].instance_profiles[0].arn.endswith("instance-profile/" + test_role)' + - role_info.iam_roles[0].managed_policies | length == 0 + - role_info.iam_roles[0].max_session_duration == 43200 + - role_info.iam_roles[0].path == '/' + - '"permissions_boundary" not in role_info.iam_roles[0]' + - role_info.iam_roles[0].role_id == iam_role.iam_role.role_id + - role_info.iam_roles[0].role_name == test_role + - role_info.iam_roles[0].tags | length == 1 + - '"TagA" in role_info.iam_roles[0].tags' + - role_info.iam_roles[0].tags.TagA == "AValue" + +# ------------------------------------------------------------------------------------------ + +- name: "Add second Tag without purge (CHECK MODE)" + iam_role: + name: "{{ test_role }}" + purge_tags: no + tags: + TagB: ValueB + check_mode: yes + register: iam_role + +- assert: + that: + - iam_role is changed + +- name: "Add second Tag without purge" + iam_role: + name: "{{ test_role }}" + purge_tags: no + tags: + TagB: ValueB + register: iam_role + +- assert: + that: + - iam_role is changed + - iam_role.iam_role.role_name == test_role + - '"TagB" in iam_role.iam_role.tags' + - iam_role.iam_role.tags.TagB == "ValueB" + +- name: "Add second Tag without purge (no change) - check mode" + iam_role: + name: "{{ test_role }}" + purge_tags: no + tags: + TagB: ValueB + register: iam_role + check_mode: yes + +- assert: + that: + - iam_role is not changed + +- name: "Add second Tag without purge (no change)" + iam_role: + name: "{{ test_role }}" + purge_tags: no + tags: + TagB: ValueB + register: iam_role + +- assert: + that: + - iam_role is not changed + - iam_role.iam_role.role_name == test_role + - '"TagB" in iam_role.iam_role.tags' + - iam_role.iam_role.tags.TagB == "ValueB" + +- name: "iam_role_info after adding second Tag without purge" + iam_role_info: + name: "{{ test_role }}" + register: role_info + +- assert: + that: + - role_info is succeeded + - role_info.iam_roles | length == 1 + - 'role_info.iam_roles[0].arn.startswith("arn")' + - 'role_info.iam_roles[0].arn.endswith("role/" + test_role )' + - '"assume_role_policy_document" in role_info.iam_roles[0]' + - '"create_date" in role_info.iam_roles[0]' + - 'role_info.iam_roles[0].description == "Ansible Test Role (updated) {{ resource_prefix }}"' + - role_info.iam_roles[0].inline_policies | length == 0 + - role_info.iam_roles[0].instance_profiles | length == 1 + - role_info.iam_roles[0].instance_profiles[0].instance_profile_name == test_role + - 'role_info.iam_roles[0].instance_profiles[0].arn.startswith("arn")' + - 'role_info.iam_roles[0].instance_profiles[0].arn.endswith("instance-profile/" + test_role)' + - role_info.iam_roles[0].managed_policies | length == 0 + - role_info.iam_roles[0].max_session_duration == 43200 + - role_info.iam_roles[0].path == '/' + - '"permissions_boundary" not in role_info.iam_roles[0]' + - role_info.iam_roles[0].role_id == iam_role.iam_role.role_id + - role_info.iam_roles[0].role_name == test_role + - role_info.iam_roles[0].tags | length == 2 + - '"TagA" in role_info.iam_roles[0].tags' + - role_info.iam_roles[0].tags.TagA == "AValue" + - '"TagB" in role_info.iam_roles[0].tags' + - role_info.iam_roles[0].tags.TagB == "ValueB" + +# ------------------------------------------------------------------------------------------ + +- name: "Purge first tag (CHECK MODE)" + iam_role: + name: "{{ test_role }}" + purge_tags: yes + tags: + TagB: ValueB + check_mode: yes + register: iam_role + +- assert: + that: + - iam_role is changed + +- name: "Purge first tag" + iam_role: + name: "{{ test_role }}" + purge_tags: yes + tags: + TagB: ValueB + register: iam_role + +- assert: + that: + - iam_role is changed + - iam_role.iam_role.role_name == test_role + - '"TagB" in iam_role.iam_role.tags' + - iam_role.iam_role.tags.TagB == "ValueB" + +- name: "Purge first tag (no change) - check mode" + iam_role: + name: "{{ test_role }}" + purge_tags: yes + tags: + TagB: ValueB + register: iam_role + +- assert: + that: + - iam_role is not changed + +- name: "Purge first tag (no change)" + iam_role: + name: "{{ test_role }}" + purge_tags: yes + tags: + TagB: ValueB + register: iam_role + +- assert: + that: + - iam_role is not changed + - iam_role.iam_role.role_name == test_role + - '"TagB" in iam_role.iam_role.tags' + - iam_role.iam_role.tags.TagB == "ValueB" + +- name: "iam_role_info after purging first Tag" + iam_role_info: + name: "{{ test_role }}" + register: role_info + +- assert: + that: + - role_info is succeeded + - role_info.iam_roles | length == 1 + - 'role_info.iam_roles[0].arn.startswith("arn")' + - 'role_info.iam_roles[0].arn.endswith("role/" + test_role )' + - '"assume_role_policy_document" in role_info.iam_roles[0]' + - '"create_date" in role_info.iam_roles[0]' + - 'role_info.iam_roles[0].description == "Ansible Test Role (updated) {{ resource_prefix }}"' + - role_info.iam_roles[0].inline_policies | length == 0 + - role_info.iam_roles[0].instance_profiles | length == 1 + - role_info.iam_roles[0].instance_profiles[0].instance_profile_name == test_role + - 'role_info.iam_roles[0].instance_profiles[0].arn.startswith("arn")' + - 'role_info.iam_roles[0].instance_profiles[0].arn.endswith("instance-profile/" + test_role)' + - role_info.iam_roles[0].managed_policies | length == 0 + - role_info.iam_roles[0].max_session_duration == 43200 + - role_info.iam_roles[0].path == '/' + - '"permissions_boundary" not in role_info.iam_roles[0]' + - role_info.iam_roles[0].role_id == iam_role.iam_role.role_id + - role_info.iam_roles[0].role_name == test_role + - role_info.iam_roles[0].tags | length == 1 + - '"TagA" not in role_info.iam_roles[0].tags' + - '"TagB" in role_info.iam_roles[0].tags' + - role_info.iam_roles[0].tags.TagB == "ValueB" diff --git a/ansible_collections/community/aws/tests/integration/targets/iam_saml_federation/aliases b/ansible_collections/community/aws/tests/integration/targets/iam_saml_federation/aliases new file mode 100644 index 000000000..4ef4b2067 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/iam_saml_federation/aliases @@ -0,0 +1 @@ +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/iam_saml_federation/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/iam_saml_federation/defaults/main.yml new file mode 100644 index 000000000..9dbf108b0 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/iam_saml_federation/defaults/main.yml @@ -0,0 +1,2 @@ +--- +provider_name: 'ansible-test-{{ resource_prefix }}' diff --git a/ansible_collections/community/aws/tests/integration/targets/iam_saml_federation/files/example1.xml b/ansible_collections/community/aws/tests/integration/targets/iam_saml_federation/files/example1.xml new file mode 100644 index 000000000..fa2130a5e --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/iam_saml_federation/files/example1.xml @@ -0,0 +1,22 @@ +<?xml version="1.0"?> +<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" validUntil="2019-08-24T20:37:21Z" cacheDuration="PT1567111041S" entityID="AnsibleSAMLTest1"> + <md:IDPSSODescriptor WantAuthnRequestsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"> + <md:KeyDescriptor use="signing"> + <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"> + <ds:X509Data> + <ds:X509Certificate>MIIDJjCCAg4CCQCiwst2XYH7fTANBgkqhkiG9w0BAQsFADBVMQswCQYDVQQGEwJYWDEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBhbnkgTHRkMREwDwYDVQQDDAhleGFtcGxlMTAeFw0xOTA4MjIyMDM2NTFaFw0yMDA4MjEyMDM2NTFaMFUxCzAJBgNVBAYTAlhYMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxHDAaBgNVBAoME0RlZmF1bHQgQ29tcGFueSBMdGQxETAPBgNVBAMMCGV4YW1wbGUxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArLbBVE6E28bfvB/gUGjOmY2lxxxLZ9Fls4fOH9js/MhGG+hh4diyj/Kb7Coo6HehXMp93TXkYYbiKGAoykT6ULEACZnYi1V9XdUs619ibumi9pRSFygBrbyN+n9peMJxf4jvM1QS/DTPWxdkgeMkqb2SARJChd3azCHd0cdGwcsx1pTkYp34SL0gP79m6W8N3TIxyJmqi0Kc7mntPQUCVH/wFSyg59JXo8SUQDQNap/yd9UwLzxP9MhH8G3DBatwQj3ijYOPnAeUPbsw7GYiKQBh/SIH5DGzW4TNHo0PiQJqzymNp0mI0eKjRO98vfnsXkeQwotzeKVbkmJ63h3PHQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBvm+zYchto1NESDxCVDK96QKObklWrfiAgKDLb49Loox+pyWTvs2mu5DOgDe0rrgEDfxngbbupo9eSu5w7OPVfip8W9rsB8k6ak+P4G8MltqkYv5A0aXbka1da1NenbIXAC3/YbMjLnsidDWQiKYZ0i0HxjuhguW3lvOFd3Dzp2rNDydzA6ilSmBXFrAcKm0RHAfP4NGy3ECdU6SQ5OBSUcJprKADMODIykuds1qh0Gz8a0ukKKmp2yJvz9bIuC4+TRXKKZtgDZKPcN0MgtqYZJ2rttoFqkCWrNBCZSUgJEASUJ78NSC3Wy8WQr3NjZvQ86KG2/mcVQ3Lm1ci82Uue</ds:X509Certificate> + </ds:X509Data> + </ds:KeyInfo> + </md:KeyDescriptor> + <md:KeyDescriptor use="encryption"> + <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"> + <ds:X509Data> + <ds:X509Certificate>MIIDJjCCAg4CCQCiwst2XYH7fTANBgkqhkiG9w0BAQsFADBVMQswCQYDVQQGEwJYWDEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBhbnkgTHRkMREwDwYDVQQDDAhleGFtcGxlMTAeFw0xOTA4MjIyMDM2NTFaFw0yMDA4MjEyMDM2NTFaMFUxCzAJBgNVBAYTAlhYMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxHDAaBgNVBAoME0RlZmF1bHQgQ29tcGFueSBMdGQxETAPBgNVBAMMCGV4YW1wbGUxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArLbBVE6E28bfvB/gUGjOmY2lxxxLZ9Fls4fOH9js/MhGG+hh4diyj/Kb7Coo6HehXMp93TXkYYbiKGAoykT6ULEACZnYi1V9XdUs619ibumi9pRSFygBrbyN+n9peMJxf4jvM1QS/DTPWxdkgeMkqb2SARJChd3azCHd0cdGwcsx1pTkYp34SL0gP79m6W8N3TIxyJmqi0Kc7mntPQUCVH/wFSyg59JXo8SUQDQNap/yd9UwLzxP9MhH8G3DBatwQj3ijYOPnAeUPbsw7GYiKQBh/SIH5DGzW4TNHo0PiQJqzymNp0mI0eKjRO98vfnsXkeQwotzeKVbkmJ63h3PHQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBvm+zYchto1NESDxCVDK96QKObklWrfiAgKDLb49Loox+pyWTvs2mu5DOgDe0rrgEDfxngbbupo9eSu5w7OPVfip8W9rsB8k6ak+P4G8MltqkYv5A0aXbka1da1NenbIXAC3/YbMjLnsidDWQiKYZ0i0HxjuhguW3lvOFd3Dzp2rNDydzA6ilSmBXFrAcKm0RHAfP4NGy3ECdU6SQ5OBSUcJprKADMODIykuds1qh0Gz8a0ukKKmp2yJvz9bIuC4+TRXKKZtgDZKPcN0MgtqYZJ2rttoFqkCWrNBCZSUgJEASUJ78NSC3Wy8WQr3NjZvQ86KG2/mcVQ3Lm1ci82Uue</ds:X509Certificate> + </ds:X509Data> + </ds:KeyInfo> + </md:KeyDescriptor> + <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://example.com/saml/logout"/> + <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat> + <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://example.com/saml/"/> + </md:IDPSSODescriptor> +</md:EntityDescriptor> diff --git a/ansible_collections/community/aws/tests/integration/targets/iam_saml_federation/files/example2.xml b/ansible_collections/community/aws/tests/integration/targets/iam_saml_federation/files/example2.xml new file mode 100644 index 000000000..76a86c7a7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/iam_saml_federation/files/example2.xml @@ -0,0 +1,22 @@ +<?xml version="1.0"?> +<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" validUntil="2019-08-24T20:38:34Z" cacheDuration="PT1567111114S" entityID="AnsibleSAMLTest2"> + <md:IDPSSODescriptor WantAuthnRequestsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"> + <md:KeyDescriptor use="signing"> + <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"> + <ds:X509Data> + <ds:X509Certificate>MIIDADCCAegCCQCgxBiDM2muazANBgkqhkiG9w0BAQsFADBCMQswCQYDVQQGEwJYWDEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBhbnkgTHRkMB4XDTE5MDgyMjIwMzY1OFoXDTIwMDgyMTIwMzY1OFowQjELMAkGA1UEBhMCWFgxFTATBgNVBAcMDERlZmF1bHQgQ2l0eTEcMBoGA1UECgwTRGVmYXVsdCBDb21wYW55IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMUJ3J1tzqoAgQwaJHx/MGl5yVTNpJLPfx8YCS0Z+RQWXIazZrssy/tpZcfgnek4+xvqrzRXR4nell31VTojIGItqR70lPhrsPES70SrN8egi+MLTZ4iddG5hjK4bn4wss88/3johi8/J85wc26/bkRz66lOvTaJ8k1pncQ3NekT9zZzWlW1LQk3uMbaPrVVocjFBEZyTsYUE9wZG+ggRBJlOMGEdhGsgPuR8Aj7OXO7X8/RolV8lB3GTzellX2GxiWnOhjnabSPBUUv5iVKcDOb2lIqxr5DScIvX1PcJSUCAGGLcd8wYK/lh3k+PFH9QNDLY6F5WHkoZq9LS46+8lkCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAWjX7E/BYAHaOKOXc5RAD9zwAaMxLMTSK5Cnq32TGIh1P4ap8jTNVaiCs9UJXHJpKwXUN+3DdVBIGMT17DzFwAeruZOzNBN0VJVl0yZ6dARgss4gpOBGvBD8blLidnVxEd5VRGldx5R5+I441ms6ASkohcHhGlF4WGbnabEZ/MtxhDIWUX2w4naOfFg6vOiPsE1C/ZXJeLDNP+dnjfueTN5DD38d+ND2mHweB7u0Qjpkd2K0TuCp0z4kXRuTgPzlfkPORNkgyU1hA3YClpT57aeUsHgO23sr/4d04jzI+hYeleGqjNM+3vDQYsOQyXx61/nANeF0Sp9ZIv3eJSTMXNw==</ds:X509Certificate> + </ds:X509Data> + </ds:KeyInfo> + </md:KeyDescriptor> + <md:KeyDescriptor use="encryption"> + <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"> + <ds:X509Data> + <ds:X509Certificate>MIIDADCCAegCCQCgxBiDM2muazANBgkqhkiG9w0BAQsFADBCMQswCQYDVQQGEwJYWDEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBhbnkgTHRkMB4XDTE5MDgyMjIwMzY1OFoXDTIwMDgyMTIwMzY1OFowQjELMAkGA1UEBhMCWFgxFTATBgNVBAcMDERlZmF1bHQgQ2l0eTEcMBoGA1UECgwTRGVmYXVsdCBDb21wYW55IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMUJ3J1tzqoAgQwaJHx/MGl5yVTNpJLPfx8YCS0Z+RQWXIazZrssy/tpZcfgnek4+xvqrzRXR4nell31VTojIGItqR70lPhrsPES70SrN8egi+MLTZ4iddG5hjK4bn4wss88/3johi8/J85wc26/bkRz66lOvTaJ8k1pncQ3NekT9zZzWlW1LQk3uMbaPrVVocjFBEZyTsYUE9wZG+ggRBJlOMGEdhGsgPuR8Aj7OXO7X8/RolV8lB3GTzellX2GxiWnOhjnabSPBUUv5iVKcDOb2lIqxr5DScIvX1PcJSUCAGGLcd8wYK/lh3k+PFH9QNDLY6F5WHkoZq9LS46+8lkCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAWjX7E/BYAHaOKOXc5RAD9zwAaMxLMTSK5Cnq32TGIh1P4ap8jTNVaiCs9UJXHJpKwXUN+3DdVBIGMT17DzFwAeruZOzNBN0VJVl0yZ6dARgss4gpOBGvBD8blLidnVxEd5VRGldx5R5+I441ms6ASkohcHhGlF4WGbnabEZ/MtxhDIWUX2w4naOfFg6vOiPsE1C/ZXJeLDNP+dnjfueTN5DD38d+ND2mHweB7u0Qjpkd2K0TuCp0z4kXRuTgPzlfkPORNkgyU1hA3YClpT57aeUsHgO23sr/4d04jzI+hYeleGqjNM+3vDQYsOQyXx61/nANeF0Sp9ZIv3eJSTMXNw==</ds:X509Certificate> + </ds:X509Data> + </ds:KeyInfo> + </md:KeyDescriptor> + <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://example.com/saml/logout"/> + <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat> + <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://example.com/saml/"/> + </md:IDPSSODescriptor> +</md:EntityDescriptor> diff --git a/ansible_collections/community/aws/tests/integration/targets/iam_saml_federation/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/iam_saml_federation/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/iam_saml_federation/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/iam_saml_federation/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/iam_saml_federation/tasks/main.yml new file mode 100644 index 000000000..b061fc601 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/iam_saml_federation/tasks/main.yml @@ -0,0 +1,188 @@ +- module_defaults: + group/aws: + region: "{{ aws_region }}" + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + collections: + - amazon.aws + block: + # ============================================================ + # TESTS + + # Create + + - name: Create the identity provider (check-mode) + iam_saml_federation: + name: '{{ provider_name }}' + state: present + saml_metadata_document: '{{ lookup("file", "example1.xml") }}' + register: create_result + check_mode: yes + - name: assert changed + assert: + that: + - create_result is changed + + - name: Create the identity provider + iam_saml_federation: + name: '{{ provider_name }}' + state: present + saml_metadata_document: '{{ lookup("file", "example1.xml") }}' + register: create_result + - name: assert idp created + assert: + that: + - create_result is changed + - "'saml_provider' in create_result" + - "'arn' in create_result.saml_provider" + - create_result.saml_provider.arn.startswith("arn:aws") + - create_result.saml_provider.arn.endswith(provider_name) + - "'create_date' in create_result.saml_provider" + - "'expire_date' in create_result.saml_provider" + - "'metadata_document' in create_result.saml_provider" + + - name: Test that nothing changes when we retry (check_mode) + iam_saml_federation: + name: '{{ provider_name }}' + state: present + saml_metadata_document: '{{ lookup("file", "example1.xml") }}' + register: create_result + check_mode: yes + - name: assert the idp doesn't change when we retry + assert: + that: + - create_result is not changed + + - name: Test that nothing changes when we retry + iam_saml_federation: + name: '{{ provider_name }}' + state: present + saml_metadata_document: '{{ lookup("file", "example1.xml") }}' + register: create_result + - name: assert the idp doesn't change when we retry + assert: + that: + - create_result is not changed + - "'saml_provider' in create_result" + - "'arn' in create_result.saml_provider" + - create_result.saml_provider.arn.startswith("arn:aws") + - create_result.saml_provider.arn.endswith(provider_name) + - "'create_date' in create_result.saml_provider" + - "'expire_date' in create_result.saml_provider" + - "'metadata_document' in create_result.saml_provider" + + # Update + + - name: Change the identity provider (check_mode) + iam_saml_federation: + name: '{{ provider_name }}' + state: present + saml_metadata_document: '{{ lookup("file", "example2.xml") }}' + register: change_result + check_mode: yes + - name: assert idp created + assert: + that: + - change_result is changed + + - name: Change the identity provider + iam_saml_federation: + name: '{{ provider_name }}' + state: present + saml_metadata_document: '{{ lookup("file", "example2.xml") }}' + register: change_result + - name: assert idp created + assert: + that: + - change_result is changed + - "'saml_provider' in create_result" + - "'arn' in create_result.saml_provider" + - change_result.saml_provider.arn.startswith("arn:aws") + - change_result.saml_provider.arn.endswith(provider_name) + - "'create_date' in create_result.saml_provider" + - "'expire_date' in create_result.saml_provider" + - "'metadata_document' in create_result.saml_provider" + + - name: Test that nothing changes when we retry (check_mode) + iam_saml_federation: + name: '{{ provider_name }}' + state: present + saml_metadata_document: '{{ lookup("file", "example2.xml") }}' + register: change_result + check_mode: yes + - name: assert the idp doesn't change when we retry + assert: + that: + - change_result is not changed + + - name: Test that nothing changes when we retry + iam_saml_federation: + name: '{{ provider_name }}' + state: present + saml_metadata_document: '{{ lookup("file", "example2.xml") }}' + register: change_result + - name: assert the idp doesn't change when we retry + assert: + that: + - change_result is not changed + - "'saml_provider' in create_result" + - "'arn' in create_result.saml_provider" + - change_result.saml_provider.arn.startswith("arn:aws") + - change_result.saml_provider.arn.endswith(provider_name) + - "'create_date' in create_result.saml_provider" + - "'expire_date' in create_result.saml_provider" + - "'metadata_document' in create_result.saml_provider" + + # delete + + - name: Delete the identity provider (check_mode) + iam_saml_federation: + name: '{{ provider_name }}' + state: absent + register: destroy_result + check_mode: yes + - name: assert changed + assert: + that: + - destroy_result is changed + + - name: Delete the identity provider + iam_saml_federation: + name: '{{ provider_name }}' + state: absent + register: destroy_result + - name: assert deleted + assert: + that: + - destroy_result is changed + + - name: Attempt to re-delete the identity provider (check_mode) + iam_saml_federation: + name: '{{ provider_name }}' + state: absent + register: destroy_result + check_mode: yes + - name: assert deleted + assert: + that: + - destroy_result is not changed + + - name: Attempt to re-delete the identity provider + iam_saml_federation: + name: '{{ provider_name }}' + state: absent + register: destroy_result + - name: assert deleted + assert: + that: + - destroy_result is not changed + + always: + # ============================================================ + # CLEAN-UP + - name: finish off by deleting the identity provider + iam_saml_federation: + name: '{{ provider_name }}' + state: absent + register: destroy_result diff --git a/ansible_collections/community/aws/tests/integration/targets/iam_server_certificate/aliases b/ansible_collections/community/aws/tests/integration/targets/iam_server_certificate/aliases new file mode 100644 index 000000000..98e604ec4 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/iam_server_certificate/aliases @@ -0,0 +1,3 @@ +cloud/aws + +iam_server_certificate_info diff --git a/ansible_collections/community/aws/tests/integration/targets/iam_server_certificate/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/iam_server_certificate/defaults/main.yml new file mode 100644 index 000000000..1f136642a --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/iam_server_certificate/defaults/main.yml @@ -0,0 +1,2 @@ +--- +cert_name: 'ansible-test-{{ tiny_prefix }}' diff --git a/ansible_collections/community/aws/tests/integration/targets/iam_server_certificate/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/iam_server_certificate/meta/main.yml new file mode 100644 index 000000000..1810d4bec --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/iam_server_certificate/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - setup_remote_tmp_dir diff --git a/ansible_collections/community/aws/tests/integration/targets/iam_server_certificate/tasks/generate-certs.yml b/ansible_collections/community/aws/tests/integration/targets/iam_server_certificate/tasks/generate-certs.yml new file mode 100644 index 000000000..02d2dac73 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/iam_server_certificate/tasks/generate-certs.yml @@ -0,0 +1,65 @@ +################################################ +# Setup SSL certs to store in IAM +################################################ +- name: 'Generate SSL Keys' + community.crypto.openssl_privatekey: + path: '{{ remote_tmp_dir }}/{{ item }}-key.pem' + size: 2048 + loop: + - 'ca' + - 'cert1' + - 'cert2' + +- name: 'Generate CSRs' + community.crypto.openssl_csr: + path: '{{ remote_tmp_dir }}/{{ item }}.csr' + privatekey_path: '{{ remote_tmp_dir }}/{{ item }}-key.pem' + common_name: '{{ item }}.ansible.test' + subject_alt_name: 'DNS:{{ item }}.ansible.test' + basic_constraints: + - 'CA:TRUE' + loop: + - 'ca' + - 'cert1' + - 'cert2' + +- name: 'Self-sign the "root"' + community.crypto.x509_certificate: + provider: selfsigned + path: '{{ remote_tmp_dir }}/ca.pem' + privatekey_path: '{{ remote_tmp_dir }}/ca-key.pem' + csr_path: '{{ remote_tmp_dir }}/ca.csr' + +- name: 'Sign the intermediate cert' + community.crypto.x509_certificate: + provider: ownca + path: '{{ remote_tmp_dir }}/cert1.pem' + csr_path: '{{ remote_tmp_dir }}/cert1.csr' + ownca_path: '{{ remote_tmp_dir }}/ca.pem' + ownca_privatekey_path: '{{ remote_tmp_dir }}/ca-key.pem' + +- name: 'Sign the end-cert' + community.crypto.x509_certificate: + provider: ownca + path: '{{ remote_tmp_dir }}/cert2.pem' + csr_path: '{{ remote_tmp_dir }}/cert2.csr' + ownca_path: '{{ remote_tmp_dir }}/cert1.pem' + ownca_privatekey_path: '{{ remote_tmp_dir }}/cert1-key.pem' + +- name: 'Re-Sign the end-cert' + community.crypto.x509_certificate: + provider: ownca + path: '{{ remote_tmp_dir }}/cert2-new.pem' + csr_path: '{{ remote_tmp_dir }}/cert2.csr' + ownca_path: '{{ remote_tmp_dir }}/cert1.pem' + ownca_privatekey_path: '{{ remote_tmp_dir }}/cert1-key.pem' + +- set_fact: + path_ca_cert: '{{ remote_tmp_dir }}/ca.pem' + path_ca_key: '{{ remote_tmp_dir }}/ca-key.pem' + path_intermediate_cert: '{{ remote_tmp_dir }}/cert1.pem' + path_intermediate_key: '{{ remote_tmp_dir }}/cert1-key.pem' + # Same key, updated cert + path_cert_a: '{{ remote_tmp_dir }}/cert2.pem' + path_cert_b: '{{ remote_tmp_dir }}/cert2-new.pem' + path_cert_key: '{{ remote_tmp_dir }}/cert2-key.pem' diff --git a/ansible_collections/community/aws/tests/integration/targets/iam_server_certificate/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/iam_server_certificate/tasks/main.yml new file mode 100644 index 000000000..0cfab38c8 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/iam_server_certificate/tasks/main.yml @@ -0,0 +1,606 @@ +--- +# iam_server_certificate integration tests +# +# Note: +# +# AWS APIs only support renaming and/or updating +# the *path*. +# +# It is not possible to update the cert/key/chain +# without deleting the ceritifate +# +- module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + block: + ################################################ + + - name: Test with no args + iam_server_certificate: {} + ignore_errors: true + register: no_args + + - assert: + that: + - no_args is failed + - no_args.msg.startswith('missing required arguments') + + ################################################ + + - include_tasks: 'generate-certs.yml' + + ################################################ + + - set_fact: + cert_a_data: '{{ lookup("file", path_cert_a) }}' + cert_b_data: '{{ lookup("file", path_cert_b) }}' + chain_cert_data: '{{ lookup("file", path_intermediate_cert) }}' + + - name: Create Certificate - check_mode + iam_server_certificate: + name: '{{ cert_name }}' + state: present + cert: '{{ cert_a_data }}' + key: '{{ lookup("file", path_cert_key) }}' + register: create_cert + check_mode: true + + - name: check result - Create Certificate - check_mode + assert: + that: + - create_cert is successful + - create_cert is changed + + - name: Create Certificate + iam_server_certificate: + name: '{{ cert_name }}' + state: present + cert: '{{ cert_a_data }}' + key: '{{ lookup("file", path_cert_key) }}' + register: create_cert + + - name: check result - Create Certificate + assert: + that: + - create_cert is successful + - create_cert is changed + - '"arn" in create_cert' + - '"cert_body" in create_cert' + - '"cert_path" in create_cert' + - '"expiration_date" in create_cert' + - '"name" in create_cert' + - '"upload_date" in create_cert' + - create_cert.arn.startswith('arn:aws') + - create_cert.arn.endswith(cert_name) + - create_cert.name == cert_name + - create_cert.cert_path == '/' + - create_cert.cert_body == cert_a_data + + - name: Create Certificate - idempotency - check_mode + iam_server_certificate: + name: '{{ cert_name }}' + state: present + cert: '{{ cert_a_data }}' + key: '{{ lookup("file", path_cert_key) }}' + register: create_cert + check_mode: true + + - name: check result - Create Certificate - idempotency + assert: + that: + - create_cert is successful + - create_cert is not changed + + - name: Create Certificate - idempotency + iam_server_certificate: + name: '{{ cert_name }}' + state: present + cert: '{{ cert_a_data }}' + key: '{{ lookup("file", path_cert_key) }}' + register: create_cert + + - name: check result - Create Certificate - idempotency + assert: + that: + - create_cert is successful + - create_cert is not changed + - '"arn" in create_cert' + - '"cert_body" in create_cert' + - '"cert_path" in create_cert' + - '"expiration_date" in create_cert' + - '"name" in create_cert' + - '"upload_date" in create_cert' + - create_cert.arn.startswith('arn:aws') + - create_cert.arn.endswith(cert_name) + - create_cert.name == cert_name + - create_cert.cert_path == '/' + - create_cert.cert_body == cert_a_data + + ################################################ + + # Module explicitly blocks updating certs + - name: Update Certificate - check_mode + iam_server_certificate: + name: '{{ cert_name }}' + state: present + cert: '{{ cert_b_data }}' + register: update_cert + ignore_errors: true + check_mode: true + + - name: check result - Update Certificate - check_mode + assert: + that: + - update_cert is failed + - '"not supported" in update_cert.msg' + + - name: Update Certificate + iam_server_certificate: + name: '{{ cert_name }}' + state: present + cert: '{{ cert_b_data }}' + register: update_cert + ignore_errors: true + + - name: check result - Update Certificate + assert: + that: + - update_cert is failed + - '"not supported" in update_cert.msg' + + - name: Update Chaining Certificate - check_mode + iam_server_certificate: + name: '{{ cert_name }}' + state: present + cert_chain: '{{ chain_cert_data }}' + register: update_cert + ignore_errors: true + check_mode: true + + - name: check result - Update Chaining Certificate - check_mode + assert: + that: + - update_cert is failed + - '"not supported" in update_cert.msg' + + - name: Update Chaining Certificate + iam_server_certificate: + name: '{{ cert_name }}' + state: present + cert_chain: '{{ chain_cert_data }}' + register: update_cert + ignore_errors: true + + - name: check result - Update Chaining Certificate + assert: + that: + - update_cert is failed + - '"not supported" in update_cert.msg' + + # AWS APIs provide no mechanism for accessing + # any information about the key, and as such + # the module can't tell if a key was updated. + + ################################################ + + - name: Delete certificate - check_mode + iam_server_certificate: + name: '{{ cert_name }}' + state: absent + register: delete_cert + check_mode: true + + - name: check result - Delete certificate - check_mode + assert: + that: + - delete_cert is successful + - delete_cert is changed + + - name: Delete certificate + iam_server_certificate: + name: '{{ cert_name }}' + state: absent + register: delete_cert + + - name: check result - Delete certificate + assert: + that: + - delete_cert is successful + - delete_cert is changed + + - name: Delete certificate - idempotency - check_mode + iam_server_certificate: + name: '{{ cert_name }}' + state: absent + register: delete_cert + check_mode: true + + - name: check result - Delete certificate - check_mode + assert: + that: + - delete_cert is successful + - delete_cert is not changed + + - name: Delete certificate - idempotency + iam_server_certificate: + name: '{{ cert_name }}' + state: absent + register: delete_cert + + - name: check result - Delete certificate + assert: + that: + - delete_cert is successful + - delete_cert is not changed + + ################################################ + + - name: Create Certificate with Chain and path - check_mode + iam_server_certificate: + name: '{{ cert_name }}' + state: present + cert: '{{ cert_a_data }}' + key: '{{ lookup("file", path_cert_key) }}' + cert_chain: '{{ chain_cert_data }}' + path: '/ansible-test-example/' + register: create_cert + check_mode: true + + - name: check result - Create Certificate with Chain and path - check_mode + assert: + that: + - create_cert is successful + - create_cert is changed + + - name: Create Certificate with Chain and path + iam_server_certificate: + name: '{{ cert_name }}' + state: present + cert: '{{ cert_a_data }}' + key: '{{ lookup("file", path_cert_key) }}' + cert_chain: '{{ chain_cert_data }}' + path: '/ansible-test-example/' + register: create_cert + + - name: check result - Create Certificate with Chain and path + assert: + that: + - create_cert is successful + - create_cert is changed + - '"arn" in create_cert' + - '"cert_body" in create_cert' + - '"cert_path" in create_cert' + - '"expiration_date" in create_cert' + - '"name" in create_cert' + - '"upload_date" in create_cert' + - create_cert.arn.startswith('arn:aws') + - create_cert.arn.endswith(cert_name) + - create_cert.name == cert_name + - create_cert.cert_path == '/ansible-test-example/' + - create_cert.cert_body == cert_a_data + + - name: Create Certificate with Chain and path - idempotency - check_mode + iam_server_certificate: + name: '{{ cert_name }}' + state: present + cert: '{{ cert_a_data }}' + key: '{{ lookup("file", path_cert_key) }}' + cert_chain: '{{ chain_cert_data }}' + path: '/ansible-test-example/' + register: create_cert + check_mode: true + + - name: check result - Create Certificate with Chain and path - idempotency - check_mode + assert: + that: + - create_cert is successful + - create_cert is not changed + + - name: Create Certificate with Chain and path - idempotency + iam_server_certificate: + name: '{{ cert_name }}' + state: present + cert: '{{ cert_a_data }}' + key: '{{ lookup("file", path_cert_key) }}' + cert_chain: '{{ chain_cert_data }}' + path: '/ansible-test-example/' + register: create_cert + + - name: check result - Create Certificate with Chain and path - idempotency + assert: + that: + - create_cert is successful + - create_cert is not changed + - '"arn" in create_cert' + - '"cert_body" in create_cert' + - '"cert_path" in create_cert' + - '"expiration_date" in create_cert' + - '"name" in create_cert' + - '"upload_date" in create_cert' + - create_cert.arn.startswith('arn:aws') + - create_cert.arn.endswith(cert_name) + - create_cert.name == cert_name + - create_cert.cert_path == '/ansible-test-example/' + - create_cert.cert_body == cert_a_data + + ################################################ + + - name: Create Certificate with identical cert dup_ok=False - check_mode + iam_server_certificate: + name: '{{ cert_name }}-duplicate' + state: present + cert: '{{ cert_a_data }}' + key: '{{ lookup("file", path_cert_key) }}' + dup_ok: false + register: create_duplicate + ignore_errors: true + + - name: check result - Create Certificate with identical cert - check_mode + assert: + that: + - create_duplicate is failed + + - name: Create Certificate with identical cert dup_ok=False + iam_server_certificate: + name: '{{ cert_name }}-duplicate' + state: present + cert: '{{ cert_a_data }}' + key: '{{ lookup("file", path_cert_key) }}' + dup_ok: false + register: create_duplicate + ignore_errors: true + + - name: check result - Create Certificate with identical cert + assert: + that: + - create_duplicate is failed + + ################################################ + + - name: Create Certificate with forced identical cert - check_mode + iam_server_certificate: + name: '{{ cert_name }}-duplicate' + state: present + cert: '{{ cert_a_data }}' + key: '{{ lookup("file", path_cert_key) }}' + register: create_duplicate + check_mode: true + + - name: check result - Create Certificate with forced identical cert - check_mode + assert: + that: + - create_duplicate is successful + - create_duplicate is changed + + - name: Create Certificate with forced identical cert + iam_server_certificate: + name: '{{ cert_name }}-duplicate' + state: present + cert: '{{ cert_a_data }}' + key: '{{ lookup("file", path_cert_key) }}' + register: create_duplicate + + - name: check result - Create Certificate with forced identical cert + assert: + that: + - create_duplicate is successful + - create_duplicate is changed + - '"arn" in create_duplicate' + - '"cert_body" in create_duplicate' + - '"cert_path" in create_duplicate' + - '"expiration_date" in create_duplicate' + - '"name" in create_duplicate' + - '"upload_date" in create_duplicate' + - create_duplicate.arn.startswith('arn:aws') + - create_duplicate.arn.endswith('-duplicate') + - create_duplicate.name.endswith('duplicate') + - create_duplicate.cert_path == '/' + - create_duplicate.cert_body == cert_a_data + + - name: Create Certificate with forced identical cert - idempotency - check_mode + iam_server_certificate: + name: '{{ cert_name }}-duplicate' + state: present + cert: '{{ cert_a_data }}' + key: '{{ lookup("file", path_cert_key) }}' + register: create_duplicate + check_mode: true + + - name: check result - Create Certificate with forced identical cert - idempotency - check_mode + assert: + that: + - create_duplicate is successful + - create_duplicate is not changed + + - name: Create Certificate with forced identical cert - idempotency + iam_server_certificate: + name: '{{ cert_name }}-duplicate' + state: present + cert: '{{ cert_a_data }}' + key: '{{ lookup("file", path_cert_key) }}' + register: create_duplicate + + - name: check result - Create Certificate with forced identical cert - idempotency + assert: + that: + - create_duplicate is successful + - create_duplicate is not changed + - '"arn" in create_duplicate' + - '"cert_body" in create_duplicate' + - '"cert_path" in create_duplicate' + - '"expiration_date" in create_duplicate' + - '"name" in create_duplicate' + - '"upload_date" in create_duplicate' + - create_duplicate.arn.startswith('arn:aws') + - create_duplicate.arn.endswith('-duplicate') + - create_duplicate.name.endswith('duplicate') + - create_duplicate.cert_path == '/' + - create_duplicate.cert_body == cert_a_data + + ################################################ + + - name: Update certificate path - check_mode + iam_server_certificate: + name: '{{ cert_name }}' + state: present + path: '/ansible-test-example/' + new_path: '/ansible-test-path/' + register: update_path + check_mode: true + + - name: check result - Update certificate path - check_mode + assert: + that: + - update_path is successful + - update_path is changed + + - name: Update certificate path + iam_server_certificate: + name: '{{ cert_name }}' + state: present + path: '/ansible-test-example/' + new_path: '/ansible-test-path/' + register: update_path + + - name: check result - Update certificate path + assert: + that: + - update_path is successful + - update_path is changed + - '"arn" in update_path' + - '"cert_body" in update_path' + - '"cert_path" in update_path' + - '"expiration_date" in update_path' + - '"name" in update_path' + - '"upload_date" in update_path' + - update_path.arn.startswith('arn:aws') + - update_path.arn.endswith(cert_name) + - update_path.name == cert_name + - update_path.cert_path == '/ansible-test-path/' + - update_path.cert_body == cert_a_data + + - name: Update certificate path - idempotency - check_mode + iam_server_certificate: + name: '{{ cert_name }}' + state: present + path: '/ansible-test-example/' + new_path: '/ansible-test-path/' + register: update_path + check_mode: true + + - name: check result - Update certificate path - idempotency - check_mode + assert: + that: + - update_path is successful + - update_path is not changed + + - name: Update certificate path - idempotency + iam_server_certificate: + name: '{{ cert_name }}' + state: present + path: '/ansible-test-example/' + new_path: '/ansible-test-path/' + register: update_path + + - name: check result - Update certificate path - idempotency + assert: + that: + - update_path is successful + - update_path is not changed + + ################################################ + + - name: Update certificate name - check_mode + iam_server_certificate: + name: '{{ cert_name }}' + new_name: '{{ cert_name }}-renamed' + state: present + register: update_name + check_mode: true + + - name: check result - Update certificate name - check_mode + assert: + that: + - update_name is successful + - update_name is changed + + - name: Update certificate name + iam_server_certificate: + name: '{{ cert_name }}' + new_name: '{{ cert_name }}-renamed' + state: present + register: update_name + + - name: check result - Update certificate name + assert: + that: + - update_name is successful + - update_name is changed + - '"arn" in update_name' + - '"cert_body" in update_name' + - '"cert_path" in update_name' + - '"expiration_date" in update_name' + - '"name" in update_name' + - '"upload_date" in update_name' + - update_name.arn.startswith('arn:aws') + - update_name.arn.endswith('-renamed') + - update_name.name.endswith('renamed') + - update_name.cert_path == '/ansible-test-path/' + - update_name.cert_body == cert_a_data + + - name: Update certificate name - idempotency - check_mode + iam_server_certificate: + name: '{{ cert_name }}' + new_name: '{{ cert_name }}-renamed' + state: present + register: update_name + check_mode: true + + - name: check result - Update certificate name - idempotency - check_mode + assert: + that: + - update_name is successful + - update_name is not changed + + - name: Update certificate name - idempotency + iam_server_certificate: + name: '{{ cert_name }}' + new_name: '{{ cert_name }}-renamed' + state: present + register: update_name + + - name: check result - Update certificate name - idempotency + assert: + that: + - update_name is successful + - update_name is not changed + - '"arn" in update_name' + - '"cert_body" in update_name' + - '"cert_path" in update_name' + - '"expiration_date" in update_name' + - '"name" in update_name' + - '"upload_date" in update_name' + - update_name.arn.startswith('arn:aws') + - update_name.arn.endswith('-renamed') + - update_name.name.endswith('renamed') + - update_name.cert_path == '/ansible-test-path/' + - update_name.cert_body == cert_a_data + + always: + + ################################################ + # TEARDOWN STARTS HERE + ################################################ + + - name: Delete certificate + iam_server_certificate: + name: '{{ item }}' + state: absent + ignore_errors: true + loop: + - '{{ cert_name }}' + - '{{ cert_name }}-renamed' + - '{{ cert_name }}-duplicate' diff --git a/ansible_collections/community/aws/tests/integration/targets/inspector_target/aliases b/ansible_collections/community/aws/tests/integration/targets/inspector_target/aliases new file mode 100644 index 000000000..1e8d92ea8 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/inspector_target/aliases @@ -0,0 +1,2 @@ +time=6m +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/inspector_target/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/inspector_target/defaults/main.yml new file mode 100644 index 000000000..8777873f0 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/inspector_target/defaults/main.yml @@ -0,0 +1,3 @@ +--- + +aws_inspector_scan_name: "aws_inspector_scan-{{ ansible_date_time.epoch }}" diff --git a/ansible_collections/community/aws/tests/integration/targets/inspector_target/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/inspector_target/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/inspector_target/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/inspector_target/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/inspector_target/tasks/main.yml new file mode 100644 index 000000000..907e1ffdd --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/inspector_target/tasks/main.yml @@ -0,0 +1,90 @@ +--- +- name: 'aws_inspector_target integration tests' + collections: + - amazon.aws + module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + block: + + - name: Create AWS Inspector Target Group + aws_inspector_target: + name: "{{ aws_inspector_scan_name }}" + state: present + tags: + Name: "{{ aws_inspector_scan_name }}" + changed: "no" + register: target_group_create + + - name: Create AWS Inspector Target Group (Verify) + aws_inspector_target: + name: "{{ aws_inspector_scan_name }}" + state: present + tags: + Name: "{{ aws_inspector_scan_name }}" + changed: "no" + register: target_group_create_verify + + - name: Assert Successful AWS Inspector Target Group Creation + assert: + that: + - target_group_create is changed + - target_group_create.name == aws_inspector_scan_name + - target_group_create.tags.Name == aws_inspector_scan_name + - target_group_create.tags.changed == "no" + - target_group_create_verify is not changed + - target_group_create_verify.name == aws_inspector_scan_name + - target_group_create_verify.tags.Name == aws_inspector_scan_name + - target_group_create_verify.tags.changed == "no" + + - name: Change AWS Inspector Target Group Tags + aws_inspector_target: + name: "{{ aws_inspector_scan_name }}" + state: present + tags: + Name: "{{ aws_inspector_scan_name }}" + changed: "yes" + register: target_group_tag_change + + - name: Change AWS Inspector Target Group Tags (Verify) + aws_inspector_target: + name: "{{ aws_inspector_scan_name }}" + state: present + tags: + Name: "{{ aws_inspector_scan_name }}" + changed: "yes" + register: target_group_tag_change_verify + + - name: Assert Successful AWS Inspector Target Group Tag Change + assert: + that: + - target_group_tag_change is changed + - target_group_tag_change.name == aws_inspector_scan_name + - target_group_tag_change.tags.Name == aws_inspector_scan_name + - target_group_tag_change.tags.changed == "yes" + - target_group_tag_change_verify is not changed + - target_group_tag_change_verify.name == aws_inspector_scan_name + - target_group_tag_change_verify.tags.Name == aws_inspector_scan_name + - target_group_tag_change_verify.tags.changed == "yes" + + always: + - name: Delete AWS Inspector Target Group + aws_inspector_target: + name: "{{ aws_inspector_scan_name }}" + state: absent + register: target_group_delete + + - name: Delete AWS Inspector Target Group (Verify) + aws_inspector_target: + name: "{{ aws_inspector_scan_name }}" + state: absent + register: target_group_delete_verify + + - name: Assert Successful AWS Inspector Target Group Deletion + assert: + that: + - target_group_delete is changed + - target_group_delete_verify is not changed diff --git a/ansible_collections/community/aws/tests/integration/targets/kinesis_stream/aliases b/ansible_collections/community/aws/tests/integration/targets/kinesis_stream/aliases new file mode 100644 index 000000000..eaef9d063 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/kinesis_stream/aliases @@ -0,0 +1,2 @@ +time=11m +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/kinesis_stream/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/kinesis_stream/defaults/main.yml new file mode 100644 index 000000000..fd548e87e --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/kinesis_stream/defaults/main.yml @@ -0,0 +1,19 @@ +--- +run_kms_tests: True +kinesis_stream_name: '{{ resource_prefix }}' + +kms_cmk_alias_1: '{{ resource_prefix }}-1' +kms_cmk_alias_2: '{{ resource_prefix }}-2' + +# A variety of camelCase and PascalCase to test things don't get re-cased +# underneath us +kinesis_stream_tags_1: + tag: value + AnExample: AValue + somethingElse: Another Value + Bleep: bloop +# Adds 2 values, Deletes 2 and keeps a value +kinesis_stream_tags_2: + tag: value + foo: Bar + Baz: quuX diff --git a/ansible_collections/community/aws/tests/integration/targets/kinesis_stream/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/kinesis_stream/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/kinesis_stream/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/kinesis_stream/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/kinesis_stream/tasks/main.yml new file mode 100644 index 000000000..b6791fb06 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/kinesis_stream/tasks/main.yml @@ -0,0 +1,688 @@ +--- +# ============================================================ +- name: 'Setup AWS Module Defaults' + module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + kinesis_stream: + # Number of shards is mandatory when state=present + shards: 1 + + block: + # ============================================================ + # Set up some additional resources for later user + + - name: 'KMS test preperation - only run when explicitly enabled' + when: + - run_kms_tests | default(False) | bool + block: + # KMS Keys + # Note: Because we're not a producer / consumer we don't actually need + # access to the keys + - name: 'Create KMS key 1' + aws_kms: + alias: '{{ kms_cmk_alias_1 }}' + state: present + enabled: yes + register: create_kms_1 + - name: 'Create KMS key 2' + aws_kms: + alias: '{{ kms_cmk_alias_2 }}' + state: present + enabled: yes + register: create_kms_2 + - name: 'Assert that we sucessfully created our keys' + assert: + that: + - create_kms_1 is success + - create_kms_2 is success + - name: 'Store the Key IDs for later' + set_fact: + kms_cmk_id_1: '{{ create_kms_1.key_id }}' + kms_cmk_arn_1: '{{ create_kms_1.key_arn }}' + kms_cmk_id_2: '{{ create_kms_2.key_id }}' + kms_cmk_arn_2: '{{ create_kms_2.key_arn }}' + # All of the valid ways to describe the CMK + kms_cmk_1: + - '{{ create_kms_1.key_id }}' + - 'alias/{{ kms_cmk_alias_1 }}' + - '{{ create_kms_1.key_arn }}' + kms_cmk_2: + - '{{ create_kms_2.key_id }}' + - 'alias/{{ kms_cmk_alias_2 }}' + - '{{ create_kms_2.key_arn }}' + + # ============================================================ + # Basic creation + - name: 'Create a basic Kinesis stream (CHECK_MODE)' + check_mode: yes + kinesis_stream: + name: '{{ kinesis_stream_name }}' + register: result + - name: 'Assert state is changed when first creating a stream (CHECK_MODE)' + assert: + that: + - result is success + - result is changed + + - name: 'Create a basic Kinesis stream' + kinesis_stream: + name: '{{ kinesis_stream_name }}' + register: result + - name: 'Assert state is changed when first creating a stream' + assert: + that: + - result is success + - result is changed + - result.encryption_type == 'NONE' + - result.open_shards_count == 1 + - result.retention_period_hours == 24 + - result.stream_arn.startswith('arn:aws:kinesis:') + - result.stream_arn.endswith(':stream/' + kinesis_stream_name) + - result.stream_name == kinesis_stream_name + - result.stream_status == 'ACTIVE' + - result.tags == {} + + # We've run tests that the ARN matches the pattern we expect, we can just test + # it doesn't change. + - name: 'Save Stream ARN for later comparison' + set_fact: + kinesis_stream_arn: '{{ result.stream_arn }}' + + - name: 'Create a basic Kinesis stream - Idempotency (CHECK_MODE)' + check_mode: yes + kinesis_stream: + name: '{{ kinesis_stream_name }}' + register: result + - name: 'Assert state is not changed when re-running the create (CHECK_MODE)' + assert: + that: + - result is success + - result is not changed + + - name: 'Create a basic Kinesis stream - Idempotency' + kinesis_stream: + name: '{{ kinesis_stream_name }}' + register: result + - name: 'Assert state is not changed when re-running the create' + assert: + that: + - result is success + - result is not changed + - result.encryption_type == 'NONE' + - result.open_shards_count == 1 + - result.retention_period_hours == 24 + - result.stream_arn == kinesis_stream_arn + - result.stream_name == kinesis_stream_name + - result.stream_status == 'ACTIVE' + - result.tags == {} + + # ============================================================ + # Retention Period + # + - name: 'Increase the retention period (CHECK_MODE)' + check_mode: yes + kinesis_stream: + name: '{{ kinesis_stream_name }}' + retention_period: 72 + register: result + - name: 'Assert state is changed when changing the retention period (CHECK_MODE)' + assert: + that: + - result is success + - result is changed + + - name: 'Increase the retention period' + kinesis_stream: + name: '{{ kinesis_stream_name }}' + retention_period: 72 + register: result + - name: 'Assert state is changed when changing the retention period' + assert: + that: + - result is success + - result is changed + - result.encryption_type == 'NONE' + - result.open_shards_count == 1 + - result.retention_period_hours == 72 + - result.stream_arn == kinesis_stream_arn + - result.stream_name == kinesis_stream_name + - result.stream_status == 'ACTIVE' + - result.tags == {} + + - name: 'Increase the retention period - Idempotency (CHECK_MODE)' + check_mode: yes + kinesis_stream: + name: '{{ kinesis_stream_name }}' + retention_period: 72 + register: result + - name: 'Assert state is not changed when not changing the retention period (CHECK_MODE)' + assert: + that: + - result is success + - result is not changed + + - name: 'Increase the retention period - Idempotency' + kinesis_stream: + name: '{{ kinesis_stream_name }}' + retention_period: 72 + register: result + - name: 'Assert state is not changed when not changing the retention period' + assert: + that: + - result is success + - result is not changed + - result.encryption_type == 'NONE' + - result.open_shards_count == 1 + - result.retention_period_hours == 72 + - result.stream_arn == kinesis_stream_arn + - result.stream_name == kinesis_stream_name + - result.stream_status == 'ACTIVE' + - result.tags == {} + + - name: 'Decrease the retention period (CHECK_MODE)' + check_mode: yes + kinesis_stream: + name: '{{ kinesis_stream_name }}' + retention_period: 48 + register: result + - name: 'Assert state is changed when changing the retention period (CHECK_MODE)' + assert: + that: + - result is success + - result is changed + + - name: 'Decrease the retention period' + kinesis_stream: + name: '{{ kinesis_stream_name }}' + retention_period: 48 + register: result + - name: 'Assert state is changed when changing the retention period' + assert: + that: + - result is success + - result is changed + - result.encryption_type == 'NONE' + - result.open_shards_count == 1 + - result.retention_period_hours == 48 + - result.stream_arn == kinesis_stream_arn + - result.stream_name == kinesis_stream_name + - result.stream_status == 'ACTIVE' + - result.tags == {} + + - name: 'Decrease the retention period - Idempotency (CHECK_MODE)' + check_mode: yes + kinesis_stream: + name: '{{ kinesis_stream_name }}' + retention_period: 48 + register: result + - name: 'Assert state is not changed when not changing the retention period (CHECK_MODE)' + assert: + that: + - result is success + - result is not changed + + - name: 'Decrease the retention period - Idempotency' + kinesis_stream: + name: '{{ kinesis_stream_name }}' + retention_period: 48 + register: result + - name: 'Assert state is not changed when not changing the retention period' + assert: + that: + - result is success + - result is not changed + - result.encryption_type == 'NONE' + - result.open_shards_count == 1 + - result.retention_period_hours == 48 + + # ============================================================ + # Basic tagging + + - name: 'Set some tags (CHECK_MODE)' + check_mode: yes + kinesis_stream: + name: '{{ kinesis_stream_name }}' + tags: '{{ kinesis_stream_tags_1 }}' + register: result + - name: 'Assert state is changed when adding tags (CHECK_MODE)' + assert: + that: + - result is success + - result is changed + + - name: 'Set some tags' + kinesis_stream: + name: '{{ kinesis_stream_name }}' + tags: '{{ kinesis_stream_tags_1 }}' + register: result + - name: 'Assert state is changed when adding tags' + assert: + that: + - result is success + - result.encryption_type == 'NONE' + - result.open_shards_count == 1 + - result.retention_period_hours == 48 + - result.stream_arn == kinesis_stream_arn + - result.stream_name == kinesis_stream_name + - result.stream_status == 'ACTIVE' + - name: 'Assert tags return as expected' + assert: + that: + - result is changed + - result.tags == kinesis_stream_tags_1 + + - name: 'Set some tags - Idempotency (CHECK_MODE)' + check_mode: yes + kinesis_stream: + name: '{{ kinesis_stream_name }}' + tags: '{{ kinesis_stream_tags_1 }}' + register: result + - name: 'Assert state is not changed when not changing the tags (CHECK_MODE)' + assert: + that: + - result is success + - result is not changed + + - name: 'Set some tags - Idempotency' + kinesis_stream: + name: '{{ kinesis_stream_name }}' + tags: '{{ kinesis_stream_tags_1 }}' + register: result + - name: 'Assert state is not changed when not changing the tags' + assert: + that: + - result is success + - result is not changed + - result.encryption_type == 'NONE' + - result.open_shards_count == 1 + - result.retention_period_hours == 48 + - result.stream_arn == kinesis_stream_arn + - result.stream_name == kinesis_stream_name + - result.stream_status == 'ACTIVE' + # Merge this into the main assertion when the return values are no longer + # snake_cased + - name: 'Assert tags return as expected' + assert: + that: + - result.tags == kinesis_stream_tags_1 + # XXX BUG (Tag_snake) + ignore_errors: yes + + - name: 'Change some tags (CHECK_MODE)' + check_mode: yes + kinesis_stream: + name: '{{ kinesis_stream_name }}' + tags: '{{ kinesis_stream_tags_2 }}' + register: result + - name: 'Assert state is changed when changing tags (CHECK_MODE)' + assert: + that: + - result is success + - result is changed + + - name: 'Change some tags' + kinesis_stream: + name: '{{ kinesis_stream_name }}' + tags: '{{ kinesis_stream_tags_2 }}' + register: result + - name: 'Assert state is changed when changing tags' + assert: + that: + - result is success + - result.encryption_type == 'NONE' + - result.open_shards_count == 1 + - result.retention_period_hours == 48 + - result.stream_arn == kinesis_stream_arn + - result.stream_name == kinesis_stream_name + - result.stream_status == 'ACTIVE' + # Merge this into the main assertion when the return values are no longer + # snake_cased + - name: 'Assert tags return as expected (tags2)' + assert: + that: + - result is changed + - result.tags == kinesis_stream_tags_2 + # XXX BUG (Tag_changed) (Tag_snake) + ignore_errors: yes + + - name: 'Change some tags - Idempotency (CHECK_MODE)' + check_mode: yes + kinesis_stream: + name: '{{ kinesis_stream_name }}' + tags: '{{ kinesis_stream_tags_2 }}' + register: result + - name: 'Assert state is not changed when not changing the tags (CHECK_MODE)' + assert: + that: + - result is success + - result is not changed + + - name: 'Change some tags - Idempotency' + kinesis_stream: + name: '{{ kinesis_stream_name }}' + tags: '{{ kinesis_stream_tags_2 }}' + register: result + - name: 'Assert state is not changed when not changing the tags' + assert: + that: + - result is success + - result is not changed + - result.encryption_type == 'NONE' + - result.open_shards_count == 1 + - result.retention_period_hours == 48 + - result.stream_arn == kinesis_stream_arn + - result.stream_name == kinesis_stream_name + - result.stream_status == 'ACTIVE' + # Merge this into the main assertion when the return values are no longer + # snake_cased + - name: 'Assert tags return as expected (tags2)' + assert: + that: + - result.tags == kinesis_stream_tags_2 + # XXX BUG (Tag_snake) + ignore_errors: yes + + # ============================================================ + # Number of shards + # + - name: 'Change the number of shards (CHECK_MODE)' + check_mode: yes + kinesis_stream: + name: '{{ kinesis_stream_name }}' + shards: 2 + register: result + - name: 'Assert state is changed when changing the number of shards (CHECK_MODE)' + assert: + that: + - result is success + - result is changed + + - name: 'Change the number of shards' + kinesis_stream: + name: '{{ kinesis_stream_name }}' + shards: 2 + register: result + - name: 'Assert state is changed when changing the number of shards' + assert: + that: + - result is success + - result is changed + - result.encryption_type == 'NONE' + - result.open_shards_count == 2 + - result.retention_period_hours == 48 + - result.stream_arn == kinesis_stream_arn + - result.stream_name == kinesis_stream_name + - result.stream_status == 'ACTIVE' + #- result.tags == kinesis_stream_tags_2 + # Merge this into the main assertion when the tag keys are no longer + # snake_cased + - name: 'Assert tags return as expected (tags2)' + assert: + that: + - result.tags == kinesis_stream_tags_2 + # XXX BUG (Tag_snake) + ignore_errors: yes + + - name: 'Change the number of shards - Idempotency (CHECK_MODE)' + check_mode: yes + kinesis_stream: + name: '{{ kinesis_stream_name }}' + shards: 2 + register: result + - name: 'Assert state is not changed when not changing the number of shards (CHECK_MODE)' + assert: + that: + - result is success + - result is not changed + + - name: 'Change the number of shards - Idempotency' + kinesis_stream: + name: '{{ kinesis_stream_name }}' + shards: 2 + register: result + - name: 'Assert state is not changed when not changing the number of shards' + assert: + that: + - result is success + - result is not changed + - result.encryption_type == 'NONE' + - result.open_shards_count == 2 + - result.retention_period_hours == 48 + - result.stream_arn == kinesis_stream_arn + - result.stream_name == kinesis_stream_name + - result.stream_status == 'ACTIVE' + #- result.tags == kinesis_stream_tags_2 + # Merge this into the main assertion when the tag keys are no longer + # snake_cased + - name: 'Assert tags return as expected (tags2)' + assert: + that: + - result.tags == kinesis_stream_tags_2 + # XXX BUG (Tag_snake) + ignore_errors: yes + + # ============================================================ + # Shards has to be passed we can't test that it's not updated when we're not + # setting it. Let's reset it to the value we set in the module_defaults + + - name: 'Reset the number of shards' + kinesis_stream: + name: '{{ kinesis_stream_name }}' + register: result + - name: 'Assert the change was successful' + assert: + that: + - result is success + - result is changed + - result.open_shards_count == 1 + + # DISABLED BY DEFAULT - KMS key creation/deletion not supported in CI at this time + - name: 'KMS tests - only run when explicitly enabled' + when: + - run_kms_tests | default(False) | bool + block: + # ============================================================ + # Encryption + - name: 'Test encryption' + vars: + key_type: '{{ item.type }}' + kinesis_key: '{{ item.key }}' + kinesis_key_id: '{{ kms_cmk_id_1 }}' + kinesis_key_alias: 'alias/{{ kms_cmk_alias_1 }}' + kinesis_key_arn: '{{ kms_cmk_arn_1 }}' + include_tasks: 'test_encryption.yml' + # Loop through and test the management and idempotency when using the + # various combinations of ID, alias and ARN of a CMK + loop: + - type: 'ID' + key: '{{ kms_cmk_id_1 }}' + - type: 'Alias' + key: 'alias/{{ kms_cmk_alias_1 }}' + - type: 'ARN' + key: '{{ kms_cmk_arn_1 }}' + + - name: 'Disable encryption - Idempotency (CHECK_MODE)' + kinesis_stream: + name: '{{ kinesis_stream_name }}' + encryption_state: 'disabled' + register: result + - name: 'Assert state is not changed when encryption_state not changed (CHECK_MODE)' + ignore_errors: yes + assert: + that: + - result is success + - result is not changed + # XXX BUG (Enc_idemp) + + - name: 'Disable encryption - Idempotency' + kinesis_stream: + name: '{{ kinesis_stream_name }}' + encryption_state: 'disabled' + register: result + - name: 'Assert state is not changed when encryption_state not changed (CHECK_MODE)' + assert: + that: + - result is success + - result is not changed + # Merge this into the main assertion when the main return keys are + # snake_cased + - name: 'Assert expected return values' + assert: + that: + - result.encryption_type == 'NONE' + - result.open_shards_count == 1 + - result.retention_period_hours == 48 + - result.stream_arn == kinesis_stream_arn + - result.stream_name == kinesis_stream_name + - result.stream_status == 'ACTIVE' + #- result.tags == kinesis_stream_tags_2 + # XXX BUG (Enc_snake) + ignore_errors: yes + # Merge this into the main assertion when the tag keys are no longer + # snake_cased + - name: 'Assert tags return as expected (tags2)' + assert: + that: + - result.tags == kinesis_stream_tags_2 + # XXX BUG (Tag_snake) + ignore_errors: yes + + - name: 'Enable encryption' + kinesis_stream: + name: '{{ kinesis_stream_name }}' + encryption_state: 'enabled' + encryption_type: 'KMS' + key_id: '{{ kms_cmk_id_1 }}' + register: result + - name: 'Assert that state is changed when enabling encryption' + assert: + that: + - result is success + - result is changed + + - name: 'Test encryption changed state when updating key (CHECK_MODE)' + check_mode: yes + kinesis_stream: + name: '{{ kinesis_stream_name }}' + encryption_state: 'enabled' + encryption_type: 'KMS' + key_id: '{{ kms_cmk_id_2 }}' + register: result + - name: 'Assert state is changed when stream encryption key is changed (CHECK_MODE)' + assert: + that: + - result is success + - result is changed + + - name: 'Test encryption changed state when updating key' + kinesis_stream: + name: '{{ kinesis_stream_name }}' + encryption_state: 'enabled' + encryption_type: 'KMS' + key_id: '{{ kms_cmk_id_2 }}' + register: result + - name: 'Assert state is changed when stream encryption key is changed' + assert: + that: + - result is success + - result is changed + # Merge this into the main assertion when the main return keys are + # snake_cased + - name: 'Assert expected return values' + assert: + that: + - result.encryption_type == 'KMS' + - result.key_id in kms_cmk_2 + - result.open_shards_count == 1 + - result.retention_period_hours == 48 + - result.stream_arn == kinesis_stream_arn + - result.stream_name == kinesis_stream_name + - result.stream_status == 'ACTIVE' + #- result.tags == kinesis_stream_tags_2 + # XXX BUG (Enc_snake) + ignore_errors: yes + # Merge this into the main assertion when the tag keys are no longer + # snake_cased + - name: 'Assert tags return as expected (tags2)' + assert: + that: + - result.tags == kinesis_stream_tags_2 + # XXX BUG (Tag_snake) + ignore_errors: yes + + # ============================================================ + + - name: 'Delete stream (CHECK_MODE)' + check_mode: yes + module_defaults: { kinesis_stream: {} } + kinesis_stream: + name: '{{ kinesis_stream_name }}' + state: absent + register: result + - name: 'Assert state is changed when deleting a stream (CHECK_MODE)' + assert: + that: + - result is success + - result is changed + + - name: 'Delete stream' + module_defaults: { kinesis_stream: {} } + kinesis_stream: + name: '{{ kinesis_stream_name }}' + state: absent + register: result + - name: 'Assert state is changed when deleting a stream' + assert: + that: + - result is success + - result is changed + + - name: 'Delete stream - Idempotency (CHECK_MODE)' + check_mode: yes + module_defaults: { kinesis_stream: {} } + kinesis_stream: + name: '{{ kinesis_stream_name }}' + state: absent + register: result + - name: 'Assert state is not changed when deleting a stream that was previously deleted (CHECK_MODE)' + assert: + that: + - result is success + - result is not changed + + - name: 'Delete stream - Idempotency' + module_defaults: { kinesis_stream: {} } + kinesis_stream: + name: '{{ kinesis_stream_name }}' + state: absent + register: result + - name: 'Assert state is not changed when deleting a stream that was previously deleted' + assert: + that: + - result is success + - result is not changed + + always: + # ============================================================ + - name: 'Ensure Kinesis stream is gone' + ignore_errors: yes + kinesis_stream: + name: '{{ kinesis_stream_name }}' + state: absent + + - name: 'KMS test preperation - only run when explicitly enabled' + when: + - run_kms_tests | default(False) | bool + block: + - name: 'Delete the KMS keys' + ignore_errors: yes + aws_kms: + state: absent + alias: '{{ item }}' + loop: + - '{{ kms_cmk_alias_1 }}' + - '{{ kms_cmk_alias_2 }}' diff --git a/ansible_collections/community/aws/tests/integration/targets/kinesis_stream/tasks/test_encryption.yml b/ansible_collections/community/aws/tests/integration/targets/kinesis_stream/tasks/test_encryption.yml new file mode 100644 index 000000000..23555a945 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/kinesis_stream/tasks/test_encryption.yml @@ -0,0 +1,250 @@ +--- +# Run through the different ways we can enable/change encryption +# Enable (check_mode) +# Enable +# Idempotency - compared to ID (idempotency) +# Idempotency - compared to ID +# Idempotency - compared to Alias (idempotency) +# Idempotency - compared to Alias +# Idempotency - compared to ARN (idempotency) +# Idempotency - compared to ARN +# Disable (check_mode) +# Disable +# +# Known issue: +# - key_id needs to be in the same form as is already set to return changed=False +# +- name: 'Enable encryption using {{ key_type }} (CHECK_MODE)' + check_mode: yes + kinesis_stream: + name: '{{ kinesis_stream_name }}' + encryption_state: 'enabled' + encryption_type: 'KMS' + key_id: '{{ kinesis_key }}' + register: result +- name: 'Assert state is changed when enabling encryption (CHECK_MODE)' + assert: + that: + - result is success + - result is changed + +- name: 'Enable encryption using {{ key_type }}' + kinesis_stream: + name: '{{ kinesis_stream_name }}' + encryption_state: 'enabled' + encryption_type: 'KMS' + key_id: '{{ kinesis_key }}' + register: result +- name: 'Assert that state is changed when enabling encryption' + assert: + that: + - result is success + - result is changed + - result.encryption_type == 'KMS' + - result.key_id in kms_cmk_1 + - result.open_shards_count == 1 + - result.retention_period_hours == 48 + - result.stream_arn == kinesis_stream_arn + - result.stream_name == kinesis_stream_name + - result.stream_status == 'ACTIVE' + - result.tags == kinesis_stream_tags_2 + +- name: 'Re-Enable encryption using {{ key_type }} (CHECK_MODE)' + kinesis_stream: + name: '{{ kinesis_stream_name }}' + encryption_state: 'enabled' + encryption_type: 'KMS' + key_id: '{{ kinesis_key }}' + check_mode: True + register: result +- name: 'Assert that state is not changed when enabling encryption' + assert: + that: + - result is success + - result is not changed + - result.encryption_type == 'KMS' + - result.key_id in kms_cmk_1 + - result.open_shards_count == 1 + - result.retention_period_hours == 48 + - result.stream_arn == kinesis_stream_arn + - result.stream_name == kinesis_stream_name + - result.stream_status == 'ACTIVE' + - result.tags == kinesis_stream_tags_2 + +- name: 'Re-Enable encryption using {{ key_type }}' + kinesis_stream: + name: '{{ kinesis_stream_name }}' + encryption_state: 'enabled' + encryption_type: 'KMS' + key_id: '{{ kinesis_key }}' + register: result +- name: 'Assert that state is not changed when enabling encryption' + assert: + that: + - result is success + - result is not changed + - result.encryption_type == 'KMS' + - result.key_id in kms_cmk_1 + - result.open_shards_count == 1 + - result.retention_period_hours == 48 + - result.stream_arn == kinesis_stream_arn + - result.stream_name == kinesis_stream_name + - result.stream_status == 'ACTIVE' + - result.tags == kinesis_stream_tags_2 + +- name: 'Test encryption idempotency comparing {{ key_type }} and ID (CHECK_MODE)' + check_mode: yes + kinesis_stream: + name: '{{ kinesis_stream_name }}' + encryption_state: 'enabled' + encryption_type: 'KMS' + key_id: '{{ kinesis_key_id }}' + register: result +- name: 'Assert state is not changed when comparing {{ key_id }} and ID (CHECK_MODE)' + assert: + that: + - result is success + - result is not changed + ignore_errors: yes + +- name: 'Test encryption idempotency comparing {{ key_type }} and ID' + kinesis_stream: + name: '{{ kinesis_stream_name }}' + encryption_state: 'enabled' + encryption_type: 'KMS' + key_id: '{{ kinesis_key_id }}' + register: result +- name: 'Assert that state is not changed when comparing {{ key_type }} and ID' + assert: + that: + - result is not changed + ignore_errors: yes +- name: 'Assert expected return values' + assert: + that: + - result is success + - result.encryption_type == 'KMS' + - result.key_id in kms_cmk_1 + - result.open_shards_count == 1 + - result.retention_period_hours == 48 + - result.stream_arn == kinesis_stream_arn + - result.stream_name == kinesis_stream_name + - result.stream_status == 'ACTIVE' + - result.tags == kinesis_stream_tags_2 + + +- name: 'Test encryption idempotency comparing {{ key_type }} and Alias (CHECK_MODE)' + check_mode: yes + kinesis_stream: + name: '{{ kinesis_stream_name }}' + encryption_state: 'enabled' + encryption_type: 'KMS' + key_id: '{{ kinesis_key_alias }}' + register: result +- name: 'Assert state is not changed when comparing {{ key_type }} and Alias (CHECK_MODE)' + assert: + that: + - result is success + - result is not changed + ignore_errors: yes + +- name: 'Test encryption idempotency comparing {{ key_type }} and Alias' + kinesis_stream: + name: '{{ kinesis_stream_name }}' + encryption_state: 'enabled' + encryption_type: 'KMS' + key_id: '{{ kinesis_key_alias }}' + register: result +- name: 'Assert that state is not changed when comparing {{ key_type }} and Alias' + assert: + that: + - result is not changed + ignore_errors: yes +- name: 'Assert expected return values' + assert: + that: + - result is success + - result.encryption_type == 'KMS' + - result.key_id in kms_cmk_1 + - result.open_shards_count == 1 + - result.retention_period_hours == 48 + - result.stream_arn == kinesis_stream_arn + - result.stream_name == kinesis_stream_name + - result.stream_status == 'ACTIVE' + - result.tags == kinesis_stream_tags_2 + +- name: 'Test encryption idempotency comparing {{ key_type }} and ARN (CHECK_MODE)' + check_mode: yes + kinesis_stream: + name: '{{ kinesis_stream_name }}' + encryption_state: 'enabled' + encryption_type: 'KMS' + key_id: '{{ kinesis_key_arn }}' + register: result +- name: 'Assert state is not changed when comparing {{ key_type }} and ARN (CHECK_MODE)' + assert: + that: + - result is success + - result is not changed + ignore_errors: yes + +- name: 'Test encryption idempotency comparing {{ key_type }} and ARN' + kinesis_stream: + name: '{{ kinesis_stream_name }}' + encryption_state: 'enabled' + encryption_type: 'KMS' + key_id: '{{ kinesis_key_arn }}' + register: result +- name: 'Assert that state is not changed when comparing {{ key_type }} and ARN' + assert: + that: + - result is not changed + ignore_errors: yes +- name: 'Assert expected return values' + assert: + that: + - result is success + - result.encryption_type == 'KMS' + - result.key_id in kms_cmk_1 + - result.open_shards_count == 1 + - result.retention_period_hours == 48 + - result.stream_arn == kinesis_stream_arn + - result.stream_name == kinesis_stream_name + - result.stream_status == 'ACTIVE' + - result.tags == kinesis_stream_tags_2 + +- name: 'Disable encryption (CHECK_MODE)' + kinesis_stream: + name: '{{ kinesis_stream_name }}' + encryption_state: 'disabled' + encryption_type: 'KMS' + # XXX Oddity of Kinesis - This needs to match the existing setting + key_id: '{{ kinesis_key_arn }}' + register: result + check_mode: yes +- name: 'Assert state is changed when disabling encryption (CHECK_MODE)' + assert: + that: + - result is success + - result is changed + +- name: 'Disable encryption' + kinesis_stream: + name: '{{ kinesis_stream_name }}' + encryption_state: 'disabled' + encryption_type: 'KMS' + # XXX Oddity of Kinesis - This needs to match the existing setting + key_id: '{{ kinesis_key_arn }}' + register: result +- name: 'Assert state is changed when disabling encryption' + assert: + that: + - result is success + - result is changed + - result.encryption_type == 'NONE' + - result.open_shards_count == 1 + - result.retention_period_hours == 48 + - result.stream_arn == kinesis_stream_arn + - result.stream_name == kinesis_stream_name + - result.stream_status == 'ACTIVE' + - result.tags == kinesis_stream_tags_2 diff --git a/ansible_collections/community/aws/tests/integration/targets/legacy_missing_tests/README.md b/ansible_collections/community/aws/tests/integration/targets/legacy_missing_tests/README.md new file mode 100644 index 000000000..f13928252 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/legacy_missing_tests/README.md @@ -0,0 +1,5 @@ +## Fake integration suite + +This is a fake integration suite including an aliases file listing every module name with missing integration tests. + +This fake suite is necessary for the new CI ansible-test-splitter behaviour. Namely, if one of the modules (listed in the aliases file) without a test suite is modified, the CI is run for the entire collection since the ansible-test-splitter won't find any target match. This fake integration suite helps handle this situation by avoiding running the CI for the whole collection. Furthermore, since the modules listed in the aliases file are marked as disabled, tests are automatically skipped).
\ No newline at end of file diff --git a/ansible_collections/community/aws/tests/integration/targets/legacy_missing_tests/aliases b/ansible_collections/community/aws/tests/integration/targets/legacy_missing_tests/aliases new file mode 100644 index 000000000..27c4351c4 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/legacy_missing_tests/aliases @@ -0,0 +1,30 @@ +disabled + +# Modules that do not have test suites +application_scaling_policy +batch_compute_environment +batch_job_definition +batch_job_queue +cloudfront_distribution_info +cloudfront_invalidation +cloudfront_origin_access_identity +data_pipeline +directconnect_confirm_connection +directconnect_connection +directconnect_gateway +directconnect_link_aggregation_group +directconnect_virtual_interface +dynamodb_ttl +ec2_ami_copy +ec2_customer_gateway +ec2_customer_gateway_info +ec2_snapshot_copy +ec2_win_password +ecs_attribute +elasticache_parameter_group +elasticache_snapshot +iam_mfa_device_info +redshift_cross_region_snapshots +s3_cors +s3_website +storagegateway_info diff --git a/ansible_collections/community/aws/tests/integration/targets/legacy_missing_tests/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/legacy_missing_tests/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/legacy_missing_tests/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/lightsail/aliases b/ansible_collections/community/aws/tests/integration/targets/lightsail/aliases new file mode 100644 index 000000000..4ef4b2067 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/lightsail/aliases @@ -0,0 +1 @@ +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/lightsail/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/lightsail/defaults/main.yml new file mode 100644 index 000000000..46f5b34e0 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/lightsail/defaults/main.yml @@ -0,0 +1,2 @@ +instance_name: "{{ resource_prefix }}_instance" +zone: "{{ aws_region }}a" diff --git a/ansible_collections/community/aws/tests/integration/targets/lightsail/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/lightsail/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/lightsail/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/lightsail/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/lightsail/tasks/main.yml new file mode 100644 index 000000000..91f13a8ba --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/lightsail/tasks/main.yml @@ -0,0 +1,122 @@ +--- + +- module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key | default(omit) }}' + aws_secret_key: '{{ aws_secret_key | default(omit) }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region | default(omit) }}' + + block: + + # ==== Tests =================================================== + + - name: Create a new instance + lightsail: + name: "{{ instance_name }}" + zone: "{{ zone }}" + blueprint_id: amazon_linux + bundle_id: nano_2_0 + wait: yes + register: result + + - assert: + that: + - result.changed == True + - "'instance' in result and result.instance.name == instance_name" + - "result.instance.state.name == 'running'" + + - name: Make sure create is idempotent + lightsail: + name: "{{ instance_name }}" + zone: "{{ zone }}" + blueprint_id: amazon_linux + bundle_id: nano_2_0 + register: result + + - assert: + that: + - result.changed == False + + - name: Start the running instance + lightsail: + name: "{{ instance_name }}" + state: running + register: result + + - assert: + that: + - result.changed == False + + - name: Stop the instance + lightsail: + name: "{{ instance_name }}" + state: stopped + wait: yes + register: result + + - assert: + that: + - result.changed == True + - "result.instance.state.name == 'stopped'" + + - name: Stop the stopped instance + lightsail: + name: "{{ instance_name }}" + state: stopped + register: result + + - assert: + that: + - result.changed == False + + - name: Start the instance + lightsail: + name: "{{ instance_name }}" + state: running + register: result + + - assert: + that: + - result.changed == True + - "result.instance.state.name == 'running'" + + - name: Restart the instance + lightsail: + name: "{{ instance_name }}" + state: restarted + register: result + + - assert: + that: + - result.changed == True + + - name: Delete the instance + lightsail: + name: "{{ instance_name }}" + state: absent + register: result + + - assert: + that: + - result.changed == True + + - name: Make sure instance deletion is idempotent + lightsail: + name: "{{ instance_name }}" + state: absent + register: result + + - assert: + that: + - result.changed == False + + # ==== Cleanup ==================================================== + + always: + + - name: Cleanup - delete instance + lightsail: + name: "{{ instance_name }}" + state: absent + ignore_errors: yes diff --git a/ansible_collections/community/aws/tests/integration/targets/lightsail_static_ip/aliases b/ansible_collections/community/aws/tests/integration/targets/lightsail_static_ip/aliases new file mode 100644 index 000000000..4ef4b2067 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/lightsail_static_ip/aliases @@ -0,0 +1 @@ +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/lightsail_static_ip/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/lightsail_static_ip/defaults/main.yml new file mode 100644 index 000000000..a024c1c79 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/lightsail_static_ip/defaults/main.yml @@ -0,0 +1 @@ +static_ip_name: "{{ resource_prefix }}_static_ip" diff --git a/ansible_collections/community/aws/tests/integration/targets/lightsail_static_ip/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/lightsail_static_ip/tasks/main.yml new file mode 100644 index 000000000..f8f327344 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/lightsail_static_ip/tasks/main.yml @@ -0,0 +1,96 @@ +--- + +- module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key | default(omit) }}' + aws_secret_key: '{{ aws_secret_key | default(omit) }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region | default(omit) }}' + + block: + + # ==== Tests =================================================== + + - name: Create a new static IP + lightsail_static_ip: + name: "{{ static_ip_name }}" + register: result + + - assert: + that: + - result.changed == True + - '"static_ip" in result' + - '"arn" in result.static_ip' + - '"created_at" in result.static_ip' + - '"ip_address" in result.static_ip' + - '"is_attached" in result.static_ip' + - '"location" in result.static_ip' + - '"name" in result.static_ip' + - '"resource_type" in result.static_ip' + - '"support_code" in result.static_ip' + - result.static_ip.arn.startswith("arn:") + - result.static_ip.name == static_ip_name + - result.static_ip.resource_type == 'StaticIp' + - result.static_ip.is_attached == false + - result.static_ip.ip_address | ansible.utils.ipaddr + - '"availability_zone" in result.static_ip.location' + - '"region_name" in result.static_ip.location' + + - set_fact: + lightsail_ip_arn: '{{ result.static_ip.arn }}' + lightsail_ip_address: '{{ result.static_ip.ip_address }}' + + - name: Make sure create is idempotent + lightsail_static_ip: + name: "{{ static_ip_name }}" + register: result + + - assert: + that: + - result.changed == False + - '"static_ip" in result' + - '"arn" in result.static_ip' + - '"created_at" in result.static_ip' + - '"ip_address" in result.static_ip' + - '"is_attached" in result.static_ip' + - '"location" in result.static_ip' + - '"name" in result.static_ip' + - '"resource_type" in result.static_ip' + - '"support_code" in result.static_ip' + - result.static_ip.arn == lightsail_ip_arn + - result.static_ip.name == static_ip_name + - result.static_ip.resource_type == 'StaticIp' + - result.static_ip.is_attached == false + - result.static_ip.ip_address == lightsail_ip_address + - '"availability_zone" in result.static_ip.location' + - '"region_name" in result.static_ip.location' + + - name: Delete the static IP + lightsail_static_ip: + name: "{{ static_ip_name }}" + state: absent + register: result + + - assert: + that: + - result.changed == True + + - name: Make sure deletion is idempotent + lightsail_static_ip: + name: "{{ static_ip_name }}" + state: absent + register: result + + - assert: + that: + - result.changed == False + + # ==== Cleanup ==================================================== + + always: + + - name: Cleanup - delete static IP + lightsail_static_ip: + name: "{{ static_ip_name }}" + state: absent + ignore_errors: yes diff --git a/ansible_collections/community/aws/tests/integration/targets/msk_cluster-auth/aliases b/ansible_collections/community/aws/tests/integration/targets/msk_cluster-auth/aliases new file mode 100644 index 000000000..3ac6603b0 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/msk_cluster-auth/aliases @@ -0,0 +1,4 @@ +cloud/aws +time=46m + +msk_cluster diff --git a/ansible_collections/community/aws/tests/integration/targets/msk_cluster-auth/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/msk_cluster-auth/defaults/main.yml new file mode 100644 index 000000000..a98a6e73f --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/msk_cluster-auth/defaults/main.yml @@ -0,0 +1,19 @@ +--- +vpc_name: "{{ resource_prefix }}-mskc-a" +vpc_cidr: '10.{{ 256 | random(seed=resource_prefix) }}.0.0/16' +vpc_subnets: + - '10.{{ 256 | random(seed=resource_prefix) }}.100.0/24' + - '10.{{ 256 | random(seed=resource_prefix) }}.101.0/24' +vpc_subnet_name_prefix: "{{ resource_prefix }}" + +msk_config_name: "{{ resource_prefix }}-msk-cluster-auth" +msk_cluster_name: "{{ tiny_prefix }}-msk-cluster-auth" +msk_version: 2.8.1 +msk_broker_nodes: 2 + +tags_create: + key1: "value1" + key2: "value2" +tags_update: + key2: "value2" + key3: "value3" diff --git a/ansible_collections/community/aws/tests/integration/targets/msk_cluster-auth/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/msk_cluster-auth/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/msk_cluster-auth/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/msk_cluster-auth/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/msk_cluster-auth/tasks/main.yml new file mode 100644 index 000000000..5a6487607 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/msk_cluster-auth/tasks/main.yml @@ -0,0 +1,91 @@ +--- +- name: aws_msk_cluster integration tests + module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + collections: + - amazon.aws + block: + - name: collect availability zone info + aws_az_info: + register: az_info + + - name: assert there are at least two zones + assert: + that: az_info.availability_zones | length >= 2 + + - name: create vpc + ec2_vpc_net: + state: present + cidr_block: '{{ vpc_cidr }}' + name: '{{ vpc_name }}' + register: vpc + + - name: create subnets + ec2_vpc_subnet: + state: present + cidr: '{{ item }}' + az: '{{ az_info.availability_zones[index].zone_name }}' + vpc_id: '{{ vpc.vpc.id }}' + tags: + Name: '{{ vpc_subnet_name_prefix }}-subnet-{{ index }}' + loop: "{{ vpc_subnets }}" + loop_control: + index_var: index + register: subnets + + - set_fact: + subnet_ids: '{{ subnets | community.general.json_query("results[].subnet.id") | list }}' + + # ============================================================ + - name: create msk configuration + aws_msk_config: + name: "{{ msk_config_name }}" + state: "present" + kafka_versions: + - "{{ msk_version }}" + register: msk_config + + - name: create test with sasl_iam + include_tasks: test_create_auth.yml + + always: + + - name: delete msk cluster + aws_msk_cluster: + name: "{{ msk_cluster_name }}" + state: absent + wait: true + ignore_errors: yes + + - name: remove msk configuration + aws_msk_config: + name: "{{ msk_config_name }}" + state: absent + ignore_errors: yes + + - name: remove subnets + ec2_vpc_subnet: + state: absent + cidr: '{{ item }}' + vpc_id: '{{ vpc.vpc.id }}' + loop: "{{ vpc_subnets }}" + ignore_errors: yes + register: removed_subnets + until: removed_subnets is succeeded + retries: 5 + delay: 5 + + - name: remove the vpc + ec2_vpc_net: + state: absent + cidr_block: '{{ vpc_cidr }}' + name: '{{ vpc_name }}' + ignore_errors: yes + register: removed_vpc + until: removed_vpc is success + retries: 5 + delay: 5 diff --git a/ansible_collections/community/aws/tests/integration/targets/msk_cluster-auth/tasks/test_create_auth.yml b/ansible_collections/community/aws/tests/integration/targets/msk_cluster-auth/tasks/test_create_auth.yml new file mode 100644 index 000000000..d7cdd3a71 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/msk_cluster-auth/tasks/test_create_auth.yml @@ -0,0 +1,101 @@ +--- +- name: create a msk cluster with authentication flipped from default (check mode) + aws_msk_cluster: + name: "{{ msk_cluster_name }}" + state: "present" + version: "{{ msk_version }}" + nodes: "{{ msk_broker_nodes }}" + ebs_volume_size: 10 + authentication: + sasl_iam: true + sasl_scram: true + unauthenticated: false + subnets: "{{ subnet_ids }}" + wait: true + tags: "{{ tags_create }}" + configuration_arn: "{{ msk_config.arn }}" + configuration_revision: "{{ msk_config.revision }}" + check_mode: yes + register: msk_cluster + +- name: assert that the msk cluster be created + assert: + that: + - msk_cluster is changed + +- name: create a msk cluster with authentication flipped from default + aws_msk_cluster: + name: "{{ msk_cluster_name }}" + state: "present" + version: "{{ msk_version }}" + nodes: "{{ msk_broker_nodes }}" + ebs_volume_size: 10 + authentication: + sasl_iam: true + sasl_scram: true + unauthenticated: false + subnets: "{{ subnet_ids }}" + wait: true + tags: "{{ tags_create }}" + configuration_arn: "{{ msk_config.arn }}" + configuration_revision: "{{ msk_config.revision }}" + register: msk_cluster + +- name: assert that the msk cluster is created + assert: + that: + - msk_cluster is changed + +- name: validate return values + assert: + that: + - "'cluster_info' in msk_cluster" + - "'bootstrap_broker_string' in msk_cluster" + - "'key1' in msk_cluster.cluster_info.tags" + - "msk_cluster.cluster_info.tags.key1 == 'value1'" + - "msk_cluster.cluster_info.cluster_name == msk_cluster_name" + - "msk_cluster.cluster_info.number_of_broker_nodes == msk_broker_nodes" + - "msk_cluster.cluster_info.broker_node_group_info.instance_type == 'kafka.t3.small'" + - "msk_cluster.cluster_info.broker_node_group_info.storage_info.ebs_storage_info.volume_size == 10" + - "msk_cluster.cluster_info.client_authentication.sasl.iam.enabled == true" + - "msk_cluster.cluster_info.client_authentication.sasl.scram.enabled == true" + # Not always returned by API + # - "msk_cluster.cluster_info.client_authentication.unauthenticated.enabled == false" + - "msk_cluster.cluster_info.open_monitoring.prometheus.jmx_exporter.enabled_in_broker == false" + - "msk_cluster.cluster_info.cluster_arn.startswith('arn:aws:kafka:{{ aws_region }}:')" + +- name: create a msk cluster with authentication flipped from default (idempotency) + aws_msk_cluster: + name: "{{ msk_cluster_name }}" + state: "present" + version: "{{ msk_version }}" + nodes: "{{ msk_broker_nodes }}" + ebs_volume_size: 10 + authentication: + sasl_iam: true + sasl_scram: true + unauthenticated: false + subnets: "{{ subnet_ids }}" + wait: true + tags: "{{ tags_create }}" + configuration_arn: "{{ msk_config.arn }}" + configuration_revision: "{{ msk_config.revision }}" + register: msk_cluster + +- name: assert that the msk cluster wasn't changed + assert: + that: + - msk_cluster is not changed + +### Keep delete simple as we're not checking delete here +- name: delete msk cluster + aws_msk_cluster: + name: "{{ msk_cluster_name }}" + state: "absent" + wait: true + register: msk_cluster + +- name: assert that the msk cluster is changed + assert: + that: + - msk_cluster is changed diff --git a/ansible_collections/community/aws/tests/integration/targets/msk_cluster/aliases b/ansible_collections/community/aws/tests/integration/targets/msk_cluster/aliases new file mode 100644 index 000000000..c9cac54c2 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/msk_cluster/aliases @@ -0,0 +1,2 @@ +cloud/aws +time=46m diff --git a/ansible_collections/community/aws/tests/integration/targets/msk_cluster/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/msk_cluster/defaults/main.yml new file mode 100644 index 000000000..e8318cd9b --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/msk_cluster/defaults/main.yml @@ -0,0 +1,19 @@ +--- +vpc_name: "{{ resource_prefix }}" +vpc_cidr: '10.{{ 256 | random(seed=resource_prefix) }}.0.0/16' +vpc_subnets: + - '10.{{ 256 | random(seed=resource_prefix) }}.100.0/24' + - '10.{{ 256 | random(seed=resource_prefix) }}.101.0/24' +vpc_subnet_name_prefix: "{{ resource_prefix }}" + +msk_config_name: "{{ resource_prefix }}-msk-cluster" +msk_cluster_name: "{{ tiny_prefix }}-msk-cluster" +msk_version: 2.8.1 +msk_broker_nodes: 2 + +tags_create: + key1: "value1" + key2: "value2" +tags_update: + key2: "value2" + key3: "value3" diff --git a/ansible_collections/community/aws/tests/integration/targets/msk_cluster/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/msk_cluster/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/msk_cluster/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/msk_cluster/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/msk_cluster/tasks/main.yml new file mode 100644 index 000000000..a3049dad0 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/msk_cluster/tasks/main.yml @@ -0,0 +1,97 @@ +--- +- name: aws_msk_cluster integration tests + module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + collections: + - amazon.aws + block: + - name: collect availability zone info + aws_az_info: + register: az_info + + - name: assert there are at least two zones + assert: + that: az_info.availability_zones | length >= 2 + + - name: create vpc + ec2_vpc_net: + state: present + cidr_block: '{{ vpc_cidr }}' + name: '{{ vpc_name }}' + register: vpc + + - name: create subnets + ec2_vpc_subnet: + state: present + cidr: '{{ item }}' + az: '{{ az_info.availability_zones[index].zone_name }}' + vpc_id: '{{ vpc.vpc.id }}' + tags: + Name: '{{ vpc_subnet_name_prefix }}-subnet-{{ index }}' + loop: "{{ vpc_subnets }}" + loop_control: + index_var: index + register: subnets + + - set_fact: + subnet_ids: '{{ subnets | community.general.json_query("results[].subnet.id") | list }}' + + # ============================================================ + - name: create msk configuration + aws_msk_config: + name: "{{ msk_config_name }}" + state: "present" + kafka_versions: + - "{{ msk_version }}" + register: msk_config + + - name: create tests + include_tasks: test_create.yml + + - name: update tests + include_tasks: test_update.yml + + - name: delete tests + include_tasks: test_delete.yml + + always: + + - name: delete msk cluster + aws_msk_cluster: + name: "{{ msk_cluster_name }}" + state: absent + wait: true + ignore_errors: yes + + - name: remove msk configuration + aws_msk_config: + name: "{{ msk_config_name }}" + state: absent + ignore_errors: yes + + - name: remove subnets + ec2_vpc_subnet: + state: absent + cidr: '{{ item }}' + vpc_id: '{{ vpc.vpc.id }}' + loop: "{{ vpc_subnets }}" + ignore_errors: yes + register: removed_subnets + until: removed_subnets is succeeded + retries: 5 + delay: 5 + + - name: remove the vpc + ec2_vpc_net: + state: absent + cidr_block: '{{ vpc_cidr }}' + name: '{{ vpc_name }}' + ignore_errors: yes + register: removed_vpc + until: removed_vpc is success + retries: 5 + delay: 5 diff --git a/ansible_collections/community/aws/tests/integration/targets/msk_cluster/tasks/test_create.yml b/ansible_collections/community/aws/tests/integration/targets/msk_cluster/tasks/test_create.yml new file mode 100644 index 000000000..4fd7073cc --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/msk_cluster/tasks/test_create.yml @@ -0,0 +1,72 @@ +--- +- name: create msk cluster (check mode) + aws_msk_cluster: + name: "{{ msk_cluster_name }}" + state: "present" + version: "{{ msk_version }}" + nodes: "{{ msk_broker_nodes }}" + ebs_volume_size: 10 + subnets: "{{ subnet_ids }}" + wait: true + tags: "{{ tags_create }}" + configuration_arn: "{{ msk_config.arn }}" + configuration_revision: "{{ msk_config.revision }}" + check_mode: yes + register: msk_cluster + +- name: assert that the msk cluster be created + assert: + that: + - msk_cluster is changed + +- name: create msk cluster + aws_msk_cluster: + name: "{{ msk_cluster_name }}" + state: "present" + version: "{{ msk_version }}" + nodes: "{{ msk_broker_nodes }}" + ebs_volume_size: 10 + subnets: "{{ subnet_ids }}" + wait: true + tags: "{{ tags_create }}" + configuration_arn: "{{ msk_config.arn }}" + configuration_revision: "{{ msk_config.revision }}" + register: msk_cluster + +- name: assert that the msk cluster is created + assert: + that: + - msk_cluster is changed + +- name: validate return values + assert: + that: + - "'cluster_info' in msk_cluster" + - "'bootstrap_broker_string' in msk_cluster" + - "'key1' in msk_cluster.cluster_info.tags" + - "msk_cluster.cluster_info.tags.key1 == 'value1'" + - "msk_cluster.cluster_info.cluster_name == msk_cluster_name" + - "msk_cluster.cluster_info.number_of_broker_nodes == msk_broker_nodes" + - "msk_cluster.cluster_info.broker_node_group_info.instance_type == 'kafka.t3.small'" + - "msk_cluster.cluster_info.broker_node_group_info.storage_info.ebs_storage_info.volume_size == 10" + - "msk_cluster.cluster_info.open_monitoring.prometheus.jmx_exporter.enabled_in_broker == false" + - "msk_cluster.cluster_info.cluster_arn.startswith('arn:aws:kafka:{{ aws_region }}:')" + +- name: create msk cluster (idempotency) + aws_msk_cluster: + name: "{{ msk_cluster_name }}" + state: "present" + version: "{{ msk_version }}" + nodes: "{{ msk_broker_nodes }}" + ebs_volume_size: 10 + subnets: "{{ subnet_ids }}" + wait: true + tags: "{{ tags_create }}" + configuration_arn: "{{ msk_config.arn }}" + configuration_revision: "{{ msk_config.revision }}" + register: msk_cluster + +- name: assert that the msk cluster wasn't changed + assert: + that: + - msk_cluster is not changed diff --git a/ansible_collections/community/aws/tests/integration/targets/msk_cluster/tasks/test_delete.yml b/ansible_collections/community/aws/tests/integration/targets/msk_cluster/tasks/test_delete.yml new file mode 100644 index 000000000..efd90fa14 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/msk_cluster/tasks/test_delete.yml @@ -0,0 +1,37 @@ +--- +- name: delete msk cluster (check mode) + aws_msk_cluster: + name: "{{ msk_cluster_name }}" + state: "absent" + wait: true + check_mode: yes + register: msk_cluster + +- name: assert that the msk cluster be changed + assert: + that: + - msk_cluster is changed + +- name: delete msk cluster + aws_msk_cluster: + name: "{{ msk_cluster_name }}" + state: "absent" + wait: true + register: msk_cluster + +- name: assert that the msk cluster is changed + assert: + that: + - msk_cluster is changed + +- name: delete msk cluster (idempotency) + aws_msk_cluster: + name: "{{ msk_cluster_name }}" + state: "absent" + wait: true + register: msk_cluster + +- name: assert that the msk cluster wasn't changed + assert: + that: + - msk_cluster is not changed diff --git a/ansible_collections/community/aws/tests/integration/targets/msk_cluster/tasks/test_update.yml b/ansible_collections/community/aws/tests/integration/targets/msk_cluster/tasks/test_update.yml new file mode 100644 index 000000000..50ac91718 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/msk_cluster/tasks/test_update.yml @@ -0,0 +1,72 @@ +--- +- name: update msk cluster (check mode) + aws_msk_cluster: + name: "{{ msk_cluster_name }}" + state: "present" + version: "{{ msk_version }}" + nodes: "{{ msk_broker_nodes }}" + ebs_volume_size: 20 + open_monitoring: + jmx_exporter: True + subnets: "{{ subnet_ids }}" + wait: true + tags: "{{ tags_update }}" + configuration_arn: "{{ msk_config.arn }}" + configuration_revision: "{{ msk_config.revision }}" + check_mode: yes + register: msk_cluster + +- name: assert that the msk cluster be changed + assert: + that: + - msk_cluster is changed + +- name: update msk cluster + aws_msk_cluster: + name: "{{ msk_cluster_name }}" + state: "present" + version: "{{ msk_version }}" + nodes: "{{ msk_broker_nodes }}" + ebs_volume_size: 20 + open_monitoring: + jmx_exporter: True + subnets: "{{ subnet_ids }}" + wait: true + tags: "{{ tags_update }}" + configuration_arn: "{{ msk_config.arn }}" + configuration_revision: "{{ msk_config.revision }}" + register: msk_cluster + +- name: assert that the msk cluster is changed + assert: + that: + - msk_cluster is changed + +- name: validate return values + assert: + that: + - "msk_cluster.cluster_info.broker_node_group_info.storage_info.ebs_storage_info.volume_size == 20" + - "msk_cluster.cluster_info.open_monitoring.prometheus.jmx_exporter.enabled_in_broker == true" + - "'key-1' not in msk_cluster.cluster_info.tags" + - "msk_cluster.cluster_info.tags.key3 == 'value3'" + +- name: update msk cluster (idempotency) + aws_msk_cluster: + name: "{{ msk_cluster_name }}" + state: "present" + version: "{{ msk_version }}" + nodes: "{{ msk_broker_nodes }}" + ebs_volume_size: 20 + open_monitoring: + jmx_exporter: True + subnets: "{{ subnet_ids }}" + wait: true + tags: "{{ tags_update }}" + configuration_arn: "{{ msk_config.arn }}" + configuration_revision: "{{ msk_config.revision }}" + register: msk_cluster + +- name: assert that the msk cluster wasn't changed + assert: + that: + - msk_cluster is not changed diff --git a/ansible_collections/community/aws/tests/integration/targets/msk_config/aliases b/ansible_collections/community/aws/tests/integration/targets/msk_config/aliases new file mode 100644 index 000000000..04b4c46b5 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/msk_config/aliases @@ -0,0 +1,2 @@ +time=10m +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/msk_config/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/msk_config/defaults/main.yml new file mode 100644 index 000000000..92aad4bd8 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/msk_config/defaults/main.yml @@ -0,0 +1,10 @@ +--- +msk_config_name: "{{ resource_prefix }}-msk-config" +msk_configs: + - auto.create.topics.enable: true + zookeeper.session.timeout.ms: 18000 + - num.io.threads: 8 + zookeeper.session.timeout.ms: 36000 +msk_kafka_versions: + - 2.6.0 + - 2.6.1 diff --git a/ansible_collections/community/aws/tests/integration/targets/msk_config/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/msk_config/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/msk_config/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/msk_config/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/msk_config/tasks/main.yml new file mode 100644 index 000000000..cef9e1dfc --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/msk_config/tasks/main.yml @@ -0,0 +1,148 @@ +--- +- name: tests for community.aws.aws_msk_config + module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + collections: + - amazon.aws + block: + - name: create msk configuration (check mode) + aws_msk_config: + name: "{{ msk_config_name }}" + state: "present" + kafka_versions: "{{ msk_kafka_versions }}" + config: "{{ msk_configs[0] }}" + check_mode: yes + register: msk_config + + - name: assert that the msk configuration be created + assert: + that: + - msk_config is changed + + - name: create msk configuration + aws_msk_config: + name: "{{ msk_config_name }}" + state: "present" + kafka_versions: "{{ msk_kafka_versions }}" + config: "{{ msk_configs[0] }}" + register: msk_config + + - name: assert that the msk configuration is created + assert: + that: + - msk_config is changed + + - name: create msk configuration (idempotency) + aws_msk_config: + name: "{{ msk_config_name }}" + state: "present" + kafka_versions: "{{ msk_kafka_versions }}" + config: "{{ msk_configs[0] }}" + register: msk_config + + - name: assert that the msk configuration wasn't changed + assert: + that: + - msk_config is not changed + + - name: validate return values + assert: + that: + - msk_config.revision == 1 + - "msk_config.arn.startswith('arn:aws:kafka:{{ aws_region }}:')" + - "'auto.create.topics.enable=True' in msk_config.server_properties" + - "'zookeeper.session.timeout.ms=18000' in msk_config.server_properties" + + - name: update msk configuration (check mode) + aws_msk_config: + name: "{{ msk_config_name }}" + state: "present" + kafka_versions: "{{ msk_kafka_versions }}" + config: "{{ msk_configs[1] }}" + check_mode: yes + register: msk_config + + - name: assert that the msk configuration be changed + assert: + that: + - msk_config is changed + + - name: update msk configuration + aws_msk_config: + name: "{{ msk_config_name }}" + state: "present" + kafka_versions: "{{ msk_kafka_versions }}" + config: "{{ msk_configs[1] }}" + register: msk_config + + - name: assert that the msk configuration is changed + assert: + that: + - msk_config is changed + + - name: validate return values (update) + assert: + that: + - msk_config.revision == 2 + - "'auto.create.topics.enable=True' not in msk_config.server_properties" + - "'num.io.threads=8' in msk_config.server_properties" + - "'zookeeper.session.timeout.ms=36000' in msk_config.server_properties" + + - name: update msk configuration (idempotency) + aws_msk_config: + name: "{{ msk_config_name }}" + state: "present" + kafka_versions: "{{ msk_kafka_versions }}" + config: "{{ msk_configs[1] }}" + register: msk_config + + - name: assert that the msk configuration wasn't changed + assert: + that: + - msk_config is not changed + + - name: delete msk configuration (check mode) + aws_msk_config: + name: "{{ msk_config_name }}" + state: "absent" + check_mode: yes + register: msk_config + + - name: assert that the msk configuration be changed + assert: + that: + - msk_config is changed + + - name: delete msk configuration + aws_msk_config: + name: "{{ msk_config_name }}" + state: "absent" + register: msk_config + + - name: assert that the msk configuration is changed + assert: + that: + - msk_config is changed + + - name: delete msk configuration (idempotency) + aws_msk_config: + name: "{{ msk_config_name }}" + state: "absent" + register: msk_config + + - name: assert that the msk configuration wasn't changed + assert: + that: + - msk_config is not changed + + always: + + - name: remove msk configuration + aws_msk_config: + name: "{{ msk_config_name }}" + state: absent + ignore_errors: yes diff --git a/ansible_collections/community/aws/tests/integration/targets/networkfirewall/aliases b/ansible_collections/community/aws/tests/integration/targets/networkfirewall/aliases new file mode 100644 index 000000000..c59fc2544 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/networkfirewall/aliases @@ -0,0 +1,9 @@ +cloud/aws +# reason: slow +unsupported + +# Takes around 45 minutes: deletion of resources has to be serial and is +# painfully slow +slow + +networkfirewall_info diff --git a/ansible_collections/community/aws/tests/integration/targets/networkfirewall/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/networkfirewall/defaults/main.yml new file mode 100644 index 000000000..d5fe55074 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/networkfirewall/defaults/main.yml @@ -0,0 +1,35 @@ +_resource_prefix: 'AnsibleTest-{{ tiny_prefix }}-NF' +firewall_name_prefix: '{{ _resource_prefix }}' +# +# _resource_prefix: 'AnsibleTest-STATIC-NF' +# firewall_name_prefix: 'AnsibleTest-{{ tiny_prefix }}-NF' + +firewall_name: '{{ firewall_name_prefix }}' + +cidr_prefix: '10.{{ 255 | random(seed=_resource_prefix) }}' +tgw_name: '{{ _resource_prefix }}' +tgw_name_2: '{{ _resource_prefix }}-2' +vpc_name_a: '{{ _resource_prefix }}-1' +vpc_name_b: '{{ _resource_prefix }}-2' +vpc_cidr_a: '{{ cidr_prefix }}.1.0/24' +vpc_cidr_b: '{{ cidr_prefix }}.2.0/24' + +subnet_cidr_a_1: '{{ cidr_prefix }}.1.0/26' +subnet_cidr_a_2: '{{ cidr_prefix }}.1.64/26' +subnet_cidr_a_3: '{{ cidr_prefix }}.1.128/26' +subnet_cidr_a_1a: '{{ cidr_prefix }}.1.192/26' +subnet_cidr_b_1: '{{ cidr_prefix }}.2.0/26' +subnet_cidr_b_2: '{{ cidr_prefix }}.2.64/26' + +subnet_name_a_1: '{{ _resource_prefix }}-a-1' +subnet_name_a_1a: '{{ _resource_prefix }}-a-1a' +subnet_name_a_2: '{{ _resource_prefix }}-a-2' +subnet_name_a_3: '{{ _resource_prefix }}-a-3' +subnet_name_b_1: '{{ _resource_prefix }}-b-1' +subnet_name_b_2: '{{ _resource_prefix }}-b-2' + +policy_name_prefix: '{{ _resource_prefix }}' +group_name_prefix: '{{ _resource_prefix }}' +default_policy_name: '{{ policy_name_prefix }}-DefaultOrder' +default_group_name: '{{ group_name_prefix }}-DefaultOrder' +rule_group_capacity: 100 diff --git a/ansible_collections/community/aws/tests/integration/targets/networkfirewall/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/networkfirewall/meta/main.yml new file mode 100644 index 000000000..aef5ca0ee --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/networkfirewall/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - role: setup_ec2_facts diff --git a/ansible_collections/community/aws/tests/integration/targets/networkfirewall/tasks/cleanup.yml b/ansible_collections/community/aws/tests/integration/targets/networkfirewall/tasks/cleanup.yml new file mode 100644 index 000000000..b5797fb7e --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/networkfirewall/tasks/cleanup.yml @@ -0,0 +1,123 @@ +--- +# +# We can't start deleting the other resources until the firewalls have finished +# detatching from the subnets +# +- name: 'Fetch all firewalls' + networkfirewall_info: {} + register: account_firewalls_info + ignore_errors: true + +- name: 'Get a list of all firewalls matching {{ firewall_name_prefix }}' + set_fact: + matching_firewalls: '{{ account_firewalls_info.firewall_list | select("search", firewall_name_prefix) | list }}' + ignore_errors: true + +- name: 'Delete matching firewalls' + networkfirewall: + arn: '{{ item }}' + state: absent + delete_protection: False + wait: True + ignore_errors: true + loop: '{{ matching_firewalls }}' + +# +# Policies can take a while to delete, but we can start the process, trigger the +# deletion of the Subnets/VPC and then come back and delete the Rule Groups +# +- name: 'Fetch all policies' + networkfirewall_policy_info: {} + register: account_policies_info + ignore_errors: true + +- name: 'Get a list of all rules matching {{ policy_name_prefix }}' + set_fact: + matching_policies: '{{ account_policies_info.policy_list | select("search", policy_name_prefix) | list }}' + ignore_errors: true + +- name: 'Delete matching policies' + networkfirewall_policy: + arn: '{{ item }}' + state: absent + wait: False + ignore_errors: true + loop: '{{ matching_policies }}' + +- name: 'Start deletion of subnets' + ec2_vpc_subnet: + state: absent + cidr: '{{ item.cidr }}' + vpc_id: '{{ item.vpc_id }}' + wait: False + loop: + - cidr: '{{ subnet_cidr_a_1 }}' + vpc_id: '{{ vpc_id_a }}' + - cidr: '{{ subnet_cidr_a_2 }}' + vpc_id: '{{ vpc_id_a }}' + - cidr: '{{ subnet_cidr_a_3 }}' + vpc_id: '{{ vpc_id_a }}' + - cidr: '{{ subnet_cidr_b_1 }}' + vpc_id: '{{ vpc_id_b }}' + - cidr: '{{ subnet_cidr_b_2 }}' + vpc_id: '{{ vpc_id_b }}' + - cidr: '{{ subnet_cidr_a_1a }}' + vpc_id: '{{ vpc_id_a }}' + ignore_errors: True + +- name: 'Wait for deletion of subnets' + ec2_vpc_subnet: + state: absent + cidr: '{{ item.cidr }}' + vpc_id: '{{ item.vpc_id }}' + wait: True + loop: + - cidr: '{{ subnet_cidr_a_1 }}' + vpc_id: '{{ vpc_id_a }}' + - cidr: '{{ subnet_cidr_a_2 }}' + vpc_id: '{{ vpc_id_a }}' + - cidr: '{{ subnet_cidr_a_3 }}' + vpc_id: '{{ vpc_id_a }}' + - cidr: '{{ subnet_cidr_b_1 }}' + vpc_id: '{{ vpc_id_b }}' + - cidr: '{{ subnet_cidr_b_2 }}' + vpc_id: '{{ vpc_id_b }}' + - cidr: '{{ subnet_cidr_a_1a }}' + vpc_id: '{{ vpc_id_a }}' + ignore_errors: True + +- name: 'Delete VPCs' + ec2_vpc_net: + state: absent + cidr_block: '{{ item.cidr }}' + name: '{{ item.name }}' + loop: + - cidr: '{{ vpc_cidr_a }}' + name: '{{ vpc_name_a }}' + - cidr: '{{ vpc_cidr_b }}' + name: '{{ vpc_name_b }}' + ignore_errors: True + +- name: 'Wait for policies to finish deletion' + networkfirewall_policy: + arn: '{{ item }}' + state: absent + ignore_errors: true + loop: '{{ matching_policies }}' + +- name: 'Fetch all account rule groups' + networkfirewall_rule_group_info: {} + register: account_rules_info + ignore_errors: true + +- name: 'Get a list of all rules matching {{ group_name_prefix }}' + set_fact: + matching_rules: '{{ account_rules_info.rule_list | select("search", group_name_prefix) | list }}' + ignore_errors: true + +- name: 'Delete matching rule groups' + networkfirewall_rule_group: + arn: '{{ item }}' + state: absent + ignore_errors: true + loop: '{{ matching_rules }}' diff --git a/ansible_collections/community/aws/tests/integration/targets/networkfirewall/tasks/complex.yml b/ansible_collections/community/aws/tests/integration/targets/networkfirewall/tasks/complex.yml new file mode 100644 index 000000000..ea8378cc9 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/networkfirewall/tasks/complex.yml @@ -0,0 +1,612 @@ +--- +# Tests the manipulation of networkfirewall resources one option at a time + +- vars: + complex_firewall_name: '{{ firewall_name_prefix }}-Complex' + first_tags: + CamelCaseKey: CamelCaseValue + 'Key with Spaces': Value with spaces + pascalCaseKey: pascalCaseValue + snake_case_key: snake_case_value + second_tags: + CamelCaseKey: CamelCaseValue + 'Key with Spaces': Updated Value with spaces + NewCamelCaseKey: CamelCaseValue + 'New Key with Spaces': Value with spaces + newPascalCaseKey: pascalCaseValue + new_snake_case_key: snake_case_value + pascalCaseKey: pascalCaseValue + snake_case_key: snake_case_value + block: + ################################################################### + # Creation + + - name: '(CHECK) Create a complex firewall' + check_mode: True + networkfirewall: + name: '{{ complex_firewall_name }}' + state: present + delete_protection: True + description: "An example Description" + policy: '{{ default_policy_names[0] }}' + policy_change_protection: True + subnets: + - '{{ subnet_id_a_1 }}' + - '{{ subnet_id_a_2 }}' + subnet_change_protection: True + tags: '{{ first_tags }}' + register: complex_firewall + + - assert: + that: + - complex_firewall is changed + - '"firewall" in complex_firewall' + - '"firewall" in complex_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_metadata" in complex_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == true + - firewall_data.description == 'An example Description' + - firewall_data.firewall_name == complex_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == true + - firewall_data.subnet_change_protection == true + - firewall_data.subnet_mappings | length == 2 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - subnet_id_a_2 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == first_tags + vars: + firewall_data: '{{ complex_firewall.firewall.firewall }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: 'Create a complex firewall' + networkfirewall: + name: '{{ complex_firewall_name }}' + state: present + delete_protection: True + description: "An example Description" + policy: '{{ default_policy_names[0] }}' + policy_change_protection: True + subnets: + - '{{ subnet_id_a_1 }}' + - '{{ subnet_id_a_2 }}' + subnet_change_protection: True + tags: '{{ first_tags }}' + register: complex_firewall + + - assert: + that: + - complex_firewall is changed + - '"firewall" in complex_firewall' + - '"firewall" in complex_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in complex_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == true + - firewall_data.description == 'An example Description' + - firewall_data.firewall_arn.startswith(account_arn) + - firewall_data.firewall_arn.endswith(complex_firewall_name) + - firewall_data.firewall_name == complex_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == true + - firewall_data.subnet_change_protection == true + - firewall_data.subnet_mappings | length == 2 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - subnet_id_a_2 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == first_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.status == "READY" + - firewall_metadata.configuration_sync_state_summary == "IN_SYNC" + vars: + firewall_data: '{{ complex_firewall.firewall.firewall }}' + firewall_metadata: '{{ complex_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: 'Save Policy ID/ARN for later' + set_fact: + complex_firewall_id: '{{ firewall_data.firewall_id }}' + complex_firewall_arn: '{{ firewall_data.firewall_arn }}' + vars: + firewall_data: '{{ complex_firewall.firewall.firewall }}' + + - name: '(CHECK) Create a complex firewall (idempotency)' + check_mode: True + networkfirewall: + name: '{{ complex_firewall_name }}' + state: present + delete_protection: True + description: "An example Description" + policy: '{{ default_policy_names[0] }}' + policy_change_protection: True + subnets: + - '{{ subnet_id_a_1 }}' + - '{{ subnet_id_a_2 }}' + subnet_change_protection: True + tags: '{{ first_tags }}' + register: complex_firewall + + - assert: + that: + - complex_firewall is not changed + - '"firewall" in complex_firewall' + - '"firewall" in complex_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in complex_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == true + - firewall_data.description == 'An example Description' + - firewall_data.firewall_arn == complex_firewall_arn + - firewall_data.firewall_id == complex_firewall_id + - firewall_data.firewall_name == complex_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == true + - firewall_data.subnet_change_protection == true + - firewall_data.subnet_mappings | length == 2 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - subnet_id_a_2 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == first_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.status == "READY" + - firewall_metadata.configuration_sync_state_summary == "IN_SYNC" + vars: + firewall_data: '{{ complex_firewall.firewall.firewall }}' + firewall_metadata: '{{ complex_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: 'Create a complex firewall (idempotency)' + networkfirewall: + name: '{{ complex_firewall_name }}' + state: present + delete_protection: True + description: "An example Description" + policy: '{{ default_policy_names[0] }}' + policy_change_protection: True + subnets: + - '{{ subnet_id_a_1 }}' + - '{{ subnet_id_a_2 }}' + subnet_change_protection: True + tags: '{{ first_tags }}' + register: complex_firewall + + - assert: + that: + - complex_firewall is not changed + - '"firewall" in complex_firewall' + - '"firewall" in complex_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in complex_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == true + - firewall_data.description == 'An example Description' + - firewall_data.firewall_arn == complex_firewall_arn + - firewall_data.firewall_id == complex_firewall_id + - firewall_data.firewall_name == complex_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == true + - firewall_data.subnet_change_protection == true + - firewall_data.subnet_mappings | length == 2 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - subnet_id_a_2 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == first_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.status == "READY" + - firewall_metadata.configuration_sync_state_summary == "IN_SYNC" + vars: + firewall_data: '{{ complex_firewall.firewall.firewall }}' + firewall_metadata: '{{ complex_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + ################################################################### + # Update + + - name: '(CHECK) Update a complex firewall' + check_mode: True + networkfirewall: + name: '{{ complex_firewall_name }}' + state: present + description: "An updated Description" + policy: '{{ default_policy_arns[2] }}' + policy_change_protection: False + subnet_change_protection: False + subnets: + - '{{ subnet_id_a_1a }}' + - '{{ subnet_id_a_2 }}' + - '{{ subnet_id_a_3 }}' + tags: '{{ second_tags }}' + register: complex_firewall + + - assert: + that: + - complex_firewall is changed + - '"firewall" in complex_firewall' + - '"firewall" in complex_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in complex_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == true + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == complex_firewall_arn + - firewall_data.firewall_id == complex_firewall_id + - firewall_data.firewall_name == complex_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[2] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 3 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1a in subnet_ids + - subnet_id_a_2 in subnet_ids + - subnet_id_a_3 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == second_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.status == "READY" + - firewall_metadata.configuration_sync_state_summary == "IN_SYNC" + vars: + firewall_data: '{{ complex_firewall.firewall.firewall }}' + firewall_metadata: '{{ complex_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: 'Update a complex firewall' + networkfirewall: + name: '{{ complex_firewall_name }}' + state: present + description: "An updated Description" + policy: '{{ default_policy_arns[2] }}' + policy_change_protection: False + subnet_change_protection: False + subnets: + - '{{ subnet_id_a_1a }}' + - '{{ subnet_id_a_2 }}' + - '{{ subnet_id_a_3 }}' + tags: '{{ second_tags }}' + register: complex_firewall + + - assert: + that: + - complex_firewall is changed + - '"firewall" in complex_firewall' + - '"firewall" in complex_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in complex_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == true + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == complex_firewall_arn + - firewall_data.firewall_id == complex_firewall_id + - firewall_data.firewall_name == complex_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[2] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 3 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1a in subnet_ids + - subnet_id_a_2 in subnet_ids + - subnet_id_a_3 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == second_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.status == "READY" + - firewall_metadata.configuration_sync_state_summary == "IN_SYNC" + vars: + firewall_data: '{{ complex_firewall.firewall.firewall }}' + firewall_metadata: '{{ complex_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + # There's a delay of a couple of seconds between requesting a change and the + # firewall entering a PENDING/PROVISIONING state. The delay is enough to + # let the firewall enter the expected PENDING state is it's going to. + - name: 'Pause to let synchroization start' + pause: + seconds: 5 + + # Also checks that we get the info we expect + - name: 'Check firewall is not still updating' + networkfirewall_info: + arn: '{{ complex_firewall_arn }}' + register: complex_firewall_info + + - assert: + that: + - complex_firewall_info is successful + - complex_firewall_info.firewalls | length == 1 + - '"firewalls" in complex_firewall_info' + - '"firewall" in complex_firewall_info.firewalls[0]' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in complex_firewall_info.firewalls[0]' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == true + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == complex_firewall_arn + - firewall_data.firewall_id == complex_firewall_id + - firewall_data.firewall_name == complex_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[2] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 3 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1a in subnet_ids + - subnet_id_a_2 in subnet_ids + - subnet_id_a_3 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == second_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.status == "READY" + - firewall_metadata.configuration_sync_state_summary == "IN_SYNC" + vars: + firewall_data: '{{ complex_firewall_info.firewalls[0].firewall }}' + firewall_metadata: '{{ complex_firewall_info.firewalls[0].firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: '(CHECK) Update a complex firewall (idempotency)' + check_mode: True + networkfirewall: + name: '{{ complex_firewall_name }}' + state: present + description: "An updated Description" + policy: '{{ default_policy_arns[2] }}' + policy_change_protection: False + subnet_change_protection: False + subnets: + - '{{ subnet_id_a_1a }}' + - '{{ subnet_id_a_2 }}' + - '{{ subnet_id_a_3 }}' + tags: '{{ second_tags }}' + register: complex_firewall + + - assert: + that: + - complex_firewall is not changed + - '"firewall" in complex_firewall' + - '"firewall" in complex_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in complex_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == true + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == complex_firewall_arn + - firewall_data.firewall_id == complex_firewall_id + - firewall_data.firewall_name == complex_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[2] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 3 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1a in subnet_ids + - subnet_id_a_2 in subnet_ids + - subnet_id_a_3 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == second_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.status == "READY" + - firewall_metadata.configuration_sync_state_summary == "IN_SYNC" + vars: + firewall_data: '{{ complex_firewall.firewall.firewall }}' + firewall_metadata: '{{ complex_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: 'Update a complex firewall (idempotency)' + networkfirewall: + name: '{{ complex_firewall_name }}' + state: present + description: "An updated Description" + policy: '{{ default_policy_arns[2] }}' + policy_change_protection: False + subnet_change_protection: False + subnets: + - '{{ subnet_id_a_1a }}' + - '{{ subnet_id_a_2 }}' + - '{{ subnet_id_a_3 }}' + tags: '{{ second_tags }}' + register: complex_firewall + + - assert: + that: + - complex_firewall is not changed + - '"firewall" in complex_firewall' + - '"firewall" in complex_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in complex_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == true + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == complex_firewall_arn + - firewall_data.firewall_id == complex_firewall_id + - firewall_data.firewall_name == complex_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[2] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 3 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1a in subnet_ids + - subnet_id_a_2 in subnet_ids + - subnet_id_a_3 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == second_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.status == "READY" + - firewall_metadata.configuration_sync_state_summary == "IN_SYNC" + vars: + firewall_data: '{{ complex_firewall.firewall.firewall }}' + firewall_metadata: '{{ complex_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + + ################################################################### + # Delete firewall + # + # This is *very* slow, so we don't wait on this one. The simple test will + # check that waiting for deletion behaves properly. What we are however + # checking is that deletion protection updates happen prior to attempting to + # delete the firewall. + + - name: '(CHECK) Delete complex firewall - no wait' + check_mode: True + networkfirewall: + name: '{{ complex_firewall_name }}' + delete_protection: False + state: absent + wait: False + register: complex_firewall + + - name: 'Delete complex firewall - no wait' + networkfirewall: + name: '{{ complex_firewall_name }}' + delete_protection: False + state: absent + wait: False + register: complex_firewall + + - assert: + that: + - complex_firewall is changed + + # We need to wait for the state to change + - pause: + seconds: 4 + + - name: 'Check firewall is not gone (expected to be in a pending state)' + networkfirewall_info: + arn: '{{ complex_firewall_arn }}' + register: complex_firewall_info + + - assert: + that: + - complex_firewall_info is successful + - complex_firewall_info.firewalls | length == 1 + + - name: '(CHECK) Delete complex firewall (idempotency)' + check_mode: True + networkfirewall: + name: '{{ complex_firewall_name }}' + delete_protection: False + state: absent + wait: False + register: complex_firewall + + - assert: + that: + - complex_firewall is not changed + + - name: 'Delete complex firewall (idempotency)' + networkfirewall: + name: '{{ complex_firewall_name }}' + delete_protection: False + state: absent + wait: False + register: complex_firewall + + - assert: + that: + - complex_firewall is not changed + + always: + - name: 'Cleanup complex firewall' + networkfirewall: + name: '{{ complex_firewall_name }}' + state: absent + delete_protection: False + wait: False + ignore_errors: True diff --git a/ansible_collections/community/aws/tests/integration/targets/networkfirewall/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/networkfirewall/tasks/main.yml new file mode 100644 index 000000000..6a77d4f93 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/networkfirewall/tasks/main.yml @@ -0,0 +1,35 @@ +--- +- name: 'networkfirewall integration tests' + collections: + - amazon.aws + - community.aws + module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + + block: + # Fetch some info about the account so we can build ARNs + - aws_caller_info: {} + register: caller_info + - name: 'Generate the ARN pattern to search for' + vars: + _caller_info: '{{ caller_info.arn.split(":") }}' + _base_arn: 'arn:{{_caller_info[1]}}:network-firewall:{{aws_region}}' + set_fact: + account_arn: '{{_base_arn}}:{{_caller_info[4]}}:firewall/' + + # Prepares various resources + - include_tasks: 'setup.yml' + + # Test manipulating multiple parameters at the same time + - include_tasks: 'complex.yml' + + # Test manipulating one parameter at a time + - include_tasks: 'simple.yml' + + always: # [] + # Cleanup after ourselves + - include_tasks: 'cleanup.yml' diff --git a/ansible_collections/community/aws/tests/integration/targets/networkfirewall/tasks/setup.yml b/ansible_collections/community/aws/tests/integration/targets/networkfirewall/tasks/setup.yml new file mode 100644 index 000000000..17f1dde88 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/networkfirewall/tasks/setup.yml @@ -0,0 +1,118 @@ +--- +# The simplest form of rule group +- name: 'Create Rule Groups with minimal settings (Default order)' + networkfirewall_rule_group: + name: '{{ default_group_name }}-{{ item }}' + type: 'stateful' + capacity: '{{ rule_group_capacity }}' + rule_strings: + - 'pass tcp any any -> any any (sid:1000001;)' + register: default_groups + loop: '{{ range(0,3,1) | list }}' + +# Store Group Names and ARNs for later +- set_fact: + default_group_names: >- + {{ default_groups.results + | map(attribute="rule_group") + | map(attribute="rule_group_metadata") + | map(attribute="rule_group_name") | list }} + default_group_arns: >- + {{ default_groups.results + | map(attribute="rule_group") + | map(attribute="rule_group_metadata") + | map(attribute="rule_group_arn") | list }} + +- name: 'Pick 2 AZs available for use' + set_fact: + subnet_az_a_1: '{{ ec2_availability_zone_names[0] }}' + subnet_az_a_1a: '{{ ec2_availability_zone_names[0] }}' + subnet_az_a_2: '{{ ec2_availability_zone_names[1] }}' + subnet_az_a_3: '{{ ec2_availability_zone_names[2] }}' + subnet_az_b_1: '{{ ec2_availability_zone_names[0] }}' + subnet_az_b_2: '{{ ec2_availability_zone_names[1] }}' + +- name: 'Create VPCs to attach to Firewall' + ec2_vpc_net: + cidr_block: '{{ item.cidr }}' + name: '{{ item.name }}' + loop: + - cidr: '{{ vpc_cidr_a }}' + name: '{{ vpc_name_a }}' + - cidr: '{{ vpc_cidr_b }}' + name: '{{ vpc_name_b }}' + register: create_vpcs + +- set_fact: + vpc_id_a: '{{ vpc_a.id }}' + vpc_id_b: '{{ vpc_b.id }}' + vpc_owner_a: '{{ vpc_a.owner_id }}' + vpc_owner_b: '{{ vpc_b.owner_id }}' + vars: + vpc_a: '{{ create_vpcs.results[0].vpc }}' + vpc_b: '{{ create_vpcs.results[1].vpc }}' + +- name: 'Create subnets' + ec2_vpc_subnet: + az: '{{ item.az }}' + cidr: '{{ item.cidr }}' + tags: + Name: '{{ item.name }}' + vpc_id: '{{ item.vpc_id }}' + loop: + - az: '{{ subnet_az_a_1 }}' + cidr: '{{ subnet_cidr_a_1 }}' + vpc_id: '{{ vpc_id_a }}' + name: '{{ subnet_name_a_1 }}' + - az: '{{ subnet_az_a_2 }}' + cidr: '{{ subnet_cidr_a_2 }}' + vpc_id: '{{ vpc_id_a }}' + name: '{{ subnet_name_a_2 }}' + - az: '{{ subnet_az_a_3 }}' + cidr: '{{ subnet_cidr_a_3 }}' + vpc_id: '{{ vpc_id_a }}' + name: '{{ subnet_name_a_3 }}' + - az: '{{ subnet_az_b_1 }}' + cidr: '{{ subnet_cidr_b_1 }}' + vpc_id: '{{ vpc_id_b }}' + name: '{{ subnet_name_b_1 }}' + - az: '{{ subnet_az_b_2 }}' + cidr: '{{ subnet_cidr_b_2 }}' + vpc_id: '{{ vpc_id_b }}' + name: '{{ subnet_name_b_2 }}' + - az: '{{ subnet_az_a_1a }}' + cidr: '{{ subnet_cidr_a_1a }}' + vpc_id: '{{ vpc_id_a }}' + name: '{{ subnet_name_a_1a }}' + register: create_subnets + +- set_fact: + subnet_id_a_1: '{{ create_subnets.results[0].subnet.id }}' + subnet_id_a_2: '{{ create_subnets.results[1].subnet.id }}' + subnet_id_a_3: '{{ create_subnets.results[2].subnet.id }}' + subnet_id_b_1: '{{ create_subnets.results[3].subnet.id }}' + subnet_id_b_2: '{{ create_subnets.results[4].subnet.id }}' + subnet_id_a_1a: '{{ create_subnets.results[5].subnet.id }}' + +- name: 'Create a simple firewall policy with `default` rule order' + networkfirewall_policy: + name: '{{ default_policy_name }}-{{ item }}' + stateful_rule_order: 'default' + stateful_rule_groups: + - '{{ default_group_names[(item)] }}' + state: present + register: default_policies + loop: '{{ range(0,3,1) | list }}' + +# Store Group Names and ARNs for later +- set_fact: + default_policy_names: >- + {{ default_policies.results + | map(attribute="policy") + | map(attribute="policy_metadata") + | map(attribute="firewall_policy_name") | list }} + default_policy_arns: >- + {{ default_policies.results + | map(attribute="policy") + | map(attribute="policy_metadata") + | map(attribute="firewall_policy_arn") | list }} diff --git a/ansible_collections/community/aws/tests/integration/targets/networkfirewall/tasks/simple.yml b/ansible_collections/community/aws/tests/integration/targets/networkfirewall/tasks/simple.yml new file mode 100644 index 000000000..271aff17d --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/networkfirewall/tasks/simple.yml @@ -0,0 +1,3893 @@ +--- +# Tests the manipulation of networkfirewall resources one option at a time + +- vars: + simple_firewall_name: '{{ firewall_name_prefix }}-Simple' + 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: + ################################################################### + # Creation + + - name: '(CHECK) Create a simple firewall with the minimum necessary parameters' + check_mode: True + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + policy: '{{ default_policy_names[0] }}' + subnets: + - '{{ subnet_id_a_1 }}' + register: simple_firewall + + - assert: + that: + - simple_firewall is changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.subnet_mappings + - firewall_data.vpc_id == vpc_id_a + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: 'Create a simple firewall with the minimum necessary parameters' + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + policy: '{{ default_policy_names[0] }}' + subnets: + - '{{ subnet_id_a_1 }}' + register: simple_firewall + + - assert: + that: + - simple_firewall is changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.firewall_arn.startswith(account_arn) + - firewall_data.firewall_arn.endswith(simple_firewall_name) + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == {} + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: 'Save Policy ID/ARN for later' + set_fact: + simple_firewall_id: '{{ firewall_data.firewall_id }}' + simple_firewall_arn: '{{ firewall_data.firewall_arn }}' + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + + - name: '(CHECK) Create a simple firewall with the minimum necessary parameters (idempotency)' + check_mode: True + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + policy: '{{ default_policy_names[0] }}' + subnets: + - '{{ subnet_id_a_1 }}' + register: simple_firewall + + - assert: + that: + - simple_firewall is not changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == {} + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: 'Create a simple firewall with the minimum necessary parameters (idempotency)' + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + policy: '{{ default_policy_names[0] }}' + subnets: + - '{{ subnet_id_a_1 }}' + register: simple_firewall + + - assert: + that: + - simple_firewall is not changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == {} + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + ### + + - name: '(CHECK) Create a simple firewall with the minimum necessary parameters - test reference by ARN (idempotency)' + check_mode: True + networkfirewall: + arn: '{{ simple_firewall_arn }}' + state: present + policy: '{{ default_policy_names[0] }}' + subnets: + - '{{ subnet_id_a_1 }}' + register: simple_firewall + + - assert: + that: + - simple_firewall is not changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == {} + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: 'Create a simple firewall with the minimum necessary parameters - test reference by ARN (idempotency)' + networkfirewall: + arn: '{{ simple_firewall_arn }}' + state: present + policy: '{{ default_policy_names[0] }}' + subnets: + - '{{ subnet_id_a_1 }}' + register: simple_firewall + + - assert: + that: + - simple_firewall is not changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == {} + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + ################################################################### + # Description + + - name: '(CHECK) Set Description' + check_mode: True + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + description: "An example Description" + register: simple_firewall + + - assert: + that: + - simple_firewall is changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.description == 'An example Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == {} + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: 'Set Description' + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + description: "An example Description" + register: simple_firewall + + - assert: + that: + - simple_firewall is changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.description == 'An example Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == {} + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: '(CHECK) Set Description (idempotency)' + check_mode: True + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + description: "An example Description" + register: simple_firewall + + - assert: + that: + - simple_firewall is not changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.description == 'An example Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == {} + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: 'Set Description (idempotency)' + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + description: "An example Description" + register: simple_firewall + + - assert: + that: + - simple_firewall is not changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.description == 'An example Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == {} + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + ### + + - name: '(CHECK) Update Description' + check_mode: True + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + description: "An updated Description" + register: simple_firewall + + - assert: + that: + - simple_firewall is changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == {} + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: 'Update Description' + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + description: "An updated Description" + register: simple_firewall + + - assert: + that: + - simple_firewall is changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == {} + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: '(CHECK) Update Description (idempotency)' + check_mode: True + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + description: "An updated Description" + register: simple_firewall + + - assert: + that: + - simple_firewall is not changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == {} + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: 'Update Description (idempotency)' + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + description: "An updated Description" + register: simple_firewall + + - assert: + that: + - simple_firewall is not changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == {} + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + ################################################################### + # Tags + + - name: '(CHECK) Set Tags' + check_mode: True + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + tags: '{{ first_tags }}' + register: simple_firewall + + - assert: + that: + - simple_firewall is changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == first_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: 'Set Tags' + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + tags: '{{ first_tags }}' + register: simple_firewall + + - assert: + that: + - simple_firewall is changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == first_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: '(CHECK) Set Tags (idempotency)' + check_mode: True + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + tags: '{{ first_tags }}' + register: simple_firewall + + - assert: + that: + - simple_firewall is not changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == first_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: 'Set Tags (idempotency)' + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + tags: '{{ first_tags }}' + register: simple_firewall + + - assert: + that: + - simple_firewall is not changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == first_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + ### + + - name: '(CHECK) Update Tags with purge' + check_mode: True + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + tags: '{{ second_tags }}' + register: simple_firewall + + - assert: + that: + - simple_firewall is changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == second_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: 'Update Tags with purge' + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + tags: '{{ second_tags }}' + register: simple_firewall + + - assert: + that: + - simple_firewall is changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == second_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: '(CHECK) Update Tags with purge (idempotency)' + check_mode: True + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + tags: '{{ second_tags }}' + register: simple_firewall + + - assert: + that: + - simple_firewall is not changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == second_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: 'Update Tags with purge (idempotency)' + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + tags: '{{ second_tags }}' + register: simple_firewall + + - assert: + that: + - simple_firewall is not changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == second_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + ### + + - name: '(CHECK) Update Tags without purge' + check_mode: True + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + tags: '{{ third_tags }}' + purge_tags: False + register: simple_firewall + + - assert: + that: + - simple_firewall is changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: 'Update Tags without purge' + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + tags: '{{ third_tags }}' + purge_tags: False + register: simple_firewall + + - assert: + that: + - simple_firewall is changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: '(CHECK) Update Tags without purge (idempotency)' + check_mode: True + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + tags: '{{ third_tags }}' + purge_tags: False + register: simple_firewall + + - assert: + that: + - simple_firewall is not changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: 'Update Tags without purge (idempotency)' + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + tags: '{{ third_tags }}' + purge_tags: False + register: simple_firewall + + - assert: + that: + - simple_firewall is not changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + ################################################################### + # Change Protection + + - name: '(CHECK) Enable deletion protection' + check_mode: True + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + delete_protection: True + register: simple_firewall + + - assert: + that: + - simple_firewall is changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == true + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: 'Enable deletion protection' + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + delete_protection: True + register: simple_firewall + + - assert: + that: + - simple_firewall is changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == true + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: '(CHECK) Enable deletion protection (idempotency)' + check_mode: True + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + delete_protection: True + register: simple_firewall + + - assert: + that: + - simple_firewall is not changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == true + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: 'Enable deletion protection (idempotency)' + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + delete_protection: True + register: simple_firewall + + - assert: + that: + - simple_firewall is not changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == true + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + ### + + - name: '(CHECK) Enable firewall policy change protection' + check_mode: True + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + policy_change_protection: True + register: simple_firewall + + - assert: + that: + - simple_firewall is changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == true + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == true + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: 'Enable firewall policy change protection' + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + policy_change_protection: True + register: simple_firewall + + - assert: + that: + - simple_firewall is changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == true + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == true + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: '(CHECK) Enable firewall policy change protection (idempotency)' + check_mode: True + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + policy_change_protection: True + register: simple_firewall + + - assert: + that: + - simple_firewall is not changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == true + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == true + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: 'Enable firewall policy change protection (idempotency)' + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + policy_change_protection: True + register: simple_firewall + + - assert: + that: + - simple_firewall is not changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == true + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == true + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + ### + + - name: '(CHECK) Enable subnet change protection' + check_mode: True + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + subnet_change_protection: True + register: simple_firewall + + - assert: + that: + - simple_firewall is changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == true + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == true + - firewall_data.subnet_change_protection == true + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: 'Enable subnet change protection' + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + subnet_change_protection: True + register: simple_firewall + + - assert: + that: + - simple_firewall is changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == true + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == true + - firewall_data.subnet_change_protection == true + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: '(CHECK) Enable subnet change protection (idempotency)' + check_mode: True + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + subnet_change_protection: True + register: simple_firewall + + - assert: + that: + - simple_firewall is not changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == true + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == true + - firewall_data.subnet_change_protection == true + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: 'Enable subnet change protection (idempotency)' + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + subnet_change_protection: True + register: simple_firewall + + - assert: + that: + - simple_firewall is not changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == true + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == true + - firewall_data.subnet_change_protection == true + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + ### + + - name: '(CHECK) Disable firewall policy change protection' + check_mode: True + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + policy_change_protection: False + register: simple_firewall + + - assert: + that: + - simple_firewall is changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == true + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == true + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: 'Disable firewall policy change protection' + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + policy_change_protection: False + register: simple_firewall + + - assert: + that: + - simple_firewall is changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == true + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == true + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: '(CHECK) Disable firewall policy change protection (idempotency)' + check_mode: True + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + policy_change_protection: False + register: simple_firewall + + - assert: + that: + - simple_firewall is not changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == true + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == true + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: 'Disable firewall policy change protection (idempotency)' + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + policy_change_protection: False + register: simple_firewall + + - assert: + that: + - simple_firewall is not changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == true + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == true + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + ### + + - name: '(CHECK) Disable subnet change protection' + check_mode: True + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + subnet_change_protection: False + register: simple_firewall + + - assert: + that: + - simple_firewall is changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == true + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: 'Disable subnet change protection' + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + subnet_change_protection: False + register: simple_firewall + + - assert: + that: + - simple_firewall is changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == true + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: '(CHECK) Disable subnet change protection (idempotency)' + check_mode: True + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + subnet_change_protection: False + register: simple_firewall + + - assert: + that: + - simple_firewall is not changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == true + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: 'Disable subnet change protection (idempotency)' + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + subnet_change_protection: False + register: simple_firewall + + - assert: + that: + - simple_firewall is not changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == true + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + ### + + - name: '(CHECK) Disable deletion protection' + check_mode: True + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + delete_protection: False + register: simple_firewall + + - assert: + that: + - simple_firewall is changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: 'Disable deletion protection' + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + delete_protection: False + register: simple_firewall + + - assert: + that: + - simple_firewall is changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: '(CHECK) Disable deletion protection (idempotency)' + check_mode: True + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + delete_protection: False + register: simple_firewall + + - assert: + that: + - simple_firewall is not changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: 'Disable deletion protection (idempotency)' + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + delete_protection: False + register: simple_firewall + + - assert: + that: + - simple_firewall is not changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[0] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + ################################################################### + # Change Firewall policy + + - name: '(CHECK) Update policy assigned to firewall - without waiting' + check_mode: True + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + wait: false + policy: '{{ default_policy_names[1] }}' + register: simple_firewall + + - assert: + that: + - simple_firewall is changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[1] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: 'Update policy assigned to firewall - without waiting' + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + wait: false + policy: '{{ default_policy_names[1] }}' + register: simple_firewall + + # There's a delay of a couple of seconds between requesting a change and the + # firewall entering a PENDING/PROVISIONING state. The delay is enough to + # let the firewall enter the expected PENDING state + - name: 'Pause to let synchroization start' + pause: + seconds: 5 + + - name: 'Check firewall state' + networkfirewall_info: + arn: '{{ simple_firewall_arn }}' + register: simple_firewall_info + + - assert: + that: + - simple_firewall is changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[1] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + # Because we're not waiting, this might not happen before we returned, + # so we check using _info a couple of seconds later + - simple_firewall_info.firewalls[0].firewall_metadata.configuration_sync_state_summary == 'PENDING' + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: '(CHECK) Update policy assigned to firewall - without waiting (idempotency)' + check_mode: True + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + wait: false + policy: '{{ default_policy_names[1] }}' + register: simple_firewall + + - assert: + that: + - simple_firewall is not changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[1] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.status == 'READY' + - firewall_metadata.configuration_sync_state_summary == 'PENDING' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: 'Update policy assigned to firewall - without waiting (idempotency)' + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + wait: false + policy: '{{ default_policy_names[1] }}' + register: simple_firewall + + - assert: + that: + - simple_firewall is not changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[1] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.status == 'READY' + - firewall_metadata.configuration_sync_state_summary == 'PENDING' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + ### + + - name: '(CHECK) Update policy assigned to firewall - without waiting - by arn (idempotency)' + check_mode: True + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + wait: false + policy: '{{ default_policy_arns[1] }}' + register: simple_firewall + + - assert: + that: + - simple_firewall is not changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[1] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.status == 'READY' + - firewall_metadata.configuration_sync_state_summary == 'PENDING' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: 'Update policy assigned to firewall - without waiting (idempotency)' + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + wait: false + policy: '{{ default_policy_arns[1] }}' + register: simple_firewall + + - assert: + that: + - simple_firewall is not changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[1] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.status == 'READY' + - firewall_metadata.configuration_sync_state_summary == 'PENDING' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + ### + + - name: '(CHECK) Update policy assigned to firewall - waiting' + check_mode: True + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + policy: '{{ default_policy_arns[2] }}' + register: simple_firewall + + - assert: + that: + - simple_firewall is changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[2] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.status == 'READY' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: 'Update policy assigned to firewall - waiting' + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + policy: '{{ default_policy_arns[2] }}' + register: simple_firewall + + # There's a delay of a couple of seconds between requesting a change and the + # firewall entering a PENDING/PROVISIONING state. The delay is enough to + # let the firewall enter the PENDING state if we didn't wait (because we're + # waiting, we should be back to IN_SYNC. + - name: 'Pause to let synchroization start' + pause: + seconds: 5 + + - name: 'Check firewall state' + networkfirewall_info: + arn: '{{ simple_firewall_arn }}' + register: simple_firewall_info + + - assert: + that: + - simple_firewall is changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[2] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.status == 'READY' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - subnet_az_a_1 in firewall_metadata.sync_states + - simple_firewall_info.firewalls[0].firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: '(CHECK) Update policy assigned to firewall - waiting (idempotency)' + check_mode: True + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + policy: '{{ default_policy_names[2] }}' + register: simple_firewall + + - assert: + that: + - simple_firewall is not changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[2] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.status == 'READY' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: 'Update policy assigned to firewall - waiting (idempotency)' + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + policy: '{{ default_policy_names[2] }}' + register: simple_firewall + + - assert: + that: + - simple_firewall is not changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[2] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.status == 'READY' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + ### + + - name: '(CHECK) Update policy assigned to firewall - waiting - by name (idempotency)' + check_mode: True + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + policy: '{{ default_policy_names[2] }}' + register: simple_firewall + + - assert: + that: + - simple_firewall is not changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[2] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.status == 'READY' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: 'Update policy assigned to firewall - waiting - by name (idempotency)' + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + policy: '{{ default_policy_arns[2] }}' + register: simple_firewall + + - assert: + that: + - simple_firewall is not changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[2] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 1 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + - firewall_metadata.status == 'READY' + - firewall_metadata.configuration_sync_state_summary == 'IN_SYNC' + - subnet_az_a_1 in firewall_metadata.sync_states + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + ################################################################### + # Change Subnets + + - name: '(CHECK) Update subnets - duplicate AZ (FAIL)' + check_mode: True + ignore_errors: True + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + subnets: + - '{{ subnet_id_a_1a }}' + purge_subnets: false + register: simple_firewall + + - assert: + that: + - simple_firewall is failed + + - name: 'Update subnets - duplicate AZ (FAIL)' + ignore_errors: True + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + subnets: + - '{{ subnet_id_a_1a }}' + purge_subnets: false + register: simple_firewall + + - assert: + that: + - simple_firewall is failed + + ### + + - name: '(CHECK) Update subnets - Different VPC (FAIL)' + check_mode: True + ignore_errors: True + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + subnets: + - '{{ subnet_id_b_2 }}' + purge_subnets: false + register: simple_firewall + + - assert: + that: + - simple_firewall is failed + + - name: 'Update subnets - Different VPC (FAIL)' + ignore_errors: True + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + subnets: + - '{{ subnet_id_b_2 }}' + purge_subnets: false + register: simple_firewall + + - assert: + that: + - simple_firewall is failed + + ### + + - name: '(CHECK) Update subnets - Different VPC with purge (FAIL)' + check_mode: True + ignore_errors: True + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + subnets: + - '{{ subnet_id_b_2 }}' + purge_subnets: true + register: simple_firewall + + - assert: + that: + - simple_firewall is failed + + - name: 'Update subnets - Different VPC with purge (FAIL)' + ignore_errors: True + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + subnets: + - '{{ subnet_id_b_2 }}' + purge_subnets: true + register: simple_firewall + + - assert: + that: + - simple_firewall is failed + + ### + + - name: '(CHECK) Update subnets - without purge' + check_mode: True + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + subnets: + - '{{ subnet_id_a_2 }}' + purge_subnets: false + wait: false + register: simple_firewall + + - assert: + that: + - simple_firewall is changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[2] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 2 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - subnet_id_a_2 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + # We're not waiting, so we can't assert anything about the sync states + # in the metadata + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: 'Update subnets - without purge' + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + subnets: + - '{{ subnet_id_a_2 }}' + purge_subnets: false + wait: false + register: simple_firewall + + - assert: + that: + - simple_firewall is changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[2] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 2 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - subnet_id_a_2 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + # We're not waiting, so we can't assert anything about the sync states + # in the metadata + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: '(CHECK) Update subnets - without purge (idempotency)' + check_mode: True + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + subnets: + - '{{ subnet_id_a_2 }}' + purge_subnets: false + wait: false + register: simple_firewall + + - assert: + that: + - simple_firewall is not changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[2] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 2 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - subnet_id_a_2 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + # We're not waiting, so we can't assert anything about the sync states + # in the metadata + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: 'Update subnets - without purge (idempotency)' + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + subnets: + - '{{ subnet_id_a_2 }}' + purge_subnets: false + wait: false + register: simple_firewall + + - assert: + that: + - simple_firewall is not changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[2] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 2 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1 in subnet_ids + - subnet_id_a_2 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + # We're not waiting, so we can't assert anything about the sync states + # in the metadata + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + ### + + - name: '(CHECK) Update subnets - with purge' + check_mode: True + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + subnets: + - '{{ subnet_id_a_2 }}' + - '{{ subnet_id_a_3 }}' + purge_subnets: true + wait: false + register: simple_firewall + + - assert: + that: + - simple_firewall is changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[2] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 2 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_2 in subnet_ids + - subnet_id_a_3 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + # We're not waiting, so we can't assert anything about the sync states + # in the metadata + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: 'Update subnets - with purge' + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + subnets: + - '{{ subnet_id_a_2 }}' + - '{{ subnet_id_a_3 }}' + purge_subnets: true + wait: false + register: simple_firewall + + - assert: + that: + - simple_firewall is changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[2] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 2 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_2 in subnet_ids + - subnet_id_a_3 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + # We're not waiting, so we can't assert anything about the sync states + # in the metadata + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: '(CHECK) Update subnets - with purge (idempotency)' + check_mode: True + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + subnets: + - '{{ subnet_id_a_2 }}' + - '{{ subnet_id_a_3 }}' + purge_subnets: true + wait: false + register: simple_firewall + + - assert: + that: + - simple_firewall is not changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[2] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 2 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_2 in subnet_ids + - subnet_id_a_3 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + # We're not waiting, so we can't assert anything about the sync states + # in the metadata + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: 'Update subnets - with purge (idempotency)' + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + subnets: + - '{{ subnet_id_a_2 }}' + - '{{ subnet_id_a_3 }}' + purge_subnets: true + wait: false + register: simple_firewall + + - assert: + that: + - simple_firewall is not changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[2] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 2 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_2 in subnet_ids + - subnet_id_a_3 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + # We're not waiting, so we can't assert anything about the sync states + # in the metadata + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + ### + + - name: '(CHECK) Update subnets - with wait' + check_mode: True + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + subnets: + - '{{ subnet_id_a_1a }}' + - '{{ subnet_id_a_2 }}' + - '{{ subnet_id_a_3 }}' + wait: true + register: simple_firewall + + - assert: + that: + - simple_firewall is changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[2] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 3 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1a in subnet_ids + - subnet_id_a_2 in subnet_ids + - subnet_id_a_3 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: 'Update subnets - with wait' + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + subnets: + - '{{ subnet_id_a_1a }}' + - '{{ subnet_id_a_2 }}' + - '{{ subnet_id_a_3 }}' + wait: true + register: simple_firewall + + - assert: + that: + - simple_firewall is changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[2] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 3 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1a in subnet_ids + - subnet_id_a_2 in subnet_ids + - subnet_id_a_3 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: '(CHECK) Update subnets - with wait (idempotency)' + check_mode: True + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + subnets: + - '{{ subnet_id_a_1a }}' + - '{{ subnet_id_a_2 }}' + - '{{ subnet_id_a_3 }}' + wait: true + register: simple_firewall + + - assert: + that: + - simple_firewall is not changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[2] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 3 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1a in subnet_ids + - subnet_id_a_2 in subnet_ids + - subnet_id_a_3 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + - name: 'Update subnets - with wait (idempotency)' + networkfirewall: + name: '{{ simple_firewall_name }}' + state: present + subnets: + - '{{ subnet_id_a_1a }}' + - '{{ subnet_id_a_2 }}' + - '{{ subnet_id_a_3 }}' + wait: true + register: simple_firewall + + - assert: + that: + - simple_firewall is not changed + - '"firewall" in simple_firewall' + - '"firewall" in simple_firewall.firewall' + - '"delete_protection" in firewall_data' + - '"description" in firewall_data' + - '"firewall_arn" in firewall_data' + - '"firewall_id" in firewall_data' + - '"firewall_metadata" in simple_firewall.firewall' + - '"firewall_name" in firewall_data' + - '"firewall_policy_arn" in firewall_data' + - '"firewall_policy_change_protection" in firewall_data' + - '"subnet_change_protection" in firewall_data' + - '"subnet_mappings" in firewall_data' + - '"tags" in firewall_data' + - '"vpc_id" in firewall_data' + - firewall_data.delete_protection == false + - firewall_data.description == 'An updated Description' + - firewall_data.firewall_arn == simple_firewall_arn + - firewall_data.firewall_id == simple_firewall_id + - firewall_data.firewall_name == simple_firewall_name + - firewall_data.firewall_policy_arn == default_policy_arns[2] + - firewall_data.firewall_policy_change_protection == false + - firewall_data.subnet_change_protection == false + - firewall_data.subnet_mappings | length == 3 + - '"subnet_id" in firewall_data.subnet_mappings[0]' + - subnet_id_a_1a in subnet_ids + - subnet_id_a_2 in subnet_ids + - subnet_id_a_3 in subnet_ids + - firewall_data.vpc_id == vpc_id_a + - firewall_data.tags == final_tags + - '"configuration_sync_state_summary" in firewall_metadata' + - '"status" in firewall_metadata' + - '"sync_states" in firewall_metadata' + vars: + firewall_data: '{{ simple_firewall.firewall.firewall }}' + firewall_metadata: '{{ simple_firewall.firewall.firewall_metadata }}' + subnet_ids: '{{ firewall_data.subnet_mappings | map(attribute="subnet_id") | list }}' + + ################################################################### + # Delete firewall + + - name: '(CHECK) Delete firewall' + check_mode: True + networkfirewall: + name: '{{ simple_firewall_name }}' + state: absent + register: simple_firewall + + - name: 'Check firewall state' + networkfirewall_info: + arn: '{{ simple_firewall_arn }}' + register: simple_firewall_info + + - assert: + that: + - simple_firewall is changed + - simple_firewall_info.firewalls[0].firewall_metadata.status == 'READY' + + - name: 'Delete firewall' + networkfirewall: + name: '{{ simple_firewall_name }}' + state: absent + register: simple_firewall + + - assert: + that: + - simple_firewall is changed + + - name: 'Check firewall is gone' + networkfirewall_info: + arn: '{{ simple_firewall_arn }}' + register: simple_firewall_info + + - assert: + that: + - simple_firewall_info is successful + - simple_firewall_info.firewalls | length == 0 + + - name: '(CHECK) Delete firewall (idempotency)' + check_mode: True + networkfirewall: + name: '{{ simple_firewall_name }}' + state: absent + register: simple_firewall + + - assert: + that: + - simple_firewall is not changed + + - name: 'Delete firewall (idempotency)' + networkfirewall: + name: '{{ simple_firewall_name }}' + state: absent + register: simple_firewall + + - assert: + that: + - simple_firewall is not changed + + always: + - name: 'Cleanup simple firewall' + networkfirewall: + name: '{{ simple_firewall_name }}' + state: absent + delete_protection: False + wait: False + ignore_errors: True diff --git a/ansible_collections/community/aws/tests/integration/targets/networkfirewall_policy/aliases b/ansible_collections/community/aws/tests/integration/targets/networkfirewall_policy/aliases new file mode 100644 index 000000000..ac8b51aa9 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/networkfirewall_policy/aliases @@ -0,0 +1,4 @@ +time=10m +cloud/aws + +networkfirewall_policy_info diff --git a/ansible_collections/community/aws/tests/integration/targets/networkfirewall_policy/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/networkfirewall_policy/defaults/main.yml new file mode 100644 index 000000000..48eaabdcc --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/networkfirewall_policy/defaults/main.yml @@ -0,0 +1,6 @@ +--- +policy_name_prefix: 'AnsibleTest-{{ tiny_prefix }}' +group_name_prefix: 'AnsibleTest-{{ tiny_prefix }}' +strict_group_name: '{{ group_name_prefix }}-StrictOrder' +default_group_name: '{{ group_name_prefix }}-DefaultOrder' +rule_group_capacity: 100 diff --git a/ansible_collections/community/aws/tests/integration/targets/networkfirewall_policy/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/networkfirewall_policy/meta/main.yml new file mode 100644 index 000000000..f09ab4af1 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/networkfirewall_policy/meta/main.yml @@ -0,0 +1,4 @@ +dependencies: + - role: setup_botocore_pip + vars: + botocore_version: "1.23.23" diff --git a/ansible_collections/community/aws/tests/integration/targets/networkfirewall_policy/tasks/actions.yml b/ansible_collections/community/aws/tests/integration/targets/networkfirewall_policy/tasks/actions.yml new file mode 100644 index 000000000..ca2d34050 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/networkfirewall_policy/tasks/actions.yml @@ -0,0 +1,1607 @@ +--- +# Tests the basic manipulation of Default actions (including custom actions) +# +# In Scope +# - Updating custom actions for stateless flows. +# - Updating actions for stateless flows. + +- vars: + action_policy_name: '{{ policy_name_prefix }}-Actions' + block: + ################################################################### + # Creation + + - name: 'A - Create a simple firewall policy with `default` rule order' + networkfirewall_policy: + name: '{{ action_policy_name }}' + stateful_rule_order: 'default' + state: present + register: action_policy + + - assert: + that: + - action_policy is changed + + - name: 'A - Save Policy ID/ARN for later' + set_fact: + action_policy_id: '{{ policy_metadata.firewall_policy_id }}' + action_policy_arn: '{{ policy_metadata.firewall_policy_arn }}' + vars: + policy_metadata: '{{ action_policy.policy.policy_metadata }}' + + ################################################################### + # Custom action management + + - name: '(CHECK) A - Add a custom action' + check_mode: True + networkfirewall_policy: + name: '{{ action_policy_name }}' + state: present + stateless_custom_actions: + - name: CustomAction + publish_metric_dimension_values: 'CustomActionExample' + register: action_policy + + - assert: + that: + - action_policy is changed + - '"policy" in action_policy' + - '"policy" in action_policy.policy' + - '"policy_metadata" in action_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == action_policy_name + - policy_metadata.firewall_policy_arn == action_policy_arn + - policy_metadata.firewall_policy_id == action_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - '"stateless_custom_actions" in policy_data' + - policy_data.stateless_custom_actions | length == 1 + - '"action_name" in policy_data.stateless_custom_actions[0]' + - '"action_definition" in policy_data.stateless_custom_actions[0]' + - '"publish_metric_action" in policy_data.stateless_custom_actions[0].action_definition' + - '"dimensions" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action' + - policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions | length == 1 + - '"value" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions[0]' + - custom_action_names[0] == "CustomAction" + - custom_action_dimensions[0] == "CustomActionExample" + vars: + policy_data: '{{ action_policy.policy.policy }}' + policy_metadata: '{{ action_policy.policy.policy_metadata }}' + custom_action_names: '{{ policy_data.stateless_custom_actions | map(attribute="action_name") | list }}' + custom_action_dimensions: '{{ policy_data.stateless_custom_actions | map(attribute="action_definition.publish_metric_action.dimensions.0.value") | list }}' + + - name: 'A - Add a custom action' + networkfirewall_policy: + name: '{{ action_policy_name }}' + state: present + stateless_custom_actions: + - name: CustomAction + publish_metric_dimension_values: 'CustomActionExample' + register: action_policy + + - assert: + that: + - action_policy is changed + - '"policy" in action_policy' + - '"policy" in action_policy.policy' + - '"policy_metadata" in action_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == action_policy_name + - policy_metadata.firewall_policy_arn == action_policy_arn + - policy_metadata.firewall_policy_id == action_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - '"stateless_custom_actions" in policy_data' + - policy_data.stateless_custom_actions | length == 1 + - '"action_name" in policy_data.stateless_custom_actions[0]' + - '"action_definition" in policy_data.stateless_custom_actions[0]' + - '"publish_metric_action" in policy_data.stateless_custom_actions[0].action_definition' + - '"dimensions" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action' + - policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions | length == 1 + - '"value" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions[0]' + - custom_action_names[0] == "CustomAction" + - custom_action_dimensions[0] == "CustomActionExample" + vars: + policy_data: '{{ action_policy.policy.policy }}' + policy_metadata: '{{ action_policy.policy.policy_metadata }}' + custom_action_names: '{{ policy_data.stateless_custom_actions | map(attribute="action_name") | list }}' + custom_action_dimensions: '{{ policy_data.stateless_custom_actions | map(attribute="action_definition.publish_metric_action.dimensions.0.value") | list }}' + + - name: '(CHECK) A - Add a custom action (idempotency)' + check_mode: True + networkfirewall_policy: + name: '{{ action_policy_name }}' + state: present + stateless_custom_actions: + - name: CustomAction + publish_metric_dimension_values: 'CustomActionExample' + register: action_policy + + - assert: + that: + - action_policy is not changed + - '"policy" in action_policy' + - '"policy" in action_policy.policy' + - '"policy_metadata" in action_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == action_policy_name + - policy_metadata.firewall_policy_arn == action_policy_arn + - policy_metadata.firewall_policy_id == action_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - '"stateless_custom_actions" in policy_data' + - policy_data.stateless_custom_actions | length == 1 + - '"action_name" in policy_data.stateless_custom_actions[0]' + - '"action_definition" in policy_data.stateless_custom_actions[0]' + - '"publish_metric_action" in policy_data.stateless_custom_actions[0].action_definition' + - '"dimensions" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action' + - policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions | length == 1 + - '"value" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions[0]' + - custom_action_names[0] == "CustomAction" + - custom_action_dimensions[0] == "CustomActionExample" + vars: + policy_data: '{{ action_policy.policy.policy }}' + policy_metadata: '{{ action_policy.policy.policy_metadata }}' + custom_action_names: '{{ policy_data.stateless_custom_actions | map(attribute="action_name") | list }}' + custom_action_dimensions: '{{ policy_data.stateless_custom_actions | map(attribute="action_definition.publish_metric_action.dimensions.0.value") | list }}' + + - name: 'A - Add a custom action (idempotency)' + networkfirewall_policy: + name: '{{ action_policy_name }}' + state: present + stateless_custom_actions: + - name: CustomAction + publish_metric_dimension_values: 'CustomActionExample' + register: action_policy + + - assert: + that: + - action_policy is not changed + - '"policy" in action_policy' + - '"policy" in action_policy.policy' + - '"policy_metadata" in action_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == action_policy_name + - policy_metadata.firewall_policy_arn == action_policy_arn + - policy_metadata.firewall_policy_id == action_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - '"stateless_custom_actions" in policy_data' + - policy_data.stateless_custom_actions | length == 1 + - '"action_name" in policy_data.stateless_custom_actions[0]' + - '"action_definition" in policy_data.stateless_custom_actions[0]' + - '"publish_metric_action" in policy_data.stateless_custom_actions[0].action_definition' + - '"dimensions" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action' + - policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions | length == 1 + - '"value" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions[0]' + - custom_action_names[0] == "CustomAction" + - custom_action_dimensions[0] == "CustomActionExample" + vars: + policy_data: '{{ action_policy.policy.policy }}' + policy_metadata: '{{ action_policy.policy.policy_metadata }}' + custom_action_names: '{{ policy_data.stateless_custom_actions | map(attribute="action_name") | list }}' + custom_action_dimensions: '{{ policy_data.stateless_custom_actions | map(attribute="action_definition.publish_metric_action.dimensions.0.value") | list }}' + + ### + + - name: '(CHECK) A - Replace a custom action' + check_mode: True + networkfirewall_policy: + name: '{{ action_policy_name }}' + state: present + stateless_custom_actions: + - name: anothercustomaction + publish_metric_dimension_values: 'another_example' + + register: action_policy + + - assert: + that: + - action_policy is changed + - '"policy" in action_policy' + - '"policy" in action_policy.policy' + - '"policy_metadata" in action_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == action_policy_name + - policy_metadata.firewall_policy_arn == action_policy_arn + - policy_metadata.firewall_policy_id == action_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - '"stateless_custom_actions" in policy_data' + - policy_data.stateless_custom_actions | length == 1 + - '"action_name" in policy_data.stateless_custom_actions[0]' + - '"action_definition" in policy_data.stateless_custom_actions[0]' + - '"publish_metric_action" in policy_data.stateless_custom_actions[0].action_definition' + - '"dimensions" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action' + - policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions | length == 1 + - '"value" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions[0]' + - custom_action_names[0] == "anothercustomaction" + - custom_action_dimensions[0] == "another_example" + vars: + policy_data: '{{ action_policy.policy.policy }}' + policy_metadata: '{{ action_policy.policy.policy_metadata }}' + custom_action_names: '{{ policy_data.stateless_custom_actions | map(attribute="action_name") | list }}' + custom_action_dimensions: '{{ policy_data.stateless_custom_actions | map(attribute="action_definition.publish_metric_action.dimensions.0.value") | list }}' + + - name: 'A - Replace a custom action' + networkfirewall_policy: + name: '{{ action_policy_name }}' + state: present + stateless_custom_actions: + - name: anothercustomaction + publish_metric_dimension_values: 'another_example' + register: action_policy + + - assert: + that: + - action_policy is changed + - '"policy" in action_policy' + - '"policy" in action_policy.policy' + - '"policy_metadata" in action_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == action_policy_name + - policy_metadata.firewall_policy_arn == action_policy_arn + - policy_metadata.firewall_policy_id == action_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - '"stateless_custom_actions" in policy_data' + - policy_data.stateless_custom_actions | length == 1 + - '"action_name" in policy_data.stateless_custom_actions[0]' + - '"action_definition" in policy_data.stateless_custom_actions[0]' + - '"publish_metric_action" in policy_data.stateless_custom_actions[0].action_definition' + - '"dimensions" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action' + - policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions | length == 1 + - '"value" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions[0]' + - custom_action_names[0] == "anothercustomaction" + - custom_action_dimensions[0] == "another_example" + vars: + policy_data: '{{ action_policy.policy.policy }}' + policy_metadata: '{{ action_policy.policy.policy_metadata }}' + custom_action_names: '{{ policy_data.stateless_custom_actions | map(attribute="action_name") | list }}' + custom_action_dimensions: '{{ policy_data.stateless_custom_actions | map(attribute="action_definition.publish_metric_action.dimensions.0.value") | list }}' + + - name: '(CHECK) A - Replace a custom action (idempotency)' + check_mode: True + networkfirewall_policy: + name: '{{ action_policy_name }}' + state: present + stateless_custom_actions: + - name: anothercustomaction + publish_metric_dimension_values: 'another_example' + register: action_policy + + - assert: + that: + - action_policy is not changed + - '"policy" in action_policy' + - '"policy" in action_policy.policy' + - '"policy_metadata" in action_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == action_policy_name + - policy_metadata.firewall_policy_arn == action_policy_arn + - policy_metadata.firewall_policy_id == action_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - '"stateless_custom_actions" in policy_data' + - policy_data.stateless_custom_actions | length == 1 + - '"action_name" in policy_data.stateless_custom_actions[0]' + - '"action_definition" in policy_data.stateless_custom_actions[0]' + - '"publish_metric_action" in policy_data.stateless_custom_actions[0].action_definition' + - '"dimensions" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action' + - policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions | length == 1 + - '"value" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions[0]' + - custom_action_names[0] == "anothercustomaction" + - custom_action_dimensions[0] == "another_example" + vars: + policy_data: '{{ action_policy.policy.policy }}' + policy_metadata: '{{ action_policy.policy.policy_metadata }}' + custom_action_names: '{{ policy_data.stateless_custom_actions | map(attribute="action_name") | list }}' + custom_action_dimensions: '{{ policy_data.stateless_custom_actions | map(attribute="action_definition.publish_metric_action.dimensions.0.value") | list }}' + + - name: 'A - Replace a custom action (idempotency)' + networkfirewall_policy: + name: '{{ action_policy_name }}' + state: present + stateless_custom_actions: + - name: anothercustomaction + publish_metric_dimension_values: 'another_example' + register: action_policy + + - assert: + that: + - action_policy is not changed + - '"policy" in action_policy' + - '"policy" in action_policy.policy' + - '"policy_metadata" in action_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == action_policy_name + - policy_metadata.firewall_policy_arn == action_policy_arn + - policy_metadata.firewall_policy_id == action_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - '"stateless_custom_actions" in policy_data' + - policy_data.stateless_custom_actions | length == 1 + - '"action_name" in policy_data.stateless_custom_actions[0]' + - '"action_definition" in policy_data.stateless_custom_actions[0]' + - '"publish_metric_action" in policy_data.stateless_custom_actions[0].action_definition' + - '"dimensions" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action' + - policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions | length == 1 + - '"value" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions[0]' + - custom_action_names[0] == "anothercustomaction" + - custom_action_dimensions[0] == "another_example" + vars: + policy_data: '{{ action_policy.policy.policy }}' + policy_metadata: '{{ action_policy.policy.policy_metadata }}' + custom_action_names: '{{ policy_data.stateless_custom_actions | map(attribute="action_name") | list }}' + custom_action_dimensions: '{{ policy_data.stateless_custom_actions | map(attribute="action_definition.publish_metric_action.dimensions.0.value") | list }}' + + ### + + - name: '(CHECK) A - Add a second action without purge' + check_mode: True + networkfirewall_policy: + name: '{{ action_policy_name }}' + state: present + stateless_custom_actions: + - name: CustomAction + publish_metric_dimension_values: 'CustomActionExample' + purge_stateless_custom_actions: False + register: action_policy + + - assert: + that: + - action_policy is changed + - '"policy" in action_policy' + - '"policy" in action_policy.policy' + - '"policy_metadata" in action_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == action_policy_name + - policy_metadata.firewall_policy_arn == action_policy_arn + - policy_metadata.firewall_policy_id == action_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - '"stateless_custom_actions" in policy_data' + - policy_data.stateless_custom_actions | length == 2 + - '"action_name" in policy_data.stateless_custom_actions[0]' + - '"action_definition" in policy_data.stateless_custom_actions[0]' + - '"publish_metric_action" in policy_data.stateless_custom_actions[0].action_definition' + - '"dimensions" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action' + - policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions | length == 1 + - '"value" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions[0]' + - '"CustomAction" in custom_action_names' + - '"anothercustomaction" in custom_action_names' + - '"CustomActionExample" in custom_action_dimensions' + - '"another_example" in custom_action_dimensions' + vars: + policy_data: '{{ action_policy.policy.policy }}' + policy_metadata: '{{ action_policy.policy.policy_metadata }}' + custom_action_names: '{{ policy_data.stateless_custom_actions | map(attribute="action_name") | list }}' + custom_action_dimensions: '{{ policy_data.stateless_custom_actions | map(attribute="action_definition.publish_metric_action.dimensions.0.value") | list }}' + + - name: 'A - Add a second action without purge' + networkfirewall_policy: + name: '{{ action_policy_name }}' + state: present + stateless_custom_actions: + - name: CustomAction + publish_metric_dimension_values: 'CustomActionExample' + purge_stateless_custom_actions: False + register: action_policy + + - assert: + that: + - action_policy is changed + - '"policy" in action_policy' + - '"policy" in action_policy.policy' + - '"policy_metadata" in action_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == action_policy_name + - policy_metadata.firewall_policy_arn == action_policy_arn + - policy_metadata.firewall_policy_id == action_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - '"stateless_custom_actions" in policy_data' + - policy_data.stateless_custom_actions | length == 2 + - '"action_name" in policy_data.stateless_custom_actions[0]' + - '"action_definition" in policy_data.stateless_custom_actions[0]' + - '"publish_metric_action" in policy_data.stateless_custom_actions[0].action_definition' + - '"dimensions" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action' + - policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions | length == 1 + - '"value" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions[0]' + - '"CustomAction" in custom_action_names' + - '"anothercustomaction" in custom_action_names' + - '"CustomActionExample" in custom_action_dimensions' + - '"another_example" in custom_action_dimensions' + vars: + policy_data: '{{ action_policy.policy.policy }}' + policy_metadata: '{{ action_policy.policy.policy_metadata }}' + custom_action_names: '{{ policy_data.stateless_custom_actions | map(attribute="action_name") | list }}' + custom_action_dimensions: '{{ policy_data.stateless_custom_actions | map(attribute="action_definition.publish_metric_action.dimensions.0.value") | list }}' + + - name: '(CHECK) A - Add a second action without purge (idempotency)' + check_mode: True + networkfirewall_policy: + name: '{{ action_policy_name }}' + state: present + stateless_custom_actions: + - name: CustomAction + publish_metric_dimension_values: 'CustomActionExample' + purge_stateless_custom_actions: False + register: action_policy + + - assert: + that: + - action_policy is not changed + - '"policy" in action_policy' + - '"policy" in action_policy.policy' + - '"policy_metadata" in action_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == action_policy_name + - policy_metadata.firewall_policy_arn == action_policy_arn + - policy_metadata.firewall_policy_id == action_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - '"stateless_custom_actions" in policy_data' + - policy_data.stateless_custom_actions | length == 2 + - '"action_name" in policy_data.stateless_custom_actions[0]' + - '"action_definition" in policy_data.stateless_custom_actions[0]' + - '"publish_metric_action" in policy_data.stateless_custom_actions[0].action_definition' + - '"dimensions" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action' + - policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions | length == 1 + - '"value" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions[0]' + - '"CustomAction" in custom_action_names' + - '"anothercustomaction" in custom_action_names' + - '"CustomActionExample" in custom_action_dimensions' + - '"another_example" in custom_action_dimensions' + vars: + policy_data: '{{ action_policy.policy.policy }}' + policy_metadata: '{{ action_policy.policy.policy_metadata }}' + custom_action_names: '{{ policy_data.stateless_custom_actions | map(attribute="action_name") | list }}' + custom_action_dimensions: '{{ policy_data.stateless_custom_actions | map(attribute="action_definition.publish_metric_action.dimensions.0.value") | list }}' + + - name: 'A - Add a second action without purge (idempotency)' + networkfirewall_policy: + name: '{{ action_policy_name }}' + state: present + stateless_custom_actions: + - name: CustomAction + publish_metric_dimension_values: 'CustomActionExample' + purge_stateless_custom_actions: False + register: action_policy + + - assert: + that: + - action_policy is not changed + - '"policy" in action_policy' + - '"policy" in action_policy.policy' + - '"policy_metadata" in action_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == action_policy_name + - policy_metadata.firewall_policy_arn == action_policy_arn + - policy_metadata.firewall_policy_id == action_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - '"stateless_custom_actions" in policy_data' + - policy_data.stateless_custom_actions | length == 2 + - '"action_name" in policy_data.stateless_custom_actions[0]' + - '"action_definition" in policy_data.stateless_custom_actions[0]' + - '"publish_metric_action" in policy_data.stateless_custom_actions[0].action_definition' + - '"dimensions" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action' + - policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions | length == 1 + - '"value" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions[0]' + - '"CustomAction" in custom_action_names' + - '"anothercustomaction" in custom_action_names' + - '"CustomActionExample" in custom_action_dimensions' + - '"another_example" in custom_action_dimensions' + vars: + policy_data: '{{ action_policy.policy.policy }}' + policy_metadata: '{{ action_policy.policy.policy_metadata }}' + custom_action_names: '{{ policy_data.stateless_custom_actions | map(attribute="action_name") | list }}' + custom_action_dimensions: '{{ policy_data.stateless_custom_actions | map(attribute="action_definition.publish_metric_action.dimensions.0.value") | list }}' + + ### + + # The order of custom actions shouldn't matter + - name: '(CHECK) A - Update custom action order (idempotency)' + check_mode: True + networkfirewall_policy: + name: '{{ action_policy_name }}' + state: present + stateless_custom_actions: + - name: CustomAction + publish_metric_dimension_values: 'CustomActionExample' + - name: anothercustomaction + publish_metric_dimension_values: 'another_example' + register: action_policy + + - assert: + that: + - action_policy is not changed + - '"policy" in action_policy' + - '"policy" in action_policy.policy' + - '"policy_metadata" in action_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == action_policy_name + - policy_metadata.firewall_policy_arn == action_policy_arn + - policy_metadata.firewall_policy_id == action_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - '"stateless_custom_actions" in policy_data' + - policy_data.stateless_custom_actions | length == 2 + - '"action_name" in policy_data.stateless_custom_actions[0]' + - '"action_definition" in policy_data.stateless_custom_actions[0]' + - '"publish_metric_action" in policy_data.stateless_custom_actions[0].action_definition' + - '"dimensions" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action' + - policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions | length == 1 + - '"value" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions[0]' + - '"CustomAction" in custom_action_names' + - '"anothercustomaction" in custom_action_names' + - '"CustomActionExample" in custom_action_dimensions' + - '"another_example" in custom_action_dimensions' + vars: + policy_data: '{{ action_policy.policy.policy }}' + policy_metadata: '{{ action_policy.policy.policy_metadata }}' + custom_action_names: '{{ policy_data.stateless_custom_actions | map(attribute="action_name") | list }}' + custom_action_dimensions: '{{ policy_data.stateless_custom_actions | map(attribute="action_definition.publish_metric_action.dimensions.0.value") | list }}' + + - name: 'A - Add a second action without purge (idempotency)' + networkfirewall_policy: + name: '{{ action_policy_name }}' + state: present + stateless_custom_actions: + - name: CustomAction + publish_metric_dimension_values: 'CustomActionExample' + - name: anothercustomaction + publish_metric_dimension_values: 'another_example' + register: action_policy + + - assert: + that: + - action_policy is not changed + - '"policy" in action_policy' + - '"policy" in action_policy.policy' + - '"policy_metadata" in action_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == action_policy_name + - policy_metadata.firewall_policy_arn == action_policy_arn + - policy_metadata.firewall_policy_id == action_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - '"stateless_custom_actions" in policy_data' + - policy_data.stateless_custom_actions | length == 2 + - '"action_name" in policy_data.stateless_custom_actions[0]' + - '"action_definition" in policy_data.stateless_custom_actions[0]' + - '"publish_metric_action" in policy_data.stateless_custom_actions[0].action_definition' + - '"dimensions" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action' + - policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions | length == 1 + - '"value" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions[0]' + - '"CustomAction" in custom_action_names' + - '"anothercustomaction" in custom_action_names' + - '"CustomActionExample" in custom_action_dimensions' + - '"another_example" in custom_action_dimensions' + vars: + policy_data: '{{ action_policy.policy.policy }}' + policy_metadata: '{{ action_policy.policy.policy_metadata }}' + custom_action_names: '{{ policy_data.stateless_custom_actions | map(attribute="action_name") | list }}' + custom_action_dimensions: '{{ policy_data.stateless_custom_actions | map(attribute="action_definition.publish_metric_action.dimensions.0.value") | list }}' + + ### + + # The order of custom actions shouldn't matter + - name: '(CHECK) A - Update custom action order (idempotency)' + check_mode: True + networkfirewall_policy: + name: '{{ action_policy_name }}' + state: present + stateless_custom_actions: + - name: anothercustomaction + publish_metric_dimension_values: 'another_example' + - name: CustomAction + publish_metric_dimension_values: 'CustomActionExample' + register: action_policy + + - assert: + that: + - action_policy is not changed + - '"policy" in action_policy' + - '"policy" in action_policy.policy' + - '"policy_metadata" in action_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == action_policy_name + - policy_metadata.firewall_policy_arn == action_policy_arn + - policy_metadata.firewall_policy_id == action_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - '"stateless_custom_actions" in policy_data' + - policy_data.stateless_custom_actions | length == 2 + - '"action_name" in policy_data.stateless_custom_actions[0]' + - '"action_definition" in policy_data.stateless_custom_actions[0]' + - '"publish_metric_action" in policy_data.stateless_custom_actions[0].action_definition' + - '"dimensions" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action' + - policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions | length == 1 + - '"value" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions[0]' + - '"CustomAction" in custom_action_names' + - '"anothercustomaction" in custom_action_names' + - '"CustomActionExample" in custom_action_dimensions' + - '"another_example" in custom_action_dimensions' + vars: + policy_data: '{{ action_policy.policy.policy }}' + policy_metadata: '{{ action_policy.policy.policy_metadata }}' + custom_action_names: '{{ policy_data.stateless_custom_actions | map(attribute="action_name") | list }}' + custom_action_dimensions: '{{ policy_data.stateless_custom_actions | map(attribute="action_definition.publish_metric_action.dimensions.0.value") | list }}' + + - name: 'A - Add a second action without purge (idempotency)' + networkfirewall_policy: + name: '{{ action_policy_name }}' + state: present + stateless_custom_actions: + - name: anothercustomaction + publish_metric_dimension_values: 'another_example' + - name: CustomAction + publish_metric_dimension_values: 'CustomActionExample' + register: action_policy + + - assert: + that: + - action_policy is not changed + - '"policy" in action_policy' + - '"policy" in action_policy.policy' + - '"policy_metadata" in action_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == action_policy_name + - policy_metadata.firewall_policy_arn == action_policy_arn + - policy_metadata.firewall_policy_id == action_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - '"stateless_custom_actions" in policy_data' + - policy_data.stateless_custom_actions | length == 2 + - '"action_name" in policy_data.stateless_custom_actions[0]' + - '"action_definition" in policy_data.stateless_custom_actions[0]' + - '"publish_metric_action" in policy_data.stateless_custom_actions[0].action_definition' + - '"dimensions" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action' + - policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions | length == 1 + - '"value" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions[0]' + - '"CustomAction" in custom_action_names' + - '"anothercustomaction" in custom_action_names' + - '"CustomActionExample" in custom_action_dimensions' + - '"another_example" in custom_action_dimensions' + vars: + policy_data: '{{ action_policy.policy.policy }}' + policy_metadata: '{{ action_policy.policy.policy_metadata }}' + custom_action_names: '{{ policy_data.stateless_custom_actions | map(attribute="action_name") | list }}' + custom_action_dimensions: '{{ policy_data.stateless_custom_actions | map(attribute="action_definition.publish_metric_action.dimensions.0.value") | list }}' + + ################################################################### + # Set stateless_default_actions + + - name: '(CHECK) A - Set stateless flow default actions with invalid action - DENIED' + check_mode: True + networkfirewall_policy: + name: '{{ action_policy_name }}' + state: present + stateless_default_actions: + - 'CustomAction' + - 'aws:junk' + register: action_policy + ignore_errors: True + + - assert: + that: + - action_policy is failed + + - name: 'A - Set stateless flow default actions with invalid action - DENIED' + networkfirewall_policy: + name: '{{ action_policy_name }}' + state: present + stateless_default_actions: + - 'CustomAction' + - 'aws:junk' + register: action_policy + ignore_errors: True + + - assert: + that: + - action_policy is failed + + ### + + - name: '(CHECK) A - Set stateless flow default action' + check_mode: True + networkfirewall_policy: + name: '{{ action_policy_name }}' + state: present + stateless_default_actions: 'aws:drop' + register: action_policy + + - assert: + that: + - action_policy is changed + - '"policy" in action_policy' + - '"policy" in action_policy.policy' + - '"policy_metadata" in action_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == action_policy_name + - policy_metadata.firewall_policy_arn == action_policy_arn + - policy_metadata.firewall_policy_id == action_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:drop"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - '"stateless_custom_actions" in policy_data' + - policy_data.stateless_custom_actions | length == 2 + - '"action_name" in policy_data.stateless_custom_actions[0]' + - '"action_definition" in policy_data.stateless_custom_actions[0]' + - '"publish_metric_action" in policy_data.stateless_custom_actions[0].action_definition' + - '"dimensions" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action' + - policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions | length == 1 + - '"value" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions[0]' + - '"CustomAction" in custom_action_names' + - '"anothercustomaction" in custom_action_names' + - '"CustomActionExample" in custom_action_dimensions' + - '"another_example" in custom_action_dimensions' + vars: + policy_data: '{{ action_policy.policy.policy }}' + policy_metadata: '{{ action_policy.policy.policy_metadata }}' + custom_action_names: '{{ policy_data.stateless_custom_actions | map(attribute="action_name") | list }}' + custom_action_dimensions: '{{ policy_data.stateless_custom_actions | map(attribute="action_definition.publish_metric_action.dimensions.0.value") | list }}' + + - name: 'A - Set stateless flow default actions' + networkfirewall_policy: + name: '{{ action_policy_name }}' + state: present + stateless_default_actions: 'aws:drop' + register: action_policy + + - assert: + that: + - action_policy is changed + - '"policy" in action_policy' + - '"policy" in action_policy.policy' + - '"policy_metadata" in action_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == action_policy_name + - policy_metadata.firewall_policy_arn == action_policy_arn + - policy_metadata.firewall_policy_id == action_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:drop"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - '"stateless_custom_actions" in policy_data' + - policy_data.stateless_custom_actions | length == 2 + - '"action_name" in policy_data.stateless_custom_actions[0]' + - '"action_definition" in policy_data.stateless_custom_actions[0]' + - '"publish_metric_action" in policy_data.stateless_custom_actions[0].action_definition' + - '"dimensions" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action' + - policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions | length == 1 + - '"value" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions[0]' + - '"CustomAction" in custom_action_names' + - '"anothercustomaction" in custom_action_names' + - '"CustomActionExample" in custom_action_dimensions' + - '"another_example" in custom_action_dimensions' + vars: + policy_data: '{{ action_policy.policy.policy }}' + policy_metadata: '{{ action_policy.policy.policy_metadata }}' + custom_action_names: '{{ policy_data.stateless_custom_actions | map(attribute="action_name") | list }}' + custom_action_dimensions: '{{ policy_data.stateless_custom_actions | map(attribute="action_definition.publish_metric_action.dimensions.0.value") | list }}' + + - name: '(CHECK) A - Set stateless flow default actions (idempotency)' + check_mode: True + networkfirewall_policy: + name: '{{ action_policy_name }}' + state: present + stateless_default_actions: 'aws:drop' + register: action_policy + + - assert: + that: + - action_policy is not changed + - '"policy" in action_policy' + - '"policy" in action_policy.policy' + - '"policy_metadata" in action_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == action_policy_name + - policy_metadata.firewall_policy_arn == action_policy_arn + - policy_metadata.firewall_policy_id == action_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:drop"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - '"stateless_custom_actions" in policy_data' + - policy_data.stateless_custom_actions | length == 2 + - '"action_name" in policy_data.stateless_custom_actions[0]' + - '"action_definition" in policy_data.stateless_custom_actions[0]' + - '"publish_metric_action" in policy_data.stateless_custom_actions[0].action_definition' + - '"dimensions" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action' + - policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions | length == 1 + - '"value" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions[0]' + - '"CustomAction" in custom_action_names' + - '"anothercustomaction" in custom_action_names' + - '"CustomActionExample" in custom_action_dimensions' + - '"another_example" in custom_action_dimensions' + vars: + policy_data: '{{ action_policy.policy.policy }}' + policy_metadata: '{{ action_policy.policy.policy_metadata }}' + custom_action_names: '{{ policy_data.stateless_custom_actions | map(attribute="action_name") | list }}' + custom_action_dimensions: '{{ policy_data.stateless_custom_actions | map(attribute="action_definition.publish_metric_action.dimensions.0.value") | list }}' + + - name: 'A - Set stateless flow default actions (idempotency)' + networkfirewall_policy: + name: '{{ action_policy_name }}' + state: present + stateless_default_actions: 'aws:drop' + register: action_policy + + - assert: + that: + - action_policy is not changed + - '"policy" in action_policy' + - '"policy" in action_policy.policy' + - '"policy_metadata" in action_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == action_policy_name + - policy_metadata.firewall_policy_arn == action_policy_arn + - policy_metadata.firewall_policy_id == action_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:drop"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - '"stateless_custom_actions" in policy_data' + - policy_data.stateless_custom_actions | length == 2 + - '"action_name" in policy_data.stateless_custom_actions[0]' + - '"action_definition" in policy_data.stateless_custom_actions[0]' + - '"publish_metric_action" in policy_data.stateless_custom_actions[0].action_definition' + - '"dimensions" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action' + - policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions | length == 1 + - '"value" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions[0]' + - '"CustomAction" in custom_action_names' + - '"anothercustomaction" in custom_action_names' + - '"CustomActionExample" in custom_action_dimensions' + - '"another_example" in custom_action_dimensions' + vars: + policy_data: '{{ action_policy.policy.policy }}' + policy_metadata: '{{ action_policy.policy.policy_metadata }}' + custom_action_names: '{{ policy_data.stateless_custom_actions | map(attribute="action_name") | list }}' + custom_action_dimensions: '{{ policy_data.stateless_custom_actions | map(attribute="action_definition.publish_metric_action.dimensions.0.value") | list }}' + + ### + + - name: '(CHECK) A - Set stateless flow default action with custom action' + check_mode: True + networkfirewall_policy: + name: '{{ action_policy_name }}' + state: present + stateless_default_actions: + - 'CustomAction' + - 'aws:drop' + register: action_policy + + - assert: + that: + - action_policy is changed + - '"policy" in action_policy' + - '"policy" in action_policy.policy' + - '"policy_metadata" in action_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == action_policy_name + - policy_metadata.firewall_policy_arn == action_policy_arn + - policy_metadata.firewall_policy_id == action_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["CustomAction", "aws:drop"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - '"stateless_custom_actions" in policy_data' + - policy_data.stateless_custom_actions | length == 2 + - '"action_name" in policy_data.stateless_custom_actions[0]' + - '"action_definition" in policy_data.stateless_custom_actions[0]' + - '"publish_metric_action" in policy_data.stateless_custom_actions[0].action_definition' + - '"dimensions" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action' + - policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions | length == 1 + - '"value" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions[0]' + - '"CustomAction" in custom_action_names' + - '"anothercustomaction" in custom_action_names' + - '"CustomActionExample" in custom_action_dimensions' + - '"another_example" in custom_action_dimensions' + vars: + policy_data: '{{ action_policy.policy.policy }}' + policy_metadata: '{{ action_policy.policy.policy_metadata }}' + custom_action_names: '{{ policy_data.stateless_custom_actions | map(attribute="action_name") | list }}' + custom_action_dimensions: '{{ policy_data.stateless_custom_actions | map(attribute="action_definition.publish_metric_action.dimensions.0.value") | list }}' + + - name: 'A - Set stateless flow default actions with custom action' + networkfirewall_policy: + name: '{{ action_policy_name }}' + state: present + stateless_default_actions: + - 'CustomAction' + - 'aws:drop' + register: action_policy + + - assert: + that: + - action_policy is changed + - '"policy" in action_policy' + - '"policy" in action_policy.policy' + - '"policy_metadata" in action_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == action_policy_name + - policy_metadata.firewall_policy_arn == action_policy_arn + - policy_metadata.firewall_policy_id == action_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["CustomAction", "aws:drop"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - '"stateless_custom_actions" in policy_data' + - policy_data.stateless_custom_actions | length == 2 + - '"action_name" in policy_data.stateless_custom_actions[0]' + - '"action_definition" in policy_data.stateless_custom_actions[0]' + - '"publish_metric_action" in policy_data.stateless_custom_actions[0].action_definition' + - '"dimensions" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action' + - policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions | length == 1 + - '"value" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions[0]' + - '"CustomAction" in custom_action_names' + - '"anothercustomaction" in custom_action_names' + - '"CustomActionExample" in custom_action_dimensions' + - '"another_example" in custom_action_dimensions' + vars: + policy_data: '{{ action_policy.policy.policy }}' + policy_metadata: '{{ action_policy.policy.policy_metadata }}' + custom_action_names: '{{ policy_data.stateless_custom_actions | map(attribute="action_name") | list }}' + custom_action_dimensions: '{{ policy_data.stateless_custom_actions | map(attribute="action_definition.publish_metric_action.dimensions.0.value") | list }}' + + - name: '(CHECK) A - Set stateless flow default actions with custom action (idempotency)' + check_mode: True + networkfirewall_policy: + name: '{{ action_policy_name }}' + state: present + stateless_default_actions: + - 'CustomAction' + - 'aws:drop' + register: action_policy + + - assert: + that: + - action_policy is not changed + - '"policy" in action_policy' + - '"policy" in action_policy.policy' + - '"policy_metadata" in action_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == action_policy_name + - policy_metadata.firewall_policy_arn == action_policy_arn + - policy_metadata.firewall_policy_id == action_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["CustomAction", "aws:drop"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - '"stateless_custom_actions" in policy_data' + - policy_data.stateless_custom_actions | length == 2 + - '"action_name" in policy_data.stateless_custom_actions[0]' + - '"action_definition" in policy_data.stateless_custom_actions[0]' + - '"publish_metric_action" in policy_data.stateless_custom_actions[0].action_definition' + - '"dimensions" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action' + - policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions | length == 1 + - '"value" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions[0]' + - '"CustomAction" in custom_action_names' + - '"anothercustomaction" in custom_action_names' + - '"CustomActionExample" in custom_action_dimensions' + - '"another_example" in custom_action_dimensions' + vars: + policy_data: '{{ action_policy.policy.policy }}' + policy_metadata: '{{ action_policy.policy.policy_metadata }}' + custom_action_names: '{{ policy_data.stateless_custom_actions | map(attribute="action_name") | list }}' + custom_action_dimensions: '{{ policy_data.stateless_custom_actions | map(attribute="action_definition.publish_metric_action.dimensions.0.value") | list }}' + + - name: 'A - Set stateless flow default actions with custom action (idempotency)' + networkfirewall_policy: + name: '{{ action_policy_name }}' + state: present + stateless_default_actions: + - 'CustomAction' + - 'aws:drop' + register: action_policy + + - assert: + that: + - action_policy is not changed + - '"policy" in action_policy' + - '"policy" in action_policy.policy' + - '"policy_metadata" in action_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == action_policy_name + - policy_metadata.firewall_policy_arn == action_policy_arn + - policy_metadata.firewall_policy_id == action_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["CustomAction", "aws:drop"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - '"stateless_custom_actions" in policy_data' + - policy_data.stateless_custom_actions | length == 2 + - '"action_name" in policy_data.stateless_custom_actions[0]' + - '"action_definition" in policy_data.stateless_custom_actions[0]' + - '"publish_metric_action" in policy_data.stateless_custom_actions[0].action_definition' + - '"dimensions" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action' + - policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions | length == 1 + - '"value" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions[0]' + - '"CustomAction" in custom_action_names' + - '"anothercustomaction" in custom_action_names' + - '"CustomActionExample" in custom_action_dimensions' + - '"another_example" in custom_action_dimensions' + vars: + policy_data: '{{ action_policy.policy.policy }}' + policy_metadata: '{{ action_policy.policy.policy_metadata }}' + custom_action_names: '{{ policy_data.stateless_custom_actions | map(attribute="action_name") | list }}' + custom_action_dimensions: '{{ policy_data.stateless_custom_actions | map(attribute="action_definition.publish_metric_action.dimensions.0.value") | list }}' + + + ################################################################### + # Set stateless_fragment_default_actions + + - name: '(CHECK) A - Set stateless fragmented packet default actions with invalid action - DENIED' + check_mode: True + networkfirewall_policy: + name: '{{ action_policy_name }}' + state: present + stateless_fragment_default_actions: + - 'CustomJunk' + - 'aws:drop' + register: action_policy + ignore_errors: True + + - assert: + that: + - action_policy is failed + + - name: 'A - Set stateless fragmented packet default actions with invalid action - DENIED' + networkfirewall_policy: + name: '{{ action_policy_name }}' + state: present + stateless_fragment_default_actions: + - 'CustomJunk' + - 'aws:drop' + register: action_policy + ignore_errors: True + + - assert: + that: + - action_policy is failed + + ### + + - name: '(CHECK) A - Set stateless fragmented packet default action' + check_mode: True + networkfirewall_policy: + name: '{{ action_policy_name }}' + state: present + stateless_fragment_default_actions: 'aws:pass' + register: action_policy + + - assert: + that: + - action_policy is changed + - '"policy" in action_policy' + - '"policy" in action_policy.policy' + - '"policy_metadata" in action_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == action_policy_name + - policy_metadata.firewall_policy_arn == action_policy_arn + - policy_metadata.firewall_policy_id == action_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["CustomAction", "aws:drop"] + - policy_data.stateless_fragment_default_actions == ["aws:pass"] + - '"stateless_custom_actions" in policy_data' + - policy_data.stateless_custom_actions | length == 2 + - '"action_name" in policy_data.stateless_custom_actions[0]' + - '"action_definition" in policy_data.stateless_custom_actions[0]' + - '"publish_metric_action" in policy_data.stateless_custom_actions[0].action_definition' + - '"dimensions" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action' + - policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions | length == 1 + - '"value" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions[0]' + - '"CustomAction" in custom_action_names' + - '"anothercustomaction" in custom_action_names' + - '"CustomActionExample" in custom_action_dimensions' + - '"another_example" in custom_action_dimensions' + vars: + policy_data: '{{ action_policy.policy.policy }}' + policy_metadata: '{{ action_policy.policy.policy_metadata }}' + custom_action_names: '{{ policy_data.stateless_custom_actions | map(attribute="action_name") | list }}' + custom_action_dimensions: '{{ policy_data.stateless_custom_actions | map(attribute="action_definition.publish_metric_action.dimensions.0.value") | list }}' + + - name: 'A - Set stateless fragmented packet default actions' + networkfirewall_policy: + name: '{{ action_policy_name }}' + state: present + stateless_fragment_default_actions: 'aws:pass' + register: action_policy + + - assert: + that: + - action_policy is changed + - '"policy" in action_policy' + - '"policy" in action_policy.policy' + - '"policy_metadata" in action_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == action_policy_name + - policy_metadata.firewall_policy_arn == action_policy_arn + - policy_metadata.firewall_policy_id == action_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["CustomAction", "aws:drop"] + - policy_data.stateless_fragment_default_actions == ["aws:pass"] + - '"stateless_custom_actions" in policy_data' + - policy_data.stateless_custom_actions | length == 2 + - '"action_name" in policy_data.stateless_custom_actions[0]' + - '"action_definition" in policy_data.stateless_custom_actions[0]' + - '"publish_metric_action" in policy_data.stateless_custom_actions[0].action_definition' + - '"dimensions" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action' + - policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions | length == 1 + - '"value" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions[0]' + - '"CustomAction" in custom_action_names' + - '"anothercustomaction" in custom_action_names' + - '"CustomActionExample" in custom_action_dimensions' + - '"another_example" in custom_action_dimensions' + vars: + policy_data: '{{ action_policy.policy.policy }}' + policy_metadata: '{{ action_policy.policy.policy_metadata }}' + custom_action_names: '{{ policy_data.stateless_custom_actions | map(attribute="action_name") | list }}' + custom_action_dimensions: '{{ policy_data.stateless_custom_actions | map(attribute="action_definition.publish_metric_action.dimensions.0.value") | list }}' + + - name: '(CHECK) A - Set stateless fragmented packet default actions (idempotency)' + check_mode: True + networkfirewall_policy: + name: '{{ action_policy_name }}' + state: present + stateless_fragment_default_actions: 'aws:pass' + register: action_policy + + - assert: + that: + - action_policy is not changed + - '"policy" in action_policy' + - '"policy" in action_policy.policy' + - '"policy_metadata" in action_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == action_policy_name + - policy_metadata.firewall_policy_arn == action_policy_arn + - policy_metadata.firewall_policy_id == action_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["CustomAction", "aws:drop"] + - policy_data.stateless_fragment_default_actions == ["aws:pass"] + - '"stateless_custom_actions" in policy_data' + - policy_data.stateless_custom_actions | length == 2 + - '"action_name" in policy_data.stateless_custom_actions[0]' + - '"action_definition" in policy_data.stateless_custom_actions[0]' + - '"publish_metric_action" in policy_data.stateless_custom_actions[0].action_definition' + - '"dimensions" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action' + - policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions | length == 1 + - '"value" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions[0]' + - '"CustomAction" in custom_action_names' + - '"anothercustomaction" in custom_action_names' + - '"CustomActionExample" in custom_action_dimensions' + - '"another_example" in custom_action_dimensions' + vars: + policy_data: '{{ action_policy.policy.policy }}' + policy_metadata: '{{ action_policy.policy.policy_metadata }}' + custom_action_names: '{{ policy_data.stateless_custom_actions | map(attribute="action_name") | list }}' + custom_action_dimensions: '{{ policy_data.stateless_custom_actions | map(attribute="action_definition.publish_metric_action.dimensions.0.value") | list }}' + + - name: 'A - Set stateless fragmented packet default actions (idempotency)' + networkfirewall_policy: + name: '{{ action_policy_name }}' + state: present + stateless_fragment_default_actions: 'aws:pass' + register: action_policy + + - assert: + that: + - action_policy is not changed + - '"policy" in action_policy' + - '"policy" in action_policy.policy' + - '"policy_metadata" in action_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == action_policy_name + - policy_metadata.firewall_policy_arn == action_policy_arn + - policy_metadata.firewall_policy_id == action_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["CustomAction", "aws:drop"] + - policy_data.stateless_fragment_default_actions == ["aws:pass"] + - '"stateless_custom_actions" in policy_data' + - policy_data.stateless_custom_actions | length == 2 + - '"action_name" in policy_data.stateless_custom_actions[0]' + - '"action_definition" in policy_data.stateless_custom_actions[0]' + - '"publish_metric_action" in policy_data.stateless_custom_actions[0].action_definition' + - '"dimensions" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action' + - policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions | length == 1 + - '"value" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions[0]' + - '"CustomAction" in custom_action_names' + - '"anothercustomaction" in custom_action_names' + - '"CustomActionExample" in custom_action_dimensions' + - '"another_example" in custom_action_dimensions' + vars: + policy_data: '{{ action_policy.policy.policy }}' + policy_metadata: '{{ action_policy.policy.policy_metadata }}' + custom_action_names: '{{ policy_data.stateless_custom_actions | map(attribute="action_name") | list }}' + custom_action_dimensions: '{{ policy_data.stateless_custom_actions | map(attribute="action_definition.publish_metric_action.dimensions.0.value") | list }}' + + ### + + - name: '(CHECK) A - Set stateless flow default action with custom action' + check_mode: True + networkfirewall_policy: + name: '{{ action_policy_name }}' + state: present + stateless_fragment_default_actions: + - 'anothercustomaction' + - 'aws:forward_to_sfe' + register: action_policy + + - assert: + that: + - action_policy is changed + - '"policy" in action_policy' + - '"policy" in action_policy.policy' + - '"policy_metadata" in action_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == action_policy_name + - policy_metadata.firewall_policy_arn == action_policy_arn + - policy_metadata.firewall_policy_id == action_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["CustomAction", "aws:drop"] + - policy_data.stateless_fragment_default_actions == ["anothercustomaction", "aws:forward_to_sfe"] + - '"stateless_custom_actions" in policy_data' + - policy_data.stateless_custom_actions | length == 2 + - '"action_name" in policy_data.stateless_custom_actions[0]' + - '"action_definition" in policy_data.stateless_custom_actions[0]' + - '"publish_metric_action" in policy_data.stateless_custom_actions[0].action_definition' + - '"dimensions" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action' + - policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions | length == 1 + - '"value" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions[0]' + - '"CustomAction" in custom_action_names' + - '"anothercustomaction" in custom_action_names' + - '"CustomActionExample" in custom_action_dimensions' + - '"another_example" in custom_action_dimensions' + vars: + policy_data: '{{ action_policy.policy.policy }}' + policy_metadata: '{{ action_policy.policy.policy_metadata }}' + custom_action_names: '{{ policy_data.stateless_custom_actions | map(attribute="action_name") | list }}' + custom_action_dimensions: '{{ policy_data.stateless_custom_actions | map(attribute="action_definition.publish_metric_action.dimensions.0.value") | list }}' + + - name: 'A - Set stateless flow default actions with custom action' + networkfirewall_policy: + name: '{{ action_policy_name }}' + state: present + stateless_fragment_default_actions: + - 'anothercustomaction' + - 'aws:forward_to_sfe' + register: action_policy + + - assert: + that: + - action_policy is changed + - '"policy" in action_policy' + - '"policy" in action_policy.policy' + - '"policy_metadata" in action_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == action_policy_name + - policy_metadata.firewall_policy_arn == action_policy_arn + - policy_metadata.firewall_policy_id == action_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["CustomAction", "aws:drop"] + - policy_data.stateless_fragment_default_actions == ["anothercustomaction", "aws:forward_to_sfe"] + - '"stateless_custom_actions" in policy_data' + - policy_data.stateless_custom_actions | length == 2 + - '"action_name" in policy_data.stateless_custom_actions[0]' + - '"action_definition" in policy_data.stateless_custom_actions[0]' + - '"publish_metric_action" in policy_data.stateless_custom_actions[0].action_definition' + - '"dimensions" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action' + - policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions | length == 1 + - '"value" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions[0]' + - '"CustomAction" in custom_action_names' + - '"anothercustomaction" in custom_action_names' + - '"CustomActionExample" in custom_action_dimensions' + - '"another_example" in custom_action_dimensions' + vars: + policy_data: '{{ action_policy.policy.policy }}' + policy_metadata: '{{ action_policy.policy.policy_metadata }}' + custom_action_names: '{{ policy_data.stateless_custom_actions | map(attribute="action_name") | list }}' + custom_action_dimensions: '{{ policy_data.stateless_custom_actions | map(attribute="action_definition.publish_metric_action.dimensions.0.value") | list }}' + + - name: '(CHECK) A - Set stateless flow default actions with custom action (idempotency)' + check_mode: True + networkfirewall_policy: + name: '{{ action_policy_name }}' + state: present + stateless_fragment_default_actions: + - 'anothercustomaction' + - 'aws:forward_to_sfe' + register: action_policy + + - assert: + that: + - action_policy is not changed + - '"policy" in action_policy' + - '"policy" in action_policy.policy' + - '"policy_metadata" in action_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == action_policy_name + - policy_metadata.firewall_policy_arn == action_policy_arn + - policy_metadata.firewall_policy_id == action_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["CustomAction", "aws:drop"] + - policy_data.stateless_fragment_default_actions == ["anothercustomaction", "aws:forward_to_sfe"] + - '"stateless_custom_actions" in policy_data' + - policy_data.stateless_custom_actions | length == 2 + - '"action_name" in policy_data.stateless_custom_actions[0]' + - '"action_definition" in policy_data.stateless_custom_actions[0]' + - '"publish_metric_action" in policy_data.stateless_custom_actions[0].action_definition' + - '"dimensions" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action' + - policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions | length == 1 + - '"value" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions[0]' + - '"CustomAction" in custom_action_names' + - '"anothercustomaction" in custom_action_names' + - '"CustomActionExample" in custom_action_dimensions' + - '"another_example" in custom_action_dimensions' + vars: + policy_data: '{{ action_policy.policy.policy }}' + policy_metadata: '{{ action_policy.policy.policy_metadata }}' + custom_action_names: '{{ policy_data.stateless_custom_actions | map(attribute="action_name") | list }}' + custom_action_dimensions: '{{ policy_data.stateless_custom_actions | map(attribute="action_definition.publish_metric_action.dimensions.0.value") | list }}' + + - name: 'A - Set stateless flow default actions with custom action (idempotency)' + networkfirewall_policy: + name: '{{ action_policy_name }}' + state: present + stateless_fragment_default_actions: + - 'anothercustomaction' + - 'aws:forward_to_sfe' + register: action_policy + + - assert: + that: + - action_policy is not changed + - '"policy" in action_policy' + - '"policy" in action_policy.policy' + - '"policy_metadata" in action_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == action_policy_name + - policy_metadata.firewall_policy_arn == action_policy_arn + - policy_metadata.firewall_policy_id == action_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["CustomAction", "aws:drop"] + - policy_data.stateless_fragment_default_actions == ["anothercustomaction", "aws:forward_to_sfe"] + - '"stateless_custom_actions" in policy_data' + - policy_data.stateless_custom_actions | length == 2 + - '"action_name" in policy_data.stateless_custom_actions[0]' + - '"action_definition" in policy_data.stateless_custom_actions[0]' + - '"publish_metric_action" in policy_data.stateless_custom_actions[0].action_definition' + - '"dimensions" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action' + - policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions | length == 1 + - '"value" in policy_data.stateless_custom_actions[0].action_definition.publish_metric_action.dimensions[0]' + - '"CustomAction" in custom_action_names' + - '"anothercustomaction" in custom_action_names' + - '"CustomActionExample" in custom_action_dimensions' + - '"another_example" in custom_action_dimensions' + vars: + policy_data: '{{ action_policy.policy.policy }}' + policy_metadata: '{{ action_policy.policy.policy_metadata }}' + custom_action_names: '{{ policy_data.stateless_custom_actions | map(attribute="action_name") | list }}' + custom_action_dimensions: '{{ policy_data.stateless_custom_actions | map(attribute="action_definition.publish_metric_action.dimensions.0.value") | list }}' + + ################################################################### + # Delete policy + + - name: 'A - Delete policy with default order' + networkfirewall_policy: + name: '{{ action_policy_name }}' + state: absent + wait: False + register: action_policy + + - assert: + that: + - action_policy is changed + + always: + - name: 'A - Cleanup firewall policy' + networkfirewall_policy: + name: '{{ action_policy_name }}' + state: absent + wait: False + ignore_errors: True diff --git a/ansible_collections/community/aws/tests/integration/targets/networkfirewall_policy/tasks/cleanup.yml b/ansible_collections/community/aws/tests/integration/targets/networkfirewall_policy/tasks/cleanup.yml new file mode 100644 index 000000000..d772b6744 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/networkfirewall_policy/tasks/cleanup.yml @@ -0,0 +1,40 @@ +--- +- name: 'Fetch all policies' + networkfirewall_policy_info: {} + register: account_policies_info + ignore_errors: true + +- name: 'Get a list of all rules matching {{ policy_name_prefix }}' + set_fact: + matching_policies: '{{ account_policies_info.policy_list | select("search", policy_name_prefix) | list }}' + ignore_errors: true + +# These should just be "no-ops" caused by the deletion being in-progress. +# Waiters are not supported at this time. +- name: 'Delete matching policies' + networkfirewall_policy: + arn: '{{ item }}' + state: absent + ignore_errors: true + loop: '{{ matching_policies }}' + +### + +- name: 'Fetch all account rule groups' + networkfirewall_rule_group_info: {} + register: account_rules_info + ignore_errors: true + +- name: 'Get a list of all rules matching {{ group_name_prefix }}' + set_fact: + matching_rules: '{{ account_rules_info.rule_list | select("search", group_name_prefix) | list }}' + ignore_errors: true + +# These should just be "no-ops" caused by the deletion being in-progress. +# Waiters are not supported at this time. +- name: 'Delete matching rule groups' + networkfirewall_rule_group: + arn: '{{ item }}' + state: absent + ignore_errors: true + loop: '{{ matching_rules }}' diff --git a/ansible_collections/community/aws/tests/integration/targets/networkfirewall_policy/tasks/common.yml b/ansible_collections/community/aws/tests/integration/targets/networkfirewall_policy/tasks/common.yml new file mode 100644 index 000000000..b04b29de6 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/networkfirewall_policy/tasks/common.yml @@ -0,0 +1,804 @@ +--- +# Tests the manipulation of common policy attributes +# +# In Scope: +# - Updating tags +# - Updating description +# - Waiting for deletion + +- vars: + common_policy_name: '{{ policy_name_prefix }}-Common' + 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: + ################################################################### + # Creation - Fully tested in 'default_order.yml' and 'strict_order.yml' + + - name: 'C - Create a simple firewall policy with `default` rule order' + networkfirewall_policy: + name: '{{ common_policy_name }}' + stateful_rule_order: 'default' + state: present + register: common_policy + + - assert: + that: + - common_policy is changed + - '"policy" in common_policy' + - '"policy" in common_policy.policy' + - '"policy_metadata" in common_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == common_policy_name + - policy_metadata.firewall_policy_arn.startswith(account_arn) + - policy_metadata.firewall_policy_arn.endswith(common_policy_name) + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + vars: + policy_data: '{{ common_policy.policy.policy }}' + policy_metadata: '{{ common_policy.policy.policy_metadata }}' + + - name: 'C - Save Policy ID/ARN for later' + set_fact: + common_policy_id: '{{ policy_metadata.firewall_policy_id }}' + common_policy_arn: '{{ policy_metadata.firewall_policy_arn }}' + vars: + policy_metadata: '{{ common_policy.policy.policy_metadata }}' + + ################################################################### + # Description + + - name: '(CHECK) C - Set Description' + check_mode: True + networkfirewall_policy: + name: '{{ common_policy_name }}' + state: present + description: 'Example Description' + register: common_policy + + - assert: + that: + - common_policy is changed + - '"policy" in common_policy' + - '"policy" in common_policy.policy' + - '"policy_metadata" in common_policy.policy' + - '"description" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.description == 'Example Description' + - policy_metadata.firewall_policy_name == common_policy_name + - policy_metadata.firewall_policy_arn == common_policy_arn + - policy_metadata.firewall_policy_id == common_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + vars: + policy_data: '{{ common_policy.policy.policy }}' + policy_metadata: '{{ common_policy.policy.policy_metadata }}' + + - name: 'C - Set Description' + networkfirewall_policy: + name: '{{ common_policy_name }}' + state: present + description: 'Example Description' + register: common_policy + + - assert: + that: + - common_policy is changed + - '"policy" in common_policy' + - '"policy" in common_policy.policy' + - '"policy_metadata" in common_policy.policy' + - '"description" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.description == 'Example Description' + - policy_metadata.firewall_policy_name == common_policy_name + - policy_metadata.firewall_policy_arn == common_policy_arn + - policy_metadata.firewall_policy_id == common_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + vars: + policy_data: '{{ common_policy.policy.policy }}' + policy_metadata: '{{ common_policy.policy.policy_metadata }}' + + - name: '(CHECK) C - Set Description (idempotency)' + check_mode: True + networkfirewall_policy: + name: '{{ common_policy_name }}' + state: present + description: 'Example Description' + register: common_policy + + - assert: + that: + - common_policy is not changed + - '"policy" in common_policy' + - '"policy" in common_policy.policy' + - '"policy_metadata" in common_policy.policy' + - '"description" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.description == 'Example Description' + - policy_metadata.firewall_policy_name == common_policy_name + - policy_metadata.firewall_policy_arn == common_policy_arn + - policy_metadata.firewall_policy_id == common_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + vars: + policy_data: '{{ common_policy.policy.policy }}' + policy_metadata: '{{ common_policy.policy.policy_metadata }}' + + - name: 'C - Set Description (idempotency)' + networkfirewall_policy: + name: '{{ common_policy_name }}' + state: present + description: 'Example Description' + register: common_policy + + - assert: + that: + - common_policy is not changed + - '"policy" in common_policy' + - '"policy" in common_policy.policy' + - '"policy_metadata" in common_policy.policy' + - '"description" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.description == 'Example Description' + - policy_metadata.firewall_policy_name == common_policy_name + - policy_metadata.firewall_policy_arn == common_policy_arn + - policy_metadata.firewall_policy_id == common_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + vars: + policy_data: '{{ common_policy.policy.policy }}' + policy_metadata: '{{ common_policy.policy.policy_metadata }}' + + ### + + - name: '(CHECK) C - Update Description' + check_mode: True + networkfirewall_policy: + name: '{{ common_policy_name }}' + state: present + description: 'Updated Description' + register: common_policy + + - assert: + that: + - common_policy is changed + - '"policy" in common_policy' + - '"policy" in common_policy.policy' + - '"policy_metadata" in common_policy.policy' + - '"description" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.description == 'Updated Description' + - policy_metadata.firewall_policy_name == common_policy_name + - policy_metadata.firewall_policy_arn == common_policy_arn + - policy_metadata.firewall_policy_id == common_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + vars: + policy_data: '{{ common_policy.policy.policy }}' + policy_metadata: '{{ common_policy.policy.policy_metadata }}' + + - name: 'C - Update Description' + networkfirewall_policy: + name: '{{ common_policy_name }}' + state: present + description: 'Updated Description' + register: common_policy + + - assert: + that: + - common_policy is changed + - '"policy" in common_policy' + - '"policy" in common_policy.policy' + - '"policy_metadata" in common_policy.policy' + - '"description" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.description == 'Updated Description' + - policy_metadata.firewall_policy_name == common_policy_name + - policy_metadata.firewall_policy_arn == common_policy_arn + - policy_metadata.firewall_policy_id == common_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + vars: + policy_data: '{{ common_policy.policy.policy }}' + policy_metadata: '{{ common_policy.policy.policy_metadata }}' + + - name: '(CHECK) C - Update Description (idempotency)' + check_mode: True + networkfirewall_policy: + name: '{{ common_policy_name }}' + state: present + description: 'Updated Description' + register: common_policy + + - assert: + that: + - common_policy is not changed + - '"policy" in common_policy' + - '"policy" in common_policy.policy' + - '"policy_metadata" in common_policy.policy' + - '"description" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.description == 'Updated Description' + - policy_metadata.firewall_policy_name == common_policy_name + - policy_metadata.firewall_policy_arn == common_policy_arn + - policy_metadata.firewall_policy_id == common_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + vars: + policy_data: '{{ common_policy.policy.policy }}' + policy_metadata: '{{ common_policy.policy.policy_metadata }}' + + - name: 'C - Update Description (idempotency)' + networkfirewall_policy: + name: '{{ common_policy_name }}' + state: present + description: 'Updated Description' + register: common_policy + + - assert: + that: + - common_policy is not changed + - '"policy" in common_policy' + - '"policy" in common_policy.policy' + - '"policy_metadata" in common_policy.policy' + - '"description" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.description == 'Updated Description' + - policy_metadata.firewall_policy_name == common_policy_name + - policy_metadata.firewall_policy_arn == common_policy_arn + - policy_metadata.firewall_policy_id == common_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + vars: + policy_data: '{{ common_policy.policy.policy }}' + policy_metadata: '{{ common_policy.policy.policy_metadata }}' + + ################################################################### + # Tags + + - name: '(CHECK) C - Set Tags' + check_mode: True + networkfirewall_policy: + name: '{{ common_policy_name }}' + state: present + tags: '{{ first_tags }}' + register: common_policy + + - assert: + that: + - common_policy is changed + - '"policy" in common_policy' + - '"policy" in common_policy.policy' + - '"policy_metadata" in common_policy.policy' + - '"description" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.description == 'Updated Description' + - policy_metadata.firewall_policy_name == common_policy_name + - policy_metadata.firewall_policy_arn == common_policy_arn + - policy_metadata.firewall_policy_id == common_policy_id + - policy_metadata.tags == first_tags + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + vars: + policy_data: '{{ common_policy.policy.policy }}' + policy_metadata: '{{ common_policy.policy.policy_metadata }}' + + - name: 'C - Set Tags' + networkfirewall_policy: + name: '{{ common_policy_name }}' + state: present + tags: '{{ first_tags }}' + register: common_policy + + - assert: + that: + - common_policy is changed + - '"policy" in common_policy' + - '"policy" in common_policy.policy' + - '"policy_metadata" in common_policy.policy' + - '"description" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.description == 'Updated Description' + - policy_metadata.firewall_policy_name == common_policy_name + - policy_metadata.firewall_policy_arn == common_policy_arn + - policy_metadata.firewall_policy_id == common_policy_id + - policy_metadata.tags == first_tags + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + vars: + policy_data: '{{ common_policy.policy.policy }}' + policy_metadata: '{{ common_policy.policy.policy_metadata }}' + + - name: '(CHECK) C - Set Tags (idempotency)' + check_mode: True + networkfirewall_policy: + name: '{{ common_policy_name }}' + state: present + tags: '{{ first_tags }}' + register: common_policy + + - assert: + that: + - common_policy is not changed + - '"policy" in common_policy' + - '"policy" in common_policy.policy' + - '"policy_metadata" in common_policy.policy' + - '"description" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.description == 'Updated Description' + - policy_metadata.firewall_policy_name == common_policy_name + - policy_metadata.firewall_policy_arn == common_policy_arn + - policy_metadata.firewall_policy_id == common_policy_id + - policy_metadata.tags == first_tags + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + vars: + policy_data: '{{ common_policy.policy.policy }}' + policy_metadata: '{{ common_policy.policy.policy_metadata }}' + + - name: 'C - Set Tags (idempotency)' + networkfirewall_policy: + name: '{{ common_policy_name }}' + state: present + tags: '{{ first_tags }}' + register: common_policy + + - assert: + that: + - common_policy is not changed + - '"policy" in common_policy' + - '"policy" in common_policy.policy' + - '"policy_metadata" in common_policy.policy' + - '"description" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.description == 'Updated Description' + - policy_metadata.firewall_policy_name == common_policy_name + - policy_metadata.firewall_policy_arn == common_policy_arn + - policy_metadata.firewall_policy_id == common_policy_id + - policy_metadata.tags == first_tags + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + vars: + policy_data: '{{ common_policy.policy.policy }}' + policy_metadata: '{{ common_policy.policy.policy_metadata }}' + + ### + + - name: '(CHECK) C - Update Tags with purge' + check_mode: True + networkfirewall_policy: + name: '{{ common_policy_name }}' + state: present + tags: '{{ second_tags }}' + register: common_policy + + - assert: + that: + - common_policy is changed + - '"policy" in common_policy' + - '"policy" in common_policy.policy' + - '"policy_metadata" in common_policy.policy' + - '"description" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.description == 'Updated Description' + - policy_metadata.firewall_policy_name == common_policy_name + - policy_metadata.firewall_policy_arn == common_policy_arn + - policy_metadata.firewall_policy_id == common_policy_id + - policy_metadata.tags == second_tags + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + vars: + policy_data: '{{ common_policy.policy.policy }}' + policy_metadata: '{{ common_policy.policy.policy_metadata }}' + + - name: 'C - Update Tags with purge' + networkfirewall_policy: + name: '{{ common_policy_name }}' + state: present + tags: '{{ second_tags }}' + register: common_policy + + - assert: + that: + - common_policy is changed + - '"policy" in common_policy' + - '"policy" in common_policy.policy' + - '"policy_metadata" in common_policy.policy' + - '"description" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.description == 'Updated Description' + - policy_metadata.firewall_policy_name == common_policy_name + - policy_metadata.firewall_policy_arn == common_policy_arn + - policy_metadata.firewall_policy_id == common_policy_id + - policy_metadata.tags == second_tags + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + vars: + policy_data: '{{ common_policy.policy.policy }}' + policy_metadata: '{{ common_policy.policy.policy_metadata }}' + + - name: '(CHECK) C - Update Tags with purge (idempotency)' + check_mode: True + networkfirewall_policy: + name: '{{ common_policy_name }}' + state: present + tags: '{{ second_tags }}' + register: common_policy + + - assert: + that: + - common_policy is not changed + - '"policy" in common_policy' + - '"policy" in common_policy.policy' + - '"policy_metadata" in common_policy.policy' + - '"description" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.description == 'Updated Description' + - policy_metadata.firewall_policy_name == common_policy_name + - policy_metadata.firewall_policy_arn == common_policy_arn + - policy_metadata.firewall_policy_id == common_policy_id + - policy_metadata.tags == second_tags + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + vars: + policy_data: '{{ common_policy.policy.policy }}' + policy_metadata: '{{ common_policy.policy.policy_metadata }}' + + - name: 'C - Update Tags with purge (idempotency)' + networkfirewall_policy: + name: '{{ common_policy_name }}' + state: present + tags: '{{ second_tags }}' + register: common_policy + + - assert: + that: + - common_policy is not changed + - '"policy" in common_policy' + - '"policy" in common_policy.policy' + - '"policy_metadata" in common_policy.policy' + - '"description" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.description == 'Updated Description' + - policy_metadata.firewall_policy_name == common_policy_name + - policy_metadata.firewall_policy_arn == common_policy_arn + - policy_metadata.firewall_policy_id == common_policy_id + - policy_metadata.tags == second_tags + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + vars: + policy_data: '{{ common_policy.policy.policy }}' + policy_metadata: '{{ common_policy.policy.policy_metadata }}' + + ### + + - name: '(CHECK) C - Update tags without purge' + check_mode: True + networkfirewall_policy: + name: '{{ common_policy_name }}' + state: present + tags: '{{ third_tags }}' + purge_tags: False + register: common_policy + + - assert: + that: + - common_policy is changed + - '"policy" in common_policy' + - '"policy" in common_policy.policy' + - '"policy_metadata" in common_policy.policy' + - '"description" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.description == 'Updated Description' + - policy_metadata.firewall_policy_name == common_policy_name + - policy_metadata.firewall_policy_arn == common_policy_arn + - policy_metadata.firewall_policy_id == common_policy_id + - policy_metadata.tags == final_tags + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + vars: + policy_data: '{{ common_policy.policy.policy }}' + policy_metadata: '{{ common_policy.policy.policy_metadata }}' + + - name: 'C - Update tags without purge' + networkfirewall_policy: + name: '{{ common_policy_name }}' + state: present + tags: '{{ third_tags }}' + purge_tags: False + register: common_policy + + - assert: + that: + - common_policy is changed + - '"policy" in common_policy' + - '"policy" in common_policy.policy' + - '"policy_metadata" in common_policy.policy' + - '"description" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.description == 'Updated Description' + - policy_metadata.firewall_policy_name == common_policy_name + - policy_metadata.firewall_policy_arn == common_policy_arn + - policy_metadata.firewall_policy_id == common_policy_id + - policy_metadata.tags == final_tags + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + vars: + policy_data: '{{ common_policy.policy.policy }}' + policy_metadata: '{{ common_policy.policy.policy_metadata }}' + + - name: '(CHECK) C - Update tags without purge (idempotency)' + check_mode: True + networkfirewall_policy: + name: '{{ common_policy_name }}' + state: present + tags: '{{ third_tags }}' + purge_tags: False + register: common_policy + + - assert: + that: + - common_policy is not changed + - '"policy" in common_policy' + - '"policy" in common_policy.policy' + - '"policy_metadata" in common_policy.policy' + - '"description" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.description == 'Updated Description' + - policy_metadata.firewall_policy_name == common_policy_name + - policy_metadata.firewall_policy_arn == common_policy_arn + - policy_metadata.firewall_policy_id == common_policy_id + - policy_metadata.tags == final_tags + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + vars: + policy_data: '{{ common_policy.policy.policy }}' + policy_metadata: '{{ common_policy.policy.policy_metadata }}' + + - name: 'C - Update tags without purge (idempotency)' + networkfirewall_policy: + name: '{{ common_policy_name }}' + state: present + tags: '{{ third_tags }}' + purge_tags: False + register: common_policy + + - assert: + that: + - common_policy is not changed + - '"policy" in common_policy' + - '"policy" in common_policy.policy' + - '"policy_metadata" in common_policy.policy' + - '"description" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.description == 'Updated Description' + - policy_metadata.firewall_policy_name == common_policy_name + - policy_metadata.firewall_policy_arn == common_policy_arn + - policy_metadata.firewall_policy_id == common_policy_id + - policy_metadata.tags == final_tags + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + vars: + policy_data: '{{ common_policy.policy.policy }}' + policy_metadata: '{{ common_policy.policy.policy_metadata }}' + + ################################################################### + # Delete policy + + - name: '(CHECK) C - Delete policy with default order' + check_mode: True + networkfirewall_policy: + name: '{{ common_policy_name }}' + state: absent + register: common_policy + + - assert: + that: + - common_policy is changed + + - name: 'C - Delete policy with default order' + networkfirewall_policy: + name: '{{ common_policy_name }}' + state: absent + register: common_policy + + - assert: + that: + - common_policy is changed + + - name: 'C - Check policy is gone' + networkfirewall_policy_info: + arn: '{{ common_policy_arn }}' + register: common_policy_info + + - assert: + that: + - common_policy_info is successful + - common_policy_info.policies | length == 0 + + - name: '(CHECK) C - Delete policy with default order (idempotency)' + check_mode: True + networkfirewall_policy: + name: '{{ common_policy_name }}' + state: absent + register: common_policy + + - assert: + that: + - common_policy is not changed + + - name: 'C - Delete policy with default order (idempotency)' + networkfirewall_policy: + name: '{{ common_policy_name }}' + state: absent + register: common_policy + + - assert: + that: + - common_policy is not changed + + always: + - name: 'C - Cleanup firewall policy' + networkfirewall_policy: + name: '{{ common_policy_name }}' + state: absent + wait: False + ignore_errors: True diff --git a/ansible_collections/community/aws/tests/integration/targets/networkfirewall_policy/tasks/default_order.yml b/ansible_collections/community/aws/tests/integration/targets/networkfirewall_policy/tasks/default_order.yml new file mode 100644 index 000000000..50df7e7ab --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/networkfirewall_policy/tasks/default_order.yml @@ -0,0 +1,1223 @@ +--- +# Tests the basic manipulation of Policies with 'default' rule order +# +# In Scope: +# - Creation +# - (Denied) update of rule order +# - Managing stateful Rule Groups +# - Adding default actions for stateless flows (basic functionality) +# - (Denied) Adding default actions for stateful flows +# - Deletion +# +# Out of Scope +# - Adding Stateless Rule Groups (not supported by networkfirewall_rule_group) +# - Updating tags ('common.yaml') +# - Updating description ('common.yaml') +# - Updating custom actions for stateless flows ('actions.yaml') +# - Updating actions for stateless flows ('actions.yaml') + +- vars: + default_policy_name: '{{ policy_name_prefix }}-DefaultOrder' + managed_group_arn: 'arn:aws:network-firewall:{{ aws_region }}:aws-managed:stateful-rulegroup/BotNetCommandAndControlDomainsActionOrder' + block: + ################################################################### + # Creation + - name: '(CHECK) D - Create a simple firewall policy with `default` rule order' + check_mode: True + networkfirewall_policy: + name: '{{ default_policy_name }}' + stateful_rule_order: 'default' + state: present + register: default_policy + + - assert: + that: + - default_policy is changed + - '"policy" in default_policy' + - '"policy" in default_policy.policy' + - '"policy_metadata" in default_policy.policy' + - '"firewall_policy_name" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - policy_metadata.tags == {} + # These are *not* available because the policy hasn't been created + - '"firewall_policy_arn" not in policy_metadata' + - '"firewall_policy_id" not in policy_metadata' + - '"firewall_policy_status" not in policy_metadata' + vars: + policy_data: '{{ default_policy.policy.policy }}' + policy_metadata: '{{ default_policy.policy.policy_metadata }}' + + - name: 'D - Create a simple firewall policy with `default` rule order' + networkfirewall_policy: + name: '{{ default_policy_name }}' + stateful_rule_order: 'default' + state: present + register: default_policy + + - assert: + that: + - default_policy is changed + - '"policy" in default_policy' + - '"policy" in default_policy.policy' + - '"policy_metadata" in default_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == default_policy_name + - policy_metadata.firewall_policy_arn.startswith(account_arn) + - policy_metadata.firewall_policy_arn.endswith(default_policy_name) + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + vars: + policy_data: '{{ default_policy.policy.policy }}' + policy_metadata: '{{ default_policy.policy.policy_metadata }}' + + - name: 'D - Save Policy ID/ARN for later' + set_fact: + default_policy_id: '{{ policy_metadata.firewall_policy_id }}' + default_policy_arn: '{{ policy_metadata.firewall_policy_arn }}' + vars: + policy_metadata: '{{ default_policy.policy.policy_metadata }}' + + - name: '(CHECK) D - Create a simple firewall policy with `default` rule order (idempotency)' + check_mode: True + networkfirewall_policy: + name: '{{ default_policy_name }}' + stateful_rule_order: 'default' + state: present + register: default_policy + + - assert: + that: + - default_policy is not changed + - '"policy" in default_policy' + - '"policy" in default_policy.policy' + - '"policy_metadata" in default_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == default_policy_name + - policy_metadata.firewall_policy_arn == default_policy_arn + - policy_metadata.firewall_policy_id == default_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + vars: + policy_data: '{{ default_policy.policy.policy }}' + policy_metadata: '{{ default_policy.policy.policy_metadata }}' + + - name: 'D - Create a simple firewall policy with `default` rule order (idempotency)' + networkfirewall_policy: + name: '{{ default_policy_name }}' + stateful_rule_order: 'default' + state: present + register: default_policy + + - assert: + that: + - default_policy is not changed + - '"policy" in default_policy' + - '"policy" in default_policy.policy' + - '"policy_metadata" in default_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == default_policy_name + - policy_metadata.firewall_policy_arn == default_policy_arn + - policy_metadata.firewall_policy_id == default_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + vars: + policy_data: '{{ default_policy.policy.policy }}' + policy_metadata: '{{ default_policy.policy.policy_metadata }}' + + ### + + - name: '(CHECK) D - Create a simple firewall policy with `default` rule order - test reference by ARN (idempotency)' + check_mode: True + networkfirewall_policy: + arn: '{{ default_policy_arn }}' + stateful_rule_order: 'default' + state: present + register: default_policy + + - assert: + that: + - default_policy is not changed + - '"policy" in default_policy' + - '"policy" in default_policy.policy' + - '"policy_metadata" in default_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == default_policy_name + - policy_metadata.firewall_policy_arn == default_policy_arn + - policy_metadata.firewall_policy_id == default_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + vars: + policy_data: '{{ default_policy.policy.policy }}' + policy_metadata: '{{ default_policy.policy.policy_metadata }}' + + - name: 'D - Create a simple firewall policy with `default` rule order (idempotency)' + networkfirewall_policy: + arn: '{{ default_policy_arn }}' + stateful_rule_order: 'default' + state: present + register: default_policy + + - assert: + that: + - default_policy is not changed + - '"policy" in default_policy' + - '"policy" in default_policy.policy' + - '"policy_metadata" in default_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == default_policy_name + - policy_metadata.firewall_policy_arn == default_policy_arn + - policy_metadata.firewall_policy_id == default_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + vars: + policy_data: '{{ default_policy.policy.policy }}' + policy_metadata: '{{ default_policy.policy.policy_metadata }}' + + ################################################################### + # Update Stateful Rule order + + - name: '(CHECK) D - Update stateful rule order - DENIED' + check_mode: True + networkfirewall_policy: + name: '{{ default_policy_name }}' + state: present + stateful_rule_order: strict + register: default_policy + ignore_errors: True + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + + - assert: + that: + - default_policy is failed + + - name: 'D - Update stateful rule order - DENIED' + networkfirewall_policy: + name: '{{ default_policy_name }}' + state: present + stateful_rule_order: strict + register: default_policy + ignore_errors: True + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + + - assert: + that: + - default_policy is failed + + ################################################################### + # Stateful Rule Group management + + - name: '(CHECK) D - Add stateful rule group' + check_mode: True + networkfirewall_policy: + name: '{{ default_policy_name }}' + state: present + stateful_rule_groups: + - '{{ default_group_names[0] }}' + register: default_policy + + - assert: + that: + - default_policy is changed + - '"policy" in default_policy' + - '"policy" in default_policy.policy' + - '"policy_metadata" in default_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - '"stateful_rule_group_references" in policy_data' + - policy_metadata.firewall_policy_name == default_policy_name + - policy_metadata.firewall_policy_arn == default_policy_arn + - policy_metadata.firewall_policy_id == default_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateful_rule_group_references | length == 1 + - default_group_arns[0] in stateful_rule_arns + vars: + policy_data: '{{ default_policy.policy.policy }}' + policy_metadata: '{{ default_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + - name: 'D - Add stateful rule group' + networkfirewall_policy: + name: '{{ default_policy_name }}' + state: present + stateful_rule_groups: + - '{{ default_group_names[0] }}' + register: default_policy + + - assert: + that: + - default_policy is changed + - '"policy" in default_policy' + - '"policy" in default_policy.policy' + - '"policy_metadata" in default_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == default_policy_name + - policy_metadata.firewall_policy_arn == default_policy_arn + - policy_metadata.firewall_policy_id == default_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateful_rule_group_references | length == 1 + - default_group_arns[0] in stateful_rule_arns + vars: + policy_data: '{{ default_policy.policy.policy }}' + policy_metadata: '{{ default_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + - name: '(CHECK) D - Add stateful rule group (idempotency)' + check_mode: True + networkfirewall_policy: + name: '{{ default_policy_name }}' + state: present + stateful_rule_groups: + - '{{ default_group_names[0] }}' + register: default_policy + + - assert: + that: + - default_policy is not changed + - '"policy" in default_policy' + - '"policy" in default_policy.policy' + - '"policy_metadata" in default_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == default_policy_name + - policy_metadata.firewall_policy_arn == default_policy_arn + - policy_metadata.firewall_policy_id == default_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateful_rule_group_references | length == 1 + - default_group_arns[0] in stateful_rule_arns + vars: + policy_data: '{{ default_policy.policy.policy }}' + policy_metadata: '{{ default_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + - name: 'D - Add stateful rule group (idempotency)' + networkfirewall_policy: + name: '{{ default_policy_name }}' + state: present + stateful_rule_groups: + - '{{ default_group_names[0] }}' + register: default_policy + + - assert: + that: + - default_policy is not changed + - '"policy" in default_policy' + - '"policy" in default_policy.policy' + - '"policy_metadata" in default_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == default_policy_name + - policy_metadata.firewall_policy_arn == default_policy_arn + - policy_metadata.firewall_policy_id == default_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateful_rule_group_references | length == 1 + - default_group_arns[0] in stateful_rule_arns + vars: + policy_data: '{{ default_policy.policy.policy }}' + policy_metadata: '{{ default_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + ### + + - name: '(CHECK) D - Add managed stateful rule group' + check_mode: True + networkfirewall_policy: + name: '{{ default_policy_name }}' + state: present + stateful_rule_groups: + - '{{ default_group_names[0] }}' + - '{{ managed_group_arn }}' + register: default_policy + + - assert: + that: + - default_policy is changed + - '"policy" in default_policy' + - '"policy" in default_policy.policy' + - '"policy_metadata" in default_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - '"stateful_rule_group_references" in policy_data' + - policy_metadata.firewall_policy_name == default_policy_name + - policy_metadata.firewall_policy_arn == default_policy_arn + - policy_metadata.firewall_policy_id == default_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateful_rule_group_references | length == 2 + - default_group_arns[0] in stateful_rule_arns + - managed_group_arn in stateful_rule_arns + vars: + policy_data: '{{ default_policy.policy.policy }}' + policy_metadata: '{{ default_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + - name: 'D - Add managed stateful rule group' + networkfirewall_policy: + name: '{{ default_policy_name }}' + state: present + stateful_rule_groups: + - '{{ default_group_names[0] }}' + - '{{ managed_group_arn }}' + register: default_policy + + - assert: + that: + - default_policy is changed + - '"policy" in default_policy' + - '"policy" in default_policy.policy' + - '"policy_metadata" in default_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == default_policy_name + - policy_metadata.firewall_policy_arn == default_policy_arn + - policy_metadata.firewall_policy_id == default_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateful_rule_group_references | length == 2 + - default_group_arns[0] in stateful_rule_arns + - managed_group_arn in stateful_rule_arns + vars: + policy_data: '{{ default_policy.policy.policy }}' + policy_metadata: '{{ default_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + - name: '(CHECK) D - Add managed stateful rule group (idempotency)' + check_mode: True + networkfirewall_policy: + name: '{{ default_policy_name }}' + state: present + stateful_rule_groups: + - '{{ default_group_names[0] }}' + - '{{ managed_group_arn }}' + register: default_policy + + - assert: + that: + - default_policy is not changed + - '"policy" in default_policy' + - '"policy" in default_policy.policy' + - '"policy_metadata" in default_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == default_policy_name + - policy_metadata.firewall_policy_arn == default_policy_arn + - policy_metadata.firewall_policy_id == default_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateful_rule_group_references | length == 2 + - default_group_arns[0] in stateful_rule_arns + - managed_group_arn in stateful_rule_arns + vars: + policy_data: '{{ default_policy.policy.policy }}' + policy_metadata: '{{ default_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + - name: 'D - Add managed stateful rule group (idempotency)' + networkfirewall_policy: + name: '{{ default_policy_name }}' + state: present + stateful_rule_groups: + - '{{ default_group_names[0] }}' + - '{{ managed_group_arn }}' + register: default_policy + + - assert: + that: + - default_policy is not changed + - '"policy" in default_policy' + - '"policy" in default_policy.policy' + - '"policy_metadata" in default_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == default_policy_name + - policy_metadata.firewall_policy_arn == default_policy_arn + - policy_metadata.firewall_policy_id == default_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateful_rule_group_references | length == 2 + - default_group_arns[0] in stateful_rule_arns + - managed_group_arn in stateful_rule_arns + vars: + policy_data: '{{ default_policy.policy.policy }}' + policy_metadata: '{{ default_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + ### + + - name: '(CHECK) D - Replace stateful rule groups' + check_mode: True + networkfirewall_policy: + name: '{{ default_policy_name }}' + state: present + stateful_rule_groups: + - '{{ default_group_names[1] }}' + - '{{ default_group_arns[2] }}' + register: default_policy + + - assert: + that: + - default_policy is changed + - '"policy" in default_policy' + - '"policy" in default_policy.policy' + - '"policy_metadata" in default_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - '"stateful_rule_group_references" in policy_data' + - policy_metadata.firewall_policy_name == default_policy_name + - policy_metadata.firewall_policy_arn == default_policy_arn + - policy_metadata.firewall_policy_id == default_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateful_rule_group_references | length == 2 + - default_group_arns[1] in stateful_rule_arns + - default_group_arns[2] in stateful_rule_arns + vars: + policy_data: '{{ default_policy.policy.policy }}' + policy_metadata: '{{ default_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + - name: 'D - Replace stateful rule groups' + networkfirewall_policy: + name: '{{ default_policy_name }}' + state: present + stateful_rule_groups: + - '{{ default_group_names[1] }}' + - '{{ default_group_arns[2] }}' + register: default_policy + + - assert: + that: + - default_policy is changed + - '"policy" in default_policy' + - '"policy" in default_policy.policy' + - '"policy_metadata" in default_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == default_policy_name + - policy_metadata.firewall_policy_arn == default_policy_arn + - policy_metadata.firewall_policy_id == default_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateful_rule_group_references | length == 2 + - default_group_arns[1] in stateful_rule_arns + - default_group_arns[2] in stateful_rule_arns + vars: + policy_data: '{{ default_policy.policy.policy }}' + policy_metadata: '{{ default_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + - name: '(CHECK) D - Replace stateful rule groups (idempotency)' + check_mode: True + networkfirewall_policy: + name: '{{ default_policy_name }}' + state: present + stateful_rule_groups: + - '{{ default_group_names[1] }}' + - '{{ default_group_arns[2] }}' + register: default_policy + + - assert: + that: + - default_policy is not changed + - '"policy" in default_policy' + - '"policy" in default_policy.policy' + - '"policy_metadata" in default_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == default_policy_name + - policy_metadata.firewall_policy_arn == default_policy_arn + - policy_metadata.firewall_policy_id == default_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateful_rule_group_references | length == 2 + - default_group_arns[1] in stateful_rule_arns + - default_group_arns[2] in stateful_rule_arns + vars: + policy_data: '{{ default_policy.policy.policy }}' + policy_metadata: '{{ default_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + - name: 'D - Replace stateful rule groups (idempotency)' + networkfirewall_policy: + name: '{{ default_policy_name }}' + state: present + stateful_rule_groups: + - '{{ default_group_names[1] }}' + - '{{ default_group_arns[2] }}' + register: default_policy + + - assert: + that: + - default_policy is not changed + - '"policy" in default_policy' + - '"policy" in default_policy.policy' + - '"policy_metadata" in default_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == default_policy_name + - policy_metadata.firewall_policy_arn == default_policy_arn + - policy_metadata.firewall_policy_id == default_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateful_rule_group_references | length == 2 + - default_group_arns[1] in stateful_rule_arns + - default_group_arns[2] in stateful_rule_arns + vars: + policy_data: '{{ default_policy.policy.policy }}' + policy_metadata: '{{ default_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + ### + + - name: '(CHECK) D - Replace stateful rule groups - ARN vs Name (idempotency)' + check_mode: True + networkfirewall_policy: + name: '{{ default_policy_name }}' + state: present + stateful_rule_groups: + - '{{ default_group_arns[1] }}' + - '{{ default_group_names[2] }}' + register: default_policy + + - assert: + that: + - default_policy is not changed + - '"policy" in default_policy' + - '"policy" in default_policy.policy' + - '"policy_metadata" in default_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == default_policy_name + - policy_metadata.firewall_policy_arn == default_policy_arn + - policy_metadata.firewall_policy_id == default_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateful_rule_group_references | length == 2 + - default_group_arns[1] in stateful_rule_arns + - default_group_arns[2] in stateful_rule_arns + vars: + policy_data: '{{ default_policy.policy.policy }}' + policy_metadata: '{{ default_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + - name: 'D - Replace stateful rule groups - ARN vs Name (idempotency)' + networkfirewall_policy: + name: '{{ default_policy_name }}' + state: present + stateful_rule_groups: + - '{{ default_group_arns[1] }}' + - '{{ default_group_names[2] }}' + register: default_policy + + - assert: + that: + - default_policy is not changed + - '"policy" in default_policy' + - '"policy" in default_policy.policy' + - '"policy_metadata" in default_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == default_policy_name + - policy_metadata.firewall_policy_arn == default_policy_arn + - policy_metadata.firewall_policy_id == default_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateful_rule_group_references | length == 2 + - default_group_arns[1] in stateful_rule_arns + - default_group_arns[2] in stateful_rule_arns + vars: + policy_data: '{{ default_policy.policy.policy }}' + policy_metadata: '{{ default_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + ### + + # When using 'default' ordering, the order of rule groups is not + # guaranteed and the rules are prioritised by type (pass, log, drop) not by + # the order they're listed. + + - name: '(CHECK) D - Replace stateful rule groups - change order (idempotency)' + check_mode: True + networkfirewall_policy: + name: '{{ default_policy_name }}' + state: present + stateful_rule_groups: + - '{{ default_group_arns[2] }}' + - '{{ default_group_arns[1] }}' + register: default_policy + + - assert: + that: + - default_policy is not changed + - '"policy" in default_policy' + - '"policy" in default_policy.policy' + - '"policy_metadata" in default_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == default_policy_name + - policy_metadata.firewall_policy_arn == default_policy_arn + - policy_metadata.firewall_policy_id == default_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateful_rule_group_references | length == 2 + - default_group_arns[1] in stateful_rule_arns + - default_group_arns[2] in stateful_rule_arns + vars: + policy_data: '{{ default_policy.policy.policy }}' + policy_metadata: '{{ default_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + - name: 'D - Replace stateful rule groups - change order (idempotency)' + networkfirewall_policy: + name: '{{ default_policy_name }}' + state: present + stateful_rule_groups: + - '{{ default_group_arns[2] }}' + - '{{ default_group_arns[1] }}' + register: default_policy + + - assert: + that: + - default_policy is not changed + - '"policy" in default_policy' + - '"policy" in default_policy.policy' + - '"policy_metadata" in default_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == default_policy_name + - policy_metadata.firewall_policy_arn == default_policy_arn + - policy_metadata.firewall_policy_id == default_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateful_rule_group_references | length == 2 + - default_group_arns[1] in stateful_rule_arns + - default_group_arns[2] in stateful_rule_arns + vars: + policy_data: '{{ default_policy.policy.policy }}' + policy_metadata: '{{ default_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + ################################################################### + # Update Stateless flow default actions (basic) + + - name: '(CHECK) D - Update Stateless flow default action' + check_mode: True + networkfirewall_policy: + name: '{{ default_policy_name }}' + state: present + stateless_default_actions: + - 'aws:drop' + register: default_policy + + - assert: + that: + - default_policy is changed + - '"policy" in default_policy' + - '"policy" in default_policy.policy' + - '"policy_metadata" in default_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - '"stateful_rule_group_references" in policy_data' + - policy_metadata.firewall_policy_name == default_policy_name + - policy_metadata.firewall_policy_arn == default_policy_arn + - policy_metadata.firewall_policy_id == default_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:drop"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateful_rule_group_references | length == 2 + - default_group_arns[1] in stateful_rule_arns + - default_group_arns[2] in stateful_rule_arns + vars: + policy_data: '{{ default_policy.policy.policy }}' + policy_metadata: '{{ default_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + - name: 'D - Update Stateless flow default action' + networkfirewall_policy: + name: '{{ default_policy_name }}' + state: present + stateless_default_actions: + - 'aws:drop' + register: default_policy + + - assert: + that: + - default_policy is changed + - '"policy" in default_policy' + - '"policy" in default_policy.policy' + - '"policy_metadata" in default_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == default_policy_name + - policy_metadata.firewall_policy_arn == default_policy_arn + - policy_metadata.firewall_policy_id == default_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:drop"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateful_rule_group_references | length == 2 + - default_group_arns[1] in stateful_rule_arns + - default_group_arns[2] in stateful_rule_arns + vars: + policy_data: '{{ default_policy.policy.policy }}' + policy_metadata: '{{ default_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + - name: '(CHECK) D - Update Stateless flow default action (idempotency)' + check_mode: True + networkfirewall_policy: + name: '{{ default_policy_name }}' + state: present + stateless_default_actions: + - 'aws:drop' + register: default_policy + + - assert: + that: + - default_policy is not changed + - '"policy" in default_policy' + - '"policy" in default_policy.policy' + - '"policy_metadata" in default_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == default_policy_name + - policy_metadata.firewall_policy_arn == default_policy_arn + - policy_metadata.firewall_policy_id == default_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:drop"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateful_rule_group_references | length == 2 + - default_group_arns[1] in stateful_rule_arns + - default_group_arns[2] in stateful_rule_arns + vars: + policy_data: '{{ default_policy.policy.policy }}' + policy_metadata: '{{ default_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + - name: 'D - Update Stateless flow default action (idempotency)' + networkfirewall_policy: + name: '{{ default_policy_name }}' + state: present + stateless_default_actions: + - 'aws:drop' + register: default_policy + + - assert: + that: + - default_policy is not changed + - '"policy" in default_policy' + - '"policy" in default_policy.policy' + - '"policy_metadata" in default_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == default_policy_name + - policy_metadata.firewall_policy_arn == default_policy_arn + - policy_metadata.firewall_policy_id == default_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:drop"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateful_rule_group_references | length == 2 + - default_group_arns[1] in stateful_rule_arns + - default_group_arns[2] in stateful_rule_arns + vars: + policy_data: '{{ default_policy.policy.policy }}' + policy_metadata: '{{ default_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + ### + + - name: '(CHECK) D - Update Stateless fragmented flow default action' + check_mode: True + networkfirewall_policy: + name: '{{ default_policy_name }}' + state: present + stateless_fragment_default_actions: + - 'aws:pass' + register: default_policy + + - assert: + that: + - default_policy is changed + - '"policy" in default_policy' + - '"policy" in default_policy.policy' + - '"policy_metadata" in default_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - '"stateful_rule_group_references" in policy_data' + - policy_metadata.firewall_policy_name == default_policy_name + - policy_metadata.firewall_policy_arn == default_policy_arn + - policy_metadata.firewall_policy_id == default_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:drop"] + - policy_data.stateless_fragment_default_actions == ["aws:pass"] + - policy_data.stateful_rule_group_references | length == 2 + - default_group_arns[1] in stateful_rule_arns + - default_group_arns[2] in stateful_rule_arns + vars: + policy_data: '{{ default_policy.policy.policy }}' + policy_metadata: '{{ default_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + - name: 'D - Update Stateless fragmented flow default action' + networkfirewall_policy: + name: '{{ default_policy_name }}' + state: present + stateless_fragment_default_actions: + - 'aws:pass' + register: default_policy + + - assert: + that: + - default_policy is changed + - '"policy" in default_policy' + - '"policy" in default_policy.policy' + - '"policy_metadata" in default_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == default_policy_name + - policy_metadata.firewall_policy_arn == default_policy_arn + - policy_metadata.firewall_policy_id == default_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:drop"] + - policy_data.stateless_fragment_default_actions == ["aws:pass"] + - policy_data.stateful_rule_group_references | length == 2 + - default_group_arns[1] in stateful_rule_arns + - default_group_arns[2] in stateful_rule_arns + vars: + policy_data: '{{ default_policy.policy.policy }}' + policy_metadata: '{{ default_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + - name: '(CHECK) D - Update Stateless fragmented flow default action (idempotency)' + check_mode: True + networkfirewall_policy: + name: '{{ default_policy_name }}' + state: present + stateless_fragment_default_actions: + - 'aws:pass' + register: default_policy + + - assert: + that: + - default_policy is not changed + - '"policy" in default_policy' + - '"policy" in default_policy.policy' + - '"policy_metadata" in default_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == default_policy_name + - policy_metadata.firewall_policy_arn == default_policy_arn + - policy_metadata.firewall_policy_id == default_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:drop"] + - policy_data.stateless_fragment_default_actions == ["aws:pass"] + - policy_data.stateful_rule_group_references | length == 2 + - default_group_arns[1] in stateful_rule_arns + - default_group_arns[2] in stateful_rule_arns + vars: + policy_data: '{{ default_policy.policy.policy }}' + policy_metadata: '{{ default_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + - name: 'D - Update Stateless fragmented flow default action (idempotency)' + networkfirewall_policy: + name: '{{ default_policy_name }}' + state: present + stateless_fragment_default_actions: + - 'aws:pass' + register: default_policy + + - assert: + that: + - default_policy is not changed + - '"policy" in default_policy' + - '"policy" in default_policy.policy' + - '"policy_metadata" in default_policy.policy' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - policy_metadata.firewall_policy_name == default_policy_name + - policy_metadata.firewall_policy_arn == default_policy_arn + - policy_metadata.firewall_policy_id == default_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:drop"] + - policy_data.stateless_fragment_default_actions == ["aws:pass"] + - policy_data.stateful_rule_group_references | length == 2 + - default_group_arns[1] in stateful_rule_arns + - default_group_arns[2] in stateful_rule_arns + vars: + policy_data: '{{ default_policy.policy.policy }}' + policy_metadata: '{{ default_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + ### + + ################################################################### + # Update Stateful flow default actions + + - name: '(CHECK) D - Set default action for stateful flows - DENIED' + check_mode: True + networkfirewall_policy: + name: '{{ default_policy_name }}' + state: present + stateful_default_actions: + - 'aws:drop_strict' + register: default_policy + ignore_errors: True + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + + - assert: + that: + - default_policy is failed + + - name: 'D - Set default action for stateful flows - DENIED' + networkfirewall_policy: + name: '{{ default_policy_name }}' + state: present + stateful_default_actions: + - 'aws:drop_strict' + register: default_policy + ignore_errors: True + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + + - assert: + that: + - default_policy is failed + + ################################################################### + # Delete policy + + - name: '(CHECK) D - Delete policy with default order' + check_mode: True + networkfirewall_policy: + name: '{{ default_policy_name }}' + state: absent + wait: False + register: default_policy + + - assert: + that: + - default_policy is changed + + - name: 'D - Delete policy with default order' + networkfirewall_policy: + name: '{{ default_policy_name }}' + state: absent + wait: False + register: default_policy + + - assert: + that: + - default_policy is changed + + - name: '(CHECK) D - Delete policy with default order (idempotency)' + check_mode: True + networkfirewall_policy: + name: '{{ default_policy_name }}' + state: absent + wait: False + register: default_policy + + - assert: + that: + - default_policy is not changed + + - name: 'D - Delete policy with default order (idempotency)' + networkfirewall_policy: + name: '{{ default_policy_name }}' + state: absent + wait: False + register: default_policy + + - assert: + that: + - default_policy is not changed + + always: + - name: 'D - Cleanup firewall policy' + networkfirewall_policy: + name: '{{ default_policy_name }}' + state: absent + wait: False + ignore_errors: True diff --git a/ansible_collections/community/aws/tests/integration/targets/networkfirewall_policy/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/networkfirewall_policy/tasks/main.yml new file mode 100644 index 000000000..d3890c680 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/networkfirewall_policy/tasks/main.yml @@ -0,0 +1,43 @@ +--- +- module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key | default(omit) }}' + aws_secret_key: '{{ aws_secret_key | default(omit) }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region | default(omit) }}' + collections: + - amazon.aws + - community.aws + block: + # Fetch some info about the account so we can build ARNs + - aws_caller_info: {} + register: caller_info + - name: 'Generate the ARN pattern to search for' + vars: + _caller_info: '{{ caller_info.arn.split(":") }}' + _base_arn: 'arn:{{_caller_info[1]}}:network-firewall:{{aws_region}}' + set_fact: + account_arn: '{{_base_arn}}:{{_caller_info[4]}}:firewall-policy/' + + # Prepare the resources we'll need for testing the policies + - include_tasks: 'setup.yml' + + # Tests specifically related to policies using 'default' rule order + - include_tasks: 'default_order.yml' + + # Tests specifically related to policies using 'strict' rule order + - include_tasks: 'strict_order.yml' + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + + - include_tasks: 'actions.yml' + + # Tests related to 'common' attributes on policies (description/tagging) + # Note: Unlike the other tests this includes *waiting* for the deletion of + # policies, and as such we should do this last so that the other policies + # can also finish deleting while we wait for the 'common' policy to be + # deleted. + - include_tasks: 'common.yml' + + always: + - include_tasks: 'cleanup.yml' diff --git a/ansible_collections/community/aws/tests/integration/targets/networkfirewall_policy/tasks/setup.yml b/ansible_collections/community/aws/tests/integration/targets/networkfirewall_policy/tasks/setup.yml new file mode 100644 index 000000000..27f0ebb48 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/networkfirewall_policy/tasks/setup.yml @@ -0,0 +1,54 @@ +--- +- name: Setup Rule Groups for later use + block: + # The simplest form of rule group + - name: 'Create Rule Groups with minimal settings (Default order)' + networkfirewall_rule_group: + name: '{{ default_group_name }}-{{ item }}' + type: 'stateful' + capacity: '{{ rule_group_capacity }}' + rule_strings: + - 'pass tcp any any -> any any (sid:1000001;)' + register: default_groups + loop: '{{ range(1,4,1) | list }}' + + # The simplest form of rule group + - name: 'Create Rule Groups with minimal settings (Strict order)' + networkfirewall_rule_group: + name: '{{ strict_group_name }}-{{ item }}' + type: 'stateful' + capacity: '{{ rule_group_capacity }}' + rule_strings: + - 'pass tcp any any -> any any (sid:1000001;)' + rule_order: strict + register: strict_groups + loop: '{{ range(1,4,1) | list }}' + # Setting rule order requires botocore>=1.23.23 + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + + - debug: + var: default_groups + + # Store Group Names and ARNs for later + - set_fact: + default_group_names: >- + {{ default_groups.results + | map(attribute="rule_group") + | map(attribute="rule_group_metadata") + | map(attribute="rule_group_name") | list }} + default_group_arns: >- + {{ default_groups.results + | map(attribute="rule_group") + | map(attribute="rule_group_metadata") + | map(attribute="rule_group_arn") | list }} + strict_group_names: >- + {{ strict_groups.results + | map(attribute="rule_group") + | map(attribute="rule_group_metadata") + | map(attribute="rule_group_name") | list }} + strict_group_arns: >- + {{ strict_groups.results + | map(attribute="rule_group") + | map(attribute="rule_group_metadata") + | map(attribute="rule_group_arn") | list }} diff --git a/ansible_collections/community/aws/tests/integration/targets/networkfirewall_policy/tasks/strict_order.yml b/ansible_collections/community/aws/tests/integration/targets/networkfirewall_policy/tasks/strict_order.yml new file mode 100644 index 000000000..b842eebae --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/networkfirewall_policy/tasks/strict_order.yml @@ -0,0 +1,1898 @@ +--- +# Tests the basic manipulation of Policies with 'strict' rule order +# +# In Scope: +# - Creation +# - Deletion +# - (Denied) update of rule order +# - Managing stateful Rule Groups +# - Managing default actions for stateful flows +# +# Out of Scope +# - Adding Stateless Rule Groups (not supported by networkfirewall_rule_group) +# - Updating tags ('common.yaml') +# - Updating description ('common.yaml') +# - Updating custom actions for stateless flows ('actions.yaml') +# - Updating actions for stateless flows ('actions.yaml') + +- vars: + strict_policy_name: '{{ policy_name_prefix }}-StrictOrder' + managed_group_arn: 'arn:aws:network-firewall:{{ aws_region }}:aws-managed:stateful-rulegroup/BotNetCommandAndControlDomainsStrictOrder' + block: + ################################################################### + # Creation + - name: '(CHECK) S - Create a simple firewall policy with `strict` rule order' + check_mode: True + networkfirewall_policy: + name: '{{ strict_policy_name }}' + stateful_rule_order: 'strict' + state: present + register: strict_policy + + - assert: + that: + - strict_policy is changed + - '"policy" in strict_policy' + - '"policy" in strict_policy.policy' + - '"policy_metadata" in strict_policy.policy' + - '"firewall_policy_name" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - '"stateful_engine_options" in policy_data' + - '"rule_order" in policy_data.stateful_engine_options' + - policy_data.stateful_engine_options.rule_order == "STRICT_ORDER" + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - policy_metadata.tags == {} + # These are *not* available because the policy hasn't been created + - '"firewall_policy_arn" not in policy_metadata' + - '"firewall_policy_id" not in policy_metadata' + - '"firewall_policy_status" not in policy_metadata' + vars: + policy_data: '{{ strict_policy.policy.policy }}' + policy_metadata: '{{ strict_policy.policy.policy_metadata }}' + + - name: 'S - Create a simple firewall policy with `strict` rule order' + networkfirewall_policy: + name: '{{ strict_policy_name }}' + stateful_rule_order: 'strict' + state: present + register: strict_policy + + - assert: + that: + - strict_policy is changed + - '"policy" in strict_policy' + - '"policy" in strict_policy.policy' + - '"policy_metadata" in strict_policy.policy' + - '"consumed_stateful_rule_capacity" in policy_metadata' + - '"consumed_stateless_rule_capacity" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - '"stateful_engine_options" in policy_data' + - '"rule_order" in policy_data.stateful_engine_options' + - policy_data.stateful_engine_options.rule_order == "STRICT_ORDER" + - policy_metadata.consumed_stateful_rule_capacity == 0 + - policy_metadata.consumed_stateless_rule_capacity == 0 + - policy_metadata.firewall_policy_name == strict_policy_name + - policy_metadata.firewall_policy_arn.startswith(account_arn) + - policy_metadata.firewall_policy_arn.endswith(strict_policy_name) + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + vars: + policy_data: '{{ strict_policy.policy.policy }}' + policy_metadata: '{{ strict_policy.policy.policy_metadata }}' + + - name: Save RuleGroup ID/ARN for later + set_fact: + strict_policy_id: '{{ policy_metadata.firewall_policy_id }}' + strict_policy_arn: '{{ policy_metadata.firewall_policy_arn }}' + vars: + policy_metadata: '{{ strict_policy.policy.policy_metadata }}' + + - name: '(CHECK) S - Create a simple firewall policy with `strict` rule order (idempotency)' + check_mode: True + networkfirewall_policy: + name: '{{ strict_policy_name }}' + stateful_rule_order: 'strict' + state: present + register: strict_policy + + - assert: + that: + - strict_policy is not changed + - '"policy" in strict_policy' + - '"policy" in strict_policy.policy' + - '"policy_metadata" in strict_policy.policy' + - '"consumed_stateful_rule_capacity" in policy_metadata' + - '"consumed_stateless_rule_capacity" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - '"stateful_engine_options" in policy_data' + - '"rule_order" in policy_data.stateful_engine_options' + - policy_data.stateful_engine_options.rule_order == "STRICT_ORDER" + - policy_metadata.consumed_stateful_rule_capacity == 0 + - policy_metadata.consumed_stateless_rule_capacity == 0 + - policy_metadata.firewall_policy_name == strict_policy_name + - policy_metadata.firewall_policy_arn == strict_policy_arn + - policy_metadata.firewall_policy_id == strict_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + vars: + policy_data: '{{ strict_policy.policy.policy }}' + policy_metadata: '{{ strict_policy.policy.policy_metadata }}' + + - name: 'S - Create a simple firewall policy with `strict` rule order (idempotency)' + networkfirewall_policy: + name: '{{ strict_policy_name }}' + stateful_rule_order: 'strict' + state: present + register: strict_policy + + - assert: + that: + - strict_policy is not changed + - '"policy" in strict_policy' + - '"policy" in strict_policy.policy' + - '"policy_metadata" in strict_policy.policy' + - '"consumed_stateful_rule_capacity" in policy_metadata' + - '"consumed_stateless_rule_capacity" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - '"stateful_engine_options" in policy_data' + - '"rule_order" in policy_data.stateful_engine_options' + - policy_data.stateful_engine_options.rule_order == "STRICT_ORDER" + - policy_metadata.consumed_stateful_rule_capacity == 0 + - policy_metadata.consumed_stateless_rule_capacity == 0 + - policy_metadata.firewall_policy_name == strict_policy_name + - policy_metadata.firewall_policy_arn == strict_policy_arn + - policy_metadata.firewall_policy_id == strict_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + vars: + policy_data: '{{ strict_policy.policy.policy }}' + policy_metadata: '{{ strict_policy.policy.policy_metadata }}' + + ### + + - name: '(CHECK) S - Create a simple firewall policy with `strict` rule order - test reference by ARN (idempotency)' + check_mode: True + networkfirewall_policy: + arn: '{{ strict_policy_arn }}' + stateful_rule_order: 'strict' + state: present + register: strict_policy + + - assert: + that: + - strict_policy is not changed + - '"policy" in strict_policy' + - '"policy" in strict_policy.policy' + - '"policy_metadata" in strict_policy.policy' + - '"consumed_stateful_rule_capacity" in policy_metadata' + - '"consumed_stateless_rule_capacity" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - '"stateful_engine_options" in policy_data' + - '"rule_order" in policy_data.stateful_engine_options' + - policy_data.stateful_engine_options.rule_order == "STRICT_ORDER" + - policy_metadata.consumed_stateful_rule_capacity == 0 + - policy_metadata.consumed_stateless_rule_capacity == 0 + - policy_metadata.firewall_policy_name == strict_policy_name + - policy_metadata.firewall_policy_arn == strict_policy_arn + - policy_metadata.firewall_policy_id == strict_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + vars: + policy_data: '{{ strict_policy.policy.policy }}' + policy_metadata: '{{ strict_policy.policy.policy_metadata }}' + + - name: 'S - Create a simple firewall policy with `strict` rule order (idempotency)' + networkfirewall_policy: + arn: '{{ strict_policy_arn }}' + stateful_rule_order: 'strict' + state: present + register: strict_policy + + - assert: + that: + - strict_policy is not changed + - '"policy" in strict_policy' + - '"policy" in strict_policy.policy' + - '"policy_metadata" in strict_policy.policy' + - '"consumed_stateful_rule_capacity" in policy_metadata' + - '"consumed_stateless_rule_capacity" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - '"stateful_engine_options" in policy_data' + - '"rule_order" in policy_data.stateful_engine_options' + - policy_data.stateful_engine_options.rule_order == "STRICT_ORDER" + - policy_metadata.consumed_stateful_rule_capacity == 0 + - policy_metadata.consumed_stateless_rule_capacity == 0 + - policy_metadata.firewall_policy_name == strict_policy_name + - policy_metadata.firewall_policy_arn == strict_policy_arn + - policy_metadata.firewall_policy_id == strict_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + vars: + policy_data: '{{ strict_policy.policy.policy }}' + policy_metadata: '{{ strict_policy.policy.policy_metadata }}' + + ################################################################### + # Update Stateful Rule order + + - name: '(CHECK) S - Update stateful rule order - DENIED' + check_mode: True + networkfirewall_policy: + name: '{{ strict_policy_name }}' + state: present + stateful_rule_order: default + register: strict_policy + ignore_errors: True + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + + - assert: + that: + - strict_policy is failed + + - name: 'S - Update stateful rule order - DENIED' + networkfirewall_policy: + name: '{{ strict_policy_name }}' + state: present + stateful_rule_order: default + register: strict_policy + ignore_errors: True + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + + - assert: + that: + - strict_policy is failed + + ################################################################### + # Stateful Rule Group management + + - name: '(CHECK) S - Add stateful rule group' + check_mode: True + networkfirewall_policy: + name: '{{ strict_policy_name }}' + state: present + stateful_rule_groups: + - '{{ strict_group_names[0] }}' + register: strict_policy + + - assert: + that: + - strict_policy is changed + - '"policy" in strict_policy' + - '"policy" in strict_policy.policy' + - '"policy_metadata" in strict_policy.policy' + - '"consumed_stateful_rule_capacity" in policy_metadata' + - '"consumed_stateless_rule_capacity" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - '"stateful_engine_options" in policy_data' + - '"rule_order" in policy_data.stateful_engine_options' + - policy_data.stateful_engine_options.rule_order == "STRICT_ORDER" + # This is metadata provided by the APIs, we can't guarantee it during + # a check_mode update + # - policy_metadata.consumed_stateful_rule_capacity == rule_group_capacity + # - policy_metadata.consumed_stateless_rule_capacity == 0 + - '"stateful_rule_group_references" in policy_data' + - policy_metadata.firewall_policy_name == strict_policy_name + - policy_metadata.firewall_policy_arn == strict_policy_arn + - policy_metadata.firewall_policy_id == strict_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateful_rule_group_references | length == 1 + - strict_group_arns[0] in stateful_rule_arns + vars: + policy_data: '{{ strict_policy.policy.policy }}' + policy_metadata: '{{ strict_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + - name: 'S - Add stateful rule group' + networkfirewall_policy: + name: '{{ strict_policy_name }}' + state: present + stateful_rule_groups: + - '{{ strict_group_names[0] }}' + register: strict_policy + + - assert: + that: + - strict_policy is changed + - '"policy" in strict_policy' + - '"policy" in strict_policy.policy' + - '"policy_metadata" in strict_policy.policy' + - '"consumed_stateful_rule_capacity" in policy_metadata' + - '"consumed_stateless_rule_capacity" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - '"stateful_engine_options" in policy_data' + - '"rule_order" in policy_data.stateful_engine_options' + - policy_data.stateful_engine_options.rule_order == "STRICT_ORDER" + - policy_metadata.consumed_stateful_rule_capacity == rule_group_capacity + - policy_metadata.consumed_stateless_rule_capacity == 0 + - policy_metadata.firewall_policy_name == strict_policy_name + - policy_metadata.firewall_policy_arn == strict_policy_arn + - policy_metadata.firewall_policy_id == strict_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateful_rule_group_references | length == 1 + - strict_group_arns[0] in stateful_rule_arns + vars: + policy_data: '{{ strict_policy.policy.policy }}' + policy_metadata: '{{ strict_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + - name: '(CHECK) S - Add stateful rule group (idempotency)' + check_mode: True + networkfirewall_policy: + name: '{{ strict_policy_name }}' + stateful_rule_order: 'strict' + state: present + stateful_rule_groups: + - '{{ strict_group_names[0] }}' + register: strict_policy + + - assert: + that: + - strict_policy is not changed + - '"policy" in strict_policy' + - '"policy" in strict_policy.policy' + - '"policy_metadata" in strict_policy.policy' + - '"consumed_stateful_rule_capacity" in policy_metadata' + - '"consumed_stateless_rule_capacity" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - '"stateful_engine_options" in policy_data' + - '"rule_order" in policy_data.stateful_engine_options' + - policy_data.stateful_engine_options.rule_order == "STRICT_ORDER" + - policy_metadata.consumed_stateful_rule_capacity == rule_group_capacity + - policy_metadata.consumed_stateless_rule_capacity == 0 + - policy_metadata.firewall_policy_name == strict_policy_name + - policy_metadata.firewall_policy_arn == strict_policy_arn + - policy_metadata.firewall_policy_id == strict_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateful_rule_group_references | length == 1 + - strict_group_arns[0] in stateful_rule_arns + vars: + policy_data: '{{ strict_policy.policy.policy }}' + policy_metadata: '{{ strict_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + - name: 'S - Add stateful rule group (idempotency)' + networkfirewall_policy: + name: '{{ strict_policy_name }}' + stateful_rule_order: 'strict' + state: present + stateful_rule_groups: + - '{{ strict_group_names[0] }}' + register: strict_policy + + - assert: + that: + - strict_policy is not changed + - '"policy" in strict_policy' + - '"policy" in strict_policy.policy' + - '"policy_metadata" in strict_policy.policy' + - '"consumed_stateful_rule_capacity" in policy_metadata' + - '"consumed_stateless_rule_capacity" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - '"stateful_engine_options" in policy_data' + - '"rule_order" in policy_data.stateful_engine_options' + - policy_data.stateful_engine_options.rule_order == "STRICT_ORDER" + - policy_metadata.consumed_stateful_rule_capacity == rule_group_capacity + - policy_metadata.consumed_stateless_rule_capacity == 0 + - policy_metadata.firewall_policy_name == strict_policy_name + - policy_metadata.firewall_policy_arn == strict_policy_arn + - policy_metadata.firewall_policy_id == strict_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateful_rule_group_references | length == 1 + - strict_group_arns[0] in stateful_rule_arns + vars: + policy_data: '{{ strict_policy.policy.policy }}' + policy_metadata: '{{ strict_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + ### + + - name: '(CHECK) S - Add managed stateful rule group' + check_mode: True + networkfirewall_policy: + name: '{{ strict_policy_name }}' + state: present + stateful_rule_groups: + - '{{ strict_group_names[0] }}' + - '{{ managed_group_arn }}' + register: strict_policy + + - assert: + that: + - strict_policy is changed + - '"policy" in strict_policy' + - '"policy" in strict_policy.policy' + - '"policy_metadata" in strict_policy.policy' + - '"consumed_stateful_rule_capacity" in policy_metadata' + - '"consumed_stateless_rule_capacity" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - '"stateful_engine_options" in policy_data' + - '"rule_order" in policy_data.stateful_engine_options' + - policy_data.stateful_engine_options.rule_order == "STRICT_ORDER" + # This is metadata provided by the APIs, we can't guarantee it during + # a check_mode update + # - policy_metadata.consumed_stateful_rule_capacity > rule_group_capacity + # - policy_metadata.consumed_stateless_rule_capacity == 0 + - '"stateful_rule_group_references" in policy_data' + - policy_metadata.firewall_policy_name == strict_policy_name + - policy_metadata.firewall_policy_arn == strict_policy_arn + - policy_metadata.firewall_policy_id == strict_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateful_rule_group_references | length == 2 + - strict_group_arns[0] in stateful_rule_arns + - managed_group_arn in stateful_rule_arns + vars: + policy_data: '{{ strict_policy.policy.policy }}' + policy_metadata: '{{ strict_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + - name: 'S - Add managed stateful rule group' + networkfirewall_policy: + name: '{{ strict_policy_name }}' + state: present + stateful_rule_groups: + - '{{ strict_group_names[0] }}' + - '{{ managed_group_arn }}' + register: strict_policy + + - assert: + that: + - strict_policy is changed + - '"policy" in strict_policy' + - '"policy" in strict_policy.policy' + - '"policy_metadata" in strict_policy.policy' + - '"consumed_stateful_rule_capacity" in policy_metadata' + - '"consumed_stateless_rule_capacity" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - '"stateful_engine_options" in policy_data' + - '"rule_order" in policy_data.stateful_engine_options' + - policy_data.stateful_engine_options.rule_order == "STRICT_ORDER" + - policy_metadata.consumed_stateful_rule_capacity > rule_group_capacity + - policy_metadata.consumed_stateless_rule_capacity == 0 + - policy_metadata.firewall_policy_name == strict_policy_name + - policy_metadata.firewall_policy_arn == strict_policy_arn + - policy_metadata.firewall_policy_id == strict_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateful_rule_group_references | length == 2 + - strict_group_arns[0] in stateful_rule_arns + - managed_group_arn in stateful_rule_arns + vars: + policy_data: '{{ strict_policy.policy.policy }}' + policy_metadata: '{{ strict_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + - name: '(CHECK) S - Add managed stateful rule group (idempotency)' + check_mode: True + networkfirewall_policy: + name: '{{ strict_policy_name }}' + stateful_rule_order: 'strict' + state: present + stateful_rule_groups: + - '{{ strict_group_names[0] }}' + - '{{ managed_group_arn }}' + register: strict_policy + + - assert: + that: + - strict_policy is not changed + - '"policy" in strict_policy' + - '"policy" in strict_policy.policy' + - '"policy_metadata" in strict_policy.policy' + - '"consumed_stateful_rule_capacity" in policy_metadata' + - '"consumed_stateless_rule_capacity" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - '"stateful_engine_options" in policy_data' + - '"rule_order" in policy_data.stateful_engine_options' + - policy_data.stateful_engine_options.rule_order == "STRICT_ORDER" + - policy_metadata.consumed_stateful_rule_capacity > rule_group_capacity + - policy_metadata.consumed_stateless_rule_capacity == 0 + - policy_metadata.firewall_policy_name == strict_policy_name + - policy_metadata.firewall_policy_arn == strict_policy_arn + - policy_metadata.firewall_policy_id == strict_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateful_rule_group_references | length == 2 + - strict_group_arns[0] in stateful_rule_arns + - managed_group_arn in stateful_rule_arns + vars: + policy_data: '{{ strict_policy.policy.policy }}' + policy_metadata: '{{ strict_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + - name: 'S - Add managed stateful rule group (idempotency)' + networkfirewall_policy: + name: '{{ strict_policy_name }}' + stateful_rule_order: 'strict' + state: present + stateful_rule_groups: + - '{{ strict_group_names[0] }}' + - '{{ managed_group_arn }}' + register: strict_policy + + - assert: + that: + - strict_policy is not changed + - '"policy" in strict_policy' + - '"policy" in strict_policy.policy' + - '"policy_metadata" in strict_policy.policy' + - '"consumed_stateful_rule_capacity" in policy_metadata' + - '"consumed_stateless_rule_capacity" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - '"stateful_engine_options" in policy_data' + - '"rule_order" in policy_data.stateful_engine_options' + - policy_data.stateful_engine_options.rule_order == "STRICT_ORDER" + - policy_metadata.consumed_stateful_rule_capacity > rule_group_capacity + - policy_metadata.consumed_stateless_rule_capacity == 0 + - policy_metadata.firewall_policy_name == strict_policy_name + - policy_metadata.firewall_policy_arn == strict_policy_arn + - policy_metadata.firewall_policy_id == strict_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateful_rule_group_references | length == 2 + - strict_group_arns[0] in stateful_rule_arns + - managed_group_arn in stateful_rule_arns + vars: + policy_data: '{{ strict_policy.policy.policy }}' + policy_metadata: '{{ strict_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + ### + + - name: '(CHECK) S - Replace stateful rule groups' + check_mode: True + networkfirewall_policy: + name: '{{ strict_policy_name }}' + state: present + stateful_rule_groups: + - '{{ strict_group_names[1] }}' + - '{{ strict_group_arns[2] }}' + register: strict_policy + + - assert: + that: + - strict_policy is changed + - '"policy" in strict_policy' + - '"policy" in strict_policy.policy' + - '"policy_metadata" in strict_policy.policy' + - '"consumed_stateful_rule_capacity" in policy_metadata' + - '"consumed_stateless_rule_capacity" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - '"stateful_engine_options" in policy_data' + - '"rule_order" in policy_data.stateful_engine_options' + - policy_data.stateful_engine_options.rule_order == "STRICT_ORDER" + # This is metadata provided by the APIs, we can't guarantee it during + # a check_mode update + # - policy_metadata.consumed_stateful_rule_capacity == (rule_group_capacity * 2) + # - policy_metadata.consumed_stateless_rule_capacity == 0 + - '"stateful_rule_group_references" in policy_data' + - policy_metadata.firewall_policy_name == strict_policy_name + - policy_metadata.firewall_policy_arn == strict_policy_arn + - policy_metadata.firewall_policy_id == strict_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateful_rule_group_references | length == 2 + - strict_group_arns[1] in stateful_rule_arns + - strict_group_arns[2] in stateful_rule_arns + vars: + policy_data: '{{ strict_policy.policy.policy }}' + policy_metadata: '{{ strict_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + - name: 'S - Replace stateful rule groups' + networkfirewall_policy: + name: '{{ strict_policy_name }}' + state: present + stateful_rule_groups: + - '{{ strict_group_names[1] }}' + - '{{ strict_group_arns[2] }}' + register: strict_policy + + - assert: + that: + - strict_policy is changed + - '"policy" in strict_policy' + - '"policy" in strict_policy.policy' + - '"policy_metadata" in strict_policy.policy' + - '"consumed_stateful_rule_capacity" in policy_metadata' + - '"consumed_stateless_rule_capacity" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - '"stateful_engine_options" in policy_data' + - '"rule_order" in policy_data.stateful_engine_options' + - policy_data.stateful_engine_options.rule_order == "STRICT_ORDER" + - policy_metadata.consumed_stateful_rule_capacity == (rule_group_capacity * 2) + - policy_metadata.consumed_stateless_rule_capacity == 0 + - policy_metadata.firewall_policy_name == strict_policy_name + - policy_metadata.firewall_policy_arn == strict_policy_arn + - policy_metadata.firewall_policy_id == strict_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateful_rule_group_references | length == 2 + - strict_group_arns[1] in stateful_rule_arns + - strict_group_arns[2] in stateful_rule_arns + vars: + policy_data: '{{ strict_policy.policy.policy }}' + policy_metadata: '{{ strict_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + - name: '(CHECK) S - Replace stateful rule groups (idempotency)' + check_mode: True + networkfirewall_policy: + name: '{{ strict_policy_name }}' + stateful_rule_order: 'strict' + state: present + stateful_rule_groups: + - '{{ strict_group_names[1] }}' + - '{{ strict_group_arns[2] }}' + register: strict_policy + + - assert: + that: + - strict_policy is not changed + - '"policy" in strict_policy' + - '"policy" in strict_policy.policy' + - '"policy_metadata" in strict_policy.policy' + - '"consumed_stateful_rule_capacity" in policy_metadata' + - '"consumed_stateless_rule_capacity" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - '"stateful_engine_options" in policy_data' + - '"rule_order" in policy_data.stateful_engine_options' + - policy_data.stateful_engine_options.rule_order == "STRICT_ORDER" + - policy_metadata.consumed_stateful_rule_capacity == (rule_group_capacity * 2) + - policy_metadata.consumed_stateless_rule_capacity == 0 + - policy_metadata.firewall_policy_name == strict_policy_name + - policy_metadata.firewall_policy_arn == strict_policy_arn + - policy_metadata.firewall_policy_id == strict_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateful_rule_group_references | length == 2 + - strict_group_arns[1] in stateful_rule_arns + - strict_group_arns[2] in stateful_rule_arns + vars: + policy_data: '{{ strict_policy.policy.policy }}' + policy_metadata: '{{ strict_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + - name: 'S - Replace stateful rule groups (idempotency)' + networkfirewall_policy: + name: '{{ strict_policy_name }}' + stateful_rule_order: 'strict' + state: present + stateful_rule_groups: + - '{{ strict_group_names[1] }}' + - '{{ strict_group_arns[2] }}' + register: strict_policy + + - assert: + that: + - strict_policy is not changed + - '"policy" in strict_policy' + - '"policy" in strict_policy.policy' + - '"policy_metadata" in strict_policy.policy' + - '"consumed_stateful_rule_capacity" in policy_metadata' + - '"consumed_stateless_rule_capacity" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - '"stateful_engine_options" in policy_data' + - '"rule_order" in policy_data.stateful_engine_options' + - policy_data.stateful_engine_options.rule_order == "STRICT_ORDER" + - policy_metadata.consumed_stateful_rule_capacity == (rule_group_capacity * 2) + - policy_metadata.consumed_stateless_rule_capacity == 0 + - policy_metadata.firewall_policy_name == strict_policy_name + - policy_metadata.firewall_policy_arn == strict_policy_arn + - policy_metadata.firewall_policy_id == strict_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateful_rule_group_references | length == 2 + - strict_group_arns[1] in stateful_rule_arns + - strict_group_arns[2] in stateful_rule_arns + vars: + policy_data: '{{ strict_policy.policy.policy }}' + policy_metadata: '{{ strict_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + ### + + - name: '(CHECK) S - Replace stateful rule groups - ARN vs Name (idempotency)' + check_mode: True + networkfirewall_policy: + name: '{{ strict_policy_name }}' + stateful_rule_order: 'strict' + state: present + stateful_rule_groups: + - '{{ strict_group_arns[1] }}' + - '{{ strict_group_names[2] }}' + register: strict_policy + + - assert: + that: + - strict_policy is not changed + - '"policy" in strict_policy' + - '"policy" in strict_policy.policy' + - '"policy_metadata" in strict_policy.policy' + - '"consumed_stateful_rule_capacity" in policy_metadata' + - '"consumed_stateless_rule_capacity" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - '"stateful_engine_options" in policy_data' + - '"rule_order" in policy_data.stateful_engine_options' + - policy_data.stateful_engine_options.rule_order == "STRICT_ORDER" + - policy_metadata.consumed_stateful_rule_capacity == (rule_group_capacity * 2) + - policy_metadata.consumed_stateless_rule_capacity == 0 + - policy_metadata.firewall_policy_name == strict_policy_name + - policy_metadata.firewall_policy_arn == strict_policy_arn + - policy_metadata.firewall_policy_id == strict_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateful_rule_group_references | length == 2 + - strict_group_arns[1] in stateful_rule_arns + - strict_group_arns[2] in stateful_rule_arns + vars: + policy_data: '{{ strict_policy.policy.policy }}' + policy_metadata: '{{ strict_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + - name: 'S - Replace stateful rule groups - ARN vs Name (idempotency)' + networkfirewall_policy: + name: '{{ strict_policy_name }}' + stateful_rule_order: 'strict' + state: present + stateful_rule_groups: + - '{{ strict_group_arns[1] }}' + - '{{ strict_group_names[2] }}' + register: strict_policy + + - assert: + that: + - strict_policy is not changed + - '"policy" in strict_policy' + - '"policy" in strict_policy.policy' + - '"policy_metadata" in strict_policy.policy' + - '"consumed_stateful_rule_capacity" in policy_metadata' + - '"consumed_stateless_rule_capacity" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - '"stateful_engine_options" in policy_data' + - '"rule_order" in policy_data.stateful_engine_options' + - policy_data.stateful_engine_options.rule_order == "STRICT_ORDER" + - policy_metadata.consumed_stateful_rule_capacity == (rule_group_capacity * 2) + - policy_metadata.consumed_stateless_rule_capacity == 0 + - policy_metadata.firewall_policy_name == strict_policy_name + - policy_metadata.firewall_policy_arn == strict_policy_arn + - policy_metadata.firewall_policy_id == strict_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateful_rule_group_references | length == 2 + - strict_group_arns[1] in stateful_rule_arns + - strict_group_arns[2] in stateful_rule_arns + vars: + policy_data: '{{ strict_policy.policy.policy }}' + policy_metadata: '{{ strict_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + ### + + - name: '(CHECK) S - Replace stateful rule groups - change order' + check_mode: True + networkfirewall_policy: + name: '{{ strict_policy_name }}' + stateful_rule_order: 'strict' + state: present + stateful_rule_groups: + - '{{ strict_group_arns[2] }}' + - '{{ strict_group_arns[1] }}' + register: strict_policy + + - assert: + that: + - strict_policy is changed + - '"policy" in strict_policy' + - '"policy" in strict_policy.policy' + - '"policy_metadata" in strict_policy.policy' + - '"consumed_stateful_rule_capacity" in policy_metadata' + - '"consumed_stateless_rule_capacity" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - '"stateful_engine_options" in policy_data' + - '"rule_order" in policy_data.stateful_engine_options' + - policy_data.stateful_engine_options.rule_order == "STRICT_ORDER" + - policy_metadata.consumed_stateful_rule_capacity == (rule_group_capacity * 2) + - policy_metadata.consumed_stateless_rule_capacity == 0 + - policy_metadata.firewall_policy_name == strict_policy_name + - policy_metadata.firewall_policy_arn == strict_policy_arn + - policy_metadata.firewall_policy_id == strict_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateful_rule_group_references | length == 2 + - strict_group_arns[1] in stateful_rule_arns + - strict_group_arns[2] in stateful_rule_arns + vars: + policy_data: '{{ strict_policy.policy.policy }}' + policy_metadata: '{{ strict_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + - name: 'S - Replace stateful rule groups - change order' + networkfirewall_policy: + name: '{{ strict_policy_name }}' + stateful_rule_order: 'strict' + state: present + stateful_rule_groups: + - '{{ strict_group_arns[2] }}' + - '{{ strict_group_arns[1] }}' + register: strict_policy + + - assert: + that: + - strict_policy is changed + - '"policy" in strict_policy' + - '"policy" in strict_policy.policy' + - '"policy_metadata" in strict_policy.policy' + - '"consumed_stateful_rule_capacity" in policy_metadata' + - '"consumed_stateless_rule_capacity" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - '"stateful_engine_options" in policy_data' + - '"rule_order" in policy_data.stateful_engine_options' + - policy_data.stateful_engine_options.rule_order == "STRICT_ORDER" + - policy_metadata.consumed_stateful_rule_capacity == (rule_group_capacity * 2) + - policy_metadata.consumed_stateless_rule_capacity == 0 + - policy_metadata.firewall_policy_name == strict_policy_name + - policy_metadata.firewall_policy_arn == strict_policy_arn + - policy_metadata.firewall_policy_id == strict_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateful_rule_group_references | length == 2 + - strict_group_arns[1] in stateful_rule_arns + - strict_group_arns[2] in stateful_rule_arns + vars: + policy_data: '{{ strict_policy.policy.policy }}' + policy_metadata: '{{ strict_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + - name: '(CHECK) S - Replace stateful rule groups - change order (idempotency)' + check_mode: True + networkfirewall_policy: + name: '{{ strict_policy_name }}' + stateful_rule_order: 'strict' + state: present + stateful_rule_groups: + - '{{ strict_group_arns[2] }}' + - '{{ strict_group_arns[1] }}' + register: strict_policy + + - assert: + that: + - strict_policy is not changed + - '"policy" in strict_policy' + - '"policy" in strict_policy.policy' + - '"policy_metadata" in strict_policy.policy' + - '"consumed_stateful_rule_capacity" in policy_metadata' + - '"consumed_stateless_rule_capacity" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - '"stateful_engine_options" in policy_data' + - '"rule_order" in policy_data.stateful_engine_options' + - policy_data.stateful_engine_options.rule_order == "STRICT_ORDER" + - policy_metadata.consumed_stateful_rule_capacity == (rule_group_capacity * 2) + - policy_metadata.consumed_stateless_rule_capacity == 0 + - policy_metadata.firewall_policy_name == strict_policy_name + - policy_metadata.firewall_policy_arn == strict_policy_arn + - policy_metadata.firewall_policy_id == strict_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateful_rule_group_references | length == 2 + - strict_group_arns[1] in stateful_rule_arns + - strict_group_arns[2] in stateful_rule_arns + vars: + policy_data: '{{ strict_policy.policy.policy }}' + policy_metadata: '{{ strict_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + - name: 'S - Replace stateful rule groups - change order (idempotency)' + networkfirewall_policy: + name: '{{ strict_policy_name }}' + stateful_rule_order: 'strict' + state: present + stateful_rule_groups: + - '{{ strict_group_arns[2] }}' + - '{{ strict_group_arns[1] }}' + register: strict_policy + + - assert: + that: + - strict_policy is not changed + - '"policy" in strict_policy' + - '"policy" in strict_policy.policy' + - '"policy_metadata" in strict_policy.policy' + - '"consumed_stateful_rule_capacity" in policy_metadata' + - '"consumed_stateless_rule_capacity" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - '"stateful_engine_options" in policy_data' + - '"rule_order" in policy_data.stateful_engine_options' + - policy_data.stateful_engine_options.rule_order == "STRICT_ORDER" + - policy_metadata.consumed_stateful_rule_capacity == (rule_group_capacity * 2) + - policy_metadata.consumed_stateless_rule_capacity == 0 + - policy_metadata.firewall_policy_name == strict_policy_name + - policy_metadata.firewall_policy_arn == strict_policy_arn + - policy_metadata.firewall_policy_id == strict_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateful_rule_group_references | length == 2 + - strict_group_arns[1] in stateful_rule_arns + - strict_group_arns[2] in stateful_rule_arns + vars: + policy_data: '{{ strict_policy.policy.policy }}' + policy_metadata: '{{ strict_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + ################################################################### + # Update Stateless flow default actions (basic) + + - name: '(CHECK) S - Update Stateless flow default action' + check_mode: True + networkfirewall_policy: + name: '{{ strict_policy_name }}' + state: present + stateless_default_actions: + - 'aws:drop' + register: strict_policy + + - assert: + that: + - strict_policy is changed + - '"policy" in strict_policy' + - '"policy" in strict_policy.policy' + - '"policy_metadata" in strict_policy.policy' + - '"consumed_stateful_rule_capacity" in policy_metadata' + - '"consumed_stateless_rule_capacity" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - '"stateful_rule_group_references" in policy_data' + - policy_metadata.firewall_policy_name == strict_policy_name + - policy_metadata.firewall_policy_arn == strict_policy_arn + - policy_metadata.firewall_policy_id == strict_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:drop"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateful_rule_group_references | length == 2 + - strict_group_arns[1] in stateful_rule_arns + - strict_group_arns[2] in stateful_rule_arns + vars: + policy_data: '{{ strict_policy.policy.policy }}' + policy_metadata: '{{ strict_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + - name: 'S - Update Stateless flow default action' + networkfirewall_policy: + name: '{{ strict_policy_name }}' + state: present + stateless_default_actions: + - 'aws:drop' + register: strict_policy + + - assert: + that: + - strict_policy is changed + - '"policy" in strict_policy' + - '"policy" in strict_policy.policy' + - '"policy_metadata" in strict_policy.policy' + - '"consumed_stateful_rule_capacity" in policy_metadata' + - '"consumed_stateless_rule_capacity" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - '"stateful_engine_options" in policy_data' + - '"rule_order" in policy_data.stateful_engine_options' + - policy_data.stateful_engine_options.rule_order == "STRICT_ORDER" + # This is metadata provided by the APIs, we can't guarantee it during + # a check_mode update + # - policy_metadata.consumed_stateful_rule_capacity == (rule_group_capacity * 2) + # - policy_metadata.consumed_stateless_rule_capacity == 0 + - policy_metadata.firewall_policy_name == strict_policy_name + - policy_metadata.firewall_policy_arn == strict_policy_arn + - policy_metadata.firewall_policy_id == strict_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:drop"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateful_rule_group_references | length == 2 + - strict_group_arns[1] in stateful_rule_arns + - strict_group_arns[2] in stateful_rule_arns + vars: + policy_data: '{{ strict_policy.policy.policy }}' + policy_metadata: '{{ strict_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + - name: '(CHECK) S - Update Stateless flow default action (idempotency)' + check_mode: True + networkfirewall_policy: + name: '{{ strict_policy_name }}' + state: present + stateless_default_actions: + - 'aws:drop' + register: strict_policy + + - assert: + that: + - strict_policy is not changed + - '"policy" in strict_policy' + - '"policy" in strict_policy.policy' + - '"policy_metadata" in strict_policy.policy' + - '"consumed_stateful_rule_capacity" in policy_metadata' + - '"consumed_stateless_rule_capacity" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - '"stateful_engine_options" in policy_data' + - '"rule_order" in policy_data.stateful_engine_options' + - policy_data.stateful_engine_options.rule_order == "STRICT_ORDER" + - policy_metadata.consumed_stateful_rule_capacity == (rule_group_capacity * 2) + - policy_metadata.consumed_stateless_rule_capacity == 0 + - policy_metadata.firewall_policy_name == strict_policy_name + - policy_metadata.firewall_policy_arn == strict_policy_arn + - policy_metadata.firewall_policy_id == strict_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:drop"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateful_rule_group_references | length == 2 + - strict_group_arns[1] in stateful_rule_arns + - strict_group_arns[2] in stateful_rule_arns + vars: + policy_data: '{{ strict_policy.policy.policy }}' + policy_metadata: '{{ strict_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + - name: 'S - Update Stateless flow default action (idempotency)' + networkfirewall_policy: + name: '{{ strict_policy_name }}' + state: present + stateless_default_actions: + - 'aws:drop' + register: strict_policy + + - assert: + that: + - strict_policy is not changed + - '"policy" in strict_policy' + - '"policy" in strict_policy.policy' + - '"policy_metadata" in strict_policy.policy' + - '"consumed_stateful_rule_capacity" in policy_metadata' + - '"consumed_stateless_rule_capacity" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - '"stateful_engine_options" in policy_data' + - '"rule_order" in policy_data.stateful_engine_options' + - policy_data.stateful_engine_options.rule_order == "STRICT_ORDER" + - policy_metadata.consumed_stateful_rule_capacity == (rule_group_capacity * 2) + - policy_metadata.consumed_stateless_rule_capacity == 0 + - policy_metadata.firewall_policy_name == strict_policy_name + - policy_metadata.firewall_policy_arn == strict_policy_arn + - policy_metadata.firewall_policy_id == strict_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:drop"] + - policy_data.stateless_fragment_default_actions == ["aws:forward_to_sfe"] + - policy_data.stateful_rule_group_references | length == 2 + - strict_group_arns[1] in stateful_rule_arns + - strict_group_arns[2] in stateful_rule_arns + vars: + policy_data: '{{ strict_policy.policy.policy }}' + policy_metadata: '{{ strict_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + ### + + - name: '(CHECK) S - Update Stateless fragmented flow default action' + check_mode: True + networkfirewall_policy: + name: '{{ strict_policy_name }}' + state: present + stateless_fragment_default_actions: + - 'aws:pass' + register: strict_policy + + - assert: + that: + - strict_policy is changed + - '"policy" in strict_policy' + - '"policy" in strict_policy.policy' + - '"policy_metadata" in strict_policy.policy' + - '"consumed_stateful_rule_capacity" in policy_metadata' + - '"consumed_stateless_rule_capacity" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - '"stateful_engine_options" in policy_data' + - '"rule_order" in policy_data.stateful_engine_options' + - policy_data.stateful_engine_options.rule_order == "STRICT_ORDER" + - policy_metadata.consumed_stateful_rule_capacity == (rule_group_capacity * 2) + - policy_metadata.consumed_stateless_rule_capacity == 0 + - '"stateful_rule_group_references" in policy_data' + - policy_metadata.firewall_policy_name == strict_policy_name + - policy_metadata.firewall_policy_arn == strict_policy_arn + - policy_metadata.firewall_policy_id == strict_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:drop"] + - policy_data.stateless_fragment_default_actions == ["aws:pass"] + - policy_data.stateful_rule_group_references | length == 2 + - strict_group_arns[1] in stateful_rule_arns + - strict_group_arns[2] in stateful_rule_arns + vars: + policy_data: '{{ strict_policy.policy.policy }}' + policy_metadata: '{{ strict_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + - name: 'S - Update Stateless fragmented flow default action' + networkfirewall_policy: + name: '{{ strict_policy_name }}' + state: present + stateless_fragment_default_actions: + - 'aws:pass' + register: strict_policy + + - assert: + that: + - strict_policy is changed + - '"policy" in strict_policy' + - '"policy" in strict_policy.policy' + - '"policy_metadata" in strict_policy.policy' + - '"consumed_stateful_rule_capacity" in policy_metadata' + - '"consumed_stateless_rule_capacity" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - '"stateful_engine_options" in policy_data' + - '"rule_order" in policy_data.stateful_engine_options' + - policy_data.stateful_engine_options.rule_order == "STRICT_ORDER" + - policy_metadata.firewall_policy_name == strict_policy_name + - policy_metadata.firewall_policy_arn == strict_policy_arn + - policy_metadata.firewall_policy_id == strict_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:drop"] + - policy_data.stateless_fragment_default_actions == ["aws:pass"] + - policy_data.stateful_rule_group_references | length == 2 + - strict_group_arns[1] in stateful_rule_arns + - strict_group_arns[2] in stateful_rule_arns + vars: + policy_data: '{{ strict_policy.policy.policy }}' + policy_metadata: '{{ strict_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + - name: '(CHECK) S - Update Stateless fragmented flow default action (idempotency)' + check_mode: True + networkfirewall_policy: + name: '{{ strict_policy_name }}' + state: present + stateless_fragment_default_actions: + - 'aws:pass' + register: strict_policy + + - assert: + that: + - strict_policy is not changed + - '"policy" in strict_policy' + - '"policy" in strict_policy.policy' + - '"policy_metadata" in strict_policy.policy' + - '"consumed_stateful_rule_capacity" in policy_metadata' + - '"consumed_stateless_rule_capacity" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - '"stateful_engine_options" in policy_data' + - '"rule_order" in policy_data.stateful_engine_options' + - policy_data.stateful_engine_options.rule_order == "STRICT_ORDER" + - policy_metadata.consumed_stateful_rule_capacity == (rule_group_capacity * 2) + - policy_metadata.consumed_stateless_rule_capacity == 0 + - policy_metadata.firewall_policy_name == strict_policy_name + - policy_metadata.firewall_policy_arn == strict_policy_arn + - policy_metadata.firewall_policy_id == strict_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:drop"] + - policy_data.stateless_fragment_default_actions == ["aws:pass"] + - policy_data.stateful_rule_group_references | length == 2 + - strict_group_arns[1] in stateful_rule_arns + - strict_group_arns[2] in stateful_rule_arns + vars: + policy_data: '{{ strict_policy.policy.policy }}' + policy_metadata: '{{ strict_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + - name: 'S - Update Stateless fragmented flow default action (idempotency)' + networkfirewall_policy: + name: '{{ strict_policy_name }}' + state: present + stateless_fragment_default_actions: + - 'aws:pass' + register: strict_policy + + - assert: + that: + - strict_policy is not changed + - '"policy" in strict_policy' + - '"policy" in strict_policy.policy' + - '"policy_metadata" in strict_policy.policy' + - '"consumed_stateful_rule_capacity" in policy_metadata' + - '"consumed_stateless_rule_capacity" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - '"stateful_engine_options" in policy_data' + - '"rule_order" in policy_data.stateful_engine_options' + - policy_data.stateful_engine_options.rule_order == "STRICT_ORDER" + - policy_metadata.consumed_stateful_rule_capacity == (rule_group_capacity * 2) + - policy_metadata.consumed_stateless_rule_capacity == 0 + - policy_metadata.firewall_policy_name == strict_policy_name + - policy_metadata.firewall_policy_arn == strict_policy_arn + - policy_metadata.firewall_policy_id == strict_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:drop"] + - policy_data.stateless_fragment_default_actions == ["aws:pass"] + - policy_data.stateful_rule_group_references | length == 2 + - strict_group_arns[1] in stateful_rule_arns + - strict_group_arns[2] in stateful_rule_arns + vars: + policy_data: '{{ strict_policy.policy.policy }}' + policy_metadata: '{{ strict_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + ################################################################### + # Update Stateful flow default actions + + - name: '(CHECK) S - Set default action for stateful flows' + check_mode: True + networkfirewall_policy: + name: '{{ strict_policy_name }}' + state: present + stateful_default_actions: + - 'aws:drop_strict' + register: strict_policy + ignore_errors: True + + - assert: + that: + - strict_policy is changed + - '"policy" in strict_policy' + - '"policy" in strict_policy.policy' + - '"policy_metadata" in strict_policy.policy' + - '"consumed_stateful_rule_capacity" in policy_metadata' + - '"consumed_stateless_rule_capacity" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateful_default_actions" in policy_data' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - '"stateful_engine_options" in policy_data' + - '"rule_order" in policy_data.stateful_engine_options' + - policy_data.stateful_engine_options.rule_order == "STRICT_ORDER" + - policy_metadata.consumed_stateful_rule_capacity == (rule_group_capacity * 2) + - policy_metadata.consumed_stateless_rule_capacity == 0 + - policy_metadata.firewall_policy_name == strict_policy_name + - policy_metadata.firewall_policy_arn == strict_policy_arn + - policy_metadata.firewall_policy_id == strict_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:drop"] + - policy_data.stateless_fragment_default_actions == ["aws:pass"] + - policy_data.stateful_default_actions == ["aws:drop_strict"] + - policy_data.stateful_rule_group_references | length == 2 + - strict_group_arns[1] in stateful_rule_arns + - strict_group_arns[2] in stateful_rule_arns + vars: + policy_data: '{{ strict_policy.policy.policy }}' + policy_metadata: '{{ strict_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + - name: 'S - Set default action for stateful flows' + networkfirewall_policy: + name: '{{ strict_policy_name }}' + state: present + stateful_default_actions: + - 'aws:drop_strict' + register: strict_policy + ignore_errors: True + + - assert: + that: + - strict_policy is changed + - '"policy" in strict_policy' + - '"policy" in strict_policy.policy' + - '"policy_metadata" in strict_policy.policy' + - '"consumed_stateful_rule_capacity" in policy_metadata' + - '"consumed_stateless_rule_capacity" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateful_default_actions" in policy_data' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - '"stateful_engine_options" in policy_data' + - '"rule_order" in policy_data.stateful_engine_options' + - policy_data.stateful_engine_options.rule_order == "STRICT_ORDER" + - policy_metadata.consumed_stateful_rule_capacity == (rule_group_capacity * 2) + - policy_metadata.consumed_stateless_rule_capacity == 0 + - policy_metadata.firewall_policy_name == strict_policy_name + - policy_metadata.firewall_policy_arn == strict_policy_arn + - policy_metadata.firewall_policy_id == strict_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:drop"] + - policy_data.stateless_fragment_default_actions == ["aws:pass"] + - policy_data.stateful_default_actions == ["aws:drop_strict"] + - policy_data.stateful_rule_group_references | length == 2 + - strict_group_arns[1] in stateful_rule_arns + - strict_group_arns[2] in stateful_rule_arns + vars: + policy_data: '{{ strict_policy.policy.policy }}' + policy_metadata: '{{ strict_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + - name: '(CHECK) S - Set default action for stateful flows (idempotency)' + check_mode: True + networkfirewall_policy: + name: '{{ strict_policy_name }}' + state: present + stateful_default_actions: + - 'aws:drop_strict' + register: strict_policy + ignore_errors: True + + - assert: + that: + - strict_policy is not changed + - '"policy" in strict_policy' + - '"policy" in strict_policy.policy' + - '"policy_metadata" in strict_policy.policy' + - '"consumed_stateful_rule_capacity" in policy_metadata' + - '"consumed_stateless_rule_capacity" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateful_default_actions" in policy_data' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - '"stateful_engine_options" in policy_data' + - '"rule_order" in policy_data.stateful_engine_options' + - policy_data.stateful_engine_options.rule_order == "STRICT_ORDER" + - policy_metadata.consumed_stateful_rule_capacity == (rule_group_capacity * 2) + - policy_metadata.consumed_stateless_rule_capacity == 0 + - policy_metadata.firewall_policy_name == strict_policy_name + - policy_metadata.firewall_policy_arn == strict_policy_arn + - policy_metadata.firewall_policy_id == strict_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:drop"] + - policy_data.stateless_fragment_default_actions == ["aws:pass"] + - policy_data.stateful_default_actions == ["aws:drop_strict"] + - policy_data.stateful_rule_group_references | length == 2 + - strict_group_arns[1] in stateful_rule_arns + - strict_group_arns[2] in stateful_rule_arns + vars: + policy_data: '{{ strict_policy.policy.policy }}' + policy_metadata: '{{ strict_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + - name: 'S - Set default action for stateful flows (idempotency)' + networkfirewall_policy: + name: '{{ strict_policy_name }}' + state: present + stateful_default_actions: + - 'aws:drop_strict' + register: strict_policy + ignore_errors: True + + - assert: + that: + - strict_policy is not changed + - '"policy" in strict_policy' + - '"policy" in strict_policy.policy' + - '"policy_metadata" in strict_policy.policy' + - '"consumed_stateful_rule_capacity" in policy_metadata' + - '"consumed_stateless_rule_capacity" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateful_default_actions" in policy_data' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - '"stateful_engine_options" in policy_data' + - '"rule_order" in policy_data.stateful_engine_options' + - policy_data.stateful_engine_options.rule_order == "STRICT_ORDER" + - policy_metadata.consumed_stateful_rule_capacity == (rule_group_capacity * 2) + - policy_metadata.consumed_stateless_rule_capacity == 0 + - policy_metadata.firewall_policy_name == strict_policy_name + - policy_metadata.firewall_policy_arn == strict_policy_arn + - policy_metadata.firewall_policy_id == strict_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:drop"] + - policy_data.stateless_fragment_default_actions == ["aws:pass"] + - policy_data.stateful_default_actions == ["aws:drop_strict"] + - policy_data.stateful_rule_group_references | length == 2 + - strict_group_arns[1] in stateful_rule_arns + - strict_group_arns[2] in stateful_rule_arns + vars: + policy_data: '{{ strict_policy.policy.policy }}' + policy_metadata: '{{ strict_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + ### + + - name: '(CHECK) S - Set multiple default actions for stateful flows' + check_mode: True + networkfirewall_policy: + name: '{{ strict_policy_name }}' + state: present + stateful_default_actions: + - 'aws:drop_established' + - 'aws:alert_strict' + - 'aws:alert_established' + register: strict_policy + ignore_errors: True + + - assert: + that: + - strict_policy is changed + - '"policy" in strict_policy' + - '"policy" in strict_policy.policy' + - '"policy_metadata" in strict_policy.policy' + - '"consumed_stateful_rule_capacity" in policy_metadata' + - '"consumed_stateless_rule_capacity" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateful_default_actions" in policy_data' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - '"stateful_engine_options" in policy_data' + - '"rule_order" in policy_data.stateful_engine_options' + - policy_data.stateful_engine_options.rule_order == "STRICT_ORDER" + - policy_metadata.consumed_stateful_rule_capacity == (rule_group_capacity * 2) + - policy_metadata.consumed_stateless_rule_capacity == 0 + - policy_metadata.firewall_policy_name == strict_policy_name + - policy_metadata.firewall_policy_arn == strict_policy_arn + - policy_metadata.firewall_policy_id == strict_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:drop"] + - policy_data.stateless_fragment_default_actions == ["aws:pass"] + - policy_data.stateful_default_actions == ["aws:drop_established", "aws:alert_strict", "aws:alert_established"] + - policy_data.stateful_rule_group_references | length == 2 + - strict_group_arns[1] in stateful_rule_arns + - strict_group_arns[2] in stateful_rule_arns + vars: + policy_data: '{{ strict_policy.policy.policy }}' + policy_metadata: '{{ strict_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + - name: 'S - Set multiple default actions for stateful flows' + networkfirewall_policy: + name: '{{ strict_policy_name }}' + state: present + stateful_default_actions: + - 'aws:drop_established' + - 'aws:alert_strict' + - 'aws:alert_established' + register: strict_policy + ignore_errors: True + + - assert: + that: + - strict_policy is changed + - '"policy" in strict_policy' + - '"policy" in strict_policy.policy' + - '"policy_metadata" in strict_policy.policy' + - '"consumed_stateful_rule_capacity" in policy_metadata' + - '"consumed_stateless_rule_capacity" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateful_default_actions" in policy_data' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - '"stateful_engine_options" in policy_data' + - '"rule_order" in policy_data.stateful_engine_options' + - policy_data.stateful_engine_options.rule_order == "STRICT_ORDER" + - policy_metadata.consumed_stateful_rule_capacity == (rule_group_capacity * 2) + - policy_metadata.consumed_stateless_rule_capacity == 0 + - policy_metadata.firewall_policy_name == strict_policy_name + - policy_metadata.firewall_policy_arn == strict_policy_arn + - policy_metadata.firewall_policy_id == strict_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:drop"] + - policy_data.stateless_fragment_default_actions == ["aws:pass"] + - policy_data.stateful_default_actions == ["aws:drop_established", "aws:alert_strict", "aws:alert_established"] + - policy_data.stateful_rule_group_references | length == 2 + - strict_group_arns[1] in stateful_rule_arns + - strict_group_arns[2] in stateful_rule_arns + vars: + policy_data: '{{ strict_policy.policy.policy }}' + policy_metadata: '{{ strict_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + - name: '(CHECK) S - Set multiple default actions for stateful flows (idempotency)' + check_mode: True + networkfirewall_policy: + name: '{{ strict_policy_name }}' + state: present + stateful_default_actions: + - 'aws:drop_established' + - 'aws:alert_strict' + - 'aws:alert_established' + register: strict_policy + ignore_errors: True + + - assert: + that: + - strict_policy is not changed + - '"policy" in strict_policy' + - '"policy" in strict_policy.policy' + - '"policy_metadata" in strict_policy.policy' + - '"consumed_stateful_rule_capacity" in policy_metadata' + - '"consumed_stateless_rule_capacity" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateful_default_actions" in policy_data' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - '"stateful_engine_options" in policy_data' + - '"rule_order" in policy_data.stateful_engine_options' + - policy_data.stateful_engine_options.rule_order == "STRICT_ORDER" + - policy_metadata.consumed_stateful_rule_capacity == (rule_group_capacity * 2) + - policy_metadata.consumed_stateless_rule_capacity == 0 + - policy_metadata.firewall_policy_name == strict_policy_name + - policy_metadata.firewall_policy_arn == strict_policy_arn + - policy_metadata.firewall_policy_id == strict_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:drop"] + - policy_data.stateless_fragment_default_actions == ["aws:pass"] + - policy_data.stateful_default_actions == ["aws:drop_established", "aws:alert_strict", "aws:alert_established"] + - policy_data.stateful_rule_group_references | length == 2 + - strict_group_arns[1] in stateful_rule_arns + - strict_group_arns[2] in stateful_rule_arns + vars: + policy_data: '{{ strict_policy.policy.policy }}' + policy_metadata: '{{ strict_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + - name: 'S - Set multiple default actions for stateful flows (idempotency)' + networkfirewall_policy: + name: '{{ strict_policy_name }}' + state: present + stateful_default_actions: + - 'aws:drop_established' + - 'aws:alert_strict' + - 'aws:alert_established' + register: strict_policy + ignore_errors: True + + - assert: + that: + - strict_policy is not changed + - '"policy" in strict_policy' + - '"policy" in strict_policy.policy' + - '"policy_metadata" in strict_policy.policy' + - '"consumed_stateful_rule_capacity" in policy_metadata' + - '"consumed_stateless_rule_capacity" in policy_metadata' + - '"firewall_policy_arn" in policy_metadata' + - '"firewall_policy_id" in policy_metadata' + - '"firewall_policy_name" in policy_metadata' + - '"firewall_policy_status" in policy_metadata' + - '"tags" in policy_metadata' + - '"stateful_default_actions" in policy_data' + - '"stateless_default_actions" in policy_data' + - '"stateless_fragment_default_actions" in policy_data' + - '"stateful_engine_options" in policy_data' + - '"rule_order" in policy_data.stateful_engine_options' + - policy_data.stateful_engine_options.rule_order == "STRICT_ORDER" + - policy_metadata.consumed_stateful_rule_capacity == (rule_group_capacity * 2) + - policy_metadata.consumed_stateless_rule_capacity == 0 + - policy_metadata.firewall_policy_name == strict_policy_name + - policy_metadata.firewall_policy_arn == strict_policy_arn + - policy_metadata.firewall_policy_id == strict_policy_id + - policy_metadata.tags == {} + - policy_data.stateless_default_actions == ["aws:drop"] + - policy_data.stateless_fragment_default_actions == ["aws:pass"] + - policy_data.stateful_default_actions == ["aws:drop_established", "aws:alert_strict", "aws:alert_established"] + - policy_data.stateful_rule_group_references | length == 2 + - strict_group_arns[1] in stateful_rule_arns + - strict_group_arns[2] in stateful_rule_arns + vars: + policy_data: '{{ strict_policy.policy.policy }}' + policy_metadata: '{{ strict_policy.policy.policy_metadata }}' + stateful_rule_arns: '{{ policy_data.stateful_rule_group_references | map(attribute="resource_arn") | list }}' + + ### + + - name: '(CHECK) S - Set invalid default actions for stateful flows' + check_mode: True + networkfirewall_policy: + name: '{{ strict_policy_name }}' + state: present + stateful_default_actions: + - 'aws:drop_established' + - 'aws:junk_action' + - 'aws:alert_established' + register: strict_policy + ignore_errors: True + + - assert: + that: + - strict_policy is failed + + - name: 'S - Set invalid default actions for stateful flows' + networkfirewall_policy: + name: '{{ strict_policy_name }}' + state: present + stateful_default_actions: + - 'aws:drop_established' + - 'aws:junk_action' + - 'aws:alert_established' + register: strict_policy + ignore_errors: True + + - assert: + that: + - strict_policy is failed + + ################################################################### + # Delete policy + + - name: '(CHECK) S - Delete policy with strict order' + check_mode: True + networkfirewall_policy: + name: '{{ strict_policy_name }}' + state: absent + wait: False + register: strict_policy + + - assert: + that: + - strict_policy is changed + + - name: 'S - Delete policy with strict order' + networkfirewall_policy: + name: '{{ strict_policy_name }}' + state: absent + wait: False + register: strict_policy + + - assert: + that: + - strict_policy is changed + + - name: '(CHECK) S - Delete policy with strict order (idempotency)' + check_mode: True + networkfirewall_policy: + name: '{{ strict_policy_name }}' + state: absent + wait: False + register: strict_policy + + - assert: + that: + - strict_policy is not changed + + - name: 'S - Delete policy with strict order (idempotency)' + networkfirewall_policy: + name: '{{ strict_policy_name }}' + state: absent + wait: False + register: strict_policy + + - assert: + that: + - strict_policy is not changed + + always: + - name: 'S - Cleanup firewall policy' + networkfirewall_policy: + name: '{{ strict_policy_name }}' + state: absent + wait: False + ignore_errors: True diff --git a/ansible_collections/community/aws/tests/integration/targets/networkfirewall_rule_group/aliases b/ansible_collections/community/aws/tests/integration/targets/networkfirewall_rule_group/aliases new file mode 100644 index 000000000..3a0301661 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/networkfirewall_rule_group/aliases @@ -0,0 +1,4 @@ +time=18m +cloud/aws + +networkfirewall_rule_group_info diff --git a/ansible_collections/community/aws/tests/integration/targets/networkfirewall_rule_group/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/networkfirewall_rule_group/defaults/main.yml new file mode 100644 index 000000000..fa49aa20c --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/networkfirewall_rule_group/defaults/main.yml @@ -0,0 +1,2 @@ +--- +group_name_prefix: 'AnsibleTest-{{ tiny_prefix }}' diff --git a/ansible_collections/community/aws/tests/integration/targets/networkfirewall_rule_group/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/networkfirewall_rule_group/meta/main.yml new file mode 100644 index 000000000..f09ab4af1 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/networkfirewall_rule_group/meta/main.yml @@ -0,0 +1,4 @@ +dependencies: + - role: setup_botocore_pip + vars: + botocore_version: "1.23.23" diff --git a/ansible_collections/community/aws/tests/integration/targets/networkfirewall_rule_group/tasks/5-tuple.yml b/ansible_collections/community/aws/tests/integration/targets/networkfirewall_rule_group/tasks/5-tuple.yml new file mode 100644 index 000000000..35d82e289 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/networkfirewall_rule_group/tasks/5-tuple.yml @@ -0,0 +1,584 @@ +--- +# +# Basic manipulation of 5-Tuple based rule groups +# - Creation +# - Deletion +# - Updating Rules +- vars: + tuple_group_name: '{{ group_name_prefix }}-5TupleGroup' + rule_one: + source: '192.0.2.74' + destination: '198.51.100.0/24' + source_port: 'any' + destination_port: 22 + action: 'pass' + protocol: 'TCP' + sid: '10001' + rule_two: + action: 'pass' + direction: 'any' + source: 'any' + destination: 'any' + source_port: 'any' + destination_port: 'any' + protocol: 'icmp' + sid: '10002' + rule_options: + itype: [3] + rule_three: + action: 'drop' + direction: 'forward' + source: '$EXAMPLE_SOURCE' + destination: '$EXAMPLE_DEST' + source_port: 'any' + destination_port: '$HTTPS_PORT' + protocol: 'http' + sid: '10003' + rule_options: + # Raw strings need the extra quotes + content: '"index.php"' + # Empty == no 'setting' (is valid) + http_uri: + ip_variables: + EXAMPLE_SOURCE: '203.0.113.0/24' + EXAMPLE_DEST: '192.0.2.117' + port_variables: + HTTPS_PORT: '8443' + # Formatted version of the options + rule_one_options: + - keyword: 'sid:10001' + rule_two_options: + - keyword: 'sid:10002' + - keyword: 'itype' + settings: ['3'] + rule_three_options: + - keyword: 'sid:10003' + - keyword: 'content' + # Ẽxtra quotes are deliberate + settings: ['"index.php"'] + - keyword: 'http_uri' + block: + ################################################################### + # Creation + + # Bare minimum rule, wouldn't actually check anything since neither HTTP not + # HTTPS traffic is being inspected + - name: '(CHECK) Create a 5-Tuple Rule Group' + networkfirewall_rule_group: + name: '{{ tuple_group_name }}' + type: 'stateful' + capacity: 50 + rule_list: + - '{{ rule_one }}' + register: tuple_group + check_mode: true + + - assert: + that: + - tuple_group is changed + - '"rule_group" in tuple_group' + - '"rule_group" in tuple_group.rule_group' + - '"rule_group_metadata" in tuple_group.rule_group' + - '"capacity" in tuple_group.rule_group.rule_group_metadata' + - '"rule_group_name" in tuple_group.rule_group.rule_group_metadata' + - '"type" in tuple_group.rule_group.rule_group_metadata' + - tuple_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - tuple_group.rule_group.rule_group_metadata.capacity == 50 + - tuple_group.rule_group.rule_group_metadata.rule_group_name == tuple_group_name + - '"rules_source" in tuple_group.rule_group.rule_group' + - '"stateful_rules" in tuple_group.rule_group.rule_group.rules_source' + - tuple_group.rule_group.rule_group.rules_source.stateful_rules | length == 1 + - '"action" in first_rule' + - '"header" in first_rule' + - '"rule_options" in first_rule' + - first_rule.action == 'PASS' + - '"destination" in first_rule.header' + - '"destination_port" in first_rule.header' + - '"direction" in first_rule.header' + - '"protocol" in first_rule.header' + - '"source" in first_rule.header' + - '"source_port" in first_rule.header' + - first_rule.header.destination == '198.51.100.0/24' + - first_rule.header.destination_port == '22' + - first_rule.header.source == '192.0.2.74' + - first_rule.header.source_port == 'any' + - first_rule.header.protocol == 'TCP' + - first_rule.header.direction == 'FORWARD' + - first_rule.rule_options == rule_one_options + vars: + first_rule: '{{ tuple_group.rule_group.rule_group.rules_source.stateful_rules[0] }}' + + - name: 'Create a 5-Tuple Rule Group' + networkfirewall_rule_group: + name: '{{ tuple_group_name }}' + type: 'stateful' + capacity: 50 + rule_list: + - '{{ rule_one }}' + register: tuple_group + + - assert: + that: + - tuple_group is changed + - '"rule_group" in tuple_group' + - '"rule_group" in tuple_group.rule_group' + - '"rule_group_metadata" in tuple_group.rule_group' + - '"capacity" in tuple_group.rule_group.rule_group_metadata' + - '"rule_group_arn" in tuple_group.rule_group.rule_group_metadata' + - '"rule_group_id" in tuple_group.rule_group.rule_group_metadata' + - '"rule_group_name" in tuple_group.rule_group.rule_group_metadata' + - '"type" in tuple_group.rule_group.rule_group_metadata' + - tuple_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - tuple_group.rule_group.rule_group_metadata.capacity == 50 + - tuple_group.rule_group.rule_group_metadata.rule_group_name == tuple_group_name + - tuple_group.rule_group.rule_group_metadata.rule_group_arn.startswith(account_arn) + - tuple_group.rule_group.rule_group_metadata.rule_group_arn.endswith(tuple_group_name) + - '"rules_source" in tuple_group.rule_group.rule_group' + + - name: Save RuleGroup ID/ARN for later + set_fact: + minimal_rule_group_id: '{{ tuple_group.rule_group.rule_group_metadata.rule_group_id }}' + minimal_rule_group_arn: '{{ tuple_group.rule_group.rule_group_metadata.rule_group_arn }}' + + - name: '(CHECK) Create a 5-Tuple Rule Group (idempotency)' + networkfirewall_rule_group: + name: '{{ tuple_group_name }}' + type: 'stateful' + capacity: 50 + rule_list: + - '{{ rule_one }}' + register: tuple_group + check_mode: true + + - assert: + that: + - tuple_group is not changed + - '"rule_group" in tuple_group' + - '"rule_group" in tuple_group.rule_group' + - '"rule_group_metadata" in tuple_group.rule_group' + - '"capacity" in tuple_group.rule_group.rule_group_metadata' + - '"rule_group_name" in tuple_group.rule_group.rule_group_metadata' + - '"type" in tuple_group.rule_group.rule_group_metadata' + - tuple_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - tuple_group.rule_group.rule_group_metadata.capacity == 50 + - tuple_group.rule_group.rule_group_metadata.rule_group_name == tuple_group_name + - tuple_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - tuple_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in tuple_group.rule_group.rule_group' + + - name: 'Create a 5-Tuple Rule Group (idempotency)' + networkfirewall_rule_group: + name: '{{ tuple_group_name }}' + type: 'stateful' + capacity: 50 + rule_list: + - '{{ rule_one }}' + register: tuple_group + + - assert: + that: + - tuple_group is not changed + - '"rule_group" in tuple_group' + - '"rule_group" in tuple_group.rule_group' + - '"rule_group_metadata" in tuple_group.rule_group' + - '"capacity" in tuple_group.rule_group.rule_group_metadata' + - '"rule_group_name" in tuple_group.rule_group.rule_group_metadata' + - '"type" in tuple_group.rule_group.rule_group_metadata' + - tuple_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - tuple_group.rule_group.rule_group_metadata.capacity == 50 + - tuple_group.rule_group.rule_group_metadata.rule_group_name == tuple_group_name + - tuple_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - tuple_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in tuple_group.rule_group.rule_group' + + ################################################################### + # Add some extra variables, properly tested in stateful.yml + + - name: 'Set IP and Port variables' + networkfirewall_rule_group: + name: '{{ tuple_group_name }}' + type: 'stateful' + ip_variables: '{{ ip_variables }}' + port_variables: '{{ port_variables }}' + register: port_variables + + - assert: + that: + - port_variables is changed + + ################################################################### + # Update + + - name: '(CHECK) Update a 5-Tuple Rule Group with new rules' + networkfirewall_rule_group: + name: '{{ tuple_group_name }}' + type: 'stateful' + rule_list: + - '{{ rule_one }}' + - '{{ rule_two }}' + - '{{ rule_three }}' + register: tuple_group + check_mode: true + + - assert: + that: + - tuple_group is changed + - '"rule_group" in tuple_group' + - '"rule_group" in tuple_group.rule_group' + - '"rule_group_metadata" in tuple_group.rule_group' + - '"capacity" in tuple_group.rule_group.rule_group_metadata' + - '"rule_group_name" in tuple_group.rule_group.rule_group_metadata' + - '"type" in tuple_group.rule_group.rule_group_metadata' + - tuple_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - tuple_group.rule_group.rule_group_metadata.capacity == 50 + - tuple_group.rule_group.rule_group_metadata.rule_group_name == tuple_group_name + - tuple_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - tuple_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in tuple_group.rule_group.rule_group' + - '"stateful_rules" in tuple_group.rule_group.rule_group.rules_source' + - tuple_group.rule_group.rule_group.rules_source.stateful_rules | length == 3 + - '"action" in first_rule' + - '"header" in first_rule' + - '"rule_options" in first_rule' + - first_rule.action == 'PASS' + - '"destination" in first_rule.header' + - '"destination_port" in first_rule.header' + - '"direction" in first_rule.header' + - '"protocol" in first_rule.header' + - '"source" in first_rule.header' + - '"source_port" in first_rule.header' + - first_rule.header.destination == '198.51.100.0/24' + - first_rule.header.destination_port == '22' + - first_rule.header.source == '192.0.2.74' + - first_rule.header.source_port == 'any' + - first_rule.header.protocol == 'TCP' + - first_rule.header.direction == 'FORWARD' + - first_rule.rule_options == rule_one_options + - '"action" in second_rule' + - '"header" in second_rule' + - '"rule_options" in second_rule' + - second_rule.action == 'PASS' + - '"destination" in second_rule.header' + - '"destination_port" in second_rule.header' + - '"direction" in second_rule.header' + - '"protocol" in second_rule.header' + - '"source" in second_rule.header' + - '"source_port" in second_rule.header' + - second_rule.header.destination == 'any' + - second_rule.header.destination_port == 'any' + - second_rule.header.source == 'any' + - second_rule.header.source_port == 'any' + - second_rule.header.protocol == 'ICMP' + - second_rule.header.direction == 'ANY' + - second_rule.rule_options == rule_two_options + - '"action" in third_rule' + - '"header" in third_rule' + - '"rule_options" in third_rule' + - third_rule.action == 'DROP' + - '"destination" in third_rule.header' + - '"destination_port" in third_rule.header' + - '"direction" in third_rule.header' + - '"protocol" in third_rule.header' + - '"source" in third_rule.header' + - '"source_port" in third_rule.header' + - third_rule.header.destination == '$EXAMPLE_DEST' + - third_rule.header.destination_port == '$HTTPS_PORT' + - third_rule.header.source == '$EXAMPLE_SOURCE' + - third_rule.header.source_port == 'any' + - third_rule.header.protocol == 'HTTP' + - third_rule.header.direction == 'FORWARD' + - third_rule.rule_options == rule_three_options + vars: + first_rule: '{{ tuple_group.rule_group.rule_group.rules_source.stateful_rules[0] }}' + second_rule: '{{ tuple_group.rule_group.rule_group.rules_source.stateful_rules[1] }}' + third_rule: '{{ tuple_group.rule_group.rule_group.rules_source.stateful_rules[2] }}' + + - name: 'Update a 5-Tuple Rule Group with new rules' + networkfirewall_rule_group: + name: '{{ tuple_group_name }}' + type: 'stateful' + rule_list: + - '{{ rule_one }}' + - '{{ rule_two }}' + - '{{ rule_three }}' + register: tuple_group + + - assert: + that: + - tuple_group is changed + - '"rule_group" in tuple_group' + - '"rule_group" in tuple_group.rule_group' + - '"rule_group_metadata" in tuple_group.rule_group' + - '"capacity" in tuple_group.rule_group.rule_group_metadata' + - '"rule_group_arn" in tuple_group.rule_group.rule_group_metadata' + - '"rule_group_id" in tuple_group.rule_group.rule_group_metadata' + - '"rule_group_name" in tuple_group.rule_group.rule_group_metadata' + - '"type" in tuple_group.rule_group.rule_group_metadata' + - tuple_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - tuple_group.rule_group.rule_group_metadata.capacity == 50 + - tuple_group.rule_group.rule_group_metadata.rule_group_name == tuple_group_name + - tuple_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - tuple_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in tuple_group.rule_group.rule_group' + + - name: '(CHECK) Update a 5-Tuple Rule Group with new rules (idempotency)' + networkfirewall_rule_group: + name: '{{ tuple_group_name }}' + type: 'stateful' + rule_list: + - '{{ rule_one }}' + - '{{ rule_two }}' + - '{{ rule_three }}' + register: tuple_group + check_mode: true + + - assert: + that: + - tuple_group is not changed + - '"rule_group" in tuple_group' + - '"rule_group" in tuple_group.rule_group' + - '"rule_group_metadata" in tuple_group.rule_group' + - '"capacity" in tuple_group.rule_group.rule_group_metadata' + - '"rule_group_name" in tuple_group.rule_group.rule_group_metadata' + - '"type" in tuple_group.rule_group.rule_group_metadata' + - tuple_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - tuple_group.rule_group.rule_group_metadata.capacity == 50 + - tuple_group.rule_group.rule_group_metadata.rule_group_name == tuple_group_name + - tuple_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - tuple_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in tuple_group.rule_group.rule_group' + + - name: 'Update a 5-Tuple Rule Group with new rules (idempotency)' + networkfirewall_rule_group: + name: '{{ tuple_group_name }}' + type: 'stateful' + rule_list: + - '{{ rule_one }}' + - '{{ rule_two }}' + - '{{ rule_three }}' + register: tuple_group + + - assert: + that: + - tuple_group is not changed + - '"rule_group" in tuple_group' + - '"rule_group" in tuple_group.rule_group' + - '"rule_group_metadata" in tuple_group.rule_group' + - '"capacity" in tuple_group.rule_group.rule_group_metadata' + - '"rule_group_name" in tuple_group.rule_group.rule_group_metadata' + - '"type" in tuple_group.rule_group.rule_group_metadata' + - tuple_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - tuple_group.rule_group.rule_group_metadata.capacity == 50 + - tuple_group.rule_group.rule_group_metadata.rule_group_name == tuple_group_name + - tuple_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - tuple_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in tuple_group.rule_group.rule_group' + + ##### + + - name: '(CHECK) Update a 5-Tuple Rule Group by removing first rule' + networkfirewall_rule_group: + name: '{{ tuple_group_name }}' + type: 'stateful' + rule_list: + - '{{ rule_two }}' + - '{{ rule_three }}' + register: tuple_group + check_mode: true + + - assert: + that: + - tuple_group is changed + - '"rule_group" in tuple_group' + - '"rule_group" in tuple_group.rule_group' + - '"rule_group_metadata" in tuple_group.rule_group' + - '"capacity" in tuple_group.rule_group.rule_group_metadata' + - '"rule_group_name" in tuple_group.rule_group.rule_group_metadata' + - '"type" in tuple_group.rule_group.rule_group_metadata' + - tuple_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - tuple_group.rule_group.rule_group_metadata.capacity == 50 + - tuple_group.rule_group.rule_group_metadata.rule_group_name == tuple_group_name + - tuple_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - tuple_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in tuple_group.rule_group.rule_group' + - '"stateful_rules" in tuple_group.rule_group.rule_group.rules_source' + - tuple_group.rule_group.rule_group.rules_source.stateful_rules | length == 2 + - '"action" in first_rule' + - '"header" in first_rule' + - '"rule_options" in first_rule' + - first_rule.action == 'PASS' + - '"destination" in first_rule.header' + - '"destination_port" in first_rule.header' + - '"direction" in first_rule.header' + - '"protocol" in first_rule.header' + - '"source" in first_rule.header' + - '"source_port" in first_rule.header' + - first_rule.header.destination == 'any' + - first_rule.header.destination_port == 'any' + - first_rule.header.source == 'any' + - first_rule.header.source_port == 'any' + - first_rule.header.protocol == 'ICMP' + - first_rule.header.direction == 'ANY' + - first_rule.rule_options == rule_two_options + - '"action" in second_rule' + - '"header" in second_rule' + - '"rule_options" in second_rule' + - second_rule.action == 'DROP' + - '"destination" in second_rule.header' + - '"destination_port" in second_rule.header' + - '"direction" in second_rule.header' + - '"protocol" in second_rule.header' + - '"source" in second_rule.header' + - '"source_port" in second_rule.header' + - second_rule.header.destination == '$EXAMPLE_DEST' + - second_rule.header.destination_port == '$HTTPS_PORT' + - second_rule.header.source == '$EXAMPLE_SOURCE' + - second_rule.header.source_port == 'any' + - second_rule.header.protocol == 'HTTP' + - second_rule.header.direction == 'FORWARD' + - second_rule.rule_options == rule_three_options + vars: + first_rule: '{{ tuple_group.rule_group.rule_group.rules_source.stateful_rules[0] }}' + second_rule: '{{ tuple_group.rule_group.rule_group.rules_source.stateful_rules[1] }}' + + - name: 'Update a 5-Tuple Rule Group by removing first rule' + networkfirewall_rule_group: + name: '{{ tuple_group_name }}' + type: 'stateful' + rule_list: + - '{{ rule_two }}' + - '{{ rule_three }}' + register: tuple_group + + - assert: + that: + - tuple_group is changed + - '"rule_group" in tuple_group' + - '"rule_group" in tuple_group.rule_group' + - '"rule_group_metadata" in tuple_group.rule_group' + - '"capacity" in tuple_group.rule_group.rule_group_metadata' + - '"rule_group_arn" in tuple_group.rule_group.rule_group_metadata' + - '"rule_group_id" in tuple_group.rule_group.rule_group_metadata' + - '"rule_group_name" in tuple_group.rule_group.rule_group_metadata' + - '"type" in tuple_group.rule_group.rule_group_metadata' + - tuple_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - tuple_group.rule_group.rule_group_metadata.capacity == 50 + - tuple_group.rule_group.rule_group_metadata.rule_group_name == tuple_group_name + - tuple_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - tuple_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in tuple_group.rule_group.rule_group' + + - name: '(CHECK) Update a 5-Tuple Rule Group by removing first rule (idempotency)' + networkfirewall_rule_group: + name: '{{ tuple_group_name }}' + type: 'stateful' + rule_list: + - '{{ rule_two }}' + - '{{ rule_three }}' + register: tuple_group + check_mode: true + + - assert: + that: + - tuple_group is not changed + - '"rule_group" in tuple_group' + - '"rule_group" in tuple_group.rule_group' + - '"rule_group_metadata" in tuple_group.rule_group' + - '"capacity" in tuple_group.rule_group.rule_group_metadata' + - '"rule_group_name" in tuple_group.rule_group.rule_group_metadata' + - '"type" in tuple_group.rule_group.rule_group_metadata' + - tuple_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - tuple_group.rule_group.rule_group_metadata.capacity == 50 + - tuple_group.rule_group.rule_group_metadata.rule_group_name == tuple_group_name + - tuple_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - tuple_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in tuple_group.rule_group.rule_group' + + - name: 'Update a 5-Tuple Rule Group by removing first rule (idempotency)' + networkfirewall_rule_group: + name: '{{ tuple_group_name }}' + type: 'stateful' + rule_list: + - '{{ rule_two }}' + - '{{ rule_three }}' + register: tuple_group + + - assert: + that: + - tuple_group is not changed + - '"rule_group" in tuple_group' + - '"rule_group" in tuple_group.rule_group' + - '"rule_group_metadata" in tuple_group.rule_group' + - '"capacity" in tuple_group.rule_group.rule_group_metadata' + - '"rule_group_name" in tuple_group.rule_group.rule_group_metadata' + - '"type" in tuple_group.rule_group.rule_group_metadata' + - tuple_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - tuple_group.rule_group.rule_group_metadata.capacity == 50 + - tuple_group.rule_group.rule_group_metadata.rule_group_name == tuple_group_name + - tuple_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - tuple_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in tuple_group.rule_group.rule_group' + + ################################################################### + # Deletion + + - name: '(CHECK) Delete Domain List rule group' + networkfirewall_rule_group: + name: '{{ tuple_group_name }}' + type: 'stateful' + state: absent + wait: False + register: tuple_group + check_mode: true + + - assert: + that: + - tuple_group is changed + + - name: 'Delete Domain List rule group' + networkfirewall_rule_group: + name: '{{ tuple_group_name }}' + type: 'stateful' + state: absent + wait: False + register: tuple_group + + - assert: + that: + - tuple_group is changed + + # The Rule Group may still exist in a "DELETING" state, we should still + # return not changed + - name: 'Delete Domain List rule group (idempotency)' + networkfirewall_rule_group: + name: '{{ tuple_group_name }}' + type: 'stateful' + state: absent + wait: False + register: tuple_group + check_mode: true + + - assert: + that: + - tuple_group is not changed + + - name: '(CHECK) Delete Domain List rule group (idempotency)' + networkfirewall_rule_group: + name: '{{ tuple_group_name }}' + type: 'stateful' + state: absent + wait: False + register: tuple_group + + - assert: + that: + - tuple_group is not changed + + always: + - name: '(always) Delete Domain List rule group' + networkfirewall_rule_group: + name: '{{ tuple_group_name }}' + type: 'stateful' + state: absent + wait: False + ignore_errors: true diff --git a/ansible_collections/community/aws/tests/integration/targets/networkfirewall_rule_group/tasks/cleanup.yml b/ansible_collections/community/aws/tests/integration/targets/networkfirewall_rule_group/tasks/cleanup.yml new file mode 100644 index 000000000..d221cd885 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/networkfirewall_rule_group/tasks/cleanup.yml @@ -0,0 +1,20 @@ +--- +- name: 'Fetch all account rule groups' + networkfirewall_rule_group_info: {} + register: account_rules_info + ignore_errors: true + +- name: 'Get a list of all rules matching {{ group_name_prefix }}' + set_fact: + matching_rules: '{{ account_rules_info.rule_list | select("search", group_name_prefix) | list }}' + ignore_errors: true + +# These should just be "no-ops" caused by the deletion being in-progress. +# Waiters are not supported at this time. +- name: 'Delete matching rule groups' + networkfirewall_rule_group: + arn: '{{ item }}' + state: absent + wait: True + ignore_errors: true + loop: '{{ matching_rules }}' diff --git a/ansible_collections/community/aws/tests/integration/targets/networkfirewall_rule_group/tasks/domain_list.yml b/ansible_collections/community/aws/tests/integration/targets/networkfirewall_rule_group/tasks/domain_list.yml new file mode 100644 index 000000000..488fe377f --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/networkfirewall_rule_group/tasks/domain_list.yml @@ -0,0 +1,1665 @@ +--- +# +# Basic manipulation of Domain List based rule groups +# - Creation +# - Deletion +# - Updating Rules +# -- Domain list +# -- Inspected Protocols +# -- Action +# -- Source IPs +- vars: + domains_group_name: '{{ group_name_prefix }}-DomainListGroup' + block: + ################################################################### + # Creation + + # Bare minimum rule, wouldn't actually check anything since neither HTTP not + # HTTPS traffic is being inspected + - name: '(CHECK) Create a Domain List Rule Group' + networkfirewall_rule_group: + name: '{{ domains_group_name }}' + type: 'stateful' + capacity: 50 + domain_list: + domain_names: 'example.com' + action: allow + register: domain_group + check_mode: true + + - assert: + that: + - domain_group is changed + - '"rule_group" in domain_group' + - '"rule_group" in domain_group.rule_group' + - '"rule_group_metadata" in domain_group.rule_group' + - '"capacity" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_name" in domain_group.rule_group.rule_group_metadata' + - '"type" in domain_group.rule_group.rule_group_metadata' + - domain_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - domain_group.rule_group.rule_group_metadata.capacity == 50 + - domain_group.rule_group.rule_group_metadata.rule_group_name == domains_group_name + - '"rules_source" in domain_group.rule_group.rule_group' + - '"rules_source_list" in domain_group.rule_group.rule_group.rules_source' + - '"generated_rules_type" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"target_types" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"targets" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.generated_rules_type == 'ALLOWLIST' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.target_types == [] + - domain_group.rule_group.rule_group.rules_source.rules_source_list.targets == ['example.com'] + + - name: 'Create a Domain List Rule Group' + networkfirewall_rule_group: + name: '{{ domains_group_name }}' + type: 'stateful' + capacity: 50 + domain_list: + domain_names: 'example.com' + action: allow + register: domain_group + + - assert: + that: + - domain_group is changed + - '"rule_group" in domain_group' + - '"rule_group" in domain_group.rule_group' + - '"rule_group_metadata" in domain_group.rule_group' + - '"capacity" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_arn" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_id" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_name" in domain_group.rule_group.rule_group_metadata' + - '"type" in domain_group.rule_group.rule_group_metadata' + - domain_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - domain_group.rule_group.rule_group_metadata.capacity == 50 + - domain_group.rule_group.rule_group_metadata.rule_group_name == domains_group_name + - domain_group.rule_group.rule_group_metadata.rule_group_arn.startswith(account_arn) + - domain_group.rule_group.rule_group_metadata.rule_group_arn.endswith(domains_group_name) + - '"rules_source" in domain_group.rule_group.rule_group' + - '"rules_source_list" in domain_group.rule_group.rule_group.rules_source' + - '"generated_rules_type" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"target_types" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"targets" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.generated_rules_type == 'ALLOWLIST' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.target_types == [] + - domain_group.rule_group.rule_group.rules_source.rules_source_list.targets == ['example.com'] + + - name: Save RuleGroup ID/ARN for later + set_fact: + minimal_rule_group_id: '{{ domain_group.rule_group.rule_group_metadata.rule_group_id }}' + minimal_rule_group_arn: '{{ domain_group.rule_group.rule_group_metadata.rule_group_arn }}' + + - name: '(CHECK) Create a Domain List Rule Group (idempotency)' + networkfirewall_rule_group: + name: '{{ domains_group_name }}' + type: 'stateful' + capacity: 50 + domain_list: + domain_names: 'example.com' + action: allow + register: domain_group + check_mode: true + + - assert: + that: + - domain_group is not changed + - '"rule_group" in domain_group' + - '"rule_group" in domain_group.rule_group' + - '"rule_group_metadata" in domain_group.rule_group' + - '"capacity" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_name" in domain_group.rule_group.rule_group_metadata' + - '"type" in domain_group.rule_group.rule_group_metadata' + - domain_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - domain_group.rule_group.rule_group_metadata.capacity == 50 + - domain_group.rule_group.rule_group_metadata.rule_group_name == domains_group_name + - domain_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - domain_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in domain_group.rule_group.rule_group' + - '"rules_source_list" in domain_group.rule_group.rule_group.rules_source' + - '"generated_rules_type" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"target_types" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"targets" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.generated_rules_type == 'ALLOWLIST' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.target_types == [] + - domain_group.rule_group.rule_group.rules_source.rules_source_list.targets == ['example.com'] + + - name: 'Create a Domain List Rule Group (idempotency)' + networkfirewall_rule_group: + name: '{{ domains_group_name }}' + type: 'stateful' + capacity: 50 + domain_list: + domain_names: 'example.com' + action: allow + register: domain_group + + - assert: + that: + - domain_group is not changed + - '"rule_group" in domain_group' + - '"rule_group" in domain_group.rule_group' + - '"rule_group_metadata" in domain_group.rule_group' + - '"capacity" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_name" in domain_group.rule_group.rule_group_metadata' + - '"type" in domain_group.rule_group.rule_group_metadata' + - domain_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - domain_group.rule_group.rule_group_metadata.capacity == 50 + - domain_group.rule_group.rule_group_metadata.rule_group_name == domains_group_name + - domain_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - domain_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in domain_group.rule_group.rule_group' + - '"rules_source_list" in domain_group.rule_group.rule_group.rules_source' + - '"generated_rules_type" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"target_types" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"targets" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.generated_rules_type == 'ALLOWLIST' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.target_types == [] + - domain_group.rule_group.rule_group.rules_source.rules_source_list.targets == ['example.com'] + + ##### + + - name: '(CHECK) Create a Domain List Rule Group - List instead of string (idempotency)' + networkfirewall_rule_group: + name: '{{ domains_group_name }}' + type: 'stateful' + capacity: 50 + domain_list: + domain_names: + - 'example.com' + action: allow + register: domain_group + check_mode: true + + - assert: + that: + - domain_group is not changed + - '"rule_group" in domain_group' + - '"rule_group" in domain_group.rule_group' + - '"rule_group_metadata" in domain_group.rule_group' + - '"capacity" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_name" in domain_group.rule_group.rule_group_metadata' + - '"type" in domain_group.rule_group.rule_group_metadata' + - domain_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - domain_group.rule_group.rule_group_metadata.capacity == 50 + - domain_group.rule_group.rule_group_metadata.rule_group_name == domains_group_name + - domain_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - domain_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in domain_group.rule_group.rule_group' + - '"rules_source_list" in domain_group.rule_group.rule_group.rules_source' + - '"generated_rules_type" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"target_types" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"targets" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.generated_rules_type == 'ALLOWLIST' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.target_types == [] + - domain_group.rule_group.rule_group.rules_source.rules_source_list.targets == ['example.com'] + + - name: 'Create a Domain List Rule Group List - instead of string (idempotency)' + networkfirewall_rule_group: + name: '{{ domains_group_name }}' + type: 'stateful' + capacity: 50 + domain_list: + domain_names: + - 'example.com' + action: allow + register: domain_group + + - assert: + that: + - domain_group is not changed + - '"rule_group" in domain_group' + - '"rule_group" in domain_group.rule_group' + - '"rule_group_metadata" in domain_group.rule_group' + - '"capacity" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_name" in domain_group.rule_group.rule_group_metadata' + - '"type" in domain_group.rule_group.rule_group_metadata' + - domain_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - domain_group.rule_group.rule_group_metadata.capacity == 50 + - domain_group.rule_group.rule_group_metadata.rule_group_name == domains_group_name + - domain_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - domain_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in domain_group.rule_group.rule_group' + - '"rules_source_list" in domain_group.rule_group.rule_group.rules_source' + - '"generated_rules_type" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"target_types" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"targets" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.generated_rules_type == 'ALLOWLIST' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.target_types == [] + - domain_group.rule_group.rule_group.rules_source.rules_source_list.targets == ['example.com'] + + + ################################################################### + # Update + + - name: '(CHECK) Update a Domain List Rule Group with new domains' + networkfirewall_rule_group: + name: '{{ domains_group_name }}' + type: 'stateful' + domain_list: + domain_names: + - 'example.com' + - '.example.net' + action: allow + register: domain_group + check_mode: true + + - assert: + that: + - domain_group is changed + - '"rule_group" in domain_group' + - '"rule_group" in domain_group.rule_group' + - '"rule_group_metadata" in domain_group.rule_group' + - '"capacity" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_name" in domain_group.rule_group.rule_group_metadata' + - '"type" in domain_group.rule_group.rule_group_metadata' + - domain_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - domain_group.rule_group.rule_group_metadata.capacity == 50 + - domain_group.rule_group.rule_group_metadata.rule_group_name == domains_group_name + - domain_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - domain_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in domain_group.rule_group.rule_group' + - '"rules_source_list" in domain_group.rule_group.rule_group.rules_source' + - '"generated_rules_type" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"target_types" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"targets" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.generated_rules_type == 'ALLOWLIST' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.target_types == [] + - domain_group.rule_group.rule_group.rules_source.rules_source_list.targets == ['example.com', '.example.net'] + + - name: 'Update a Domain List Rule Group with new domains' + networkfirewall_rule_group: + name: '{{ domains_group_name }}' + type: 'stateful' + domain_list: + domain_names: + - 'example.com' + - '.example.net' + action: allow + register: domain_group + + - assert: + that: + - domain_group is changed + - '"rule_group" in domain_group' + - '"rule_group" in domain_group.rule_group' + - '"rule_group_metadata" in domain_group.rule_group' + - '"capacity" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_arn" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_id" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_name" in domain_group.rule_group.rule_group_metadata' + - '"type" in domain_group.rule_group.rule_group_metadata' + - domain_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - domain_group.rule_group.rule_group_metadata.capacity == 50 + - domain_group.rule_group.rule_group_metadata.rule_group_name == domains_group_name + - domain_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - domain_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in domain_group.rule_group.rule_group' + - '"rules_source_list" in domain_group.rule_group.rule_group.rules_source' + - '"generated_rules_type" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"target_types" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"targets" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.generated_rules_type == 'ALLOWLIST' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.target_types == [] + - domain_group.rule_group.rule_group.rules_source.rules_source_list.targets == ['example.com', '.example.net'] + + - name: '(CHECK) Update a Domain List Rule Group with new domains (idempotency)' + networkfirewall_rule_group: + name: '{{ domains_group_name }}' + type: 'stateful' + domain_list: + domain_names: + - 'example.com' + - '.example.net' + action: allow + register: domain_group + check_mode: true + + - assert: + that: + - domain_group is not changed + - '"rule_group" in domain_group' + - '"rule_group" in domain_group.rule_group' + - '"rule_group_metadata" in domain_group.rule_group' + - '"capacity" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_name" in domain_group.rule_group.rule_group_metadata' + - '"type" in domain_group.rule_group.rule_group_metadata' + - domain_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - domain_group.rule_group.rule_group_metadata.capacity == 50 + - domain_group.rule_group.rule_group_metadata.rule_group_name == domains_group_name + - domain_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - domain_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in domain_group.rule_group.rule_group' + - '"rules_source_list" in domain_group.rule_group.rule_group.rules_source' + - '"generated_rules_type" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"target_types" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"targets" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.generated_rules_type == 'ALLOWLIST' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.target_types == [] + - domain_group.rule_group.rule_group.rules_source.rules_source_list.targets == ['example.com', '.example.net'] + + - name: 'Update a Domain List Rule Group with new domains (idempotency)' + networkfirewall_rule_group: + name: '{{ domains_group_name }}' + type: 'stateful' + domain_list: + domain_names: + - 'example.com' + - '.example.net' + action: allow + register: domain_group + + - assert: + that: + - domain_group is not changed + - '"rule_group" in domain_group' + - '"rule_group" in domain_group.rule_group' + - '"rule_group_metadata" in domain_group.rule_group' + - '"capacity" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_name" in domain_group.rule_group.rule_group_metadata' + - '"type" in domain_group.rule_group.rule_group_metadata' + - domain_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - domain_group.rule_group.rule_group_metadata.capacity == 50 + - domain_group.rule_group.rule_group_metadata.rule_group_name == domains_group_name + - domain_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - domain_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in domain_group.rule_group.rule_group' + - '"rules_source_list" in domain_group.rule_group.rule_group.rules_source' + - '"generated_rules_type" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"target_types" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"targets" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.generated_rules_type == 'ALLOWLIST' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.target_types == [] + - domain_group.rule_group.rule_group.rules_source.rules_source_list.targets == ['example.com', '.example.net'] + + ##### + + - name: '(CHECK) Update a Domain List Rule Group with new action' + networkfirewall_rule_group: + name: '{{ domains_group_name }}' + type: 'stateful' + domain_list: + domain_names: + - 'example.com' + - '.example.net' + action: deny + register: domain_group + check_mode: true + + - assert: + that: + - domain_group is changed + - '"rule_group" in domain_group' + - '"rule_group" in domain_group.rule_group' + - '"rule_group_metadata" in domain_group.rule_group' + - '"capacity" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_name" in domain_group.rule_group.rule_group_metadata' + - '"type" in domain_group.rule_group.rule_group_metadata' + - domain_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - domain_group.rule_group.rule_group_metadata.capacity == 50 + - domain_group.rule_group.rule_group_metadata.rule_group_name == domains_group_name + - domain_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - domain_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in domain_group.rule_group.rule_group' + - '"rules_source_list" in domain_group.rule_group.rule_group.rules_source' + - '"generated_rules_type" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"target_types" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"targets" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.generated_rules_type == 'DENYLIST' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.target_types == [] + - domain_group.rule_group.rule_group.rules_source.rules_source_list.targets == ['example.com', '.example.net'] + + - name: 'Update a Domain List Rule Group with new action' + networkfirewall_rule_group: + name: '{{ domains_group_name }}' + type: 'stateful' + domain_list: + domain_names: + - 'example.com' + - '.example.net' + action: deny + register: domain_group + + - assert: + that: + - domain_group is changed + - '"rule_group" in domain_group' + - '"rule_group" in domain_group.rule_group' + - '"rule_group_metadata" in domain_group.rule_group' + - '"capacity" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_arn" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_id" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_name" in domain_group.rule_group.rule_group_metadata' + - '"type" in domain_group.rule_group.rule_group_metadata' + - domain_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - domain_group.rule_group.rule_group_metadata.capacity == 50 + - domain_group.rule_group.rule_group_metadata.rule_group_name == domains_group_name + - domain_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - domain_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in domain_group.rule_group.rule_group' + - '"rules_source_list" in domain_group.rule_group.rule_group.rules_source' + - '"generated_rules_type" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"target_types" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"targets" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.generated_rules_type == 'DENYLIST' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.target_types == [] + - domain_group.rule_group.rule_group.rules_source.rules_source_list.targets == ['example.com', '.example.net'] + + - name: '(CHECK) Update a Domain List Rule Group with new action (idempotency)' + networkfirewall_rule_group: + name: '{{ domains_group_name }}' + type: 'stateful' + domain_list: + domain_names: + - 'example.com' + - '.example.net' + action: deny + register: domain_group + check_mode: true + + - assert: + that: + - domain_group is not changed + - '"rule_group" in domain_group' + - '"rule_group" in domain_group.rule_group' + - '"rule_group_metadata" in domain_group.rule_group' + - '"capacity" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_name" in domain_group.rule_group.rule_group_metadata' + - '"type" in domain_group.rule_group.rule_group_metadata' + - domain_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - domain_group.rule_group.rule_group_metadata.capacity == 50 + - domain_group.rule_group.rule_group_metadata.rule_group_name == domains_group_name + - domain_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - domain_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in domain_group.rule_group.rule_group' + - '"rules_source_list" in domain_group.rule_group.rule_group.rules_source' + - '"generated_rules_type" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"target_types" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"targets" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.generated_rules_type == 'DENYLIST' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.target_types == [] + - domain_group.rule_group.rule_group.rules_source.rules_source_list.targets == ['example.com', '.example.net'] + + - name: 'Update a Domain List Rule Group with new action (idempotency)' + networkfirewall_rule_group: + name: '{{ domains_group_name }}' + type: 'stateful' + domain_list: + domain_names: + - 'example.com' + - '.example.net' + action: deny + register: domain_group + + - assert: + that: + - domain_group is not changed + - '"rule_group" in domain_group' + - '"rule_group" in domain_group.rule_group' + - '"rule_group_metadata" in domain_group.rule_group' + - '"capacity" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_name" in domain_group.rule_group.rule_group_metadata' + - '"type" in domain_group.rule_group.rule_group_metadata' + - domain_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - domain_group.rule_group.rule_group_metadata.capacity == 50 + - domain_group.rule_group.rule_group_metadata.rule_group_name == domains_group_name + - domain_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - domain_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in domain_group.rule_group.rule_group' + - '"rules_source_list" in domain_group.rule_group.rule_group.rules_source' + - '"generated_rules_type" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"target_types" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"targets" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.generated_rules_type == 'DENYLIST' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.target_types == [] + - domain_group.rule_group.rule_group.rules_source.rules_source_list.targets == ['example.com', '.example.net'] + + ##### + + - name: '(CHECK) Update a Domain List Rule Group with HTTP only' + networkfirewall_rule_group: + name: '{{ domains_group_name }}' + type: 'stateful' + domain_list: + domain_names: + - 'example.com' + - '.example.net' + action: deny + filter_http: true + register: domain_group + check_mode: true + + - assert: + that: + - domain_group is changed + - '"rule_group" in domain_group' + - '"rule_group" in domain_group.rule_group' + - '"rule_group_metadata" in domain_group.rule_group' + - '"capacity" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_name" in domain_group.rule_group.rule_group_metadata' + - '"type" in domain_group.rule_group.rule_group_metadata' + - domain_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - domain_group.rule_group.rule_group_metadata.capacity == 50 + - domain_group.rule_group.rule_group_metadata.rule_group_name == domains_group_name + - domain_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - domain_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in domain_group.rule_group.rule_group' + - '"rules_source_list" in domain_group.rule_group.rule_group.rules_source' + - '"generated_rules_type" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"target_types" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"targets" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.generated_rules_type == 'DENYLIST' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.target_types == ['HTTP_HOST'] + - domain_group.rule_group.rule_group.rules_source.rules_source_list.targets == ['example.com', '.example.net'] + + - name: 'Update a Domain List Rule Group with HTTP only' + networkfirewall_rule_group: + name: '{{ domains_group_name }}' + type: 'stateful' + domain_list: + domain_names: + - 'example.com' + - '.example.net' + action: deny + filter_http: true + register: domain_group + + - assert: + that: + - domain_group is changed + - '"rule_group" in domain_group' + - '"rule_group" in domain_group.rule_group' + - '"rule_group_metadata" in domain_group.rule_group' + - '"capacity" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_arn" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_id" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_name" in domain_group.rule_group.rule_group_metadata' + - '"type" in domain_group.rule_group.rule_group_metadata' + - domain_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - domain_group.rule_group.rule_group_metadata.capacity == 50 + - domain_group.rule_group.rule_group_metadata.rule_group_name == domains_group_name + - domain_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - domain_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in domain_group.rule_group.rule_group' + - '"rules_source_list" in domain_group.rule_group.rule_group.rules_source' + - '"generated_rules_type" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"target_types" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"targets" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.generated_rules_type == 'DENYLIST' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.target_types == ['HTTP_HOST'] + - domain_group.rule_group.rule_group.rules_source.rules_source_list.targets == ['example.com', '.example.net'] + + - name: '(CHECK) Update a Domain List Rule Group with HTTP only (idempotency)' + networkfirewall_rule_group: + name: '{{ domains_group_name }}' + type: 'stateful' + domain_list: + domain_names: + - 'example.com' + - '.example.net' + action: deny + filter_http: true + register: domain_group + check_mode: true + + - assert: + that: + - domain_group is not changed + - '"rule_group" in domain_group' + - '"rule_group" in domain_group.rule_group' + - '"rule_group_metadata" in domain_group.rule_group' + - '"capacity" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_name" in domain_group.rule_group.rule_group_metadata' + - '"type" in domain_group.rule_group.rule_group_metadata' + - domain_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - domain_group.rule_group.rule_group_metadata.capacity == 50 + - domain_group.rule_group.rule_group_metadata.rule_group_name == domains_group_name + - domain_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - domain_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in domain_group.rule_group.rule_group' + - '"rules_source_list" in domain_group.rule_group.rule_group.rules_source' + - '"generated_rules_type" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"target_types" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"targets" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.generated_rules_type == 'DENYLIST' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.target_types == ['HTTP_HOST'] + - domain_group.rule_group.rule_group.rules_source.rules_source_list.targets == ['example.com', '.example.net'] + + - name: 'Update a Domain List Rule Group with HTTP only (idempotency)' + networkfirewall_rule_group: + name: '{{ domains_group_name }}' + type: 'stateful' + domain_list: + domain_names: + - 'example.com' + - '.example.net' + action: deny + filter_http: true + register: domain_group + + - assert: + that: + - domain_group is not changed + - '"rule_group" in domain_group' + - '"rule_group" in domain_group.rule_group' + - '"rule_group_metadata" in domain_group.rule_group' + - '"capacity" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_name" in domain_group.rule_group.rule_group_metadata' + - '"type" in domain_group.rule_group.rule_group_metadata' + - domain_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - domain_group.rule_group.rule_group_metadata.capacity == 50 + - domain_group.rule_group.rule_group_metadata.rule_group_name == domains_group_name + - domain_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - domain_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in domain_group.rule_group.rule_group' + - '"rules_source_list" in domain_group.rule_group.rule_group.rules_source' + - '"generated_rules_type" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"target_types" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"targets" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.generated_rules_type == 'DENYLIST' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.target_types == ['HTTP_HOST'] + - domain_group.rule_group.rule_group.rules_source.rules_source_list.targets == ['example.com', '.example.net'] + + ##### + + - name: '(CHECK) Update a Domain List Rule Group with HTTPS only' + networkfirewall_rule_group: + name: '{{ domains_group_name }}' + type: 'stateful' + domain_list: + domain_names: + - 'example.com' + - '.example.net' + action: deny + filter_https: true + register: domain_group + check_mode: true + + - assert: + that: + - domain_group is changed + - '"rule_group" in domain_group' + - '"rule_group" in domain_group.rule_group' + - '"rule_group_metadata" in domain_group.rule_group' + - '"capacity" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_name" in domain_group.rule_group.rule_group_metadata' + - '"type" in domain_group.rule_group.rule_group_metadata' + - domain_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - domain_group.rule_group.rule_group_metadata.capacity == 50 + - domain_group.rule_group.rule_group_metadata.rule_group_name == domains_group_name + - domain_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - domain_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in domain_group.rule_group.rule_group' + - '"rules_source_list" in domain_group.rule_group.rule_group.rules_source' + - '"generated_rules_type" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"target_types" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"targets" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.generated_rules_type == 'DENYLIST' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.target_types == ['TLS_SNI'] + - domain_group.rule_group.rule_group.rules_source.rules_source_list.targets == ['example.com', '.example.net'] + + - name: 'Update a Domain List Rule Group with HTTPS only' + networkfirewall_rule_group: + name: '{{ domains_group_name }}' + type: 'stateful' + domain_list: + domain_names: + - 'example.com' + - '.example.net' + action: deny + filter_https: true + register: domain_group + + - assert: + that: + - domain_group is changed + - '"rule_group" in domain_group' + - '"rule_group" in domain_group.rule_group' + - '"rule_group_metadata" in domain_group.rule_group' + - '"capacity" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_arn" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_id" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_name" in domain_group.rule_group.rule_group_metadata' + - '"type" in domain_group.rule_group.rule_group_metadata' + - domain_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - domain_group.rule_group.rule_group_metadata.capacity == 50 + - domain_group.rule_group.rule_group_metadata.rule_group_name == domains_group_name + - domain_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - domain_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in domain_group.rule_group.rule_group' + - '"rules_source_list" in domain_group.rule_group.rule_group.rules_source' + - '"generated_rules_type" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"target_types" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"targets" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.generated_rules_type == 'DENYLIST' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.target_types == ['TLS_SNI'] + - domain_group.rule_group.rule_group.rules_source.rules_source_list.targets == ['example.com', '.example.net'] + + - name: '(CHECK) Update a Domain List Rule Group with HTTPS only (idempotency)' + networkfirewall_rule_group: + name: '{{ domains_group_name }}' + type: 'stateful' + domain_list: + domain_names: + - 'example.com' + - '.example.net' + action: deny + filter_https: true + register: domain_group + check_mode: true + + - assert: + that: + - domain_group is not changed + - '"rule_group" in domain_group' + - '"rule_group" in domain_group.rule_group' + - '"rule_group_metadata" in domain_group.rule_group' + - '"capacity" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_name" in domain_group.rule_group.rule_group_metadata' + - '"type" in domain_group.rule_group.rule_group_metadata' + - domain_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - domain_group.rule_group.rule_group_metadata.capacity == 50 + - domain_group.rule_group.rule_group_metadata.rule_group_name == domains_group_name + - domain_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - domain_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in domain_group.rule_group.rule_group' + - '"rules_source_list" in domain_group.rule_group.rule_group.rules_source' + - '"generated_rules_type" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"target_types" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"targets" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.generated_rules_type == 'DENYLIST' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.target_types == ['TLS_SNI'] + - domain_group.rule_group.rule_group.rules_source.rules_source_list.targets == ['example.com', '.example.net'] + + - name: 'Update a Domain List Rule Group with HTTPS only (idempotency)' + networkfirewall_rule_group: + name: '{{ domains_group_name }}' + type: 'stateful' + domain_list: + domain_names: + - 'example.com' + - '.example.net' + action: deny + filter_https: true + register: domain_group + + - assert: + that: + - domain_group is not changed + - '"rule_group" in domain_group' + - '"rule_group" in domain_group.rule_group' + - '"rule_group_metadata" in domain_group.rule_group' + - '"capacity" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_name" in domain_group.rule_group.rule_group_metadata' + - '"type" in domain_group.rule_group.rule_group_metadata' + - domain_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - domain_group.rule_group.rule_group_metadata.capacity == 50 + - domain_group.rule_group.rule_group_metadata.rule_group_name == domains_group_name + - domain_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - domain_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in domain_group.rule_group.rule_group' + - '"rules_source_list" in domain_group.rule_group.rule_group.rules_source' + - '"generated_rules_type" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"target_types" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"targets" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.generated_rules_type == 'DENYLIST' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.target_types == ['TLS_SNI'] + - domain_group.rule_group.rule_group.rules_source.rules_source_list.targets == ['example.com', '.example.net'] + + ##### + + - name: '(CHECK) Update a Domain List Rule Group with HTTP and HTTPS' + networkfirewall_rule_group: + name: '{{ domains_group_name }}' + type: 'stateful' + domain_list: + domain_names: + - 'example.com' + - '.example.net' + action: deny + filter_http: true + filter_https: true + register: domain_group + check_mode: true + + - assert: + that: + - domain_group is changed + - '"rule_group" in domain_group' + - '"rule_group" in domain_group.rule_group' + - '"rule_group_metadata" in domain_group.rule_group' + - '"capacity" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_name" in domain_group.rule_group.rule_group_metadata' + - '"type" in domain_group.rule_group.rule_group_metadata' + - domain_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - domain_group.rule_group.rule_group_metadata.capacity == 50 + - domain_group.rule_group.rule_group_metadata.rule_group_name == domains_group_name + - domain_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - domain_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in domain_group.rule_group.rule_group' + - '"rules_source_list" in domain_group.rule_group.rule_group.rules_source' + - '"generated_rules_type" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"target_types" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"targets" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.generated_rules_type == 'DENYLIST' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.target_types == ['HTTP_HOST', 'TLS_SNI'] + - domain_group.rule_group.rule_group.rules_source.rules_source_list.targets == ['example.com', '.example.net'] + + - name: 'Update a Domain List Rule Group with HTTP and HTTPS' + networkfirewall_rule_group: + name: '{{ domains_group_name }}' + type: 'stateful' + domain_list: + domain_names: + - 'example.com' + - '.example.net' + action: deny + filter_http: true + filter_https: true + register: domain_group + + - assert: + that: + - domain_group is changed + - '"rule_group" in domain_group' + - '"rule_group" in domain_group.rule_group' + - '"rule_group_metadata" in domain_group.rule_group' + - '"capacity" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_arn" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_id" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_name" in domain_group.rule_group.rule_group_metadata' + - '"type" in domain_group.rule_group.rule_group_metadata' + - domain_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - domain_group.rule_group.rule_group_metadata.capacity == 50 + - domain_group.rule_group.rule_group_metadata.rule_group_name == domains_group_name + - domain_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - domain_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in domain_group.rule_group.rule_group' + - '"rules_source_list" in domain_group.rule_group.rule_group.rules_source' + - '"generated_rules_type" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"target_types" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"targets" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.generated_rules_type == 'DENYLIST' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.target_types == ['HTTP_HOST', 'TLS_SNI'] + - domain_group.rule_group.rule_group.rules_source.rules_source_list.targets == ['example.com', '.example.net'] + + - name: '(CHECK) Update a Domain List Rule Group with HTTP and HTTPS (idempotency)' + networkfirewall_rule_group: + name: '{{ domains_group_name }}' + type: 'stateful' + domain_list: + domain_names: + - 'example.com' + - '.example.net' + action: deny + filter_http: true + filter_https: true + register: domain_group + check_mode: true + + - assert: + that: + - domain_group is not changed + - '"rule_group" in domain_group' + - '"rule_group" in domain_group.rule_group' + - '"rule_group_metadata" in domain_group.rule_group' + - '"capacity" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_name" in domain_group.rule_group.rule_group_metadata' + - '"type" in domain_group.rule_group.rule_group_metadata' + - domain_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - domain_group.rule_group.rule_group_metadata.capacity == 50 + - domain_group.rule_group.rule_group_metadata.rule_group_name == domains_group_name + - domain_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - domain_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in domain_group.rule_group.rule_group' + - '"rules_source_list" in domain_group.rule_group.rule_group.rules_source' + - '"generated_rules_type" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"target_types" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"targets" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.generated_rules_type == 'DENYLIST' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.target_types == ['HTTP_HOST', 'TLS_SNI'] + - domain_group.rule_group.rule_group.rules_source.rules_source_list.targets == ['example.com', '.example.net'] + + - name: 'Update a Domain List Rule Group with HTTP and HTTPS (idempotency)' + networkfirewall_rule_group: + name: '{{ domains_group_name }}' + type: 'stateful' + domain_list: + domain_names: + - 'example.com' + - '.example.net' + action: deny + filter_http: true + filter_https: true + register: domain_group + + - assert: + that: + - domain_group is not changed + - '"rule_group" in domain_group' + - '"rule_group" in domain_group.rule_group' + - '"rule_group_metadata" in domain_group.rule_group' + - '"capacity" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_name" in domain_group.rule_group.rule_group_metadata' + - '"type" in domain_group.rule_group.rule_group_metadata' + - domain_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - domain_group.rule_group.rule_group_metadata.capacity == 50 + - domain_group.rule_group.rule_group_metadata.rule_group_name == domains_group_name + - domain_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - domain_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in domain_group.rule_group.rule_group' + - '"rules_source_list" in domain_group.rule_group.rule_group.rules_source' + - '"generated_rules_type" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"target_types" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"targets" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.generated_rules_type == 'DENYLIST' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.target_types == ['HTTP_HOST', 'TLS_SNI'] + - domain_group.rule_group.rule_group.rules_source.rules_source_list.targets == ['example.com', '.example.net'] + + ##### + + - name: '(CHECK) Update a Domain List Rule Group with Source IP list' + networkfirewall_rule_group: + name: '{{ domains_group_name }}' + type: 'stateful' + domain_list: + domain_names: + - 'example.com' + - '.example.net' + action: deny + filter_http: true + filter_https: true + source_ips: '203.0.113.0/24' + register: domain_group + check_mode: true + + - assert: + that: + - domain_group is changed + - '"rule_group" in domain_group' + - '"rule_group" in domain_group.rule_group' + - '"rule_group_metadata" in domain_group.rule_group' + - '"capacity" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_name" in domain_group.rule_group.rule_group_metadata' + - '"type" in domain_group.rule_group.rule_group_metadata' + - domain_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - domain_group.rule_group.rule_group_metadata.capacity == 50 + - domain_group.rule_group.rule_group_metadata.rule_group_name == domains_group_name + - domain_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - domain_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in domain_group.rule_group.rule_group' + - '"rules_source_list" in domain_group.rule_group.rule_group.rules_source' + - '"generated_rules_type" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"target_types" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"targets" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.generated_rules_type == 'DENYLIST' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.target_types == ['HTTP_HOST', 'TLS_SNI'] + - domain_group.rule_group.rule_group.rules_source.rules_source_list.targets == ['example.com', '.example.net'] + - '"rule_variables" in domain_group.rule_group.rule_group' + - '"ip_sets" in domain_group.rule_group.rule_group.rule_variables' + - '"HOME_NET" in domain_group.rule_group.rule_group.rule_variables.ip_sets' + - domain_group.rule_group.rule_group.rule_variables.ip_sets['HOME_NET'] == ['203.0.113.0/24'] + + - name: 'Update a Domain List Rule Group with Source IP list' + networkfirewall_rule_group: + name: '{{ domains_group_name }}' + type: 'stateful' + domain_list: + domain_names: + - 'example.com' + - '.example.net' + action: deny + filter_http: true + filter_https: true + source_ips: '203.0.113.0/24' + register: domain_group + + - assert: + that: + - domain_group is changed + - '"rule_group" in domain_group' + - '"rule_group" in domain_group.rule_group' + - '"rule_group_metadata" in domain_group.rule_group' + - '"capacity" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_arn" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_id" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_name" in domain_group.rule_group.rule_group_metadata' + - '"type" in domain_group.rule_group.rule_group_metadata' + - domain_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - domain_group.rule_group.rule_group_metadata.capacity == 50 + - domain_group.rule_group.rule_group_metadata.rule_group_name == domains_group_name + - domain_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - domain_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in domain_group.rule_group.rule_group' + - '"rules_source_list" in domain_group.rule_group.rule_group.rules_source' + - '"generated_rules_type" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"target_types" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"targets" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.generated_rules_type == 'DENYLIST' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.target_types == ['HTTP_HOST', 'TLS_SNI'] + - domain_group.rule_group.rule_group.rules_source.rules_source_list.targets == ['example.com', '.example.net'] + - '"rule_variables" in domain_group.rule_group.rule_group' + - '"ip_sets" in domain_group.rule_group.rule_group.rule_variables' + - '"HOME_NET" in domain_group.rule_group.rule_group.rule_variables.ip_sets' + - domain_group.rule_group.rule_group.rule_variables.ip_sets['HOME_NET'] == ['203.0.113.0/24'] + + - name: '(CHECK) Update a Domain List Rule Group with Source IP list (idempotency)' + networkfirewall_rule_group: + name: '{{ domains_group_name }}' + type: 'stateful' + domain_list: + domain_names: + - 'example.com' + - '.example.net' + action: deny + filter_http: true + filter_https: true + source_ips: '203.0.113.0/24' + register: domain_group + check_mode: true + + - assert: + that: + - domain_group is not changed + - '"rule_group" in domain_group' + - '"rule_group" in domain_group.rule_group' + - '"rule_group_metadata" in domain_group.rule_group' + - '"capacity" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_name" in domain_group.rule_group.rule_group_metadata' + - '"type" in domain_group.rule_group.rule_group_metadata' + - domain_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - domain_group.rule_group.rule_group_metadata.capacity == 50 + - domain_group.rule_group.rule_group_metadata.rule_group_name == domains_group_name + - domain_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - domain_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in domain_group.rule_group.rule_group' + - '"rules_source_list" in domain_group.rule_group.rule_group.rules_source' + - '"generated_rules_type" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"target_types" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"targets" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.generated_rules_type == 'DENYLIST' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.target_types == ['HTTP_HOST', 'TLS_SNI'] + - domain_group.rule_group.rule_group.rules_source.rules_source_list.targets == ['example.com', '.example.net'] + - '"rule_variables" in domain_group.rule_group.rule_group' + - '"ip_sets" in domain_group.rule_group.rule_group.rule_variables' + - '"HOME_NET" in domain_group.rule_group.rule_group.rule_variables.ip_sets' + - domain_group.rule_group.rule_group.rule_variables.ip_sets['HOME_NET'] == ['203.0.113.0/24'] + + - name: 'Update a Domain List Rule Group with Source IP list (idempotency)' + networkfirewall_rule_group: + name: '{{ domains_group_name }}' + type: 'stateful' + domain_list: + domain_names: + - 'example.com' + - '.example.net' + action: deny + filter_http: true + filter_https: true + source_ips: '203.0.113.0/24' + register: domain_group + + - assert: + that: + - domain_group is not changed + - '"rule_group" in domain_group' + - '"rule_group" in domain_group.rule_group' + - '"rule_group_metadata" in domain_group.rule_group' + - '"capacity" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_name" in domain_group.rule_group.rule_group_metadata' + - '"type" in domain_group.rule_group.rule_group_metadata' + - domain_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - domain_group.rule_group.rule_group_metadata.capacity == 50 + - domain_group.rule_group.rule_group_metadata.rule_group_name == domains_group_name + - domain_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - domain_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in domain_group.rule_group.rule_group' + - '"rules_source_list" in domain_group.rule_group.rule_group.rules_source' + - '"generated_rules_type" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"target_types" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"targets" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.generated_rules_type == 'DENYLIST' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.target_types == ['HTTP_HOST', 'TLS_SNI'] + - domain_group.rule_group.rule_group.rules_source.rules_source_list.targets == ['example.com', '.example.net'] + - '"rule_variables" in domain_group.rule_group.rule_group' + - '"ip_sets" in domain_group.rule_group.rule_group.rule_variables' + - '"HOME_NET" in domain_group.rule_group.rule_group.rule_variables.ip_sets' + - domain_group.rule_group.rule_group.rule_variables.ip_sets['HOME_NET'] == ['203.0.113.0/24'] + + ##### + + - name: '(CHECK) Update a Domain List Rule Group with updated Source IP list' + networkfirewall_rule_group: + name: '{{ domains_group_name }}' + type: 'stateful' + domain_list: + domain_names: + - 'example.com' + - '.example.net' + action: deny + filter_http: true + filter_https: true + source_ips: + - '203.0.113.0/24' + - '198.51.100.248' + register: domain_group + check_mode: true + + - assert: + that: + - domain_group is changed + - '"rule_group" in domain_group' + - '"rule_group" in domain_group.rule_group' + - '"rule_group_metadata" in domain_group.rule_group' + - '"capacity" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_name" in domain_group.rule_group.rule_group_metadata' + - '"type" in domain_group.rule_group.rule_group_metadata' + - domain_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - domain_group.rule_group.rule_group_metadata.capacity == 50 + - domain_group.rule_group.rule_group_metadata.rule_group_name == domains_group_name + - domain_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - domain_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in domain_group.rule_group.rule_group' + - '"rules_source_list" in domain_group.rule_group.rule_group.rules_source' + - '"generated_rules_type" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"target_types" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"targets" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.generated_rules_type == 'DENYLIST' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.target_types == ['HTTP_HOST', 'TLS_SNI'] + - domain_group.rule_group.rule_group.rules_source.rules_source_list.targets == ['example.com', '.example.net'] + - '"rule_variables" in domain_group.rule_group.rule_group' + - '"ip_sets" in domain_group.rule_group.rule_group.rule_variables' + - '"HOME_NET" in domain_group.rule_group.rule_group.rule_variables.ip_sets' + - domain_group.rule_group.rule_group.rule_variables.ip_sets['HOME_NET'] == ['203.0.113.0/24', '198.51.100.248'] + + - name: 'Update a Domain List Rule Group with updated Source IP list' + networkfirewall_rule_group: + name: '{{ domains_group_name }}' + type: 'stateful' + domain_list: + domain_names: + - 'example.com' + - '.example.net' + action: deny + filter_http: true + filter_https: true + source_ips: + - '203.0.113.0/24' + - '198.51.100.248' + register: domain_group + + - assert: + that: + - domain_group is changed + - '"rule_group" in domain_group' + - '"rule_group" in domain_group.rule_group' + - '"rule_group_metadata" in domain_group.rule_group' + - '"capacity" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_arn" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_id" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_name" in domain_group.rule_group.rule_group_metadata' + - '"type" in domain_group.rule_group.rule_group_metadata' + - domain_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - domain_group.rule_group.rule_group_metadata.capacity == 50 + - domain_group.rule_group.rule_group_metadata.rule_group_name == domains_group_name + - domain_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - domain_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in domain_group.rule_group.rule_group' + - '"rules_source_list" in domain_group.rule_group.rule_group.rules_source' + - '"generated_rules_type" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"target_types" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"targets" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.generated_rules_type == 'DENYLIST' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.target_types == ['HTTP_HOST', 'TLS_SNI'] + - domain_group.rule_group.rule_group.rules_source.rules_source_list.targets == ['example.com', '.example.net'] + - '"rule_variables" in domain_group.rule_group.rule_group' + - '"ip_sets" in domain_group.rule_group.rule_group.rule_variables' + - '"HOME_NET" in domain_group.rule_group.rule_group.rule_variables.ip_sets' + - domain_group.rule_group.rule_group.rule_variables.ip_sets['HOME_NET'] == ['203.0.113.0/24', '198.51.100.248'] + + - name: '(CHECK) Update a Domain List Rule Group with updated Source IP list (idempotency)' + networkfirewall_rule_group: + name: '{{ domains_group_name }}' + type: 'stateful' + domain_list: + domain_names: + - 'example.com' + - '.example.net' + action: deny + filter_http: true + filter_https: true + source_ips: + - '203.0.113.0/24' + - '198.51.100.248' + register: domain_group + check_mode: true + + - assert: + that: + - domain_group is not changed + - '"rule_group" in domain_group' + - '"rule_group" in domain_group.rule_group' + - '"rule_group_metadata" in domain_group.rule_group' + - '"capacity" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_name" in domain_group.rule_group.rule_group_metadata' + - '"type" in domain_group.rule_group.rule_group_metadata' + - domain_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - domain_group.rule_group.rule_group_metadata.capacity == 50 + - domain_group.rule_group.rule_group_metadata.rule_group_name == domains_group_name + - domain_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - domain_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in domain_group.rule_group.rule_group' + - '"rules_source_list" in domain_group.rule_group.rule_group.rules_source' + - '"generated_rules_type" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"target_types" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"targets" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.generated_rules_type == 'DENYLIST' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.target_types == ['HTTP_HOST', 'TLS_SNI'] + - domain_group.rule_group.rule_group.rules_source.rules_source_list.targets == ['example.com', '.example.net'] + - '"rule_variables" in domain_group.rule_group.rule_group' + - '"ip_sets" in domain_group.rule_group.rule_group.rule_variables' + - '"HOME_NET" in domain_group.rule_group.rule_group.rule_variables.ip_sets' + - domain_group.rule_group.rule_group.rule_variables.ip_sets['HOME_NET'] == ['203.0.113.0/24', '198.51.100.248'] + + - name: 'Update a Domain List Rule Group with updated Source IP list (idempotency)' + networkfirewall_rule_group: + name: '{{ domains_group_name }}' + type: 'stateful' + domain_list: + domain_names: + - 'example.com' + - '.example.net' + action: deny + filter_http: true + filter_https: true + source_ips: + - '203.0.113.0/24' + - '198.51.100.248' + register: domain_group + + - assert: + that: + - domain_group is not changed + - '"rule_group" in domain_group' + - '"rule_group" in domain_group.rule_group' + - '"rule_group_metadata" in domain_group.rule_group' + - '"capacity" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_name" in domain_group.rule_group.rule_group_metadata' + - '"type" in domain_group.rule_group.rule_group_metadata' + - domain_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - domain_group.rule_group.rule_group_metadata.capacity == 50 + - domain_group.rule_group.rule_group_metadata.rule_group_name == domains_group_name + - domain_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - domain_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in domain_group.rule_group.rule_group' + - '"rules_source_list" in domain_group.rule_group.rule_group.rules_source' + - '"generated_rules_type" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"target_types" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"targets" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.generated_rules_type == 'DENYLIST' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.target_types == ['HTTP_HOST', 'TLS_SNI'] + - domain_group.rule_group.rule_group.rules_source.rules_source_list.targets == ['example.com', '.example.net'] + - '"rule_variables" in domain_group.rule_group.rule_group' + - '"ip_sets" in domain_group.rule_group.rule_group.rule_variables' + - '"HOME_NET" in domain_group.rule_group.rule_group.rule_variables.ip_sets' + - domain_group.rule_group.rule_group.rule_variables.ip_sets['HOME_NET'] == ['203.0.113.0/24', '198.51.100.248'] + + ##### + + - name: '(CHECK) Update a Domain List Rule Group with removed Source IP list' + networkfirewall_rule_group: + name: '{{ domains_group_name }}' + type: 'stateful' + domain_list: + domain_names: + - 'example.com' + - '.example.net' + action: deny + filter_http: true + filter_https: true + register: domain_group + check_mode: true + + - assert: + that: + - domain_group is changed + - '"rule_group" in domain_group' + - '"rule_group" in domain_group.rule_group' + - '"rule_group_metadata" in domain_group.rule_group' + - '"capacity" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_name" in domain_group.rule_group.rule_group_metadata' + - '"type" in domain_group.rule_group.rule_group_metadata' + - domain_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - domain_group.rule_group.rule_group_metadata.capacity == 50 + - domain_group.rule_group.rule_group_metadata.rule_group_name == domains_group_name + - domain_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - domain_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in domain_group.rule_group.rule_group' + - '"rules_source_list" in domain_group.rule_group.rule_group.rules_source' + - '"generated_rules_type" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"target_types" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"targets" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.generated_rules_type == 'DENYLIST' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.target_types == ['HTTP_HOST', 'TLS_SNI'] + - domain_group.rule_group.rule_group.rules_source.rules_source_list.targets == ['example.com', '.example.net'] + - '"rule_variables" in domain_group.rule_group.rule_group' + - domain_group.rule_group.rule_group.rule_variables == {} + + - name: 'Update a Domain List Rule Group with removed Source IP list' + networkfirewall_rule_group: + name: '{{ domains_group_name }}' + type: 'stateful' + domain_list: + domain_names: + - 'example.com' + - '.example.net' + action: deny + filter_http: true + filter_https: true + register: domain_group + + - assert: + that: + - domain_group is changed + - '"rule_group" in domain_group' + - '"rule_group" in domain_group.rule_group' + - '"rule_group_metadata" in domain_group.rule_group' + - '"capacity" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_arn" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_id" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_name" in domain_group.rule_group.rule_group_metadata' + - '"type" in domain_group.rule_group.rule_group_metadata' + - domain_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - domain_group.rule_group.rule_group_metadata.capacity == 50 + - domain_group.rule_group.rule_group_metadata.rule_group_name == domains_group_name + - domain_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - domain_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in domain_group.rule_group.rule_group' + - '"rules_source_list" in domain_group.rule_group.rule_group.rules_source' + - '"generated_rules_type" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"target_types" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"targets" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.generated_rules_type == 'DENYLIST' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.target_types == ['HTTP_HOST', 'TLS_SNI'] + - domain_group.rule_group.rule_group.rules_source.rules_source_list.targets == ['example.com', '.example.net'] + - '"rule_variables" in domain_group.rule_group.rule_group' + - domain_group.rule_group.rule_group.rule_variables == {} + + - name: '(CHECK) Update a Domain List Rule Group with removed Source IP list (idempotency)' + networkfirewall_rule_group: + name: '{{ domains_group_name }}' + type: 'stateful' + domain_list: + domain_names: + - 'example.com' + - '.example.net' + action: deny + filter_http: true + filter_https: true + register: domain_group + check_mode: true + + - assert: + that: + - domain_group is not changed + - '"rule_group" in domain_group' + - '"rule_group" in domain_group.rule_group' + - '"rule_group_metadata" in domain_group.rule_group' + - '"capacity" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_name" in domain_group.rule_group.rule_group_metadata' + - '"type" in domain_group.rule_group.rule_group_metadata' + - domain_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - domain_group.rule_group.rule_group_metadata.capacity == 50 + - domain_group.rule_group.rule_group_metadata.rule_group_name == domains_group_name + - domain_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - domain_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in domain_group.rule_group.rule_group' + - '"rules_source_list" in domain_group.rule_group.rule_group.rules_source' + - '"generated_rules_type" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"target_types" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"targets" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.generated_rules_type == 'DENYLIST' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.target_types == ['HTTP_HOST', 'TLS_SNI'] + - domain_group.rule_group.rule_group.rules_source.rules_source_list.targets == ['example.com', '.example.net'] + - '"rule_variables" in domain_group.rule_group.rule_group' + - domain_group.rule_group.rule_group.rule_variables == {} + + - name: 'Update a Domain List Rule Group with removed Source IP list (idempotency)' + networkfirewall_rule_group: + name: '{{ domains_group_name }}' + type: 'stateful' + domain_list: + domain_names: + - 'example.com' + - '.example.net' + action: deny + filter_http: true + filter_https: true + register: domain_group + + - assert: + that: + - domain_group is not changed + - '"rule_group" in domain_group' + - '"rule_group" in domain_group.rule_group' + - '"rule_group_metadata" in domain_group.rule_group' + - '"capacity" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_name" in domain_group.rule_group.rule_group_metadata' + - '"type" in domain_group.rule_group.rule_group_metadata' + - domain_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - domain_group.rule_group.rule_group_metadata.capacity == 50 + - domain_group.rule_group.rule_group_metadata.rule_group_name == domains_group_name + - domain_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - domain_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in domain_group.rule_group.rule_group' + - '"rules_source_list" in domain_group.rule_group.rule_group.rules_source' + - '"generated_rules_type" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"target_types" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"targets" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.generated_rules_type == 'DENYLIST' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.target_types == ['HTTP_HOST', 'TLS_SNI'] + - domain_group.rule_group.rule_group.rules_source.rules_source_list.targets == ['example.com', '.example.net'] + - '"rule_variables" in domain_group.rule_group.rule_group' + - domain_group.rule_group.rule_group.rule_variables == {} + + ##### + + - name: '(CHECK) Update a Domain List Rule Group with no protocols' + networkfirewall_rule_group: + name: '{{ domains_group_name }}' + type: 'stateful' + domain_list: + domain_names: + - 'example.com' + - '.example.net' + action: deny + register: domain_group + check_mode: true + + - assert: + that: + - domain_group is changed + - '"rule_group" in domain_group' + - '"rule_group" in domain_group.rule_group' + - '"rule_group_metadata" in domain_group.rule_group' + - '"capacity" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_name" in domain_group.rule_group.rule_group_metadata' + - '"type" in domain_group.rule_group.rule_group_metadata' + - domain_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - domain_group.rule_group.rule_group_metadata.capacity == 50 + - domain_group.rule_group.rule_group_metadata.rule_group_name == domains_group_name + - domain_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - domain_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in domain_group.rule_group.rule_group' + - '"rules_source_list" in domain_group.rule_group.rule_group.rules_source' + - '"generated_rules_type" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"target_types" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"targets" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.generated_rules_type == 'DENYLIST' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.target_types == [] + - domain_group.rule_group.rule_group.rules_source.rules_source_list.targets == ['example.com', '.example.net'] + + - name: 'Update a Domain List Rule Group with no protocols' + networkfirewall_rule_group: + name: '{{ domains_group_name }}' + type: 'stateful' + domain_list: + domain_names: + - 'example.com' + - '.example.net' + action: deny + register: domain_group + + - assert: + that: + - domain_group is changed + - '"rule_group" in domain_group' + - '"rule_group" in domain_group.rule_group' + - '"rule_group_metadata" in domain_group.rule_group' + - '"capacity" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_arn" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_id" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_name" in domain_group.rule_group.rule_group_metadata' + - '"type" in domain_group.rule_group.rule_group_metadata' + - domain_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - domain_group.rule_group.rule_group_metadata.capacity == 50 + - domain_group.rule_group.rule_group_metadata.rule_group_name == domains_group_name + - domain_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - domain_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in domain_group.rule_group.rule_group' + - '"rules_source_list" in domain_group.rule_group.rule_group.rules_source' + - '"generated_rules_type" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"target_types" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"targets" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.generated_rules_type == 'DENYLIST' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.target_types == [] + - domain_group.rule_group.rule_group.rules_source.rules_source_list.targets == ['example.com', '.example.net'] + + - name: '(CHECK) Update a Domain List Rule Group with no protocols (idempotency)' + networkfirewall_rule_group: + name: '{{ domains_group_name }}' + type: 'stateful' + domain_list: + domain_names: + - 'example.com' + - '.example.net' + action: deny + register: domain_group + check_mode: true + + - assert: + that: + - domain_group is not changed + - '"rule_group" in domain_group' + - '"rule_group" in domain_group.rule_group' + - '"rule_group_metadata" in domain_group.rule_group' + - '"capacity" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_name" in domain_group.rule_group.rule_group_metadata' + - '"type" in domain_group.rule_group.rule_group_metadata' + - domain_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - domain_group.rule_group.rule_group_metadata.capacity == 50 + - domain_group.rule_group.rule_group_metadata.rule_group_name == domains_group_name + - domain_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - domain_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in domain_group.rule_group.rule_group' + - '"rules_source_list" in domain_group.rule_group.rule_group.rules_source' + - '"generated_rules_type" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"target_types" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"targets" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.generated_rules_type == 'DENYLIST' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.target_types == [] + - domain_group.rule_group.rule_group.rules_source.rules_source_list.targets == ['example.com', '.example.net'] + + - name: 'Update a Domain List Rule Group with no protocols (idempotency)' + networkfirewall_rule_group: + name: '{{ domains_group_name }}' + type: 'stateful' + domain_list: + domain_names: + - 'example.com' + - '.example.net' + action: deny + register: domain_group + + - assert: + that: + - domain_group is not changed + - '"rule_group" in domain_group' + - '"rule_group" in domain_group.rule_group' + - '"rule_group_metadata" in domain_group.rule_group' + - '"capacity" in domain_group.rule_group.rule_group_metadata' + - '"rule_group_name" in domain_group.rule_group.rule_group_metadata' + - '"type" in domain_group.rule_group.rule_group_metadata' + - domain_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - domain_group.rule_group.rule_group_metadata.capacity == 50 + - domain_group.rule_group.rule_group_metadata.rule_group_name == domains_group_name + - domain_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - domain_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in domain_group.rule_group.rule_group' + - '"rules_source_list" in domain_group.rule_group.rule_group.rules_source' + - '"generated_rules_type" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"target_types" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - '"targets" in domain_group.rule_group.rule_group.rules_source.rules_source_list' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.generated_rules_type == 'DENYLIST' + - domain_group.rule_group.rule_group.rules_source.rules_source_list.target_types == [] + - domain_group.rule_group.rule_group.rules_source.rules_source_list.targets == ['example.com', '.example.net'] + + ################################################################### + # Deletion + + - name: '(CHECK) Delete Domain List rule group' + networkfirewall_rule_group: + name: '{{ domains_group_name }}' + type: 'stateful' + state: absent + register: domain_group + check_mode: true + + - assert: + that: + - domain_group is changed + + - name: 'Delete Domain List rule group' + networkfirewall_rule_group: + name: '{{ domains_group_name }}' + type: 'stateful' + state: absent + register: domain_group + + - assert: + that: + - domain_group is changed + + # The Rule Group may still exist in a "DELETING" state, we should still + # return not changed + - name: 'Delete Domain List rule group (idempotency)' + networkfirewall_rule_group: + name: '{{ domains_group_name }}' + type: 'stateful' + state: absent + wait: False + register: domain_group + check_mode: true + + - assert: + that: + - domain_group is not changed + + - name: '(CHECK) Delete Domain List rule group (idempotency)' + networkfirewall_rule_group: + name: '{{ domains_group_name }}' + type: 'stateful' + state: absent + wait: False + register: domain_group + + - assert: + that: + - domain_group is not changed + + always: + - name: '(always) Delete Domain List rule group' + networkfirewall_rule_group: + name: '{{ domains_group_name }}' + type: 'stateful' + state: absent + wait: False + ignore_errors: true diff --git a/ansible_collections/community/aws/tests/integration/targets/networkfirewall_rule_group/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/networkfirewall_rule_group/tasks/main.yml new file mode 100644 index 000000000..a6e84426e --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/networkfirewall_rule_group/tasks/main.yml @@ -0,0 +1,49 @@ +--- +- module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key | default(omit) }}' + aws_secret_key: '{{ aws_secret_key | default(omit) }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region | default(omit) }}' + collections: + - amazon.aws + - community.aws + block: + # Fetch some info about the account so we can build ARNs + - aws_caller_info: {} + register: caller_info + - name: 'Generate the ARN pattern to search for' + vars: + _caller_info: '{{ caller_info.arn.split(":") }}' + _base_arn: 'arn:{{_caller_info[1]}}:network-firewall:{{aws_region}}' + set_fact: + account_arn: '{{_base_arn}}:{{_caller_info[4]}}:stateful-rulegroup/' + managed_arn: '{{_base_arn}}:aws-managed:stateful-rulegroup/' + + # List the Managed Rule Groups (there's no access to the rules themselves) + - include_tasks: 'managed.yml' + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + + # Minimal tests and manipulation of common metadata + - include_tasks: 'minimal.yml' + + # Tests Manipulation of common Stateful settings + - include_tasks: 'stateful.yml' + + # XXX Not yet supported, + # please also update networkfirewall_policy tests once supported. + # # Tests Manipulation of common Stateless rule group settings + # - include_tasks: 'stateless.yml' + + # Tests Manipulation of Suricata formatted rule strings + - include_tasks: 'rule_strings.yml' + + # Tests Manipulation of DomainList rule groups + - include_tasks: 'domain_list.yml' + + # Tests Manipulation of 5-Tuple rule groups + - include_tasks: '5-tuple.yml' + + always: + - include_tasks: 'cleanup.yml' diff --git a/ansible_collections/community/aws/tests/integration/targets/networkfirewall_rule_group/tasks/managed.yml b/ansible_collections/community/aws/tests/integration/targets/networkfirewall_rule_group/tasks/managed.yml new file mode 100644 index 000000000..a79a5d9ba --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/networkfirewall_rule_group/tasks/managed.yml @@ -0,0 +1,10 @@ +--- +# Tests related to the Managed Firewall rules +- networkfirewall_rule_group_info: + scope: managed + register: managed_rules_info + +- assert: + that: + - '"rule_list" in managed_rules_info' + - managed_rules_info.rule_list | length > 0 diff --git a/ansible_collections/community/aws/tests/integration/targets/networkfirewall_rule_group/tasks/minimal.yml b/ansible_collections/community/aws/tests/integration/targets/networkfirewall_rule_group/tasks/minimal.yml new file mode 100644 index 000000000..73f96d3a5 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/networkfirewall_rule_group/tasks/minimal.yml @@ -0,0 +1,787 @@ +--- +# +# Basic manipulation of a Firewall Group +# - Minimal Creation +# - Deletion +# - Updating Metadata +# -- description +# -- tags +# +# Uses an 'allow all' string based rule, but doesn't attempt to manipulate the +# rule itself +# +- vars: + minimal_group_name: '{{ group_name_prefix }}-MinimalGroup' + missing_group_name: '{{ group_name_prefix }}-MissingGroup' + 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: + # Test basic functionality of the modules + - name: 'Fetch all account rule groups' + networkfirewall_rule_group_info: {} + register: account_rules_info + + - assert: + that: + - '"rule_list" in account_rules_info' + - '"rule_groups" in account_rules_info' + # We've not created anything yet, so there's no guarantee anything will be here + + - name: 'Fetch non existent rule groups' + networkfirewall_rule_group_info: + name: '{{ minimal_group_name }}-Missing' + rule_type: 'stateful' + register: account_rules_info + + - assert: + that: + - '"rule_groups" in account_rules_info' + - account_rules_info.rule_groups | length == 0 + + ################################################################### + # Creation + + # The simplest form of rule group + - name: '(CHECK) Create a Rule Group with minimal settings' + networkfirewall_rule_group: + name: '{{ minimal_group_name }}' + type: 'stateful' + # Needed for creation + capacity: 100 + # Needed for creation - We'll test manipulating them later + rule_strings: + - 'pass tcp any any -> any any (sid:1000001;)' + register: minimal_group + check_mode: true + + - assert: + that: + - minimal_group is changed + - '"rule_group" in minimal_group' + - '"rule_group" in minimal_group.rule_group' + - '"rule_group_metadata" in minimal_group.rule_group' + - '"capacity" in minimal_group.rule_group.rule_group_metadata' + - '"rule_group_name" in minimal_group.rule_group.rule_group_metadata' + - '"type" in minimal_group.rule_group.rule_group_metadata' + - minimal_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - minimal_group.rule_group.rule_group_metadata.capacity == 100 + - minimal_group.rule_group.rule_group_metadata.rule_group_name == minimal_group_name + + # The simplest form of rule group + - name: 'Create a Rule Group with minimal settings' + networkfirewall_rule_group: + name: '{{ minimal_group_name }}' + type: 'stateful' + # Needed for creation + capacity: 100 + # Needed for creation - We'll test manipulating them later + rule_strings: + - 'pass tcp any any -> any any (sid:1000001;)' + register: minimal_group + + - assert: + that: + - minimal_group is changed + - '"rule_group" in minimal_group' + - '"rule_group" in minimal_group.rule_group' + - '"rule_group_metadata" in minimal_group.rule_group' + - '"capacity" in minimal_group.rule_group.rule_group_metadata' + - '"rule_group_arn" in minimal_group.rule_group.rule_group_metadata' + - '"rule_group_id" in minimal_group.rule_group.rule_group_metadata' + - '"rule_group_name" in minimal_group.rule_group.rule_group_metadata' + - '"type" in minimal_group.rule_group.rule_group_metadata' + - minimal_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - minimal_group.rule_group.rule_group_metadata.capacity == 100 + - minimal_group.rule_group.rule_group_metadata.rule_group_name == minimal_group_name + - minimal_group.rule_group.rule_group_metadata.rule_group_arn.startswith(account_arn) + - minimal_group.rule_group.rule_group_metadata.rule_group_arn.endswith(minimal_group_name) + + - name: Save RuleGroup ID/ARN for later + set_fact: + minimal_rule_group_id: '{{ minimal_group.rule_group.rule_group_metadata.rule_group_id }}' + minimal_rule_group_arn: '{{ minimal_group.rule_group.rule_group_metadata.rule_group_arn }}' + + - name: '(CHECK) Create a Rule Group with minimal settings (idempotency)' + networkfirewall_rule_group: + name: '{{ minimal_group_name }}' + type: 'stateful' + capacity: 100 + register: minimal_group + check_mode: true + + - assert: + that: + - minimal_group is not changed + - '"rule_group" in minimal_group' + - '"rule_group" in minimal_group.rule_group' + - '"rule_group_metadata" in minimal_group.rule_group' + - '"capacity" in minimal_group.rule_group.rule_group_metadata' + - '"rule_group_name" in minimal_group.rule_group.rule_group_metadata' + - '"type" in minimal_group.rule_group.rule_group_metadata' + - minimal_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - minimal_group.rule_group.rule_group_metadata.capacity == 100 + - minimal_group.rule_group.rule_group_metadata.rule_group_name == minimal_group_name + - minimal_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - minimal_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + + - name: 'Create a Rule Group with minimal settings (idempotency)' + networkfirewall_rule_group: + name: '{{ minimal_group_name }}' + type: 'stateful' + capacity: 100 + register: minimal_group + + - assert: + that: + - minimal_group is not changed + - '"rule_group" in minimal_group' + - '"rule_group" in minimal_group.rule_group' + - '"rule_group_metadata" in minimal_group.rule_group' + - '"capacity" in minimal_group.rule_group.rule_group_metadata' + - '"rule_group_name" in minimal_group.rule_group.rule_group_metadata' + - '"type" in minimal_group.rule_group.rule_group_metadata' + - minimal_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - minimal_group.rule_group.rule_group_metadata.capacity == 100 + - minimal_group.rule_group.rule_group_metadata.rule_group_name == minimal_group_name + - minimal_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - minimal_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + + ################################################################### + # Capacity + + # Capacity can't be changed after creation + - name: '(CHECK) Attempt to change capacity' + networkfirewall_rule_group: + name: '{{ minimal_group_name }}' + type: 'stateful' + capacity: 200 + register: minimal_group + ignore_errors: true + check_mode: true + + - assert: + that: + - minimal_group is failed + + - name: 'Attempt to change capacity' + networkfirewall_rule_group: + name: '{{ minimal_group_name }}' + type: 'stateful' + capacity: 200 + register: minimal_group + ignore_errors: true + + - assert: + that: + - minimal_group is failed + + ################################################################### + # Description + + - name: '(CHECK) Add a description' + networkfirewall_rule_group: + name: '{{ minimal_group_name }}' + type: 'stateful' + description: 'Example Description' + register: minimal_group + check_mode: true + + - assert: + that: + - minimal_group is changed + - '"rule_group" in minimal_group' + - '"rule_group" in minimal_group.rule_group' + - '"rule_group_metadata" in minimal_group.rule_group' + - '"capacity" in minimal_group.rule_group.rule_group_metadata' + - '"rule_group_name" in minimal_group.rule_group.rule_group_metadata' + - '"type" in minimal_group.rule_group.rule_group_metadata' + - minimal_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - minimal_group.rule_group.rule_group_metadata.capacity == 100 + - minimal_group.rule_group.rule_group_metadata.rule_group_name == minimal_group_name + - minimal_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - minimal_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - minimal_group.rule_group.rule_group_metadata.description == 'Example Description' + + - name: 'Add a description' + networkfirewall_rule_group: + name: '{{ minimal_group_name }}' + type: 'stateful' + description: 'Example Description' + register: minimal_group + + - assert: + that: + - minimal_group is changed + - '"rule_group" in minimal_group' + - '"rule_group" in minimal_group.rule_group' + - '"rule_group_metadata" in minimal_group.rule_group' + - '"capacity" in minimal_group.rule_group.rule_group_metadata' + - '"rule_group_name" in minimal_group.rule_group.rule_group_metadata' + - '"type" in minimal_group.rule_group.rule_group_metadata' + - minimal_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - minimal_group.rule_group.rule_group_metadata.capacity == 100 + - minimal_group.rule_group.rule_group_metadata.rule_group_name == minimal_group_name + - minimal_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - minimal_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - minimal_group.rule_group.rule_group_metadata.description == 'Example Description' + + - name: '(CHECK) Add a description (idempotency)' + networkfirewall_rule_group: + name: '{{ minimal_group_name }}' + type: 'stateful' + description: 'Example Description' + register: minimal_group + check_mode: true + + - assert: + that: + - minimal_group is not changed + - '"rule_group" in minimal_group' + - '"rule_group" in minimal_group.rule_group' + - '"rule_group_metadata" in minimal_group.rule_group' + - '"capacity" in minimal_group.rule_group.rule_group_metadata' + - '"rule_group_name" in minimal_group.rule_group.rule_group_metadata' + - '"type" in minimal_group.rule_group.rule_group_metadata' + - minimal_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - minimal_group.rule_group.rule_group_metadata.capacity == 100 + - minimal_group.rule_group.rule_group_metadata.rule_group_name == minimal_group_name + - minimal_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - minimal_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - minimal_group.rule_group.rule_group_metadata.description == 'Example Description' + + - name: 'Add a description (idempotency)' + networkfirewall_rule_group: + name: '{{ minimal_group_name }}' + type: 'stateful' + description: 'Example Description' + register: minimal_group + + - assert: + that: + - minimal_group is not changed + - '"rule_group" in minimal_group' + - '"rule_group" in minimal_group.rule_group' + - '"rule_group_metadata" in minimal_group.rule_group' + - '"capacity" in minimal_group.rule_group.rule_group_metadata' + - '"rule_group_name" in minimal_group.rule_group.rule_group_metadata' + - '"type" in minimal_group.rule_group.rule_group_metadata' + - minimal_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - minimal_group.rule_group.rule_group_metadata.capacity == 100 + - minimal_group.rule_group.rule_group_metadata.rule_group_name == minimal_group_name + - minimal_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - minimal_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - minimal_group.rule_group.rule_group_metadata.description == 'Example Description' + + ##### + + - name: '(CHECK) Update a description' + networkfirewall_rule_group: + name: '{{ minimal_group_name }}' + type: 'stateful' + description: 'Updated description' + register: minimal_group + check_mode: true + + - assert: + that: + - minimal_group is changed + - '"rule_group" in minimal_group' + - '"rule_group" in minimal_group.rule_group' + - '"rule_group_metadata" in minimal_group.rule_group' + - '"capacity" in minimal_group.rule_group.rule_group_metadata' + - '"rule_group_name" in minimal_group.rule_group.rule_group_metadata' + - '"type" in minimal_group.rule_group.rule_group_metadata' + - minimal_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - minimal_group.rule_group.rule_group_metadata.capacity == 100 + - minimal_group.rule_group.rule_group_metadata.rule_group_name == minimal_group_name + - minimal_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - minimal_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - minimal_group.rule_group.rule_group_metadata.description == 'Updated description' + + - name: 'Update a description' + networkfirewall_rule_group: + name: '{{ minimal_group_name }}' + type: 'stateful' + description: 'Updated description' + register: minimal_group + + - assert: + that: + - minimal_group is changed + - '"rule_group" in minimal_group' + - '"rule_group" in minimal_group.rule_group' + - '"rule_group_metadata" in minimal_group.rule_group' + - '"capacity" in minimal_group.rule_group.rule_group_metadata' + - '"rule_group_name" in minimal_group.rule_group.rule_group_metadata' + - '"type" in minimal_group.rule_group.rule_group_metadata' + - minimal_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - minimal_group.rule_group.rule_group_metadata.capacity == 100 + - minimal_group.rule_group.rule_group_metadata.rule_group_name == minimal_group_name + - minimal_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - minimal_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - minimal_group.rule_group.rule_group_metadata.description == 'Updated description' + + - name: '(CHECK) Update a description (idempotency)' + networkfirewall_rule_group: + name: '{{ minimal_group_name }}' + type: 'stateful' + description: 'Updated description' + register: minimal_group + check_mode: true + + - assert: + that: + - minimal_group is not changed + - '"rule_group" in minimal_group' + - '"rule_group" in minimal_group.rule_group' + - '"rule_group_metadata" in minimal_group.rule_group' + - '"capacity" in minimal_group.rule_group.rule_group_metadata' + - '"rule_group_name" in minimal_group.rule_group.rule_group_metadata' + - '"type" in minimal_group.rule_group.rule_group_metadata' + - minimal_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - minimal_group.rule_group.rule_group_metadata.capacity == 100 + - minimal_group.rule_group.rule_group_metadata.rule_group_name == minimal_group_name + - minimal_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - minimal_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - minimal_group.rule_group.rule_group_metadata.description == 'Updated description' + + - name: 'Update a description (idempotency)' + networkfirewall_rule_group: + name: '{{ minimal_group_name }}' + type: 'stateful' + description: 'Updated description' + register: minimal_group + + - assert: + that: + - minimal_group is not changed + - '"rule_group" in minimal_group' + - '"rule_group" in minimal_group.rule_group' + - '"rule_group_metadata" in minimal_group.rule_group' + - '"capacity" in minimal_group.rule_group.rule_group_metadata' + - '"rule_group_name" in minimal_group.rule_group.rule_group_metadata' + - '"type" in minimal_group.rule_group.rule_group_metadata' + - minimal_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - minimal_group.rule_group.rule_group_metadata.capacity == 100 + - minimal_group.rule_group.rule_group_metadata.rule_group_name == minimal_group_name + - minimal_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - minimal_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - minimal_group.rule_group.rule_group_metadata.description == 'Updated description' + + ################################################################### + # Tags + + - name: '(CHECK) Tag Rule Group' + networkfirewall_rule_group: + name: '{{ minimal_group_name }}' + type: 'stateful' + tags: '{{ first_tags }}' + register: minimal_group + check_mode: true + + - assert: + that: + - minimal_group is changed + - '"rule_group" in minimal_group' + - '"rule_group" in minimal_group.rule_group' + - '"rule_group_metadata" in minimal_group.rule_group' + - '"capacity" in minimal_group.rule_group.rule_group_metadata' + - '"rule_group_name" in minimal_group.rule_group.rule_group_metadata' + - '"type" in minimal_group.rule_group.rule_group_metadata' + - '"tags" in minimal_group.rule_group.rule_group_metadata' + - minimal_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - minimal_group.rule_group.rule_group_metadata.capacity == 100 + - minimal_group.rule_group.rule_group_metadata.rule_group_name == minimal_group_name + - minimal_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - minimal_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - minimal_group.rule_group.rule_group_metadata.description == 'Updated description' + - minimal_group.rule_group.rule_group_metadata.tags == first_tags + + - name: 'Tag Rule Group' + networkfirewall_rule_group: + name: '{{ minimal_group_name }}' + type: 'stateful' + tags: '{{ first_tags }}' + register: minimal_group + + - assert: + that: + - minimal_group is changed + - '"rule_group" in minimal_group' + - '"rule_group" in minimal_group.rule_group' + - '"rule_group_metadata" in minimal_group.rule_group' + - '"capacity" in minimal_group.rule_group.rule_group_metadata' + - '"rule_group_name" in minimal_group.rule_group.rule_group_metadata' + - '"type" in minimal_group.rule_group.rule_group_metadata' + - minimal_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - minimal_group.rule_group.rule_group_metadata.capacity == 100 + - minimal_group.rule_group.rule_group_metadata.rule_group_name == minimal_group_name + - minimal_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - minimal_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - minimal_group.rule_group.rule_group_metadata.description == 'Updated description' + - minimal_group.rule_group.rule_group_metadata.tags == first_tags + + - name: '(CHECK) Tag Rule Group (idempotency)' + networkfirewall_rule_group: + name: '{{ minimal_group_name }}' + type: 'stateful' + tags: '{{ first_tags }}' + register: minimal_group + check_mode: true + + - assert: + that: + - minimal_group is not changed + - '"rule_group" in minimal_group' + - '"rule_group" in minimal_group.rule_group' + - '"rule_group_metadata" in minimal_group.rule_group' + - '"capacity" in minimal_group.rule_group.rule_group_metadata' + - '"rule_group_name" in minimal_group.rule_group.rule_group_metadata' + - '"type" in minimal_group.rule_group.rule_group_metadata' + - minimal_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - minimal_group.rule_group.rule_group_metadata.capacity == 100 + - minimal_group.rule_group.rule_group_metadata.rule_group_name == minimal_group_name + - minimal_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - minimal_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - minimal_group.rule_group.rule_group_metadata.description == 'Updated description' + - minimal_group.rule_group.rule_group_metadata.tags == first_tags + + - name: 'Tag Rule Group (idempotency)' + networkfirewall_rule_group: + name: '{{ minimal_group_name }}' + type: 'stateful' + tags: '{{ first_tags }}' + register: minimal_group + + - assert: + that: + - minimal_group is not changed + - '"rule_group" in minimal_group' + - '"rule_group" in minimal_group.rule_group' + - '"rule_group_metadata" in minimal_group.rule_group' + - '"capacity" in minimal_group.rule_group.rule_group_metadata' + - '"rule_group_name" in minimal_group.rule_group.rule_group_metadata' + - '"type" in minimal_group.rule_group.rule_group_metadata' + - minimal_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - minimal_group.rule_group.rule_group_metadata.capacity == 100 + - minimal_group.rule_group.rule_group_metadata.rule_group_name == minimal_group_name + - minimal_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - minimal_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - minimal_group.rule_group.rule_group_metadata.description == 'Updated description' + - minimal_group.rule_group.rule_group_metadata.tags == first_tags + + ##### + + - name: '(CHECK) Update tags with purge' + networkfirewall_rule_group: + name: '{{ minimal_group_name }}' + type: 'stateful' + tags: '{{ second_tags }}' + register: minimal_group + check_mode: true + + - assert: + that: + - minimal_group is changed + - '"rule_group" in minimal_group' + - '"rule_group" in minimal_group.rule_group' + - '"rule_group_metadata" in minimal_group.rule_group' + - '"capacity" in minimal_group.rule_group.rule_group_metadata' + - '"rule_group_name" in minimal_group.rule_group.rule_group_metadata' + - '"type" in minimal_group.rule_group.rule_group_metadata' + - '"tags" in minimal_group.rule_group.rule_group_metadata' + - minimal_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - minimal_group.rule_group.rule_group_metadata.capacity == 100 + - minimal_group.rule_group.rule_group_metadata.rule_group_name == minimal_group_name + - minimal_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - minimal_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - minimal_group.rule_group.rule_group_metadata.description == 'Updated description' + - minimal_group.rule_group.rule_group_metadata.tags == second_tags + + - name: 'Update tags with purge' + networkfirewall_rule_group: + name: '{{ minimal_group_name }}' + type: 'stateful' + tags: '{{ second_tags }}' + register: minimal_group + + - assert: + that: + - minimal_group is changed + - '"rule_group" in minimal_group' + - '"rule_group" in minimal_group.rule_group' + - '"rule_group_metadata" in minimal_group.rule_group' + - '"capacity" in minimal_group.rule_group.rule_group_metadata' + - '"rule_group_name" in minimal_group.rule_group.rule_group_metadata' + - '"type" in minimal_group.rule_group.rule_group_metadata' + - minimal_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - minimal_group.rule_group.rule_group_metadata.capacity == 100 + - minimal_group.rule_group.rule_group_metadata.rule_group_name == minimal_group_name + - minimal_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - minimal_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - minimal_group.rule_group.rule_group_metadata.description == 'Updated description' + - minimal_group.rule_group.rule_group_metadata.tags == second_tags + + - name: '(CHECK) Update tags with purge (idempotency)' + networkfirewall_rule_group: + name: '{{ minimal_group_name }}' + type: 'stateful' + tags: '{{ second_tags }}' + register: minimal_group + check_mode: true + + - assert: + that: + - minimal_group is not changed + - '"rule_group" in minimal_group' + - '"rule_group" in minimal_group.rule_group' + - '"rule_group_metadata" in minimal_group.rule_group' + - '"capacity" in minimal_group.rule_group.rule_group_metadata' + - '"rule_group_name" in minimal_group.rule_group.rule_group_metadata' + - '"type" in minimal_group.rule_group.rule_group_metadata' + - minimal_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - minimal_group.rule_group.rule_group_metadata.capacity == 100 + - minimal_group.rule_group.rule_group_metadata.rule_group_name == minimal_group_name + - minimal_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - minimal_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - minimal_group.rule_group.rule_group_metadata.description == 'Updated description' + - minimal_group.rule_group.rule_group_metadata.tags == second_tags + + - name: 'Update tags with purge (idempotency)' + networkfirewall_rule_group: + name: '{{ minimal_group_name }}' + type: 'stateful' + tags: '{{ second_tags }}' + register: minimal_group + + - assert: + that: + - minimal_group is not changed + - '"rule_group" in minimal_group' + - '"rule_group" in minimal_group.rule_group' + - '"rule_group_metadata" in minimal_group.rule_group' + - '"capacity" in minimal_group.rule_group.rule_group_metadata' + - '"rule_group_name" in minimal_group.rule_group.rule_group_metadata' + - '"type" in minimal_group.rule_group.rule_group_metadata' + - minimal_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - minimal_group.rule_group.rule_group_metadata.capacity == 100 + - minimal_group.rule_group.rule_group_metadata.rule_group_name == minimal_group_name + - minimal_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - minimal_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - minimal_group.rule_group.rule_group_metadata.description == 'Updated description' + - minimal_group.rule_group.rule_group_metadata.tags == second_tags + + ##### + + - name: '(CHECK) Update tags with no purge' + networkfirewall_rule_group: + name: '{{ minimal_group_name }}' + type: 'stateful' + tags: '{{ third_tags }}' + purge_tags: false + register: minimal_group + check_mode: true + + - assert: + that: + - minimal_group is changed + - '"rule_group" in minimal_group' + - '"rule_group" in minimal_group.rule_group' + - '"rule_group_metadata" in minimal_group.rule_group' + - '"capacity" in minimal_group.rule_group.rule_group_metadata' + - '"rule_group_name" in minimal_group.rule_group.rule_group_metadata' + - '"type" in minimal_group.rule_group.rule_group_metadata' + - '"tags" in minimal_group.rule_group.rule_group_metadata' + - minimal_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - minimal_group.rule_group.rule_group_metadata.capacity == 100 + - minimal_group.rule_group.rule_group_metadata.rule_group_name == minimal_group_name + - minimal_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - minimal_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - minimal_group.rule_group.rule_group_metadata.description == 'Updated description' + - minimal_group.rule_group.rule_group_metadata.tags == final_tags + + - name: 'Update tags with no purge' + networkfirewall_rule_group: + name: '{{ minimal_group_name }}' + type: 'stateful' + tags: '{{ third_tags }}' + purge_tags: false + register: minimal_group + + - assert: + that: + - minimal_group is changed + - '"rule_group" in minimal_group' + - '"rule_group" in minimal_group.rule_group' + - '"rule_group_metadata" in minimal_group.rule_group' + - '"capacity" in minimal_group.rule_group.rule_group_metadata' + - '"rule_group_name" in minimal_group.rule_group.rule_group_metadata' + - '"type" in minimal_group.rule_group.rule_group_metadata' + - minimal_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - minimal_group.rule_group.rule_group_metadata.capacity == 100 + - minimal_group.rule_group.rule_group_metadata.rule_group_name == minimal_group_name + - minimal_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - minimal_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - minimal_group.rule_group.rule_group_metadata.description == 'Updated description' + - minimal_group.rule_group.rule_group_metadata.tags == final_tags + + - name: '(CHECK) Update tags with no purge (idempotency)' + networkfirewall_rule_group: + name: '{{ minimal_group_name }}' + type: 'stateful' + tags: '{{ third_tags }}' + purge_tags: false + register: minimal_group + check_mode: true + + - assert: + that: + - minimal_group is not changed + - '"rule_group" in minimal_group' + - '"rule_group" in minimal_group.rule_group' + - '"rule_group_metadata" in minimal_group.rule_group' + - '"capacity" in minimal_group.rule_group.rule_group_metadata' + - '"rule_group_name" in minimal_group.rule_group.rule_group_metadata' + - '"type" in minimal_group.rule_group.rule_group_metadata' + - minimal_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - minimal_group.rule_group.rule_group_metadata.capacity == 100 + - minimal_group.rule_group.rule_group_metadata.rule_group_name == minimal_group_name + - minimal_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - minimal_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - minimal_group.rule_group.rule_group_metadata.description == 'Updated description' + - minimal_group.rule_group.rule_group_metadata.tags == final_tags + + - name: 'Update tags with no purge (idempotency)' + networkfirewall_rule_group: + name: '{{ minimal_group_name }}' + type: 'stateful' + tags: '{{ third_tags }}' + purge_tags: false + register: minimal_group + + - assert: + that: + - minimal_group is not changed + - '"rule_group" in minimal_group' + - '"rule_group" in minimal_group.rule_group' + - '"rule_group_metadata" in minimal_group.rule_group' + - '"capacity" in minimal_group.rule_group.rule_group_metadata' + - '"rule_group_name" in minimal_group.rule_group.rule_group_metadata' + - '"type" in minimal_group.rule_group.rule_group_metadata' + - minimal_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - minimal_group.rule_group.rule_group_metadata.capacity == 100 + - minimal_group.rule_group.rule_group_metadata.rule_group_name == minimal_group_name + - minimal_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - minimal_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - minimal_group.rule_group.rule_group_metadata.description == 'Updated description' + - minimal_group.rule_group.rule_group_metadata.tags == final_tags + + ################################################################### + # Deletion + + - name: '(CHECK) Delete minimal rule group' + networkfirewall_rule_group: + name: '{{ minimal_group_name }}' + type: 'stateful' + state: absent + wait: False + register: minimal_group + check_mode: true + + - assert: + that: + - minimal_group is changed + + - name: 'Delete minimal rule group' + networkfirewall_rule_group: + name: '{{ minimal_group_name }}' + type: 'stateful' + state: absent + wait: False + register: minimal_group + + - assert: + that: + - minimal_group is changed + + # The Rule Group may still exist in a "DELETING" state, we should still + # return not changed + - name: 'Delete minimal rule group (idempotency)' + networkfirewall_rule_group: + name: '{{ minimal_group_name }}' + type: 'stateful' + state: absent + wait: False + register: minimal_group + check_mode: true + + - assert: + that: + - minimal_group is not changed + + - name: '(CHECK) Delete minimal rule group (idempotency)' + networkfirewall_rule_group: + name: '{{ minimal_group_name }}' + type: 'stateful' + state: absent + wait: False + register: minimal_group + + - assert: + that: + - minimal_group is not changed + + ##### + - name: '(CHECK) Delete missing rule group' + networkfirewall_rule_group: + name: '{{ missing_group_name }}' + type: 'stateful' + state: absent + register: missing_group + check_mode: true + + - assert: + that: + - missing_group is not changed + + - name: 'Delete missing rule group' + networkfirewall_rule_group: + name: '{{ missing_group_name }}' + type: 'stateful' + state: absent + + - assert: + that: + - missing_group is not changed + + always: + - name: '(always) Delete minimal rule group' + networkfirewall_rule_group: + name: '{{ minimal_group_name }}' + type: 'stateful' + state: absent + wait: False + ignore_errors: true diff --git a/ansible_collections/community/aws/tests/integration/targets/networkfirewall_rule_group/tasks/rule_strings.yml b/ansible_collections/community/aws/tests/integration/targets/networkfirewall_rule_group/tasks/rule_strings.yml new file mode 100644 index 000000000..6820434d3 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/networkfirewall_rule_group/tasks/rule_strings.yml @@ -0,0 +1,485 @@ +--- +# +# Basic manipulation of Suricata String based rule groups +# - Minimal Creation +# - Deletion +# - Updating Rules +# +- vars: + strings_group_name: '{{ group_name_prefix }}-SuricataGroup' + rule_one: 'pass tcp any any -> any any (sid:1000001;)' + rule_two: 'drop tcp any any -> any any (sid:1000002;)' + rule_three: 'alert tcp any any -> any any (sid:1000003;)' + all_rules: |- + {{ rule_one }} + {{ rule_two }} + {{ rule_three }} + last_rules: |- + {{ rule_two }} + {{ rule_three }} + block: + ################################################################### + # Creation + + - name: '(CHECK) Create a rule_strings Rule Group' + networkfirewall_rule_group: + name: '{{ strings_group_name }}' + type: 'stateful' + capacity: 100 + rule_strings: '{{ rule_one }}' + register: strings_group + check_mode: true + + - assert: + that: + - strings_group is changed + - '"rule_group" in strings_group' + - '"rule_group" in strings_group.rule_group' + - '"rule_group_metadata" in strings_group.rule_group' + - '"capacity" in strings_group.rule_group.rule_group_metadata' + - '"rule_group_name" in strings_group.rule_group.rule_group_metadata' + - '"type" in strings_group.rule_group.rule_group_metadata' + - strings_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - strings_group.rule_group.rule_group_metadata.capacity == 100 + - strings_group.rule_group.rule_group_metadata.rule_group_name == strings_group_name + - '"rules_source" in strings_group.rule_group.rule_group' + - '"rules_string" in strings_group.rule_group.rule_group.rules_source' + - strings_group.rule_group.rule_group.rules_source.rules_string == rule_one + + - name: 'Create a rule_strings Rule Group' + networkfirewall_rule_group: + name: '{{ strings_group_name }}' + type: 'stateful' + capacity: 100 + rule_strings: '{{ rule_one }}' + register: strings_group + + - assert: + that: + - strings_group is changed + - '"rule_group" in strings_group' + - '"rule_group" in strings_group.rule_group' + - '"rule_group_metadata" in strings_group.rule_group' + - '"capacity" in strings_group.rule_group.rule_group_metadata' + - '"rule_group_arn" in strings_group.rule_group.rule_group_metadata' + - '"rule_group_id" in strings_group.rule_group.rule_group_metadata' + - '"rule_group_name" in strings_group.rule_group.rule_group_metadata' + - '"type" in strings_group.rule_group.rule_group_metadata' + - strings_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - strings_group.rule_group.rule_group_metadata.capacity == 100 + - strings_group.rule_group.rule_group_metadata.rule_group_name == strings_group_name + - strings_group.rule_group.rule_group_metadata.rule_group_arn.startswith(account_arn) + - strings_group.rule_group.rule_group_metadata.rule_group_arn.endswith(strings_group_name) + - '"rules_source" in strings_group.rule_group.rule_group' + - '"rules_string" in strings_group.rule_group.rule_group.rules_source' + - strings_group.rule_group.rule_group.rules_source.rules_string == rule_one + + - name: Save RuleGroup ID/ARN for later + set_fact: + minimal_rule_group_id: '{{ strings_group.rule_group.rule_group_metadata.rule_group_id }}' + minimal_rule_group_arn: '{{ strings_group.rule_group.rule_group_metadata.rule_group_arn }}' + + - name: '(CHECK) Create a rule_strings Rule Group (idempotency)' + networkfirewall_rule_group: + name: '{{ strings_group_name }}' + type: 'stateful' + capacity: 100 + rule_strings: '{{ rule_one }}' + register: strings_group + check_mode: true + + - assert: + that: + - strings_group is not changed + - '"rule_group" in strings_group' + - '"rule_group" in strings_group.rule_group' + - '"rule_group_metadata" in strings_group.rule_group' + - '"capacity" in strings_group.rule_group.rule_group_metadata' + - '"rule_group_name" in strings_group.rule_group.rule_group_metadata' + - '"type" in strings_group.rule_group.rule_group_metadata' + - strings_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - strings_group.rule_group.rule_group_metadata.capacity == 100 + - strings_group.rule_group.rule_group_metadata.rule_group_name == strings_group_name + - strings_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - strings_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in strings_group.rule_group.rule_group' + - '"rules_string" in strings_group.rule_group.rule_group.rules_source' + - strings_group.rule_group.rule_group.rules_source.rules_string == rule_one + + - name: 'Create a rule_strings Rule Group (idempotency)' + networkfirewall_rule_group: + name: '{{ strings_group_name }}' + type: 'stateful' + capacity: 100 + rule_strings: '{{ rule_one }}' + register: strings_group + + - assert: + that: + - strings_group is not changed + - '"rule_group" in strings_group' + - '"rule_group" in strings_group.rule_group' + - '"rule_group_metadata" in strings_group.rule_group' + - '"capacity" in strings_group.rule_group.rule_group_metadata' + - '"rule_group_name" in strings_group.rule_group.rule_group_metadata' + - '"type" in strings_group.rule_group.rule_group_metadata' + - strings_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - strings_group.rule_group.rule_group_metadata.capacity == 100 + - strings_group.rule_group.rule_group_metadata.rule_group_name == strings_group_name + - strings_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - strings_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in strings_group.rule_group.rule_group' + - '"rules_string" in strings_group.rule_group.rule_group.rules_source' + - strings_group.rule_group.rule_group.rules_source.rules_string == rule_one + + ##### + + - name: '(CHECK) Test that rule_strings as a list with one element behaves the same as a single string' + networkfirewall_rule_group: + name: '{{ strings_group_name }}' + type: 'stateful' + rule_strings: + - '{{ rule_one }}' + register: strings_group + check_mode: true + + - assert: + that: + - strings_group is not changed + - '"rule_group" in strings_group' + - '"rule_group" in strings_group.rule_group' + - '"rule_group_metadata" in strings_group.rule_group' + - '"capacity" in strings_group.rule_group.rule_group_metadata' + - '"rule_group_name" in strings_group.rule_group.rule_group_metadata' + - '"type" in strings_group.rule_group.rule_group_metadata' + - strings_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - strings_group.rule_group.rule_group_metadata.capacity == 100 + - strings_group.rule_group.rule_group_metadata.rule_group_name == strings_group_name + - strings_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - strings_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in strings_group.rule_group.rule_group' + - '"rules_string" in strings_group.rule_group.rule_group.rules_source' + - strings_group.rule_group.rule_group.rules_source.rules_string == rule_one + + - name: 'Test that rule_strings as a list with one element behaves the same as a single string' + networkfirewall_rule_group: + name: '{{ strings_group_name }}' + type: 'stateful' + rule_strings: + - '{{ rule_one }}' + register: strings_group + + - assert: + that: + - strings_group is not changed + - '"rule_group" in strings_group' + - '"rule_group" in strings_group.rule_group' + - '"rule_group_metadata" in strings_group.rule_group' + - '"capacity" in strings_group.rule_group.rule_group_metadata' + - '"rule_group_name" in strings_group.rule_group.rule_group_metadata' + - '"type" in strings_group.rule_group.rule_group_metadata' + - strings_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - strings_group.rule_group.rule_group_metadata.capacity == 100 + - strings_group.rule_group.rule_group_metadata.rule_group_name == strings_group_name + - strings_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - strings_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in strings_group.rule_group.rule_group' + - '"rules_string" in strings_group.rule_group.rule_group.rules_source' + - strings_group.rule_group.rule_group.rules_source.rules_string == rule_one + + ################################################################### + # Update + + - name: '(CHECK) Update a rule_strings Rule Group' + networkfirewall_rule_group: + name: '{{ strings_group_name }}' + type: 'stateful' + rule_strings: + - '{{ rule_one }}' + - '{{ rule_two }}' + - '{{ rule_three }}' + register: strings_group + check_mode: true + + - assert: + that: + - strings_group is changed + - '"rule_group" in strings_group' + - '"rule_group" in strings_group.rule_group' + - '"rule_group_metadata" in strings_group.rule_group' + - '"capacity" in strings_group.rule_group.rule_group_metadata' + - '"rule_group_name" in strings_group.rule_group.rule_group_metadata' + - '"type" in strings_group.rule_group.rule_group_metadata' + - strings_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - strings_group.rule_group.rule_group_metadata.capacity == 100 + - strings_group.rule_group.rule_group_metadata.rule_group_name == strings_group_name + - strings_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - strings_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in strings_group.rule_group.rule_group' + - '"rules_string" in strings_group.rule_group.rule_group.rules_source' + - strings_group.rule_group.rule_group.rules_source.rules_string == all_rules + + - name: 'Update a rule_strings Rule Group' + networkfirewall_rule_group: + name: '{{ strings_group_name }}' + type: 'stateful' + rule_strings: + - '{{ rule_one }}' + - '{{ rule_two }}' + - '{{ rule_three }}' + register: strings_group + + - assert: + that: + - strings_group is changed + - '"rule_group" in strings_group' + - '"rule_group" in strings_group.rule_group' + - '"rule_group_metadata" in strings_group.rule_group' + - '"capacity" in strings_group.rule_group.rule_group_metadata' + - '"rule_group_arn" in strings_group.rule_group.rule_group_metadata' + - '"rule_group_id" in strings_group.rule_group.rule_group_metadata' + - '"rule_group_name" in strings_group.rule_group.rule_group_metadata' + - '"type" in strings_group.rule_group.rule_group_metadata' + - strings_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - strings_group.rule_group.rule_group_metadata.capacity == 100 + - strings_group.rule_group.rule_group_metadata.rule_group_name == strings_group_name + - strings_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - strings_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in strings_group.rule_group.rule_group' + - '"rules_string" in strings_group.rule_group.rule_group.rules_source' + - strings_group.rule_group.rule_group.rules_source.rules_string == all_rules + + - name: '(CHECK) Update a rule_strings Rule Group (idempotency)' + networkfirewall_rule_group: + name: '{{ strings_group_name }}' + type: 'stateful' + rule_strings: + - '{{ rule_one }}' + - '{{ rule_two }}' + - '{{ rule_three }}' + register: strings_group + check_mode: true + + - assert: + that: + - strings_group is not changed + - '"rule_group" in strings_group' + - '"rule_group" in strings_group.rule_group' + - '"rule_group_metadata" in strings_group.rule_group' + - '"capacity" in strings_group.rule_group.rule_group_metadata' + - '"rule_group_name" in strings_group.rule_group.rule_group_metadata' + - '"type" in strings_group.rule_group.rule_group_metadata' + - strings_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - strings_group.rule_group.rule_group_metadata.capacity == 100 + - strings_group.rule_group.rule_group_metadata.rule_group_name == strings_group_name + - strings_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - strings_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in strings_group.rule_group.rule_group' + - '"rules_string" in strings_group.rule_group.rule_group.rules_source' + - strings_group.rule_group.rule_group.rules_source.rules_string == all_rules + + - name: 'Update a rule_strings Rule Group (idempotency)' + networkfirewall_rule_group: + name: '{{ strings_group_name }}' + type: 'stateful' + rule_strings: + - '{{ rule_one }}' + - '{{ rule_two }}' + - '{{ rule_three }}' + register: strings_group + + - assert: + that: + - strings_group is not changed + - '"rule_group" in strings_group' + - '"rule_group" in strings_group.rule_group' + - '"rule_group_metadata" in strings_group.rule_group' + - '"capacity" in strings_group.rule_group.rule_group_metadata' + - '"rule_group_name" in strings_group.rule_group.rule_group_metadata' + - '"type" in strings_group.rule_group.rule_group_metadata' + - strings_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - strings_group.rule_group.rule_group_metadata.capacity == 100 + - strings_group.rule_group.rule_group_metadata.rule_group_name == strings_group_name + - strings_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - strings_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in strings_group.rule_group.rule_group' + - '"rules_string" in strings_group.rule_group.rule_group.rules_source' + - strings_group.rule_group.rule_group.rules_source.rules_string == all_rules + + ##### + + - name: '(CHECK) Update(2) a rule_strings Rule Group' + networkfirewall_rule_group: + name: '{{ strings_group_name }}' + type: 'stateful' + rule_strings: + - '{{ rule_two }}' + - '{{ rule_three }}' + register: strings_group + check_mode: true + + - assert: + that: + - strings_group is changed + - '"rule_group" in strings_group' + - '"rule_group" in strings_group.rule_group' + - '"rule_group_metadata" in strings_group.rule_group' + - '"capacity" in strings_group.rule_group.rule_group_metadata' + - '"rule_group_name" in strings_group.rule_group.rule_group_metadata' + - '"type" in strings_group.rule_group.rule_group_metadata' + - strings_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - strings_group.rule_group.rule_group_metadata.capacity == 100 + - strings_group.rule_group.rule_group_metadata.rule_group_name == strings_group_name + - strings_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - strings_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in strings_group.rule_group.rule_group' + - '"rules_string" in strings_group.rule_group.rule_group.rules_source' + - strings_group.rule_group.rule_group.rules_source.rules_string == last_rules + + - name: 'Update(2) a rule_strings Rule Group' + networkfirewall_rule_group: + name: '{{ strings_group_name }}' + type: 'stateful' + rule_strings: + - '{{ rule_two }}' + - '{{ rule_three }}' + register: strings_group + + - assert: + that: + - strings_group is changed + - '"rule_group" in strings_group' + - '"rule_group" in strings_group.rule_group' + - '"rule_group_metadata" in strings_group.rule_group' + - '"capacity" in strings_group.rule_group.rule_group_metadata' + - '"rule_group_arn" in strings_group.rule_group.rule_group_metadata' + - '"rule_group_id" in strings_group.rule_group.rule_group_metadata' + - '"rule_group_name" in strings_group.rule_group.rule_group_metadata' + - '"type" in strings_group.rule_group.rule_group_metadata' + - strings_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - strings_group.rule_group.rule_group_metadata.capacity == 100 + - strings_group.rule_group.rule_group_metadata.rule_group_name == strings_group_name + - strings_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - strings_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in strings_group.rule_group.rule_group' + - '"rules_string" in strings_group.rule_group.rule_group.rules_source' + - strings_group.rule_group.rule_group.rules_source.rules_string == last_rules + + - name: '(CHECK) Update(2) a rule_strings Rule Group (idempotency)' + networkfirewall_rule_group: + name: '{{ strings_group_name }}' + type: 'stateful' + rule_strings: + - '{{ rule_two }}' + - '{{ rule_three }}' + register: strings_group + check_mode: true + + - assert: + that: + - strings_group is not changed + - '"rule_group" in strings_group' + - '"rule_group" in strings_group.rule_group' + - '"rule_group_metadata" in strings_group.rule_group' + - '"capacity" in strings_group.rule_group.rule_group_metadata' + - '"rule_group_name" in strings_group.rule_group.rule_group_metadata' + - '"type" in strings_group.rule_group.rule_group_metadata' + - strings_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - strings_group.rule_group.rule_group_metadata.capacity == 100 + - strings_group.rule_group.rule_group_metadata.rule_group_name == strings_group_name + - strings_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - strings_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in strings_group.rule_group.rule_group' + - '"rules_string" in strings_group.rule_group.rule_group.rules_source' + - strings_group.rule_group.rule_group.rules_source.rules_string == last_rules + + - name: 'Update(2) a rule_strings Rule Group (idempotency)' + networkfirewall_rule_group: + name: '{{ strings_group_name }}' + type: 'stateful' + rule_strings: + - '{{ rule_two }}' + - '{{ rule_three }}' + register: strings_group + + - assert: + that: + - strings_group is not changed + - '"rule_group" in strings_group' + - '"rule_group" in strings_group.rule_group' + - '"rule_group_metadata" in strings_group.rule_group' + - '"capacity" in strings_group.rule_group.rule_group_metadata' + - '"rule_group_name" in strings_group.rule_group.rule_group_metadata' + - '"type" in strings_group.rule_group.rule_group_metadata' + - strings_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - strings_group.rule_group.rule_group_metadata.capacity == 100 + - strings_group.rule_group.rule_group_metadata.rule_group_name == strings_group_name + - strings_group.rule_group.rule_group_metadata.rule_group_arn == minimal_rule_group_arn + - strings_group.rule_group.rule_group_metadata.rule_group_id == minimal_rule_group_id + - '"rules_source" in strings_group.rule_group.rule_group' + - '"rules_string" in strings_group.rule_group.rule_group.rules_source' + - strings_group.rule_group.rule_group.rules_source.rules_string == last_rules + + ################################################################### + # Deletion + + - name: '(CHECK) Delete rule_strings rule group' + networkfirewall_rule_group: + name: '{{ strings_group_name }}' + type: 'stateful' + state: absent + wait: False + register: strings_group + check_mode: true + + - assert: + that: + - strings_group is changed + + - name: 'Delete rule_strings rule group' + networkfirewall_rule_group: + name: '{{ strings_group_name }}' + type: 'stateful' + state: absent + wait: False + register: strings_group + + - assert: + that: + - strings_group is changed + + # The Rule Group may still exist in a "DELETING" state, we should still + # return not changed + - name: 'Delete rule_strings rule group (idempotency)' + networkfirewall_rule_group: + name: '{{ strings_group_name }}' + type: 'stateful' + state: absent + wait: False + register: strings_group + check_mode: true + + - assert: + that: + - strings_group is not changed + + - name: '(CHECK) Delete rule_strings rule group (idempotency)' + networkfirewall_rule_group: + name: '{{ strings_group_name }}' + type: 'stateful' + state: absent + wait: False + register: strings_group + + - assert: + that: + - strings_group is not changed + + always: + - name: '(always) Delete rule_strings rule group' + networkfirewall_rule_group: + name: '{{ strings_group_name }}' + type: 'stateful' + state: absent + wait: False + ignore_errors: true diff --git a/ansible_collections/community/aws/tests/integration/targets/networkfirewall_rule_group/tasks/stateful.yml b/ansible_collections/community/aws/tests/integration/targets/networkfirewall_rule_group/tasks/stateful.yml new file mode 100644 index 000000000..3b92a4cee --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/networkfirewall_rule_group/tasks/stateful.yml @@ -0,0 +1,1366 @@ +--- +# +# Manipulation of options common to stateful rules +# - Minimal Creation +# - Updating IP Variables +# - Updating Port Variables +# - Setting RuleOrder +# +- vars: + strict_ro_group_name: '{{ group_name_prefix }}-StrictROGroup' + default_ro_group_name: '{{ group_name_prefix }}-DefaultROGroup' + stateful_group_name: '{{ group_name_prefix }}-StatefulGroup' + rule_one: 'pass tcp any any -> any any (sid:1000001;)' + ip_variable_one: + EXAMPLE_IP: '192.0.2.5' + ip_variable_one_list: + EXAMPLE_IP: ['192.0.2.5'] + ip_variable_two: + ANOTHER_EXAMPLE: ['198.51.100.13', '198.51.100.235', '203.0.113.0/24'] + ip_variable_both: + EXAMPLE_IP: ['192.0.2.5'] + ANOTHER_EXAMPLE: ['198.51.100.13', '198.51.100.235', '203.0.113.0/24'] + port_variable_one: + EXAMPLE_PORT: '22' + port_variable_one_list: + EXAMPLE_PORT: ['22'] + port_variable_two: + ANOTHER_PORT: ['443', '8443'] + port_variable_both: + EXAMPLE_PORT: ['22'] + ANOTHER_PORT: ['443', '8443'] + block: + ################################################################### + # Creation + + - name: 'Create a Stateful Rule Group' + networkfirewall_rule_group: + name: '{{ stateful_group_name }}' + type: 'stateful' + capacity: 100 + rule_strings: '{{ rule_one }}' + register: stateful_group + + - assert: + that: + - stateful_group is changed + - '"rule_group" in stateful_group' + - '"rule_group" in stateful_group.rule_group' + - '"rule_group_metadata" in stateful_group.rule_group' + - '"capacity" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_arn" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_id" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_name" in stateful_group.rule_group.rule_group_metadata' + - '"type" in stateful_group.rule_group.rule_group_metadata' + - stateful_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - stateful_group.rule_group.rule_group_metadata.capacity == 100 + - stateful_group.rule_group.rule_group_metadata.rule_group_name == stateful_group_name + - stateful_group.rule_group.rule_group_metadata.rule_group_arn.startswith(account_arn) + - stateful_group.rule_group.rule_group_metadata.rule_group_arn.endswith(stateful_group_name) + # Check that we've defaulted to the DEFAULT rule order + + - name: Save RuleGroup ID/ARN for later + set_fact: + stateful_rule_group_id: '{{ stateful_group.rule_group.rule_group_metadata.rule_group_id }}' + stateful_rule_group_arn: '{{ stateful_group.rule_group.rule_group_metadata.rule_group_arn }}' + + ################################################################### + # Update IP Variables + + - name: '(CHECK) Add IP Variable' + networkfirewall_rule_group: + name: '{{ stateful_group_name }}' + type: 'stateful' + ip_variables: '{{ ip_variable_one }}' + register: stateful_group + check_mode: true + + - assert: + that: + - stateful_group is changed + - '"rule_group" in stateful_group' + - '"rule_group" in stateful_group.rule_group' + - '"rule_group_metadata" in stateful_group.rule_group' + - '"capacity" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_name" in stateful_group.rule_group.rule_group_metadata' + - '"type" in stateful_group.rule_group.rule_group_metadata' + - stateful_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - stateful_group.rule_group.rule_group_metadata.capacity == 100 + - stateful_group.rule_group.rule_group_metadata.rule_group_name == stateful_group_name + - stateful_group.rule_group.rule_group_metadata.rule_group_arn == stateful_rule_group_arn + - stateful_group.rule_group.rule_group_metadata.rule_group_id == stateful_rule_group_id + - '"rule_variables" in stateful_group.rule_group.rule_group' + - '"ip_sets" in stateful_group.rule_group.rule_group.rule_variables' + - stateful_group.rule_group.rule_group.rule_variables.ip_sets == ip_variable_one_list + + - name: 'Add IP Variable' + networkfirewall_rule_group: + name: '{{ stateful_group_name }}' + type: 'stateful' + ip_variables: '{{ ip_variable_one }}' + register: stateful_group + + - assert: + that: + - stateful_group is changed + - '"rule_group" in stateful_group' + - '"rule_group" in stateful_group.rule_group' + - '"rule_group_metadata" in stateful_group.rule_group' + - '"capacity" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_arn" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_id" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_name" in stateful_group.rule_group.rule_group_metadata' + - '"type" in stateful_group.rule_group.rule_group_metadata' + - stateful_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - stateful_group.rule_group.rule_group_metadata.capacity == 100 + - stateful_group.rule_group.rule_group_metadata.rule_group_name == stateful_group_name + - stateful_group.rule_group.rule_group_metadata.rule_group_arn == stateful_rule_group_arn + - stateful_group.rule_group.rule_group_metadata.rule_group_id == stateful_rule_group_id + - '"rule_variables" in stateful_group.rule_group.rule_group' + - '"ip_sets" in stateful_group.rule_group.rule_group.rule_variables' + - stateful_group.rule_group.rule_group.rule_variables.ip_sets == ip_variable_one_list + + - name: '(CHECK) Add IP Variable (idempotency)' + networkfirewall_rule_group: + name: '{{ stateful_group_name }}' + type: 'stateful' + ip_variables: '{{ ip_variable_one }}' + register: stateful_group + check_mode: true + + - assert: + that: + - stateful_group is not changed + - '"rule_group" in stateful_group' + - '"rule_group" in stateful_group.rule_group' + - '"rule_group_metadata" in stateful_group.rule_group' + - '"capacity" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_name" in stateful_group.rule_group.rule_group_metadata' + - '"type" in stateful_group.rule_group.rule_group_metadata' + - stateful_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - stateful_group.rule_group.rule_group_metadata.capacity == 100 + - stateful_group.rule_group.rule_group_metadata.rule_group_name == stateful_group_name + - stateful_group.rule_group.rule_group_metadata.rule_group_arn == stateful_rule_group_arn + - stateful_group.rule_group.rule_group_metadata.rule_group_id == stateful_rule_group_id + - '"rule_variables" in stateful_group.rule_group.rule_group' + - '"ip_sets" in stateful_group.rule_group.rule_group.rule_variables' + - stateful_group.rule_group.rule_group.rule_variables.ip_sets == ip_variable_one_list + + - name: 'Add IP Variable (idempotency)' + networkfirewall_rule_group: + name: '{{ stateful_group_name }}' + type: 'stateful' + ip_variables: '{{ ip_variable_one }}' + register: stateful_group + + - assert: + that: + - stateful_group is not changed + - '"rule_group" in stateful_group' + - '"rule_group" in stateful_group.rule_group' + - '"rule_group_metadata" in stateful_group.rule_group' + - '"capacity" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_name" in stateful_group.rule_group.rule_group_metadata' + - '"type" in stateful_group.rule_group.rule_group_metadata' + - stateful_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - stateful_group.rule_group.rule_group_metadata.capacity == 100 + - stateful_group.rule_group.rule_group_metadata.rule_group_name == stateful_group_name + - stateful_group.rule_group.rule_group_metadata.rule_group_arn == stateful_rule_group_arn + - stateful_group.rule_group.rule_group_metadata.rule_group_id == stateful_rule_group_id + - '"rule_variables" in stateful_group.rule_group.rule_group' + - '"ip_sets" in stateful_group.rule_group.rule_group.rule_variables' + - stateful_group.rule_group.rule_group.rule_variables.ip_sets == ip_variable_one_list + + ##### + + - name: '(CHECK) Ensure IP Variable string/list equivalence (idempotency)' + networkfirewall_rule_group: + name: '{{ stateful_group_name }}' + type: 'stateful' + ip_variables: '{{ ip_variable_one_list }}' + register: stateful_group + check_mode: true + + - assert: + that: + - stateful_group is not changed + - '"rule_group" in stateful_group' + - '"rule_group" in stateful_group.rule_group' + - '"rule_group_metadata" in stateful_group.rule_group' + - '"capacity" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_name" in stateful_group.rule_group.rule_group_metadata' + - '"type" in stateful_group.rule_group.rule_group_metadata' + - stateful_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - stateful_group.rule_group.rule_group_metadata.capacity == 100 + - stateful_group.rule_group.rule_group_metadata.rule_group_name == stateful_group_name + - stateful_group.rule_group.rule_group_metadata.rule_group_arn == stateful_rule_group_arn + - stateful_group.rule_group.rule_group_metadata.rule_group_id == stateful_rule_group_id + - '"rule_variables" in stateful_group.rule_group.rule_group' + - '"ip_sets" in stateful_group.rule_group.rule_group.rule_variables' + - stateful_group.rule_group.rule_group.rule_variables.ip_sets == ip_variable_one_list + + - name: 'Ensure IP Variable string/list equivalence (idempotency)' + networkfirewall_rule_group: + name: '{{ stateful_group_name }}' + type: 'stateful' + ip_variables: '{{ ip_variable_one_list }}' + register: stateful_group + + - assert: + that: + - stateful_group is not changed + - '"rule_group" in stateful_group' + - '"rule_group" in stateful_group.rule_group' + - '"rule_group_metadata" in stateful_group.rule_group' + - '"capacity" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_name" in stateful_group.rule_group.rule_group_metadata' + - '"type" in stateful_group.rule_group.rule_group_metadata' + - stateful_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - stateful_group.rule_group.rule_group_metadata.capacity == 100 + - stateful_group.rule_group.rule_group_metadata.rule_group_name == stateful_group_name + - stateful_group.rule_group.rule_group_metadata.rule_group_arn == stateful_rule_group_arn + - stateful_group.rule_group.rule_group_metadata.rule_group_id == stateful_rule_group_id + - '"rule_variables" in stateful_group.rule_group.rule_group' + - '"ip_sets" in stateful_group.rule_group.rule_group.rule_variables' + - stateful_group.rule_group.rule_group.rule_variables.ip_sets == ip_variable_one_list + + ##### + + - name: '(CHECK) Replace IP Variable' + networkfirewall_rule_group: + name: '{{ stateful_group_name }}' + type: 'stateful' + ip_variables: '{{ ip_variable_two }}' + purge_ip_variables: true + register: stateful_group + check_mode: true + + - assert: + that: + - stateful_group is changed + - '"rule_group" in stateful_group' + - '"rule_group" in stateful_group.rule_group' + - '"rule_group_metadata" in stateful_group.rule_group' + - '"capacity" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_name" in stateful_group.rule_group.rule_group_metadata' + - '"type" in stateful_group.rule_group.rule_group_metadata' + - stateful_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - stateful_group.rule_group.rule_group_metadata.capacity == 100 + - stateful_group.rule_group.rule_group_metadata.rule_group_name == stateful_group_name + - stateful_group.rule_group.rule_group_metadata.rule_group_arn == stateful_rule_group_arn + - stateful_group.rule_group.rule_group_metadata.rule_group_id == stateful_rule_group_id + - '"rule_variables" in stateful_group.rule_group.rule_group' + - '"ip_sets" in stateful_group.rule_group.rule_group.rule_variables' + - stateful_group.rule_group.rule_group.rule_variables.ip_sets == ip_variable_two + + - name: 'Replace IP Variable' + networkfirewall_rule_group: + name: '{{ stateful_group_name }}' + type: 'stateful' + ip_variables: '{{ ip_variable_two }}' + purge_ip_variables: true + register: stateful_group + + - assert: + that: + - stateful_group is changed + - '"rule_group" in stateful_group' + - '"rule_group" in stateful_group.rule_group' + - '"rule_group_metadata" in stateful_group.rule_group' + - '"capacity" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_arn" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_id" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_name" in stateful_group.rule_group.rule_group_metadata' + - '"type" in stateful_group.rule_group.rule_group_metadata' + - stateful_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - stateful_group.rule_group.rule_group_metadata.capacity == 100 + - stateful_group.rule_group.rule_group_metadata.rule_group_name == stateful_group_name + - stateful_group.rule_group.rule_group_metadata.rule_group_arn == stateful_rule_group_arn + - stateful_group.rule_group.rule_group_metadata.rule_group_id == stateful_rule_group_id + - '"rule_variables" in stateful_group.rule_group.rule_group' + - '"ip_sets" in stateful_group.rule_group.rule_group.rule_variables' + - stateful_group.rule_group.rule_group.rule_variables.ip_sets == ip_variable_two + + - name: '(CHECK) Replace IP Variable (idempotency)' + networkfirewall_rule_group: + name: '{{ stateful_group_name }}' + type: 'stateful' + ip_variables: '{{ ip_variable_two }}' + purge_ip_variables: true + register: stateful_group + check_mode: true + + - assert: + that: + - stateful_group is not changed + - '"rule_group" in stateful_group' + - '"rule_group" in stateful_group.rule_group' + - '"rule_group_metadata" in stateful_group.rule_group' + - '"capacity" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_name" in stateful_group.rule_group.rule_group_metadata' + - '"type" in stateful_group.rule_group.rule_group_metadata' + - stateful_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - stateful_group.rule_group.rule_group_metadata.capacity == 100 + - stateful_group.rule_group.rule_group_metadata.rule_group_name == stateful_group_name + - stateful_group.rule_group.rule_group_metadata.rule_group_arn == stateful_rule_group_arn + - stateful_group.rule_group.rule_group_metadata.rule_group_id == stateful_rule_group_id + - '"rule_variables" in stateful_group.rule_group.rule_group' + - '"ip_sets" in stateful_group.rule_group.rule_group.rule_variables' + - stateful_group.rule_group.rule_group.rule_variables.ip_sets == ip_variable_two + + - name: 'Replace IP Variable (idempotency)' + networkfirewall_rule_group: + name: '{{ stateful_group_name }}' + type: 'stateful' + ip_variables: '{{ ip_variable_two }}' + purge_ip_variables: true + register: stateful_group + + - assert: + that: + - stateful_group is not changed + - '"rule_group" in stateful_group' + - '"rule_group" in stateful_group.rule_group' + - '"rule_group_metadata" in stateful_group.rule_group' + - '"capacity" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_name" in stateful_group.rule_group.rule_group_metadata' + - '"type" in stateful_group.rule_group.rule_group_metadata' + - stateful_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - stateful_group.rule_group.rule_group_metadata.capacity == 100 + - stateful_group.rule_group.rule_group_metadata.rule_group_name == stateful_group_name + - stateful_group.rule_group.rule_group_metadata.rule_group_arn == stateful_rule_group_arn + - stateful_group.rule_group.rule_group_metadata.rule_group_id == stateful_rule_group_id + - '"rule_variables" in stateful_group.rule_group.rule_group' + - '"ip_sets" in stateful_group.rule_group.rule_group.rule_variables' + - stateful_group.rule_group.rule_group.rule_variables.ip_sets == ip_variable_two + + ##### + + - name: '(CHECK) Add extra IP Variable' + networkfirewall_rule_group: + name: '{{ stateful_group_name }}' + type: 'stateful' + ip_variables: '{{ ip_variable_one_list }}' + purge_ip_variables: false + register: stateful_group + check_mode: true + + - assert: + that: + - stateful_group is changed + - '"rule_group" in stateful_group' + - '"rule_group" in stateful_group.rule_group' + - '"rule_group_metadata" in stateful_group.rule_group' + - '"capacity" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_name" in stateful_group.rule_group.rule_group_metadata' + - '"type" in stateful_group.rule_group.rule_group_metadata' + - stateful_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - stateful_group.rule_group.rule_group_metadata.capacity == 100 + - stateful_group.rule_group.rule_group_metadata.rule_group_name == stateful_group_name + - stateful_group.rule_group.rule_group_metadata.rule_group_arn == stateful_rule_group_arn + - stateful_group.rule_group.rule_group_metadata.rule_group_id == stateful_rule_group_id + - '"rule_variables" in stateful_group.rule_group.rule_group' + - '"ip_sets" in stateful_group.rule_group.rule_group.rule_variables' + - stateful_group.rule_group.rule_group.rule_variables.ip_sets == ip_variable_both + + - name: 'Add extra IP Variable' + networkfirewall_rule_group: + name: '{{ stateful_group_name }}' + type: 'stateful' + ip_variables: '{{ ip_variable_one_list }}' + purge_ip_variables: false + register: stateful_group + + - assert: + that: + - stateful_group is changed + - '"rule_group" in stateful_group' + - '"rule_group" in stateful_group.rule_group' + - '"rule_group_metadata" in stateful_group.rule_group' + - '"capacity" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_arn" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_id" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_name" in stateful_group.rule_group.rule_group_metadata' + - '"type" in stateful_group.rule_group.rule_group_metadata' + - stateful_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - stateful_group.rule_group.rule_group_metadata.capacity == 100 + - stateful_group.rule_group.rule_group_metadata.rule_group_name == stateful_group_name + - stateful_group.rule_group.rule_group_metadata.rule_group_arn == stateful_rule_group_arn + - stateful_group.rule_group.rule_group_metadata.rule_group_id == stateful_rule_group_id + - '"rule_variables" in stateful_group.rule_group.rule_group' + - '"ip_sets" in stateful_group.rule_group.rule_group.rule_variables' + - stateful_group.rule_group.rule_group.rule_variables.ip_sets == ip_variable_both + + - name: '(CHECK) Add extra IP Variable (idempotency)' + networkfirewall_rule_group: + name: '{{ stateful_group_name }}' + type: 'stateful' + ip_variables: '{{ ip_variable_one_list }}' + purge_ip_variables: false + register: stateful_group + check_mode: true + + - assert: + that: + - stateful_group is not changed + - '"rule_group" in stateful_group' + - '"rule_group" in stateful_group.rule_group' + - '"rule_group_metadata" in stateful_group.rule_group' + - '"capacity" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_name" in stateful_group.rule_group.rule_group_metadata' + - '"type" in stateful_group.rule_group.rule_group_metadata' + - stateful_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - stateful_group.rule_group.rule_group_metadata.capacity == 100 + - stateful_group.rule_group.rule_group_metadata.rule_group_name == stateful_group_name + - stateful_group.rule_group.rule_group_metadata.rule_group_arn == stateful_rule_group_arn + - stateful_group.rule_group.rule_group_metadata.rule_group_id == stateful_rule_group_id + - '"rule_variables" in stateful_group.rule_group.rule_group' + - '"ip_sets" in stateful_group.rule_group.rule_group.rule_variables' + - stateful_group.rule_group.rule_group.rule_variables.ip_sets == ip_variable_both + + - name: 'Add extra IP Variable (idempotency)' + networkfirewall_rule_group: + name: '{{ stateful_group_name }}' + type: 'stateful' + ip_variables: '{{ ip_variable_one_list }}' + purge_ip_variables: false + register: stateful_group + + - assert: + that: + - stateful_group is not changed + - '"rule_group" in stateful_group' + - '"rule_group" in stateful_group.rule_group' + - '"rule_group_metadata" in stateful_group.rule_group' + - '"capacity" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_name" in stateful_group.rule_group.rule_group_metadata' + - '"type" in stateful_group.rule_group.rule_group_metadata' + - stateful_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - stateful_group.rule_group.rule_group_metadata.capacity == 100 + - stateful_group.rule_group.rule_group_metadata.rule_group_name == stateful_group_name + - stateful_group.rule_group.rule_group_metadata.rule_group_arn == stateful_rule_group_arn + - stateful_group.rule_group.rule_group_metadata.rule_group_id == stateful_rule_group_id + - '"rule_variables" in stateful_group.rule_group.rule_group' + - '"ip_sets" in stateful_group.rule_group.rule_group.rule_variables' + - stateful_group.rule_group.rule_group.rule_variables.ip_sets == ip_variable_both + + ################################################################### + # Update Port Variables + + - name: '(CHECK) Add IP Variable' + networkfirewall_rule_group: + name: '{{ stateful_group_name }}' + type: 'stateful' + port_variables: '{{ port_variable_one }}' + register: stateful_group + check_mode: true + + - assert: + that: + - stateful_group is changed + - '"rule_group" in stateful_group' + - '"rule_group" in stateful_group.rule_group' + - '"rule_group_metadata" in stateful_group.rule_group' + - '"capacity" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_name" in stateful_group.rule_group.rule_group_metadata' + - '"type" in stateful_group.rule_group.rule_group_metadata' + - stateful_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - stateful_group.rule_group.rule_group_metadata.capacity == 100 + - stateful_group.rule_group.rule_group_metadata.rule_group_name == stateful_group_name + - stateful_group.rule_group.rule_group_metadata.rule_group_arn == stateful_rule_group_arn + - stateful_group.rule_group.rule_group_metadata.rule_group_id == stateful_rule_group_id + - '"rule_variables" in stateful_group.rule_group.rule_group' + - '"port_sets" in stateful_group.rule_group.rule_group.rule_variables' + - '"ip_sets" in stateful_group.rule_group.rule_group.rule_variables' + - stateful_group.rule_group.rule_group.rule_variables.ip_sets == ip_variable_both + - stateful_group.rule_group.rule_group.rule_variables.port_sets == port_variable_one_list + + - name: 'Add IP Variable' + networkfirewall_rule_group: + name: '{{ stateful_group_name }}' + type: 'stateful' + port_variables: '{{ port_variable_one }}' + register: stateful_group + + - assert: + that: + - stateful_group is changed + - '"rule_group" in stateful_group' + - '"rule_group" in stateful_group.rule_group' + - '"rule_group_metadata" in stateful_group.rule_group' + - '"capacity" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_arn" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_id" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_name" in stateful_group.rule_group.rule_group_metadata' + - '"type" in stateful_group.rule_group.rule_group_metadata' + - stateful_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - stateful_group.rule_group.rule_group_metadata.capacity == 100 + - stateful_group.rule_group.rule_group_metadata.rule_group_name == stateful_group_name + - stateful_group.rule_group.rule_group_metadata.rule_group_arn == stateful_rule_group_arn + - stateful_group.rule_group.rule_group_metadata.rule_group_id == stateful_rule_group_id + - '"rule_variables" in stateful_group.rule_group.rule_group' + - '"port_sets" in stateful_group.rule_group.rule_group.rule_variables' + - '"ip_sets" in stateful_group.rule_group.rule_group.rule_variables' + - stateful_group.rule_group.rule_group.rule_variables.ip_sets == ip_variable_both + - stateful_group.rule_group.rule_group.rule_variables.port_sets == port_variable_one_list + + - name: '(CHECK) Add IP Variable (idempotency)' + networkfirewall_rule_group: + name: '{{ stateful_group_name }}' + type: 'stateful' + port_variables: '{{ port_variable_one }}' + register: stateful_group + check_mode: true + + - assert: + that: + - stateful_group is not changed + - '"rule_group" in stateful_group' + - '"rule_group" in stateful_group.rule_group' + - '"rule_group_metadata" in stateful_group.rule_group' + - '"capacity" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_name" in stateful_group.rule_group.rule_group_metadata' + - '"type" in stateful_group.rule_group.rule_group_metadata' + - stateful_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - stateful_group.rule_group.rule_group_metadata.capacity == 100 + - stateful_group.rule_group.rule_group_metadata.rule_group_name == stateful_group_name + - stateful_group.rule_group.rule_group_metadata.rule_group_arn == stateful_rule_group_arn + - stateful_group.rule_group.rule_group_metadata.rule_group_id == stateful_rule_group_id + - '"rule_variables" in stateful_group.rule_group.rule_group' + - '"port_sets" in stateful_group.rule_group.rule_group.rule_variables' + - '"ip_sets" in stateful_group.rule_group.rule_group.rule_variables' + - stateful_group.rule_group.rule_group.rule_variables.ip_sets == ip_variable_both + - stateful_group.rule_group.rule_group.rule_variables.port_sets == port_variable_one_list + + - name: 'Add IP Variable (idempotency)' + networkfirewall_rule_group: + name: '{{ stateful_group_name }}' + type: 'stateful' + port_variables: '{{ port_variable_one }}' + register: stateful_group + + - assert: + that: + - stateful_group is not changed + - '"rule_group" in stateful_group' + - '"rule_group" in stateful_group.rule_group' + - '"rule_group_metadata" in stateful_group.rule_group' + - '"capacity" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_name" in stateful_group.rule_group.rule_group_metadata' + - '"type" in stateful_group.rule_group.rule_group_metadata' + - stateful_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - stateful_group.rule_group.rule_group_metadata.capacity == 100 + - stateful_group.rule_group.rule_group_metadata.rule_group_name == stateful_group_name + - stateful_group.rule_group.rule_group_metadata.rule_group_arn == stateful_rule_group_arn + - stateful_group.rule_group.rule_group_metadata.rule_group_id == stateful_rule_group_id + - '"rule_variables" in stateful_group.rule_group.rule_group' + - '"port_sets" in stateful_group.rule_group.rule_group.rule_variables' + - '"ip_sets" in stateful_group.rule_group.rule_group.rule_variables' + - stateful_group.rule_group.rule_group.rule_variables.ip_sets == ip_variable_both + - stateful_group.rule_group.rule_group.rule_variables.port_sets == port_variable_one_list + + ##### + + - name: '(CHECK) Ensure IP Variable string/list equivalence (idempotency)' + networkfirewall_rule_group: + name: '{{ stateful_group_name }}' + type: 'stateful' + port_variables: '{{ port_variable_one_list }}' + register: stateful_group + check_mode: true + + - assert: + that: + - stateful_group is not changed + - '"rule_group" in stateful_group' + - '"rule_group" in stateful_group.rule_group' + - '"rule_group_metadata" in stateful_group.rule_group' + - '"capacity" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_name" in stateful_group.rule_group.rule_group_metadata' + - '"type" in stateful_group.rule_group.rule_group_metadata' + - stateful_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - stateful_group.rule_group.rule_group_metadata.capacity == 100 + - stateful_group.rule_group.rule_group_metadata.rule_group_name == stateful_group_name + - stateful_group.rule_group.rule_group_metadata.rule_group_arn == stateful_rule_group_arn + - stateful_group.rule_group.rule_group_metadata.rule_group_id == stateful_rule_group_id + - '"rule_variables" in stateful_group.rule_group.rule_group' + - '"port_sets" in stateful_group.rule_group.rule_group.rule_variables' + - '"ip_sets" in stateful_group.rule_group.rule_group.rule_variables' + - stateful_group.rule_group.rule_group.rule_variables.ip_sets == ip_variable_both + - stateful_group.rule_group.rule_group.rule_variables.port_sets == port_variable_one_list + + - name: 'Ensure IP Variable string/list equivalence (idempotency)' + networkfirewall_rule_group: + name: '{{ stateful_group_name }}' + type: 'stateful' + port_variables: '{{ port_variable_one_list }}' + register: stateful_group + + - assert: + that: + - stateful_group is not changed + - '"rule_group" in stateful_group' + - '"rule_group" in stateful_group.rule_group' + - '"rule_group_metadata" in stateful_group.rule_group' + - '"capacity" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_name" in stateful_group.rule_group.rule_group_metadata' + - '"type" in stateful_group.rule_group.rule_group_metadata' + - stateful_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - stateful_group.rule_group.rule_group_metadata.capacity == 100 + - stateful_group.rule_group.rule_group_metadata.rule_group_name == stateful_group_name + - stateful_group.rule_group.rule_group_metadata.rule_group_arn == stateful_rule_group_arn + - stateful_group.rule_group.rule_group_metadata.rule_group_id == stateful_rule_group_id + - '"rule_variables" in stateful_group.rule_group.rule_group' + - '"port_sets" in stateful_group.rule_group.rule_group.rule_variables' + - '"ip_sets" in stateful_group.rule_group.rule_group.rule_variables' + - stateful_group.rule_group.rule_group.rule_variables.ip_sets == ip_variable_both + - stateful_group.rule_group.rule_group.rule_variables.port_sets == port_variable_one_list + + ##### + + - name: '(CHECK) Replace IP Variable' + networkfirewall_rule_group: + name: '{{ stateful_group_name }}' + type: 'stateful' + port_variables: '{{ port_variable_two }}' + purge_port_variables: true + register: stateful_group + check_mode: true + + - assert: + that: + - stateful_group is changed + - '"rule_group" in stateful_group' + - '"rule_group" in stateful_group.rule_group' + - '"rule_group_metadata" in stateful_group.rule_group' + - '"capacity" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_name" in stateful_group.rule_group.rule_group_metadata' + - '"type" in stateful_group.rule_group.rule_group_metadata' + - stateful_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - stateful_group.rule_group.rule_group_metadata.capacity == 100 + - stateful_group.rule_group.rule_group_metadata.rule_group_name == stateful_group_name + - stateful_group.rule_group.rule_group_metadata.rule_group_arn == stateful_rule_group_arn + - stateful_group.rule_group.rule_group_metadata.rule_group_id == stateful_rule_group_id + - '"rule_variables" in stateful_group.rule_group.rule_group' + - '"port_sets" in stateful_group.rule_group.rule_group.rule_variables' + - '"ip_sets" in stateful_group.rule_group.rule_group.rule_variables' + - stateful_group.rule_group.rule_group.rule_variables.ip_sets == ip_variable_both + - stateful_group.rule_group.rule_group.rule_variables.port_sets == port_variable_two + + - name: 'Replace IP Variable' + networkfirewall_rule_group: + name: '{{ stateful_group_name }}' + type: 'stateful' + port_variables: '{{ port_variable_two }}' + purge_port_variables: true + register: stateful_group + + - assert: + that: + - stateful_group is changed + - '"rule_group" in stateful_group' + - '"rule_group" in stateful_group.rule_group' + - '"rule_group_metadata" in stateful_group.rule_group' + - '"capacity" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_arn" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_id" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_name" in stateful_group.rule_group.rule_group_metadata' + - '"type" in stateful_group.rule_group.rule_group_metadata' + - stateful_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - stateful_group.rule_group.rule_group_metadata.capacity == 100 + - stateful_group.rule_group.rule_group_metadata.rule_group_name == stateful_group_name + - stateful_group.rule_group.rule_group_metadata.rule_group_arn == stateful_rule_group_arn + - stateful_group.rule_group.rule_group_metadata.rule_group_id == stateful_rule_group_id + - '"rule_variables" in stateful_group.rule_group.rule_group' + - '"port_sets" in stateful_group.rule_group.rule_group.rule_variables' + - '"ip_sets" in stateful_group.rule_group.rule_group.rule_variables' + - stateful_group.rule_group.rule_group.rule_variables.ip_sets == ip_variable_both + - stateful_group.rule_group.rule_group.rule_variables.port_sets == port_variable_two + + - name: '(CHECK) Replace IP Variable (idempotency)' + networkfirewall_rule_group: + name: '{{ stateful_group_name }}' + type: 'stateful' + port_variables: '{{ port_variable_two }}' + purge_port_variables: true + register: stateful_group + check_mode: true + + - assert: + that: + - stateful_group is not changed + - '"rule_group" in stateful_group' + - '"rule_group" in stateful_group.rule_group' + - '"rule_group_metadata" in stateful_group.rule_group' + - '"capacity" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_name" in stateful_group.rule_group.rule_group_metadata' + - '"type" in stateful_group.rule_group.rule_group_metadata' + - stateful_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - stateful_group.rule_group.rule_group_metadata.capacity == 100 + - stateful_group.rule_group.rule_group_metadata.rule_group_name == stateful_group_name + - stateful_group.rule_group.rule_group_metadata.rule_group_arn == stateful_rule_group_arn + - stateful_group.rule_group.rule_group_metadata.rule_group_id == stateful_rule_group_id + - '"rule_variables" in stateful_group.rule_group.rule_group' + - '"port_sets" in stateful_group.rule_group.rule_group.rule_variables' + - '"ip_sets" in stateful_group.rule_group.rule_group.rule_variables' + - stateful_group.rule_group.rule_group.rule_variables.ip_sets == ip_variable_both + - stateful_group.rule_group.rule_group.rule_variables.port_sets == port_variable_two + + - name: 'Replace IP Variable (idempotency)' + networkfirewall_rule_group: + name: '{{ stateful_group_name }}' + type: 'stateful' + port_variables: '{{ port_variable_two }}' + purge_port_variables: true + register: stateful_group + + - assert: + that: + - stateful_group is not changed + - '"rule_group" in stateful_group' + - '"rule_group" in stateful_group.rule_group' + - '"rule_group_metadata" in stateful_group.rule_group' + - '"capacity" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_name" in stateful_group.rule_group.rule_group_metadata' + - '"type" in stateful_group.rule_group.rule_group_metadata' + - stateful_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - stateful_group.rule_group.rule_group_metadata.capacity == 100 + - stateful_group.rule_group.rule_group_metadata.rule_group_name == stateful_group_name + - stateful_group.rule_group.rule_group_metadata.rule_group_arn == stateful_rule_group_arn + - stateful_group.rule_group.rule_group_metadata.rule_group_id == stateful_rule_group_id + - '"rule_variables" in stateful_group.rule_group.rule_group' + - '"port_sets" in stateful_group.rule_group.rule_group.rule_variables' + - '"ip_sets" in stateful_group.rule_group.rule_group.rule_variables' + - stateful_group.rule_group.rule_group.rule_variables.ip_sets == ip_variable_both + - stateful_group.rule_group.rule_group.rule_variables.port_sets == port_variable_two + + ##### + + - name: '(CHECK) Add extra IP Variable' + networkfirewall_rule_group: + name: '{{ stateful_group_name }}' + type: 'stateful' + port_variables: '{{ port_variable_one_list }}' + purge_port_variables: false + register: stateful_group + check_mode: true + + - assert: + that: + - stateful_group is changed + - '"rule_group" in stateful_group' + - '"rule_group" in stateful_group.rule_group' + - '"rule_group_metadata" in stateful_group.rule_group' + - '"capacity" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_name" in stateful_group.rule_group.rule_group_metadata' + - '"type" in stateful_group.rule_group.rule_group_metadata' + - stateful_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - stateful_group.rule_group.rule_group_metadata.capacity == 100 + - stateful_group.rule_group.rule_group_metadata.rule_group_name == stateful_group_name + - stateful_group.rule_group.rule_group_metadata.rule_group_arn == stateful_rule_group_arn + - stateful_group.rule_group.rule_group_metadata.rule_group_id == stateful_rule_group_id + - '"rule_variables" in stateful_group.rule_group.rule_group' + - '"port_sets" in stateful_group.rule_group.rule_group.rule_variables' + - '"ip_sets" in stateful_group.rule_group.rule_group.rule_variables' + - stateful_group.rule_group.rule_group.rule_variables.ip_sets == ip_variable_both + - stateful_group.rule_group.rule_group.rule_variables.port_sets == port_variable_both + + - name: 'Add extra IP Variable' + networkfirewall_rule_group: + name: '{{ stateful_group_name }}' + type: 'stateful' + port_variables: '{{ port_variable_one_list }}' + purge_port_variables: false + register: stateful_group + + - assert: + that: + - stateful_group is changed + - '"rule_group" in stateful_group' + - '"rule_group" in stateful_group.rule_group' + - '"rule_group_metadata" in stateful_group.rule_group' + - '"capacity" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_arn" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_id" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_name" in stateful_group.rule_group.rule_group_metadata' + - '"type" in stateful_group.rule_group.rule_group_metadata' + - stateful_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - stateful_group.rule_group.rule_group_metadata.capacity == 100 + - stateful_group.rule_group.rule_group_metadata.rule_group_name == stateful_group_name + - stateful_group.rule_group.rule_group_metadata.rule_group_arn == stateful_rule_group_arn + - stateful_group.rule_group.rule_group_metadata.rule_group_id == stateful_rule_group_id + - '"rule_variables" in stateful_group.rule_group.rule_group' + - '"port_sets" in stateful_group.rule_group.rule_group.rule_variables' + - '"ip_sets" in stateful_group.rule_group.rule_group.rule_variables' + - stateful_group.rule_group.rule_group.rule_variables.ip_sets == ip_variable_both + - stateful_group.rule_group.rule_group.rule_variables.port_sets == port_variable_both + + - name: '(CHECK) Add extra IP Variable (idempotency)' + networkfirewall_rule_group: + name: '{{ stateful_group_name }}' + type: 'stateful' + port_variables: '{{ port_variable_one_list }}' + purge_port_variables: false + register: stateful_group + check_mode: true + + - assert: + that: + - stateful_group is not changed + - '"rule_group" in stateful_group' + - '"rule_group" in stateful_group.rule_group' + - '"rule_group_metadata" in stateful_group.rule_group' + - '"capacity" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_name" in stateful_group.rule_group.rule_group_metadata' + - '"type" in stateful_group.rule_group.rule_group_metadata' + - stateful_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - stateful_group.rule_group.rule_group_metadata.capacity == 100 + - stateful_group.rule_group.rule_group_metadata.rule_group_name == stateful_group_name + - stateful_group.rule_group.rule_group_metadata.rule_group_arn == stateful_rule_group_arn + - stateful_group.rule_group.rule_group_metadata.rule_group_id == stateful_rule_group_id + - '"rule_variables" in stateful_group.rule_group.rule_group' + - '"port_sets" in stateful_group.rule_group.rule_group.rule_variables' + - '"ip_sets" in stateful_group.rule_group.rule_group.rule_variables' + - stateful_group.rule_group.rule_group.rule_variables.ip_sets == ip_variable_both + - stateful_group.rule_group.rule_group.rule_variables.port_sets == port_variable_both + + - name: 'Add extra IP Variable (idempotency)' + networkfirewall_rule_group: + name: '{{ stateful_group_name }}' + type: 'stateful' + port_variables: '{{ port_variable_one_list }}' + purge_port_variables: false + register: stateful_group + + - assert: + that: + - stateful_group is not changed + - '"rule_group" in stateful_group' + - '"rule_group" in stateful_group.rule_group' + - '"rule_group_metadata" in stateful_group.rule_group' + - '"capacity" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_name" in stateful_group.rule_group.rule_group_metadata' + - '"type" in stateful_group.rule_group.rule_group_metadata' + - stateful_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - stateful_group.rule_group.rule_group_metadata.capacity == 100 + - stateful_group.rule_group.rule_group_metadata.rule_group_name == stateful_group_name + - stateful_group.rule_group.rule_group_metadata.rule_group_arn == stateful_rule_group_arn + - stateful_group.rule_group.rule_group_metadata.rule_group_id == stateful_rule_group_id + - '"rule_variables" in stateful_group.rule_group.rule_group' + - '"port_sets" in stateful_group.rule_group.rule_group.rule_variables' + - '"ip_sets" in stateful_group.rule_group.rule_group.rule_variables' + - stateful_group.rule_group.rule_group.rule_variables.ip_sets == ip_variable_both + - stateful_group.rule_group.rule_group.rule_variables.port_sets == port_variable_both + + ##### + + - name: '(CHECK) Remove Port Variable' + networkfirewall_rule_group: + name: '{{ stateful_group_name }}' + type: 'stateful' + port_variables: {} + register: stateful_group + check_mode: true + + - assert: + that: + - stateful_group is changed + - '"rule_group" in stateful_group' + - '"rule_group" in stateful_group.rule_group' + - '"rule_group_metadata" in stateful_group.rule_group' + - '"capacity" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_name" in stateful_group.rule_group.rule_group_metadata' + - '"type" in stateful_group.rule_group.rule_group_metadata' + - stateful_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - stateful_group.rule_group.rule_group_metadata.capacity == 100 + - stateful_group.rule_group.rule_group_metadata.rule_group_name == stateful_group_name + - stateful_group.rule_group.rule_group_metadata.rule_group_arn == stateful_rule_group_arn + - stateful_group.rule_group.rule_group_metadata.rule_group_id == stateful_rule_group_id + - '"rule_variables" in stateful_group.rule_group.rule_group' + - '"port_sets" not in stateful_group.rule_group.rule_group.rule_variables' + - '"ip_sets" in stateful_group.rule_group.rule_group.rule_variables' + - stateful_group.rule_group.rule_group.rule_variables.ip_sets == ip_variable_both + + - name: 'Remove Port Variable' + networkfirewall_rule_group: + name: '{{ stateful_group_name }}' + type: 'stateful' + port_variables: {} + register: stateful_group + + - assert: + that: + - stateful_group is changed + - '"rule_group" in stateful_group' + - '"rule_group" in stateful_group.rule_group' + - '"rule_group_metadata" in stateful_group.rule_group' + - '"capacity" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_arn" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_id" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_name" in stateful_group.rule_group.rule_group_metadata' + - '"type" in stateful_group.rule_group.rule_group_metadata' + - stateful_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - stateful_group.rule_group.rule_group_metadata.capacity == 100 + - stateful_group.rule_group.rule_group_metadata.rule_group_name == stateful_group_name + - stateful_group.rule_group.rule_group_metadata.rule_group_arn == stateful_rule_group_arn + - stateful_group.rule_group.rule_group_metadata.rule_group_id == stateful_rule_group_id + - '"rule_variables" in stateful_group.rule_group.rule_group' + - '"port_sets" not in stateful_group.rule_group.rule_group.rule_variables' + - '"ip_sets" in stateful_group.rule_group.rule_group.rule_variables' + - stateful_group.rule_group.rule_group.rule_variables.ip_sets == ip_variable_both + + - name: '(CHECK) Remove Port Variable (idempotency)' + networkfirewall_rule_group: + name: '{{ stateful_group_name }}' + type: 'stateful' + port_variables: {} + register: stateful_group + check_mode: true + + - assert: + that: + - stateful_group is not changed + - '"rule_group" in stateful_group' + - '"rule_group" in stateful_group.rule_group' + - '"rule_group_metadata" in stateful_group.rule_group' + - '"capacity" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_name" in stateful_group.rule_group.rule_group_metadata' + - '"type" in stateful_group.rule_group.rule_group_metadata' + - stateful_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - stateful_group.rule_group.rule_group_metadata.capacity == 100 + - stateful_group.rule_group.rule_group_metadata.rule_group_name == stateful_group_name + - stateful_group.rule_group.rule_group_metadata.rule_group_arn == stateful_rule_group_arn + - stateful_group.rule_group.rule_group_metadata.rule_group_id == stateful_rule_group_id + - '"rule_variables" in stateful_group.rule_group.rule_group' + - '"port_sets" not in stateful_group.rule_group.rule_group.rule_variables' + - '"ip_sets" in stateful_group.rule_group.rule_group.rule_variables' + - stateful_group.rule_group.rule_group.rule_variables.ip_sets == ip_variable_both + + - name: 'Remove Port Variable (idempotency)' + networkfirewall_rule_group: + name: '{{ stateful_group_name }}' + type: 'stateful' + port_variables: {} + register: stateful_group + + - assert: + that: + - stateful_group is not changed + - '"rule_group" in stateful_group' + - '"rule_group" in stateful_group.rule_group' + - '"rule_group_metadata" in stateful_group.rule_group' + - '"capacity" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_name" in stateful_group.rule_group.rule_group_metadata' + - '"type" in stateful_group.rule_group.rule_group_metadata' + - stateful_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - stateful_group.rule_group.rule_group_metadata.capacity == 100 + - stateful_group.rule_group.rule_group_metadata.rule_group_name == stateful_group_name + - stateful_group.rule_group.rule_group_metadata.rule_group_arn == stateful_rule_group_arn + - stateful_group.rule_group.rule_group_metadata.rule_group_id == stateful_rule_group_id + - '"rule_variables" in stateful_group.rule_group.rule_group' + - '"port_sets" not in stateful_group.rule_group.rule_group.rule_variables' + - '"ip_sets" in stateful_group.rule_group.rule_group.rule_variables' + - stateful_group.rule_group.rule_group.rule_variables.ip_sets == ip_variable_both + + ##### + + - name: '(CHECK) Remove IP Variable' + networkfirewall_rule_group: + name: '{{ stateful_group_name }}' + type: 'stateful' + ip_variables: {} + register: stateful_group + check_mode: true + + - assert: + that: + - stateful_group is changed + - '"rule_group" in stateful_group' + - '"rule_group" in stateful_group.rule_group' + - '"rule_group_metadata" in stateful_group.rule_group' + - '"capacity" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_name" in stateful_group.rule_group.rule_group_metadata' + - '"type" in stateful_group.rule_group.rule_group_metadata' + - stateful_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - stateful_group.rule_group.rule_group_metadata.capacity == 100 + - stateful_group.rule_group.rule_group_metadata.rule_group_name == stateful_group_name + - stateful_group.rule_group.rule_group_metadata.rule_group_arn == stateful_rule_group_arn + - stateful_group.rule_group.rule_group_metadata.rule_group_id == stateful_rule_group_id + - '"rule_variables" in stateful_group.rule_group.rule_group' + - '"port_sets" not in stateful_group.rule_group.rule_group.rule_variables' + - '"ip_sets" not in stateful_group.rule_group.rule_group.rule_variables' + + - name: 'Remove IP Variable' + networkfirewall_rule_group: + name: '{{ stateful_group_name }}' + type: 'stateful' + ip_variables: {} + register: stateful_group + + - assert: + that: + - stateful_group is changed + - '"rule_group" in stateful_group' + - '"rule_group" in stateful_group.rule_group' + - '"rule_group_metadata" in stateful_group.rule_group' + - '"capacity" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_arn" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_id" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_name" in stateful_group.rule_group.rule_group_metadata' + - '"type" in stateful_group.rule_group.rule_group_metadata' + - stateful_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - stateful_group.rule_group.rule_group_metadata.capacity == 100 + - stateful_group.rule_group.rule_group_metadata.rule_group_name == stateful_group_name + - stateful_group.rule_group.rule_group_metadata.rule_group_arn == stateful_rule_group_arn + - stateful_group.rule_group.rule_group_metadata.rule_group_id == stateful_rule_group_id + - '"rule_variables" in stateful_group.rule_group.rule_group' + - '"port_sets" not in stateful_group.rule_group.rule_group.rule_variables' + - '"ip_sets" not in stateful_group.rule_group.rule_group.rule_variables' + + - name: '(CHECK) Remove IP Variable (idempotency)' + networkfirewall_rule_group: + name: '{{ stateful_group_name }}' + type: 'stateful' + ip_variables: {} + register: stateful_group + check_mode: true + + - assert: + that: + - stateful_group is not changed + - '"rule_group" in stateful_group' + - '"rule_group" in stateful_group.rule_group' + - '"rule_group_metadata" in stateful_group.rule_group' + - '"capacity" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_name" in stateful_group.rule_group.rule_group_metadata' + - '"type" in stateful_group.rule_group.rule_group_metadata' + - stateful_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - stateful_group.rule_group.rule_group_metadata.capacity == 100 + - stateful_group.rule_group.rule_group_metadata.rule_group_name == stateful_group_name + - stateful_group.rule_group.rule_group_metadata.rule_group_arn == stateful_rule_group_arn + - stateful_group.rule_group.rule_group_metadata.rule_group_id == stateful_rule_group_id + - '"rule_variables" in stateful_group.rule_group.rule_group' + - '"port_sets" not in stateful_group.rule_group.rule_group.rule_variables' + - '"ip_sets" not in stateful_group.rule_group.rule_group.rule_variables' + + - name: 'Remove IP Variable (idempotency)' + networkfirewall_rule_group: + name: '{{ stateful_group_name }}' + type: 'stateful' + ip_variables: {} + register: stateful_group + + - assert: + that: + - stateful_group is not changed + - '"rule_group" in stateful_group' + - '"rule_group" in stateful_group.rule_group' + - '"rule_group_metadata" in stateful_group.rule_group' + - '"capacity" in stateful_group.rule_group.rule_group_metadata' + - '"rule_group_name" in stateful_group.rule_group.rule_group_metadata' + - '"type" in stateful_group.rule_group.rule_group_metadata' + - stateful_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - stateful_group.rule_group.rule_group_metadata.capacity == 100 + - stateful_group.rule_group.rule_group_metadata.rule_group_name == stateful_group_name + - stateful_group.rule_group.rule_group_metadata.rule_group_arn == stateful_rule_group_arn + - stateful_group.rule_group.rule_group_metadata.rule_group_id == stateful_rule_group_id + - '"rule_variables" in stateful_group.rule_group.rule_group' + - '"port_sets" not in stateful_group.rule_group.rule_group.rule_variables' + - '"ip_sets" not in stateful_group.rule_group.rule_group.rule_variables' + + ################################################################### + # Rule Order + + - name: '(CHECK) Attempt to update the Default Rule Order' + networkfirewall_rule_group: + name: '{{ stateful_group_name }}' + type: 'stateful' + rule_order: 'strict' + register: stateful_group + ignore_errors: True + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + + - assert: + that: + - stateful_group is failed + + - name: 'Attempt to update the Default Rule Order' + networkfirewall_rule_group: + name: '{{ stateful_group_name }}' + type: 'stateful' + rule_order: 'strict' + register: stateful_group + ignore_errors: True + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + + - assert: + that: + - stateful_group is failed + + ##### + + - name: '(CHECK) Attempt to update the Default Rule Order (idempotency)' + networkfirewall_rule_group: + name: '{{ stateful_group_name }}' + type: 'stateful' + rule_order: 'default' + register: stateful_group + ignore_errors: True + + # Because the default rule order doesn't necessitate the setting of + # RuleOptions, for 'default' the existence of statefule_rule_options (and specifically rule_order) + # isn't guaranteed, so we don't explicitly test for it here, instead we rely + # on 'changed' doing the right thing. + - assert: + that: + - stateful_group is not changed + + - name: 'Attempt to update the Default Rule Order (idempotency)' + networkfirewall_rule_group: + name: '{{ stateful_group_name }}' + type: 'stateful' + rule_order: 'default' + register: stateful_group + ignore_errors: True + + - assert: + that: + - stateful_group is not changed + + ################################################################### + # Creation with 'strict' rule ordering + + - name: '(CHECK) Create a Rule Group with strict order' + networkfirewall_rule_group: + name: '{{ strict_ro_group_name }}' + type: 'stateful' + capacity: 100 + rule_strings: + - 'pass tcp any any -> any any (sid:1000001;)' + rule_order: strict + register: strict_group + check_mode: true + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + + - assert: + that: + - strict_group is changed + - '"rule_group" in strict_group' + - '"rule_group" in strict_group.rule_group' + - '"rule_group_metadata" in strict_group.rule_group' + - '"capacity" in strict_group.rule_group.rule_group_metadata' + - '"rule_group_name" in strict_group.rule_group.rule_group_metadata' + - '"type" in strict_group.rule_group.rule_group_metadata' + - strict_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - strict_group.rule_group.rule_group_metadata.capacity == 100 + - strict_group.rule_group.rule_group_metadata.rule_group_name == strict_ro_group_name + - '"stateful_rule_options" in strict_group.rule_group.rule_group' + - '"rule_order" in strict_group.rule_group.rule_group.stateful_rule_options' + - strict_group.rule_group.rule_group.stateful_rule_options.rule_order == 'STRICT_ORDER' + + - name: 'Create a Rule Group with strict order' + networkfirewall_rule_group: + name: '{{ strict_ro_group_name }}' + type: 'stateful' + capacity: 100 + rule_strings: + - 'pass tcp any any -> any any (sid:1000001;)' + rule_order: strict + register: strict_group + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + + - assert: + that: + - strict_group is changed + - '"rule_group" in strict_group' + - '"rule_group" in strict_group.rule_group' + - '"rule_group_metadata" in strict_group.rule_group' + - '"capacity" in strict_group.rule_group.rule_group_metadata' + - '"rule_group_arn" in strict_group.rule_group.rule_group_metadata' + - '"rule_group_id" in strict_group.rule_group.rule_group_metadata' + - '"rule_group_name" in strict_group.rule_group.rule_group_metadata' + - '"type" in strict_group.rule_group.rule_group_metadata' + - strict_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - strict_group.rule_group.rule_group_metadata.capacity == 100 + - strict_group.rule_group.rule_group_metadata.rule_group_name == strict_ro_group_name + - strict_group.rule_group.rule_group_metadata.rule_group_arn.startswith(account_arn) + - strict_group.rule_group.rule_group_metadata.rule_group_arn.endswith(strict_ro_group_name) + - '"stateful_rule_options" in strict_group.rule_group.rule_group' + - '"rule_order" in strict_group.rule_group.rule_group.stateful_rule_options' + - strict_group.rule_group.rule_group.stateful_rule_options.rule_order == 'STRICT_ORDER' + + - name: Save RuleGroup ID/ARN for later + set_fact: + strict_rule_group_id: '{{ strict_group.rule_group.rule_group_metadata.rule_group_id }}' + strict_rule_group_arn: '{{ strict_group.rule_group.rule_group_metadata.rule_group_arn }}' + + - name: '(CHECK) Create a Rule Group with strict order (idempotency)' + networkfirewall_rule_group: + name: '{{ strict_ro_group_name }}' + type: 'stateful' + capacity: 100 + rule_strings: + - 'pass tcp any any -> any any (sid:1000001;)' + rule_order: strict + register: strict_group + check_mode: true + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + + - assert: + that: + - strict_group is not changed + - '"rule_group" in strict_group' + - '"rule_group" in strict_group.rule_group' + - '"rule_group_metadata" in strict_group.rule_group' + - '"capacity" in strict_group.rule_group.rule_group_metadata' + - '"rule_group_name" in strict_group.rule_group.rule_group_metadata' + - '"type" in strict_group.rule_group.rule_group_metadata' + - strict_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - strict_group.rule_group.rule_group_metadata.capacity == 100 + - strict_group.rule_group.rule_group_metadata.rule_group_name == strict_ro_group_name + - strict_group.rule_group.rule_group_metadata.rule_group_arn == strict_rule_group_arn + - strict_group.rule_group.rule_group_metadata.rule_group_id == strict_rule_group_id + - '"stateful_rule_options" in strict_group.rule_group.rule_group' + - '"rule_order" in strict_group.rule_group.rule_group.stateful_rule_options' + - strict_group.rule_group.rule_group.stateful_rule_options.rule_order == 'STRICT_ORDER' + + - name: 'Create a Rule Group with strict order (idempotency)' + networkfirewall_rule_group: + name: '{{ strict_ro_group_name }}' + type: 'stateful' + capacity: 100 + rule_strings: + - 'pass tcp any any -> any any (sid:1000001;)' + rule_order: strict + register: strict_group + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + + - assert: + that: + - strict_group is not changed + - '"rule_group" in strict_group' + - '"rule_group" in strict_group.rule_group' + - '"rule_group_metadata" in strict_group.rule_group' + - '"capacity" in strict_group.rule_group.rule_group_metadata' + - '"rule_group_name" in strict_group.rule_group.rule_group_metadata' + - '"type" in strict_group.rule_group.rule_group_metadata' + - strict_group.rule_group.rule_group_metadata.type == 'STATEFUL' + - strict_group.rule_group.rule_group_metadata.capacity == 100 + - strict_group.rule_group.rule_group_metadata.rule_group_name == strict_ro_group_name + - strict_group.rule_group.rule_group_metadata.rule_group_arn == strict_rule_group_arn + - strict_group.rule_group.rule_group_metadata.rule_group_id == strict_rule_group_id + - '"stateful_rule_options" in strict_group.rule_group.rule_group' + - '"rule_order" in strict_group.rule_group.rule_group.stateful_rule_options' + - strict_group.rule_group.rule_group.stateful_rule_options.rule_order == 'STRICT_ORDER' + + ################################################################### + # Rule Order + + - name: '(CHECK) Attempt to update the Default Rule Order from strict' + networkfirewall_rule_group: + name: '{{ strict_ro_group_name }}' + type: 'stateful' + rule_order: 'default' + register: strict_group + ignore_errors: True + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + + - assert: + that: + - strict_group is failed + + - name: 'Attempt to update the Default Rule Order from strict' + networkfirewall_rule_group: + name: '{{ strict_ro_group_name }}' + type: 'stateful' + rule_order: 'default' + register: strict_group + ignore_errors: True + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + + - assert: + that: + - strict_group is failed + + ##### + + - name: '(CHECK) Attempt to update the Default Rule Order from strict (idempotency)' + networkfirewall_rule_group: + name: '{{ strict_ro_group_name }}' + type: 'stateful' + rule_order: 'strict' + register: strict_group + ignore_errors: True + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + + - assert: + that: + - strict_group is not changed + + - name: 'Attempt to update the Default Rule Order from strict (idempotency)' + networkfirewall_rule_group: + name: '{{ strict_ro_group_name }}' + type: 'stateful' + rule_order: 'strict' + register: strict_group + ignore_errors: True + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + + - assert: + that: + - strict_group is not changed + + + ################################################################### + # Deletion + + - name: 'Delete Stateful rule group' + networkfirewall_rule_group: + name: '{{ stateful_group_name }}' + type: 'stateful' + state: absent + wait: False + register: stateful_group + + - assert: + that: + - stateful_group is changed + + - name: 'Delete Strict rule group' + networkfirewall_rule_group: + name: '{{ strict_ro_group_name }}' + type: 'stateful' + state: absent + wait: False + register: strict_group + + - assert: + that: + - strict_group is changed + + always: + - name: '(always) Delete Stateful rule group' + networkfirewall_rule_group: + name: '{{ stateful_group_name }}' + type: 'stateful' + state: absent + wait: False + ignore_errors: true + + - name: '(always) Delete Strict rule group' + networkfirewall_rule_group: + name: '{{ strict_ro_group_name }}' + type: 'stateful' + state: absent + wait: False + ignore_errors: true diff --git a/ansible_collections/community/aws/tests/integration/targets/opensearch/aliases b/ansible_collections/community/aws/tests/integration/targets/opensearch/aliases new file mode 100644 index 000000000..643f94c94 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/opensearch/aliases @@ -0,0 +1,24 @@ +# reason: slow +# +# The duration of the tasks below was obtained from a single test job. +# There may be significant variations across runs. +# +# OpenSearch Cluster attached to Internet: +# 13 minutes to create ES cluster. Task is not waiting for cluster to become available. +# OpenSearch Cluster attached to VPC: +# 17 minutes to create ES cluster with 2 nodes and dedicated masters. +# 12 minutes to increase size of EBS volumes. +# 12 minutes to increase the node count from 2 to 4. +# 7 minutes to reduce the node count from 4 to 2. +# 35 minutes to upgrade cluster from 7.1 to 7.10. +# 23 minutes to enable node-to-node encryption. +# 36 minutes to enable encryption at rest. +# 30 minutes to enable warm storage. +# 30 minutes to enable cold storage. +# 30 minutes to enable and enforce HTTPS on the domain endpoint. +# 3 minutes to enable auto-tune option. +# 45 minutes to delete cluster. + +disabled + +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/opensearch/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/opensearch/defaults/main.yml new file mode 100644 index 000000000..da6aef4bb --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/opensearch/defaults/main.yml @@ -0,0 +1,2 @@ +--- +# defaults file for opensearch tests diff --git a/ansible_collections/community/aws/tests/integration/targets/opensearch/files/opensearch_policy.json b/ansible_collections/community/aws/tests/integration/targets/opensearch/files/opensearch_policy.json new file mode 100644 index 000000000..eb8b4bc29 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/opensearch/files/opensearch_policy.json @@ -0,0 +1,16 @@ +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Service": "opensearch.amazonaws.com" + }, + "Action": [ + "es:*", + "kms:List*", + "kms:Describe*" + ] + } + ] +} diff --git a/ansible_collections/community/aws/tests/integration/targets/opensearch/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/opensearch/meta/main.yml new file mode 100644 index 000000000..13d6ecd91 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/opensearch/meta/main.yml @@ -0,0 +1,4 @@ +dependencies: + - role: setup_botocore_pip + vars: + botocore_version: "1.21.38" diff --git a/ansible_collections/community/aws/tests/integration/targets/opensearch/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/opensearch/tasks/main.yml new file mode 100644 index 000000000..6d3b47cad --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/opensearch/tasks/main.yml @@ -0,0 +1,30 @@ +--- +# tasks file for test_opensearch +- name: Run opensearch integration tests. + + module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + route53: + # Route53 is explicitly a global service + region: null + collections: + - amazon.aws + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + + block: + # Get some information about who we are before starting our tests + # we'll need this as soon as we start working on the policies + - name: get ARN of calling user + aws_caller_info: + register: aws_caller_info + - include_tasks: test_delete_resources.yml + - include_tasks: test_create_cert.yml + - include_tasks: test_vpc_setup.yml + - include_tasks: test_opensearch.yml + always: + - include_tasks: test_delete_resources.yml diff --git a/ansible_collections/community/aws/tests/integration/targets/opensearch/tasks/test_create_cert.yml b/ansible_collections/community/aws/tests/integration/targets/opensearch/tasks/test_create_cert.yml new file mode 100644 index 000000000..533e75e96 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/opensearch/tasks/test_create_cert.yml @@ -0,0 +1,53 @@ +- pip: + name: + # The 'cryptography' module is required by community.crypto.openssl_privatekey + - 'cryptography' + virtualenv: "{{ botocore_virtualenv }}" + virtualenv_command: "{{ botocore_virtualenv_command }}" + virtualenv_site_packages: no +- name: Create temporary directory + ansible.builtin.tempfile: + state: directory + suffix: build + register: tempdir_1 +- name: Generate private key + community.crypto.openssl_privatekey: + path: '{{ tempdir_1.path }}/rsa-private-key.pem' + type: RSA + size: 2048 +- name: Generate an OpenSSL Certificate Signing Request for own certs + community.crypto.openssl_csr: + path: '{{ tempdir_1.path }}/rsa-csr.pem' + privatekey_path: '{{ tempdir_1.path }}/rsa-private-key.pem' + common_name: 'opensearch.ansible-integ-test.com' +- name: Generate a Self Signed certificate + community.crypto.x509_certificate: + provider: selfsigned + path: '{{ tempdir_1.path }}/rsa-certificate.pem' + csr_path: '{{ tempdir_1.path }}/rsa-csr.pem' + privatekey_path: '{{ tempdir_1.path }}/rsa-private-key.pem' + selfsigned_digest: sha256 +- name: import certificate to ACM + aws_acm: + name_tag: 'opensearch.ansible-integ-test.com' + domain_name: 'opensearch.ansible-integ-test.com' + certificate: "{{ lookup('file', tempdir_1.path + '/rsa-certificate.pem') }}" + private_key: "{{ lookup('file', tempdir_1.path + '/rsa-private-key.pem') }}" + state: present + # tags: + # Application: search + # Environment: development + # purge_tags: false + register: upload_cert +- assert: + that: + - upload_cert.certificate.arn is defined + - upload_cert.certificate.domain_name == 'opensearch.ansible-integ-test.com' + - upload_cert.changed + +- set_fact: + opensearch_certificate_arn: "{{ upload_cert.certificate.arn }}" +- name: Delete temporary directory + ansible.builtin.file: + state: absent + path: "{{ tempdir_1.path }}"
\ No newline at end of file diff --git a/ansible_collections/community/aws/tests/integration/targets/opensearch/tasks/test_delete_resources.yml b/ansible_collections/community/aws/tests/integration/targets/opensearch/tasks/test_delete_resources.yml new file mode 100644 index 000000000..d9ddfc913 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/opensearch/tasks/test_delete_resources.yml @@ -0,0 +1,61 @@ +- name: Delete all resources that were created in the integration tests + block: + - name: Get list of OpenSearch domains + opensearch_info: + tags: + Environment: "Testing" + Application: "Search" + AnsibleTest: "AnsibleTestOpenSearchCluster" + register: opensearch_domains + + - name: Initiate deletion of all test-related OpenSearch clusters + opensearch: + state: absent + domain_name: "{{ domain_name }}" + with_items: "{{ opensearch_domains.domains }}" + vars: + domain_name: "{{ item.domain_status.domain_name }}" + + # We have to wait until the cluster is deleted before proceeding to delete + # security group, VPC, KMS. Otherwise there will be active references and + # deletion of the security group will fail. + - name: Delete OpenSearch clusters, wait until deletion is complete + opensearch: + state: absent + domain_name: "{{ domain_name }}" + wait: true + wait_timeout: "{{ 60 * 60 }}" + with_items: "{{ opensearch_domains.domains }}" + vars: + domain_name: "{{ item.domain_status.domain_name }}" + + - name: Get VPC info + ec2_vpc_net_info: + filters: + "tag:AnsibleTest": "AnsibleTestVpc" + register: vpc_info + + - name: delete VPC resources + include_tasks: test_delete_vpc_resources.yml + with_items: "{{ vpc_info.vpcs }}" + vars: + vpc_id: "{{ item.vpc_id }}" + vpc_name: "{{ item.tags['Name'] }}" + + - name: collect info about KMS keys used for test purpose + aws_kms_info: + filters: + "tag:AnsibleTest": "AnsibleTestVpc" + register: kms_info + - name: Delete KMS keys that were created for test purpose + aws_kms: + key_id: "{{ kms_arn }}" + state: absent + with_items: "{{ kms_info.kms_keys }}" + vars: + kms_arn: "{{ item.key_arn }}" + + - name: delete certificate from ACM + aws_acm: + name_tag: 'opensearch.ansible-integ-test.com' + state: absent diff --git a/ansible_collections/community/aws/tests/integration/targets/opensearch/tasks/test_delete_vpc_resources.yml b/ansible_collections/community/aws/tests/integration/targets/opensearch/tasks/test_delete_vpc_resources.yml new file mode 100644 index 000000000..5fb803c90 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/opensearch/tasks/test_delete_vpc_resources.yml @@ -0,0 +1,92 @@ +- debug: + msg: "Deleting resources in VPC name: {{ vpc_name }}, id: {{ vpc_id }}" + +- name: Get the hosted Route53 zones + route53_info: + query: hosted_zone + hosted_zone_method: list + register: route53_zone_info + +- name: Get Route53 zone id + set_fact: + route53_zone_ids: "{{ route53_zone_info.HostedZones | selectattr('Name', 'equalto', 'ansible-integ-test.com.') | map(attribute='Id') | list }}" + +- name: Delete Route53 record + route53: + record: "opensearch.ansible-integ-test.com" + hosted_zone_id: "{{ route53_zone_ids[0] }}" + private_zone: true + type: CNAME + state: absent + vpc_id: '{{ vpc_id }}' + when: route53_zone_ids | length > 0 + +- name: Delete private Route53 zone for the VPC + route53_zone: + zone: "ansible-integ-test.com" + hosted_zone_id: "{{ route53_zone_ids[0] }}" + state: absent + vpc_id: '{{ vpc_id }}' + when: route53_zone_ids | length > 0 + +- name: Get security groups that have been created for test purpose in the VPC + ec2_group_info: + filters: + vpc-id: "{{ vpc_id }}" + register: sg_info + +- name: Delete security groups + ec2_group: + group_id: "{{ sg_id }}" + state: absent + loop_control: + loop_var: sg_item + with_items: "{{ sg_info.security_groups }}" + vars: + sg_id: "{{ sg_item.group_id }}" + sg_name: "{{ sg_item.group_name }}" + when: sg_name != 'default' + +- name: Delete internet gateway + ec2_vpc_igw: + vpc_id: "{{ vpc_id }}" + state: absent + +- name: Delete subnet_1 + ec2_vpc_subnet: + state: absent + vpc_id: "{{ vpc_id }}" + cidr: 10.55.77.0/24 + +- name: Delete subnet_2 + ec2_vpc_subnet: + state: absent + vpc_id: "{{ vpc_id }}" + cidr: 10.55.78.0/24 + +- name: Collect info about routing tables + ec2_vpc_route_table_info: + filters: + vpc-id: "{{ vpc_id }}" + # Exclude the main route table, which should not be deleted explicitly. + # It will be deleted automatically when the VPC is deleted + "tag:AnsibleTest": "AnsibleTestVpc" + register: routing_table_info + +- name: Delete routing tables + ec2_vpc_route_table: + state: absent + lookup: id + route_table_id: "{{ route_table_id }}" + loop_control: + loop_var: route_item + with_items: "{{ routing_table_info.route_tables }}" + vars: + route_table_id: "{{ route_item.id }}" + +- name: Delete VPC for use in testing + ec2_vpc_net: + name: "{{ vpc_name }}" + cidr_block: 10.55.0.0/16 + purge_cidrs: true + state: absent diff --git a/ansible_collections/community/aws/tests/integration/targets/opensearch/tasks/test_opensearch.yml b/ansible_collections/community/aws/tests/integration/targets/opensearch/tasks/test_opensearch.yml new file mode 100644 index 000000000..7ce1f8d94 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/opensearch/tasks/test_opensearch.yml @@ -0,0 +1,1281 @@ +- name: Create OpenSearch clusters + block: + - name: test without specifying required module options + opensearch: + engine_version: "Elasticsearch_7.1" + ignore_errors: true + register: result + + - name: assert domain_name is a required module option + assert: + that: + - "result.msg == 'missing required arguments: domain_name'" + + - name: Create public-facing OpenSearch cluster in check mode + opensearch: + state: present + domain_name: "es-{{ tiny_prefix }}-public" + engine_version: "OpenSearch_1.1" + cluster_config: + instance_type: "t2.small.search" + instance_count: 2 + dedicated_master: false + access_policies: "{{ lookup('file', 'opensearch_policy.json') | from_json }}" + ebs_options: + ebs_enabled: true + volume_size: 10 + tags: + Name: "es-{{ tiny_prefix }}-public" + Environment: Testing + Application: Search + AnsibleTest: "AnsibleTestOpenSearchCluster" + check_mode: true + register: opensearch_domain + - assert: + that: + - "opensearch_domain is changed" + + - name: Create public-facing OpenSearch cluster, changes expected + # This could take 30 minutes + opensearch: + state: present + # Note domain_name must be less than 28 characters and satisfy regex [a-z][a-z0-9\\-]+ + domain_name: "es-{{ tiny_prefix }}-public" + engine_version: "OpenSearch_1.1" + cluster_config: + instance_type: "t2.small.search" + instance_count: 2 + dedicated_master: false + access_policies: "{{ lookup('file', 'opensearch_policy.json') | from_json }}" + ebs_options: + # EBS must be enabled for "t2.small.search" + ebs_enabled: true + volume_size: 10 + tags: + # Note: The domain name must start with a letter, but the tiny prefix may start with + # a digit. + Name: "es-{{ tiny_prefix }}-public" + Environment: Testing + Application: Search + AnsibleTest: "AnsibleTestOpenSearchCluster" + # Intentionally not waiting for cluster to be available. + # All other integration tests are performed with the + # OpenSearch cluster attached to the VPC. + wait: false + register: opensearch_domain + - assert: + that: + - "opensearch_domain.tags | length == 4" + - "opensearch_domain.engine_version == 'OpenSearch_1.1'" + - "opensearch_domain.cluster_config.instance_count == 2" + - "opensearch_domain.cluster_config.instance_type == 't2.small.search'" + - "opensearch_domain.cluster_config.dedicated_master_enabled == false" + - "opensearch_domain.cluster_config.warm_enabled == false" + - "opensearch_domain.ebs_options.ebs_enabled == true" + - "opensearch_domain.ebs_options.volume_size == 10" + # Below should be default settings when not specified as input arguments + - "opensearch_domain.ebs_options.volume_type == 'gp2'" + - "opensearch_domain.advanced_security_options.enabled == false" + - "opensearch_domain.cognito_options.enabled == false" + - "opensearch_domain.domain_endpoint_options.custom_endpoint_enabled == false" + - "opensearch_domain.encryption_at_rest_options.enabled == false" + - "opensearch_domain.node_to_node_encryption_options.enabled == false" + - "opensearch_domain.snapshot_options.automated_snapshot_start_hour == 0" + # Assert task has changed/not changed OpenSearch domain + - "opensearch_domain is changed" + + - name: Create public-facing OpenSearch cluster in check mode again + opensearch: + state: present + domain_name: "es-{{ tiny_prefix }}-public" + engine_version: "OpenSearch_1.1" + cluster_config: + instance_type: "t2.small.search" + instance_count: 2 + dedicated_master: false + access_policies: "{{ lookup('file', 'opensearch_policy.json') | from_json }}" + ebs_options: + ebs_enabled: true + volume_size: 10 + tags: + Name: "es-{{ tiny_prefix }}-public" + Environment: Testing + Application: Search + AnsibleTest: "AnsibleTestOpenSearchCluster" + check_mode: true + register: opensearch_domain + - assert: + that: + - opensearch_domain is not changed + + - name: Create public-facing OpenSearch cluster, no change expected + opensearch: + state: present + domain_name: "es-{{ tiny_prefix }}-public" + engine_version: "OpenSearch_1.1" + cluster_config: + instance_type: "t2.small.search" + instance_count: 2 + dedicated_master: false + access_policies: "{{ lookup('file', 'opensearch_policy.json') | from_json }}" + ebs_options: + ebs_enabled: true + volume_size: 10 + tags: + Name: "es-{{ tiny_prefix }}-public" + Environment: Testing + Application: Search + AnsibleTest: "AnsibleTestOpenSearchCluster" + register: opensearch_domain + - assert: + that: + - opensearch_domain is not changed + + - name: Create VPC-attached OpenSearch cluster, check mode + opensearch: + state: present + domain_name: "es-{{ tiny_prefix }}-vpc" + engine_version: "Elasticsearch_7.1" + cluster_config: + instance_type: "m5.large.search" + instance_count: 2 + zone_awareness: true + availability_zone_count: 2 + dedicated_master: true + dedicated_master_instance_type: "m5.large.search" + dedicated_master_instance_count: 3 + ebs_options: + ebs_enabled: true + volume_type: "gp2" + volume_size: 10 + snapshot_options: + automated_snapshot_start_hour: 12 + access_policies: "{{ lookup('file', 'opensearch_policy.json') | from_json }}" + vpc_options: + subnets: + - "{{ testing_subnet_1.subnet.id }}" + - "{{ testing_subnet_2.subnet.id }}" + security_groups: + - "{{ sg.group_id }}" + encryption_at_rest_options: + enabled: false + tags: + Name: "es-{{ tiny_prefix }}-public" + Environment: Testing + Application: Search + AnsibleTest: "AnsibleTestOpenSearchCluster" + wait: true + check_mode: true + register: opensearch_domain + - assert: + that: + - "opensearch_domain is changed" + + - name: Create VPC-attached OpenSearch cluster, change expected + # Considerations for selecting node instances for test purpose: + # 1) Low cost + # 2) Supports encryption at rest, advanced security options, node-to-node encryption. + # See https://docs.aws.amazon.com/opensearch-service/latest/developerguide/supported-instance-types.html + opensearch: + state: present + domain_name: "es-{{ tiny_prefix }}-vpc" + engine_version: "Elasticsearch_7.1" + cluster_config: + instance_type: "m5.large.search" + instance_count: 2 + zone_awareness: true + availability_zone_count: 2 + dedicated_master: true + dedicated_master_instance_type: "m5.large.search" + dedicated_master_instance_count: 3 + ebs_options: + ebs_enabled: true + volume_type: "gp2" + volume_size: 10 + snapshot_options: + automated_snapshot_start_hour: 12 + access_policies: "{{ lookup('file', 'opensearch_policy.json') | from_json }}" + vpc_options: + subnets: + - "{{ testing_subnet_1.subnet.id }}" + - "{{ testing_subnet_2.subnet.id }}" + security_groups: + - "{{ sg.group_id }}" + encryption_at_rest_options: + enabled: false + tags: + Name: "es-{{ tiny_prefix }}-public" + Environment: Testing + Application: Search + AnsibleTest: "AnsibleTestOpenSearchCluster" + wait: true + # It could take about 45 minutes for the cluster to be created and available. + wait_timeout: "{{ 45 * 60 }}" + register: opensearch_domain + - assert: + that: + - "opensearch_domain.tags | length == 4" + - "opensearch_domain.engine_version == 'Elasticsearch_7.1'" + - "opensearch_domain.cluster_config.instance_count == 2" + - "opensearch_domain.cluster_config.instance_type == 'm5.large.search'" + - "opensearch_domain.cluster_config.dedicated_master_enabled == true" + - "opensearch_domain.cluster_config.dedicated_master_type == 'm5.large.search'" + - "opensearch_domain.cluster_config.dedicated_master_count == 3" + - "opensearch_domain.cluster_config.warm_enabled == false" + - "opensearch_domain.cluster_config.zone_awareness_enabled == true" + - "opensearch_domain.ebs_options.ebs_enabled == true" + - "opensearch_domain.ebs_options.volume_size == 10" + - "opensearch_domain.ebs_options.volume_type == 'gp2'" + - "opensearch_domain.snapshot_options.automated_snapshot_start_hour == 12" + - "opensearch_domain.vpc_options is defined" + - "opensearch_domain.vpc_options.vpc_id is defined" + - "opensearch_domain.vpc_options.vpc_id == testing_vpc.vpc.id" + - "opensearch_domain.vpc_options.subnet_ids is defined" + - "opensearch_domain.vpc_options.subnet_ids | length == 2" + - "opensearch_domain.vpc_options.security_group_ids is defined" + - "opensearch_domain.vpc_options.security_group_ids | length == 1" + # Assert task has changed/not changed OpenSearch domain + - "opensearch_domain is changed" + + - name: Create VPC-attached OpenSearch cluster, check mode again + opensearch: + state: present + domain_name: "es-{{ tiny_prefix }}-vpc" + engine_version: "Elasticsearch_7.1" + cluster_config: + instance_type: "m5.large.search" + instance_count: 2 + zone_awareness: true + availability_zone_count: 2 + dedicated_master: true + dedicated_master_instance_type: "m5.large.search" + dedicated_master_instance_count: 3 + ebs_options: + ebs_enabled: true + volume_type: "gp2" + volume_size: 10 + snapshot_options: + automated_snapshot_start_hour: 12 + access_policies: "{{ lookup('file', 'opensearch_policy.json') | from_json }}" + vpc_options: + subnets: + - "{{ testing_subnet_1.subnet.id }}" + - "{{ testing_subnet_2.subnet.id }}" + security_groups: + - "{{ sg.group_id }}" + encryption_at_rest_options: + enabled: false + tags: + Name: "es-{{ tiny_prefix }}-public" + Environment: Testing + Application: Search + AnsibleTest: "AnsibleTestOpenSearchCluster" + wait: true + check_mode: true + register: opensearch_domain + - assert: + that: + - opensearch_domain is not changed + + - name: Create VPC-attached OpenSearch cluster, no change expected + opensearch: + state: present + domain_name: "es-{{ tiny_prefix }}-vpc" + engine_version: "Elasticsearch_7.1" + cluster_config: + instance_type: "m5.large.search" + instance_count: 2 + zone_awareness: true + availability_zone_count: 2 + dedicated_master: true + dedicated_master_instance_type: "m5.large.search" + dedicated_master_instance_count: 3 + ebs_options: + ebs_enabled: true + volume_type: "gp2" + volume_size: 10 + snapshot_options: + automated_snapshot_start_hour: 12 + access_policies: "{{ lookup('file', 'opensearch_policy.json') | from_json }}" + vpc_options: + subnets: + - "{{ testing_subnet_1.subnet.id }}" + - "{{ testing_subnet_2.subnet.id }}" + security_groups: + - "{{ sg.group_id }}" + encryption_at_rest_options: + enabled: false + tags: + Name: "es-{{ tiny_prefix }}-public" + Environment: Testing + Application: Search + AnsibleTest: "AnsibleTestOpenSearchCluster" + wait: true + register: opensearch_domain + - assert: + that: + - opensearch_domain is not changed + + - name: Get list of OpenSearch domains + opensearch_info: + tags: + Environment: "Testing" + Application: "Search" + AnsibleTest: "AnsibleTestOpenSearchCluster" + register: opensearch_domains_info + - assert: + that: + - opensearch_domains_info.domains is defined + - opensearch_domains_info.domains | length == 2 + + - name: Get OpenSearch domains matching domain name + opensearch_info: + domain_name: "es-{{ tiny_prefix }}-vpc" + register: opensearch_domains_info + - assert: + that: + - opensearch_domains_info is not changed + - opensearch_domains_info.domains is defined + - opensearch_domains_info.domains | length == 1 + - opensearch_domains_info.domains[0].domain_config is defined + - opensearch_domains_info.domains[0].domain_status is defined + # Even after waiting for the OpenSearch cluster to complete installation, + # it may take a few additional minutes for the 'Endpoints' property + # to be present in the AWS API. + # See further down below. The same `opensearch_info` task is invoked + # after running a task that takes time, and that time there is + # an assert on the 'endpoint' property. + #- opensearch_domains_info.domains[0].domain_status.endpoints is defined + #- opensearch_domains_info.domains[0].domain_status.endpoints.vpc is defined + + - name: Tag Opensearch domain, check mode + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + tags: + Name: "es-{{ tiny_prefix }}-public" + Environment: Testing + tag_a: 'value 1' + Application: Search + AnsibleTest: "AnsibleTestOpenSearchCluster" + wait: true + check_mode: true + register: opensearch_domain + - assert: + that: + - opensearch_domain is changed + + - name: Tag Opensearch domain, expect changes + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + tags: + Name: "es-{{ tiny_prefix }}-public" + Environment: Testing + tag_a: 'value 1' + Application: Search + AnsibleTest: "AnsibleTestOpenSearchCluster" + wait: true + register: opensearch_domain + - assert: + that: + - "opensearch_domain.tags | length == 5" + - opensearch_domain is changed + + - name: Tag Opensearch domain, check mode again + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + tags: + Name: "es-{{ tiny_prefix }}-public" + Environment: Testing + tag_a: 'value 1' + Application: Search + AnsibleTest: "AnsibleTestOpenSearchCluster" + wait: true + check_mode: true + register: opensearch_domain + - assert: + that: + - opensearch_domain is not changed + + - name: Tag Opensearch domain, expect no change + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + tags: + Name: "es-{{ tiny_prefix }}-public" + Environment: Testing + tag_a: 'value 1' + Application: Search + AnsibleTest: "AnsibleTestOpenSearchCluster" + wait: true + register: opensearch_domain + - assert: + that: + - "opensearch_domain.tags | length == 5" + - opensearch_domain is not changed + + - name: Add tag_b to Opensearch domain without purging, check mode + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + tags: + tag_b: 'value 2' + purge_tags: false + wait: true + check_mode: true + register: opensearch_domain + - assert: + that: + - opensearch_domain is changed + + - name: Add tag_b to Opensearch domain without purging + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + tags: + tag_b: 'value 2' + purge_tags: false + wait: true + register: opensearch_domain + - assert: + that: + - "opensearch_domain.tags | length == 6" + - opensearch_domain is changed + + - name: Add tag_b to Opensearch domain without purging, check mode again + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + tags: + tag_b: 'value 2' + purge_tags: false + wait: true + check_mode: true + register: opensearch_domain + - assert: + that: + - opensearch_domain is not changed + + - name: Add tag_b to Opensearch domain without purging, no change expected + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + tags: + tag_b: 'value 2' + purge_tags: false + wait: true + register: opensearch_domain + - assert: + that: + - "opensearch_domain.tags | length == 6" + - opensearch_domain is not changed + + - name: Set tags to Opensearch domain and purge tags, check mode + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + tags: + Name: "es-{{ tiny_prefix }}-public" + Environment: Testing + Application: Search + AnsibleTest: "AnsibleTestOpenSearchCluster" + purge_tags: true + wait: true + check_mode: true + register: opensearch_domain + - assert: + that: + - opensearch_domain is changed + + - name: Set tags to Opensearch domain and purge tags + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + tags: + Name: "es-{{ tiny_prefix }}-public" + Environment: Testing + Application: Search + AnsibleTest: "AnsibleTestOpenSearchCluster" + purge_tags: true + wait: true + register: opensearch_domain + - assert: + that: + - "opensearch_domain.tags | length == 4" + - opensearch_domain is changed + + - name: Set tags to Opensearch domain and purge tags, check mode again + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + tags: + Name: "es-{{ tiny_prefix }}-public" + Environment: Testing + Application: Search + AnsibleTest: "AnsibleTestOpenSearchCluster" + purge_tags: true + wait: true + check_mode: true + register: opensearch_domain + - assert: + that: + - opensearch_domain is not changed + + - name: Set tags to Opensearch domain and purge tags, no change expected + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + tags: + Name: "es-{{ tiny_prefix }}-public" + Environment: Testing + Application: Search + AnsibleTest: "AnsibleTestOpenSearchCluster" + purge_tags: true + wait: true + register: opensearch_domain + - assert: + that: + - "opensearch_domain.tags | length == 4" + - opensearch_domain is not changed + +- name: Change EBS storage configuration, snapshot hour, instance count + block: + - name: Increase size of EBS volumes, check mode + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + ebs_options: + volume_size: 12 + wait: true + check_mode: true + register: opensearch_domain + - assert: + that: + - opensearch_domain is changed + + - name: Increase size of EBS volumes, expect changes + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + ebs_options: + volume_size: 12 + wait: true + # It could take 20 minutes to increase the size of the EBS volumes + wait_timeout: "{{ 30 * 60 }}" + register: opensearch_domain + - assert: + that: + - "opensearch_domain.ebs_options.volume_size == 12" + - opensearch_domain is changed + + - name: Increase size of EBS volumes, check mode again + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + ebs_options: + volume_size: 12 + wait: true + check_mode: true + register: opensearch_domain + - assert: + that: + - opensearch_domain is not changed + + - name: Increase size of EBS volumes, expect no change + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + ebs_options: + volume_size: 12 + wait: true + register: opensearch_domain + - assert: + that: + - "opensearch_domain.ebs_options.volume_size == 12" + - opensearch_domain is not changed + + - name: Get OpenSearch domains matching domain name + opensearch_info: + domain_name: "es-{{ tiny_prefix }}-vpc" + register: opensearch_domains_info + - assert: + that: + - opensearch_domains_info is not changed + - opensearch_domains_info.domains is defined + - opensearch_domains_info.domains | length == 1 + - opensearch_domains_info.domains[0].domain_config is defined + - opensearch_domains_info.domains[0].domain_status is defined + # Even after waiting for the OpenSearch cluster to complete installation, + # it may take a few additional minutes for the 'Endpoints' property + # to be present in the AWS API. + - opensearch_domains_info.domains[0].domain_status.endpoints is defined + - opensearch_domains_info.domains[0].domain_status.endpoints.vpc is defined + + - name: Set fact for OpenSearch endpoint FQDN in the VPC + set_fact: + opensearch_vpc_fqdn: "{{ opensearch_domains_info.domains[0].domain_status.endpoints.vpc }}" + + # Create a Route53 CNAME record. + # This CNAME record will be used when configuring a custom endpoint + # for the OpenSearch cluster. + - name: Create CNAME record for the OpenSearch custom endpoint + route53: + state: present + hosted_zone_id: "{{ route53_zone_id }}" + record: "opensearch.ansible-integ-test.com" + private_zone: true + type: CNAME + value: "{{ opensearch_vpc_fqdn }}" + + - name: Change snapshot start hour, check mode + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + snapshot_options: + automated_snapshot_start_hour: 3 + wait: true + check_mode: true + register: opensearch_domain + - assert: + that: + - opensearch_domain is changed + + - name: Change snapshot start hour + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + snapshot_options: + automated_snapshot_start_hour: 3 + wait: true + register: opensearch_domain + - assert: + that: + - "opensearch_domain.snapshot_options.automated_snapshot_start_hour == 3" + - opensearch_domain is changed + + - name: Change snapshot start hour, check mode again + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + snapshot_options: + automated_snapshot_start_hour: 3 + wait: true + check_mode: true + register: opensearch_domain + - assert: + that: + - opensearch_domain is not changed + + - name: Change snapshot start hour, no change expected + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + snapshot_options: + automated_snapshot_start_hour: 3 + wait: true + register: opensearch_domain + - assert: + that: + - opensearch_domain is not changed + + - name: Set instance count to 4 in the OpenSearch cluster, check mode + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + cluster_config: + # It's a 2-AZ deployment, therefore the node count must be even. + instance_count: 4 + wait: true + check_mode: true + register: opensearch_domain + - assert: + that: + - opensearch_domain is changed + + - name: Set instance count to 4 in the OpenSearch cluster, changes expected + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + cluster_config: + # It's a 2-AZ deployment, therefore the node count must be even. + instance_count: 4 + wait: true + # It could take 20 minutes to increase the number of nodes in the cluster + wait_timeout: "{{ 30 * 60 }}" + register: opensearch_domain + - assert: + that: + - "opensearch_domain.cluster_config.instance_count == 4" + - opensearch_domain is changed + + - name: Set instance count to 4 in the OpenSearch cluster, check mode again + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + cluster_config: + # It's a 2-AZ deployment, therefore the node count must be even. + instance_count: 4 + wait: true + check_mode: true + register: opensearch_domain + - assert: + that: + - opensearch_domain is not changed + + - name: Set instance count to 4 in the OpenSearch cluster, no change expected + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + cluster_config: + # It's a 2-AZ deployment, therefore the node count must be even. + instance_count: 4 + wait: true + register: opensearch_domain + - assert: + that: + - opensearch_domain is not changed + + - name: Set instance count to 2 in the OpenSearch cluster, check mode + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + cluster_config: + instance_count: 2 + wait: true + check_mode: true + register: opensearch_domain + - assert: + that: + - opensearch_domain is changed + + - name: Set instance count to 2 in the OpenSearch cluster, changes expected + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + cluster_config: + instance_count: 2 + wait: true + # It could take 20 minutes to decrease the number of nodes in the cluster + wait_timeout: "{{ 30 * 60 }}" + register: opensearch_domain + - assert: + that: + - "opensearch_domain.cluster_config.instance_count == 2" + - opensearch_domain is changed + + - name: Set instance count to 2 in the OpenSearch cluster, check mode again + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + cluster_config: + instance_count: 2 + wait: true + check_mode: true + register: opensearch_domain + - assert: + that: + - opensearch_domain is not changed + + - name: Set instance count to 2 in the OpenSearch cluster, no change expected + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + cluster_config: + instance_count: 2 + wait: true + register: opensearch_domain + - assert: + that: + - "opensearch_domain.cluster_config.instance_count == 2" + - opensearch_domain is not changed + +- name: Upgrade OpenSearch cluster + block: + - name: Upgrade OpenSearch cluster, check mode + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + engine_version: "Elasticsearch_7.10" + check_mode: true + register: opensearch_domain + - assert: + that: + - opensearch_domain is changed + + - name: Upgrade OpenSearch cluster, change expected + # Upgrade from version 7.1 to version 7.10. + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + engine_version: "Elasticsearch_7.10" + wait: true + # It could take 40 minutes to upgrade the cluster + wait_timeout: "{{ 50 * 60 }}" + register: opensearch_domain + - assert: + that: + - "opensearch_domain.engine_version == 'Elasticsearch_7.10'" + - "opensearch_domain.cluster_config.instance_count == 2" + - opensearch_domain is changed + + - name: Upgrade OpenSearch cluster, check mode again + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + engine_version: "Elasticsearch_7.10" + check_mode: true + register: opensearch_domain + - assert: + that: + - opensearch_domain is not changed + + - name: Upgrade OpenSearch cluster, no change expected + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + engine_version: "Elasticsearch_7.10" + register: opensearch_domain + - assert: + that: + - "opensearch_domain.engine_version == 'Elasticsearch_7.10'" + - "opensearch_domain.cluster_config.instance_count == 2" + - opensearch_domain is not changed + +- name: Configure encryption + when: false + block: + - name: Enable node to node encryption, check mode + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + node_to_node_encryption_options: + enabled: true + wait: true + check_mode: true + register: opensearch_domain + - assert: + that: + - opensearch_domain is changed + + - name: Enable node to node encryption, change expected + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + node_to_node_encryption_options: + enabled: true + wait: true + # This may take a long time. + wait_timeout: "{{ 60 * 60 }}" + register: opensearch_domain + - assert: + that: + - "opensearch_domain.node_to_node_encryption_options.enabled == True" + - opensearch_domain is changed + + - name: Enable node to node encryption, check mode again + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + node_to_node_encryption_options: + enabled: true + wait: true + check_mode: true + register: opensearch_domain + - assert: + that: + - opensearch_domain is not changed + + - name: Enable node to node encryption, no change expected + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + node_to_node_encryption_options: + enabled: true + wait: true + register: opensearch_domain + - assert: + that: + - "opensearch_domain.node_to_node_encryption_options.enabled == True" + - opensearch_domain is not changed + + - name: Enable encryption at rest, check mode + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + encryption_at_rest_options: + enabled: true + #kms_key_id: "{{ kms_test_key.key_id }}" + wait: true + check_mode: true + register: opensearch_domain + - assert: + that: + - opensearch_domain is changed + + - name: Enable encryption at rest, change expected + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + encryption_at_rest_options: + enabled: true + # key ARN has the format: + # 'arn:aws:kms:{{region}}:{{account}}:key/{{key_id}}' + # The API docs indicate the value is the KMS key id, which works + # However, the KMS key ARN seems to be a better option because + # the AWS API actually returns the KMS key ARN. + + # Do not set 'kms_key_id' to let AWS manage the key. + #kms_key_id: "{{ kms_test_key.key_arn }}" + wait: true + # It may take a long time for the task to complete. + wait_timeout: "{{ 60 * 60 }}" + register: opensearch_domain + - assert: + that: + - "opensearch_domain.encryption_at_rest_options.enabled == True" + - opensearch_domain is changed + + - name: Enable encryption at rest, check mode again + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + encryption_at_rest_options: + enabled: true + #kms_key_id: "{{ kms_test_key.key_arn }}" + wait: true + check_mode: true + register: opensearch_domain + - assert: + that: + - opensearch_domain is not changed + + - name: Enable encryption at rest, no change expected + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + encryption_at_rest_options: + enabled: true + #kms_key_id: "{{ kms_test_key.key_arn }}" + wait: true + register: opensearch_domain + - assert: + that: + - opensearch_domain is not changed + +- name: Configure HTTPs endpoint + when: false + block: + - name: Enforce HTTPS for OpenSearch endpoint, check mode + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + domain_endpoint_options: + enforce_https: true + tls_security_policy: "Policy-Min-TLS-1-0-2019-07" + wait: true + check_mode: true + register: opensearch_domain + - assert: + that: + - opensearch_domain is changed + + - name: Enforce HTTPS for OpenSearch endpoint, changes expected + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + domain_endpoint_options: + enforce_https: true + tls_security_policy: "Policy-Min-TLS-1-0-2019-07" + # Refer to CNAME that was defined in the previous tasks. + custom_endpoint_enabled: true + custom_endpoint: "opensearch.ansible-integ-test.com" + # TODO: create a certificate. There is a dependency on the aws_acm module + # which does not support certificates issued by AWS. + # The OpenSearch endpoint should have a certificate issued by a trusted CA + # otherwise clients to the OpenSearch cluster will not be able to validate + # the x.509 certificate of the OpenSearch endpoint. + custom_endpoint_certificate_arn: "{{ opensearch_certificate_arn }}" + wait: true + wait_timeout: "{{ 60 * 60 }}" + register: opensearch_domain + until: opensearch_domain is not failed + ignore_errors: true + retries: 10 + # After enabling at rest encryption, there is a period during which the API fails, so retry. + delay: 30 + - assert: + that: + - "opensearch_domain.domain_endpoint_options.enforce_https == True" + - "opensearch_domain.domain_endpoint_options.tls_security_policy == 'Policy-Min-TLS-1-0-2019-07'" + #- "opensearch_domain.domain_endpoint_options.custom_endpoint_enabled == True" + - opensearch_domain is changed + + - name: Change TLS policy, check mode + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + domain_endpoint_options: + tls_security_policy: "Policy-Min-TLS-1-2-2019-07" + wait: true + check_mode: true + register: opensearch_domain + - assert: + that: + - opensearch_domain is changed + + - name: Change TLS policy, change expected + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + domain_endpoint_options: + tls_security_policy: "Policy-Min-TLS-1-2-2019-07" + wait: true + wait_timeout: "{{ 60 * 60 }}" + register: opensearch_domain + - assert: + that: + - "opensearch_domain.domain_endpoint_options.enforce_https == True" + - "opensearch_domain.domain_endpoint_options.tls_security_policy == 'Policy-Min-TLS-1-2-2019-07'" + - opensearch_domain is changed + +- name: Set common facts for advanced security tests + set_fact: + test_master_user_name: my_custom_admin_username + test_master_user_password: "{{ lookup('ansible.builtin.password', '/dev/null chars=ascii_lowercase,digits length=16') }}" + +- name: Configure advanced security + block: + - name: Enable advanced security, check mode + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + advanced_security_options: + enabled: true + internal_user_database_enabled: false + master_user_options: + master_user_name: "{{ test_master_user_name }}" + master_user_password: "{{ test_master_user_password }}" + wait: true + check_mode: true + register: opensearch_domain + - assert: + that: + - opensearch_domain is changed + + # Pre-requisites before enabling advanced security options: + # 1) node-to-node encryption must be enabled. + # 2) encryption at rest to must be enabled. + # 3) Enforce HTTPS in the domain endpoint options. + - name: Enable advanced security, change expected + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + advanced_security_options: + enabled: true + internal_user_database_enabled: false + master_user_options: + master_user_name: "{{ test_master_user_name }}" + master_user_password: "{{ test_master_user_password }}" + wait: true + wait_timeout: "{{ 60 * 60 }}" + register: opensearch_domain + - assert: + that: + - "opensearch_domain.advanced_security_options.enabled == True" + - "opensearch_domain.advanced_security_options.internal_user_database_enabled == False" + - "opensearch_domain.advanced_security_options.master_user_options is defined" + - "opensearch_domain.advanced_security_options.master_user_options.master_user_name is test_master_user_name" + - "opensearch_domain.advanced_security_options.master_user_options.master_user_password is test_master_user_password" + - opensearch_domain is changed + + - name: Enable advanced security, check mode again + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + advanced_security_options: + enabled: true + internal_user_database_enabled: false + master_user_options: + master_user_name: "{{ test_master_user_name }}" + master_user_password: "{{ test_master_user_password }}" + wait: true + check_mode: true + register: opensearch_domain + - assert: + that: + - opensearch_domain is not changed + + - name: Enable advanced security, no change expected + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + advanced_security_options: + enabled: true + internal_user_database_enabled: false + master_user_options: + master_user_name: "{{ test_master_user_name }}" + master_user_password: "{{ test_master_user_password }}" + wait: true + register: opensearch_domain + - assert: + that: + - "opensearch_domain.advanced_security_options.enabled == True" + - "opensearch_domain.advanced_security_options.internal_user_database_enabled == False" + - "opensearch_domain.advanced_security_options.master_user_options is defined" + - "opensearch_domain.advanced_security_options.master_user_options.master_user_name is test_master_user_name" + - "opensearch_domain.advanced_security_options.master_user_options.master_user_password is test_master_user_password" + - opensearch_domain is not changed + +- name: Configure warm and cold storage + block: + - name: Enable warm storage in check mode + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + cluster_config: + warm_enabled: true + warm_type: "ultrawarm1.medium.search" + warm_count: 2 + wait: true + check_mode: true + register: opensearch_domain + - assert: + that: + - opensearch_domain is changed + + - name: Enable warm storage, change expected + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + cluster_config: + warm_enabled: true + warm_type: "ultrawarm1.medium.search" + warm_count: 2 + wait: true + # Adding warm storage may take a long time. + wait_timeout: "{{ 45 * 60 }}" + register: opensearch_domain + - assert: + that: + - "opensearch_domain.cluster_config.warm_enabled == True" + - "opensearch_domain.cluster_config.warm_count == 2" + - "opensearch_domain.cluster_config.warm_type == 'ultrawarm1.medium.search'" + - opensearch_domain is changed + + - name: Enable warm storage in check mode again + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + cluster_config: + warm_enabled: true + warm_type: "ultrawarm1.medium.search" + warm_count: 2 + wait: true + check_mode: true + register: opensearch_domain + - assert: + that: + - opensearch_domain is not changed + + - name: Enable warm storage, no change expected + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + cluster_config: + warm_enabled: true + warm_type: "ultrawarm1.medium.search" + warm_count: 2 + wait: true + register: opensearch_domain + - assert: + that: + - "opensearch_domain.cluster_config.warm_enabled == True" + - "opensearch_domain.cluster_config.warm_count == 2" + - opensearch_domain is not changed + + - name: Enable cold storage in check mode + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + cluster_config: + cold_storage_options: + enabled: true + wait: true + check_mode: true + register: opensearch_domain + - assert: + that: + - opensearch_domain is changed + + - name: Enable cold storage, change expected + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + cluster_config: + cold_storage_options: + enabled: true + wait: true + # Adding cold storage may take a long time. + wait_timeout: "{{ 45 * 60 }}" + register: opensearch_domain + - assert: + that: + - "opensearch_domain.cluster_config.cold_storage_options.enabled == True" + - opensearch_domain is changed + + - name: Enable cold storage in check mode again + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + cluster_config: + cold_storage_options: + enabled: true + wait: true + check_mode: true + register: opensearch_domain + - assert: + that: + - opensearch_domain is not changed + + - name: Enable cold storage, no change expected + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + cluster_config: + cold_storage_options: + enabled: true + wait: true + register: opensearch_domain + - assert: + that: + - "opensearch_domain.cluster_config.cold_storage_options.enabled == True" + - opensearch_domain is not changed + +- name: Configure auto-tune options + block: + - name: Enable auto-tune options in check mode + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + auto_tune_options: + desired_state: "ENABLED" + maintenance_schedules: + - start_at: "{{ ansible_date_time.year|int + 1 }}-01-01" + duration: + value: 10 + unit: "HOURS" + cron_expression_for_recurrence: "cron(0 12 * * ? *)" + wait: true + check_mode: true + register: opensearch_domain + - assert: + that: + - opensearch_domain is changed + + - name: Enable auto-tune options, changes expected + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + auto_tune_options: + desired_state: "ENABLED" + maintenance_schedules: + # Create a schedule one year from the date we start the test. + # If we hard code the date, we have to pick a date, but the API will fail + # if start_at is in the past. + - start_at: "{{ ansible_date_time.year|int + 1 }}-01-01" + duration: + value: 10 + unit: "HOURS" + cron_expression_for_recurrence: "cron(0 12 * * ? *)" + # I have observed that at least during one test run, + # the cluster status was stuck in 'processing=true' for over 24 hours + # after enabling auto-tune options. + # This does not seem right. From a test perspective, that means we cannot + # execute any change that needs to wait until the cluster is not processing + # background tasks. + wait: false + register: opensearch_domain + - assert: + that: + - "opensearch_domain.auto_tune_options.state == 'ENABLED'" + - opensearch_domain is changed + + - name: Enable auto-tune options in check mode again + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + auto_tune_options: + desired_state: "ENABLED" + maintenance_schedules: + - start_at: "{{ ansible_date_time.year|int + 1 }}-01-01" + duration: + value: 10 + unit: "HOURS" + cron_expression_for_recurrence: "cron(0 12 * * ? *)" + wait: false + check_mode: true + register: opensearch_domain + - assert: + that: + - opensearch_domain is not changed + + - name: Enable auto-tune options, no change expected + opensearch: + domain_name: "es-{{ tiny_prefix }}-vpc" + auto_tune_options: + desired_state: "ENABLED" + maintenance_schedules: + - start_at: "{{ ansible_date_time.year|int + 1 }}-01-01" + duration: + value: 10 + unit: "HOURS" + cron_expression_for_recurrence: "cron(0 12 * * ? *)" + wait: false + register: opensearch_domain + - assert: + that: + - "opensearch_domain.auto_tune_options.state == 'ENABLED'" + - opensearch_domain is not changed diff --git a/ansible_collections/community/aws/tests/integration/targets/opensearch/tasks/test_vpc_setup.yml b/ansible_collections/community/aws/tests/integration/targets/opensearch/tasks/test_vpc_setup.yml new file mode 100644 index 000000000..90aeb50bb --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/opensearch/tasks/test_vpc_setup.yml @@ -0,0 +1,134 @@ +- name: Configure pre-requisites for test, VPC and KMS resources + block: + - name: Create VPC for use in testing + ec2_vpc_net: + name: "{{ tiny_prefix }}-vpc" + state: present + cidr_block: 10.55.0.0/16 + tenancy: default + tags: + AnsibleEIPTest: "{{ tiny_prefix }}-vpc" + AnsibleTest: AnsibleTestVpc + register: testing_vpc + + - name: Wait until VPC is created. + ec2_vpc_net_info: + filters: + tag:AnsibleEIPTest: + - "{{ tiny_prefix }}-vpc" + register: vpc_info + retries: 120 + delay: 5 + until: + - vpc_info.vpcs | length > 0 + - vpc_info.vpcs[0].state == 'available' + + - name: Create internet gateway for use in testing + ec2_vpc_igw: + vpc_id: "{{ testing_vpc.vpc.id }}" + state: present + resource_tags: + Name: "{{ tiny_prefix }}-igw" + AnsibleTest: AnsibleTestVpc + register: igw + + # The list of AZs varies across regions and accounts. + # Gather info and pick two AZs for test purpose. + - name: gather AZ info in VPC for test purpose + amazon.aws.aws_az_info: + region: "{{ aws_region }}" + register: az_info + - assert: + that: + # We cannot run the test if this region does not have at least 2 AZs + - "az_info.availability_zones | length >= 2" + - set_fact: + test_az_zone1: "{{ az_info.availability_zones[0].zone_name }}" + test_az_zone2: "{{ az_info.availability_zones[1].zone_name }}" + - name: Create subnet_1 for use in testing + ec2_vpc_subnet: + state: present + vpc_id: "{{ testing_vpc.vpc.id }}" + cidr: 10.55.77.0/24 + az: "{{ test_az_zone1 }}" + resource_tags: + Name: "{{ tiny_prefix }}-subnet_1" + AnsibleTest: AnsibleTestVpc + wait: true + register: testing_subnet_1 + + - name: Create subnet_2 for use in testing + ec2_vpc_subnet: + state: present + vpc_id: "{{ testing_vpc.vpc.id }}" + cidr: 10.55.78.0/24 + az: "{{ test_az_zone2 }}" + resource_tags: + Name: "{{ tiny_prefix }}-subnet_2" + AnsibleTest: AnsibleTestVpc + wait: true + register: testing_subnet_2 + + - name: Create routing rules + ec2_vpc_route_table: + vpc_id: "{{ testing_vpc.vpc.id }}" + routes: + - dest: 0.0.0.0/0 + gateway_id: "{{ igw.gateway_id }}" + subnets: + - "{{ testing_subnet_1.subnet.id }}" + - "{{ testing_subnet_2.subnet.id }}" + tags: + Name: "{{ tiny_prefix }}-route" + AnsibleTest: AnsibleTestVpc + + - name: Create security group for use in testing + ec2_group: + name: "{{ tiny_prefix }}-sg" + description: a security group for ansible tests + vpc_id: "{{ testing_vpc.vpc.id }}" + tags: + Name: "{{ tiny_prefix }}-sg" + AnsibleTest: AnsibleTestVpc + rules: + - proto: tcp + from_port: 22 + to_port: 22 + cidr_ip: 0.0.0.0/0 + - proto: tcp + from_port: 80 + to_port: 80 + cidr_ip: 0.0.0.0/0 + register: sg + + # A private route53 zone is needed to configure a custom endpoint in the OpenSearch cluster. + # See https://docs.aws.amazon.com/opensearch-service/latest/developerguide/customendpoint.html + - name: Create private Route53 zone for the VPC + register: route53_zone + route53_zone: + comment: "zone for ansible integration tests" + zone: "ansible-integ-test.com" + state: present + vpc_id: '{{ testing_vpc.vpc.id }}' + tags: + Name: "{{ tiny_prefix }}-zone" + AnsibleTest: AnsibleTestVpc + + - name: Set fact for route53 zone id + set_fact: + route53_zone_id: "{{ route53_zone.result.zone_id }}" + + - name: Create KMS key for test purpose + # The key is needed for OpenSearch encryption at rest. + aws_kms: + alias: "{{ tiny_prefix }}-kms" + description: a key used for encryption at rest in test OpenSearch cluster + state: present + enabled: yes + key_spec: SYMMETRIC_DEFAULT + key_usage: ENCRYPT_DECRYPT + policy: "{{ lookup('template', 'kms_policy.j2') }}" + tags: + Name: "{{ tiny_prefix }}-kms" + AnsibleTest: AnsibleTestVpc + register: kms_test_key diff --git a/ansible_collections/community/aws/tests/integration/targets/opensearch/templates/kms_policy.j2 b/ansible_collections/community/aws/tests/integration/targets/opensearch/templates/kms_policy.j2 new file mode 100644 index 000000000..e9ddc2472 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/opensearch/templates/kms_policy.j2 @@ -0,0 +1,68 @@ +{ + "Id": "key-ansible-test-policy-123", + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "Allow access for root user", + "Effect": "Allow", + "Principal": { + "AWS": "arn:aws:iam::{{ aws_caller_info.account }}:root" + }, + "Action": "kms:*", + "Resource": "*" + }, + { + "Sid": "AnsibleTestManage", + "Effect": "Allow", + "Principal": { + "AWS": "{{ aws_caller_info.arn }}" + }, + "Action": "kms:*", + "Resource": "*" + }, + { + "Sid": "Allow access for Key Administrators", + "Effect": "Allow", + "Principal": { + "AWS": "arn:aws:iam::{{ aws_caller_info.account }}:role/admin" + }, + "Action": "kms:*", + "Resource": "*" + }, + { + "Sid": "Allow access through OpenSearch Service for all principals in the account that are authorized to use OpenSearch Service", + "Effect": "Allow", + "Principal": { + "AWS": "*" + }, + "Action": [ + "kms:Encrypt", + "kms:Decrypt", + "kms:ReEncrypt*", + "kms:GenerateDataKey*", + "kms:CreateGrant", + "kms:DescribeKey" + ], + "Resource": "*", + "Condition": { + "StringEquals": { + "kms:CallerAccount": "{{ aws_caller_info.account }}", + "kms:ViaService": "es.{{ aws_region }}.amazonaws.com" + } + } + }, + { + "Sid": "Allow OpenSearch service principals to describe the key directly", + "Effect": "Allow", + "Principal": { + "Service": "es.amazonaws.com" + }, + "Action": [ + "kms:Describe*", + "kms:Get*", + "kms:List*" + ], + "Resource": "*" + } + ] +} diff --git a/ansible_collections/community/aws/tests/integration/targets/redshift/aliases b/ansible_collections/community/aws/tests/integration/targets/redshift/aliases new file mode 100644 index 000000000..b39c99f5c --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/redshift/aliases @@ -0,0 +1,3 @@ +time=35m +cloud/aws +redshift_info diff --git a/ansible_collections/community/aws/tests/integration/targets/redshift/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/redshift/defaults/main.yml new file mode 100644 index 000000000..18046aa1f --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/redshift/defaults/main.yml @@ -0,0 +1,6 @@ +--- +# defaults file for test_redshift +redshift_cluster_name: 'ansible-test-{{ tiny_prefix }}' +reshift_master_password: "th1s_is_A_test" +redshift_master_username: "master_user" +node_type: "dc2.large" diff --git a/ansible_collections/community/aws/tests/integration/targets/redshift/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/redshift/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/redshift/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/redshift/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/redshift/tasks/main.yml new file mode 100644 index 000000000..f79991d4e --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/redshift/tasks/main.yml @@ -0,0 +1,358 @@ +--- +# A Note about ec2 environment variable name preference: +# - EC2_URL -> AWS_URL +# - EC2_ACCESS_KEY -> AWS_ACCESS_KEY_ID -> AWS_ACCESS_KEY +# - EC2_SECRET_KEY -> AWS_SECRET_ACCESS_KEY -> AWX_SECRET_KEY +# - EC2_REGION -> AWS_REGION +# + +- module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + block: + + # ============================================================ + - name: test failure with no parameters + redshift: + register: result + ignore_errors: true + + - name: assert failure with no parameters + assert: + that: + - 'result.failed' + - 'result.msg == "missing required arguments: command, identifier"' + + # ============================================================ + - name: test failure with only identifier + redshift: + identifier: '{{ redshift_cluster_name }}' + register: result + ignore_errors: true + + - name: assert failure with only identifier + assert: + that: + - 'result.failed' + - 'result.msg == "missing required arguments: command"' + + # ============================================================ + - name: test create with no identifier + redshift: + command: create + register: result + ignore_errors: true + + - name: assert failure with no identifier + assert: + that: + - 'result.failed' + - 'result.msg == "missing required arguments: identifier"' + + # ============================================================ + - name: test create with missing node_type + redshift: + command: create + identifier: "{{ redshift_cluster_name }}" + register: result + ignore_errors: true + + - name: assert failure with missing node_type + assert: + that: + - 'result.failed' + - 'result.msg == "command is create but all of the following are missing: node_type, username, password"' + + # ============================================================ + + - name: test create with missing password + redshift: + command: create + identifier: "{{ redshift_cluster_name }}" + username: "{{ redshift_master_username }}" + register: result + ignore_errors: true + + - name: assert create failure with missing password + assert: + that: + - 'result.failed' + - 'result.msg == "command is create but all of the following are missing: node_type, password"' + + # ============================================================ + + - name: test create with missing username + redshift: + command: create + identifier: "{{ redshift_cluster_name }}" + password: "{{ reshift_master_password }}" + register: result + ignore_errors: true + + - name: assert create failure with missing username + assert: + that: + - 'result.failed' + - 'result.msg == "command is create but all of the following are missing: node_type, username"' + + # ============================================================ + + - name: test create with default params + redshift: + command: create + identifier: "{{ redshift_cluster_name }}" + username: "{{ redshift_master_username }}" + password: "{{ reshift_master_password }}" + node_type: "{{ node_type }}" + wait: yes + wait_timeout: 1000 + tags: + foo: bar + Tizio: Caio + register: result + - debug: + msg: "{{ result }}" + verbosity: 1 + - name: assert create success + assert: + that: + - 'result.changed' + - 'result.cluster.identifier == "{{ redshift_cluster_name }}"' + - 'result.cluster.tags.foo == "bar"' + - 'result.cluster.tags.Tizio == "Caio"' + + # ============================================================ + + - name: test create again with default params + redshift: + command: create + identifier: "{{ redshift_cluster_name }}" + username: "{{ redshift_master_username }}" + password: "{{ reshift_master_password }}" + node_type: "{{ node_type }}" + wait_timeout: 600 + tags: + foo: bar + Tizio: Caio + register: result + + - name: assert no change gets made to the existing cluster + assert: + that: + - 'not result.changed' + - 'result.cluster.identifier == "{{ redshift_cluster_name }}"' + - 'result.cluster.tags.foo == "bar"' + - 'result.cluster.tags.Tizio == "Caio"' + - 'result.cluster.tags | count() == 2' + + # ============================================================ + + - name: test modify cluster + redshift: + command: modify + identifier: "{{ redshift_cluster_name }}" + new_cluster_identifier: "{{ redshift_cluster_name }}-modified" + enhanced_vpc_routing: True + wait: yes + wait_timeout: 1000 + tags: + foo: bar + register: result + + - name: assert cluster was modified + assert: + that: + - 'result.changed' + - 'result.cluster.identifier == "{{ redshift_cluster_name }}-modified"' + - 'result.cluster.enhanced_vpc_routing == True' + - 'result.cluster.tags | count() == 1' + - 'result.cluster.tags.foo == "bar"' + + # ============================================================ + - name: test delete with no cluster identifier + redshift: + command: delete + register: result + ignore_errors: true + + - name: assert failure with no identifier + assert: + that: + - 'result.failed' + - 'result.msg == "missing required arguments: identifier"' + + # ============================================================ + - name: test delete with no snapshot id + redshift: + command: delete + identifier: "{{ redshift_cluster_name }}" + register: result + ignore_errors: true + + - name: assert failure for no snapshot identifier + assert: + that: + - 'result.failed' + - 'result.msg == "Need to specify final_cluster_snapshot_identifier if skip_final_cluster_snapshot is False"' + + + # ============================================================ + - name: test successful delete + redshift: + command: delete + identifier: "{{ redshift_cluster_name }}-modified" + skip_final_cluster_snapshot: true + wait: yes + wait_timeout: 1200 + register: result + + - name: assert delete + assert: + that: + - 'result.changed' + + # ============================================================ + + - name: test create multi-node cluster with custom db-name + redshift: + command: create + identifier: "{{ redshift_cluster_name }}" + username: "{{ redshift_master_username }}" + password: "{{ reshift_master_password }}" + node_type: "{{ node_type }}" + cluster_type: multi-node + number_of_nodes: 3 + wait: yes + db_name: "integration_test" + wait_timeout: 1800 + register: result + + - name: assert create + assert: + that: + - 'result.changed' + - 'result.cluster.identifier == "{{ redshift_cluster_name }}"' + - 'result.cluster.db_name == "integration_test"' + + # ============================================================ + + - name: test tag update on existing cluster + redshift: + command: create + identifier: "{{ redshift_cluster_name }}" + username: "{{ redshift_master_username }}" + password: "{{ reshift_master_password }}" + node_type: "{{ node_type }}" + cluster_type: multi-node + number_of_nodes: 3 + wait: yes + db_name: "integration_test" + tags: + foo: bar + wait_timeout: 1800 + register: result + + + - name: assert tags change + assert: + that: + - 'result.changed' + - 'result.cluster.identifier == "{{ redshift_cluster_name }}"' + - 'result.cluster.db_name == "integration_test"' + - 'result.cluster.tags.foo == "bar"' + + + # ============================================================ + + - name: test purge tags + redshift: + command: create + identifier: "{{ redshift_cluster_name }}" + username: "{{ redshift_master_username }}" + password: "{{ reshift_master_password }}" + node_type: "{{ node_type }}" + cluster_type: multi-node + number_of_nodes: 3 + wait: yes + db_name: "integration_test" + tags: + test1: value1 + purge_tags: false + wait_timeout: 1800 + register: result + + + - name: assert tags change + assert: + that: + - 'result.changed' + - 'result.cluster.identifier == "{{ redshift_cluster_name }}"' + - 'result.cluster.db_name == "integration_test"' + - 'result.cluster.tags.test1 == "value1"' + - 'result.cluster.tags.foo == "bar"' + - 'result.cluster.tags | count() == 2' + + + + # ============================================================ + + - name: test no change to tags when tags is None + redshift: + command: create + identifier: "{{ redshift_cluster_name }}" + username: "{{ redshift_master_username }}" + password: "{{ reshift_master_password }}" + node_type: "{{ node_type }}" + cluster_type: multi-node + number_of_nodes: 3 + wait: yes + db_name: "integration_test" + wait_timeout: 1800 + register: result + + + - name: assert create + assert: + that: + - 'not result.changed' + - 'result.cluster.identifier == "{{ redshift_cluster_name }}"' + - 'result.cluster.db_name == "integration_test"' + - 'result.cluster.tags | count() == 2' + + + # ============================================================ + + - name: test successful delete of multi-node cluster + redshift: + command: delete + identifier: "{{ redshift_cluster_name }}" + skip_final_cluster_snapshot: true + wait: yes + wait_timeout: 1200 + register: result + + - name: assert delete + assert: + that: + - 'result.changed' + + always: + + - name: Remove cluster if tests failed + redshift: + command: delete + identifier: "{{ item }}" + skip_final_cluster_snapshot: true + wait: yes + wait_timeout: 1200 + register: cleanup + ignore_errors: yes + retries: 10 + delay: 10 + until: cleanup is success + loop: + - "{{ redshift_cluster_name }}" + - "{{ redshift_cluster_name }}-modified" diff --git a/ansible_collections/community/aws/tests/integration/targets/redshift_subnet_group/aliases b/ansible_collections/community/aws/tests/integration/targets/redshift_subnet_group/aliases new file mode 100644 index 000000000..4ef4b2067 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/redshift_subnet_group/aliases @@ -0,0 +1 @@ +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/redshift_subnet_group/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/redshift_subnet_group/defaults/main.yml new file mode 100644 index 000000000..ea8921880 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/redshift_subnet_group/defaults/main.yml @@ -0,0 +1,42 @@ +--- +availability_zone: '{{ ec2_availability_zone_names[0] }}' + +vpc_name: '{{ resource_prefix }}' +subnet_name_a: '{{ resource_prefix }}-a' +subnet_name_b: '{{ resource_prefix }}-b' +subnet_name_c: '{{ resource_prefix }}-c' +subnet_name_d: '{{ resource_prefix }}-d' + +vpc_cidr: '10.{{ 256 | random(seed=resource_prefix) }}.0.0/16' +subnet_cidr_a: '10.{{ 256 | random(seed=resource_prefix) }}.1.0/24' +subnet_cidr_b: '10.{{ 256 | random(seed=resource_prefix) }}.2.0/24' +subnet_cidr_c: '10.{{ 256 | random(seed=resource_prefix) }}.3.0/24' +subnet_cidr_d: '10.{{ 256 | random(seed=resource_prefix) }}.4.0/24' + +subnet_zone_a: '{{ ec2_availability_zone_names[0] }}' +subnet_zone_b: '{{ ec2_availability_zone_names[1] }}' +subnet_zone_c: '{{ ec2_availability_zone_names[0] }}' +subnet_zone_d: '{{ ec2_availability_zone_names[1] }}' + +group_name: '{{ resource_prefix }}' +description_default: 'Subnet Description' +description_updated: 'updated subnet description' + +# Tagging not currently supported, planned with boto3 upgrade +tags_default: + snake_case_key: snake_case_value + camelCaseKey: camelCaseValue + PascalCaseKey: PascalCaseValue + 'key with spaces': value with spaces + 'Upper With Spaces': Upper With Spaces + +partial_tags: + snake_case_key: snake_case_value + camelCaseKey: camelCaseValue + +updated_tags: + updated_snake_case_key: updated_snake_case_value + updatedCamelCaseKey: updatedCamelCaseValue + UpdatedPascalCaseKey: UpdatedPascalCaseValue + 'updated key with spaces': updated value with spaces + 'updated Upper With Spaces': Updated Upper With Spaces diff --git a/ansible_collections/community/aws/tests/integration/targets/redshift_subnet_group/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/redshift_subnet_group/meta/main.yml new file mode 100644 index 000000000..1471b11f6 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/redshift_subnet_group/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - setup_ec2_facts diff --git a/ansible_collections/community/aws/tests/integration/targets/redshift_subnet_group/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/redshift_subnet_group/tasks/main.yml new file mode 100644 index 000000000..e15ee9b93 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/redshift_subnet_group/tasks/main.yml @@ -0,0 +1,692 @@ +--- +# redshift_subnet_group integration tests +# +# Current module limitations: +# - check_mode not supported +# - Tagging not supported +# - Module is not idempotent +# - Returned values *very* limited +# +- module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + block: + + # ============================================================ + + - name: Create Subnet Group with no subnets - check_mode + redshift_subnet_group: + state: present + name: '{{ group_name }}' + register: create_group + ignore_errors: True + check_mode: True + + - name: Check result - Create Subnet Group with no subnets - check_mode + assert: + that: + - create_group is failed + # Check we caught the issue before trying to create + - '"CreateClusterSubnetGroup" not in create_group.resource_actions' + # Check that we don't refer to the boto3 parameter + - '"subnetIds" not in create_group.msg' + # Loosely check the message + - '"subnet" in create_group.msg' + - '"At least" in create_group.msg' + + - name: Create Subnet Group with no subnets + redshift_subnet_group: + state: present + name: '{{ group_name }}' + register: create_group + ignore_errors: True + + - name: Check result - Create Subnet Group with no subnets + assert: + that: + - create_group is failed + # Check we caught the issue before trying to create + - '"CreateClusterSubnetGroup" not in create_group.resource_actions' + # Check that we don't refer to the boto3 parameter + - '"subnetIds" not in create_group.msg' + # Loosely check the message + - '"subnet" in create_group.msg' + - '"At least" in create_group.msg' + + # ============================================================ + # Setup infra needed for tests + - name: create a VPC + ec2_vpc_net: + state: present + name: '{{ vpc_name }}' + cidr_block: '{{ vpc_cidr }}' + tags: + TestPrefix: '{{ resource_prefix }}' + register: vpc_result + + - name: create subnets + ec2_vpc_subnet: + state: present + cidr: '{{ item.cidr }}' + az: '{{ item.zone }}' + vpc_id: '{{ vpc_result.vpc.id }}' + tags: + Name: '{{ item.name }}' + TestPrefix: '{{ resource_prefix }}' + register: vpc_subnet_create + loop: + - name: '{{ subnet_name_a }}' + cidr: '{{ subnet_cidr_a }}' + zone: '{{ subnet_zone_a }}' + - name: '{{ subnet_name_b }}' + cidr: '{{ subnet_cidr_b }}' + zone: '{{ subnet_zone_b }}' + - name: '{{ subnet_name_c }}' + cidr: '{{ subnet_cidr_c }}' + zone: '{{ subnet_zone_c }}' + - name: '{{ subnet_name_d }}' + cidr: '{{ subnet_cidr_d }}' + zone: '{{ subnet_zone_d }}' + + - name: Store IDs of subnets and VPC + set_fact: + vpc_id: '{{ vpc_result.vpc.id }}' + subnet_id_a: '{{ vpc_subnet_create.results[0].subnet.id }}' + subnet_id_b: '{{ vpc_subnet_create.results[1].subnet.id }}' + subnet_id_c: '{{ vpc_subnet_create.results[2].subnet.id }}' + subnet_id_d: '{{ vpc_subnet_create.results[3].subnet.id }}' + + # ============================================================ + + - name: Create Subnet Group - check_mode + redshift_subnet_group: + state: present + group_name: '{{ group_name }}' + group_description: '{{ description_default }}' + group_subnets: + - '{{ subnet_id_a }}' + - '{{ subnet_id_b }}' + register: create_group + check_mode: True + + - name: Check result - Create Subnet Group - check_mode + assert: + that: + - create_group is successful + - create_group is changed + + - name: Create Subnet Group + redshift_subnet_group: + state: present + group_name: '{{ group_name }}' + group_description: '{{ description_default }}' + group_subnets: + - '{{ subnet_id_a }}' + - '{{ subnet_id_b }}' + register: create_group + + - name: Check result - Create Subnet Group + assert: + that: + - create_group is successful + - create_group is changed + - '"group" in create_group' + - '"name" in create_group.group' + - '"vpc_id" in create_group.group' + - create_group.group.name == group_name + - create_group.group.vpc_id == vpc_id + - '"cluster_subnet_group" in create_group' + - '"description" in create_group.cluster_subnet_group' + - '"subnet_ids" in create_group.cluster_subnet_group' + - '"vpc_id" in create_group.cluster_subnet_group' + - create_group.cluster_subnet_group.name == group_name + - create_group.cluster_subnet_group.description == description_default + - subnet_id_a in create_group.cluster_subnet_group.subnet_ids + - subnet_id_b in create_group.cluster_subnet_group.subnet_ids + - subnet_id_c not in create_group.cluster_subnet_group.subnet_ids + - subnet_id_d not in create_group.cluster_subnet_group.subnet_ids + - create_group.cluster_subnet_group.vpc_id == vpc_id + + - name: Create Subnet Group - idempotency - check_mode + redshift_subnet_group: + state: present + group_name: '{{ group_name }}' + group_description: '{{ description_default }}' + group_subnets: + - '{{ subnet_id_a }}' + - '{{ subnet_id_b }}' + register: create_group + check_mode: True + + - name: Check result - Create Subnet Group - idempotency - check_mode + assert: + that: + - create_group is successful + - create_group is not changed + + - name: Create Subnet Group - idempotency + redshift_subnet_group: + state: present + group_name: '{{ group_name }}' + group_description: '{{ description_default }}' + group_subnets: + - '{{ subnet_id_a }}' + - '{{ subnet_id_b }}' + register: create_group + + - name: Check result - Create Subnet Group - idempotency + assert: + that: + - create_group is successful + - create_group is not changed + - '"group" in create_group' + - '"name" in create_group.group' + - '"vpc_id" in create_group.group' + - create_group.group.name == group_name + - create_group.group.vpc_id == vpc_id + - '"cluster_subnet_group" in create_group' + - '"description" in create_group.cluster_subnet_group' + - '"subnet_ids" in create_group.cluster_subnet_group' + - '"vpc_id" in create_group.cluster_subnet_group' + - create_group.cluster_subnet_group.name == group_name + - create_group.cluster_subnet_group.description == description_default + - subnet_id_a in create_group.cluster_subnet_group.subnet_ids + - subnet_id_b in create_group.cluster_subnet_group.subnet_ids + - subnet_id_c not in create_group.cluster_subnet_group.subnet_ids + - subnet_id_d not in create_group.cluster_subnet_group.subnet_ids + - create_group.cluster_subnet_group.vpc_id == vpc_id + + # ============================================================ + + - name: Update Subnet Group Description - check_mode + redshift_subnet_group: + state: present + group_name: '{{ group_name }}' + group_description: '{{ description_updated }}' + ## No longer mandatory + # group_subnets: + # - '{{ subnet_id_a }}' + # - '{{ subnet_id_b }}' + register: update_description + check_mode: True + + - name: Check result - Update Subnet Group Description - check_mode + assert: + that: + - update_description is successful + - update_description is changed + + - name: Update Subnet Group Description + redshift_subnet_group: + state: present + group_name: '{{ group_name }}' + group_description: '{{ description_updated }}' + ## No longer mandatory + # group_subnets: + # - '{{ subnet_id_a }}' + # - '{{ subnet_id_b }}' + register: update_description + + - name: Check result - Update Subnet Group Description + assert: + that: + - update_description is successful + - update_description is changed + - '"group" in update_description' + - '"name" in update_description.group' + - '"vpc_id" in update_description.group' + - update_description.group.name == group_name + - update_description.group.vpc_id == vpc_id + - '"cluster_subnet_group" in update_description' + - '"description" in update_description.cluster_subnet_group' + - '"subnet_ids" in update_description.cluster_subnet_group' + - '"vpc_id" in update_description.cluster_subnet_group' + - update_description.cluster_subnet_group.name == group_name + - update_description.cluster_subnet_group.description == description_updated + - subnet_id_a in update_description.cluster_subnet_group.subnet_ids + - subnet_id_b in update_description.cluster_subnet_group.subnet_ids + - subnet_id_c not in update_description.cluster_subnet_group.subnet_ids + - subnet_id_d not in update_description.cluster_subnet_group.subnet_ids + - update_description.cluster_subnet_group.vpc_id == vpc_id + + - name: Update Subnet Group Description - idempotency - check_mode + redshift_subnet_group: + state: present + group_name: '{{ group_name }}' + group_description: '{{ description_updated }}' + ## No longer mandatory + # group_subnets: + # - '{{ subnet_id_a }}' + # - '{{ subnet_id_b }}' + register: update_description + check_mode: True + + - name: Check result - Update Subnet Group Description - idempotency - check_mode + assert: + that: + - update_description is successful + - update_description is not changed + + - name: Update Subnet Group Description - idempotency + redshift_subnet_group: + state: present + group_name: '{{ group_name }}' + group_description: '{{ description_updated }}' + ## No longer mandatory + # group_subnets: + # - '{{ subnet_id_a }}' + # - '{{ subnet_id_b }}' + register: update_description + + - name: Check result - Update Subnet Group Description - idempotency + assert: + that: + - update_description is successful + - update_description is not changed + - '"group" in update_description' + - '"name" in update_description.group' + - '"vpc_id" in update_description.group' + - update_description.group.name == group_name + - update_description.group.vpc_id == vpc_id + - '"cluster_subnet_group" in update_description' + - '"description" in update_description.cluster_subnet_group' + - '"subnet_ids" in update_description.cluster_subnet_group' + - '"vpc_id" in update_description.cluster_subnet_group' + - update_description.cluster_subnet_group.name == group_name + - update_description.cluster_subnet_group.description == description_updated + - subnet_id_a in update_description.cluster_subnet_group.subnet_ids + - subnet_id_b in update_description.cluster_subnet_group.subnet_ids + - subnet_id_c not in update_description.cluster_subnet_group.subnet_ids + - subnet_id_d not in update_description.cluster_subnet_group.subnet_ids + - update_description.cluster_subnet_group.vpc_id == vpc_id + + # ============================================================ + + - name: Update Subnet Group subnets - check_mode + redshift_subnet_group: + state: present + group_name: '{{ group_name }}' + ## No longer mandatory + # group_description: '{{ description_updated }}' + group_subnets: + - '{{ subnet_id_c }}' + - '{{ subnet_id_d }}' + register: update_subnets + check_mode: True + + - name: Check result - Update Subnet Group subnets - check_mode + assert: + that: + - update_subnets is successful + - update_subnets is changed + + - name: Update Subnet Group subnets + redshift_subnet_group: + state: present + group_name: '{{ group_name }}' + ## No longer mandatory + # group_description: '{{ description_updated }}' + group_subnets: + - '{{ subnet_id_c }}' + - '{{ subnet_id_d }}' + register: update_subnets + + - name: Check result - Update Subnet Group subnets + assert: + that: + - update_subnets is successful + - update_subnets is changed + - '"group" in update_subnets' + - '"name" in update_subnets.group' + - '"vpc_id" in update_subnets.group' + - update_subnets.group.name == group_name + - update_subnets.group.vpc_id == vpc_id + - '"cluster_subnet_group" in update_subnets' + - '"description" in update_subnets.cluster_subnet_group' + - '"subnet_ids" in update_subnets.cluster_subnet_group' + - '"vpc_id" in update_subnets.cluster_subnet_group' + - update_subnets.cluster_subnet_group.name == group_name + - update_subnets.cluster_subnet_group.description == description_updated + - subnet_id_a not in update_subnets.cluster_subnet_group.subnet_ids + - subnet_id_b not in update_subnets.cluster_subnet_group.subnet_ids + - subnet_id_c in update_subnets.cluster_subnet_group.subnet_ids + - subnet_id_d in update_subnets.cluster_subnet_group.subnet_ids + - update_subnets.cluster_subnet_group.vpc_id == vpc_id + + - name: Update Subnet Group subnets - idempotency - check_mode + redshift_subnet_group: + state: present + group_name: '{{ group_name }}' + ## No longer mandatory + # group_description: '{{ description_updated }}' + group_subnets: + - '{{ subnet_id_c }}' + - '{{ subnet_id_d }}' + register: update_subnets + check_mode: True + + - name: Check result - Update Subnet Group subnets - idempotency - check_mode + assert: + that: + - update_subnets is successful + - update_subnets is not changed + + - name: Update Subnet Group subnets - idempotency + redshift_subnet_group: + state: present + group_name: '{{ group_name }}' + ## No longer mandatory + # group_description: '{{ description_updated }}' + group_subnets: + - '{{ subnet_id_c }}' + - '{{ subnet_id_d }}' + register: update_subnets + + - name: Check result - Update Subnet Group subnets - idempotency + assert: + that: + - update_subnets is successful + - update_subnets is not changed + - '"group" in update_subnets' + - '"name" in update_subnets.group' + - '"vpc_id" in update_subnets.group' + - update_subnets.group.name == group_name + - update_subnets.group.vpc_id == vpc_id + - '"cluster_subnet_group" in update_subnets' + - '"description" in update_subnets.cluster_subnet_group' + - '"subnet_ids" in update_subnets.cluster_subnet_group' + - '"vpc_id" in update_subnets.cluster_subnet_group' + - update_subnets.cluster_subnet_group.name == group_name + - update_subnets.cluster_subnet_group.description == description_updated + - subnet_id_a not in update_subnets.cluster_subnet_group.subnet_ids + - subnet_id_b not in update_subnets.cluster_subnet_group.subnet_ids + - subnet_id_c in update_subnets.cluster_subnet_group.subnet_ids + - subnet_id_d in update_subnets.cluster_subnet_group.subnet_ids + - update_subnets.cluster_subnet_group.vpc_id == vpc_id + + # ============================================================ + + - name: Delete Subnet Group - check_mode + redshift_subnet_group: + state: absent + group_name: '{{ group_name }}' + register: delete_group + check_mode: True + + - name: Check result - Delete Subnet Group - check_mode + assert: + that: + - delete_group is changed + + - name: Delete Subnet Group + redshift_subnet_group: + state: absent + group_name: '{{ group_name }}' + register: delete_group + + - name: Check result - Delete Subnet Group + assert: + that: + - delete_group is changed + + - name: Delete Subnet Group - idempotency - check_mode + redshift_subnet_group: + state: absent + group_name: '{{ group_name }}' + register: delete_group + check_mode: True + + - name: Check result - Delete Subnet Group - idempotency - check_mode + assert: + that: + - delete_group is not changed + + - name: Delete Subnet Group - idempotency + redshift_subnet_group: + state: absent + group_name: '{{ group_name }}' + register: delete_group + + - name: Check result - Delete Subnet Group - idempotency + assert: + that: + - delete_group is not changed + + # ============================================================ + + - name: Create minimal Subnet Group - check_mode + redshift_subnet_group: + state: present + name: '{{ group_name }}' + subnets: + - '{{ subnet_id_a }}' + register: create_group + check_mode: True + + - name: Check result - Create minimal Subnet Group - check_mode + assert: + that: + - create_group is successful + - create_group is changed + + - name: Create minimal Subnet Group + redshift_subnet_group: + state: present + name: '{{ group_name }}' + subnets: + - '{{ subnet_id_a }}' + register: create_group + + - name: Check result - Create minimal Subnet Group + assert: + that: + - create_group is successful + - create_group is changed + - '"group" in create_group' + - '"name" in create_group.group' + - '"vpc_id" in create_group.group' + - create_group.group.name == group_name + - create_group.group.vpc_id == vpc_id + - '"cluster_subnet_group" in create_group' + - '"description" in create_group.cluster_subnet_group' + - '"subnet_ids" in create_group.cluster_subnet_group' + - '"vpc_id" in create_group.cluster_subnet_group' + - create_group.cluster_subnet_group.name == group_name + - create_group.cluster_subnet_group.description == group_name + - subnet_id_a in create_group.cluster_subnet_group.subnet_ids + - subnet_id_b not in create_group.cluster_subnet_group.subnet_ids + - subnet_id_c not in create_group.cluster_subnet_group.subnet_ids + - subnet_id_d not in create_group.cluster_subnet_group.subnet_ids + - create_group.cluster_subnet_group.vpc_id == vpc_id + + - name: Create minimal Subnet Group - idempotency - check_mode + redshift_subnet_group: + state: present + name: '{{ group_name }}' + subnets: + - '{{ subnet_id_a }}' + register: create_group + check_mode: True + + - name: Check result - Create minimal Subnet Group - idempotency - check_mode + assert: + that: + - create_group is successful + - create_group is not changed + + - name: Create minimal Subnet Group - idempotency + redshift_subnet_group: + state: present + name: '{{ group_name }}' + subnets: + - '{{ subnet_id_a }}' + register: create_group + + - name: Check result - Create minimal Subnet Group - idempotency + assert: + that: + - create_group is successful + - create_group is not changed + - '"group" in create_group' + - '"name" in create_group.group' + - '"vpc_id" in create_group.group' + - create_group.group.name == group_name + - create_group.group.vpc_id == vpc_id + - '"cluster_subnet_group" in create_group' + - '"description" in create_group.cluster_subnet_group' + - '"subnet_ids" in create_group.cluster_subnet_group' + - '"vpc_id" in create_group.cluster_subnet_group' + - create_group.cluster_subnet_group.name == group_name + - create_group.cluster_subnet_group.description == group_name + - subnet_id_a in create_group.cluster_subnet_group.subnet_ids + - subnet_id_b not in create_group.cluster_subnet_group.subnet_ids + - subnet_id_c not in create_group.cluster_subnet_group.subnet_ids + - subnet_id_d not in create_group.cluster_subnet_group.subnet_ids + - create_group.cluster_subnet_group.vpc_id == vpc_id + + # ============================================================ + + - name: Full Update Subnet Group - check_mode + redshift_subnet_group: + state: present + name: '{{ group_name }}' + description: '{{ description_updated }}' + subnets: + - '{{ subnet_id_a }}' + - '{{ subnet_id_b }}' + register: update_complex + check_mode: True + + - name: Check result - Full Update Subnet Group - check_mode + assert: + that: + - update_complex is successful + - update_complex is changed + + - name: Full Update Subnet Group + redshift_subnet_group: + state: present + name: '{{ group_name }}' + description: '{{ description_updated }}' + subnets: + - '{{ subnet_id_a }}' + - '{{ subnet_id_b }}' + register: update_complex + + - name: Check result - Full Update Subnet Group + assert: + that: + - update_complex is successful + - update_complex is changed + - '"group" in update_complex' + - '"name" in update_complex.group' + - '"vpc_id" in update_complex.group' + - update_complex.group.name == group_name + - update_complex.group.vpc_id == vpc_id + - '"cluster_subnet_group" in update_complex' + - '"description" in update_complex.cluster_subnet_group' + - '"subnet_ids" in update_complex.cluster_subnet_group' + - '"vpc_id" in update_complex.cluster_subnet_group' + - update_complex.cluster_subnet_group.name == group_name + - update_complex.cluster_subnet_group.description == description_updated + - subnet_id_a in update_complex.cluster_subnet_group.subnet_ids + - subnet_id_b in update_complex.cluster_subnet_group.subnet_ids + - subnet_id_c not in update_complex.cluster_subnet_group.subnet_ids + - subnet_id_d not in update_complex.cluster_subnet_group.subnet_ids + - update_complex.cluster_subnet_group.vpc_id == vpc_id + + - name: Full Update Subnet Group - idempotency - check_mode + redshift_subnet_group: + state: present + name: '{{ group_name }}' + description: '{{ description_updated }}' + subnets: + - '{{ subnet_id_a }}' + - '{{ subnet_id_b }}' + register: update_complex + check_mode: True + + - name: Check result - Full Update Subnet Group - idempotency - check_mode + assert: + that: + - update_complex is successful + - update_complex is not changed + + - name: Full Update Subnet Group - idempotency + redshift_subnet_group: + state: present + name: '{{ group_name }}' + description: '{{ description_updated }}' + subnets: + - '{{ subnet_id_a }}' + - '{{ subnet_id_b }}' + register: update_complex + + - name: Check result - Full Update Subnet Group - idempotency + assert: + that: + - update_complex is successful + - update_complex is not changed + - '"group" in update_complex' + - '"name" in update_complex.group' + - '"vpc_id" in update_complex.group' + - update_complex.group.name == group_name + - update_complex.group.vpc_id == vpc_id + - '"cluster_subnet_group" in update_complex' + - '"description" in update_complex.cluster_subnet_group' + - '"subnet_ids" in update_complex.cluster_subnet_group' + - '"vpc_id" in update_complex.cluster_subnet_group' + - update_complex.cluster_subnet_group.name == group_name + - update_complex.cluster_subnet_group.description == description_updated + - subnet_id_a in update_complex.cluster_subnet_group.subnet_ids + - subnet_id_b in update_complex.cluster_subnet_group.subnet_ids + - subnet_id_c not in update_complex.cluster_subnet_group.subnet_ids + - subnet_id_d not in update_complex.cluster_subnet_group.subnet_ids + - update_complex.cluster_subnet_group.vpc_id == vpc_id + + # ============================================================ + + - name: Delete Subnet Group + redshift_subnet_group: + state: absent + name: '{{ group_name }}' + register: delete_group + + - name: Check result - Delete Subnet Group + assert: + that: + - delete_group is changed + + always: + + ################################################ + # TEARDOWN STARTS HERE + ################################################ + + - name: Delete Subnet Group + redshift_subnet_group: + state: absent + group_name: '{{ group_name }}' + ignore_errors: True + + - name: tidy up subnet + ec2_vpc_subnet: + state: absent + cidr: '{{ item }}' + vpc_id: '{{ vpc_result.vpc.id }}' + loop: + - '{{ subnet_cidr_a }}' + - '{{ subnet_cidr_b }}' + - '{{ subnet_cidr_c }}' + - '{{ subnet_cidr_d }}' + ignore_errors: True + + - name: tidy up VPC + ec2_vpc_net: + state: absent + name: '{{ vpc_name }}' + cidr_block: '{{ vpc_cidr }}' + ignore_errors: True diff --git a/ansible_collections/community/aws/tests/integration/targets/s3_bucket_info/aliases b/ansible_collections/community/aws/tests/integration/targets/s3_bucket_info/aliases new file mode 100644 index 000000000..4ef4b2067 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/s3_bucket_info/aliases @@ -0,0 +1 @@ +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/s3_bucket_info/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/s3_bucket_info/defaults/main.yml new file mode 100644 index 000000000..464c0a299 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/s3_bucket_info/defaults/main.yml @@ -0,0 +1,5 @@ +--- +name_pattern: "testbucket-ansible-integration" +testing_buckets: + - "{{ tiny_prefix }}-{{ name_pattern }}-1" + - "{{ tiny_prefix }}-{{ name_pattern }}-2" diff --git a/ansible_collections/community/aws/tests/integration/targets/s3_bucket_info/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/s3_bucket_info/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/s3_bucket_info/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/s3_bucket_info/tasks/basic.yml b/ansible_collections/community/aws/tests/integration/targets/s3_bucket_info/tasks/basic.yml new file mode 100644 index 000000000..bf09665af --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/s3_bucket_info/tasks/basic.yml @@ -0,0 +1,72 @@ +--- +- name: Get simple S3 bucket list + aws_s3_bucket_info: + register: bucket_list + +- name: Assert result.changed == False and bucket list was retrieved + assert: + that: + - bucket_list.changed == False + - bucket_list.buckets + +- name: Get complex S3 bucket list + aws_s3_bucket_info: + name_filter: "{{ name_pattern }}" + bucket_facts: + bucket_accelerate_configuration: true + bucket_acl: true + bucket_cors: true + bucket_encryption: true + bucket_lifecycle_configuration: true + bucket_location: true + bucket_logging: true + bucket_notification_configuration: true + bucket_policy: true + bucket_policy_status: true + bucket_replication: true + bucket_request_payment: true + bucket_tagging: true + bucket_website: true + public_access_block: true + transform_location: true + register: bucket_list + +- name: Assert that buckets list contains requested bucket facts + assert: + that: + - item.name is search(name_pattern) + - item.bucket_accelerate_configuration is defined + - item.bucket_acl is defined + - item.bucket_cors is defined + - item.bucket_encryption is defined + - item.bucket_lifecycle_configuration is defined + - item.bucket_location is defined + - item.bucket_logging is defined + - item.bucket_notification_configuration is defined + - item.bucket_policy is defined + - item.bucket_policy_status is defined + - item.bucket_replication is defined + - item.bucket_request_payment is defined + - item.bucket_tagging is defined + - item.bucket_website is defined + - item.public_access_block is defined + loop: "{{ bucket_list.buckets }}" + loop_control: + label: "{{ item.name }}" + +- name: Assert that retrieved bucket facts contains valid data + assert: + that: + - item.bucket_acl.Owner is defined + - item.bucket_tagging.snake_case is defined + - item.bucket_tagging.CamelCase is defined + - item.bucket_tagging["lowercase spaced"] is defined + - item.bucket_tagging["Title Case"] is defined + - item.bucket_tagging.snake_case == 'simple_snake_case' + - item.bucket_tagging.CamelCase == 'SimpleCamelCase' + - item.bucket_tagging["lowercase spaced"] == 'hello cruel world' + - item.bucket_tagging["Title Case"] == 'Hello Cruel World' + - item.bucket_location.LocationConstraint == aws_region + loop: "{{ bucket_list.buckets }}" + loop_control: + label: "{{ item.name }}" diff --git a/ansible_collections/community/aws/tests/integration/targets/s3_bucket_info/tasks/bucket_ownership_controls.yml b/ansible_collections/community/aws/tests/integration/targets/s3_bucket_info/tasks/bucket_ownership_controls.yml new file mode 100644 index 000000000..3acd99cf6 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/s3_bucket_info/tasks/bucket_ownership_controls.yml @@ -0,0 +1,81 @@ +--- +- name: Get S3 bucket ownership controls + aws_s3_bucket_info: + name_filter: "{{ name_pattern }}" + bucket_facts: + bucket_ownership_controls: true + transform_location: true + register: bucket_list + +- name: Assert that buckets list contains requested bucket facts + assert: + that: + - item.name is search(name_pattern) + - item.bucket_ownership_controls is defined + loop: "{{ bucket_list.buckets }}" + loop_control: + label: "{{ item.name }}" + +- name: Get complex S3 bucket list (including ownership controls) + aws_s3_bucket_info: + name_filter: "{{ name_pattern }}" + bucket_facts: + bucket_accelerate_configuration: true + bucket_acl: true + bucket_cors: true + bucket_encryption: true + bucket_lifecycle_configuration: true + bucket_location: true + bucket_logging: true + bucket_notification_configuration: true + bucket_ownership_controls: true + bucket_policy: true + bucket_policy_status: true + bucket_replication: true + bucket_request_payment: true + bucket_tagging: true + bucket_website: true + public_access_block: true + transform_location: true + register: bucket_list + +- name: Assert that buckets list contains requested bucket facts + assert: + that: + - item.name is search(name_pattern) + - item.bucket_accelerate_configuration is defined + - item.bucket_acl is defined + - item.bucket_cors is defined + - item.bucket_encryption is defined + - item.bucket_lifecycle_configuration is defined + - item.bucket_location is defined + - item.bucket_logging is defined + - item.bucket_notification_configuration is defined + - item.bucket_ownership_controls is defined + - item.bucket_policy is defined + - item.bucket_policy_status is defined + - item.bucket_replication is defined + - item.bucket_request_payment is defined + - item.bucket_tagging is defined + - item.bucket_website is defined + - item.public_access_block is defined + loop: "{{ bucket_list.buckets }}" + loop_control: + label: "{{ item.name }}" + +- name: Assert that retrieved bucket facts contains valid data + assert: + that: + - item.bucket_acl.Owner is defined + - item.bucket_tagging.snake_case is defined + - item.bucket_tagging.CamelCase is defined + - item.bucket_tagging["lowercase spaced"] is defined + - item.bucket_tagging["Title Case"] is defined + - item.bucket_tagging.snake_case == 'simple_snake_case' + - item.bucket_tagging.CamelCase == 'SimpleCamelCase' + - item.bucket_tagging["lowercase spaced"] == 'hello cruel world' + - item.bucket_tagging["Title Case"] == 'Hello Cruel World' + - item.bucket_location.LocationConstraint == aws_region + loop: "{{ bucket_list.buckets }}" + loop_control: + label: "{{ item.name }}" diff --git a/ansible_collections/community/aws/tests/integration/targets/s3_bucket_info/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/s3_bucket_info/tasks/main.yml new file mode 100644 index 000000000..47d24cd0e --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/s3_bucket_info/tasks/main.yml @@ -0,0 +1,30 @@ +--- +- name: Test community.aws.aws_s3_bucket_info + module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + block: + - name: Create a simple s3_bucket + s3_bucket: + name: "{{ item }}" + state: present + tags: + "lowercase spaced": "hello cruel world" + "Title Case": "Hello Cruel World" + CamelCase: "SimpleCamelCase" + snake_case: "simple_snake_case" + register: output + loop: "{{ testing_buckets }}" + + - include_tasks: basic.yml + - include_tasks: bucket_ownership_controls.yml + + always: + - name: Delete simple s3_buckets + s3_bucket: + name: "{{ item }}" + state: absent + loop: "{{ testing_buckets }}" diff --git a/ansible_collections/community/aws/tests/integration/targets/s3_bucket_notification/aliases b/ansible_collections/community/aws/tests/integration/targets/s3_bucket_notification/aliases new file mode 100644 index 000000000..4ef4b2067 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/s3_bucket_notification/aliases @@ -0,0 +1 @@ +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/s3_bucket_notification/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/s3_bucket_notification/defaults/main.yml new file mode 100644 index 000000000..859da227e --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/s3_bucket_notification/defaults/main.yml @@ -0,0 +1,11 @@ +--- +# defaults file for s3_bucket_notifications integration test +lambda_function_name: "{{ resource_prefix }}" +# IAM role names have to be less than 64 characters +# we hash the resource_prefix to get a shorter, unique string +bucket_name: "{{ tiny_prefix }}-bucket" +lambda_name: "{{ tiny_prefix }}-lambda" +topic_name: "{{ tiny_prefix }}-topic" +queue_name: "{{ tiny_prefix }}-queue" +event_name: "{{ tiny_prefix }}-on_file_add_or_remove" +lambda_role_name: "ansible-test-{{ tiny_prefix }}-s3-notifications" diff --git a/ansible_collections/community/aws/tests/integration/targets/s3_bucket_notification/files/lambda-trust-policy.json b/ansible_collections/community/aws/tests/integration/targets/s3_bucket_notification/files/lambda-trust-policy.json new file mode 100644 index 000000000..fb84ae9de --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/s3_bucket_notification/files/lambda-trust-policy.json @@ -0,0 +1,12 @@ +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + }, + "Action": "sts:AssumeRole" + } + ] +} diff --git a/ansible_collections/community/aws/tests/integration/targets/s3_bucket_notification/files/mini_lambda.py b/ansible_collections/community/aws/tests/integration/targets/s3_bucket_notification/files/mini_lambda.py new file mode 100644 index 000000000..d0d08dae9 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/s3_bucket_notification/files/mini_lambda.py @@ -0,0 +1,13 @@ +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import json + + +def lambda_handler(event, context): + return { + 'statusCode': 200, + 'body': json.dumps('Hello from Lambda!') + } diff --git a/ansible_collections/community/aws/tests/integration/targets/s3_bucket_notification/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/s3_bucket_notification/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/s3_bucket_notification/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/s3_bucket_notification/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/s3_bucket_notification/tasks/main.yml new file mode 100644 index 000000000..ea7201065 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/s3_bucket_notification/tasks/main.yml @@ -0,0 +1,13 @@ +- name: test add s3 bucket notification + collections: + - amazon.aws + - community.general + module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + block: + - include_tasks: test_sns_sqs_notifications.yml + - include_tasks: test_lambda_notifications.yml diff --git a/ansible_collections/community/aws/tests/integration/targets/s3_bucket_notification/tasks/test_lambda_notifications.yml b/ansible_collections/community/aws/tests/integration/targets/s3_bucket_notification/tasks/test_lambda_notifications.yml new file mode 100644 index 000000000..23ed32e32 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/s3_bucket_notification/tasks/test_lambda_notifications.yml @@ -0,0 +1,298 @@ +--- +- name: Run s3 notification lambda target tests + block: + ## Setup test resources + - name: create minimal lambda role + iam_role: + name: "{{ lambda_role_name }}" + assume_role_policy_document: '{{ lookup("file", "lambda-trust-policy.json") }}' + create_instance_profile: false + managed_policies: + - "arn:aws:iam::aws:policy/AWSXrayWriteOnlyAccess" + register: iam_role + + - name: wait 10 seconds for role to become available + pause: + seconds: 10 + when: + - iam_role is changed + + - name: move lambda into place for archive module + copy: + src: mini_lambda.py + dest: "{{ output_dir }}/mini_lambda.py" + mode: preserve + + - name: bundle lambda into a zip + register: function_res + archive: + format: zip + path: "{{ output_dir }}/mini_lambda.py" + dest: "{{ output_dir }}/mini_lambda.zip" + + - name: register bucket + s3_bucket: + name: "{{ bucket_name }}" + state: present + register: bucket_info + + - name: register lambda + lambda: + name: '{{ lambda_name }}' + state: present + role: "{{ lambda_role_name }}" + runtime: python3.7 + zip_file: '{{function_res.dest}}' + handler: lambda_function.lambda_handler + memory_size: '128' + timeout: '30' + register: lambda_info + + ## Start lambda notification target tests + - name: register notification without invoke permissions + s3_bucket_notification: + state: present + event_name: "{{ event_name }}" + bucket_name: "{{ bucket_name }}" + lambda_function_arn: "{{ lambda_info.configuration.function_arn }}" + events: + - s3:ObjectCreated:* + - s3:ObjectRemoved:* + prefix: images/ + suffix: .jpg + register: result + ignore_errors: true + + - name: assert nice message returned + assert: + that: + - result is failed + - result.msg != 'MODULE FAILURE' + + - name: Add invocation permission of Lambda function on AWS + lambda_policy: + function_name: "{{ lambda_info.configuration.function_arn }}" + statement_id: allow_lambda_invoke + action: lambda:InvokeFunction + principal: s3.amazonaws.com + source_arn: arn:aws:s3:::{{ bucket_info.name }} + + - name: wait 10 seconds for policy update + pause: + seconds: 10 + + - name: create s3 bucket notification - check_mode + s3_bucket_notification: + state: present + event_name: "{{ event_name }}" + bucket_name: "{{ bucket_name }}" + lambda_function_arn: "{{ lambda_info.configuration.function_arn }}" + events: + - s3:ObjectCreated:* + - s3:ObjectRemoved:* + prefix: images/ + suffix: .jpg + register: result + check_mode: true + + - name: create s3 bucket notification - check_mode + assert: + that: + - result is changed + + - name: create s3 bucket notification + s3_bucket_notification: + state: present + event_name: "{{ event_name }}" + bucket_name: "{{ bucket_name }}" + lambda_function_arn: "{{ lambda_info.configuration.function_arn }}" + events: + - s3:ObjectCreated:* + - s3:ObjectRemoved:* + prefix: images/ + suffix: .jpg + register: result + + - name: create s3 bucket notification + assert: + that: + - result is changed + - result.notification_configuration.lambda_function_configurations | length == 1 + - result.notification_configuration.lambda_function_configurations[0].id == event_name + - result.notification_configuration.lambda_function_configurations[0].events | length == 2 + - result.notification_configuration.lambda_function_configurations[0].lambda_function_arn == lambda_info.configuration.function_arn + + - name: create s3 bucket notification - idempotency + s3_bucket_notification: + state: present + event_name: "{{ event_name }}" + bucket_name: "{{ bucket_name }}" + lambda_function_arn: "{{ lambda_info.configuration.function_arn }}" + events: + - s3:ObjectCreated:* + - s3:ObjectRemoved:* + prefix: images/ + suffix: .jpg + register: result + + - name: create s3 bucket notification - idempotency + assert: + that: + - result is not changed + + - name: test mutually exclusive parameters + s3_bucket_notification: + state: present + event_name: "{{ event_name }}" + bucket_name: "{{ bucket_name }}" + lambda_function_arn: "{{ lambda_info.configuration.function_arn }}" + events: + - s3:ObjectCreated:Post + prefix: photos/ + suffix: .gif + lambda_version: 0 + lambda_alias: 0 + register: result + ignore_errors: true + + - name: test mutually exclusive parameters + assert: + that: + - result is failed + - "result.msg == 'parameters are mutually exclusive: lambda_alias|lambda_version'" + + - name: test configuration change on suffix + s3_bucket_notification: + state: present + event_name: "{{ event_name }}" + bucket_name: "{{ bucket_name }}" + lambda_function_arn: "{{ lambda_info.configuration.function_arn }}" + events: + - s3:ObjectCreated:* + - s3:ObjectRemoved:* + prefix: images/ + suffix: .gif + register: result + + - name: test configuration change on suffix + assert: + that: + - result is changed + + - name: test configuration change on prefix + s3_bucket_notification: + state: present + event_name: "{{ event_name }}" + bucket_name: "{{ bucket_name }}" + lambda_function_arn: "{{ lambda_info.configuration.function_arn }}" + events: + - s3:ObjectCreated:* + - s3:ObjectRemoved:* + prefix: photos/ + suffix: .gif + register: result + + - name: test configuration change on prefix + assert: + that: + - result is changed + + - name: test configuration change on new events added + s3_bucket_notification: + state: present + event_name: "{{ event_name }}" + bucket_name: "{{ bucket_name }}" + lambda_function_arn: "{{ lambda_info.configuration.function_arn }}" + events: + - s3:ObjectCreated:* + - s3:ObjectRemoved:* + - s3:ObjectRestore:Post + prefix: photos/ + suffix: .gif + register: result + + - name: test configuration change on new events added + assert: + that: + - result is changed + - result.notification_configuration.lambda_function_configurations[0].events | length == 3 + + - name: test that event order does not result in change + s3_bucket_notification: + state: present + event_name: "{{ event_name }}" + bucket_name: "{{ bucket_name }}" + lambda_function_arn: "{{ lambda_info.configuration.function_arn }}" + events: + - s3:ObjectRestore:Post + - s3:ObjectRemoved:* + - s3:ObjectCreated:* + prefix: photos/ + suffix: .gif + register: result + + - name: test that event order does not matter + assert: + that: + - result is not changed + + - name: test configuration change on events removed + s3_bucket_notification: + state: present + event_name: "{{ event_name }}" + bucket_name: "{{ bucket_name }}" + lambda_function_arn: "{{ lambda_info.configuration.function_arn }}" + events: + - s3:ObjectCreated:Post + prefix: photos/ + suffix: .gif + register: result + + - name: test configuration change on events removed + assert: + that: + - result is changed + - result.notification_configuration.lambda_function_configurations[0].events | length == 1 + + ## Test removal of event notification + - name: test remove notification + s3_bucket_notification: + state: absent + event_name: "{{ event_name }}" + bucket_name: "{{ bucket_name }}" + register: result + + - name: test remove notification + assert: + that: + - result is changed + + - name: test that events is already removed + s3_bucket_notification: + state: absent + event_name: "{{ event_name }}" + bucket_name: "{{ bucket_name }}" + register: result + + - name: test that events is already removed + assert: + that: + - result is not changed + always: + - name: clean-up bucket + s3_bucket: + name: "{{ bucket_name }}" + state: absent + ignore_errors: true + + - name: clean-up lambda + lambda: + name: "{{ lambda_name }}" + state: absent + ignore_errors: true + + - name: cleam-up role + iam_role: + name: "{{ lambda_role_name }}" + state: absent + ignore_errors: true diff --git a/ansible_collections/community/aws/tests/integration/targets/s3_bucket_notification/tasks/test_sns_sqs_notifications.yml b/ansible_collections/community/aws/tests/integration/targets/s3_bucket_notification/tasks/test_sns_sqs_notifications.yml new file mode 100644 index 000000000..c5d6d6c1a --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/s3_bucket_notification/tasks/test_sns_sqs_notifications.yml @@ -0,0 +1,341 @@ +--- +- name: Run s3 notification SNS SQS target tests + block: + ## Setup required resources + - name: create bucket + s3_bucket: + name: "{{ bucket_name }}" + state: present + register: bucket_info + + - name: create test SNS topic + sns_topic: + name: "{{ topic_name }}" + state: present + display_name: "Test SNS topic" + policy: + Id: test-policy-sns + Version: 2012-10-17 + Statement: + - Sid: allowNotifications + Effect: Allow + Resource: "arn:aws:sns:*:*:{{ topic_name }}" + Principal: + Service: s3.amazonaws.com + Action: sns:Publish + Condition: + ArnLike: + aws:SourceArn: "arn:aws:s3:*:*:{{ bucket_name }}" + delivery_policy: + http: + defaultHealthyRetryPolicy: + minDelayTarget: 2 + maxDelayTarget: 4 + numRetries: 9 + numMaxDelayRetries: 5 + numMinDelayRetries: 2 + numNoDelayRetries: 2 + backoffFunction: "linear" + disableSubscriptionOverrides: True + defaultThrottlePolicy: + maxReceivesPerSecond: 10 + register: topic_creation + + - name: assert SNS topic creation success + assert: + that: + - topic_creation is successful + + - name: create test SQS queue + sqs_queue: + name: "{{ queue_name }}" + default_visibility_timeout: 120 + message_retention_period: 86400 + maximum_message_size: 1024 + delivery_delay: 30 + receive_message_wait_time: 20 + policy: + Version: 2012-10-17 + Id: test-policy-sqs + Statement: + - Sid: allowNotifications + Effect: Allow + Principal: + Service: s3.amazonaws.com + Action: + - SQS:SendMessage + Resource: "arn:aws:sqs:*:*:{{ queue_name }}" + Condition: + ArnLike: + aws:SourceArn: "arn:aws:s3:*:*:{{ bucket_name }}" + register: queue_creation + + - name: assert SQS queue creation success + assert: + that: + - queue_creation is successful + + ## Start tests + - name: create bucket notification - check_mode + s3_bucket_notification: + state: present + event_name: "{{ event_name }}" + bucket_name: "{{ bucket_name }}" + topic_arn: "{{ topic_creation.sns_arn }}" + events: + - s3:ObjectCreated:* + prefix: images/ + suffix: .jpg + register: result + check_mode: true + + - name: create bucket notification - check_mode + assert: + that: + - result is changed + + - name: create bucket notification + s3_bucket_notification: + state: present + event_name: "{{ event_name }}" + bucket_name: "{{ bucket_name }}" + topic_arn: "{{ topic_creation.sns_arn }}" + events: + - s3:ObjectCreated:* + prefix: images/ + suffix: .jpg + register: result + + - name: create bucket notification + assert: + that: + - result is successful + - result is changed + - result.notification_configuration.topic_configurations | length == 1 + - result.notification_configuration.topic_configurations[0].id == event_name + - result.notification_configuration.topic_configurations[0].topic_arn == topic_creation.sns_arn + + - name: create bucket notification - idempotency + s3_bucket_notification: + state: present + event_name: "{{ event_name }}" + bucket_name: "{{ bucket_name }}" + topic_arn: "{{ topic_creation.sns_arn }}" + events: + - s3:ObjectCreated:* + prefix: images/ + suffix: .jpg + register: result + + - name: create bucket notification - idempotency + assert: + that: + - result is not changed + + - name: test mutually exclusive parameters + s3_bucket_notification: + state: present + event_name: "{{ event_name }}" + bucket_name: "{{ bucket_name }}" + topic_arn: "{{ topic_creation.sns_arn }}" + queue_arn: "" + events: + - s3:ObjectCreated:Post + prefix: photos/ + suffix: .gif + register: result + ignore_errors: true + + - name: assert task failed + assert: + that: + - result is failed + - "result.msg == 'parameters are mutually exclusive: queue_arn|topic_arn|lambda_function_arn'" + + - name: test configuration change on suffix + s3_bucket_notification: + state: present + event_name: "{{ event_name }}" + bucket_name: "{{ bucket_name }}" + topic_arn: "{{ topic_creation.sns_arn }}" + events: + - s3:ObjectCreated:* + - s3:ObjectRemoved:* + prefix: images/ + suffix: .gif + register: result + + - name: test configuration change on suffix + assert: + that: + - result is changed + - result.notification_configuration.topic_configurations[0].filter.key.filter_rules[1].value == '.gif' + + - name: test configuration change on prefix + s3_bucket_notification: + state: present + event_name: "{{ event_name }}" + bucket_name: "{{ bucket_name }}" + topic_arn: "{{ topic_creation.sns_arn }}" + events: + - s3:ObjectCreated:* + - s3:ObjectRemoved:* + prefix: photos/ + suffix: .gif + register: result + + - name: test configuration change on prefix + assert: + that: + - result is changed + - result.notification_configuration.topic_configurations[0].filter.key.filter_rules[0].value == 'photos/' + + - name: test configuration change on new events added + s3_bucket_notification: + state: present + event_name: "{{ event_name }}" + bucket_name: "{{ bucket_name }}" + topic_arn: "{{ topic_creation.sns_arn }}" + events: + - s3:ObjectCreated:* + - s3:ObjectRemoved:* + - s3:ObjectRestore:Post + prefix: photos/ + suffix: .gif + register: result + + - name: test configuration change on new events added + assert: + that: + - result is changed + - result.notification_configuration.topic_configurations[0].events | length == 3 + + - name: test configuration change on events removed + s3_bucket_notification: + state: present + event_name: "{{ event_name }}" + bucket_name: "{{ bucket_name }}" + topic_arn: "{{ topic_creation.sns_arn }}" + events: + - s3:ObjectCreated:Post + prefix: photos/ + suffix: .gif + register: result + + - name: test configuration change on events removed + assert: + that: + - result is changed + - result.notification_configuration.topic_configurations[0].events | length == 1 + + ## Test change of event notification target + - name: test changing from sns to sqs target - check_mode + s3_bucket_notification: + state: present + event_name: "{{ event_name }}" + bucket_name: "{{ bucket_name }}" + queue_arn: "{{ queue_creation.queue_arn }}" + events: + - s3:ObjectCreated:Post + prefix: photos/ + suffix: .gif + register: result + check_mode: yes + + - name: test changing from sns to sqs target - check_mode + assert: + that: + - result is changed + + - name: test changing from sns to sqs target + s3_bucket_notification: + state: present + event_name: "{{ event_name }}" + bucket_name: "{{ bucket_name }}" + queue_arn: "{{ queue_creation.queue_arn }}" + events: + - s3:ObjectCreated:Post + prefix: photos/ + suffix: .gif + register: result + + - name: test changing from sns to sqs target + assert: + that: + - result is changed + - result.notification_configuration.queue_configurations | length == 1 + - result.notification_configuration.queue_configurations[0].queue_arn == queue_creation.queue_arn + + - name: test changing from sns to sqs target - idempotency + s3_bucket_notification: + state: present + event_name: "{{ event_name }}" + bucket_name: "{{ bucket_name }}" + queue_arn: "{{ queue_creation.queue_arn }}" + events: + - s3:ObjectCreated:Post + prefix: photos/ + suffix: .gif + register: result + + - name: test changing from sns to sqs target - idempotency + assert: + that: + - result is not changed + + ## Test removal of event notification + - name: remove notification - check_mode + s3_bucket_notification: + state: absent + event_name: "{{ event_name }}" + bucket_name: "{{ bucket_name }}" + register: result + check_mode: yes + + - name: remove notification - check_mode + assert: + that: + - result is changed + + - name: remove notification + s3_bucket_notification: + state: absent + event_name: "{{ event_name }}" + bucket_name: "{{ bucket_name }}" + register: result + + - name: remove notification + assert: + that: + - result is changed + - result.notification_configuration.queue_configurations | length < 1 + + - name: remove notification - idempotency + s3_bucket_notification: + state: absent + event_name: "{{ event_name }}" + bucket_name: "{{ bucket_name }}" + register: result + + - name: remove notification - idempotency + assert: + that: + - result is not changed + always: + - name: clean-up bucket + s3_bucket: + name: "{{ bucket_name }}" + state: absent + ignore_errors: true + + - name: clean-up topic + sns_topic: + name: "{{ topic_name }}" + state: absent + ignore_errors: true + + - name: clean-up queue + sqs_queue: + name: "{{ queue_name }}" + state: absent + ignore_errors: true diff --git a/ansible_collections/community/aws/tests/integration/targets/s3_lifecycle/aliases b/ansible_collections/community/aws/tests/integration/targets/s3_lifecycle/aliases new file mode 100644 index 000000000..4ef4b2067 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/s3_lifecycle/aliases @@ -0,0 +1 @@ +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/s3_lifecycle/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/s3_lifecycle/defaults/main.yml new file mode 100644 index 000000000..3015292c6 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/s3_lifecycle/defaults/main.yml @@ -0,0 +1 @@ +bucket_name: '{{ tiny_prefix }}-s3-lifecycle' diff --git a/ansible_collections/community/aws/tests/integration/targets/s3_lifecycle/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/s3_lifecycle/meta/main.yml new file mode 100644 index 000000000..c01990664 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/s3_lifecycle/meta/main.yml @@ -0,0 +1,4 @@ +dependencies: + - role: setup_botocore_pip + vars: + botocore_version: "1.23.12" diff --git a/ansible_collections/community/aws/tests/integration/targets/s3_lifecycle/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/s3_lifecycle/tasks/main.yml new file mode 100644 index 000000000..7a15e4b66 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/s3_lifecycle/tasks/main.yml @@ -0,0 +1,709 @@ +--- +- name: 's3_lifecycle integration tests' + collections: + - amazon.aws + module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + s3_lifecycle: + wait: true + block: + + # ============================================================ + - name: Create simple s3_bucket + s3_bucket: + name: '{{ bucket_name }}' + state: present + register: output + + - assert: + that: + - output.changed + - output.name == bucket_name + - not output.requester_pays + # ============================================================ + - name: Create a number of policies and check if all of them are created + community.aws.s3_lifecycle: + name: "{{ bucket_name }}" + rule_id: "{{ item }}" + expiration_days: 10 + prefix: "{{ item }}" + status: enabled + state: present + wait: true + register: output + loop: + - rule_1 + - rule_2 + - rule_3 + - assert: + that: + - (output.results | last).rules | length == 3 + # ============================================================ + - name: Remove previously created policies and check if all of them are removed + community.aws.s3_lifecycle: + name: "{{ bucket_name }}" + rule_id: "{{ item }}" + expiration_days: 10 + prefix: "{{ item }}" + status: enabled + state: absent + wait: true + register: output + loop: + - rule_1 + - rule_2 + - rule_3 + - assert: + that: + - (output.results | last).rules | length == 0 + # ============================================================ + - name: Create a lifecycle policy + s3_lifecycle: + name: '{{ bucket_name }}' + expiration_days: 300 + prefix: '' + register: output + + - assert: + that: + - output is changed + # ============================================================ + - name: Create a lifecycle policy (idempotency) + s3_lifecycle: + name: '{{ bucket_name }}' + expiration_days: 300 + register: output + + - assert: + that: + - output is not changed + # ============================================================ + - name: Create a second lifecycle policy + s3_lifecycle: + name: '{{ bucket_name }}' + transition_days: 30 + prefix: /something + register: output + + - assert: + that: + - output is changed + # ============================================================ + - name: Create a second lifecycle policy (idempotency) + s3_lifecycle: + name: '{{ bucket_name }}' + transition_days: 30 + prefix: /something + register: output + + - assert: + that: + - output is not changed + # ============================================================ + - name: Disable the second lifecycle policy + s3_lifecycle: + name: '{{ bucket_name }}' + status: disabled + transition_days: 30 + prefix: /something + register: output + + - assert: + that: + - output is changed + # ============================================================ + - name: Disable the second lifecycle policy (idempotency) + s3_lifecycle: + name: '{{ bucket_name }}' + status: disabled + transition_days: 30 + prefix: /something + register: output + + - assert: + that: + - output is not changed + # ============================================================ + - name: Re-enable the second lifecycle policy + s3_lifecycle: + name: '{{ bucket_name }}' + status: enabled + transition_days: 300 + prefix: /something + register: output + + - assert: + that: + - output is changed + # ============================================================ + - name: Re-enable the second lifecycle policy (idempotency) + s3_lifecycle: + name: '{{ bucket_name }}' + status: enabled + transition_days: 300 + prefix: /something + register: output + + - assert: + that: + - output is not changed + # ============================================================ + - name: Delete the second lifecycle policy + s3_lifecycle: + name: '{{ bucket_name }}' + state: absent + prefix: /something + register: output + + - assert: + that: + - output is changed + # ============================================================ + - name: Delete the second lifecycle policy (idempotency) + s3_lifecycle: + name: '{{ bucket_name }}' + state: absent + prefix: /something + register: output + + - assert: + that: + - output is not changed + # ============================================================ + - name: Create a second lifecycle policy, with infrequent access + s3_lifecycle: + name: '{{ bucket_name }}' + transition_days: 30 + storage_class: standard_ia + prefix: /something + register: output + + - assert: + that: + - output is changed + # ============================================================ + - name: Create a second lifecycle policy, with infrequent access (idempotency) + s3_lifecycle: + name: '{{ bucket_name }}' + storage_class: standard_ia + transition_days: 30 + prefix: /something + register: output + + - assert: + that: + - output is not changed + # ============================================================ + - name: Create a second lifecycle policy, with glacier + s3_lifecycle: + name: '{{ bucket_name }}' + transition_days: 300 + prefix: /something + register: output + + - assert: + that: + - output is changed + # ============================================================ + - name: Create a second lifecycle policy, with glacier (idempotency) + s3_lifecycle: + name: '{{ bucket_name }}' + transition_days: 300 + prefix: /something + register: output + + - assert: + that: + - output is not changed + # ============================================================ + - name: Create a lifecycle policy, with glacier and transition_days to 0 + s3_lifecycle: + name: '{{ bucket_name }}' + transition_days: 0 + storage_class: glacier + prefix: /something + register: output + + - assert: + that: + - output is changed + # ============================================================ + - name: Create a lifecycle policy, with glacier and transition_days to 0 (idempotency) + s3_lifecycle: + name: '{{ bucket_name }}' + transition_days: 0 + storage_class: glacier + prefix: /something + register: output + + - assert: + that: + - output is not changed + # ============================================================ + - name: Create a lifecycle policy with infrequent access + s3_lifecycle: + name: '{{ bucket_name }}' + transition_days: 30 + storage_class: standard_ia + prefix: /something + register: output + + - name: Create a second lifecycle policy, with glacier + s3_lifecycle: + name: '{{ bucket_name }}' + transition_days: 300 + prefix: /something + purge_transitions: false + register: output + + - name: Create a lifecycle policy with infrequent access (idempotency) + s3_lifecycle: + name: '{{ bucket_name }}' + storage_class: standard_ia + transition_days: 30 + prefix: /something + purge_transitions: false + register: output + + - assert: + that: + - output is not changed + + - name: Create a second lifecycle policy, with glacier (idempotency) + s3_lifecycle: + name: '{{ bucket_name }}' + transition_days: 300 + prefix: /something + purge_transitions: false + register: output + + - assert: + that: + - output is not changed + # ============================================================ + - name: Create a lifecycle policy, with noncurrent expiration + s3_lifecycle: + name: '{{ bucket_name }}' + noncurrent_version_expiration_days: 300 + prefix: /something + register: output + + - assert: + that: + - output is changed + # ============================================================ + - name: Create a lifecycle policy, with noncurrent expiration + s3_lifecycle: + name: '{{ bucket_name }}' + noncurrent_version_expiration_days: 300 + prefix: /something + register: output + + - assert: + that: + - output is not changed + # ============================================================ + - name: Create a lifecycle policy, with noncurrent transition + s3_lifecycle: + name: '{{ bucket_name }}' + noncurrent_version_transition_days: 300 + prefix: /something + register: output + + - assert: + that: + - output is changed + # ============================================================ + - name: Create a lifecycle policy, with noncurrent transitions and expirations + s3_lifecycle: + name: '{{ bucket_name }}' + noncurrent_version_transition_days: 300 + prefix: /something + register: output + + - assert: + that: + - output is not changed + # ============================================================ + - name: Create a lifecycle policy, with noncurrent transition + s3_lifecycle: + name: '{{ bucket_name }}' + noncurrent_version_transition_days: 300 + noncurrent_version_storage_class: standard_ia + prefix: /something + register: output + + - assert: + that: + - output is changed + # ============================================================ + - name: Create a lifecycle policy, with noncurrent transitions and expirations + s3_lifecycle: + name: '{{ bucket_name }}' + noncurrent_version_storage_class: standard_ia + noncurrent_version_transition_days: 300 + prefix: /something + register: output + + - assert: + that: + - output is not changed + # ============================================================ + - name: Create a lifecycle policy, with noncurrent transitions + s3_lifecycle: + name: '{{ bucket_name }}' + noncurrent_version_transitions: + - transition_days: 30 + storage_class: standard_ia + - transition_days: 60 + storage_class: onezone_ia + - transition_days: 90 + storage_class: glacier + prefix: /something + register: output + + - assert: + that: + - output is changed + # ============================================================ + - name: Create a lifecycle policy, with noncurrent transitions + s3_lifecycle: + name: '{{ bucket_name }}' + noncurrent_version_transitions: + - transition_days: 30 + storage_class: standard_ia + - transition_days: 60 + storage_class: onezone_ia + - transition_days: 90 + storage_class: glacier + prefix: /something + register: output + + - assert: + that: + - output is not changed + + # ============================================================ + - name: Create a lifecycle policy, with abort_incomplete_multipart_upload + s3_lifecycle: + name: '{{ bucket_name }}' + abort_incomplete_multipart_upload_days: 1 + prefix: /something + register: output + + - assert: + that: + - output is changed + + # ============================================================ + - name: Create a lifecycle policy, with abort_incomplete_multipart_upload (idempotency) + s3_lifecycle: + name: '{{ bucket_name }}' + abort_incomplete_multipart_upload_days: 1 + prefix: /something + register: output + + - assert: + that: + - output is not changed + + # ============================================================ + - name: Create a lifecycle policy, with expired_object_delete_marker + s3_lifecycle: + name: '{{ bucket_name }}' + expire_object_delete_marker: True + prefix: /something + register: output + + - assert: + that: + - output is changed + + - name: Create a lifecycle policy, with expired_object_delete_marker (idempotency) + s3_lifecycle: + name: '{{ bucket_name }}' + expire_object_delete_marker: True + prefix: /something + register: output + + - assert: + that: + - output is not changed + + # ============================================================ + - name: Update lifecycle policy, with noncurrent_version_expiration_days + s3_lifecycle: + name: '{{ bucket_name }}' + noncurrent_version_expiration_days: 5 + prefix: /something + register: output + + - assert: + that: + - output is changed + + - name: Update lifecycle policy, with noncurrent_version_expiration_days (idempotency) + s3_lifecycle: + name: '{{ bucket_name }}' + noncurrent_version_expiration_days: 5 + prefix: /something + register: output + + - assert: + that: + - output is not changed + + # ============================================================ + - name: Update lifecycle policy, with noncurrent_version_keep_newer + s3_lifecycle: + name: '{{ bucket_name }}' + noncurrent_version_expiration_days: 10 + noncurrent_version_keep_newer: 6 + prefix: /something + register: output + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + + - assert: + that: + - output is changed + + - name: Update lifecycle policy, with noncurrent_version_keep_newer (idempotency) + s3_lifecycle: + name: '{{ bucket_name }}' + noncurrent_version_expiration_days: 10 + noncurrent_version_keep_newer: 6 + prefix: /something + register: output + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + + - assert: + that: + - output is not changed + + # ============================================================ + # test all the examples + # Configure a lifecycle rule on a bucket to expire (delete) items with a prefix of /logs/ after 30 days + - name: example 1 + s3_lifecycle: + name: '{{ bucket_name }}' + expiration_days: 30 + prefix: /logs/ + status: enabled + state: present + register: output + - assert: + that: + - output is changed + + - name: example 1 (idempotency) + s3_lifecycle: + name: '{{ bucket_name }}' + expiration_days: 30 + prefix: /logs/ + status: enabled + state: present + register: output + - assert: + that: + - output is not changed + + # Configure a lifecycle rule to transition all items with a prefix of /logs/ to glacier after 7 days and then delete after 90 days + - name: example 2 + s3_lifecycle: + name: '{{ bucket_name }}' + transition_days: 7 + expiration_days: 90 + prefix: /logs/ + status: enabled + state: present + register: output + - assert: + that: + - output is changed + + - name: example 2 (idempotency) + s3_lifecycle: + name: '{{ bucket_name }}' + transition_days: 7 + expiration_days: 90 + prefix: /logs/ + status: enabled + state: present + register: output + - assert: + that: + - output is not changed + + # Configure a lifecycle rule to transition all items with a prefix of /logs/ to glacier on 31 Dec 2020 and then delete on 31 Dec 2030. + # Note that midnight GMT must be specified. + # Be sure to quote your date strings + - name: example 3 + s3_lifecycle: + name: '{{ bucket_name }}' + transition_date: "2020-12-30T00:00:00.000Z" + expiration_date: "2030-12-30T00:00:00.000Z" + prefix: /logs/ + status: enabled + state: present + register: output + - assert: + that: + - output is changed + + - name: example 3 (idempotency) + s3_lifecycle: + name: '{{ bucket_name }}' + transition_date: "2020-12-30T00:00:00.000Z" + expiration_date: "2030-12-30T00:00:00.000Z" + prefix: /logs/ + status: enabled + state: present + register: output + - assert: + that: + - output is not changed + + # Disable the rule created above + - name: example 4 + s3_lifecycle: + name: '{{ bucket_name }}' + prefix: /logs/ + status: disabled + state: present + register: output + - assert: + that: + - output is changed + + - name: example 4 (idempotency) + s3_lifecycle: + name: '{{ bucket_name }}' + prefix: /logs/ + status: disabled + state: present + register: output + - assert: + that: + - output is not changed + + # Delete the lifecycle rule created above + - name: example 5 + s3_lifecycle: + name: '{{ bucket_name }}' + prefix: /logs/ + state: absent + register: output + - assert: + that: + - output is changed + + - name: example 5 (idempotency) + s3_lifecycle: + name: '{{ bucket_name }}' + prefix: /logs/ + state: absent + register: output + - assert: + that: + - output is not changed + + # Configure a lifecycle rule to transition all backup files older than 31 days in /backups/ to standard infrequent access class. + - name: example 6 + s3_lifecycle: + name: '{{ bucket_name }}' + prefix: /backups/ + storage_class: standard_ia + transition_days: 31 + state: present + status: enabled + register: output + - assert: + that: + - output is changed + + - name: example 6 (idempotency) + s3_lifecycle: + name: '{{ bucket_name }}' + prefix: /backups/ + storage_class: standard_ia + transition_days: 31 + state: present + status: enabled + register: output + - assert: + that: + - output is not changed + + # Configure a lifecycle rule to transition files to infrequent access after 30 days and glacier after 90 + - name: example 7 + s3_lifecycle: + name: '{{ bucket_name }}' + prefix: /other_logs/ + state: present + status: enabled + transitions: + - transition_days: 30 + storage_class: standard_ia + - transition_days: 90 + storage_class: glacier + register: output + - assert: + that: + - output is changed + + - name: example 7 (idempotency) + s3_lifecycle: + name: '{{ bucket_name }}' + prefix: /other_logs/ + state: present + status: enabled + transitions: + - transition_days: 30 + storage_class: standard_ia + - transition_days: 90 + storage_class: glacier + register: output + - assert: + that: + - output is not changed + + # Check create and delete lifecycle policy with an empty prefix + - name: Create rule with no prefix + s3_lifecycle: + name: "{{ bucket_name }}" + rule_id: empty-prefix + state: present + status: enabled + expiration_days: 30 + register: output + - assert: + that: + - output is changed + + - name: Delete rule with no prefix + s3_lifecycle: + name: "{{ bucket_name }}" + rule_id: empty-prefix + state: absent + status: enabled + expiration_days: 30 + register: output + - assert: + that: + - output is changed + + # ============================================================ + always: + - name: Ensure all buckets are deleted + s3_bucket: + name: "{{item}}" + state: absent + ignore_errors: true + with_items: + - '{{ bucket_name }}' diff --git a/ansible_collections/community/aws/tests/integration/targets/s3_logging/aliases b/ansible_collections/community/aws/tests/integration/targets/s3_logging/aliases new file mode 100644 index 000000000..4ef4b2067 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/s3_logging/aliases @@ -0,0 +1 @@ +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/s3_logging/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/s3_logging/defaults/main.yml new file mode 100644 index 000000000..f8f1d7587 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/s3_logging/defaults/main.yml @@ -0,0 +1,4 @@ +--- +test_bucket: '{{ tiny_prefix }}-s3-logging' +log_bucket_1: '{{ tiny_prefix }}-logs-1' +log_bucket_2: '{{ tiny_prefix }}-logs-2' diff --git a/ansible_collections/community/aws/tests/integration/targets/s3_logging/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/s3_logging/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/s3_logging/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/s3_logging/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/s3_logging/tasks/main.yml new file mode 100644 index 000000000..f6c9a1710 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/s3_logging/tasks/main.yml @@ -0,0 +1,303 @@ +--- +# Integration tests for s3_logging +# +# Notes: +# - s3_logging doesn't support check_mode and the only output is 'changed' +# - During initial testing we hit issues with boto reporting +# "You must give the log-delivery group WRITE and READ_ACP permissions +# to the target bucket" +# a long term solution might be to port s3_logging to AnsibleAWSModule +# so we can add retries +# +- module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + collections: + - amazon.aws + block: + + # ============================================================ + + - name: Try to enable logging before creating target_bucket + s3_logging: + state: present + name: '{{ test_bucket }}' + register: result + ignore_errors: yes + - assert: + that: + - result is failed + + # ============================================================ + - name: Create simple s3_bucket to be logged + s3_bucket: + state: present + name: '{{ test_bucket }}' + register: output + - assert: + that: + - output is changed + - output.name == test_bucket + + - name: Create simple s3_bucket as target for logs + s3_bucket: + state: present + name: '{{ log_bucket_1 }}' + object_ownership: BucketOwnerPreferred + register: output + - assert: + that: + - output is changed + - output.name == log_bucket_1 + + - name: Create simple s3_bucket as second target for logs + s3_bucket: + state: present + name: '{{ log_bucket_2 }}' + object_ownership: BucketOwnerPreferred + register: output + - assert: + that: + - output is changed + - output.name == log_bucket_2 + +# ============================================================ + + - name: Enable logging (check_mode) + s3_logging: + state: present + name: '{{ test_bucket }}' + target_bucket: '{{ log_bucket_1 }}' + register: result + check_mode: True + - assert: + that: + - result is changed + + - name: Enable logging + s3_logging: + state: present + name: '{{ test_bucket }}' + target_bucket: '{{ log_bucket_1 }}' + register: result + - assert: + that: + - result is changed + + - name: Enable logging idempotency (check_mode) + s3_logging: + state: present + name: '{{ test_bucket }}' + target_bucket: '{{ log_bucket_1 }}' + register: result + check_mode: True + - assert: + that: + - result is not changed + + - name: Enable logging idempotency + s3_logging: + state: present + name: '{{ test_bucket }}' + target_bucket: '{{ log_bucket_1 }}' + register: result + - assert: + that: + - result is not changed + +# ============================================================ + + - name: Change logging bucket (check_mode) + s3_logging: + state: present + name: '{{ test_bucket }}' + target_bucket: '{{ log_bucket_2 }}' + register: result + check_mode: True + - assert: + that: + - result is changed + + - name: Change logging bucket + s3_logging: + state: present + name: '{{ test_bucket }}' + target_bucket: '{{ log_bucket_2 }}' + register: result + - assert: + that: + - result is changed + + - name: Change logging bucket idempotency (check_mode) + s3_logging: + state: present + name: '{{ test_bucket }}' + target_bucket: '{{ log_bucket_2 }}' + register: result + check_mode: True + - assert: + that: + - result is not changed + + - name: Change logging bucket idempotency + s3_logging: + state: present + name: '{{ test_bucket }}' + target_bucket: '{{ log_bucket_2 }}' + register: result + - assert: + that: + - result is not changed + +# ============================================================ + + - name: Change logging prefix (check_mode) + s3_logging: + state: present + name: '{{ test_bucket }}' + target_bucket: '{{ log_bucket_2 }}' + target_prefix: '/{{ resource_prefix }}/' + register: result + check_mode: True + - assert: + that: + - result is changed + + - name: Change logging prefix + s3_logging: + state: present + name: '{{ test_bucket }}' + target_bucket: '{{ log_bucket_2 }}' + target_prefix: '/{{ resource_prefix }}/' + register: result + - assert: + that: + - result is changed + + - name: Change logging prefix idempotency (check_mode) + s3_logging: + state: present + name: '{{ test_bucket }}' + target_bucket: '{{ log_bucket_2 }}' + target_prefix: '/{{ resource_prefix }}/' + register: result + check_mode: True + - assert: + that: + - result is not changed + + - name: Change logging prefix idempotency + s3_logging: + state: present + name: '{{ test_bucket }}' + target_bucket: '{{ log_bucket_2 }}' + target_prefix: '/{{ resource_prefix }}/' + register: result + - assert: + that: + - result is not changed + +# ============================================================ + + - name: Remove logging prefix (check_mode) + s3_logging: + state: present + name: '{{ test_bucket }}' + target_bucket: '{{ log_bucket_2 }}' + register: result + check_mode: True + - assert: + that: + - result is changed + + - name: Remove logging prefix + s3_logging: + state: present + name: '{{ test_bucket }}' + target_bucket: '{{ log_bucket_2 }}' + register: result + - assert: + that: + - result is changed + + - name: Remove logging prefix idempotency (check_mode) + s3_logging: + state: present + name: '{{ test_bucket }}' + target_bucket: '{{ log_bucket_2 }}' + register: result + check_mode: True + - assert: + that: + - result is not changed + + - name: Remove logging prefix idempotency + s3_logging: + state: present + name: '{{ test_bucket }}' + target_bucket: '{{ log_bucket_2 }}' + register: result + - assert: + that: + - result is not changed + +# ============================================================ + + - name: Disable logging (check_mode) + s3_logging: + state: absent + name: '{{ test_bucket }}' + register: result + check_mode: True + - assert: + that: + - result is changed + + - name: Disable logging + s3_logging: + state: absent + name: '{{ test_bucket }}' + register: result + - assert: + that: + - result is changed + + - name: Disable logging idempotency (check_mode) + s3_logging: + state: absent + name: '{{ test_bucket }}' + register: result + check_mode: True + - assert: + that: + - result is not changed + + - name: Disable logging idempotency + s3_logging: + state: absent + name: '{{ test_bucket }}' + register: result + - assert: + that: + - result is not changed + +# ============================================================ + always: + - name: Delete bucket being logged + s3_bucket: + name: '{{ test_bucket }}' + state: absent + ignore_errors: yes + - name: Delete first bucket containing logs + s3_bucket: + name: '{{ log_bucket_1 }}' + state: absent + ignore_errors: yes + - name: Delete second bucket containing logs + s3_bucket: + name: '{{ log_bucket_2 }}' + state: absent + ignore_errors: yes diff --git a/ansible_collections/community/aws/tests/integration/targets/s3_metrics_configuration/aliases b/ansible_collections/community/aws/tests/integration/targets/s3_metrics_configuration/aliases new file mode 100644 index 000000000..4ef4b2067 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/s3_metrics_configuration/aliases @@ -0,0 +1 @@ +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/s3_metrics_configuration/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/s3_metrics_configuration/defaults/main.yml new file mode 100644 index 000000000..85f5435bd --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/s3_metrics_configuration/defaults/main.yml @@ -0,0 +1,2 @@ +--- +test_bucket: '{{ tiny_prefix }}-testbucket' diff --git a/ansible_collections/community/aws/tests/integration/targets/s3_metrics_configuration/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/s3_metrics_configuration/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/s3_metrics_configuration/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/s3_metrics_configuration/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/s3_metrics_configuration/tasks/main.yml new file mode 100644 index 000000000..ba5cce9e6 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/s3_metrics_configuration/tasks/main.yml @@ -0,0 +1,243 @@ +--- +# Integration tests for s3_metrics_configuration +# +# Notes: +# - The module only outputs 'changed' since its very simple +# +- module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key | default(omit) }}' + aws_secret_key: '{{ aws_secret_key | default(omit) }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region | default(omit) }}' + collections: + - amazon.aws + block: + + # TODO: Until there's a module to get info s3 metrics configuration, awscli is needed + - name: Install awscli + pip: + state: present + name: awscli + + # ============================================================ + - name: Try to create metrics configuration for non-existing bucket + s3_metrics_configuration: + bucket_name: '{{ test_bucket }}' + id: 'EntireBucket' + state: present + register: result + ignore_errors: yes + + - assert: + that: + - result is failed + + # ============================================================ + - name: Create simple s3_bucket to act upon it + s3_bucket: + name: '{{ test_bucket }}' + state: present + register: output + + - assert: + that: + - output is changed + - output.name == test_bucket + + # ============================================================ + - name: Create a metrics configuration under check mode + s3_metrics_configuration: + bucket_name: '{{ test_bucket }}' + id: 'EntireBucket' + state: present + check_mode: yes + register: result + + - include_tasks: './s3_metrics_info.yml' + + - assert: + that: + - result is changed + - metrics_info | selectattr('Id', 'search', 'EntireBucket') | list | length == 0 + + # ============================================================ + - name: Create a metrics configuration that enables metrics for an entire bucket + s3_metrics_configuration: + bucket_name: '{{ test_bucket }}' + id: 'EntireBucket' + state: present + register: result + + - include_tasks: './s3_metrics_info.yml' + + - assert: + that: + - result is changed + - metrics_info | selectattr('Id', 'search', 'EntireBucket') | list | length == 1 + + # ============================================================ + - name: Create a metrics configuration idempotency under check mode + s3_metrics_configuration: + bucket_name: '{{ test_bucket }}' + id: 'EntireBucket' + state: present + check_mode: yes + register: result + + - assert: + that: + - result is not changed + + # ============================================================ + - name: Create a metrics configuration idempotency + s3_metrics_configuration: + bucket_name: '{{ test_bucket }}' + id: 'EntireBucket' + state: present + register: result + + - assert: + that: + - result is not changed + + # ============================================================ + - name: Put a metrics configuration that enables metrics for objects starting with a prefix + s3_metrics_configuration: + bucket_name: "{{ test_bucket }}" + id: Assets + filter_prefix: assets + state: present + register: result + + - include_tasks: './s3_metrics_info.yml' + + - assert: + that: + - result is changed + - (metrics_info | selectattr('Id', 'search', 'Assets') | list | first).Filter.Prefix == 'assets' + + # ============================================================ + - name: Update existing metrics configuration under check mode + s3_metrics_configuration: + bucket_name: "{{ test_bucket }}" + id: Assets + filter_tag: + kind: Asset + state: present + check_mode: yes + register: result + + - include_tasks: './s3_metrics_info.yml' + + - assert: + that: + - result is changed + - (metrics_info | selectattr('Id', 'search', 'Assets') | list | first).Filter.Prefix == 'assets' + - (metrics_info | selectattr('Id', 'search', 'Assets') | list | first).Filter.Tag is not defined + + # ============================================================ + - name: Update existing metrics configuration and enable metrics for objects with specific tag + s3_metrics_configuration: + bucket_name: "{{ test_bucket }}" + id: Assets + filter_tag: + kind: Asset + state: present + register: result + + - include_tasks: './s3_metrics_info.yml' + + - assert: + that: + - result is changed + - (metrics_info | selectattr('Id', 'search', 'Assets') | list | first).Filter.Prefix is not defined + - (metrics_info | selectattr('Id', 'search', 'Assets') | list | first).Filter.Tag.Key == 'kind' + - (metrics_info | selectattr('Id', 'search', 'Assets') | list | first).Filter.Tag.Value == 'Asset' + + # ============================================================ + - name: Put a metrics configuration that enables metrics for objects that start with a particular prefix and have specific tags applied + s3_metrics_configuration: + bucket_name: "{{ test_bucket }}" + id: ImportantBlueDocuments + filter_prefix: documents + filter_tags: + priority: High + class: Blue + state: present + register: result + + - include_tasks: './s3_metrics_info.yml' + + - assert: + that: + - result is changed + - (metrics_info | selectattr('Id', 'search', 'ImportantBlueDocuments') | list | first).Filter.And.Prefix == 'documents' + - (metrics_info | selectattr('Id', 'search', 'ImportantBlueDocuments') | list | first).Filter.And.Tags[0].Key == 'priority' + - (metrics_info | selectattr('Id', 'search', 'ImportantBlueDocuments') | list | first).Filter.And.Tags[0].Value == 'High' + - (metrics_info | selectattr('Id', 'search', 'ImportantBlueDocuments') | list | first).Filter.And.Tags[1].Key == 'class' + - (metrics_info | selectattr('Id', 'search', 'ImportantBlueDocuments') | list | first).Filter.And.Tags[1].Value == 'Blue' + + # ============================================================ + - name: Delete metrics configuration in check mode + s3_metrics_configuration: + bucket_name: '{{ test_bucket }}' + id: 'EntireBucket' + state: absent + check_mode: yes + register: result + + - include_tasks: './s3_metrics_info.yml' + + - assert: + that: + - result is changed + - metrics_info | selectattr('Id', 'search', 'EntireBucket') | list | length == 1 # still present + + # ============================================================ + - name: Delete metrics configuration + s3_metrics_configuration: + bucket_name: '{{ test_bucket }}' + id: 'EntireBucket' + state: absent + register: result + + - include_tasks: './s3_metrics_info.yml' + + - assert: + that: + - result is changed + - metrics_info | selectattr('Id', 'search', 'EntireBucket') | list | length == 0 + + # ============================================================ + - name: Try to delete non-existing metrics configuration + s3_metrics_configuration: + bucket_name: '{{ test_bucket }}' + id: 'EntireBucket' + state: absent + register: result + + - assert: + that: + - result is not changed + + # ============================================================ + - name: Try to delete metrics configuration for non-existing bucket + s3_metrics_configuration: + bucket_name: '{{ test_bucket }}-non-existing' + id: 'EntireBucket' + state: absent + register: result + ignore_errors: yes + + - assert: + that: + - result is failed + + # ============================================================ + always: + - name: Delete test bucket + s3_bucket: + name: '{{ test_bucket }}' + state: absent + ignore_errors: yes diff --git a/ansible_collections/community/aws/tests/integration/targets/s3_metrics_configuration/tasks/s3_metrics_info.yml b/ansible_collections/community/aws/tests/integration/targets/s3_metrics_configuration/tasks/s3_metrics_info.yml new file mode 100644 index 000000000..cca7cad05 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/s3_metrics_configuration/tasks/s3_metrics_info.yml @@ -0,0 +1,16 @@ +--- +# Utility tasks to list bucket metrics configurations +# TODO: Update this when an s3_metrics_configuration_info module exists +- name: List s3 bucket metrics configurations + command: > + aws s3api list-bucket-metrics-configurations + --bucket {{ test_bucket }} + environment: + AWS_ACCESS_KEY_ID: "{{ aws_access_key | default(omit) }}" + AWS_SECRET_ACCESS_KEY: "{{ aws_secret_key | default(omit) }}" + AWS_SESSION_TOKEN: "{{ security_token | default(omit) }}" + AWS_DEFAULT_REGION: "{{ aws_region | default(omit) }}" + register: list_comand_result + +- set_fact: + metrics_info: "{{ (list_comand_result.stdout | from_json)['MetricsConfigurationList'] | default([]) }}" diff --git a/ansible_collections/community/aws/tests/integration/targets/s3_sync/aliases b/ansible_collections/community/aws/tests/integration/targets/s3_sync/aliases new file mode 100644 index 000000000..4ef4b2067 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/s3_sync/aliases @@ -0,0 +1 @@ +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/s3_sync/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/s3_sync/defaults/main.yml new file mode 100644 index 000000000..b48f03b78 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/s3_sync/defaults/main.yml @@ -0,0 +1,3 @@ +test_bucket: "{{ tiny_prefix }}-testbucket-ansible" +test_bucket_2: "{{ tiny_prefix }}-testbucket-ansible-2" +test_bucket_3: "{{ tiny_prefix }}-testbucket-ansible-3" diff --git a/ansible_collections/community/aws/tests/integration/targets/s3_sync/files/test1.txt b/ansible_collections/community/aws/tests/integration/targets/s3_sync/files/test1.txt new file mode 100644 index 000000000..f079749c4 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/s3_sync/files/test1.txt @@ -0,0 +1 @@ +test1
\ No newline at end of file diff --git a/ansible_collections/community/aws/tests/integration/targets/s3_sync/files/test2.yml b/ansible_collections/community/aws/tests/integration/targets/s3_sync/files/test2.yml new file mode 100644 index 000000000..b80aba06e --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/s3_sync/files/test2.yml @@ -0,0 +1,2 @@ +--- +test2: example diff --git a/ansible_collections/community/aws/tests/integration/targets/s3_sync/files/test3.json b/ansible_collections/community/aws/tests/integration/targets/s3_sync/files/test3.json new file mode 100644 index 000000000..23483fb26 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/s3_sync/files/test3.json @@ -0,0 +1,3 @@ +{ + "test3": "value" +}
\ No newline at end of file diff --git a/ansible_collections/community/aws/tests/integration/targets/s3_sync/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/s3_sync/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/s3_sync/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/s3_sync/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/s3_sync/tasks/main.yml new file mode 100644 index 000000000..08496cd74 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/s3_sync/tasks/main.yml @@ -0,0 +1,220 @@ +--- +- name: S3 bucket creation + collections: + - amazon.aws + - community.general + module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + block: + # ============================================================ + - name: Create simple s3_bucket + s3_bucket: + name: "{{ test_bucket }}" + state: present + public_access: + block_public_acls: false + object_ownership: BucketOwnerPreferred + register: output + + - assert: + that: + - output.changed + - output.name == "{{ test_bucket }}" + - not output.requester_pays + # ============================================================ + - name: Prepare fixtures folder + file: + path: "{{ output_dir }}/s3_sync" + state: directory + mode: '0755' + + - name: Prepare files to sync + copy: + src: "{{ item }}" + dest: "{{ output_dir }}/s3_sync/{{ item }}" + mode: preserve + with_items: + - test1.txt + - test2.yml + - test3.json + + - name: Prepare file with size bigger than chunk size + shell: | + dd if=/dev/zero of=test4.txt bs=1M count=10 + args: + chdir: "{{ output_dir }}/s3_sync" + + - name: Sync files with remote bucket + s3_sync: + bucket: "{{ test_bucket }}" + file_root: "{{ output_dir }}/s3_sync" + register: output + - assert: + that: + - output is changed + + # ============================================================ + - name: Create a second s3_bucket + s3_bucket: + name: "{{ test_bucket_2 }}" + state: present + register: output + + - assert: + that: + - output.changed + - output.name == "{{ test_bucket_2 }}" + - not output.requester_pays + + - name: Sync files with remote bucket using glacier storage class + s3_sync: + bucket: "{{ test_bucket_2 }}" + file_root: "{{ output_dir }}/s3_sync" + storage_class: "GLACIER" + register: output + + - assert: + that: + - output is changed + + # ============================================================ + + - name: Sync files already present + s3_sync: + bucket: "{{ test_bucket }}" + file_root: "{{ output_dir }}/s3_sync" + register: output + - assert: + that: + - output is not changed + + # ============================================================ + - name: Sync files with etag calculation + s3_sync: + bucket: "{{ test_bucket }}" + file_root: "{{ output_dir }}/s3_sync" + file_change_strategy: checksum + register: output + - assert: + that: + - output is not changed + + # ============================================================ + - name: Create a third s3_bucket + s3_bucket: + name: "{{ test_bucket_3 }}" + state: present + register: output + + - assert: + that: + - output.changed + - output.name == "{{ test_bucket_3 }}" + - not output.requester_pays + + - name: Sync individual file with remote bucket + s3_sync: + bucket: "{{ test_bucket_3 }}" + file_root: "{{ output_dir }}/s3_sync/test1.txt" + register: output + - assert: + that: + - output is changed + + # ============================================================ + # DOCUMENTATION EXAMPLES + # ============================================================ + - name: all the options + s3_sync: + bucket: "{{ test_bucket }}" + file_root: "{{ output_dir }}/s3_sync" + mime_map: + .yml: application/text + .json: application/text + key_prefix: config_files/web + file_change_strategy: force + permission: public-read + cache_control: "public, max-age=31536000" + include: "*" + exclude: "*.txt,.*" + register: output + + - assert: + that: + - output is changed + - '"uploads" in output' + - '"filelist_initial" in output' + - '"filelist_local_etag" in output' + - '"filelist_s3" in output' + - '"filelist_typed" in output' + - '"filelist_actionable" in output' + + always: + + - name: Empty all buckets before deleting + block: + - name: list test_bucket objects + aws_s3: + bucket: "{{ test_bucket }}" + mode: list + register: objects + ignore_errors: true + + - name: remove objects from test_bucket + aws_s3: + bucket: "{{ test_bucket }}" + mode: delobj + object: "{{ obj }}" + with_items: "{{ objects.s3_keys }}" + loop_control: + loop_var: obj + ignore_errors: true + + - name: list test_bucket_2 objects + aws_s3: + bucket: "{{ test_bucket_2 }}" + mode: list + register: objects + ignore_errors: true + + - name: remove objects from test_bucket_2 + aws_s3: + bucket: "{{ test_bucket_2 }}" + mode: delobj + object: "{{ obj }}" + with_items: "{{ objects.s3_keys }}" + loop_control: + loop_var: obj + ignore_errors: true + + - name: list test_bucket_3 objects + aws_s3: + bucket: "{{ test_bucket_3 }}" + mode: list + register: objects + ignore_errors: true + + - name: remove objects from test_bucket_3 + aws_s3: + bucket: "{{ test_bucket_3 }}" + mode: delobj + object: "{{ obj }}" + with_items: "{{ objects.s3_keys }}" + loop_control: + loop_var: obj + ignore_errors: true + + - name: Ensure all buckets are deleted + s3_bucket: + name: "{{item}}" + state: absent + force: true + ignore_errors: yes + with_items: + - "{{ test_bucket }}" + - "{{ test_bucket_2 }}" + - "{{ test_bucket_3 }}" diff --git a/ansible_collections/community/aws/tests/integration/targets/secretsmanager_secret/aliases b/ansible_collections/community/aws/tests/integration/targets/secretsmanager_secret/aliases new file mode 100644 index 000000000..4ef4b2067 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/secretsmanager_secret/aliases @@ -0,0 +1 @@ +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/secretsmanager_secret/defaults/main.yaml b/ansible_collections/community/aws/tests/integration/targets/secretsmanager_secret/defaults/main.yaml new file mode 100644 index 000000000..5cd06e010 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/secretsmanager_secret/defaults/main.yaml @@ -0,0 +1,5 @@ +--- +super_secret_string: 'Test12345' +secret_manager_role: "{{ resource_prefix }}-secrets-manager" +secret_name: "{{ resource_prefix }}-secrets-manager-secret" +lambda_name: "{{ resource_prefix }}-secrets-manager-secret" diff --git a/ansible_collections/community/aws/tests/integration/targets/secretsmanager_secret/files/hello_world.zip b/ansible_collections/community/aws/tests/integration/targets/secretsmanager_secret/files/hello_world.zip Binary files differnew file mode 100644 index 000000000..8fd9e058f --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/secretsmanager_secret/files/hello_world.zip diff --git a/ansible_collections/community/aws/tests/integration/targets/secretsmanager_secret/files/secretsmanager-trust-policy.json b/ansible_collections/community/aws/tests/integration/targets/secretsmanager_secret/files/secretsmanager-trust-policy.json new file mode 100644 index 000000000..c53e30964 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/secretsmanager_secret/files/secretsmanager-trust-policy.json @@ -0,0 +1,19 @@ +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + }, + "Action": "sts:AssumeRole" + }, + { + "Effect": "Allow", + "Principal": { + "Service": "secretsmanager.amazonaws.com" + }, + "Action": "sts:AssumeRole" + } + ] +} diff --git a/ansible_collections/community/aws/tests/integration/targets/secretsmanager_secret/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/secretsmanager_secret/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/secretsmanager_secret/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/secretsmanager_secret/tasks/basic.yml b/ansible_collections/community/aws/tests/integration/targets/secretsmanager_secret/tasks/basic.yml new file mode 100644 index 000000000..5d1fb071e --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/secretsmanager_secret/tasks/basic.yml @@ -0,0 +1,820 @@ +--- +############################################################ +# This Integration test is currently very minimal +# It would be good if someone would update the tests to run through the standard 4-step checks for +# all of the module parameters +# +# 1) Set Value (Check mode) +# - changed is True +# - object *NOT* updated +# 2) Set Value +# - changed is True +# - object updated +# 3) Set same Value (Check mode) +# - changed is False +# - object *NOT* updated +# - Tests idemoptency +# 4) Set same Value +# - changed is False +# - object *NOT* updated +# - Tests idemoptency + +- set_fact: + # As a lookup plugin we won't have access to module_defaults + connection_args: + region: "{{ aws_region }}" + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + aws_security_token: "{{ security_token | default(omit) }}" + no_log: True + +- 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: + # ============================================================ + # Preparation + # ============================================================ + - name: 'Retrieve caller facts' + aws_caller_info: + register: caller_info + + - name: 'Generate the ARN pattern to search for' + vars: + _caller_info: '{{ caller_info.arn.split(":") }}' + _base_arn: 'arn:{{_caller_info[1]}}:secretsmanager:{{ aws_region }}' + set_fact: + account_arn: '{{_base_arn}}:{{_caller_info[4]}}:secret:' + + # ============================================================ + # Creation testing + # ============================================================ + - name: add secret to AWS Secrets Manager + aws_secret: + name: "{{ secret_name }}" + state: present + secret_type: 'string' + secret: "{{ super_secret_string }}" + register: result + + - name: assert correct keys are returned + assert: + that: + - result.changed + - '"arn" in result.secret' + # - '"last_accessed_date" in result.secret' + - '"last_changed_date" in result.secret' + - '"name" in result.secret' + - '"version_ids_to_stages" in result.secret' + - result.secret.arn.startswith(account_arn) + # ARN includes a random string after the name + - secret_name in result.secret.arn + - result.secret.name == secret_name + - result.secret.version_ids_to_stages | length == 1 + + - name: save the ARN for later comparison + set_fact: + secret_arn: '{{ result.secret.arn }}' + + - name: no changes to secret + aws_secret: + name: "{{ secret_name }}" + state: present + secret_type: 'string' + secret: "{{ super_secret_string }}" + register: result + + - name: assert correct keys are returned + assert: + that: + - not result.changed + - '"arn" in result.secret' + - '"created_date" in result.secret' + - '"last_accessed_date" in result.secret' + - '"last_changed_date" in result.secret' + - '"name" in result.secret' + - '"version_ids_to_stages" in result.secret' + - result.secret.arn == secret_arn + - result.secret.name == secret_name + - result.secret.version_ids_to_stages | length == 1 + + - name: Set secret description + aws_secret: + name: "{{ secret_name }}" + description: 'this is a change to this secret' + state: present + secret_type: 'string' + secret: "{{ super_secret_string }}" + register: result + + - name: assert correct keys are returned + assert: + that: + - result.changed + - '"arn" in result.secret' + - '"created_date" in result.secret' + - '"description" in result.secret' + - '"last_accessed_date" in result.secret' + - '"last_changed_date" in result.secret' + - '"name" in result.secret' + - '"version_ids_to_stages" in result.secret' + - result.secret.description == "this is a change to this secret" + - result.secret.arn == secret_arn + - result.secret.name == secret_name + - result.secret.version_ids_to_stages | length == 2 + + ############################################################### + # Tagging + ############################################################### + + - name: Set tags (CHECK_MODE) + aws_secret: + name: "{{ secret_name }}" + description: 'this is a change to this secret' + state: present + secret_type: 'string' + secret: "{{ super_secret_string }}" + tags: "{{ first_tags }}" + register: result + check_mode: True + + - name: assert changed + assert: + that: + - result is changed + + - name: Set tags + aws_secret: + name: "{{ secret_name }}" + description: 'this is a change to this secret' + state: present + secret_type: 'string' + secret: "{{ super_secret_string }}" + tags: "{{ first_tags }}" + register: result + + - name: assert correct keys are returned + assert: + that: + - result is changed + - '"arn" in result.secret' + - '"created_date" in result.secret' + - '"description" in result.secret' + - '"last_accessed_date" in result.secret' + - '"last_changed_date" in result.secret' + - '"name" in result.secret' + - '"tags" in result.secret' + - '"tags_dict" in result.secret' + - '"version_ids_to_stages" in result.secret' + - result.secret.description == "this is a change to this secret" + - result.secret.arn == secret_arn + - result.secret.name == secret_name + - result.secret.tags | length == 4 + - result.secret.tags_dict == first_tags + - result.secret.version_ids_to_stages | length == 2 + + - name: Set tags - idempotency (CHECK_MODE) + aws_secret: + name: "{{ secret_name }}" + description: 'this is a change to this secret' + state: present + secret_type: 'string' + secret: "{{ super_secret_string }}" + tags: "{{ first_tags }}" + register: result + check_mode: True + + - name: assert changed + assert: + that: + - result is not changed + + - name: Set tags - idempotency + aws_secret: + name: "{{ secret_name }}" + description: 'this is a change to this secret' + state: present + secret_type: 'string' + secret: "{{ super_secret_string }}" + tags: "{{ first_tags }}" + register: result + + - name: assert correct keys are returned + assert: + that: + - result is not changed + - '"arn" in result.secret' + - '"created_date" in result.secret' + - '"description" in result.secret' + - '"last_accessed_date" in result.secret' + - '"last_changed_date" in result.secret' + - '"name" in result.secret' + - '"tags" in result.secret' + - '"tags_dict" in result.secret' + - '"version_ids_to_stages" in result.secret' + - result.secret.description == "this is a change to this secret" + - result.secret.arn == secret_arn + - result.secret.name == secret_name + - result.secret.tags | length == 4 + - result.secret.tags_dict == first_tags + - result.secret.version_ids_to_stages | length == 2 + + ### + + - name: Update tags with purge (CHECK_MODE) + aws_secret: + name: "{{ secret_name }}" + description: 'this is a change to this secret' + state: present + secret_type: 'string' + secret: "{{ super_secret_string }}" + tags: "{{ second_tags }}" + register: result + check_mode: True + + - name: assert changed + assert: + that: + - result is changed + + - name: Update tags with purge + aws_secret: + name: "{{ secret_name }}" + description: 'this is a change to this secret' + state: present + secret_type: 'string' + secret: "{{ super_secret_string }}" + tags: "{{ second_tags }}" + register: result + + - name: assert correct keys are returned + assert: + that: + - result is changed + - '"arn" in result.secret' + - '"created_date" in result.secret' + - '"description" in result.secret' + - '"last_accessed_date" in result.secret' + - '"last_changed_date" in result.secret' + - '"name" in result.secret' + - '"tags" in result.secret' + - '"tags_dict" in result.secret' + - '"version_ids_to_stages" in result.secret' + - result.secret.description == "this is a change to this secret" + - result.secret.arn == secret_arn + - result.secret.name == secret_name + - result.secret.tags | length == 4 + - result.secret.tags_dict == second_tags + - result.secret.version_ids_to_stages | length == 2 + + - name: Update tags with purge - idempotency (CHECK_MODE) + aws_secret: + name: "{{ secret_name }}" + description: 'this is a change to this secret' + state: present + secret_type: 'string' + secret: "{{ super_secret_string }}" + tags: "{{ second_tags }}" + register: result + check_mode: True + + - name: assert changed + assert: + that: + - result is not changed + + - name: Update tags with purge - idempotency + aws_secret: + name: "{{ secret_name }}" + description: 'this is a change to this secret' + state: present + secret_type: 'string' + secret: "{{ super_secret_string }}" + tags: "{{ second_tags }}" + register: result + + - name: assert correct keys are returned + assert: + that: + - result is not changed + - '"arn" in result.secret' + - '"created_date" in result.secret' + - '"description" in result.secret' + - '"last_accessed_date" in result.secret' + - '"last_changed_date" in result.secret' + - '"name" in result.secret' + - '"tags" in result.secret' + - '"tags_dict" in result.secret' + - '"version_ids_to_stages" in result.secret' + - result.secret.description == "this is a change to this secret" + - result.secret.arn == secret_arn + - result.secret.name == secret_name + - result.secret.tags | length == 4 + - result.secret.tags_dict == second_tags + - result.secret.version_ids_to_stages | length == 2 + + ### + + - name: Update tags without purge (CHECK_MODE) + aws_secret: + name: "{{ secret_name }}" + description: 'this is a change to this secret' + state: present + secret_type: 'string' + secret: "{{ super_secret_string }}" + tags: "{{ third_tags }}" + purge_tags: false + register: result + check_mode: True + + - name: assert changed + assert: + that: + - result is changed + + - name: Update tags without purge + aws_secret: + name: "{{ secret_name }}" + description: 'this is a change to this secret' + state: present + secret_type: 'string' + secret: "{{ super_secret_string }}" + tags: "{{ third_tags }}" + purge_tags: false + register: result + + - name: assert correct keys are returned + assert: + that: + - result is changed + - '"arn" in result.secret' + - '"created_date" in result.secret' + - '"description" in result.secret' + - '"last_accessed_date" in result.secret' + - '"last_changed_date" in result.secret' + - '"name" in result.secret' + - '"tags" in result.secret' + - '"tags_dict" in result.secret' + - '"version_ids_to_stages" in result.secret' + - result.secret.description == "this is a change to this secret" + - result.secret.arn == secret_arn + - result.secret.name == secret_name + - result.secret.tags | length == 8 + - result.secret.tags_dict == final_tags + - result.secret.version_ids_to_stages | length == 2 + + - name: Update tags without purge - idempotency (CHECK_MODE) + aws_secret: + name: "{{ secret_name }}" + description: 'this is a change to this secret' + state: present + secret_type: 'string' + secret: "{{ super_secret_string }}" + tags: "{{ third_tags }}" + purge_tags: false + register: result + check_mode: True + + - name: assert changed + assert: + that: + - result is not changed + + - name: Update tags without purge - idempotency + aws_secret: + name: "{{ secret_name }}" + description: 'this is a change to this secret' + state: present + secret_type: 'string' + secret: "{{ super_secret_string }}" + tags: "{{ third_tags }}" + purge_tags: false + register: result + + - name: assert correct keys are returned + assert: + that: + - result is not changed + - '"arn" in result.secret' + - '"created_date" in result.secret' + - '"description" in result.secret' + - '"last_accessed_date" in result.secret' + - '"last_changed_date" in result.secret' + - '"name" in result.secret' + - '"tags" in result.secret' + - '"tags_dict" in result.secret' + - '"version_ids_to_stages" in result.secret' + - result.secret.description == "this is a change to this secret" + - result.secret.arn == secret_arn + - result.secret.name == secret_name + - result.secret.tags | length == 8 + - result.secret.tags_dict == final_tags + - result.secret.version_ids_to_stages | length == 2 + + ### + + - name: Tags not set - idempotency (CHECK_MODE) + aws_secret: + name: "{{ secret_name }}" + description: 'this is a change to this secret' + state: present + secret_type: 'string' + secret: "{{ super_secret_string }}" + register: result + check_mode: True + + - name: assert changed + assert: + that: + - result is not changed + + - name: Tags not set - idempotency + aws_secret: + name: "{{ secret_name }}" + description: 'this is a change to this secret' + state: present + secret_type: 'string' + secret: "{{ super_secret_string }}" + register: result + + - name: assert correct keys are returned + assert: + that: + - result is not changed + - '"arn" in result.secret' + - '"created_date" in result.secret' + - '"description" in result.secret' + - '"last_accessed_date" in result.secret' + - '"last_changed_date" in result.secret' + - '"name" in result.secret' + - '"tags" in result.secret' + - '"tags_dict" in result.secret' + - '"version_ids_to_stages" in result.secret' + - result.secret.description == "this is a change to this secret" + - result.secret.arn == secret_arn + - result.secret.name == secret_name + - result.secret.tags | length == 8 + - result.secret.tags_dict == final_tags + - result.secret.version_ids_to_stages | length == 2 + + ### + + - name: remove all tags from secret (CHECK_MODE) + aws_secret: + name: "{{ secret_name }}" + description: 'this is a change to this secret' + state: present + secret_type: 'string' + secret: "{{ super_secret_string }}" + tags: {} + register: result + check_mode: true + + - name: assert correct keys are returned + assert: + that: + - result is changed + + - name: remove all tags from secret + aws_secret: + name: "{{ secret_name }}" + description: 'this is a change to this secret' + state: present + secret_type: 'string' + secret: "{{ super_secret_string }}" + tags: {} + register: result + + - name: assert correct keys are returned + assert: + that: + - result is changed + - '"arn" in result.secret' + - '"created_date" in result.secret' + - '"description" in result.secret' + - '"last_accessed_date" in result.secret' + - '"last_changed_date" in result.secret' + - '"name" in result.secret' + - '"tags" in result.secret' + - '"tags_dict" in result.secret' + - '"version_ids_to_stages" in result.secret' + - result.secret.description == "this is a change to this secret" + - result.secret.arn == secret_arn + - result.secret.name == secret_name + - result.secret.tags | length == 0 + - result.secret.tags_dict | length == 0 + - result.secret.version_ids_to_stages | length == 2 + + - name: remove all tags from secret - idempotency (CHECK_MODE) + aws_secret: + name: "{{ secret_name }}" + description: 'this is a change to this secret' + state: present + secret_type: 'string' + secret: "{{ super_secret_string }}" + tags: {} + register: result + check_mode: true + + - name: assert correct keys are returned + assert: + that: + - result is not changed + + - name: remove all tags from secret + aws_secret: + name: "{{ secret_name }}" + description: 'this is a change to this secret' + state: present + secret_type: 'string' + secret: "{{ super_secret_string }}" + tags: {} + register: result + + - name: assert correct keys are returned - idempotency + assert: + that: + - result is not changed + - '"arn" in result.secret' + - '"created_date" in result.secret' + - '"description" in result.secret' + - '"last_accessed_date" in result.secret' + - '"last_changed_date" in result.secret' + - '"name" in result.secret' + - '"tags" in result.secret' + - '"tags_dict" in result.secret' + - '"version_ids_to_stages" in result.secret' + - result.secret.description == "this is a change to this secret" + - result.secret.arn == secret_arn + - result.secret.name == secret_name + - result.secret.tags | length == 0 + - result.secret.tags_dict | length == 0 + - result.secret.version_ids_to_stages | length == 2 + + ############################################################### + # Resource policy + ############################################################### + + - name: add resource policy to secret + aws_secret: + name: "{{ secret_name }}" + description: 'this is a change to this secret' + state: present + secret_type: 'string' + secret: "{{ super_secret_string }}" + resource_policy: "{{ lookup('template', 'secret-policy.j2', convert_data=False) | string }}" + register: result + + - name: assert correct keys are returned + assert: + that: + - result.changed + + - name: remove existing resource policy from secret + aws_secret: + name: "{{ secret_name }}" + description: 'this is a change to this secret' + state: present + secret_type: 'string' + secret: "{{ super_secret_string }}" + register: result + + - name: assert correct keys are returned + assert: + that: + - result.changed + + - name: remove resource policy from secret (idempotency) + aws_secret: + name: "{{ secret_name }}" + description: 'this is a change to this secret' + state: present + secret_type: 'string' + secret: "{{ super_secret_string }}" + register: result + + - name: assert no change happened + assert: + that: + - not result.changed + + # ============================================================ + # Update secret using JSON string + # ============================================================ + + - name: Update secret with JSON (CHECK_MODE) + aws_secret: + name: "{{ secret_name }}" + description: 'this is a change to this secret' + state: present + secret_type: 'string' + json_secret: + my_key: '{{ super_secret_string }}' + register: result + check_mode: True + + - name: assert key would be changed + assert: + that: + - result.changed + + - name: Update secret with JSON + aws_secret: + name: "{{ secret_name }}" + state: present + description: 'this is a change to this secret' + secret_type: 'string' + json_secret: + my_key: '{{ super_secret_string }}' + register: result + + - name: assert key is changed + assert: + that: + - result.changed + + - name: Update secret with JSON - idempotency (CHECK_MODE) + aws_secret: + name: "{{ secret_name }}" + description: 'this is a change to this secret' + state: present + secret_type: 'string' + json_secret: + my_key: '{{ super_secret_string }}' + register: result + check_mode: True + + - name: assert key is not changed + assert: + that: + - result is not changed + + - name: Update secret with JSON - idempotency + aws_secret: + name: "{{ secret_name }}" + description: 'this is a change to this secret' + state: present + secret_type: 'string' + json_secret: + my_key: '{{ super_secret_string }}' + register: result + check_mode: True + + - name: assert key is not changed + assert: + that: + - result is not changed + + # ============================================================ + # Overwrite testing + # ============================================================ + + - name: Create secret with overwrite = False (Check mode) + aws_secret: + name: "{{ secret_name }}-2" + state: present + secret_type: 'string' + secret: "{{ super_secret_string }}" + overwrite: False + register: result + check_mode: True + + - name: assert key is changed + assert: + that: + - result is changed + + - name: Create secret with overwrite = False + aws_secret: + name: "{{ secret_name }}-2" + state: present + secret_type: 'string' + secret: "{{ super_secret_string }}" + overwrite: False + register: result + + - name: assert key is changed + assert: + that: + - result is changed + + - name: Update secret with overwrite = False (Check mode) + aws_secret: + name: "{{ secret_name }}-2" + state: present + secret_type: 'string' + secret: "{{ super_secret_string }}-2" + overwrite: False + register: result + check_mode: True + + - name: assert key is not changed + assert: + that: + - result is not changed + + - name: Create secret with overwrite = False + aws_secret: + name: "{{ secret_name }}-2" + state: present + secret_type: 'string' + secret: "{{ super_secret_string }}-2" + overwrite: False + register: result + + - name: assert key is not changed + assert: + that: + - result is not changed + + # ============================================================ + # Removal testing + # ============================================================ + + - name: remove secret + aws_secret: + name: "{{ secret_name }}" + state: absent + recovery_window: 7 + register: result + + - name: assert key is deleted + assert: + that: + - result.changed + + - name: remove secret (idempotency) + aws_secret: + name: "{{ secret_name }}" + state: absent + recovery_window: 7 + register: result + + - name: assert no change happened + assert: + that: + - not result.changed + + - name: immediate secret removal + aws_secret: + name: "{{ secret_name }}" + state: absent + recovery_window: 0 + register: result + + - name: assert key is deleted + assert: + that: + - result.changed + + # AWS Doesn't expose when the secret will be removed, all we can do is + # check that we didn't throw an error + - name: immediate secret removal + aws_secret: + name: "{{ secret_name }}" + state: absent + recovery_window: 0 + register: result + + - name: assert no change happened + assert: + that: + - not result.failed + + always: + - name: remove secret + aws_secret: + name: "{{ secret_name }}" + state: absent + recovery_window: 0 + ignore_errors: yes + + - name: remove secret 2 + aws_secret: + name: "{{ secret_name }}-2" + state: absent + recovery_window: 0 + ignore_errors: yes diff --git a/ansible_collections/community/aws/tests/integration/targets/secretsmanager_secret/tasks/main.yaml b/ansible_collections/community/aws/tests/integration/targets/secretsmanager_secret/tasks/main.yaml new file mode 100644 index 000000000..41fbedd9d --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/secretsmanager_secret/tasks/main.yaml @@ -0,0 +1,16 @@ +--- +- module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + collections: + - amazon.aws + + block: + - include_tasks: 'basic.yml' + # Permissions missing + #- include_tasks: 'rotation.yml' + # Multi-Region CI not supported (yet) + #- include_tasks: 'replication.yml' diff --git a/ansible_collections/community/aws/tests/integration/targets/secretsmanager_secret/tasks/replication.yml b/ansible_collections/community/aws/tests/integration/targets/secretsmanager_secret/tasks/replication.yml new file mode 100644 index 000000000..30d3a9484 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/secretsmanager_secret/tasks/replication.yml @@ -0,0 +1,116 @@ +--- +- block: + # ============================================================ + # Creation/Deletion testing + # ============================================================ + - name: add secret to AWS Secrets Manager + aws_secret: + name: "{{ secret_name }}" + state: present + secret_type: 'string' + secret: "{{ super_secret_string }}" + replica: + - region: 'us-east-2' + - region: 'us-west-2' + kms_key_id: 'alias/aws/secretsmanager' + register: result + + - name: assert correct keys are returned + assert: + that: + - result.changed + - result.arn is not none + - result.name is not none + - result.secret.replication_status[0]["region"] == 'us-east-2' + - result.secret.replication_status[1]["region"] == 'us-west-2' + - result.secret.replication_status[1]["kms_key_id"] == 'alias/aws/secretsmanager' + - result.tags is not none + - result.version_ids_to_stages is not none + + - name: no changes to secret + aws_secret: + name: "{{ secret_name }}" + state: present + secret: "{{ super_secret_string }}" + replica: + - region: 'us-east-2' + - region: 'us-west-2' + kms_key_id: 'alias/aws/secretsmanager' + register: result + + - name: assert correct keys are returned + assert: + that: + - not result.changed + - result.arn is not none + + - name: remove region replica + aws_secret: + name: "{{ secret_name }}" + description: 'this is a change to remove replication' + secret: "{{ super_secret_string }}" + state: present + replica: [] + register: result + + - name: assert that replica was removed + assert: + that: + - not result.failed + - '"replication_status" not in result.secret' + + - name: add region replica to an existing secret + aws_secret: + name: "{{ secret_name }}" + description: 'this is a change add replication' + secret: "{{ super_secret_string }}" + state: present + replica: + - region: 'us-east-2' + - region: 'us-west-2' + kms_key_id: 'alias/aws/secretsmanager' + register: result + + - name: assert that replica was created + assert: + that: + - not result.failed + - result.secret.replication_status[0]["region"] == 'us-east-2' + - result.secret.replication_status[1]["region"] == 'us-west-2' + - result.secret.replication_status[1]["kms_key_id"] == 'alias/aws/secretsmanager' + + - name: change replica regions + aws_secret: + name: "{{ secret_name }}" + state: present + secret: "{{ super_secret_string }}" + replica: + - region: 'us-east-2' + - region: 'eu-central-1' + kms_key_id: 'alias/aws/secretsmanager' + register: result + + - name: assert that replica regions changed + assert: + that: + - not result.failed + - result.secret.replication_status[0]["region"] == 'us-east-2' + - result.secret.replication_status[1]["region"] == 'eu-central-1' + - result.secret.replication_status[1]["kms_key_id"] == 'alias/aws/secretsmanager' + + always: + - name: remove region replica + aws_secret: + name: "{{ secret_name }}" + description: 'this is a change to remove replication' + state: present + secret: "{{ super_secret_string }}" + register: result + ignore_errors: yes + + - name: remove secret + aws_secret: + name: "{{ secret_name }}" + state: absent + recovery_window: 0 + ignore_errors: yes diff --git a/ansible_collections/community/aws/tests/integration/targets/secretsmanager_secret/tasks/rotation.yml b/ansible_collections/community/aws/tests/integration/targets/secretsmanager_secret/tasks/rotation.yml new file mode 100644 index 000000000..5a1d146e5 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/secretsmanager_secret/tasks/rotation.yml @@ -0,0 +1,191 @@ +--- +- module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + collections: + - amazon.aws + + block: + - name: retrieve caller facts + aws_caller_info: + register: test_caller_facts + + - name: ensure IAM role exists + iam_role: + name: "{{ secret_manager_role }}" + assume_role_policy_document: "{{ lookup('file','secretsmanager-trust-policy.json') }}" + state: present + create_instance_profile: no + managed_policy: + - 'arn:aws:iam::aws:policy/SecretsManagerReadWrite' + register: iam_role + ignore_errors: yes + + - name: wait 10 seconds for role to become available + pause: + seconds: 10 + when: iam_role.changed + + # CI does not remove the role and comparing policies has a bug on Python3; fall back to use iam_role_info + - name: get IAM role + iam_role_info: + name: "{{ secret_manager_role }}" + register: iam_role_info + + - name: set iam_role_output + set_fact: + iam_role_output: "{{ iam_role_info.iam_roles[0] }}" + when: iam_role_info is defined + + - name: create a temporary directory + tempfile: + state: directory + register: tmp + + - name: move lambda into place for upload + copy: + src: "files/hello_world.zip" + dest: "{{ tmp.path }}/hello_world.zip" + + - name: dummy lambda for testing + lambda: + name: "{{ lambda_name }}" + state: present + zip_file: "{{ tmp.path }}/hello_world.zip" + runtime: 'python3.9' + role: "{{ iam_role_output.arn }}" + handler: 'hello_world.lambda_handler' + register: lambda_output + until: not lambda_output.failed + retries: 10 + delay: 5 + + - debug: + var: lambda_output + + # ============================================================ + # Creation/Deletion testing + # ============================================================ + - name: add secret to AWS Secrets Manager + aws_secret: + name: "{{ secret_name }}-rotate" + state: present + secret_type: 'string' + secret: "{{ super_secret_string }}" + register: result + + - name: assert correct keys are returned + assert: + that: + - result.changed + - result.arn is not none + - result.name is not none + - result.tags is not none + - result.version_ids_to_stages is not none + + - name: lambda policy for secrets manager + lambda_policy: + state: present + function_name: "{{ lambda_name }}" + statement_id: LambdaSecretsManagerTestPolicy + action: 'lambda:InvokeFunction' + principal: "secretsmanager.amazonaws.com" + + - name: add rotation lambda to secret + aws_secret: + name: "{{ secret_name }}-rotate" + description: 'this is a change to this secret' + state: present + secret_type: 'string' + secret: "{{ super_secret_string }}" + rotation_lambda: "arn:aws:lambda:{{ aws_region }}:{{ test_caller_facts.account }}:function:{{ lambda_name }}" + register: result + retries: 100 + delay: 5 + until: not result.failed + + - name: assert correct keys are returned + assert: + that: + - result.changed + + - name: remove rotation lambda from secret + aws_secret: + name: "{{ secret_name }}-rotate" + description: 'this is a change to this secret' + state: present + secret_type: 'string' + secret: "{{ super_secret_string }}" + register: result + + - name: assert correct keys are returned + assert: + that: + - result.changed + + - name: remove rotation lambda from secret + aws_secret: + name: "{{ secret_name }}-rotate" + description: 'this is a change to this secret' + state: present + secret_type: 'string' + secret: "{{ super_secret_string }}" + register: result + + - name: assert correct keys are returned + assert: + that: + - not result.changed + + - name: remove secret + aws_secret: + name: "{{ secret_name }}-rotate" + state: absent + recovery_window: 0 + ignore_errors: yes + + always: + - name: remove secret + aws_secret: + name: "{{ secret_name }}-rotate" + state: absent + recovery_window: 0 + ignore_errors: yes + + - name: remove lambda policy + lambda_policy: + state: absent + function_name: "{{ lambda_name }}" + statement_id: lambda-secretsmanager-test-policy + action: lambda:InvokeFunction + principal: secretsmanager.amazonaws.com + ignore_errors: yes + + - name: remove dummy lambda + lambda: + name: "{{ lambda_name }}" + state: absent + zip_file: "{{ tmp.path }}/hello_world.zip" + runtime: 'python3.9' + role: "{{ secret_manager_role }}" + handler: 'hello_world.lambda_handler' + ignore_errors: yes + + # CI does not remove the IAM role + - name: remove IAM role + iam_role: + name: "{{ secret_manager_role }}" + assume_role_policy_document: "{{ lookup('file','secretsmanager-trust-policy.json') }}" + state: absent + create_instance_profile: no + managed_policy: + - 'arn:aws:iam::aws:policy/SecretsManagerReadWrite' + ignore_errors: yes + + - name: remove temporary dir + file: + path: "{{ tmp.path }}" + state: absent diff --git a/ansible_collections/community/aws/tests/integration/targets/secretsmanager_secret/templates/secret-policy.j2 b/ansible_collections/community/aws/tests/integration/targets/secretsmanager_secret/templates/secret-policy.j2 new file mode 100644 index 000000000..8b084e166 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/secretsmanager_secret/templates/secret-policy.j2 @@ -0,0 +1,11 @@ +{ + "Version" : "2012-10-17", + "Statement" : [ { + "Effect" : "Allow", + "Principal" : { + "AWS" : "arn:aws:iam::{{ caller_info.account }}:root" + }, + "Action" : "secretsmanager:*", + "Resource" : "*" + } ] +} diff --git a/ansible_collections/community/aws/tests/integration/targets/ses_identity/aliases b/ansible_collections/community/aws/tests/integration/targets/ses_identity/aliases new file mode 100644 index 000000000..4ef4b2067 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ses_identity/aliases @@ -0,0 +1 @@ +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/ses_identity/defaults/main.yaml b/ansible_collections/community/aws/tests/integration/targets/ses_identity/defaults/main.yaml new file mode 100644 index 000000000..f36d01793 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ses_identity/defaults/main.yaml @@ -0,0 +1,4 @@ +--- +email_identity: "{{ resource_prefix }}@example.com" +domain_identity: "{{ resource_prefix }}.example.com" +notification_queue_name: "{{ resource_prefix }}-notification-queue" diff --git a/ansible_collections/community/aws/tests/integration/targets/ses_identity/meta/main.yaml b/ansible_collections/community/aws/tests/integration/targets/ses_identity/meta/main.yaml new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ses_identity/meta/main.yaml diff --git a/ansible_collections/community/aws/tests/integration/targets/ses_identity/tasks/assert_defaults.yaml b/ansible_collections/community/aws/tests/integration/targets/ses_identity/tasks/assert_defaults.yaml new file mode 100644 index 000000000..0f74d2f05 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ses_identity/tasks/assert_defaults.yaml @@ -0,0 +1,23 @@ +- name: assert returned identity + assert: + that: + - result.identity == identity +- name: assert returned identity_arn + assert: + that: + - "result.identity_arn|regex_search('^arn:aws:ses:' + ec2_region + ':[0-9]*:identity/' + identity + '$')" + msg: "'{{ result.identity_arn}}' doesn't match regex '^arn:aws:ses:{{ ec2_region }}:[0-9]*:identity/{{ identity }}'" +- name: assert verification_attributes.verification_status == 'Pending' + assert: + that: + - result.verification_attributes.verification_status == 'Pending' +- name: assert notification defaults + assert: + that: + - result.notification_attributes.forwarding_enabled == True + - result.notification_attributes.headers_in_bounce_notifications_enabled == False + - result.notification_attributes.headers_in_complaint_notifications_enabled == False + - result.notification_attributes.headers_in_delivery_notifications_enabled == False + - "'bounce_topic' not in result.notification_attributes" + - "'complaint_topic' not in result.notification_attributes" + - "'delivery_topic' not in result.notification_attributes" diff --git a/ansible_collections/community/aws/tests/integration/targets/ses_identity/tasks/main.yaml b/ansible_collections/community/aws/tests/integration/targets/ses_identity/tasks/main.yaml new file mode 100644 index 000000000..81ab3c4a7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ses_identity/tasks/main.yaml @@ -0,0 +1,595 @@ +--- +- name: 'aws_ses_identity integration tests' + collections: + - amazon.aws + module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + block: + + # ============================================================ + - name: test register email identity + block: + - name: register email identity + aws_ses_identity: + identity: "{{ email_identity }}" + state: present + register: result + - name: assert changed is True + assert: + that: + - result.changed == True + - import_tasks: assert_defaults.yaml + vars: + identity: "{{ email_identity }}" + always: + - name: cleanup email identity + aws_ses_identity: + identity: "{{ email_identity }}" + state: absent + # ============================================================ + - name: test register domain identity + block: + - name: register domain identity + aws_ses_identity: + identity: "{{ domain_identity }}" + state: present + register: result + - name: assert changed is True + assert: + that: + - result.changed == True + - import_tasks: assert_defaults.yaml + vars: + identity: "{{ domain_identity }}" + - name: assert verification_attributes.verification_token is defined + assert: + that: + - result.verification_attributes.verification_token + always: + - name: cleanup domain identity + aws_ses_identity: + identity: "{{ domain_identity }}" + state: absent + # ============================================================ + - name: test email_identity unchanged when already existing + block: + - name: register identity + aws_ses_identity: + identity: "{{ email_identity }}" + state: present + - name: duplicate register identity + aws_ses_identity: + identity: "{{ email_identity }}" + state: present + register: result + - name: assert changed is False + assert: + that: + - result.changed == False + - import_tasks: assert_defaults.yaml + vars: + identity: "{{ email_identity }}" + always: + - name: cleanup identity + aws_ses_identity: + identity: "{{ email_identity }}" + state: absent + # ============================================================ + - name: test domain_identity unchanged when already existing + block: + - name: register identity + aws_ses_identity: + identity: "{{ domain_identity }}" + state: present + - name: duplicate register identity + aws_ses_identity: + identity: "{{ domain_identity }}" + state: present + register: result + - name: assert changed is False + assert: + that: + - result.changed == False + - import_tasks: assert_defaults.yaml + vars: + identity: "{{ domain_identity }}" + always: + - name: cleanup identity + aws_ses_identity: + identity: "{{ domain_identity }}" + state: absent + # ============================================================ + # Test for https://github.com/ansible/ansible/issues/51531 + # because aws region is explicitly used rather than just to + # obtain a connection, make sure this still works when + # region comes from an environment rather than a parameter. + - name: test register identity without explicit region + block: + - name: register email identity without explicit region + aws_ses_identity: + identity: "{{ email_identity }}" + state: present + region: "{{ omit }}" + register: result + environment: + AWS_DEFAULT_REGION: "{{ aws_region }}" + - name: assert changed is True + assert: + that: + - result.changed == True + - import_tasks: assert_defaults.yaml + vars: + identity: "{{ email_identity }}" + always: + - name: cleanup email identity + aws_ses_identity: + identity: "{{ email_identity }}" + state: absent + # ============================================================ + - name: test register email identity check mode + block: + - name: register email identity check mode + aws_ses_identity: + identity: "{{ email_identity }}" + state: present + register: result + check_mode: True + + - name: assert changed is True + assert: + that: + - result.changed == True + + - import_tasks: assert_defaults.yaml + vars: + identity: "{{ email_identity }}" + + always: + - name: cleanup email identity + aws_ses_identity: + identity: "{{ email_identity }}" + state: absent + register: result + + - name: assert nothing to clean up since check mode + assert: + that: + - result.changed == False + # ============================================================ + - name: test register domain identity check mode + block: + - name: register domain identity check mode + aws_ses_identity: + identity: "{{ domain_identity }}" + state: present + register: result + check_mode: True + + - name: assert changed is True + assert: + that: + - result.changed == True + + - import_tasks: assert_defaults.yaml + vars: + identity: "{{ domain_identity }}" + + always: + - name: cleanup domain identity + aws_ses_identity: + identity: "{{ domain_identity }}" + state: absent + register: result + + - name: assert nothing to clean up since check mode + assert: + that: + - result.changed == False + # ============================================================ + - name: remove non-existent email identity + aws_ses_identity: + identity: "{{ email_identity }}" + state: absent + register: result + - name: assert changed is False + assert: + that: + - result.changed == False + # ============================================================ + - name: remove non-existent domain identity + aws_ses_identity: + identity: "{{ domain_identity }}" + state: absent + register: result + - name: assert changed is False + assert: + that: + - result.changed == False + # ============================================================ + - name: test remove email identity check mode + block: + - name: register email identity + aws_ses_identity: + identity: "{{ email_identity }}" + state: present + register: result + + - name: remove email identity check mode + aws_ses_identity: + identity: "{{ email_identity }}" + state: absent + register: result + check_mode: True + + - name: assert changed is True + assert: + that: + - result.changed == True + always: + - name: cleanup email identity + aws_ses_identity: + identity: "{{ email_identity }}" + state: absent + register: result + + - name: assert something to clean up since remove was check mode + assert: + that: + - result.changed == True + # ============================================================ + - name: test remove domain identity check mode + block: + - name: register domain identity + aws_ses_identity: + identity: "{{ domain_identity }}" + state: present + register: result + + - name: remove domain identity check mode + aws_ses_identity: + identity: "{{ domain_identity }}" + state: absent + register: result + check_mode: True + + - name: assert changed is True + assert: + that: + - result.changed == True + always: + - name: cleanup domain identity + aws_ses_identity: + identity: "{{ domain_identity }}" + state: absent + register: result + + - name: assert something to clean up since remove was check mode + assert: + that: + - result.changed == True + # ============================================================ + - name: test set notification queues + block: + - name: test topic + sns_topic: + name: "{{ notification_queue_name }}-{{ item }}" + state: present + register: topic_info + with_items: + - bounce + - complaint + - delivery + - name: register email identity + aws_ses_identity: + identity: "{{ email_identity }}" + state: present + bounce_notifications: + topic: "{{ topic_info.results[0].sns_arn }}" + complaint_notifications: + topic: "{{ topic_info.results[1].sns_arn }}" + delivery_notifications: + topic: "{{ topic_info.results[2].sns_arn }}" + register: result + - name: assert notification settings + assert: + that: + - result.notification_attributes.bounce_topic == topic_info.results[0].sns_arn + - result.notification_attributes.complaint_topic == topic_info.results[1].sns_arn + - result.notification_attributes.delivery_topic == topic_info.results[2].sns_arn + - name: assert notification headers unchanged + assert: + that: + - result.notification_attributes.headers_in_bounce_notifications_enabled == False + - result.notification_attributes.headers_in_complaint_notifications_enabled == False + - result.notification_attributes.headers_in_delivery_notifications_enabled == False + always: + - name: cleanup topics + sns_topic: + name: "{{ notification_queue_name }}-{{ item }}" + state: absent + with_items: + - bounce + - complaint + - delivery + - name: cleanup email identity + aws_ses_identity: + identity: "{{ email_identity }}" + state: absent + # ============================================================ + - name: test change notification queues after create + block: + - name: test topic + sns_topic: + name: "{{ notification_queue_name }}-{{ item }}" + state: present + register: topic_info + with_items: + - bounce + - complaint + - delivery + - name: register email identity + aws_ses_identity: + identity: "{{ email_identity }}" + state: present + - name: set notification topics + aws_ses_identity: + identity: "{{ email_identity }}" + state: present + bounce_notifications: + topic: "{{ topic_info.results[0].sns_arn }}" + complaint_notifications: + topic: "{{ topic_info.results[1].sns_arn }}" + delivery_notifications: + topic: "{{ topic_info.results[2].sns_arn }}" + register: result + - name: assert changed is True + assert: + that: + - result.changed == True + - name: assert notification settings + assert: + that: + - result.notification_attributes.bounce_topic == topic_info.results[0].sns_arn + - result.notification_attributes.complaint_topic == topic_info.results[1].sns_arn + - result.notification_attributes.delivery_topic == topic_info.results[2].sns_arn + always: + - name: cleanup topics + sns_topic: + name: "{{ notification_queue_name }}-{{ item }}" + state: absent + with_items: + - bounce + - complaint + - delivery + - name: cleanup email identity + aws_ses_identity: + identity: "{{ email_identity }}" + state: absent + # ============================================================ + - name: test change notification settings check mode + block: + - name: test topic + sns_topic: + name: "{{ notification_queue_name }}-{{ item }}" + state: present + register: topic_info + with_items: + - bounce + - complaint + - delivery + + - name: register email identity + aws_ses_identity: + identity: "{{ email_identity }}" + state: present + + - name: set notification settings check mode + aws_ses_identity: + identity: "{{ email_identity }}" + state: present + bounce_notifications: + topic: "{{ topic_info.results[0].sns_arn }}" + include_headers: Yes + complaint_notifications: + topic: "{{ topic_info.results[1].sns_arn }}" + include_headers: Yes + delivery_notifications: + topic: "{{ topic_info.results[2].sns_arn }}" + include_headers: Yes + feedback_forwarding: No + register: result + check_mode: True + + - name: assert changed is True + assert: + that: + - result.changed == True + + - name: assert notification settings + assert: + that: + - result.notification_attributes.bounce_topic == topic_info.results[0].sns_arn + - result.notification_attributes.headers_in_bounce_notifications_enabled == True + - result.notification_attributes.delivery_topic == topic_info.results[2].sns_arn + - result.notification_attributes.headers_in_delivery_notifications_enabled == True + - result.notification_attributes.complaint_topic == topic_info.results[1].sns_arn + - result.notification_attributes.headers_in_complaint_notifications_enabled == True + - result.notification_attributes.forwarding_enabled == False + + - name: re-register base email identity + aws_ses_identity: + identity: "{{ email_identity }}" + state: present + register: result + + - name: assert no change since notifications were check mode + assert: + that: + - result.changed == False + - "'bounce_topic' not in result.notification_attributes" + - result.notification_attributes.headers_in_bounce_notifications_enabled == False + - "'delivery_topic' not in result.notification_attributes" + - result.notification_attributes.headers_in_delivery_notifications_enabled == False + - "'complaint_topic' not in result.notification_attributes" + - result.notification_attributes.headers_in_complaint_notifications_enabled == False + - result.notification_attributes.forwarding_enabled == True + + always: + - name: cleanup topics + sns_topic: + name: "{{ notification_queue_name }}-{{ item }}" + state: absent + with_items: + - bounce + - complaint + - delivery + + - name: cleanup email identity + aws_ses_identity: + identity: "{{ email_identity }}" + state: absent + # ============================================================ + - name: test include headers on notification queues + block: + - name: register email identity + aws_ses_identity: + identity: "{{ email_identity }}" + state: present + bounce_notifications: + include_headers: Yes + complaint_notifications: + include_headers: Yes + delivery_notifications: + include_headers: Yes + register: result + - name: assert notification headers enabled + assert: + that: + - result.notification_attributes.headers_in_bounce_notifications_enabled == True + - result.notification_attributes.headers_in_complaint_notifications_enabled == True + - result.notification_attributes.headers_in_delivery_notifications_enabled == True + always: + - name: cleanup email identity + aws_ses_identity: + identity: "{{ email_identity }}" + state: absent + # ============================================================ + - name: test disable feedback forwarding + block: + - name: test topic + sns_topic: + name: "{{ notification_queue_name }}-{{ item }}" + state: present + register: topic_info + with_items: + - bounce + - complaint + - name: register email identity + aws_ses_identity: + identity: "{{ email_identity }}" + state: present + bounce_notifications: + topic: "{{ topic_info.results[0].sns_arn }}" + complaint_notifications: + topic: "{{ topic_info.results[1].sns_arn }}" + feedback_forwarding: No + register: result + - name: assert feedback_forwarding == False + assert: + that: + - result.notification_attributes.forwarding_enabled == False + always: + - name: cleanup topics + sns_topic: + name: "{{ notification_queue_name }}-{{ item }}" + state: absent + with_items: + - bounce + - complaint + - name: cleanup email identity + aws_ses_identity: + identity: "{{ email_identity }}" + state: absent + # ============================================================ + - name: test disable feedback forwarding fails if no topics + block: + - name: register identity + aws_ses_identity: + identity: "{{ domain_identity }}" + state: present + feedback_forwarding: No + register: result + failed_when: result.failed == False + - name: assert error message starts with "Invalid Parameter Value" + assert: + that: + - '"Invalid Parameter Value" in result.msg' + always: + - name: cleanup identity + aws_ses_identity: + identity: "{{ domain_identity }}" + state: absent + # ============================================================ + - name: test disable feedback forwarding fails if no complaint topic + block: + - name: test topic + sns_topic: + name: "{{ notification_queue_name }}-bounce" + state: present + register: topic_info + - name: register email identity + aws_ses_identity: + identity: "{{ email_identity }}" + state: present + bounce_notifications: + topic: "{{ topic_info.sns_arn }}" + feedback_forwarding: No + register: result + failed_when: result.failed == False + - name: assert error message starts with "Invalid Parameter Value" + assert: + that: + - '"Invalid Parameter Value" in result.msg' + always: + - name: cleanup topics + sns_topic: + name: "{{ notification_queue_name }}-bounce" + state: absent + - name: cleanup identity + aws_ses_identity: + identity: "{{ email_identity }}" + state: absent + # ============================================================ + - name: test disable feedback forwarding fails if no bounce topic + block: + - name: test topic + sns_topic: + name: "{{ notification_queue_name }}-complaint" + state: present + register: topic_info + - name: register email identity + aws_ses_identity: + identity: "{{ email_identity }}" + state: present + complaint_notifications: + topic: "{{ topic_info.sns_arn }}" + feedback_forwarding: No + register: result + failed_when: result.failed == False + - name: assert error message starts with "Invalid Parameter Value" + assert: + that: + - '"Invalid Parameter Value" in result.msg' + always: + - name: cleanup topics + sns_topic: + name: "{{ notification_queue_name }}-complaint" + state: absent + - name: cleanup identity + aws_ses_identity: + identity: "{{ email_identity }}" + state: absent diff --git a/ansible_collections/community/aws/tests/integration/targets/ses_identity_policy/aliases b/ansible_collections/community/aws/tests/integration/targets/ses_identity_policy/aliases new file mode 100644 index 000000000..4ef4b2067 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ses_identity_policy/aliases @@ -0,0 +1 @@ +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/ses_identity_policy/defaults/main.yaml b/ansible_collections/community/aws/tests/integration/targets/ses_identity_policy/defaults/main.yaml new file mode 100644 index 000000000..e77f32d08 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ses_identity_policy/defaults/main.yaml @@ -0,0 +1,3 @@ +--- +domain_identity: "{{ resource_prefix }}.example.com" +policy_name: "TestPolicy" diff --git a/ansible_collections/community/aws/tests/integration/targets/ses_identity_policy/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/ses_identity_policy/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ses_identity_policy/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/ses_identity_policy/tasks/main.yaml b/ansible_collections/community/aws/tests/integration/targets/ses_identity_policy/tasks/main.yaml new file mode 100644 index 000000000..5aa3d867b --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ses_identity_policy/tasks/main.yaml @@ -0,0 +1,309 @@ +--- +- name: 'aws_ses_identity_policy integration tests' + collections: + - amazon.aws + module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + block: + + # ============================================================ + - name: test add identity policy + block: + - name: register identity + aws_ses_identity: + identity: "{{ domain_identity }}" + state: present + register: identity_info + + - name: register identity policy + aws_ses_identity_policy: + identity: "{{ domain_identity }}" + policy_name: "{{ policy_name }}" + policy: "{{ lookup('template', 'policy.json.j2') }}" + state: present + register: result + + - name: assert result.changed == True + assert: + that: + - result.changed == True + + - name: assert result.policies contains only policy + assert: + that: + - result.policies|length == 1 + - result.policies|select('equalto', policy_name)|list|length == 1 + + always: + - name: clean-up identity + aws_ses_identity: + identity: "{{ domain_identity }}" + state: absent + # ============================================================ + - name: test add duplicate identity policy + block: + - name: register identity + aws_ses_identity: + identity: "{{ domain_identity }}" + state: present + register: identity_info + + - name: register identity policy + aws_ses_identity_policy: + identity: "{{ domain_identity }}" + policy_name: "{{ policy_name }}" + policy: "{{ lookup('template', 'policy.json.j2') }}" + state: present + + - name: register duplicate identity policy + aws_ses_identity_policy: + identity: "{{ domain_identity }}" + policy_name: "{{ policy_name }}" + policy: "{{ lookup('template', 'policy.json.j2') }}" + state: present + register: result + + - name: assert result.changed == False + assert: + that: + - result.changed == False + + - name: assert result.policies contains only policy + assert: + that: + - result.policies|length == 1 + - result.policies|select('equalto', policy_name)|list|length == 1 + + always: + - name: clean-up identity + aws_ses_identity: + identity: "{{ domain_identity }}" + state: absent + # ============================================================ + - name: test add identity policy by identity arn + block: + - name: register identity + aws_ses_identity: + identity: "{{ domain_identity }}" + state: present + register: identity_info + + - name: register identity policy + aws_ses_identity_policy: + identity: "{{ identity_info.identity_arn }}" + policy_name: "{{ policy_name }}" + policy: "{{ lookup('template', 'policy.json.j2') }}" + state: present + register: result + + - name: assert result.changed == True + assert: + that: + - result.changed == True + + - name: assert result.policies contains only policy + assert: + that: + - result.policies|length == 1 + - result.policies|select('equalto', policy_name)|list|length == 1 + + always: + - name: clean-up identity + aws_ses_identity: + identity: "{{ domain_identity }}" + state: absent + # ============================================================ + - name: test add multiple identity policies + block: + - name: register identity + aws_ses_identity: + identity: "{{ domain_identity }}" + state: present + register: identity_info + + - name: register identity policy + aws_ses_identity_policy: + identity: "{{ domain_identity }}" + policy_name: "{{ policy_name }}-{{ item }}" + policy: "{{ lookup('template', 'policy.json.j2') }}" + state: present + with_items: + - 1 + - 2 + register: result + + - name: assert result.policies contains policies + assert: + that: + - result.results[1].policies|length == 2 + - result.results[1].policies|select('equalto', policy_name + '-1')|list|length == 1 + - result.results[1].policies|select('equalto', policy_name + '-2')|list|length == 1 + + always: + - name: clean-up identity + aws_ses_identity: + identity: "{{ domain_identity }}" + state: absent + # ============================================================ + - name: test add inline identity policy + block: + - name: register identity + aws_ses_identity: + identity: "{{ domain_identity }}" + state: present + register: identity_info + + - name: register identity policy + aws_ses_identity_policy: + identity: "{{ domain_identity }}" + policy_name: "{{ policy_name }}" + policy: + Id: SampleAuthorizationPolicy + Version: "2012-10-17" + Statement: + - Sid: DenyAll + Effect: Deny + Resource: "{{ identity_info.identity_arn }}" + Principal: "*" + Action: "*" + state: present + register: result + + - name: assert result.changed == True + assert: + that: + - result.changed == True + + - name: assert result.policies contains only policy + assert: + that: + - result.policies|length == 1 + - result.policies|select('equalto', policy_name)|list|length == 1 + + - name: register duplicate identity policy + aws_ses_identity_policy: + identity: "{{ domain_identity }}" + policy_name: "{{ policy_name }}" + policy: + Id: SampleAuthorizationPolicy + Version: "2012-10-17" + Statement: + - Sid: DenyAll + Effect: Deny + Resource: "{{ identity_info.identity_arn }}" + Principal: "*" + Action: "*" + state: present + register: result + + - name: assert result.changed == False + assert: + that: + - result.changed == False + + always: + - name: clean-up identity + aws_ses_identity: + identity: "{{ domain_identity }}" + state: absent + # ============================================================ + - name: test remove identity policy + block: + - name: register identity + aws_ses_identity: + identity: "{{ domain_identity }}" + state: present + register: identity_info + + - name: register identity policy + aws_ses_identity_policy: + identity: "{{ domain_identity }}" + policy_name: "{{ policy_name }}" + policy: "{{ lookup('template', 'policy.json.j2') }}" + state: present + + - name: delete identity policy + aws_ses_identity_policy: + identity: "{{ domain_identity }}" + policy_name: "{{ policy_name }}" + state: absent + register: result + + - name: assert result.changed == True + assert: + that: + - result.changed == True + + - name: assert result.policies empty + assert: + that: + - result.policies|length == 0 + + always: + - name: clean-up identity + aws_ses_identity: + identity: "{{ domain_identity }}" + state: absent + # ============================================================ + - name: test remove missing identity policy + block: + - name: register identity + aws_ses_identity: + identity: "{{ domain_identity }}" + state: present + register: identity_info + + - name: delete identity policy + aws_ses_identity_policy: + identity: "{{ domain_identity }}" + policy_name: "{{ policy_name }}" + state: absent + register: result + + - name: assert result.changed == False + assert: + that: + - result.changed == False + + - name: assert result.policies empty + assert: + that: + - result.policies|length == 0 + + always: + - name: clean-up identity + aws_ses_identity: + identity: "{{ domain_identity }}" + state: absent + # ============================================================ + - name: test add identity policy with invalid policy + block: + - name: register identity + aws_ses_identity: + identity: "{{ domain_identity }}" + state: present + register: identity_info + + - name: register identity policy + aws_ses_identity_policy: + identity: "{{ domain_identity }}" + policy_name: "{{ policy_name }}" + policy: '{"noSuchAttribute": 2}' + state: present + register: result + failed_when: result.failed == False + + - name: assert error.code == InvalidPolicy + assert: + that: + - result.error.code == 'InvalidPolicy' + + always: + - name: clean-up identity + aws_ses_identity: + identity: "{{ domain_identity }}" + state: absent diff --git a/ansible_collections/community/aws/tests/integration/targets/ses_identity_policy/templates/policy.json.j2 b/ansible_collections/community/aws/tests/integration/targets/ses_identity_policy/templates/policy.json.j2 new file mode 100644 index 000000000..b198e38f7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ses_identity_policy/templates/policy.json.j2 @@ -0,0 +1,13 @@ +{ + "Id": "SampleAuthorizationPolicy", + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "DenyAll", + "Effect": "Deny", + "Resource": "{{ identity_info.identity_arn }}", + "Principal": "*", + "Action": "*" + } + ] +} diff --git a/ansible_collections/community/aws/tests/integration/targets/ses_rule_set/aliases b/ansible_collections/community/aws/tests/integration/targets/ses_rule_set/aliases new file mode 100644 index 000000000..4ef4b2067 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ses_rule_set/aliases @@ -0,0 +1 @@ +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/ses_rule_set/defaults/main.yaml b/ansible_collections/community/aws/tests/integration/targets/ses_rule_set/defaults/main.yaml new file mode 100644 index 000000000..53fbc388b --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ses_rule_set/defaults/main.yaml @@ -0,0 +1,9 @@ +--- +default_rule_set: "{{ tiny_prefix }}-default-rule-set" +second_rule_set: "{{ tiny_prefix }}-second-rule-set" + +# See comment in obtain-lock.yaml for definitions of these variables +max_obtain_lock_attempts: 10 +obtain_lock_delay_seconds: 30 +lock_timeout_seconds: 900 +lock_log_group_prefix: "ansible-testing-locks/aws_ses_rule_set" diff --git a/ansible_collections/community/aws/tests/integration/targets/ses_rule_set/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/ses_rule_set/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ses_rule_set/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/ses_rule_set/tasks/active-rule-set-tests.yaml b/ansible_collections/community/aws/tests/integration/targets/ses_rule_set/tasks/active-rule-set-tests.yaml new file mode 100644 index 000000000..ea79dbbcc --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ses_rule_set/tasks/active-rule-set-tests.yaml @@ -0,0 +1,305 @@ +--- +# ============================================================ +# These tests all rely on making rule sets active. There can only be +# a single active rule set so multiple builds must not run these tests +# in parallel or they will fail intermittently. +# See the locking block in main.yaml for how this restriction is enforced +# ============================================================ + +# ============================================================ +- name: mark rule set active + block: + - name: create rule set + aws_ses_rule_set: + name: "{{ default_rule_set }}" + - name: mark rule set active + aws_ses_rule_set: + name: "{{ default_rule_set }}" + active: True + register: result + - name: assert changed to active + assert: + that: + - result.changed == True + - result.active == True + - name: remark rule set active + aws_ses_rule_set: + name: "{{ default_rule_set }}" + active: True + register: result + - name: assert changed is False + assert: + that: + - result.changed == False + always: + - name: cleanup rule set + aws_ses_rule_set: + name: "{{ default_rule_set }}" + state: absent + force: True + +# ============================================================ +- name: create rule set active + block: + - name: create rule set + aws_ses_rule_set: + name: "{{ default_rule_set }}" + active: True + register: result + - name: assert changed to existing and active + assert: + that: + - result.changed == True + - result.active == True + - "default_rule_set in result.rule_sets|map(attribute='name')" + - name: remark rule set active + aws_ses_rule_set: + name: "{{ default_rule_set }}" + active: True + register: result + - name: assert changed is False + assert: + that: + - result.changed == False + always: + - name: cleanup rule set + aws_ses_rule_set: + name: "{{ default_rule_set }}" + state: absent + force: True + +# ============================================================ +- name: mark rule set inactive + block: + - name: create active rule set + aws_ses_rule_set: + name: "{{ default_rule_set }}" + active: True + - name: mark rule set inactive + aws_ses_rule_set: + name: "{{ default_rule_set }}" + active: False + register: result + - name: assert changed to inactive + assert: + that: + - result.changed == True + - result.active == False + - name: remark rule set inactive + aws_ses_rule_set: + name: "{{ default_rule_set }}" + active: False + register: result + - name: assert changed is False + assert: + that: + - result.changed == False + always: + - name: cleanup rule set + aws_ses_rule_set: + name: "{{ default_rule_set }}" + state: absent + force: True + +# ============================================================ +- name: Absent active flag does not change active status + block: + - name: create active rule set + aws_ses_rule_set: + name: "{{ default_rule_set }}" + active: True + - name: recreate rule set + aws_ses_rule_set: + name: "{{ default_rule_set }}" + register: result + - name: assert not changed and still active + assert: + that: + - result.changed == False + - result.active == True + always: + - name: cleanup rule set + aws_ses_rule_set: + name: "{{ default_rule_set }}" + state: absent + force: True + +# ============================================================ +- name: Cannot Remove Active Rule Set + block: + - name: create active rule set + aws_ses_rule_set: + name: "{{ default_rule_set }}" + active: True + - name: remove rule set + aws_ses_rule_set: + name: "{{ default_rule_set }}" + state: absent + register: result + failed_when: "result.error.code != 'CannotDelete'" + - name: assert error code is CannotDelete + assert: + that: + - "result.error.code == 'CannotDelete'" + always: + - name: cleanup rule set + aws_ses_rule_set: + name: "{{ default_rule_set }}" + state: absent + force: True + +# ============================================================ +- name: Remove Active Rule Set with Force + block: + - name: create active rule set + aws_ses_rule_set: + name: "{{ default_rule_set }}" + active: True + - name: force remove rule set + aws_ses_rule_set: + name: "{{ default_rule_set }}" + state: absent + force: True + register: result + - name: assert changed and absent + assert: + that: + - result.changed == True + - "default_rule_set not in result.rule_sets|map(attribute='name')" + always: + - name: cleanup rule set + aws_ses_rule_set: + name: "{{ default_rule_set }}" + state: absent + force: True + +# ============================================================ +- name: Force Remove of Inactive Rule Set does Not Affect Active Rule Set + block: + - name: create active rule set + aws_ses_rule_set: + name: "{{ default_rule_set }}" + active: True + - name: create inactive rule set + aws_ses_rule_set: + name: "{{ second_rule_set }}" + active: False + - name: force remove inactiave rule set + aws_ses_rule_set: + name: "{{ second_rule_set }}" + state: absent + force: True + register: result + - name: assert changed and absent + assert: + that: + - result.changed == True + - "second_rule_set not in result.rule_sets|map(attribute='name')" + - name: remark active rule set active + aws_ses_rule_set: + name: "{{ default_rule_set }}" + active: True + register: result + - name: assert no change + assert: + that: + - result.changed == False + always: + - name: cleanup rule set + aws_ses_rule_set: + name: "{{ item }}" + state: absent + force: True + loop: + - "{{ default_rule_set }}" + - "{{ second_rule_set }}" + +# ============================================================ +- name: mark rule set inactive in check mode + block: + - name: create rule set + aws_ses_rule_set: + name: "{{ default_rule_set }}" + active: True + - name: mark rule set inactive in check mode + aws_ses_rule_set: + name: "{{ default_rule_set }}" + active: False + register: result + check_mode: True + - name: assert changed to inactive + assert: + that: + - result.changed == True + - result.active == False + - name: remark rule set inactive + aws_ses_rule_set: + name: "{{ default_rule_set }}" + active: False + register: result + - name: assert changed is True since previous inactive was in check mode + assert: + that: + - result.changed == True + always: + - name: cleanup rule set + aws_ses_rule_set: + name: "{{ default_rule_set }}" + state: absent + force: True + +# ============================================================ +- name: Cannot Remove Active Rule Set in check mode + block: + - name: create active rule set + aws_ses_rule_set: + name: "{{ default_rule_set }}" + active: True + - name: remove rule set + aws_ses_rule_set: + name: "{{ default_rule_set }}" + state: absent + register: result + failed_when: "result.error.code != 'CannotDelete'" + check_mode: True + - name: assert error code is CannotDelete + assert: + that: + - "result.error.code == 'CannotDelete'" + always: + - name: cleanup rule set + aws_ses_rule_set: + name: "{{ default_rule_set }}" + state: absent + force: True + +# ============================================================ +- name: Remove Active Rule Set with Force in check mode + block: + - name: create active rule set + aws_ses_rule_set: + name: "{{ default_rule_set }}" + active: True + - name: force remove rule set + aws_ses_rule_set: + name: "{{ default_rule_set }}" + state: absent + force: True + register: result + check_mode: True + - name: assert changed and absent + assert: + that: + - result.changed == True + - "default_rule_set not in result.rule_sets|map(attribute='name')" + always: + - name: cleanup rule set + aws_ses_rule_set: + name: "{{ default_rule_set }}" + state: absent + force: True + register: result + - name: assert changed is True since previous removal was in check mode + assert: + that: + - result.changed == True diff --git a/ansible_collections/community/aws/tests/integration/targets/ses_rule_set/tasks/cleanup-lock.yaml b/ansible_collections/community/aws/tests/integration/targets/ses_rule_set/tasks/cleanup-lock.yaml new file mode 100644 index 000000000..155bf472e --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ses_rule_set/tasks/cleanup-lock.yaml @@ -0,0 +1,15 @@ +--- +# ============================================================ +# Release a lock obtained using obtain-lock.yaml +# This should be included in the always clause of a block to +# ensure the lock is released. See obtain-lock.yaml for more +# details of how the locking works. +# ============================================================ + +- cloudwatchlogs_log_group: + log_group_name: "{{ lock_attempt_log_group_name }}" + state: absent + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token }}" + region: "{{ aws_region }}" diff --git a/ansible_collections/community/aws/tests/integration/targets/ses_rule_set/tasks/inactive-rule-set-tests.yaml b/ansible_collections/community/aws/tests/integration/targets/ses_rule_set/tasks/inactive-rule-set-tests.yaml new file mode 100644 index 000000000..845168c23 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ses_rule_set/tasks/inactive-rule-set-tests.yaml @@ -0,0 +1,162 @@ +--- +# ============================================================ +# These tests work on rule sets without making them active. +# so multiple builds can safely run these tests as is normal. +# +# DO NOT ADD TESTS THAT RELY ON ACTIVE RULE SETS TO THIS FILE +# +# Any test that make rule sets active must be added in +# active-rule-set-tests.yaml or you will have intermittent failures +# from multiple builds interacting +# ============================================================ + +# ============================================================ +- name: test create rule sets + block: + - name: create rule set + aws_ses_rule_set: + name: "{{ default_rule_set }}" + register: result + - name: assert changed to exists inactive + assert: + that: + - result.changed == True + - result.active == False + - "default_rule_set in result.rule_sets|map(attribute='name')" + - name: recreate rule set + aws_ses_rule_set: + name: "{{ default_rule_set }}" + register: result + - name: assert changed is False + assert: + that: + - result.changed == False + always: + - name: cleanup rule set + aws_ses_rule_set: + name: "{{ default_rule_set }}" + state: absent + force: True +# ============================================================ +- name: Remove No Such Rules Set + block: + - name: remove ruleset + aws_ses_rule_set: + name: "{{ default_rule_set }}" + state: absent + register: result + - name: assert not changed and absent + assert: + that: + - result.changed == False + - "default_rule_set not in result.rule_sets|map(attribute='name')" +# ============================================================ +- name: Remove Inactive Rule Set + block: + - name: create rule set + aws_ses_rule_set: + name: "{{ default_rule_set }}" + - name: remove rule set + aws_ses_rule_set: + name: "{{ default_rule_set }}" + state: absent + register: result + - name: assert changed and removed + assert: + that: + - result.changed == True + - "default_rule_set not in result.rule_sets|map(attribute='name')" + always: + - name: cleanup rule set + aws_ses_rule_set: + name: "{{ default_rule_set }}" + state: absent + force: True +# ============================================================ +- name: test create in check mode + block: + - name: create rule set in check mode + aws_ses_rule_set: + name: "{{ default_rule_set }}" + register: result + check_mode: True + - name: assert changed inactive and present + assert: + that: + - result.changed == True + - result.active == False + - "default_rule_set in result.rule_sets|map(attribute='name')" + always: + - name: cleanup rule set + aws_ses_rule_set: + name: "{{ default_rule_set }}" + state: absent + force: True + register: result + - name: assert nothing to clean up since create was in check mode + assert: + that: + - result.changed == False +# ============================================================ +- name: mark rule set active in check mode + block: + - name: create rule set + aws_ses_rule_set: + name: "{{ default_rule_set }}" + - name: mark rule set active in check mode + aws_ses_rule_set: + name: "{{ default_rule_set }}" + active: True + register: result + check_mode: True + - name: assert changed and active + assert: + that: + - result.changed == True + - result.active == True + # We check the rule set is still inactive rather than making + # it active again as that way this test can be run in + # parallel + - name: Ensure rule set is inactive + aws_ses_rule_set: + name: "{{ default_rule_set }}" + active: False + register: result + - name: assert unchanged since activation was in check mode + assert: + that: + - result.changed == False + always: + - name: cleanup rule set + aws_ses_rule_set: + name: "{{ default_rule_set }}" + state: absent + force: True +# ============================================================ +- name: Remove Inactive Rule Set in check mode + block: + - name: create rule set + aws_ses_rule_set: + name: "{{ default_rule_set }}" + - name: remove rule set + aws_ses_rule_set: + name: "{{ default_rule_set }}" + state: absent + register: result + check_mode: True + - name: assert changed and removed + assert: + that: + - result.changed == True + - "default_rule_set not in result.rule_sets|map(attribute='name')" + always: + - name: cleanup rule set + aws_ses_rule_set: + name: "{{ default_rule_set }}" + state: absent + force: True + register: result + - name: assert changed is True since previous removal was in check mode + assert: + that: + - result.changed == True diff --git a/ansible_collections/community/aws/tests/integration/targets/ses_rule_set/tasks/main.yaml b/ansible_collections/community/aws/tests/integration/targets/ses_rule_set/tasks/main.yaml new file mode 100644 index 000000000..4902b5c60 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ses_rule_set/tasks/main.yaml @@ -0,0 +1,47 @@ +--- +- name: 'aws_ses_rule_set integration tests' + collections: + - amazon.aws + module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + block: + + - include_tasks: inactive-rule-set-tests.yaml + + # ============================================================ + # There can only be a single active rule set, tests that + # relies on the active state of the rule cannot be run in + # parallel. + # To prevent failures due to parallel runs in the integration + # builds, the below block creates a lock to ensure that only + # one process will be running these tests in the same region + # and same AWS account. + # See obtain-lock.yaml for explanation of how the lock is + # constructed. + # ============================================================ + - name: Active Rule Set Tests + block: + - name: Obtain Lock + include_tasks: obtain-lock-wrapper.yaml + # Use of loop here is a workaround for lack of support for + # do-until loops on includes. See: + # https://github.com/ansible/ansible/issues/17098 + loop: "{{ range(0, max_obtain_lock_attempts, 1)|list }}" + loop_control: + loop_var: obtain_lock_attempt + + # Because of the above workaround we have to explicitly check + # that the lock was obtained + - name: Check Obtained Lock + assert: + msg: "Could not obtain lock after {{ max_obtain_lock_attempts }} attempts." + that: won_lock|bool + + - include_tasks: active-rule-set-tests.yaml + + always: + - include_tasks: cleanup-lock.yaml diff --git a/ansible_collections/community/aws/tests/integration/targets/ses_rule_set/tasks/obtain-lock-wrapper.yaml b/ansible_collections/community/aws/tests/integration/targets/ses_rule_set/tasks/obtain-lock-wrapper.yaml new file mode 100644 index 000000000..36969897c --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ses_rule_set/tasks/obtain-lock-wrapper.yaml @@ -0,0 +1,26 @@ +# ============================================================ +# Do While loops cannot be used on task includes. +# See: https://github.com/ansible/ansible/issues/17098 +# +# So as a workaround we use a regular loop to repeatedly attempt +# obtaining a lock. +# +# For this to work we need to skip the subsequent iterations +# once we get a lock, and delay between iterations if we +# did not obtain the lock. +# +# This file encapsulates this logic to reduce the spam from +# skipped tasks in the ansible log. +# ============================================================ + +- include_tasks: obtain-lock.yaml + # Skip obtaining a lock if we've already succeeded in getting it + when: "not won_lock|default(False)|bool" + +- name: Lock Retry Delay + wait_for: + # Add some random jitter to the delay to reduce lock contention + timeout: "{{ obtain_lock_delay_seconds + 15|random }}" + # Only delay if we're retrying, so skip the delay if we're + # on the last attempt or have got the lock + when: "obtain_lock_attempt < (max_obtain_lock_attempts - 1) and not won_lock|bool" diff --git a/ansible_collections/community/aws/tests/integration/targets/ses_rule_set/tasks/obtain-lock.yaml b/ansible_collections/community/aws/tests/integration/targets/ses_rule_set/tasks/obtain-lock.yaml new file mode 100644 index 000000000..3ed19959e --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ses_rule_set/tasks/obtain-lock.yaml @@ -0,0 +1,113 @@ +# ============================================================ +# This file attempts to obtain a global lock (for a given +# region / account combination. +# +# This makes one attempt to get the lock and will set the +# won_lock variable to True or False to indicate whether +# or not we got the lock. +# +# It's expected that this will be executed in a retry loop +# so that if we don't get the lock we delay then try again. +# +# This should only be used in a block with cleanup-lock.yaml +# included in the always clause to ensure the lock is released. +# +# There are several variables that control the locking behaviour: +# * lock_timeout_seconds +# How old a lock must be before it's assumed to be an expired +# lock that was not cleaned up by the owner. Any locks older +# than this will not prevent a lock being obtained and will +# be deleted when a new process obtains the lock. +# * lock_log_group_prefix +# The log_group prefix that represents the lock being obtained. +# This must be the same across all processes trying to obtain +# the lock. +# * lock_process_id +# A unique identifier of this process. Each process that might +# attempt to lock the process must have a different identifier. +# This defaults to the resource_prefix which is generally +# appropriate. +# * max_obtain_lock_attempts +# How many attempts to make to get the lock before giving up +# NB: This is actually done in main.yaml +# * obtain_lock_delay_seconds: +# How long to delay after failing to get the lock before +# trying again. +# NB: This is actually done in obtain-lock-wrapper.yaml +# +# The locking here is based around creating cloudwatch log groups. +# This resource was chosen because: +# A) it's free +# B) we have a built in grouping concept because of the hierarchy +# that allows us to easily group attempts for the same lock +# C) the creation time is tracked and returned which gives us +# a mechanism for deterministically picking a winner +# +# Each lock is represented by a log group prefix. Each attempt +# to obtain the lock is a log group of the lock_process_id below +# that prefix. +# +# The winning lock is the one with the earliest creation time. +# +# To prevent a hanging lock from permanently hanging the build +# lock attempts older than the lock timeout are ignored and +# cleaned up by the next process to win the lock. +# ============================================================ + +- name: Set lock_attempt_log_group_name + set_fact: + lock_attempt_log_group_name: "{{ lock_log_group_prefix }}/{{ lock_process_id|default(resource_prefix) }}" + + # Note the overwrite below to ensure that the creation time + # is upated. This is important as we calculate expiry relative + # the attempt creation. + # + # Because of this it's imporatnt that we delete the attempt + # if we don't get the lock. Otherwise we can get a deadlock + # where the stale atttempt from one process wins, but then + # because that process updates the creation date it doesn't + # consider its self to havewone. +- name: Create Lock Attempt Log Group + cloudwatchlogs_log_group: + log_group_name: "{{ lock_attempt_log_group_name }}" + state: present + overwrite: True + register: lock_attempt_log_group_result + +- name: Get Lock Attempt Lock Groups + cloudwatchlogs_log_group_info: + log_group_name: "{{ lock_log_group_prefix }}/" + register: lock_attempt_log_groups + +- name: Calculate Expired Lock Attempt Timestamp + set_fact: + expired_lock_timestamp: "{{ lock_attempt_log_group_result.creation_time - (lock_timeout_seconds * 1000) }}" + +- name: Get Expired and Active Lock Attempts + set_fact: + expired_lock_attempts: "{{ lock_attempt_log_groups.log_groups|selectattr('creation_time', 'lt', expired_lock_timestamp|int)|list }}" + active_lock_attempts: "{{ lock_attempt_log_groups.log_groups|selectattr('creation_time', 'ge', expired_lock_timestamp|int)|list }}" + +- name: Pick Winning Lock Attempt + set_fact: + winning_lock_attempt: "{{ active_lock_attempts|sort(attribute='creation_time')|first }}" + +- name: Determine if Won Lock + set_fact: + won_lock: "{{ winning_lock_attempt.log_group_name == lock_attempt_log_group_name }}" + + # Remove the lock attempt if we didn't get the lock. This prevents + # our stale lock attempt blocking another process from getting the lock. + # See more detailed comment above Create Lock Attempt Log Group +- name: Remove Failed Lock Attempt Log Group + cloudwatchlogs_log_group: + log_group_name: "{{ lock_attempt_log_group_name }}" + state: absent + when: "not won_lock|bool" + +- name: Delete Expired Lock Attempts + cloudwatchlogs_log_group: + log_group_name: "{{ item.log_group_name }}" + state: absent + when: "won_lock|bool" + loop: "{{ expired_lock_attempts }}" diff --git a/ansible_collections/community/aws/tests/integration/targets/setup_botocore_pip/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/setup_botocore_pip/defaults/main.yml new file mode 100644 index 000000000..16ad00270 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/setup_botocore_pip/defaults/main.yml @@ -0,0 +1,2 @@ +default_botocore_version: '1.21.0' +default_boto3_version: '1.18.0' diff --git a/ansible_collections/community/aws/tests/integration/targets/setup_botocore_pip/handlers/main.yml b/ansible_collections/community/aws/tests/integration/targets/setup_botocore_pip/handlers/main.yml new file mode 100644 index 000000000..2536d1ac7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/setup_botocore_pip/handlers/main.yml @@ -0,0 +1,2 @@ +- name: 'Delete temporary pip environment' + include_tasks: cleanup.yml diff --git a/ansible_collections/community/aws/tests/integration/targets/setup_botocore_pip/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/setup_botocore_pip/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/setup_botocore_pip/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/setup_botocore_pip/tasks/cleanup.yml b/ansible_collections/community/aws/tests/integration/targets/setup_botocore_pip/tasks/cleanup.yml new file mode 100644 index 000000000..25b3ec27e --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/setup_botocore_pip/tasks/cleanup.yml @@ -0,0 +1,5 @@ +- name: 'Delete temporary pip environment' + file: + path: "{{ botocore_pip_directory }}" + state: absent + no_log: yes diff --git a/ansible_collections/community/aws/tests/integration/targets/setup_botocore_pip/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/setup_botocore_pip/tasks/main.yml new file mode 100644 index 000000000..b183b7d72 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/setup_botocore_pip/tasks/main.yml @@ -0,0 +1,42 @@ +- name: 'Ensure that we have virtualenv available to us' + pip: + name: virtualenv + +- name: 'Create temporary directory for pip environment' + tempfile: + state: directory + prefix: botocore + suffix: .test + register: botocore_pip_directory + notify: + - 'Delete temporary pip environment' + +- name: 'Record temporary directory' + set_fact: + botocore_pip_directory: "{{ botocore_pip_directory.path }}" + +- set_fact: + botocore_virtualenv: "{{ botocore_pip_directory }}/virtualenv" + botocore_virtualenv_command: "{{ ansible_python_interpreter }} -m virtualenv" + +- set_fact: + botocore_virtualenv_interpreter: "{{ botocore_virtualenv }}/bin/python" + +- pip: + name: + - 'boto3{{ _boto3_comparison }}{{ _boto3_version }}' + - 'botocore{{ _botocore_comparison }}{{ _botocore_version }}' + - 'coverage<5' + virtualenv: "{{ botocore_virtualenv }}" + virtualenv_command: "{{ botocore_virtualenv_command }}" + virtualenv_site_packages: no + vars: + _boto3_version: '{{ boto3_version | default(default_boto3_version) }}' + _botocore_version: '{{ botocore_version | default(default_botocore_version) }}' + _is_default_boto3: '{{ _boto3_version == default_boto3_version }}' + _is_default_botocore: '{{ _botocore_version == default_botocore_version }}' + # Only set the default to >= if the other dep has been updated and the dep has not been set + _default_boto3_comparison: '{% if _is_default_boto3 and not _is_default_botocore %}>={% else %}=={% endif %}' + _default_botocore_comparison: '{% if _is_default_botocore and not _is_default_boto3 %}>={% else %}=={% endif %}' + _boto3_comparison: '{{ boto3_comparison | default(_default_boto3_comparison) }}' + _botocore_comparison: '{{ botocore_comparison | default(_default_botocore_comparison) }}' diff --git a/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/README.md b/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/README.md new file mode 100644 index 000000000..bc12a83e1 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/README.md @@ -0,0 +1,43 @@ +# AWS SSM Integration Test Setup + +## aws_ssm_integration_test_setup_teardown + +An Ansible role was created to perform integration test across aws_ssm connection plugin. The role performs the following actions. + +- Create AWS Resources in user specified region. +- Perform integration Test across aws_ssm connection plugin. +- TearDown/Remove AWS Resources that are created for testing plugin. + +### Prerequisites + +- Make sure the machine used for testing already has Ansible repo with ssm connection plugin. +- AWS CLI/IAM-Role configured to the machine which has permissions to spin-up AWS resources. + +### Variables referred in Ansible Role + +The following table provide details about variables referred within Ansible Role. + +| Variable Name | Details | +| ------ | ------ | +| aws_region | Name of AWS-region | +| iam_role_name | Name of IAM Role which will be attached to newly-created EC2-Instance | +| iam_policy_name | Name of IAM Policy which will be attached to the IAM role referred above | +| instance_type | Instance type user for creating EC2-Instance | +| instance_id | AWS EC2 Instance-Id (This gets populated by role) | +| bucket_name | Name of S3 buckted used by SSM (This gets populated by role) | + +### Example Playbook + +A sample example to demonstrate the usage of role within Ansible-playbook.(Make sure the respective variables are passed as parameters.) + +```yaml + - hosts: localhost + roles: + - aws_ssm_integration_test_setup_teardown +``` + +#### Author's Information + +Krishna Nand Choudhary (krishnanandchoudhary) +Nikhil Araga (araganik) +Gaurav Ashtikar (gau1991) diff --git a/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/aliases b/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/aliases new file mode 100644 index 000000000..fc6c7dd0f --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/aliases @@ -0,0 +1,3 @@ +# Used by the connection_amazon_aws plugins to build/destroy test infrastructure +hidden +disabled diff --git a/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/defaults/main.yml new file mode 100644 index 000000000..ec7cf0ec6 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/defaults/main.yml @@ -0,0 +1,45 @@ +--- +instance_type: t3.micro + +ami_details: + fedora: + owner: 125523088429 + name: Fedora-Cloud-Base-34-1.2.x86_64* + user_data: | + #!/bin/sh + sudo dnf install -y https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/linux_amd64/amazon-ssm-agent.rpm + sudo systemctl start amazon-ssm-agent + os_type: linux + amazon: + ssm_parameter: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2 + # owner: amazon + # name: amzn2-ami-kernel-5.10-hvm-*-x86_64-gp2 + user_data: | + #!/bin/sh + # Pre-Installed just needs started + sudo systemctl start amazon-ssm-agent + os_type: linux + ubuntu: + ssm_parameter: /aws/service/canonical/ubuntu/server-minimal/jammy/stable/current/amd64/hvm/ebs-gp2/ami-id + # owner: amazon + # name: ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server* + user_data: | + #!/bin/sh + # Pre-Installed just needs started + sudo systemctl start amazon-ssm-agent + os_type: linux + windows: + ssm_parameter: /aws/service/ami-windows-latest/Windows_Server-2022-English-Full-Base + # owner: amazon + # name: Windows_Server-2022-English-Full-Base-* + user_data: | + <powershell> + Invoke-WebRequest -Uri "https://amazon-ssm-us-east-1.s3.amazonaws.com/latest/windows_amd64/AmazonSSMAgentSetup.exe" -OutFile "C:\AmazonSSMAgentSetup.exe" + Start-Process -FilePath C:\AmazonSSMAgentSetup.exe -ArgumentList "/S","/v","/qn" -Wait + Restart-Service AmazonSSMAgent + </powershell> + os_type: windows + +s3_bucket_name: "{{ tiny_prefix }}-connection-ssm-{{ test_suffix | default(target_os) }}" +kms_key_name: "{{ resource_prefix }}-connection-ssm" +ssm_document_name: "{{ resource_prefix }}-connection-ssm" diff --git a/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/files/ec2-trust-policy.json b/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/files/ec2-trust-policy.json new file mode 100644 index 000000000..63d22eaec --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/files/ec2-trust-policy.json @@ -0,0 +1,13 @@ +{ + "Version": "2008-10-17", + "Statement": [ + { + "Sid": "", + "Effect": "Allow", + "Principal": { + "Service": "ec2.amazonaws.com" + }, + "Action": "sts:AssumeRole" + } + ] + } diff --git a/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/files/ssm-document.json b/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/files/ssm-document.json new file mode 100644 index 000000000..b3e5d9114 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/files/ssm-document.json @@ -0,0 +1,15 @@ +{ + "schemaVersion": "1.0", + "description": "Custom SSM document", + "sessionType": "Standard_Stream", + "inputs": { + "s3EncryptionEnabled": false, + "cloudWatchLogGroupName": "", + "cloudWatchEncryptionEnabled": false, + "idleSessionTimeout": "20", + "cloudWatchStreamingEnabled": false, + "kmsKeyId": "", + "runAsEnabled": false, + "runAsDefaultUser": "" + } +} diff --git a/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/tasks/cleanup.yml b/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/tasks/cleanup.yml new file mode 100644 index 000000000..6171e5eb6 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/tasks/cleanup.yml @@ -0,0 +1,88 @@ +--- +- name: 'aws_ssm connection plugin integration test resource cleanup' + collections: + - amazon.aws + module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + block: + + - name: setup connection argments fact + include_tasks: 'connection_args.yml' + + - name: Check if instance_vars_to_delete.yml is present + stat: + path: "{{ playbook_dir }}/instance_vars_to_delete.yml" + register: ec2_vars_file + + - name: Include variable file to delete EC2 infra + include_vars: "{{ playbook_dir }}/instance_vars_to_delete.yml" + when: ec2_vars_file.stat.exists == true + + - name: Check if s3_vars_to_delete.yml is present + stat: + path: "{{playbook_dir}}/s3_vars_to_delete.yml" + register: s3_vars_file + + - name: Include variable file to delete S3 Infra infra + include_vars: "{{playbook_dir}}/s3_vars_to_delete.yml" + when: s3_vars_file.stat.exists == true + + - name: Check if iam_role_vars_to_delete.yml is present + stat: + path: "{{ playbook_dir }}/iam_role_vars_to_delete.yml" + register: iam_role_vars_file + + - name: Include variable file to delete IAM Role infra + include_vars: "{{ playbook_dir }}/iam_role_vars_to_delete.yml" + when: iam_role_vars_file.stat.exists == true + + - name: Check if ssm_vars_to_delete.yml is present + stat: + path: "{{ playbook_dir }}/ssm_vars_to_delete.yml" + register: ssm_vars_file + + - name: Include variable file to delete SSM infra + include_vars: "{{ playbook_dir }}/ssm_vars_to_delete.yml" + when: ssm_vars_file.stat.exists == true + + - name: Terminate EC2 instances that were previously launched + ec2_instance: + instance_ids: "{{ created_instance_ids }}" + state: absent + wait: True + ignore_errors: yes + when: ec2_vars_file.stat.exists == true + + - name: Delete S3 bucket + include_tasks: 'delete_bucket.yml' + loop: + - "{{ bucket_name }}" + when: s3_vars_file.stat.exists == true + ignore_errors: yes + + - name: Delete IAM role + iam_role: + name: "{{ iam_role_name }}" + state: absent + ignore_errors: yes + when: iam_role_vars_file.stat.exists == true + + - name: Delete the KMS key + aws_kms: + state: absent + alias: '{{ kms_key_name }}' + + - name: Delete SSM document + command: "aws ssm delete-document --name {{ ssm_document_name }}" + environment: "{{ connection_env }}" + ignore_errors: yes + + - name: Delete AWS keys environement + file: + path: "{{ playbook_dir }}/aws-env-vars.sh" + state: absent + ignore_errors: yes diff --git a/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/tasks/connection_args.yml b/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/tasks/connection_args.yml new file mode 100644 index 000000000..727220e49 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/tasks/connection_args.yml @@ -0,0 +1,14 @@ +--- +- set_fact: + # As a lookup plugin we don't have access to module_defaults + connection_args: + region: "{{ aws_region }}" + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + aws_security_token: "{{ security_token | default(omit) }}" + connection_env: + AWS_DEFAULT_REGION: "{{ aws_region }}" + AWS_ACCESS_KEY_ID: "{{ aws_access_key }}" + AWS_SECRET_ACCESS_KEY: "{{ aws_secret_key }}" + AWS_SESSION_TOKEN: "{{ security_token | default(omit) }}" + no_log: True diff --git a/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/tasks/debian.yml b/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/tasks/debian.yml new file mode 100644 index 000000000..2fa55723f --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/tasks/debian.yml @@ -0,0 +1,14 @@ +- name: Download SSM plugin + get_url: + url: https://s3.amazonaws.com/session-manager-downloads/plugin/latest/ubuntu_64bit/session-manager-plugin.deb + dest: /tmp/session-manager-plugin.deb + mode: '0440' + tags: setup_infra +- name: Install SSM Plugin + become: true + apt: + deb: /tmp/session-manager-plugin.deb + tags: setup_infra +- name: Check the SSM Plugin + shell: /usr/local/sessionmanagerplugin/bin/session-manager-plugin --version + tags: setup_infra diff --git a/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/tasks/delete_bucket.yml b/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/tasks/delete_bucket.yml new file mode 100644 index 000000000..f5b390311 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/tasks/delete_bucket.yml @@ -0,0 +1,25 @@ +- name: delete bucket at the end of Integration tests + block: + - name: list bucket object + amazon.aws.s3_object: + bucket: "{{ item }}" + mode: list + register: objects + ignore_errors: true + + - name: remove objects from bucket + amazon.aws.s3_object: + bucket: "{{ item }}" + mode: delobj + object: "{{ obj }}" + with_items: "{{ objects.s3_keys }}" + loop_control: + loop_var: obj + when: "'s3_keys' in objects" + ignore_errors: true + + - name: delete the bucket + amazon.aws.s3_bucket: + name: "{{ item }}" + state: absent + ignore_errors: true diff --git a/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/tasks/encryption.yml b/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/tasks/encryption.yml new file mode 100644 index 000000000..949892d18 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/tasks/encryption.yml @@ -0,0 +1,43 @@ +--- +## Task file for setup/teardown AWS resources for aws_ssm integration testing +- name: create a KMS key + aws_kms: + alias: '{{ kms_key_name }}' + grants: + - name: SSM-Agent-Access + grantee_principal: '{{ role_output.iam_role.arn }}' + retiring_principal: '{{ aws_caller_info.arn }}' + operations: + - Decrypt + - Encrypt + - GenerateDataKey + - GenerateDataKeyWithoutPlaintext + - DescribeKey + - Verify + - Sign + - RetireGrant + - name: Ansible-Test-Access + grantee_principal: '{{ aws_caller_info.arn }}' + retiring_principal: '{{ aws_caller_info.arn }}' + operations: + - Decrypt + - Encrypt + - GenerateDataKey + - GenerateDataKeyWithoutPlaintext + - DescribeKey + - Verify + - Sign + - RetireGrant + tags: + ansible-test: '{{ resource_prefix }}-connection-ssm' + register: cmk_setup + +- name: Ensure bucket is configured for encryption + s3_bucket: + name: "{{ s3_bucket_name }}" + region: "{{ s3_bucket_region | default(omit)}}" + encryption: "aws:kms" + encryption_key_id: "{{ cmk_setup.key_arn }}" + bucket_key_enabled: "{{ s3_bucket_encryption | default(False) }}" + when: + - encrypted_bucket | default(False) diff --git a/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/tasks/main.yml new file mode 100644 index 000000000..830bd5fcc --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/tasks/main.yml @@ -0,0 +1,155 @@ +--- +## Task file for setup/teardown AWS resources for aws_ssm integration testing +- name: 'aws_ssm connection plugin integration test resource creation' + collections: + - amazon.aws + module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + block: + + - name: get ARN of calling user + aws_caller_info: + register: aws_caller_info + + - name: setup connection argments fact + include_tasks: 'connection_args.yml' + + - name: Ensure IAM instance role exists + iam_role: + name: "ansible-test-{{tiny_prefix}}-aws-ssm-role" + assume_role_policy_document: "{{ lookup('file','ec2-trust-policy.json') }}" + state: present + create_instance_profile: yes + managed_policy: + - AmazonSSMManagedInstanceCore + wait: True + register: role_output + + - name: Lookup AMI configuration + set_fact: + ami_configuration: '{{ ami_details[(target_os | default("fedora"))] }}' + + - name: AMI Lookup (ami_info) + ec2_ami_info: + owners: '{{ ami_configuration.owner | default("amazon") }}' + filters: + name: '{{ ami_configuration.name }}' + register: ec2_amis + when: + - ami_configuration.name | default(False) + + - name: AMI Lookup (SSM Parameter) + when: + - ami_configuration.ssm_parameter | default(False) + block: + - set_fact: + ssm_amis: "{{ lookup('aws_ssm', ami_configuration.ssm_parameter, **connection_args) }}" + + - name: Set facts with latest AMIs + vars: + latest_ami: '{{ ec2_amis.images | default([]) | sort(attribute="creation_date") | last }}' + set_fact: + latest_ami_id: '{{ ssm_amis | default(latest_ami.image_id) }}' + + # (Local installation of the SSM **client** which is then used by the plugin) + - name: Install Session Manager Client for Debian/Ubuntu + include_tasks: debian.yml + when: ansible_distribution in ["Ubuntu", "Debian"] + register: install_plugin_debian + + - name: Install Session Manager Client for RedHat/Amazon + include_tasks: redhat.yml + when: ansible_distribution in ["CentOS", "RedHat", "Amazon", "Fedora"] + register: install_plugin_redhat + + - block: + - name: Fail if the plugin was not installed + fail: + msg: The distribution does not contain the required Session Manager Plugin + when: + - install_plugin_debian is skipped + - install_plugin_redhat is skipped + always: + - debug: + var: ansible_distribution + + - name: Create EC2 instance + ec2_instance: + instance_type: "{{ instance_type }}" + ebs_optimized: True + image_id: "{{ latest_ami_id }}" + wait: "yes" + instance_role: "{{ role_output.iam_role.role_name }}" + name: "{{ resource_prefix }}-connection-aws-ssm" + user_data: "{{ ami_configuration.user_data }}" + state: running + tags: + TestPrefix: '{{ resource_prefix }}' + register: instance_output + + - name: setup SSM document + include_tasks: 'ssm_document.yml' + when: + - use_ssm_document | default(False) + + - name: Create S3 bucket + s3_bucket: + name: "{{ s3_bucket_name }}" + region: "{{ s3_bucket_region | default(omit)}}" + register: s3_output + + - name: setup encryption + include_tasks: 'encryption.yml' + when: + - encrypted_bucket | default(False) + + - name: Create Inventory file + template: + dest: "{{ playbook_dir }}/ssm_inventory" + src: inventory-combined.aws_ssm.j2 + + - name: Create AWS Keys Environement + template: + dest: "{{ playbook_dir }}/aws-env-vars.sh" + src: aws-env-vars.j2 + no_log: yes + + - name: Create out boto3 config file' + template: + dest: "{{ playbook_dir }}/boto3_config" + src: "boto_config.j2" + + always: + - name: Create EC2 Linux vars_to_delete.yml + template: + dest: "{{ playbook_dir }}/instance_vars_to_delete.yml" + src: ec2_instance_vars_to_delete.yml.j2 + ignore_errors: yes + when: + - instance_output is successful + + - name: Create IAM Role vars_to_delete.yml + template: + dest: "{{ playbook_dir }}/iam_role_vars_to_delete.yml" + src: iam_role_vars_to_delete.yml.j2 + when: + - role_output is successful + ignore_errors: yes + + - name: Create S3.yml + template: + dest: "{{ playbook_dir }}/s3_vars_to_delete.yml" + src: s3_vars_to_delete.yml.j2 + when: + - s3_output is successful + ignore_errors: yes + + - name: Create SSM vars_to_delete.yml + template: + dest: "{{ playbook_dir }}/ssm_vars_to_delete.yml" + src: ssm_vars_to_delete.yml.j2 + ignore_errors: yes diff --git a/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/tasks/redhat.yml b/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/tasks/redhat.yml new file mode 100644 index 000000000..52b3540c0 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/tasks/redhat.yml @@ -0,0 +1,16 @@ +- name: Download SSM plugin + get_url: + url: https://s3.amazonaws.com/session-manager-downloads/plugin/latest/linux_64bit/session-manager-plugin.rpm + dest: /tmp/session-manager-plugin.rpm + mode: '0440' + tags: setup_infra +- name: Install SSM Plugin + become: true + yum: + name: /tmp/session-manager-plugin.rpm + state: present + disable_gpg_check: true + tags: setup_infra +- name: Check the SSM Plugin + shell: /usr/local/sessionmanagerplugin/bin/session-manager-plugin --version + tags: setup_infra diff --git a/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/tasks/ssm_document.yml b/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/tasks/ssm_document.yml new file mode 100644 index 000000000..2e0781dd9 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/tasks/ssm_document.yml @@ -0,0 +1,11 @@ +--- +- block: + - name: Create custom SSM document + command: "aws ssm create-document --content file://{{ role_path }}/files/ssm-document.json --name {{ ssm_document_name }} --document-type Session" + environment: "{{ connection_env }}" + always: + - name: Create SSM vars_to_delete.yml + template: + dest: "{{ playbook_dir }}/ssm_vars_to_delete.yml" + src: ssm_vars_to_delete.yml.j2 + ignore_errors: yes diff --git a/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/templates/aws-env-vars.j2 b/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/templates/aws-env-vars.j2 new file mode 100644 index 000000000..1ae02f8a5 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/templates/aws-env-vars.j2 @@ -0,0 +1,6 @@ +export AWS_ACCESS_KEY_ID={{aws_access_key}} +export AWS_SECRET_ACCESS_KEY={{aws_secret_key}} +{% if security_token is defined %} +export AWS_SESSION_TOKEN={{security_token}} +{% endif %} +export AWS_REGION={{aws_region}} diff --git a/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/templates/boto_config.j2 b/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/templates/boto_config.j2 new file mode 100644 index 000000000..f8668f057 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/templates/boto_config.j2 @@ -0,0 +1,5 @@ +[profile test_profile] +region = {{ aws_region }} +aws_access_key_id = {{ session_access_key | default(aws_access_key) }} +aws_secret_access_key = {{ session_secret_key | default(aws_secret_key) }} +aws_security_token = {{ session_security_token | default(security_token) }} diff --git a/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/templates/ec2_instance_vars_to_delete.yml.j2 b/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/templates/ec2_instance_vars_to_delete.yml.j2 new file mode 100644 index 000000000..6165486b4 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/templates/ec2_instance_vars_to_delete.yml.j2 @@ -0,0 +1,7 @@ +--- +created_instance_ids: +{% if instance_output | default(False) %} +{% for instance_id in instance_output.instance_ids %} +- {{ instance_id }} +{% endfor %} +{% endif %} diff --git a/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/templates/ec2_windows_vars_to_delete.yml.j2 b/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/templates/ec2_windows_vars_to_delete.yml.j2 new file mode 100644 index 000000000..67c00ebf8 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/templates/ec2_windows_vars_to_delete.yml.j2 @@ -0,0 +1,2 @@ +--- +windows_instance_id: {{ windows_output.instance_ids[0] }} diff --git a/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/templates/iam_role_vars_to_delete.yml.j2 b/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/templates/iam_role_vars_to_delete.yml.j2 new file mode 100644 index 000000000..0d87d3ed6 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/templates/iam_role_vars_to_delete.yml.j2 @@ -0,0 +1,2 @@ +--- +iam_role_name: {{role_output.iam_role.role_name}} diff --git a/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/templates/inventory-combined.aws_ssm.j2 b/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/templates/inventory-combined.aws_ssm.j2 new file mode 100644 index 000000000..1d0100144 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/templates/inventory-combined.aws_ssm.j2 @@ -0,0 +1,63 @@ + +[aws_ssm_linux] +{% if instance_output | default(False) %} +{% if ( ami_configuration.os_type | default("linux")) == "linux" %} +linux_{{ instance_output.instance_ids[0] }} ansible_aws_ssm_instance_id={{ instance_output.instance_ids[0] }} ansible_aws_ssm_region={{ aws_region }} +{% endif %} +{% endif %} + +[aws_ssm_windows] +{% if instance_output | default(False) %} +{% if ( ami_configuration.os_type | default("linux")) == "windows" %} +windows_{{ instance_output.instance_ids[0] }} ansible_aws_ssm_instance_id={{ instance_output.instance_ids[0] }} ansible_aws_ssm_region={{ aws_region }} +{% endif %} +{% endif %} + +[aws_ssm_linux:vars] +remote_tmp=/tmp/ansible-remote +action_prefix=ansible.builtin. + +[aws_ssm_windows:vars] +ansible_shell_type=powershell +remote_tmp=c:/windows/temp/ansible-remote +action_prefix=ansible.windows.win_ + +[aws_ssm:children] +aws_ssm_linux +aws_ssm_windows + +[aws_ssm:vars] +ansible_connection=community.aws.aws_ssm +ansible_aws_ssm_plugin=/usr/local/sessionmanagerplugin/bin/session-manager-plugin +ansible_python_interpreter=/usr/bin/env python3 +local_tmp=/tmp/ansible-local-{{ tiny_prefix }} +ansible_aws_ssm_bucket_name={{ s3_bucket_name }} +{% if s3_addressing_style | default(False) %} +ansible_aws_ssm_s3_addressing_style={{ s3_addressing_style }} +{% endif %} +{% if encrypted_bucket | default(False) %} +{% if not (s3_bucket_encryption | default(False)) %} +ansible_aws_ssm_bucket_sse_mode='aws:kms' +ansible_aws_ssm_bucket_sse_kms_key_id=alias/{{ kms_key_name }} +{% endif %} +{% endif %} +{% if use_ssm_document | default(False) %} +ansible_aws_ssm_document={{ ssm_document_name }} +{% endif %} +{% if endpoint_url | default(False) %} +ansible_aws_ssm_bucket_endpoint_url={{ endpoint_url }} +{% endif %} +{% if credential_vars | default(False) %} +ansible_aws_ssm_access_key_id='{{ aws_access_key }}' +ansible_aws_ssm_secret_access_key='{{aws_secret_key }}' +{% if security_token is defined %} +ansible_aws_ssm_session_token='{{ security_token }}' +{% endif %} +{% endif %} +{% if profile_name | default(False) %} +ansible_aws_ssm_profile={{ profile_name }} +{% endif %} + +# support tests that target testhost +[testhost:children] +aws_ssm diff --git a/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/templates/s3_vars_to_delete.yml.j2 b/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/templates/s3_vars_to_delete.yml.j2 new file mode 100644 index 000000000..3839fb3c6 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/templates/s3_vars_to_delete.yml.j2 @@ -0,0 +1,2 @@ +--- +bucket_name: {{s3_output.name}} diff --git a/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/templates/ssm_vars_to_delete.yml.j2 b/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/templates/ssm_vars_to_delete.yml.j2 new file mode 100644 index 000000000..af017f5ba --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/setup_connection_aws_ssm/templates/ssm_vars_to_delete.yml.j2 @@ -0,0 +1,2 @@ +--- +ssm_document_name: {{ ssm_document_name }} diff --git a/ansible_collections/community/aws/tests/integration/targets/setup_ec2_facts/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/setup_ec2_facts/defaults/main.yml new file mode 100644 index 000000000..6fbe55e83 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/setup_ec2_facts/defaults/main.yml @@ -0,0 +1,6 @@ +# CentOS Community Platform Engineering (CPE) +ec2_ami_owner_id: '125523088429' +#ec2_ami_name: 'Fedora-Cloud-Base-*.x86_64*' +ec2_ami_name: 'CentOS Stream 9 x86_64*' +#ec2_ami_ssh_user: 'fedora' +ec2_ami_ssh_user: 'centos' diff --git a/ansible_collections/community/aws/tests/integration/targets/setup_ec2_facts/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/setup_ec2_facts/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/setup_ec2_facts/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/setup_ec2_facts/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/setup_ec2_facts/tasks/main.yml new file mode 100644 index 000000000..f41791073 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/setup_ec2_facts/tasks/main.yml @@ -0,0 +1,53 @@ +--- +# Setup a couple of common facts about the AWS Region +# +# Information about availablity zones +# - ec2_availability_zone_names +# +# An EC2 AMI that can be used for spinning up Instances performs as search +# rather than hardcoding the IDs so we're not limited to specific Regions +# - ec2_ami_id +# +- module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + + run_once: True + block: + # ============================================================ + + - name: Get available AZs + aws_az_info: + filters: + region-name: '{{ aws_region }}' + register: _az_info + + - name: Pick an AZ + set_fact: + ec2_availability_zone_names: '{{ _az_info.availability_zones | selectattr("zone_name", "defined") | map(attribute="zone_name") | list }}' + + # ============================================================ + + - name: Get a list of images + ec2_ami_info: + filters: + name: '{{ ec2_ami_name }}' + owner-id: '{{ ec2_ami_owner_id }}' + architecture: x86_64 + virtualization-type: hvm + root-device-type: ebs + register: _images_info + # Very spammy + no_log: True + + - name: Set Fact for latest AMI + vars: + latest_image: '{{ _images_info.images | sort(attribute="creation_date") | reverse | first }}' + set_fact: + ec2_ami_id: '{{ latest_image.image_id }}' + ec2_ami_details: '{{ latest_image }}' + ec2_ami_root_disk: '{{ latest_image.block_device_mappings[0].device_name }}' + ec2_ami_ssh_user: '{{ ec2_ami_ssh_user }}' diff --git a/ansible_collections/community/aws/tests/integration/targets/setup_remote_tmp_dir/handlers/main.yml b/ansible_collections/community/aws/tests/integration/targets/setup_remote_tmp_dir/handlers/main.yml new file mode 100644 index 000000000..229037c8b --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/setup_remote_tmp_dir/handlers/main.yml @@ -0,0 +1,5 @@ +- name: delete temporary directory + include_tasks: default-cleanup.yml + +- name: delete temporary directory (windows) + include_tasks: windows-cleanup.yml diff --git a/ansible_collections/community/aws/tests/integration/targets/setup_remote_tmp_dir/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/setup_remote_tmp_dir/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/setup_remote_tmp_dir/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/setup_remote_tmp_dir/tasks/default-cleanup.yml b/ansible_collections/community/aws/tests/integration/targets/setup_remote_tmp_dir/tasks/default-cleanup.yml new file mode 100644 index 000000000..39872d749 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/setup_remote_tmp_dir/tasks/default-cleanup.yml @@ -0,0 +1,5 @@ +- name: delete temporary directory + file: + path: "{{ remote_tmp_dir }}" + state: absent + no_log: yes diff --git a/ansible_collections/community/aws/tests/integration/targets/setup_remote_tmp_dir/tasks/default.yml b/ansible_collections/community/aws/tests/integration/targets/setup_remote_tmp_dir/tasks/default.yml new file mode 100644 index 000000000..00877dca0 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/setup_remote_tmp_dir/tasks/default.yml @@ -0,0 +1,12 @@ +- name: create temporary directory + tempfile: + path: /var/tmp + state: directory + suffix: .test + register: remote_tmp_dir + notify: + - delete temporary directory + +- name: record temporary directory + set_fact: + remote_tmp_dir: "{{ remote_tmp_dir.path }}" diff --git a/ansible_collections/community/aws/tests/integration/targets/setup_remote_tmp_dir/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/setup_remote_tmp_dir/tasks/main.yml new file mode 100644 index 000000000..f8df391b5 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/setup_remote_tmp_dir/tasks/main.yml @@ -0,0 +1,10 @@ +- name: make sure we have the ansible_os_family and ansible_distribution_version facts + setup: + gather_subset: distribution + when: ansible_facts == {} + +- include_tasks: "{{ lookup('first_found', files)}}" + vars: + files: + - "{{ ansible_os_family | lower }}.yml" + - "default.yml" diff --git a/ansible_collections/community/aws/tests/integration/targets/setup_remote_tmp_dir/tasks/windows-cleanup.yml b/ansible_collections/community/aws/tests/integration/targets/setup_remote_tmp_dir/tasks/windows-cleanup.yml new file mode 100644 index 000000000..32f372d0f --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/setup_remote_tmp_dir/tasks/windows-cleanup.yml @@ -0,0 +1,4 @@ +- name: delete temporary directory (windows) + ansible.windows.win_file: + path: '{{ remote_tmp_dir }}' + state: absent diff --git a/ansible_collections/community/aws/tests/integration/targets/setup_remote_tmp_dir/tasks/windows.yml b/ansible_collections/community/aws/tests/integration/targets/setup_remote_tmp_dir/tasks/windows.yml new file mode 100644 index 000000000..317c146db --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/setup_remote_tmp_dir/tasks/windows.yml @@ -0,0 +1,10 @@ +- name: create temporary directory + register: remote_tmp_dir + notify: + - delete temporary directory (windows) + ansible.windows.win_tempfile: + state: directory + suffix: .test +- name: record temporary directory + set_fact: + remote_tmp_dir: '{{ remote_tmp_dir.path }}' diff --git a/ansible_collections/community/aws/tests/integration/targets/setup_sshkey/files/ec2-fingerprint.py b/ansible_collections/community/aws/tests/integration/targets/setup_sshkey/files/ec2-fingerprint.py new file mode 100644 index 000000000..ea2f51b0f --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/setup_sshkey/files/ec2-fingerprint.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python +""" +Reads an OpenSSH Public key and spits out the 'AWS' MD5 sum +The equivalent of + +ssh-keygen -f id_rsa.pub -e -m PKCS8 | openssl pkey -pubin -outform DER | openssl md5 -c | cut -f 2 -d ' ' + +(but without needing the OpenSSL CLI) +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import hashlib +import sys +from Crypto.PublicKey import RSA + +if len(sys.argv) == 0: + ssh_public_key = "id_rsa.pub" +else: + ssh_public_key = sys.argv[1] + +with open(ssh_public_key, 'r') as key_fh: + data = key_fh.read() + +# Convert from SSH format to DER format +public_key = RSA.importKey(data).exportKey('DER') +md5digest = hashlib.md5(public_key).hexdigest() +# Format the md5sum into the normal format +pairs = zip(md5digest[::2], md5digest[1::2]) +md5string = ":".join(["".join(pair) for pair in pairs]) + +print(md5string) diff --git a/ansible_collections/community/aws/tests/integration/targets/setup_sshkey/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/setup_sshkey/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/setup_sshkey/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/setup_sshkey/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/setup_sshkey/tasks/main.yml new file mode 100644 index 000000000..31bd2176e --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/setup_sshkey/tasks/main.yml @@ -0,0 +1,71 @@ +# (c) 2014, James Laska <jlaska@ansible.com> + +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. + +- name: create a temp dir + tempfile: + state: directory + register: sshkey_dir + tags: + - prepare + +- name: ensure script is available + copy: + src: ec2-fingerprint.py + dest: '{{ sshkey_dir.path }}/ec2-fingerprint.py' + mode: 0700 + tags: + - prepare + +- name: Set location of SSH keys + set_fact: + sshkey: '{{ sshkey_dir.path }}/key_one' + another_sshkey: '{{ sshkey_dir.path }}/key_two' + sshkey_pub: '{{ sshkey_dir.path }}/key_one.pub' + another_sshkey_pub: '{{ sshkey_dir.path }}/key_two.pub' + +- name: generate sshkey + shell: echo 'y' | ssh-keygen -P '' -f '{{ sshkey }}' + tags: + - prepare + +- name: record fingerprint + shell: '{{ sshkey_dir.path }}/ec2-fingerprint.py {{ sshkey_pub }}' + register: fingerprint + tags: + - prepare + +- name: generate another_sshkey + shell: echo 'y' | ssh-keygen -P '' -f {{ another_sshkey }} + tags: + - prepare + +- name: record another fingerprint + shell: '{{ sshkey_dir.path }}/ec2-fingerprint.py {{ another_sshkey_pub }}' + register: another_fingerprint + tags: + - prepare + +- name: set facts for future roles + set_fact: + # Public SSH keys (OpenSSH format) + key_material: "{{ lookup('file', sshkey_pub) }}" + another_key_material: "{{ lookup('file', another_sshkey_pub) }}" + # AWS 'fingerprint' (md5digest) + fingerprint: '{{ fingerprint.stdout }}' + another_fingerprint: '{{ another_fingerprint.stdout }}' + tags: + - prepare diff --git a/ansible_collections/community/aws/tests/integration/targets/sns/aliases b/ansible_collections/community/aws/tests/integration/targets/sns/aliases new file mode 100644 index 000000000..4ef4b2067 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/sns/aliases @@ -0,0 +1 @@ +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/sns/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/sns/defaults/main.yml new file mode 100644 index 000000000..f81d04e9d --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/sns/defaults/main.yml @@ -0,0 +1,2 @@ +sns_topic_name: "{{ resource_prefix }}-topic" +sns_fifo_topic_name: "{{ resource_prefix }}-fifo-topic" diff --git a/ansible_collections/community/aws/tests/integration/targets/sns/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/sns/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/sns/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/sns/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/sns/tasks/main.yml new file mode 100644 index 000000000..42ef9b190 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/sns/tasks/main.yml @@ -0,0 +1,77 @@ +- name: set up AWS connection info + module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + collections: + - amazon.aws + + block: + - name: Create an SNS topic + sns_topic: + name: "{{ sns_topic_name }}" + display_name: "Test topic" + register: sns_topic + + - name: Publish to the topic by name + sns: + topic: "{{ sns_topic_name }}" + subject: Test message + msg: Default test message + http: Test message for HTTP + https: Test message for HTTPS + email: Test message for email + email_json: Test message for email-json + sms: Short test message for SMS + sqs: Test message for SQS + application: Test message for apps + lambda: Test message for Lambda + register: result + + - name: Check for expected result structure + assert: + that: + - result is not changed + - "'message_id' in result" + + - name: Publish to the topic by ARN + sns: + topic: "{{ sns_topic.sns_arn }}" + subject: Second test message + msg: Simple test message + + - name: Create an FIFO SNS topic + sns_topic: + name: "{{ sns_fifo_topic_name }}" + display_name: "Test fifo topic" + topic_type: "fifo" + register: sns_fifo_topic + + - name: Publish to the fifo topic + sns: + topic: "{{ sns_fifo_topic.sns_arn }}" + subject: Fifo test message + msg: Simple test message + message_group_id: group-id + message_deduplication_id: deduplication-id + register: result + + - name: Check for expected result structure + assert: + that: + - result is not changed + - "'sequence_number' in result" + always: + - name: Remove topic + sns_topic: + name: "{{ sns_topic_name }}" + state: absent + ignore_errors: yes + + - name: Remove fifo topic + sns_topic: + name: "{{ sns_fifo_topic_name }}" + state: absent + ignore_errors: yes diff --git a/ansible_collections/community/aws/tests/integration/targets/sns_topic/aliases b/ansible_collections/community/aws/tests/integration/targets/sns_topic/aliases new file mode 100644 index 000000000..b1656d7e3 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/sns_topic/aliases @@ -0,0 +1,3 @@ +cloud/aws + +sns_topic_info diff --git a/ansible_collections/community/aws/tests/integration/targets/sns_topic/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/sns_topic/defaults/main.yml new file mode 100644 index 000000000..878a58e22 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/sns_topic/defaults/main.yml @@ -0,0 +1,19 @@ +# we hash the resource_prefix to get a shorter, unique string +sns_topic_topic_name: "ansible-test-{{ tiny_prefix }}-topic" +sns_sqs_subscription_attributes: {} +sns_topic_subscriptions: + - endpoint: "{{ sns_topic_subscriber_arn }}" + protocol: "lambda" + - endpoint: "{{ sns_topic_subscriber_sqs_arn }}" + protocol: sqs + attributes: "{{ sns_sqs_subscription_attributes }}" +# Amazon owned +sns_topic_third_party_topic_arn: "arn:aws:sns:us-east-1:806199016981:AmazonIpSpaceChanged" +sns_topic_third_party_region: "{{ sns_topic_third_party_topic_arn.split(':')[3] }}" + +# additional test resource namings +sns_topic_lambda_function: "sns_topic_lambda" +sns_topic_lambda_name: "ansible-test-{{ tiny_prefix }}-{{ sns_topic_lambda_function }}" +sns_topic_lambda_role: "ansible-test-{{ tiny_prefix }}-sns-lambda" + +sns_topic_sqs_name: "ansible-test-{{ tiny_prefix }}-sns" diff --git a/ansible_collections/community/aws/tests/integration/targets/sns_topic/files/lambda-trust-policy.json b/ansible_collections/community/aws/tests/integration/targets/sns_topic/files/lambda-trust-policy.json new file mode 100644 index 000000000..fb84ae9de --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/sns_topic/files/lambda-trust-policy.json @@ -0,0 +1,12 @@ +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + }, + "Action": "sts:AssumeRole" + } + ] +} diff --git a/ansible_collections/community/aws/tests/integration/targets/sns_topic/files/sns_topic_lambda/sns_topic_lambda.py b/ansible_collections/community/aws/tests/integration/targets/sns_topic/files/sns_topic_lambda/sns_topic_lambda.py new file mode 100644 index 000000000..98f657836 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/sns_topic/files/sns_topic_lambda/sns_topic_lambda.py @@ -0,0 +1,9 @@ +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + + +def handler(event, context): + print(event) + return True diff --git a/ansible_collections/community/aws/tests/integration/targets/sns_topic/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/sns_topic/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/sns_topic/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/sns_topic/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/sns_topic/tasks/main.yml new file mode 100644 index 000000000..d5b389e4d --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/sns_topic/tasks/main.yml @@ -0,0 +1,672 @@ +- module_defaults: + group/aws: + aws_secret_key: '{{ aws_secret_key }}' + aws_access_key: '{{ aws_access_key }}' + security_token: '{{ security_token|default(omit) }}' + region: '{{ aws_region }}' + + block: + + - name: create minimal lambda role (needed for subscription test further down) + iam_role: + name: '{{ sns_topic_lambda_role }}' + assume_role_policy_document: '{{ lookup("file", "lambda-trust-policy.json") }}' + create_instance_profile: false + managed_policies: + - 'arn:aws:iam::aws:policy/AWSXrayWriteOnlyAccess' + wait: True + register: iam_role + + - name: list all the topics (check_mode) + sns_topic_info: + check_mode: true + register: sns_topic_list + + - name: assert success + assert: + that: + - sns_topic_list is successful + + - name: list all the topics + sns_topic_info: + register: sns_topic_list + + - name: assert success + assert: + that: + - sns_topic_list is successful + + - name: create standard SNS topic + sns_topic: + name: '{{ sns_topic_topic_name }}' + display_name: My topic name + register: sns_topic_create + + - name: assert that creation worked + assert: + that: + - sns_topic_create.changed + + - name: set sns_arn fact + set_fact: + sns_arn: '{{ sns_topic_create.sns_arn }}' + + - name: get info on specific topic (check_mode) + sns_topic_info: + topic_arn: "{{ sns_arn }}" + check_mode: true + register: sns_topic_info + + - name: assert success + assert: + that: + - sns_topic_info is successful + - "'result' in sns_topic_info" + - sns_topic_info.result["sns_arn"] == "{{ sns_arn }}" + - "'sns_topic' in sns_topic_info.result" + - "'display_name' in sns_topic_info.result['sns_topic']" + - sns_topic_info.result["sns_topic"]["display_name"] == "My topic name" + - "'owner' in sns_topic_info.result['sns_topic']" + - "'policy' in sns_topic_info.result['sns_topic']" + + - name: get info on specific topic + sns_topic_info: + topic_arn: "{{ sns_arn }}" + register: sns_topic_info + + - name: assert success + assert: + that: + - sns_topic_info is successful + - "'result' in sns_topic_info" + - sns_topic_info.result["sns_arn"] == "{{ sns_arn }}" + - "'sns_topic' in sns_topic_info.result" + - "'display_name' in sns_topic_info.result['sns_topic']" + - sns_topic_info.result["sns_topic"]["display_name"] == "My topic name" + - "'owner' in sns_topic_info.result['sns_topic']" + - "'policy' in sns_topic_info.result['sns_topic']" + + - name: create topic again (expect changed=False) + sns_topic: + name: '{{ sns_topic_topic_name }}' + display_name: My topic name + register: sns_topic_no_change + + - name: assert that recreation had no effect + assert: + that: + - not sns_topic_no_change.changed + - sns_topic_no_change.sns_arn == sns_topic_create.sns_arn + + - name: Create a FIFO topic (not providing .fifo suffix, should be done automatically) + sns_topic: + name: '{{ sns_topic_topic_name }}-fifo' + topic_type: fifo + display_name: My FIFO topic + register: sns_fifo_topic + + - name: assert that FIFO SNS topic creation worked + assert: + that: + - sns_fifo_topic.changed + - sns_fifo_topic.sns_topic.topic_type == 'fifo' + - sns_fifo_topic.sns_topic.name == '{{ sns_topic_topic_name }}-fifo' + + - name: Run create a FIFO topic again for idempotence test (with .fifo) + sns_topic: + name: '{{ sns_topic_topic_name }}-fifo.fifo' + topic_type: fifo + display_name: My FIFO topic + register: sns_fifo_topic + + - name: assert that FIFO SNS topic creation worked (without .fifo) + assert: + that: + - not sns_fifo_topic.changed + + - name: Run create a FIFO topic again for idempotence test + sns_topic: + name: '{{ sns_topic_topic_name }}-fifo.fifo' + topic_type: fifo + display_name: My FIFO topic + register: sns_fifo_topic + + - name: assert that FIFO SNS topic creation worked + assert: + that: + - not sns_fifo_topic.changed + + - name: set content_based_deduplication + sns_topic: + name: '{{ sns_topic_topic_name }}-fifo' + topic_type: fifo + display_name: My FIFO topic + content_based_deduplication: "enabled" + register: sns_fifo_topic + + - name: assert that FIFO SNS topic creation worked + assert: + that: + - sns_fifo_topic.changed + + - name: set content_based_deduplication (idemopotence) + sns_topic: + name: '{{ sns_topic_topic_name }}-fifo' + topic_type: fifo + display_name: My FIFO topic + content_based_deduplication: "enabled" + register: sns_fifo_topic + + - name: assert that FIFO SNS topic creation worked + assert: + that: + - not sns_fifo_topic.changed + + - name: update display name + sns_topic: + name: '{{ sns_topic_topic_name }}' + display_name: My new topic name + register: sns_topic_update_name + - name: assert that updating name worked + assert: + that: + - sns_topic_update_name.changed + - sns_topic_update_name.sns_topic.display_name == "My new topic name" + + - name: add access policy to SNS topic + sns_topic: + name: '{{ sns_topic_topic_name }}' + display_name: My new topic name + policy: '{{ lookup(''template'', ''initial-policy.json'') }}' + register: sns_topic_add_policy + - name: assert that adding policy worked + assert: + that: + - sns_topic_add_policy.changed + + - name: rerun same policy + sns_topic: + name: '{{ sns_topic_topic_name }}' + display_name: My new topic name + policy: '{{ lookup(''template'', ''initial-policy.json'') }}' + register: sns_topic_rerun_policy + + - name: assert that rerunning policy had no effect + assert: + that: + - not sns_topic_rerun_policy.changed + + - name: update SNS policy + sns_topic: + name: '{{ sns_topic_topic_name }}' + display_name: My new topic name + policy: '{{ lookup(''template'', ''updated-policy.json'') }}' + register: sns_topic_update_policy + - name: assert that updating policy worked + assert: + that: + - sns_topic_update_policy.changed + + - name: add delivery policy + sns_topic: + name: '{{ sns_topic_topic_name }}' + display_name: My new topic name + delivery_policy: + http: + defaultHealthyRetryPolicy: + minDelayTarget: "20" + maxDelayTarget: 20 + numRetries: 3 + numMaxDelayRetries: 0 + numNoDelayRetries: 0 + numMinDelayRetries: 0 + backoffFunction: linear + register: sns_topic_add_delivery_policy + + - name: assert that adding delivery policy worked + vars: + delivery_policy: '{{ sns_topic_add_delivery_policy.sns_topic.delivery_policy | from_json }}' + assert: + that: + - sns_topic_add_delivery_policy.changed + - delivery_policy.http.defaultHealthyRetryPolicy.minDelayTarget == 20 + - delivery_policy.http.defaultHealthyRetryPolicy.maxDelayTarget == 20 + - delivery_policy.http.defaultHealthyRetryPolicy.numRetries == 3 + + - name: rerun same delivery policy + sns_topic: + name: '{{ sns_topic_topic_name }}' + display_name: My new topic name + delivery_policy: + http: + defaultHealthyRetryPolicy: + minDelayTarget: "20" + maxDelayTarget: 20 + numRetries: 3 + numMaxDelayRetries: 0 + numNoDelayRetries: 0 + numMinDelayRetries: 0 + backoffFunction: linear + register: sns_topic_rerun_delivery_policy + + - name: assert that rerunning delivery_policy had no effect + vars: + delivery_policy: '{{ sns_topic_rerun_delivery_policy.sns_topic.delivery_policy | from_json }}' + assert: + that: + - not sns_topic_rerun_delivery_policy.changed + - delivery_policy.http.defaultHealthyRetryPolicy.minDelayTarget == 20 + - delivery_policy.http.defaultHealthyRetryPolicy.maxDelayTarget == 20 + - delivery_policy.http.defaultHealthyRetryPolicy.numRetries == 3 + + - name: rerun a slightly different delivery policy + sns_topic: + name: '{{ sns_topic_topic_name }}' + display_name: My new topic name + delivery_policy: + http: + defaultHealthyRetryPolicy: + minDelayTarget: "40" + maxDelayTarget: 40 + numRetries: 6 + numMaxDelayRetries: 0 + numNoDelayRetries: 0 + numMinDelayRetries: 0 + backoffFunction: linear + register: sns_topic_rerun_delivery_policy + + - name: assert that rerunning delivery_policy worked + vars: + delivery_policy: '{{ sns_topic_rerun_delivery_policy.sns_topic.delivery_policy | from_json }}' + assert: + that: + - sns_topic_rerun_delivery_policy.changed + - delivery_policy.http.defaultHealthyRetryPolicy.minDelayTarget == 40 + - delivery_policy.http.defaultHealthyRetryPolicy.maxDelayTarget == 40 + - delivery_policy.http.defaultHealthyRetryPolicy.numRetries == 6 + + - name: create SQS queue for subscribing + sqs_queue: + name: '{{ sns_topic_sqs_name }}' + register: sqs_result + + - set_fact: + sns_topic_subscriber_sqs_arn: '{{ sqs_result.queue_arn }}' + + - name: create temp dir + tempfile: + state: directory + register: tempdir + + - name: ensure zip file exists + archive: + path: '{{ lookup(''first_found'', sns_topic_lambda_function) }}' + dest: '{{ tempdir.path }}/{{ sns_topic_lambda_function }}.zip' + format: zip + + - name: create lambda for subscribing (only auto-subscribing target available) + lambda: + name: '{{ sns_topic_lambda_name }}' + state: present + zip_file: '{{ tempdir.path }}/{{ sns_topic_lambda_function }}.zip' + runtime: python3.9 + role: '{{ sns_topic_lambda_role }}' + handler: '{{ sns_topic_lambda_function }}.handler' + register: lambda_result + + - set_fact: + sns_topic_subscriber_arn: '{{ lambda_result.configuration.function_arn }}' + + - name: subscribe to topic + sns_topic: + name: '{{ sns_topic_topic_name }}' + display_name: My new topic name + purge_subscriptions: false + subscriptions: '{{ sns_topic_subscriptions }}' + register: sns_topic_subscribe + + - name: assert that subscribing worked + assert: + that: + - sns_topic_subscribe.changed + - sns_topic_subscribe.sns_topic.subscriptions|length == 2 + + - name: enable raw message delivery for sqs subscription (attributes) + set_fact: + sns_sqs_subscription_attributes: + RawMessageDelivery: true + + - name: update topic subscriptions - raw message enabled + sns_topic: + name: '{{ sns_topic_topic_name }}' + display_name: My new topic name + purge_subscriptions: false + subscriptions: '{{ sns_topic_subscriptions }}' + register: sns_topic_subscribe_update_raw_on + + - name: assert sqs subscription was updated + assert: + that: + - sns_topic_subscribe_update_raw_on.changed + + - name: rerun topic subscriptions with raw message enabled - expect no changes + sns_topic: + name: '{{ sns_topic_topic_name }}' + display_name: My new topic name + purge_subscriptions: false + subscriptions: '{{ sns_topic_subscriptions }}' + register: rerun_sns_topic_subscribe_update_raw_on + - name: assert no changes after rerun + assert: + that: + - not rerun_sns_topic_subscribe_update_raw_on.changed + + - name: run again with purge_subscriptions set to false + sns_topic: + name: '{{ sns_topic_topic_name }}' + display_name: My new topic name + purge_subscriptions: false + register: sns_topic_no_purge + + - name: assert that not purging subscriptions had no effect + assert: + that: + - not sns_topic_no_purge.changed + - sns_topic_no_purge.sns_topic.subscriptions|length == 2 + + - name: run again with purge_subscriptions set to true + sns_topic: + name: '{{ sns_topic_topic_name }}' + display_name: My new topic name + purge_subscriptions: true + register: sns_topic_purge + - name: assert that purging subscriptions worked + assert: + that: + - sns_topic_purge.changed + - sns_topic_purge.sns_topic.subscriptions|length == 0 + + - name: delete topic + sns_topic: + name: '{{ sns_topic_topic_name }}' + state: absent + + - name: remove subscription attributes before dealing with third party topic + set_fact: + sns_sqs_subscription_attributes: {} + + - name: no-op with third party topic (effectively get existing subscriptions) + sns_topic: + name: '{{ sns_topic_third_party_topic_arn }}' + region: '{{ sns_topic_third_party_region }}' + register: third_party_topic + + - name: subscribe to third party topic + sns_topic: + name: '{{ sns_topic_third_party_topic_arn }}' + subscriptions: '{{ sns_topic_subscriptions }}' + region: '{{ sns_topic_third_party_region }}' + register: third_party_topic_subscribe + + - name: assert that subscribing worked + assert: + that: + - third_party_topic_subscribe is changed + - (third_party_topic_subscribe.sns_topic.subscriptions|length) - (third_party_topic.sns_topic.subscriptions|length) == 2 + + - name: attempt to change name of third party topic + sns_topic: + name: '{{ sns_topic_third_party_topic_arn }}' + display_name: This should not work + subscriptions: '{{ sns_topic_subscriptions }}' + region: '{{ sns_topic_third_party_region }}' + ignore_errors: true + register: third_party_name_change + + - name: assert that attempting to change display name does not work + assert: + that: + - third_party_name_change is failed + + - name: unsubscribe from third party topic (purge_subscription defaults to true) + sns_topic: + name: '{{ sns_topic_third_party_topic_arn }}' + subscriptions: '{{ third_party_topic.sns_topic.subscriptions }}' + region: '{{ sns_topic_third_party_region }}' + register: third_party_unsubscribe + + - name: assert that unsubscribing from third party topic works + assert: + that: + - third_party_unsubscribe.changed + - third_party_topic.sns_topic.subscriptions|length == third_party_unsubscribe.sns_topic.subscriptions|length + + - name: attempt to delete third party topic + sns_topic: + name: '{{ sns_topic_third_party_topic_arn }}' + state: absent + subscriptions: '{{ subscriptions }}' + region: '{{ sns_topic_third_party_region }}' + ignore_errors: true + register: third_party_deletion + + - name: no-op after third party deletion + sns_topic: + name: '{{ sns_topic_third_party_topic_arn }}' + region: '{{ sns_topic_third_party_region }}' + register: third_party_deletion_facts + + - name: assert that attempting to delete third party topic does not work and preser + assert: + that: + - third_party_deletion is failed + - third_party_topic.sns_topic.subscriptions|length == third_party_deletion_facts.sns_topic.subscriptions|length + + # Test tags + - name: create standard SNS topic + sns_topic: + name: '{{ sns_topic_topic_name }}' + display_name: My topic name + register: sns_topic_create + + - name: assert that creation worked + assert: + that: + - sns_topic_create.changed + + - name: set sns_arn fact + set_fact: + sns_arn: '{{ sns_topic_create.sns_arn }}' + + - name: Add tags to topic - CHECK_MODE + sns_topic: + name: '{{ sns_topic_topic_name }}' + tags: + tag_one: '{{ tiny_prefix }} One' + "Tag Two": 'two {{ tiny_prefix }}' + check_mode: true + register: sns_topic_tags + + - assert: + that: + - sns_topic_tags is changed + + - name: Add tags to topic + sns_topic: + name: '{{ sns_topic_topic_name }}' + tags: + tag_one: '{{ tiny_prefix }} One' + "Tag Two": 'two {{ tiny_prefix }}' + register: sns_topic_tags + + - assert: + that: + - sns_topic_tags is changed + + - name: Add tags to topic to verify idempotency - CHECK_MODE + sns_topic: + name: '{{ sns_topic_topic_name }}' + tags: + tag_one: '{{ tiny_prefix }} One' + "Tag Two": 'two {{ tiny_prefix }}' + check_mode: true + register: sns_topic_tags + + - assert: + that: + - sns_topic_tags is not changed + + - name: Add tags to topic to verify idempotency + sns_topic: + name: '{{ sns_topic_topic_name }}' + tags: + tag_one: '{{ tiny_prefix }} One' + "Tag Two": 'two {{ tiny_prefix }}' + register: sns_topic_tags + + - assert: + that: + - sns_topic_tags is not changed + + - name: Update (add/remove) tags - CHECK_MODE + sns_topic: + name: '{{ sns_topic_topic_name }}' + tags: + tag_three: '{{ tiny_prefix }} Three' + "Tag Two": 'two {{ tiny_prefix }}' + check_mode: true + register: sns_topic_tags + + - assert: + that: + - sns_topic_tags is changed + + - name: Update tags to verify idempotency + sns_topic: + name: '{{ sns_topic_topic_name }}' + tags: + tag_three: '{{ tiny_prefix }} Three' + "Tag Two": 'two {{ tiny_prefix }}' + register: sns_topic_tags + + - assert: + that: + - sns_topic_tags is changed + + - name: Update tags without purge - CHECK_MODE + sns_topic: + name: '{{ sns_topic_topic_name }}' + purge_tags: no + tags: + tag_one: '{{ tiny_prefix }} One' + check_mode: true + register: sns_topic_tags + + - assert: + that: + - sns_topic_tags is changed + + - name: Update tags without purge + sns_topic: + name: '{{ sns_topic_topic_name }}' + purge_tags: no + tags: + tag_one: '{{ tiny_prefix }} One' + register: sns_topic_tags + + - assert: + that: + - sns_topic_tags is changed + + - name: Remove all the tags - CHECK_MODE + sns_topic: + name: '{{ sns_topic_topic_name }}' + purge_tags: yes + tags: {} + check_mode: true + register: sns_topic_tags + + - assert: + that: + - sns_topic_tags is changed + + - name: Remove all the tags + sns_topic: + name: '{{ sns_topic_topic_name }}' + purge_tags: yes + tags: {} + register: sns_topic_tags + + - assert: + that: + - sns_topic_tags is changed + + - name: Update with CamelCase tags + sns_topic: + name: '{{ sns_topic_topic_name }}' + purge_tags: no + tags: + "lowercase spaced": 'hello cruel world' + "Title Case": 'Hello Cruel World' + CamelCase: 'SimpleCamelCase' + snake_case: 'simple_snake_case' + register: sns_topic_tags + + - assert: + that: + - sns_topic_tags is changed + + - name: Do not specify any tag to ensure previous tags are not removed + sns_topic: + name: '{{ sns_topic_topic_name }}' + purge_tags: no + register: sns_topic_tags + + - assert: + that: + - sns_topic_tags is not changed + + always: + + - name: announce teardown start + debug: + msg: '************** TEARDOWN STARTS HERE *******************' + + - name: remove topic + sns_topic: + name: '{{ sns_topic_topic_name }}' + state: absent + ignore_errors: true + + - name: unsubscribe from third party topic + sns_topic: + name: '{{ sns_topic_third_party_topic_arn }}' + subscriptions: [] + purge_subscriptions: true + region: '{{ sns_topic_third_party_region }}' + ignore_errors: true + + - name: remove lambda + lambda: + name: '{{ sns_topic_lambda_name }}' + state: absent + ignore_errors: true + + - name: remove SQS queue + sqs_queue: + name: '{{ sns_topic_sqs_name }}' + state: absent + ignore_errors: true + + - name: remove tempdir + file: + path: '{{ tempdir.path }}' + state: absent + when: tempdir is defined + ignore_errors: true + + - name: remove iam role + iam_role: + name: '{{ sns_topic_lambda_role }}' + state: absent + ignore_errors: true diff --git a/ansible_collections/community/aws/tests/integration/targets/sns_topic/templates/initial-policy.json b/ansible_collections/community/aws/tests/integration/targets/sns_topic/templates/initial-policy.json new file mode 100644 index 000000000..235c59952 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/sns_topic/templates/initial-policy.json @@ -0,0 +1,20 @@ +{ + "Version":"2012-10-17", + "Id":"SomePolicyId", + "Statement" :[ + { + "Sid":"Statement1", + "Effect":"Allow", + "Principal" :{ + "AWS":"{{ sns_arn.split(':')[4] }}" + }, + "Action":["sns:Subscribe"], + "Resource": "{{ sns_arn }}", + "Condition" :{ + "StringEquals" :{ + "sns:Protocol":"email" + } + } + } + ] +} diff --git a/ansible_collections/community/aws/tests/integration/targets/sns_topic/templates/updated-policy.json b/ansible_collections/community/aws/tests/integration/targets/sns_topic/templates/updated-policy.json new file mode 100644 index 000000000..c796bb4d1 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/sns_topic/templates/updated-policy.json @@ -0,0 +1,20 @@ +{ + "Version":"2012-10-17", + "Id":"SomePolicyId", + "Statement" :[ + { + "Sid":"ANewSid", + "Effect":"Allow", + "Principal" :{ + "AWS":"{{ sns_arn.split(':')[4] }}" + }, + "Action":["sns:Subscribe"], + "Resource": "{{ sns_arn }}", + "Condition" :{ + "StringEquals" :{ + "sns:Protocol":"email" + } + } + } + ] +} diff --git a/ansible_collections/community/aws/tests/integration/targets/sqs_queue/aliases b/ansible_collections/community/aws/tests/integration/targets/sqs_queue/aliases new file mode 100644 index 000000000..4ef4b2067 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/sqs_queue/aliases @@ -0,0 +1 @@ +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/sqs_queue/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/sqs_queue/defaults/main.yml new file mode 100644 index 000000000..ed97d539c --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/sqs_queue/defaults/main.yml @@ -0,0 +1 @@ +--- diff --git a/ansible_collections/community/aws/tests/integration/targets/sqs_queue/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/sqs_queue/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/sqs_queue/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/sqs_queue/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/sqs_queue/tasks/main.yml new file mode 100644 index 000000000..bcba06c8f --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/sqs_queue/tasks/main.yml @@ -0,0 +1,210 @@ +--- +- name: Main test block + + module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + + block: + - block: + - name: Test creating SQS queue + sqs_queue: + name: "{{ resource_prefix }}{{ 1000 | random }}" + register: create_result + + - name: Assert SQS queue created + assert: + that: + - create_result.changed + - create_result.region == "{{ aws_region }}" + + always: + - name: Test deleting SQS queue + sqs_queue: + name: "{{ create_result.name }}" + state: absent + register: delete_result + retries: 3 + delay: 3 + until: delete_result.changed + + - name: Assert SQS queue deleted + assert: + that: + - delete_result.changed + + - name: Test delete SQS queue that doesn't exist + sqs_queue: + name: "{{ resource_prefix }}{{ 1000 | random }}" + state: absent + register: delete_result + + - name: Assert delete non-existant queue returns cleanly + assert: + that: + - delete_result.changed == False + + - name: Test queue features + block: + - name: Test create queue with attributes + sqs_queue: + name: "{{ resource_prefix }}{{ 1000 | random }}" + default_visibility_timeout: 900 + delivery_delay: 900 + # Test SSE + kms_master_key_id: alias/aws/sqs + maximum_message_size: 9009 + message_retention_period: 900 + receive_message_wait_time: 10 + policy: + Version: "2012-10-17" + Statement: + Effect: Allow + Action: "*" + register: create_result + + - name: Assert queue created with configuration + assert: + that: + - create_result.changed + - create_result.default_visibility_timeout == 900 + - create_result.delivery_delay == 900 + - create_result.kms_master_key_id == "alias/aws/sqs" + - create_result.maximum_message_size == 9009 + - create_result.message_retention_period == 900 + - create_result.receive_message_wait_time == 10 + - create_result.policy.Version == "2012-10-17" + - create_result.policy.Statement[0].Effect == "Allow" + - create_result.policy.Statement[0].Action == "*" + + - name: Test idempotentcy + sqs_queue: + name: "{{ create_result.name }}" + default_visibility_timeout: 900 + delivery_delay: 900 + maximum_message_size: 9009 + message_retention_period: 900 + receive_message_wait_time: 10 + policy: + Version: "2012-10-17" + Statement: + Effect: Allow + Action: "*" + kms_master_key_id: alias/aws/sqs + register: create_result + + - name: Assert nothing changed + assert: + that: + - not create_result.changed + + - name: Test update + sqs_queue: + name: "{{ create_result.name }}" + default_visibility_timeout: 899 + delivery_delay: 899 + maximum_message_size: 9008 + message_retention_period: 899 + receive_message_wait_time: 9 + policy: + Version: "2012-10-17" + Statement: + Effect: Allow + Action: "*" + register: create_result + + - name: Assert queue updated with configuration + assert: + that: + - create_result.changed + - create_result.default_visibility_timeout == 899 + - create_result.delivery_delay == 899 + - create_result.kms_master_key_id == "alias/aws/sqs" + - create_result.maximum_message_size == 9008 + - create_result.message_retention_period == 899 + - create_result.receive_message_wait_time == 9 + - create_result.policy.Version == "2012-10-17" + - create_result.policy.Statement[0].Effect == "Allow" + - create_result.policy.Statement[0].Action == "*" + always: + - name: Cleaning up queue + sqs_queue: + name: "{{ create_result.name }}" + state: absent + register: delete_result + retries: 3 + delay: 3 + until: delete_result.changed + + - name: Test queue with redrive + block: + - name: Creating dead letter queue + sqs_queue: + name: "{{ resource_prefix }}{{ 1000 | random }}" + register: dead_letter_queue + + - name: Test create queue with redrive_policy + sqs_queue: + name: "{{ resource_prefix }}{{ 1000 | random }}" + redrive_policy: + maxReceiveCount: 5 + deadLetterTargetArn: "{{ dead_letter_queue.queue_arn }}" + register: create_result + + - name: Assert queue created with configuration + assert: + that: + - create_result.changed + always: + - name: Cleaning up queue + sqs_queue: + name: "{{ item.name }}" + state: absent + register: delete_result + retries: 3 + delay: 3 + with_items: + - { name: "{{ create_result.name }}" } + - { name: "{{ dead_letter_queue.name }}" } + + - name: Test FIFO queue + block: + - name: Creating FIFO queue + sqs_queue: + name: "{{ resource_prefix }}{{ 1000 | random }}" + queue_type: fifo + content_based_deduplication: yes + register: create_result + + - name: Assert queue created with configuration + assert: + that: + - create_result.changed + + - name: Update FIFO queue + sqs_queue: + name: "{{ resource_prefix }}{{ 1000 | random }}" + queue_type: fifo + content_based_deduplication: yes + fifo_throughput_limit: perMessageGroupId + deduplication_scope: messageGroup + register: update_result + + - name: Assert queue updated with configuration + assert: + that: + - update_result.changed + + always: + - name: Cleaning up queue + sqs_queue: + name: "{{ item.name }}" + state: absent + register: delete_result + retries: 3 + delay: 3 + with_items: + - { name: "{{ create_result.name }}" } diff --git a/ansible_collections/community/aws/tests/integration/targets/ssm_parameter/aliases b/ansible_collections/community/aws/tests/integration/targets/ssm_parameter/aliases new file mode 100644 index 000000000..4ef4b2067 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ssm_parameter/aliases @@ -0,0 +1 @@ +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/ssm_parameter/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/ssm_parameter/defaults/main.yml new file mode 100644 index 000000000..218afac1c --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ssm_parameter/defaults/main.yml @@ -0,0 +1,2 @@ +--- +ssm_key_prefix: '{{ resource_prefix }}' diff --git a/ansible_collections/community/aws/tests/integration/targets/ssm_parameter/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/ssm_parameter/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ssm_parameter/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/ssm_parameter/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/ssm_parameter/tasks/main.yml new file mode 100644 index 000000000..ac461392a --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/ssm_parameter/tasks/main.yml @@ -0,0 +1,1597 @@ +--- +- set_fact: + # As a lookup plugin we don't have access to module_defaults + connection_args: + region: "{{ aws_region }}" + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + aws_security_token: "{{ security_token | default(omit) }}" + no_log: True + +- name: 'aws_ssm lookup plugin integration tests' + collections: + - amazon.aws + module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + vars: + simple_name: '/{{ ssm_key_prefix }}/Simple' + simple_description: 'This is a simple example' + simple_value: 'A simple VALue' + updated_description: 'This is an updated example' + updated_value: 'A simple VALue **UPDATED**' + simple_tag_param_name: '/{{ ssm_key_prefix }}/SimpleWithTags' + simple_tag_param_description: 'This is a simple example with tags' + simple_tag_param_updated_description: 'This is a simple example with tags (updated description)' + simple_tag_param_value: 'A simple VALue (w/ tags)' + single_tag: + Contact: "non-existent@ansible.com" + simple_tags_orig: + Contact: "non-existent@ansible.com" + Environment: "dev" + Version: "1.0" + Confidentiality: "low" + Tag With Space: "tag value with spaces" + simple_tags_add_owner: + Contact: "non-existent@ansible.com" + Environment: "dev" + Version: "1.0" + Confidentiality: "low" + Tag With Space: "foo" + Owner: "AWS" + simple_tags_change_environment: + Contact: "non-existent@ansible.com" + Environment: "test" + Version: "1.0" + Confidentiality: "low" + Tag With Space: "foo" + simple_tags_delete_version: + Contact: "non-existent@ansible.com" + Environment: "dev" + Confidentiality: "low" + Tag With Space: "foo" + simple_tags_delete_tag_with_space: + Contact: "non-existent@ansible.com" + Environment: "dev" + Version: "1.0" + Confidentiality: "low" + simple_tags_add_delete_change: + Contact: "non-existent@ansible.com" + Environment: "test" + Confidentiality: "low" + Tag With Space: "foo" + Owner: "AWS" + simple_tags_delete_all_tags: {} + simple_tags_purge_false_add_owner: + Owner: "AWS" + simple_tags_purge_false_add_multiple: + Contact1: "person1" + Contact2: "person2" + Contact3: "person3" + simple_tags_purge_false_change_environment: + Environment: "test" + simple_tags_purge_false_change_multiple: + Environment: "test" + Version: "2.0" + Confidentiality: "med" + Tag With Space: "tag value even more spaces" + simple_tags_purge_false_add_and_change: + Owner: "AWS" + Environment: "test" + block: + + # ============================================================ + # Create + + - name: Create key/value pair in aws parameter store (CHECK) + aws_ssm_parameter_store: + name: '{{ simple_name }}' + description: '{{ simple_description }}' + value: '{{ simple_value }}' + register: result + check_mode: True + - assert: + that: + - result is changed + + - name: Create key/value pair in aws parameter store + aws_ssm_parameter_store: + name: '{{ simple_name }}' + description: '{{ simple_description }}' + value: '{{ simple_value }}' + register: result + + - name: Lookup a single key + set_fact: + lookup_value: "{{ lookup('amazon.aws.aws_ssm', simple_name, **connection_args) }}" + - assert: + that: + - result is changed + - lookup_value == simple_value + - '"parameter_metadata" in result' + - '"data_type" in result.parameter_metadata' + - '"description" in result.parameter_metadata' + - '"last_modified_date" in result.parameter_metadata' + - '"last_modified_user" in result.parameter_metadata' + - '"name" in result.parameter_metadata' + - '"policies" in result.parameter_metadata' + - '"tier" in result.parameter_metadata' + - '"type" in result.parameter_metadata' + - '"version" in result.parameter_metadata' + - result.parameter_metadata.data_type == 'text' + - result.parameter_metadata.description == simple_description + - result.parameter_metadata.name == simple_name + - result.parameter_metadata.policies | length == 0 + - result.parameter_metadata.tier == 'Standard' + - result.parameter_metadata.type == 'String' + + - name: Create key/value pair in aws parameter store - idempotency (CHECK) + aws_ssm_parameter_store: + name: '{{ simple_name }}' + description: '{{ simple_description }}' + value: '{{ simple_value }}' + register: result + check_mode: True + - assert: + that: + - result is not changed + + - name: Create key/value pair in aws parameter store - idempotency + aws_ssm_parameter_store: + name: '{{ simple_name }}' + description: '{{ simple_description }}' + value: '{{ simple_value }}' + register: result + + - name: Lookup a single key + set_fact: + lookup_value: "{{ lookup('amazon.aws.aws_ssm', simple_name, **connection_args) }}" + - assert: + that: + - result is not changed + - lookup_value == simple_value + - '"parameter_metadata" in result' + - '"data_type" in result.parameter_metadata' + - '"description" in result.parameter_metadata' + - '"last_modified_date" in result.parameter_metadata' + - '"last_modified_user" in result.parameter_metadata' + - '"name" in result.parameter_metadata' + - '"policies" in result.parameter_metadata' + - '"tier" in result.parameter_metadata' + - '"type" in result.parameter_metadata' + - '"version" in result.parameter_metadata' + - result.parameter_metadata.data_type == 'text' + - result.parameter_metadata.description == simple_description + - result.parameter_metadata.name == simple_name + - result.parameter_metadata.policies | length == 0 + - result.parameter_metadata.tier == 'Standard' + - result.parameter_metadata.type == 'String' + + # ============================================================ + # Update description + + - name: Update description (CHECK) + aws_ssm_parameter_store: + name: '{{ simple_name }}' + description: '{{ updated_description }}' + register: result + check_mode: True + - assert: + that: + - result is changed + + - name: Update description + aws_ssm_parameter_store: + name: '{{ simple_name }}' + description: '{{ updated_description }}' + register: result + + - name: Lookup a single key + set_fact: + lookup_value: "{{ lookup('amazon.aws.aws_ssm', simple_name, **connection_args) }}" + - assert: + that: + - result is changed + - lookup_value == simple_value + - '"parameter_metadata" in result' + - '"data_type" in result.parameter_metadata' + - '"description" in result.parameter_metadata' + - '"last_modified_date" in result.parameter_metadata' + - '"last_modified_user" in result.parameter_metadata' + - '"name" in result.parameter_metadata' + - '"policies" in result.parameter_metadata' + - '"tier" in result.parameter_metadata' + - '"type" in result.parameter_metadata' + - '"version" in result.parameter_metadata' + - result.parameter_metadata.data_type == 'text' + #- result.parameter_metadata.description == updated_description + - result.parameter_metadata.name == simple_name + - result.parameter_metadata.policies | length == 0 + - result.parameter_metadata.tier == 'Standard' + - result.parameter_metadata.type == 'String' + + - name: Update description - idempotency (CHECK) + aws_ssm_parameter_store: + name: '{{ simple_name }}' + description: '{{ updated_description }}' + register: result + check_mode: True + - assert: + that: + - result is not changed + + - name: Update description - idempotency + aws_ssm_parameter_store: + name: '{{ simple_name }}' + description: '{{ updated_description }}' + register: result + + - name: Lookup a single key + set_fact: + lookup_value: "{{ lookup('amazon.aws.aws_ssm', simple_name, **connection_args) }}" + - assert: + that: + - result is not changed + - lookup_value == simple_value + - lookup_value == simple_value + - '"parameter_metadata" in result' + - '"data_type" in result.parameter_metadata' + - '"description" in result.parameter_metadata' + - '"last_modified_date" in result.parameter_metadata' + - '"last_modified_user" in result.parameter_metadata' + - '"name" in result.parameter_metadata' + - '"policies" in result.parameter_metadata' + - '"tier" in result.parameter_metadata' + - '"type" in result.parameter_metadata' + - '"version" in result.parameter_metadata' + - result.parameter_metadata.data_type == 'text' + - result.parameter_metadata.description == updated_description + - result.parameter_metadata.name == simple_name + - result.parameter_metadata.policies | length == 0 + - result.parameter_metadata.tier == 'Standard' + - result.parameter_metadata.type == 'String' + + # ============================================================ + # Update value + + - name: Update key/value pair in aws parameter store (CHECK) + aws_ssm_parameter_store: + name: '{{ simple_name }}' + value: '{{ updated_value }}' + register: result + check_mode: True + - assert: + that: + - result is changed + + - name: Update key/value pair in aws parameter store + aws_ssm_parameter_store: + name: '{{ simple_name }}' + value: '{{ updated_value }}' + register: result + + - name: Lookup a single key + set_fact: + lookup_value: "{{ lookup('amazon.aws.aws_ssm', simple_name, **connection_args) }}" + - assert: + that: + - result is changed + - lookup_value == updated_value + - '"parameter_metadata" in result' + - '"data_type" in result.parameter_metadata' + - '"description" in result.parameter_metadata' + - '"last_modified_date" in result.parameter_metadata' + - '"last_modified_user" in result.parameter_metadata' + - '"name" in result.parameter_metadata' + - '"policies" in result.parameter_metadata' + - '"tier" in result.parameter_metadata' + - '"type" in result.parameter_metadata' + - '"version" in result.parameter_metadata' + - result.parameter_metadata.data_type == 'text' + - result.parameter_metadata.description == updated_description + - result.parameter_metadata.name == simple_name + - result.parameter_metadata.policies | length == 0 + - result.parameter_metadata.tier == 'Standard' + - result.parameter_metadata.type == 'String' + + - name: Update key/value pair in aws parameter store - idempotency (CHECK) + aws_ssm_parameter_store: + name: '{{ simple_name }}' + value: '{{ updated_value }}' + register: result + check_mode: True + - assert: + that: + - result is not changed + + - name: Update key/value pair in aws parameter store - idempotency + aws_ssm_parameter_store: + name: '{{ simple_name }}' + value: '{{ updated_value }}' + register: result + + - name: Lookup a single key + set_fact: + lookup_value: "{{ lookup('amazon.aws.aws_ssm', simple_name, **connection_args) }}" + - assert: + that: + - result is not changed + - lookup_value == updated_value + - '"parameter_metadata" in result' + - '"data_type" in result.parameter_metadata' + - '"description" in result.parameter_metadata' + - '"last_modified_date" in result.parameter_metadata' + - '"last_modified_user" in result.parameter_metadata' + - '"name" in result.parameter_metadata' + - '"policies" in result.parameter_metadata' + - '"tier" in result.parameter_metadata' + - '"type" in result.parameter_metadata' + - '"version" in result.parameter_metadata' + - result.parameter_metadata.data_type == 'text' + - result.parameter_metadata.description == updated_description + - result.parameter_metadata.name == simple_name + - result.parameter_metadata.policies | length == 0 + - result.parameter_metadata.tier == 'Standard' + - result.parameter_metadata.type == 'String' + + # ============================================================ + # Complex update + + - name: Complex update to key/value pair in aws parameter store (CHECK) + aws_ssm_parameter_store: + name: '{{ simple_name }}' + value: '{{ simple_value }}' + description: '{{ simple_description }}' + register: result + check_mode: True + - assert: + that: + - result is changed + + - name: Complex update to key/value pair in aws parameter store + aws_ssm_parameter_store: + name: '{{ simple_name }}' + value: '{{ simple_value }}' + description: '{{ simple_description }}' + register: result + + - name: Lookup a single key + set_fact: + lookup_value: "{{ lookup('amazon.aws.aws_ssm', simple_name, **connection_args) }}" + - assert: + that: + - result is changed + - lookup_value == simple_value + - '"parameter_metadata" in result' + - '"data_type" in result.parameter_metadata' + - '"description" in result.parameter_metadata' + - '"last_modified_date" in result.parameter_metadata' + - '"last_modified_user" in result.parameter_metadata' + - '"name" in result.parameter_metadata' + - '"policies" in result.parameter_metadata' + - '"tier" in result.parameter_metadata' + - '"type" in result.parameter_metadata' + - '"version" in result.parameter_metadata' + - result.parameter_metadata.data_type == 'text' + - result.parameter_metadata.description == simple_description + - result.parameter_metadata.name == simple_name + - result.parameter_metadata.policies | length == 0 + - result.parameter_metadata.tier == 'Standard' + - result.parameter_metadata.type == 'String' + + - name: Complex update to key/value pair in aws parameter store - idempotency (CHECK) + aws_ssm_parameter_store: + name: '{{ simple_name }}' + value: '{{ simple_value }}' + description: '{{ simple_description }}' + register: result + check_mode: True + - assert: + that: + - result is not changed + + - name: Complex update to key/value pair in aws parameter store - idempotency + aws_ssm_parameter_store: + name: '{{ simple_name }}' + value: '{{ simple_value }}' + description: '{{ simple_description }}' + register: result + + - name: Lookup a single key + set_fact: + lookup_value: "{{ lookup('amazon.aws.aws_ssm', simple_name, **connection_args) }}" + - assert: + that: + - result is not changed + - lookup_value == simple_value + - '"parameter_metadata" in result' + - '"data_type" in result.parameter_metadata' + - '"description" in result.parameter_metadata' + - '"last_modified_date" in result.parameter_metadata' + - '"last_modified_user" in result.parameter_metadata' + - '"name" in result.parameter_metadata' + - '"policies" in result.parameter_metadata' + - '"tier" in result.parameter_metadata' + - '"type" in result.parameter_metadata' + - '"version" in result.parameter_metadata' + - result.parameter_metadata.data_type == 'text' + - result.parameter_metadata.description == simple_description + - result.parameter_metadata.name == simple_name + - result.parameter_metadata.policies | length == 0 + - result.parameter_metadata.tier == 'Standard' + - result.parameter_metadata.type == 'String' + + # ============================================================ + # Delete + + - name: Delete key/value pair in aws parameter store (CHECK) + aws_ssm_parameter_store: + name: '{{ simple_name }}' + state: absent + register: result + check_mode: True + - assert: + that: + - result is changed + + - name: Delete key/value pair in aws parameter store + aws_ssm_parameter_store: + name: '{{ simple_name }}' + state: absent + register: result + + - name: Lookup a single (missing) key + set_fact: + lookup_value: "{{ lookup('amazon.aws.aws_ssm', simple_name, **connection_args) }}" + register: info_result + ignore_errors: true + - assert: + that: + - result is changed + - info_result is failed + + - name: Delete key/value pair in aws parameter store - idempotency (CHECK) + aws_ssm_parameter_store: + name: '{{ simple_name }}' + state: absent + register: result + check_mode: True + - assert: + that: + - result is not changed + + - name: Delete key/value pair in aws parameter store - idempotency + aws_ssm_parameter_store: + name: '{{ simple_name }}' + state: absent + register: result + + - assert: + that: + - result is not changed + + - name: Create key/value pair in aws parameter store with no description + aws_ssm_parameter_store: + name: '{{ simple_name }}' + value: '{{ simple_value }}' + register: result + + - assert: + that: + - result is changed + - '"description" not in result.parameter_metadata' + + - name: Add a description + aws_ssm_parameter_store: + name: '{{ simple_name }}' + value: '{{ simple_value }}' + description: '{{ simple_description }}' + register: result + + - assert: + that: + - result is changed + - '"description" in result.parameter_metadata' + - result.parameter_metadata.description == simple_description + + # ============================================================ + # Test tags - Create parameter with tags case + + - name: Create parameter with tags case - Create parameter (CHECK) + aws_ssm_parameter_store: + name: '{{ simple_tag_param_name }}' + description: '{{ simple_tag_param_description }}' + value: '{{ simple_tag_param_value }}' + tags: '{{ simple_tags_orig }}' + register: result + check_mode: True + - assert: + that: + - result is changed + + - name: Create parameter with tags case - Create parameter + aws_ssm_parameter_store: + name: '{{ simple_tag_param_name }}' + description: '{{ simple_tag_param_description }}' + value: '{{ simple_tag_param_value }}' + tags: '{{ simple_tags_orig }}' + register: result + + - name: Create parameter with tags case - Ensure tags is correct + assert: + that: + - result.parameter_metadata.tags['{{ item.key }}'] == simple_tags_orig['{{ item.key }}'] + loop: "{{ simple_tags_orig | dict2items }}" + + - name: Create parameter with tags case - Ensure no missing or additional tags + assert: + that: + - result.parameter_metadata.tags | length == simple_tags_orig | length + + - name: Create parameter with tags case - Ensure only tags have changed + set_fact: + lookup_value: "{{ lookup('amazon.aws.aws_ssm', simple_tag_param_name, **connection_args) }}" + - assert: + that: + - result is changed + - lookup_value == simple_tag_param_value + - '"parameter_metadata" in result' + - '"data_type" in result.parameter_metadata' + - '"description" in result.parameter_metadata' + - '"last_modified_date" in result.parameter_metadata' + - '"last_modified_user" in result.parameter_metadata' + - '"name" in result.parameter_metadata' + - '"policies" in result.parameter_metadata' + - '"tier" in result.parameter_metadata' + - '"type" in result.parameter_metadata' + - '"version" in result.parameter_metadata' + - '"tags" in result.parameter_metadata' + - result.parameter_metadata.data_type == 'text' + - result.parameter_metadata.description == simple_tag_param_description + - result.parameter_metadata.name == simple_tag_param_name + - result.parameter_metadata.policies | length == 0 + - result.parameter_metadata.tier == 'Standard' + - result.parameter_metadata.type == 'String' + + # ============================================================ + # Test tags - Update description only case + + - name: Update description only case - Update parameter (CHECK) + aws_ssm_parameter_store: + name: '{{ simple_tag_param_name }}' + description: '{{ simple_tag_param_updated_description }}' + register: result + check_mode: True + - assert: + that: + - result is changed + + - name: Update description only case - Update parameter + aws_ssm_parameter_store: + name: '{{ simple_tag_param_name }}' + description: '{{ simple_tag_param_updated_description }}' + register: result + + - name: Update description only case - Ensure expected tags is correct + assert: + that: + - result.parameter_metadata.tags['{{ item.key }}'] == simple_tags_orig['{{ item.key }}'] + loop: "{{ simple_tags_orig | dict2items }}" + + - name: Update description only case - Ensure no missing or additional tags + assert: + that: + - result.parameter_metadata.tags | length == simple_tags_orig | length + + - name: Update description only case - Ensure only description changed + set_fact: + lookup_value: "{{ lookup('amazon.aws.aws_ssm', simple_tag_param_name, **connection_args) }}" + - assert: + that: + - result is changed + - lookup_value == simple_tag_param_value + - '"parameter_metadata" in result' + - '"data_type" in result.parameter_metadata' + - '"description" in result.parameter_metadata' + - '"last_modified_date" in result.parameter_metadata' + - '"last_modified_user" in result.parameter_metadata' + - '"name" in result.parameter_metadata' + - '"policies" in result.parameter_metadata' + - '"tier" in result.parameter_metadata' + - '"type" in result.parameter_metadata' + - '"version" in result.parameter_metadata' + - '"tags" in result.parameter_metadata' + - result.parameter_metadata.data_type == 'text' + - result.parameter_metadata.description == simple_tag_param_updated_description + - result.parameter_metadata.name == simple_tag_param_name + - result.parameter_metadata.policies | length == 0 + - result.parameter_metadata.tier == 'Standard' + - result.parameter_metadata.type == 'String' + + # ============================================================ + # Test tags - Add tag to existing parameter case + + - name: Add tag to existing parameter case - Update parameter (CHECK) + aws_ssm_parameter_store: + name: '{{ simple_tag_param_name }}' + tags: '{{ simple_tags_add_owner }}' + register: result + check_mode: True + - assert: + that: + - result is changed + + - name: Add tag to existing parameter case - Update parameter + aws_ssm_parameter_store: + name: '{{ simple_tag_param_name }}' + tags: '{{ simple_tags_add_owner }}' + register: result + + - name: Add tag to existing parameter case - Ensure tags correct + assert: + that: + - result.parameter_metadata.tags['{{ item.key }}'] == simple_tags_add_owner['{{ item.key }}'] + loop: "{{ simple_tags_add_owner | dict2items }}" + + - name: Add tag to existing parameter case - Ensure no missing or additional tags + assert: + that: + - result.parameter_metadata.tags | length == simple_tags_add_owner | length + + - name: Add tag to existing parameter case - Ensure only tags changed + set_fact: + lookup_value: "{{ lookup('amazon.aws.aws_ssm', simple_tag_param_name, **connection_args) }}" + - assert: + that: + - result is changed + - lookup_value == simple_tag_param_value + - '"parameter_metadata" in result' + - '"data_type" in result.parameter_metadata' + - '"description" in result.parameter_metadata' + - '"last_modified_date" in result.parameter_metadata' + - '"last_modified_user" in result.parameter_metadata' + - '"name" in result.parameter_metadata' + - '"policies" in result.parameter_metadata' + - '"tier" in result.parameter_metadata' + - '"type" in result.parameter_metadata' + - '"version" in result.parameter_metadata' + - '"tags" in result.parameter_metadata' + - result.parameter_metadata.data_type == 'text' + - result.parameter_metadata.description == simple_tag_param_updated_description + - result.parameter_metadata.name == simple_tag_param_name + - result.parameter_metadata.policies | length == 0 + - result.parameter_metadata.tier == 'Standard' + - result.parameter_metadata.type == 'String' + + - name: Add tag to existing parameter case - Delete parameter + aws_ssm_parameter_store: + name: "{{item}}" + state: absent + ignore_errors: True + with_items: + - '{{ simple_tag_param_name }}' + + # ============================================================ + # Test tags - update tags only - change tag + + - name: Change single tag case - Create parameter + aws_ssm_parameter_store: + name: '{{ simple_tag_param_name }}' + description: '{{ simple_tag_param_description }}' + value: '{{ simple_tag_param_value }}' + tags: '{{ simple_tags_orig }}' + register: result + + - name: Change single tag case - Update tag (CHECK) + aws_ssm_parameter_store: + name: '{{ simple_tag_param_name }}' + tags: '{{ simple_tags_change_environment }}' + register: result + check_mode: True + - assert: + that: + - result is changed + + - name: Change single tag case - Update tag + aws_ssm_parameter_store: + name: '{{ simple_tag_param_name }}' + tags: '{{ simple_tags_change_environment }}' + register: result + + - name: Change single tag case - Ensure expected tags is correct + assert: + that: + - result.parameter_metadata.tags['{{ item.key }}'] == simple_tags_change_environment['{{ item.key }}'] + loop: "{{ simple_tags_change_environment | dict2items }}" + + - name: Change single tag case - Ensure no missing or additional tags + assert: + that: + - result.parameter_metadata.tags | length == simple_tags_change_environment | length + + - name: Change single tag case - Lookup a tagged parameter + set_fact: + lookup_value: "{{ lookup('amazon.aws.aws_ssm', simple_tag_param_name, **connection_args) }}" + - assert: + that: + - result is changed + - lookup_value == simple_tag_param_value + - '"parameter_metadata" in result' + - '"data_type" in result.parameter_metadata' + - '"description" in result.parameter_metadata' + - '"last_modified_date" in result.parameter_metadata' + - '"last_modified_user" in result.parameter_metadata' + - '"name" in result.parameter_metadata' + - '"policies" in result.parameter_metadata' + - '"tier" in result.parameter_metadata' + - '"type" in result.parameter_metadata' + - '"version" in result.parameter_metadata' + - '"tags" in result.parameter_metadata' + - result.parameter_metadata.data_type == 'text' + - result.parameter_metadata.description == simple_tag_param_description + - result.parameter_metadata.name == simple_tag_param_name + - result.parameter_metadata.policies | length == 0 + - result.parameter_metadata.tier == 'Standard' + - result.parameter_metadata.type == 'String' + + - name: Change single tag case - Delete parameter + aws_ssm_parameter_store: + name: "{{item}}" + state: absent + ignore_errors: True + with_items: + - '{{ simple_tag_param_name }}' + + # ============================================================ + # Test tags - delete tag case + + - name: Delete single tag case - Create parameter + aws_ssm_parameter_store: + name: '{{ simple_tag_param_name }}' + description: '{{ simple_tag_param_description }}' + value: '{{ simple_tag_param_value }}' + tags: '{{ simple_tags_orig }}' + register: result + + - name: Delete single tag case - Update tag (CHECK) + aws_ssm_parameter_store: + name: '{{ simple_tag_param_name }}' + tags: '{{ simple_tags_delete_version }}' + register: result + check_mode: True + - assert: + that: + - result is changed + + - name: Delete single tag case - Update tag + aws_ssm_parameter_store: + name: '{{ simple_tag_param_name }}' + tags: '{{ simple_tags_delete_version }}' + register: result + + - name: Delete single tag case - Ensure expected tags is correct + assert: + that: + - result.parameter_metadata.tags['{{ item.key }}'] == simple_tags_delete_version['{{ item.key }}'] + loop: "{{ simple_tags_delete_version | dict2items }}" + + - name: Delete single tag case - Ensure no missing or additional tags + assert: + that: + - result.parameter_metadata.tags | length == simple_tags_delete_version | length + + - name: Delete single tag case - Lookup a tagged parameter + set_fact: + lookup_value: "{{ lookup('amazon.aws.aws_ssm', simple_tag_param_name, **connection_args) }}" + - assert: + that: + - result is changed + - lookup_value == simple_tag_param_value + - '"parameter_metadata" in result' + - '"data_type" in result.parameter_metadata' + - '"description" in result.parameter_metadata' + - '"last_modified_date" in result.parameter_metadata' + - '"last_modified_user" in result.parameter_metadata' + - '"name" in result.parameter_metadata' + - '"policies" in result.parameter_metadata' + - '"tier" in result.parameter_metadata' + - '"type" in result.parameter_metadata' + - '"version" in result.parameter_metadata' + - '"tags" in result.parameter_metadata' + - result.parameter_metadata.data_type == 'text' + - result.parameter_metadata.description == simple_tag_param_description + - result.parameter_metadata.name == simple_tag_param_name + - result.parameter_metadata.policies | length == 0 + - result.parameter_metadata.tier == 'Standard' + - result.parameter_metadata.type == 'String' + + - name: Delete single tag case - Delete parameter + aws_ssm_parameter_store: + name: "{{item}}" + state: absent + ignore_errors: True + with_items: + - '{{ simple_tag_param_name }}' + + # ============================================================ + # Test tags - delete tag w/ spaces case + + - name: Delete single tag w/ spaces case - Create parameter + aws_ssm_parameter_store: + name: '{{ simple_tag_param_name }}' + description: '{{ simple_tag_param_description }}' + value: '{{ simple_tag_param_value }}' + tags: '{{ simple_tags_orig }}' + register: result + + - name: Delete single tag w/ spaces case - Update tag (CHECK) + aws_ssm_parameter_store: + name: '{{ simple_tag_param_name }}' + tags: '{{ simple_tags_delete_tag_with_space }}' + register: result + check_mode: True + - assert: + that: + - result is changed + + - name: Delete single tag w/ spaces case - Update tag + aws_ssm_parameter_store: + name: '{{ simple_tag_param_name }}' + tags: '{{ simple_tags_delete_tag_with_space }}' + register: result + + - name: Delete single tag w/ spaces case - Ensure expected tags is correct + assert: + that: + - result.parameter_metadata.tags['{{ item.key }}'] == simple_tags_delete_tag_with_space['{{ item.key }}'] + loop: "{{ simple_tags_delete_tag_with_space | dict2items }}" + + - name: Delete single tag w/ spaces case - Ensure no missing or additional tags + assert: + that: + - result.parameter_metadata.tags | length == simple_tags_delete_tag_with_space | length + + - name: Delete single tag w/ spaces case - Lookup a tagged parameter + set_fact: + lookup_value: "{{ lookup('amazon.aws.aws_ssm', simple_tag_param_name, **connection_args) }}" + - assert: + that: + - result is changed + - lookup_value == simple_tag_param_value + - '"parameter_metadata" in result' + - '"data_type" in result.parameter_metadata' + - '"description" in result.parameter_metadata' + - '"last_modified_date" in result.parameter_metadata' + - '"last_modified_user" in result.parameter_metadata' + - '"name" in result.parameter_metadata' + - '"policies" in result.parameter_metadata' + - '"tier" in result.parameter_metadata' + - '"type" in result.parameter_metadata' + - '"version" in result.parameter_metadata' + - '"tags" in result.parameter_metadata' + - result.parameter_metadata.data_type == 'text' + - result.parameter_metadata.description == simple_tag_param_description + - result.parameter_metadata.name == simple_tag_param_name + - result.parameter_metadata.policies | length == 0 + - result.parameter_metadata.tier == 'Standard' + - result.parameter_metadata.type == 'String' + + - name: Delete single tag w/ spaces case - Delete parameter + aws_ssm_parameter_store: + name: "{{item}}" + state: absent + ignore_errors: True + with_items: + - '{{ simple_tag_param_name }}' + + # ============================================================ + # Test tags - Add/delete/change tags case + + - name: Add/delete/change tags case - Create parameter + aws_ssm_parameter_store: + name: '{{ simple_tag_param_name }}' + description: '{{ simple_tag_param_description }}' + value: '{{ simple_tag_param_value }}' + tags: '{{ simple_tags_orig }}' + register: result + + - name: Add/delete/change tags case - Update tag (CHECK) + aws_ssm_parameter_store: + name: '{{ simple_tag_param_name }}' + tags: '{{ simple_tags_add_delete_change }}' + register: result + check_mode: True + - assert: + that: + - result is changed + + - name: Add/delete/change tags case - Update tag + aws_ssm_parameter_store: + name: '{{ simple_tag_param_name }}' + tags: '{{ simple_tags_add_delete_change }}' + register: result + + - name: Add/delete/change tags case - Ensure expected tags is correct + assert: + that: + - result.parameter_metadata.tags['{{ item.key }}'] == simple_tags_add_delete_change['{{ item.key }}'] + loop: "{{ simple_tags_add_delete_change | dict2items }}" + + - name: Add/delete/change tags case - Ensure no missing or additional tags + assert: + that: + - result.parameter_metadata.tags | length == simple_tags_add_delete_change | length + + - name: Add/delete/change tags case - Lookup a tagged parameter + set_fact: + lookup_value: "{{ lookup('amazon.aws.aws_ssm', simple_tag_param_name, **connection_args) }}" + - assert: + that: + - result is changed + - lookup_value == simple_tag_param_value + - '"parameter_metadata" in result' + - '"data_type" in result.parameter_metadata' + - '"description" in result.parameter_metadata' + - '"last_modified_date" in result.parameter_metadata' + - '"last_modified_user" in result.parameter_metadata' + - '"name" in result.parameter_metadata' + - '"policies" in result.parameter_metadata' + - '"tier" in result.parameter_metadata' + - '"type" in result.parameter_metadata' + - '"version" in result.parameter_metadata' + - '"tags" in result.parameter_metadata' + - result.parameter_metadata.data_type == 'text' + - result.parameter_metadata.description == simple_tag_param_description + - result.parameter_metadata.name == simple_tag_param_name + - result.parameter_metadata.policies | length == 0 + - result.parameter_metadata.tier == 'Standard' + - result.parameter_metadata.type == 'String' + + - name: Add/delete/change tags case - Delete parameter + aws_ssm_parameter_store: + name: "{{item}}" + state: absent + ignore_errors: True + with_items: + - '{{ simple_tag_param_name }}' + + # ============================================================ + # Test tags - Delete all tags case + + - name: Delete all tags case - Create parameter + aws_ssm_parameter_store: + name: '{{ simple_tag_param_name }}' + description: '{{ simple_tag_param_description }}' + value: '{{ simple_tag_param_value }}' + tags: '{{ simple_tags_orig }}' + register: result + + - name: Delete all tags case - Update tag (CHECK) + aws_ssm_parameter_store: + name: '{{ simple_tag_param_name }}' + tags: '{{ simple_tags_delete_all_tags }}' + register: result + check_mode: True + - assert: + that: + - result is changed + + - name: Delete all tags case - Update tag + aws_ssm_parameter_store: + name: '{{ simple_tag_param_name }}' + tags: '{{ simple_tags_delete_all_tags }}' + register: result + + - name: Delete all tags case - Ensure expected tags is correct + assert: + that: + - result.parameter_metadata.tags['{{ item.key }}'] == simple_tags_delete_all_tags['{{ item.key }}'] + loop: "{{ simple_tags_delete_all_tags | dict2items }}" + + - name: Delete all tags case - Ensure no missing or additional tags + assert: + that: + - result.parameter_metadata.tags | length == simple_tags_delete_all_tags | length + + - name: Delete all tags case - Lookup a tagged parameter + set_fact: + lookup_value: "{{ lookup('amazon.aws.aws_ssm', simple_tag_param_name, **connection_args) }}" + - assert: + that: + - result is changed + - lookup_value == simple_tag_param_value + - '"parameter_metadata" in result' + - '"data_type" in result.parameter_metadata' + - '"description" in result.parameter_metadata' + - '"last_modified_date" in result.parameter_metadata' + - '"last_modified_user" in result.parameter_metadata' + - '"name" in result.parameter_metadata' + - '"policies" in result.parameter_metadata' + - '"tier" in result.parameter_metadata' + - '"type" in result.parameter_metadata' + - '"version" in result.parameter_metadata' + - '"tags" in result.parameter_metadata' + - result.parameter_metadata.data_type == 'text' + - result.parameter_metadata.description == simple_tag_param_description + - result.parameter_metadata.name == simple_tag_param_name + - result.parameter_metadata.policies | length == 0 + - result.parameter_metadata.tier == 'Standard' + - result.parameter_metadata.type == 'String' + + - name: Delete all tags case - Delete parameter + aws_ssm_parameter_store: + name: "{{item}}" + state: absent + ignore_errors: True + with_items: + - '{{ simple_tag_param_name }}' + + # ============================================================ + # Test tags - Add tag case (purge_tags=false) + + - name: Add tag case (purge_tags=false) - Create parameter + aws_ssm_parameter_store: + name: '{{ simple_tag_param_name }}' + description: '{{ simple_tag_param_description }}' + value: '{{ simple_tag_param_value }}' + tags: '{{ simple_tags_orig }}' + register: result + + - name: Add tag case (purge_tags=false) - Add tag (CHECK) + aws_ssm_parameter_store: + name: '{{ simple_tag_param_name }}' + tags: '{{ simple_tags_purge_false_add_owner }}' + purge_tags: False + register: result + check_mode: True + - assert: + that: + - result is changed + + - name: Add tag case (purge_tags=false) - Add tag + aws_ssm_parameter_store: + name: '{{ simple_tag_param_name }}' + tags: '{{ simple_tags_purge_false_add_owner }}' + purge_tags: False + register: result + + - name: Add tag case (purge_tags=false) - Ensure expected tags is correct + assert: + that: + - > + result.parameter_metadata.tags['{{ item.key }}'] == + (simple_tags_orig | combine(simple_tags_purge_false_add_owner))['{{ item.key }}'] + loop: > + {{ simple_tags_orig | combine(simple_tags_purge_false_add_owner) | dict2items }} + + - name: Add tag case (purge_tags=false) - Ensure no missing or additional tags + assert: + that: + - > + result.parameter_metadata.tags | length == {{ simple_tags_orig | + combine(simple_tags_purge_false_add_owner) | dict2items }} | length + + - name: Add tag case (purge_tags=false) - Lookup a tagged parameter + set_fact: + lookup_value: "{{ lookup('amazon.aws.aws_ssm', simple_tag_param_name, **connection_args) }}" + - assert: + that: + - result is changed + - lookup_value == simple_tag_param_value + - '"parameter_metadata" in result' + - '"data_type" in result.parameter_metadata' + - '"description" in result.parameter_metadata' + - '"last_modified_date" in result.parameter_metadata' + - '"last_modified_user" in result.parameter_metadata' + - '"name" in result.parameter_metadata' + - '"policies" in result.parameter_metadata' + - '"tier" in result.parameter_metadata' + - '"type" in result.parameter_metadata' + - '"version" in result.parameter_metadata' + - '"tags" in result.parameter_metadata' + - result.parameter_metadata.data_type == 'text' + - result.parameter_metadata.description == simple_tag_param_description + - result.parameter_metadata.name == simple_tag_param_name + - result.parameter_metadata.policies | length == 0 + - result.parameter_metadata.tier == 'Standard' + - result.parameter_metadata.type == 'String' + + - name: Add tag case (purge_tags=false) - Delete parameter + aws_ssm_parameter_store: + name: "{{item}}" + state: absent + ignore_errors: True + with_items: + - '{{ simple_tag_param_name }}' + + # ============================================================ + # Test tags - Add multiple tags case (purge_tags=false) + + - name: Add multiple tags case (purge_tags=false) - Create parameter + aws_ssm_parameter_store: + name: '{{ simple_tag_param_name }}' + description: '{{ simple_tag_param_description }}' + value: '{{ simple_tag_param_value }}' + tags: '{{ simple_tags_orig }}' + register: result + + - name: Add multiple tags case (purge_tags=false) - Add tag (CHECK) + aws_ssm_parameter_store: + name: '{{ simple_tag_param_name }}' + tags: '{{ simple_tags_purge_false_add_multiple }}' + purge_tags: False + register: result + check_mode: True + - assert: + that: + - result is changed + + - name: Add multiple tags case (purge_tags=false) - Add tag + aws_ssm_parameter_store: + name: '{{ simple_tag_param_name }}' + tags: '{{ simple_tags_purge_false_add_multiple }}' + purge_tags: False + register: result + + - name: Add multiple tags case (purge_tags=false) - Ensure expected tags is correct + assert: + that: + - > + result.parameter_metadata.tags['{{ item.key }}'] == + (simple_tags_orig | combine(simple_tags_purge_false_add_multiple))['{{ item.key }}'] + loop: > + {{ simple_tags_orig | combine(simple_tags_purge_false_add_multiple) | dict2items }} + + - name: Add multiple tags case (purge_tags=false) - Ensure no missing or additional tags + assert: + that: + - > + result.parameter_metadata.tags | length == {{ simple_tags_orig | + combine(simple_tags_purge_false_add_multiple) | dict2items }} | length + + - name: Add multiple tags case (purge_tags=false) - Lookup a tagged parameter + set_fact: + lookup_value: "{{ lookup('amazon.aws.aws_ssm', simple_tag_param_name, **connection_args) }}" + - assert: + that: + - result is changed + - lookup_value == simple_tag_param_value + - '"parameter_metadata" in result' + - '"data_type" in result.parameter_metadata' + - '"description" in result.parameter_metadata' + - '"last_modified_date" in result.parameter_metadata' + - '"last_modified_user" in result.parameter_metadata' + - '"name" in result.parameter_metadata' + - '"policies" in result.parameter_metadata' + - '"tier" in result.parameter_metadata' + - '"type" in result.parameter_metadata' + - '"version" in result.parameter_metadata' + - '"tags" in result.parameter_metadata' + - result.parameter_metadata.data_type == 'text' + - result.parameter_metadata.description == simple_tag_param_description + - result.parameter_metadata.name == simple_tag_param_name + - result.parameter_metadata.policies | length == 0 + - result.parameter_metadata.tier == 'Standard' + - result.parameter_metadata.type == 'String' + + - name: Add multiple tags case (purge_tags=false) - Delete parameter + aws_ssm_parameter_store: + name: "{{item}}" + state: absent + ignore_errors: True + with_items: + - '{{ simple_tag_param_name }}' + + # ============================================================ + # Test tags - Change tag case (purge_tags=false) + + - name: Change tag case (purge_tags=false) - Create parameter + aws_ssm_parameter_store: + name: '{{ simple_tag_param_name }}' + description: '{{ simple_tag_param_description }}' + value: '{{ simple_tag_param_value }}' + tags: '{{ simple_tags_orig }}' + register: result + + - name: Change tag case (purge_tags=false) - Change tag (CHECK) + aws_ssm_parameter_store: + name: '{{ simple_tag_param_name }}' + tags: '{{ simple_tags_purge_false_change_environment}}' + purge_tags: False + register: result + check_mode: True + - assert: + that: + - result is changed + + - name: Change tag case (purge_tags=false) - Change tag + aws_ssm_parameter_store: + name: '{{ simple_tag_param_name }}' + tags: '{{ simple_tags_purge_false_change_environment }}' + purge_tags: False + register: result + + - name: Change tag case (purge_tags=false) - Ensure expected tags is correct + assert: + that: + - > + result.parameter_metadata.tags['{{ item.key }}'] == + (simple_tags_orig | combine(simple_tags_purge_false_change_environment))['{{ item.key }}'] + loop: > + {{ simple_tags_orig | combine(simple_tags_purge_false_change_environment) | dict2items }} + loop_control: + extended: yes + + + - name: Change tag case (purge_tags=false) - Ensure no missing or additional tags + assert: + that: + - > + result.parameter_metadata.tags | length == {{ simple_tags_orig | + combine(simple_tags_purge_false_change_environment) | dict2items }} | length + + - name: Change tag case (purge_tags=false) - Lookup a tagged parameter + set_fact: + lookup_value: "{{ lookup('amazon.aws.aws_ssm', simple_tag_param_name, **connection_args) }}" + - assert: + that: + - result is changed + - lookup_value == simple_tag_param_value + - '"parameter_metadata" in result' + - '"data_type" in result.parameter_metadata' + - '"description" in result.parameter_metadata' + - '"last_modified_date" in result.parameter_metadata' + - '"last_modified_user" in result.parameter_metadata' + - '"name" in result.parameter_metadata' + - '"policies" in result.parameter_metadata' + - '"tier" in result.parameter_metadata' + - '"type" in result.parameter_metadata' + - '"version" in result.parameter_metadata' + - '"tags" in result.parameter_metadata' + - result.parameter_metadata.data_type == 'text' + - result.parameter_metadata.description == simple_tag_param_description + - result.parameter_metadata.name == simple_tag_param_name + - result.parameter_metadata.policies | length == 0 + - result.parameter_metadata.tier == 'Standard' + - result.parameter_metadata.type == 'String' + + - name: Change tag case (purge_tags=false) - Delete parameter + aws_ssm_parameter_store: + name: "{{item}}" + state: absent + ignore_errors: True + with_items: + - '{{ simple_tag_param_name }}' + + # ============================================================ + # Test tags - Change multiple tags case (purge_tags=false) + + - name: Change multiple tags (purge_tags=false) - Create parameter + aws_ssm_parameter_store: + name: '{{ simple_tag_param_name }}' + description: '{{ simple_tag_param_description }}' + value: '{{ simple_tag_param_value }}' + tags: '{{ simple_tags_orig }}' + register: result + + - name: Change multiple tags (purge_tags=false) - Change tag (CHECK) + aws_ssm_parameter_store: + name: '{{ simple_tag_param_name }}' + tags: '{{ simple_tags_purge_false_change_multiple}}' + purge_tags: False + register: result + check_mode: True + - assert: + that: + - result is changed + + - name: Change multiple tags (purge_tags=false) - Change tag + aws_ssm_parameter_store: + name: '{{ simple_tag_param_name }}' + tags: '{{ simple_tags_purge_false_change_multiple }}' + purge_tags: False + register: result + + - name: Change multiple tags (purge_tags=false) - Ensure expected tags is correct + assert: + that: + - > + result.parameter_metadata.tags['{{ item.key }}'] == + (simple_tags_orig | combine(simple_tags_purge_false_change_multiple))['{{ item.key }}'] + loop: > + {{ simple_tags_orig | combine(simple_tags_purge_false_change_multiple) | dict2items }} + loop_control: + extended: yes + + + - name: Change multiple tags (purge_tags=false) - Ensure no missing or additional tags + assert: + that: + - > + result.parameter_metadata.tags | length == {{ simple_tags_orig | + combine(simple_tags_purge_false_change_multiple) | dict2items }} | length + + - name: Change multiple tags (purge_tags=false) - Lookup a tagged parameter + set_fact: + lookup_value: "{{ lookup('amazon.aws.aws_ssm', simple_tag_param_name, **connection_args) }}" + - assert: + that: + - result is changed + - lookup_value == simple_tag_param_value + - '"parameter_metadata" in result' + - '"data_type" in result.parameter_metadata' + - '"description" in result.parameter_metadata' + - '"last_modified_date" in result.parameter_metadata' + - '"last_modified_user" in result.parameter_metadata' + - '"name" in result.parameter_metadata' + - '"policies" in result.parameter_metadata' + - '"tier" in result.parameter_metadata' + - '"type" in result.parameter_metadata' + - '"version" in result.parameter_metadata' + - '"tags" in result.parameter_metadata' + - result.parameter_metadata.data_type == 'text' + - result.parameter_metadata.description == simple_tag_param_description + - result.parameter_metadata.name == simple_tag_param_name + - result.parameter_metadata.policies | length == 0 + - result.parameter_metadata.tier == 'Standard' + - result.parameter_metadata.type == 'String' + + - name: Change multiple tags (purge_tags=false) - Delete parameter + aws_ssm_parameter_store: + name: "{{item}}" + state: absent + ignore_errors: True + with_items: + - '{{ simple_tag_param_name }}' + + # ============================================================ + # Test tags - Add/Change multiple tags case (purge_tags=false) + + - name: Add/Change multiple tags (purge_tags=false) - Create parameter + aws_ssm_parameter_store: + name: '{{ simple_tag_param_name }}' + description: '{{ simple_tag_param_description }}' + value: '{{ simple_tag_param_value }}' + tags: '{{ simple_tags_orig }}' + register: result + + - name: Add/Change multiple tags (purge_tags=false) - Change tag (CHECK) + aws_ssm_parameter_store: + name: '{{ simple_tag_param_name }}' + tags: '{{ simple_tags_purge_false_add_and_change}}' + purge_tags: False + register: result + check_mode: True + - assert: + that: + - result is changed + + - name: Add/Change multiple tags (purge_tags=false) - Change tag + aws_ssm_parameter_store: + name: '{{ simple_tag_param_name }}' + tags: '{{ simple_tags_purge_false_add_and_change }}' + purge_tags: False + register: result + + - name: Add/Change multiple tags (purge_tags=false) - Ensure expected tags is correct + assert: + that: + - > + result.parameter_metadata.tags['{{ item.key }}'] == + (simple_tags_orig | combine(simple_tags_purge_false_add_and_change))['{{ item.key }}'] + loop: > + {{ simple_tags_orig | combine(simple_tags_purge_false_add_and_change) | dict2items }} + loop_control: + extended: yes + + + - name: Add/Change multiple tags (purge_tags=false) - Ensure no missing or additional tags + assert: + that: + - > + result.parameter_metadata.tags | length == {{ simple_tags_orig | + combine(simple_tags_purge_false_add_and_change) | dict2items }} | length + + - name: Add/Change multiple tags (purge_tags=false) - Lookup a tagged parameter + set_fact: + lookup_value: "{{ lookup('amazon.aws.aws_ssm', simple_tag_param_name, **connection_args) }}" + - assert: + that: + - result is changed + - lookup_value == simple_tag_param_value + - '"parameter_metadata" in result' + - '"data_type" in result.parameter_metadata' + - '"description" in result.parameter_metadata' + - '"last_modified_date" in result.parameter_metadata' + - '"last_modified_user" in result.parameter_metadata' + - '"name" in result.parameter_metadata' + - '"policies" in result.parameter_metadata' + - '"tier" in result.parameter_metadata' + - '"type" in result.parameter_metadata' + - '"version" in result.parameter_metadata' + - '"tags" in result.parameter_metadata' + - result.parameter_metadata.data_type == 'text' + - result.parameter_metadata.description == simple_tag_param_description + - result.parameter_metadata.name == simple_tag_param_name + - result.parameter_metadata.policies | length == 0 + - result.parameter_metadata.tier == 'Standard' + - result.parameter_metadata.type == 'String' + + - name: Add/Change multiple tags (purge_tags=false) - Delete parameter + aws_ssm_parameter_store: + name: "{{item}}" + state: absent + ignore_errors: True + with_items: + - '{{ simple_tag_param_name }}' + + # ============================================================ + # Test tags - Empty tags dict case (purge_tags=false) # should be no change + + - name: Empty tags dict (purge_tags=false) - Create parameter + aws_ssm_parameter_store: + name: '{{ simple_tag_param_name }}' + description: '{{ simple_tag_param_description }}' + value: '{{ simple_tag_param_value }}' + tags: '{{ simple_tags_orig }}' + register: result + + - name: Empty tags dict (purge_tags=false) - Change tag (CHECK) + aws_ssm_parameter_store: + name: '{{ simple_tag_param_name }}' + tags: {} + purge_tags: False + register: result + check_mode: True + - assert: + that: + - result != 'changed' + + - name: Empty tags dict (purge_tags=false) - Change tag + aws_ssm_parameter_store: + name: '{{ simple_tag_param_name }}' + tags: {} + purge_tags: False + register: result + + - name: Empty tags dict (purge_tags=false) - Ensure expected tags is correct + assert: + that: + - > + result.parameter_metadata.tags['{{ item.key }}'] == simple_tags_orig['{{ item.key }}'] + loop: > + {{ simple_tags_orig | dict2items }} + loop_control: + extended: yes + + - name: Empty tags dict (purge_tags=false) - Ensure no missing or additional tags + assert: + that: + - > + result.parameter_metadata.tags | length + == {{ simple_tags_orig | dict2items }} | length + + - name: Empty tags dict (purge_tags=false) - Lookup a tagged parameter + set_fact: + lookup_value: "{{ lookup('amazon.aws.aws_ssm', simple_tag_param_name, **connection_args) }}" + - assert: + that: + - result != 'changed' + - lookup_value == simple_tag_param_value + - '"parameter_metadata" in result' + - '"data_type" in result.parameter_metadata' + - '"description" in result.parameter_metadata' + - '"last_modified_date" in result.parameter_metadata' + - '"last_modified_user" in result.parameter_metadata' + - '"name" in result.parameter_metadata' + - '"policies" in result.parameter_metadata' + - '"tier" in result.parameter_metadata' + - '"type" in result.parameter_metadata' + - '"version" in result.parameter_metadata' + - '"tags" in result.parameter_metadata' + - result.parameter_metadata.data_type == 'text' + - result.parameter_metadata.description == simple_tag_param_description + - result.parameter_metadata.name == simple_tag_param_name + - result.parameter_metadata.policies | length == 0 + - result.parameter_metadata.tier == 'Standard' + - result.parameter_metadata.type == 'String' + + - name: Empty tags dict (purge_tags=false) - Delete parameter + aws_ssm_parameter_store: + name: "{{item}}" + state: absent + ignore_errors: True + with_items: + - '{{ simple_tag_param_name }}' + + # ============================================================ + # Test tags - No tags parameter (purge_tags=true) case # should be no change + + - name: No tags parameter (purge_tags=true) - Create parameter + aws_ssm_parameter_store: + name: '{{ simple_tag_param_name }}' + description: '{{ simple_tag_param_description }}' + value: '{{ simple_tag_param_value }}' + tags: '{{ simple_tags_orig }}' + register: result + + - name: No tags parameter (purge_tags=true) - Change tag (CHECK) + aws_ssm_parameter_store: + name: '{{ simple_tag_param_name }}' + description: '{{ simple_tag_param_updated_description }}' + register: result + check_mode: True + - assert: + that: + - result is changed + + - name: No tags parameter (purge_tags=true) - Change tag + aws_ssm_parameter_store: + name: '{{ simple_tag_param_name }}' + description: '{{ simple_tag_param_updated_description }}' + register: result + + - name: No tags parameter (purge_tags=true) - Ensure expected tags is correct + assert: + that: + - > + result.parameter_metadata.tags['{{ item.key }}'] + == simple_tags_orig['{{ item.key }}'] + loop: > + {{ simple_tags_orig | dict2items }} + loop_control: + extended: true + + - name: No tags parameter (purge_tags=true) - Ensure no missing or additional tags + assert: + that: + - > + result.parameter_metadata.tags | length + == {{ simple_tags_orig | dict2items }} | length + + - name: No tags parameter (purge_tags=true) - Lookup a tagged parameter + set_fact: + lookup_value: "{{ lookup('amazon.aws.aws_ssm', simple_tag_param_name, **connection_args) }}" + - assert: + that: + - result is changed + - lookup_value == simple_tag_param_value + - '"parameter_metadata" in result' + - '"data_type" in result.parameter_metadata' + - '"description" in result.parameter_metadata' + - '"last_modified_date" in result.parameter_metadata' + - '"last_modified_user" in result.parameter_metadata' + - '"name" in result.parameter_metadata' + - '"policies" in result.parameter_metadata' + - '"tier" in result.parameter_metadata' + - '"type" in result.parameter_metadata' + - '"version" in result.parameter_metadata' + - '"tags" in result.parameter_metadata' + - result.parameter_metadata.data_type == 'text' + - result.parameter_metadata.description == simple_tag_param_updated_description + - result.parameter_metadata.name == simple_tag_param_name + - result.parameter_metadata.policies | length == 0 + - result.parameter_metadata.tier == 'Standard' + - result.parameter_metadata.type == 'String' + + - name: No tags parameter (purge_tags=true) - Delete parameter + aws_ssm_parameter_store: + name: "{{item}}" + state: absent + ignore_errors: true + with_items: + - '{{ simple_tag_param_name }}' + + always: + # ============================================================ + - name: Delete remaining key/value pairs in aws parameter store + aws_ssm_parameter_store: + name: "{{item}}" + state: absent + ignore_errors: True + with_items: + - '{{ simple_name }}' + - '{{ simple_tag_param_name }}' diff --git a/ansible_collections/community/aws/tests/integration/targets/stepfunctions_state_machine/aliases b/ansible_collections/community/aws/tests/integration/targets/stepfunctions_state_machine/aliases new file mode 100644 index 000000000..0aecb0d50 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/stepfunctions_state_machine/aliases @@ -0,0 +1,3 @@ +cloud/aws + +stepfunctions_state_machine_execution diff --git a/ansible_collections/community/aws/tests/integration/targets/stepfunctions_state_machine/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/stepfunctions_state_machine/defaults/main.yml new file mode 100644 index 000000000..dd0965e88 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/stepfunctions_state_machine/defaults/main.yml @@ -0,0 +1,4 @@ +# the random_num is generated in a set_fact task at the start of the testsuite +state_machine_name: "{{ tiny_prefix }}_step_function_{{ random_num }}" +step_functions_role_name: "ansible-test-{{ tiny_prefix }}-step-function" +execution_name: "{{ resource_prefix }}_sfn_execution" diff --git a/ansible_collections/community/aws/tests/integration/targets/stepfunctions_state_machine/files/alternative_state_machine.json b/ansible_collections/community/aws/tests/integration/targets/stepfunctions_state_machine/files/alternative_state_machine.json new file mode 100644 index 000000000..7b51bebb1 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/stepfunctions_state_machine/files/alternative_state_machine.json @@ -0,0 +1,15 @@ +{ + "StartAt": "HelloWorld", + "States": { + "HelloWorld": { + "Type": "Pass", + "Result": "Some other result", + "Next": "Wait" + }, + "Wait": { + "Type": "Wait", + "Seconds": 30, + "End": true + } + } +}
\ No newline at end of file diff --git a/ansible_collections/community/aws/tests/integration/targets/stepfunctions_state_machine/files/state_machine.json b/ansible_collections/community/aws/tests/integration/targets/stepfunctions_state_machine/files/state_machine.json new file mode 100644 index 000000000..c07d5ceba --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/stepfunctions_state_machine/files/state_machine.json @@ -0,0 +1,10 @@ +{ + "StartAt": "HelloWorld", + "States": { + "HelloWorld": { + "Type": "Pass", + "Result": "Hello World!", + "End": true + } + } +}
\ No newline at end of file diff --git a/ansible_collections/community/aws/tests/integration/targets/stepfunctions_state_machine/files/state_machines_iam_trust_policy.json b/ansible_collections/community/aws/tests/integration/targets/stepfunctions_state_machine/files/state_machines_iam_trust_policy.json new file mode 100644 index 000000000..48d627220 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/stepfunctions_state_machine/files/state_machines_iam_trust_policy.json @@ -0,0 +1,12 @@ +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Service": "states.amazonaws.com" + }, + "Action": "sts:AssumeRole" + } + ] +}
\ No newline at end of file diff --git a/ansible_collections/community/aws/tests/integration/targets/stepfunctions_state_machine/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/stepfunctions_state_machine/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/stepfunctions_state_machine/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/stepfunctions_state_machine/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/stepfunctions_state_machine/tasks/main.yml new file mode 100644 index 000000000..8c4bbec71 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/stepfunctions_state_machine/tasks/main.yml @@ -0,0 +1,305 @@ +--- + +- name: Integration test for AWS Step Function state machine module + module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + collections: + - amazon.aws + + block: + + # ==== Setup ================================================== + + - name: Create IAM service role needed for Step Functions + iam_role: + name: "{{ step_functions_role_name }}" + description: Role with permissions for AWS Step Functions actions. + assume_role_policy_document: "{{ lookup('file', 'state_machines_iam_trust_policy.json') }}" + state: present + register: step_functions_role + + - name: Pause a few seconds to ensure IAM role is available to next task + pause: + seconds: 10 + + - name: Create a random component for state machine name + set_fact: + random_num: "{{ 999999999 | random }}" + + # ==== Tests =================================================== + + - name: Create a new state machine -- check_mode + aws_step_functions_state_machine: + name: "{{ state_machine_name }}" + definition: "{{ lookup('file','state_machine.json') }}" + role_arn: "{{ step_functions_role.iam_role.arn }}" + tags: + project: helloWorld + state: present + register: creation_check + check_mode: yes + + - assert: + that: + - creation_check.changed == True + - creation_check.output == 'State machine would be created.' + + - name: Create a new state machine + aws_step_functions_state_machine: + name: "{{ state_machine_name }}" + definition: "{{ lookup('file','state_machine.json') }}" + role_arn: "{{ step_functions_role.iam_role.arn }}" + tags: + project: helloWorld + state: present + register: creation_output + + - assert: + that: + - creation_output.changed == True + - '"state_machine_arn" in creation_output' + + - name: Pause a few seconds to ensure state machine role is available + pause: + seconds: 5 + + - name: Idempotent rerun of same state function -- check_mode + aws_step_functions_state_machine: + name: "{{ state_machine_name }}" + definition: "{{ lookup('file','state_machine.json') }}" + role_arn: "{{ step_functions_role.iam_role.arn }}" + tags: + project: helloWorld + state: present + register: result + check_mode: yes + + - assert: + that: + - result.changed == False + - result.output == 'State is up-to-date.' + + - name: Idempotent rerun of same state function + aws_step_functions_state_machine: + name: "{{ state_machine_name }}" + definition: "{{ lookup('file','state_machine.json') }}" + role_arn: "{{ step_functions_role.iam_role.arn }}" + tags: + project: helloWorld + state: present + register: result + + - assert: + that: + - result.changed == False + - result.state_machine_arn == creation_output.state_machine_arn + + - name: Update an existing state machine -- check_mode + aws_step_functions_state_machine: + name: "{{ state_machine_name }}" + definition: "{{ lookup('file','alternative_state_machine.json') }}" + role_arn: "{{ step_functions_role.iam_role.arn }}" + tags: + differentTag: different_tag + state: present + register: update_check + check_mode: yes + + - assert: + that: + - update_check.changed == True + - "update_check.output == 'State machine would be updated: {{ creation_output.state_machine_arn }}'" + + - name: Update an existing state machine + aws_step_functions_state_machine: + name: "{{ state_machine_name }}" + definition: "{{ lookup('file','alternative_state_machine.json') }}" + role_arn: "{{ step_functions_role.iam_role.arn }}" + tags: + differentTag: different_tag + state: present + register: update_output + + - assert: + that: + - update_output.changed == True + - update_output.state_machine_arn == creation_output.state_machine_arn + + - name: Start execution of state machine -- check_mode + aws_step_functions_state_machine_execution: + name: "{{ execution_name }}" + execution_input: "{}" + state_machine_arn: "{{ creation_output.state_machine_arn }}" + register: start_execution_output + check_mode: yes + + - assert: + that: + - start_execution_output.changed == True + - "start_execution_output.output == 'State machine execution would be started.'" + + - name: Start execution of state machine + aws_step_functions_state_machine_execution: + name: "{{ execution_name }}" + execution_input: "{}" + state_machine_arn: "{{ creation_output.state_machine_arn }}" + register: start_execution_output + + - assert: + that: + - start_execution_output.changed + - "'execution_arn' in start_execution_output" + - "'start_date' in start_execution_output" + + - name: Start execution of state machine (check for idempotency) (check mode) + aws_step_functions_state_machine_execution: + name: "{{ execution_name }}" + execution_input: "{}" + state_machine_arn: "{{ creation_output.state_machine_arn }}" + register: start_execution_output_idem_check + check_mode: yes + + - assert: + that: + - not start_execution_output_idem_check.changed + - "start_execution_output_idem_check.output == 'State machine execution already exists.'" + + - name: Start execution of state machine (check for idempotency) + aws_step_functions_state_machine_execution: + name: "{{ execution_name }}" + execution_input: "{}" + state_machine_arn: "{{ creation_output.state_machine_arn }}" + register: start_execution_output_idem + + - assert: + that: + - not start_execution_output_idem.changed + + - name: Stop execution of state machine -- check_mode + aws_step_functions_state_machine_execution: + action: stop + execution_arn: "{{ start_execution_output.execution_arn }}" + cause: "cause of the failure" + error: "error code of the failure" + register: stop_execution_output + check_mode: yes + + - assert: + that: + - stop_execution_output.changed + - "stop_execution_output.output == 'State machine execution would be stopped.'" + + - name: Stop execution of state machine + aws_step_functions_state_machine_execution: + action: stop + execution_arn: "{{ start_execution_output.execution_arn }}" + cause: "cause of the failure" + error: "error code of the failure" + register: stop_execution_output + + - assert: + that: + - stop_execution_output.changed + - "'stop_date' in stop_execution_output" + + - name: Stop execution of state machine (check for idempotency) + aws_step_functions_state_machine_execution: + action: stop + execution_arn: "{{ start_execution_output.execution_arn }}" + cause: "cause of the failure" + error: "error code of the failure" + register: stop_execution_output + + - assert: + that: + - not stop_execution_output.changed + + - name: Try stopping a non-running execution -- check_mode + aws_step_functions_state_machine_execution: + action: stop + execution_arn: "{{ start_execution_output.execution_arn }}" + cause: "cause of the failure" + error: "error code of the failure" + register: stop_execution_output + check_mode: yes + + - assert: + that: + - not stop_execution_output.changed + - "stop_execution_output.output == 'State machine execution is not running.'" + + - name: Try stopping a non-running execution + aws_step_functions_state_machine_execution: + action: stop + execution_arn: "{{ start_execution_output.execution_arn }}" + cause: "cause of the failure" + error: "error code of the failure" + register: stop_execution_output + check_mode: yes + + - assert: + that: + - not stop_execution_output.changed + + - name: Start execution of state machine with the same execution name + aws_step_functions_state_machine_execution: + name: "{{ execution_name }}" + state_machine_arn: "{{ creation_output.state_machine_arn }}" + register: start_execution_output_again + + - assert: + that: + - not start_execution_output_again.changed + + - name: Remove state machine -- check_mode + aws_step_functions_state_machine: + name: "{{ state_machine_name }}" + state: absent + register: deletion_check + check_mode: yes + + - assert: + that: + - deletion_check.changed == True + - "deletion_check.output == 'State machine would be deleted: {{ creation_output.state_machine_arn }}'" + + - name: Remove state machine + aws_step_functions_state_machine: + name: "{{ state_machine_name }}" + state: absent + register: deletion_output + + - assert: + that: + - deletion_output.changed == True + - deletion_output.state_machine_arn == creation_output.state_machine_arn + + - name: Non-existent state machine is absent + aws_step_functions_state_machine: + name: "non_existing_state_machine" + state: absent + register: result + + - assert: + that: + - result.changed == False + + # ==== Cleanup ==================================================== + + always: + + - name: Cleanup - delete state machine + aws_step_functions_state_machine: + name: "{{ state_machine_name }}" + state: absent + ignore_errors: true + + - name: Cleanup - delete IAM role needed for Step Functions test + iam_role: + name: "{{ step_functions_role_name }}" + state: absent + ignore_errors: true diff --git a/ansible_collections/community/aws/tests/integration/targets/sts_assume_role/aliases b/ansible_collections/community/aws/tests/integration/targets/sts_assume_role/aliases new file mode 100644 index 000000000..4ef4b2067 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/sts_assume_role/aliases @@ -0,0 +1 @@ +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/sts_assume_role/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/sts_assume_role/defaults/main.yml new file mode 100644 index 000000000..17072d6a4 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/sts_assume_role/defaults/main.yml @@ -0,0 +1 @@ +iam_role_name: "ansible-test-{{ tiny_prefix }}" diff --git a/ansible_collections/community/aws/tests/integration/targets/sts_assume_role/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/sts_assume_role/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/sts_assume_role/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/sts_assume_role/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/sts_assume_role/tasks/main.yml new file mode 100644 index 000000000..be684dcea --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/sts_assume_role/tasks/main.yml @@ -0,0 +1,332 @@ +--- +# tasks file for sts_assume_role + +- module_defaults: + group/aws: + region: "{{ aws_region }}" + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + collections: + - amazon.aws + block: + # Get some information about who we are before starting our tests + # we'll need this as soon as we start working on the policies + - name: get ARN of calling user + aws_caller_info: + register: aws_caller_info + + - name: register account id + set_fact: + aws_account: "{{ aws_caller_info.account }}" + + # ============================================================ + - name: create test iam role + iam_role: + name: "{{ iam_role_name }}" + assume_role_policy_document: "{{ lookup('template','policy.json.j2') }}" + create_instance_profile: False + managed_policy: + - arn:aws:iam::aws:policy/IAMReadOnlyAccess + state: present + register: test_role + + # ============================================================ + - name: pause to ensure role exists before using + pause: + seconds: 30 + + # ============================================================ + - name: test with no parameters + sts_assume_role: + aws_access_key: '{{ omit }}' + aws_secret_key: '{{ omit }}' + security_token: '{{ omit }}' + register: result + ignore_errors: true + + - name: assert with no parameters + assert: + that: + - 'result.failed' + - "'missing required arguments:' in result.msg" + + # ============================================================ + - name: test with empty parameters + sts_assume_role: + role_arn: + role_session_name: + policy: + duration_seconds: + external_id: + mfa_token: + mfa_serial_number: + register: result + ignore_errors: true + + - name: assert with empty parameters + assert: + that: + - 'result.failed' + - "'Missing required parameter in input:' in result.msg" + when: result.module_stderr is not defined + + - name: assert with empty parameters + assert: + that: + - 'result.failed' + - "'Member must have length greater than or equal to 20' in result.module_stderr" + when: result.module_stderr is defined + + # ============================================================ + - name: test with only 'role_arn' parameter + sts_assume_role: + role_arn: "{{ test_role.iam_role.arn }}" + register: result + ignore_errors: true + + - name: assert with only 'role_arn' parameter + assert: + that: + - 'result.failed' + - "'missing required arguments: role_session_name' in result.msg" + + # ============================================================ + - name: test with only 'role_session_name' parameter + sts_assume_role: + role_session_name: "AnsibleTest" + register: result + ignore_errors: true + + - name: assert with only 'role_session_name' parameter + assert: + that: + - 'result.failed' + - "'missing required arguments: role_arn' in result.msg" + + # ============================================================ + - name: test assume role with invalid policy + sts_assume_role: + role_arn: "{{ test_role.iam_role.arn }}" + role_session_name: "AnsibleTest" + policy: "invalid policy" + register: result + ignore_errors: true + + - name: assert assume role with invalid policy + assert: + that: + - 'result.failed' + - "'The policy is not in the valid JSON format.' in result.msg" + when: result.module_stderr is not defined + + - name: assert assume role with invalid policy + assert: + that: + - 'result.failed' + - "'The policy is not in the valid JSON format.' in result.module_stderr" + when: result.module_stderr is defined + + # ============================================================ + - name: test assume role with invalid duration seconds + sts_assume_role: + role_arn: "{{ test_role.iam_role.arn }}" + role_session_name: AnsibleTest + duration_seconds: invalid duration + register: result + ignore_errors: true + + - name: assert assume role with invalid duration seconds + assert: + that: + - result is failed + - "'duration_seconds' in result.msg" + - "'cannot be converted to an int' in result.msg" + + # ============================================================ + - name: test assume role with invalid external id + sts_assume_role: + role_arn: "{{ test_role.iam_role.arn }}" + role_session_name: AnsibleTest + external_id: invalid external id + register: result + ignore_errors: true + + - name: assert assume role with invalid external id + assert: + that: + - 'result.failed' + - "'Member must satisfy regular expression pattern:' in result.msg" + when: result.module_stderr is not defined + + - name: assert assume role with invalid external id + assert: + that: + - 'result.failed' + - "'Member must satisfy regular expression pattern:' in result.module_stderr" + when: result.module_stderr is defined + + # ============================================================ + - name: test assume role with invalid mfa serial number + sts_assume_role: + role_arn: "{{ test_role.iam_role.arn }}" + role_session_name: AnsibleTest + mfa_serial_number: invalid serial number + register: result + ignore_errors: true + + - name: assert assume role with invalid mfa serial number + assert: + that: + - 'result.failed' + - "'Member must satisfy regular expression pattern:' in result.msg" + when: result.module_stderr is not defined + + - name: assert assume role with invalid mfa serial number + assert: + that: + - 'result.failed' + - "'Member must satisfy regular expression pattern:' in result.module_stderr" + when: result.module_stderr is defined + + # ============================================================ + - name: test assume role with invalid mfa token code + sts_assume_role: + role_arn: "{{ test_role.iam_role.arn }}" + role_session_name: AnsibleTest + mfa_token: invalid token code + register: result + ignore_errors: true + + - name: assert assume role with invalid mfa token code + assert: + that: + - 'result.failed' + - "'Member must satisfy regular expression pattern:' in result.msg" + when: result.module_stderr is not defined + + - name: assert assume role with invalid mfa token code + assert: + that: + - 'result.failed' + - "'Member must satisfy regular expression pattern:' in result.module_stderr" + when: result.module_stderr is defined + + # ============================================================ + - name: test assume role with invalid role_arn + sts_assume_role: + role_arn: invalid role arn + role_session_name: AnsibleTest + register: result + ignore_errors: true + + - name: assert assume role with invalid role_arn + assert: + that: + - result.failed + - "'Invalid length for parameter RoleArn' in result.msg" + when: result.module_stderr is not defined + + - name: assert assume role with invalid role_arn + assert: + that: + - 'result.failed' + - "'Member must have length greater than or equal to 20' in result.module_stderr" + when: result.module_stderr is defined + + # ============================================================ + - name: test assume not existing sts role + sts_assume_role: + role_arn: "arn:aws:iam::123456789:role/non-existing-role" + role_session_name: "AnsibleTest" + register: result + ignore_errors: true + + - name: assert assume not existing sts role + assert: + that: + - 'result.failed' + - "'is not authorized to perform: sts:AssumeRole' in result.msg" + when: result.module_stderr is not defined + + - name: assert assume not existing sts role + assert: + that: + - 'result.failed' + - "'is not authorized to perform: sts:AssumeRole' in result.msg" + when: result.module_stderr is defined + + # ============================================================ + - name: test assume role + sts_assume_role: + role_arn: "{{ test_role.iam_role.arn }}" + role_session_name: AnsibleTest + register: assumed_role + + - name: assert assume role + assert: + that: + - 'not assumed_role.failed' + - "'sts_creds' in assumed_role" + - "'access_key' in assumed_role.sts_creds" + - "'secret_key' in assumed_role.sts_creds" + - "'session_token' in assumed_role.sts_creds" + + # ============================================================ + - name: test that assumed credentials have IAM read-only access + iam_role: + aws_access_key: "{{ assumed_role.sts_creds.access_key }}" + aws_secret_key: "{{ assumed_role.sts_creds.secret_key }}" + security_token: "{{ assumed_role.sts_creds.session_token }}" + name: "{{ iam_role_name }}" + assume_role_policy_document: "{{ lookup('template','policy.json.j2') }}" + create_instance_profile: False + state: present + register: result + + - name: assert assumed role with privileged action (expect changed=false) + assert: + that: + - 'not result.failed' + - 'not result.changed' + - "'iam_role' in result" + + # ============================================================ + - name: test assumed role with unprivileged action + iam_role: + aws_access_key: "{{ assumed_role.sts_creds.access_key }}" + aws_secret_key: "{{ assumed_role.sts_creds.secret_key }}" + security_token: "{{ assumed_role.sts_creds.session_token }}" + name: "{{ iam_role_name }}-new" + assume_role_policy_document: "{{ lookup('template','policy.json.j2') }}" + state: present + register: result + ignore_errors: true + + - name: assert assumed role with unprivileged action (expect changed=false) + assert: + that: + - 'result.failed' + - "'is not authorized to perform: iam:CreateRole' in result.msg" + # runs on Python2 + when: result.module_stderr is not defined + + - name: assert assumed role with unprivileged action (expect changed=false) + assert: + that: + - 'result.failed' + - "'is not authorized to perform: iam:CreateRole' in result.module_stderr" + # runs on Python3 + when: result.module_stderr is defined + + # ============================================================ + always: + + - name: delete test iam role + iam_role: + name: "{{ iam_role_name }}" + assume_role_policy_document: "{{ lookup('template','policy.json.j2') }}" + delete_instance_profile: True + managed_policy: + - arn:aws:iam::aws:policy/IAMReadOnlyAccess + state: absent diff --git a/ansible_collections/community/aws/tests/integration/targets/sts_assume_role/templates/policy.json.j2 b/ansible_collections/community/aws/tests/integration/targets/sts_assume_role/templates/policy.json.j2 new file mode 100644 index 000000000..559562fd9 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/sts_assume_role/templates/policy.json.j2 @@ -0,0 +1,12 @@ +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "AWS": "arn:aws:iam::{{ aws_account }}:root" + }, + "Action": "sts:AssumeRole" + } + ] +}
\ No newline at end of file diff --git a/ansible_collections/community/aws/tests/integration/targets/sts_session_token/aliases b/ansible_collections/community/aws/tests/integration/targets/sts_session_token/aliases new file mode 100644 index 000000000..c0a6cc960 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/sts_session_token/aliases @@ -0,0 +1,4 @@ +# To use sts_session_token you need a user, CI uses an assumed role. +unsupported + +cloud/aws diff --git a/ansible_collections/community/aws/tests/integration/targets/sts_session_token/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/sts_session_token/defaults/main.yml new file mode 100644 index 000000000..ed97d539c --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/sts_session_token/defaults/main.yml @@ -0,0 +1 @@ +--- diff --git a/ansible_collections/community/aws/tests/integration/targets/sts_session_token/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/sts_session_token/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/sts_session_token/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/sts_session_token/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/sts_session_token/tasks/main.yml new file mode 100644 index 000000000..6231119ec --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/sts_session_token/tasks/main.yml @@ -0,0 +1,68 @@ +--- +# tasks file for sts_assume_role +- module_defaults: + group/aws: + region: "{{ aws_region }}" + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + collections: + - amazon.aws + block: + # Get some information about who we are before starting our tests + # we'll need this as soon as we start working on the policies + - name: get ARN of calling user + aws_caller_info: + register: original_aws_caller_info + + # ============================================================ + + - name: Get a session token + sts_session_token: + register: token_details + no_log: True + + - assert: + that: + - '"sts_creds" in token_details' + - '"access_key" in token_details.sts_creds' + - '"expiration" in token_details.sts_creds' + - '"secret_key" in token_details.sts_creds' + - '"session_token" in token_details.sts_creds' + + - name: Get a session token + sts_session_token: + duration_seconds: 1000 + register: token_details + no_log: True + + - debug: + var: ansible_date_time + + - assert: + that: + - '"sts_creds" in token_details' + - '"access_key" in token_details.sts_creds' + - '"expiration" in token_details.sts_creds' + - '"secret_key" in token_details.sts_creds' + - '"session_token" in token_details.sts_creds' + # Expiry within a minute of given duration + - (((expiry | int) - (ansible_date_time.epoch | int)) - 1000 | abs) < 60 + vars: + # 2022-06-21T03:26:13+00:00 + expiry: "{{ (token_details.sts_creds.expiration | to_datetime('%Y-%m-%dT%H:%M:%S%z')).strftime('%s') }}" + + - name: Get ARN of user when running with generated token + aws_caller_info: + aws_access_key: "{{ token_details.sts_creds.access_key }}" + aws_secret_key: "{{ token_details.sts_creds.secret_key }}" + security_token: "{{ token_details.sts_creds.session_token }}" + register: token_aws_caller_info + + - assert: + that: + - token_aws_caller_info.account == original_aws_caller_info.account + - token_aws_caller_info.arn == original_aws_caller_info.arn + + # ============================================================ + always: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/waf_web_acl/aliases b/ansible_collections/community/aws/tests/integration/targets/waf_web_acl/aliases new file mode 100644 index 000000000..7e34262e4 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/waf_web_acl/aliases @@ -0,0 +1,9 @@ +# reason: broken +# ansible/ansible#38258 +disabled + +cloud/aws + +waf_condition +waf_info +waf_rule diff --git a/ansible_collections/community/aws/tests/integration/targets/waf_web_acl/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/waf_web_acl/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/waf_web_acl/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/waf_web_acl/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/waf_web_acl/tasks/main.yml new file mode 100644 index 000000000..c176e7def --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/waf_web_acl/tasks/main.yml @@ -0,0 +1,1121 @@ +--- +- name: 'aws_waf_web_acl integration tests' + collections: + - amazon.aws + module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + block: + + ################################################## + # aws_waf_condition tests + ################################################## + + - name: create WAF IP condition + aws_waf_condition: + name: "{{ resource_prefix }}_ip_condition" + filters: + - ip_address: "10.0.0.0/8" + type: ip + register: create_waf_ip_condition + + - name: add an IP address to WAF condition + aws_waf_condition: + name: "{{ resource_prefix }}_ip_condition" + filters: + - ip_address: "10.0.0.0/8" + - ip_address: "192.168.0.0/24" + type: ip + register: add_ip_address_to_waf_condition + + - name: check expected waf filter length + assert: + that: + - add_ip_address_to_waf_condition.condition.ip_set_descriptors|length == 2 + + - name: add an IP address to WAF condition (rely on purge_filters defaulting to false) + aws_waf_condition: + name: "{{ resource_prefix }}_ip_condition" + filters: + - ip_address: "192.168.10.0/24" + type: ip + register: add_ip_address_to_waf_condition_no_purge + + - name: check waf filter length has increased + assert: + that: + - add_ip_address_to_waf_condition_no_purge.condition.ip_set_descriptors|length == 3 + - add_ip_address_to_waf_condition_no_purge.changed + + - name: add an IP address to WAF condition (set purge_filters) + aws_waf_condition: + name: "{{ resource_prefix }}_ip_condition" + filters: + - ip_address: "192.168.20.0/24" + purge_filters: yes + type: ip + register: add_ip_address_to_waf_condition_purge + + - name: check waf filter length has reduced + assert: + that: + - add_ip_address_to_waf_condition_purge.condition.ip_set_descriptors|length == 1 + - add_ip_address_to_waf_condition_purge.changed + + - name: create WAF byte condition + aws_waf_condition: + name: "{{ resource_prefix }}_byte_condition" + filters: + - field_to_match: header + position: STARTS_WITH + target_string: Hello + header: Content-type + type: byte + register: create_waf_byte_condition + + - name: recreate WAF byte condition + aws_waf_condition: + name: "{{ resource_prefix }}_byte_condition" + filters: + - field_to_match: header + position: STARTS_WITH + target_string: Hello + header: Content-type + type: byte + register: recreate_waf_byte_condition + + - name: assert that no change was made + assert: + that: + - not recreate_waf_byte_condition.changed + + - name: create WAF geo condition + aws_waf_condition: + name: "{{ resource_prefix }}_geo_condition" + filters: + - country: US + - country: AU + - country: AT + type: geo + register: create_waf_geo_condition + + - name: create WAF size condition + aws_waf_condition: + name: "{{ resource_prefix }}_size_condition" + filters: + - field_to_match: query_string + size: 300 + comparison: GT + type: size + register: create_waf_size_condition + + - name: create WAF sql condition + aws_waf_condition: + name: "{{ resource_prefix }}_sql_condition" + filters: + - field_to_match: query_string + transformation: url_decode + type: sql + register: create_waf_sql_condition + + - name: create WAF xss condition + aws_waf_condition: + name: "{{ resource_prefix }}_xss_condition" + filters: + - field_to_match: query_string + transformation: url_decode + type: xss + register: create_waf_xss_condition + + - name: create WAF regex condition + aws_waf_condition: + name: "{{ resource_prefix }}_regex_condition" + filters: + - field_to_match: query_string + regex_pattern: + name: greetings + regex_strings: + - '[hH]ello' + - '^Hi there' + - '.*Good Day to You' + type: regex + register: create_waf_regex_condition + + - name: create a second WAF regex condition with the same regex + aws_waf_condition: + name: "{{ resource_prefix }}_regex_condition_part_2" + filters: + - field_to_match: header + header: cookie + regex_pattern: + name: greetings + regex_strings: + - '[hH]ello' + - '^Hi there' + - '.*Good Day to You' + type: regex + register: create_second_waf_regex_condition + + - name: check that the pattern is shared + assert: + that: + - > + create_waf_regex_condition.condition.regex_match_tuples[0].regex_pattern_set_id == + create_second_waf_regex_condition.condition.regex_match_tuples[0].regex_pattern_set_id + - create_second_waf_regex_condition.changed + + + - name: delete first WAF regex condition + aws_waf_condition: + name: "{{ resource_prefix }}_regex_condition" + filters: + - field_to_match: query_string + regex_pattern: + name: greetings + regex_strings: + - '[hH]ello' + - '^Hi there' + - '.*Good Day to You' + type: regex + state: absent + register: delete_waf_regex_condition + + - name: delete second WAF regex condition + aws_waf_condition: + name: "{{ resource_prefix }}_regex_condition_part_2" + filters: + - field_to_match: header + header: cookie + regex_pattern: + name: greetings + regex_strings: + - '[hH]ello' + - '^Hi there' + - '.*Good Day to You' + type: regex + state: absent + register: delete_second_waf_regex_condition + + - name: create WAF regex condition + aws_waf_condition: + name: "{{ resource_prefix }}_regex_condition" + filters: + - field_to_match: query_string + regex_pattern: + name: greetings + regex_strings: + - '[hH]ello' + - '^Hi there' + - '.*Good Day to You' + type: regex + register: recreate_waf_regex_condition + + - name: check that a new pattern is created (because the first pattern should have been deleted once unused) + assert: + that: + - > + recreate_waf_regex_condition.condition.regex_match_tuples[0].regex_pattern_set_id != + create_waf_regex_condition.condition.regex_match_tuples[0].regex_pattern_set_id + + - name: create WAF Regional IP condition + aws_waf_condition: + name: "{{ resource_prefix }}_ip_condition" + filters: + - ip_address: "10.0.0.0/8" + type: ip + region: "{{ aws_region }}" + waf_regional: true + register: create_waf_regional_ip_condition + + - name: add an IP address to WAF Regional condition + aws_waf_condition: + name: "{{ resource_prefix }}_ip_condition" + filters: + - ip_address: "10.0.0.0/8" + - ip_address: "192.168.0.0/24" + type: ip + region: "{{ aws_region }}" + waf_regional: true + register: add_ip_address_to_waf_regional_condition + + - name: check expected WAF Regional filter length + assert: + that: + - add_ip_address_to_waf_regional_condition.condition.ip_set_descriptors|length == 2 + + - name: add an IP address to WAF Regional condition (rely on purge_filters defaulting to false) + aws_waf_condition: + name: "{{ resource_prefix }}_ip_condition" + filters: + - ip_address: "192.168.10.0/24" + type: ip + region: "{{ aws_region }}" + waf_regional: true + register: add_ip_address_to_waf_regional_condition_no_purge + + - name: check WAF Regional filter length has increased + assert: + that: + - add_ip_address_to_waf_regional_condition_no_purge.condition.ip_set_descriptors|length == 3 + - add_ip_address_to_waf_regional_condition_no_purge.changed + + - name: add an IP address to WAF Regional condition (set purge_filters) + aws_waf_condition: + name: "{{ resource_prefix }}_ip_condition" + filters: + - ip_address: "192.168.20.0/24" + purge_filters: yes + type: ip + region: "{{ aws_region }}" + waf_regional: true + register: add_ip_address_to_waf_regional_condition_purge + + - name: check WAF Regional filter length has reduced + assert: + that: + - add_ip_address_to_waf_regional_condition_purge.condition.ip_set_descriptors|length == 1 + - add_ip_address_to_waf_regional_condition_purge.changed + + - name: create WAF Regional byte condition + aws_waf_condition: + name: "{{ resource_prefix }}_byte_condition" + filters: + - field_to_match: header + position: STARTS_WITH + target_string: Hello + header: Content-type + type: byte + region: "{{ aws_region }}" + waf_regional: true + register: create_waf_regional_byte_condition + + - name: recreate WAF Regional byte condition + aws_waf_condition: + name: "{{ resource_prefix }}_byte_condition" + filters: + - field_to_match: header + position: STARTS_WITH + target_string: Hello + header: Content-type + type: byte + region: "{{ aws_region }}" + waf_regional: true + register: recreate_waf_regional_byte_condition + + - name: assert that no change was made + assert: + that: + - not recreate_waf_regional_byte_condition.changed + + - name: create WAF Regional geo condition + aws_waf_condition: + name: "{{ resource_prefix }}_geo_condition" + filters: + - country: US + - country: AU + - country: AT + type: geo + region: "{{ aws_region }}" + waf_regional: true + register: create_waf_regional_geo_condition + + - name: create WAF Regional size condition + aws_waf_condition: + name: "{{ resource_prefix }}_size_condition" + filters: + - field_to_match: query_string + size: 300 + comparison: GT + type: size + region: "{{ aws_region }}" + waf_regional: true + register: create_waf_regional_size_condition + + - name: create WAF Regional sql condition + aws_waf_condition: + name: "{{ resource_prefix }}_sql_condition" + filters: + - field_to_match: query_string + transformation: url_decode + type: sql + region: "{{ aws_region }}" + waf_regional: true + register: create_waf_regional_sql_condition + + - name: create WAF Regional xss condition + aws_waf_condition: + name: "{{ resource_prefix }}_xss_condition" + filters: + - field_to_match: query_string + transformation: url_decode + type: xss + region: "{{ aws_region }}" + waf_regional: true + register: create_waf_regional_xss_condition + + - name: create WAF Regional regex condition + aws_waf_condition: + name: "{{ resource_prefix }}_regex_condition" + filters: + - field_to_match: query_string + regex_pattern: + name: greetings + regex_strings: + - '[hH]ello' + - '^Hi there' + - '.*Good Day to You' + type: regex + region: "{{ aws_region }}" + waf_regional: true + register: create_waf_regional_regex_condition + + - name: create a second WAF Regional regex condition with the same regex + aws_waf_condition: + name: "{{ resource_prefix }}_regex_condition_part_2" + filters: + - field_to_match: header + header: cookie + regex_pattern: + name: greetings + regex_strings: + - '[hH]ello' + - '^Hi there' + - '.*Good Day to You' + type: regex + region: "{{ aws_region }}" + waf_regional: true + register: create_second_waf_regional_regex_condition + + - name: check that the pattern is shared + assert: + that: + - > + create_waf_regional_regex_condition.condition.regex_match_tuples[0].regex_pattern_set_id == + create_second_waf_regional_regex_condition.condition.regex_match_tuples[0].regex_pattern_set_id + - create_second_waf_regional_regex_condition.changed + + + - name: delete first WAF Regional regex condition + aws_waf_condition: + name: "{{ resource_prefix }}_regex_condition" + filters: + - field_to_match: query_string + regex_pattern: + name: greetings + regex_strings: + - '[hH]ello' + - '^Hi there' + - '.*Good Day to You' + type: regex + state: absent + region: "{{ aws_region }}" + waf_regional: true + register: delete_waf_regional_regex_condition + + - name: delete second WAF Regional regex condition + aws_waf_condition: + name: "{{ resource_prefix }}_regex_condition_part_2" + filters: + - field_to_match: header + header: cookie + regex_pattern: + name: greetings + regex_strings: + - '[hH]ello' + - '^Hi there' + - '.*Good Day to You' + type: regex + state: absent + region: "{{ aws_region }}" + waf_regional: true + register: delete_second_waf_regional_regex_condition + + - name: create WAF Regional regex condition + aws_waf_condition: + name: "{{ resource_prefix }}_regex_condition" + filters: + - field_to_match: query_string + regex_pattern: + name: greetings + regex_strings: + - '[hH]ello' + - '^Hi there' + - '.*Good Day to You' + type: regex + region: "{{ aws_region }}" + waf_regional: true + register: recreate_waf_regional_regex_condition + + - name: check that a new pattern is created (because the first pattern should have been deleted once unused) + assert: + that: + - > + recreate_waf_regional_regex_condition.condition.regex_match_tuples[0].regex_pattern_set_id != + create_waf_regional_regex_condition.condition.regex_match_tuples[0].regex_pattern_set_id + + ################################################## + # aws_waf_rule tests + ################################################## + + - name: create WAF rule + aws_waf_rule: + name: "{{ resource_prefix }}_rule" + conditions: + - name: "{{ resource_prefix }}_regex_condition" + type: regex + negated: no + - name: "{{ resource_prefix }}_geo_condition" + type: geo + negated: no + - name: "{{ resource_prefix }}_byte_condition" + type: byte + negated: no + purge_conditions: yes + register: create_aws_waf_rule + + - name: check WAF rule + assert: + that: + - create_aws_waf_rule.changed + - create_aws_waf_rule.rule.predicates|length == 3 + + - name: recreate WAF rule + aws_waf_rule: + name: "{{ resource_prefix }}_rule" + conditions: + - name: "{{ resource_prefix }}_regex_condition" + type: regex + negated: no + - name: "{{ resource_prefix }}_geo_condition" + type: geo + negated: no + - name: "{{ resource_prefix }}_byte_condition" + type: byte + negated: no + register: create_aws_waf_rule + + - name: check WAF rule did not change + assert: + that: + - not create_aws_waf_rule.changed + - create_aws_waf_rule.rule.predicates|length == 3 + + - name: add further WAF rules relying on purge_conditions defaulting to false + aws_waf_rule: + name: "{{ resource_prefix }}_rule" + conditions: + - name: "{{ resource_prefix }}_ip_condition" + type: ip + negated: yes + - name: "{{ resource_prefix }}_sql_condition" + type: sql + negated: no + - name: "{{ resource_prefix }}_xss_condition" + type: xss + negated: no + register: add_conditions_to_aws_waf_rule + + - name: check WAF rule added rules + assert: + that: + - add_conditions_to_aws_waf_rule.changed + - add_conditions_to_aws_waf_rule.rule.predicates|length == 6 + + - name: remove some rules through purging conditions + aws_waf_rule: + name: "{{ resource_prefix }}_rule" + conditions: + - name: "{{ resource_prefix }}_ip_condition" + type: ip + negated: yes + - name: "{{ resource_prefix }}_xss_condition" + type: xss + negated: no + - name: "{{ resource_prefix }}_byte_condition" + type: byte + negated: no + - name: "{{ resource_prefix }}_size_condition" + type: size + negated: no + purge_conditions: yes + register: add_and_remove_waf_rule_conditions + + - name: check WAF rules were updated as expected + assert: + that: + - add_and_remove_waf_rule_conditions.changed + - add_and_remove_waf_rule_conditions.rule.predicates|length == 4 + + - name: attempt to remove an in use condition + aws_waf_condition: + name: "{{ resource_prefix }}_size_condition" + type: size + state: absent + ignore_errors: yes + register: remove_in_use_condition + + - name: check failure was sensible + assert: + that: + - remove_in_use_condition.failed + - "'Condition {{ resource_prefix }}_size_condition is in use' in remove_in_use_condition.msg" + + - name: create WAF Regional rule + aws_waf_rule: + name: "{{ resource_prefix }}_rule" + conditions: + - name: "{{ resource_prefix }}_regex_condition" + type: regex + negated: no + - name: "{{ resource_prefix }}_geo_condition" + type: geo + negated: no + - name: "{{ resource_prefix }}_byte_condition" + type: byte + negated: no + purge_conditions: yes + region: "{{ aws_region }}" + waf_regional: true + register: create_aws_waf_regional_rule + + - name: check WAF Regional rule + assert: + that: + - create_aws_waf_regional_rule.changed + - create_aws_waf_regional_rule.rule.predicates|length == 3 + + - name: recreate WAF Regional rule + aws_waf_rule: + name: "{{ resource_prefix }}_rule" + conditions: + - name: "{{ resource_prefix }}_regex_condition" + type: regex + negated: no + - name: "{{ resource_prefix }}_geo_condition" + type: geo + negated: no + - name: "{{ resource_prefix }}_byte_condition" + type: byte + negated: no + region: "{{ aws_region }}" + waf_regional: true + register: create_aws_waf_regional_rule + + - name: check WAF Regional rule did not change + assert: + that: + - not create_aws_waf_regional_rule.changed + - create_aws_waf_regional_rule.rule.predicates|length == 3 + + - name: add further WAF Regional rules relying on purge_conditions defaulting to false + aws_waf_rule: + name: "{{ resource_prefix }}_rule" + conditions: + - name: "{{ resource_prefix }}_ip_condition" + type: ip + negated: yes + - name: "{{ resource_prefix }}_sql_condition" + type: sql + negated: no + - name: "{{ resource_prefix }}_xss_condition" + type: xss + negated: no + region: "{{ aws_region }}" + waf_regional: true + register: add_conditions_to_aws_waf_regional_rule + + - name: check WAF Regional rule added rules + assert: + that: + - add_conditions_to_aws_waf_regional_rule.changed + - add_conditions_to_aws_waf_regional_rule.rule.predicates|length == 6 + + - name: remove some rules through purging conditions + aws_waf_rule: + name: "{{ resource_prefix }}_rule" + conditions: + - name: "{{ resource_prefix }}_ip_condition" + type: ip + negated: yes + - name: "{{ resource_prefix }}_xss_condition" + type: xss + negated: no + - name: "{{ resource_prefix }}_byte_condition" + type: byte + negated: no + - name: "{{ resource_prefix }}_size_condition" + type: size + negated: no + purge_conditions: yes + region: "{{ aws_region }}" + waf_regional: true + register: add_and_remove_waf_regional_rule_conditions + + - name: check WAF Regional rules were updated as expected + assert: + that: + - add_and_remove_waf_regional_rule_conditions.changed + - add_and_remove_waf_regional_rule_conditions.rule.predicates|length == 4 + + - name: attempt to remove an WAF Regional in use condition + aws_waf_condition: + name: "{{ resource_prefix }}_size_condition" + type: size + state: absent + region: "{{ aws_region }}" + waf_regional: true + ignore_errors: yes + register: remove_in_use_condition + + - name: check failure was sensible + assert: + that: + - remove_in_use_condition.failed + - "'Condition {{ resource_prefix }}_size_condition is in use' in remove_in_use_condition.msg" + + ################################################## + # aws_waf_web_acl tests + ################################################## + + - name: create web ACL + aws_waf_web_acl: + name: "{{ resource_prefix }}_web_acl" + rules: + - name: "{{ resource_prefix }}_rule" + priority: 1 + action: block + default_action: block + purge_rules: yes + state: present + register: create_web_acl + + - name: recreate web acl + aws_waf_web_acl: + name: "{{ resource_prefix }}_web_acl" + rules: + - name: "{{ resource_prefix }}_rule" + priority: 1 + action: block + default_action: block + state: present + register: recreate_web_acl + + - name: check web acl was not changed + assert: + that: + - not recreate_web_acl.changed + - recreate_web_acl.web_acl.rules|length == 1 + + - name: create a second WAF rule + aws_waf_rule: + name: "{{ resource_prefix }}_rule_2" + conditions: + - name: "{{ resource_prefix }}_ip_condition" + type: ip + negated: yes + - name: "{{ resource_prefix }}_sql_condition" + type: sql + negated: no + - name: "{{ resource_prefix }}_xss_condition" + type: xss + negated: no + + - name: add a new rule to the web acl + aws_waf_web_acl: + name: "{{ resource_prefix }}_web_acl" + rules: + - name: "{{ resource_prefix }}_rule_2" + priority: 2 + action: allow + default_action: block + state: present + register: web_acl_add_rule + + - name: check that rule was added to the web acl + assert: + that: + - web_acl_add_rule.changed + - web_acl_add_rule.web_acl.rules|length == 2 + + - name: use purge rules to remove the first rule + aws_waf_web_acl: + name: "{{ resource_prefix }}_web_acl" + rules: + - name: "{{ resource_prefix }}_rule_2" + priority: 2 + action: allow + purge_rules: yes + default_action: block + state: present + register: web_acl_add_rule + + - name: check that rule was removed from the web acl + assert: + that: + - web_acl_add_rule.changed + - web_acl_add_rule.web_acl.rules|length == 1 + + - name: swap two rules of same priority + aws_waf_web_acl: + name: "{{ resource_prefix }}_web_acl" + rules: + - name: "{{ resource_prefix }}_rule" + priority: 2 + action: allow + purge_rules: yes + default_action: block + state: present + register: web_acl_swap_rule + + - name: attempt to delete the inuse first rule + aws_waf_rule: + name: "{{ resource_prefix }}_rule" + state: absent + ignore_errors: yes + register: remove_inuse_rule + + - name: check that removing in-use rule fails + assert: + that: + - remove_inuse_rule.failed + + - name: delete the web acl + aws_waf_web_acl: + name: "{{ resource_prefix }}_web_acl" + state: absent + register: delete_web_acl + + - name: check that web acl was deleted + assert: + that: + - delete_web_acl.changed + - not delete_web_acl.web_acl + + - name: delete the no longer in use first rule + aws_waf_rule: + name: "{{ resource_prefix }}_rule" + state: absent + + - name: create WAF Regional web ACL + aws_waf_web_acl: + name: "{{ resource_prefix }}_web_acl" + rules: + - name: "{{ resource_prefix }}_rule" + priority: 1 + action: block + default_action: block + purge_rules: yes + state: present + region: "{{ aws_region }}" + waf_regional: true + register: create_waf_regional_web_acl + + - name: recreate WAF Regional web acl + aws_waf_web_acl: + name: "{{ resource_prefix }}_web_acl" + rules: + - name: "{{ resource_prefix }}_rule" + priority: 1 + action: block + default_action: block + state: present + region: "{{ aws_region }}" + waf_regional: true + register: recreate_waf_regional_web_acl + + - name: check WAF Regional web acl was not changed + assert: + that: + - not recreate_waf_regional_web_acl.changed + - recreate_waf_regional_web_acl.web_acl.rules|length == 1 + + - name: create a second WAF Regional rule + aws_waf_rule: + name: "{{ resource_prefix }}_rule_2" + conditions: + - name: "{{ resource_prefix }}_ip_condition" + type: ip + negated: yes + - name: "{{ resource_prefix }}_sql_condition" + type: sql + negated: no + - name: "{{ resource_prefix }}_xss_condition" + type: xss + negated: no + region: "{{ aws_region }}" + waf_regional: true + + - name: add a new rule to the WAF Regional web acl + aws_waf_web_acl: + name: "{{ resource_prefix }}_web_acl" + rules: + - name: "{{ resource_prefix }}_rule_2" + priority: 2 + action: allow + default_action: block + state: present + region: "{{ aws_region }}" + waf_regional: true + register: waf_regional_web_acl_add_rule + + - name: check that rule was added to the WAF Regional web acl + assert: + that: + - waf_regional_web_acl_add_rule.changed + - waf_regional_web_acl_add_rule.web_acl.rules|length == 2 + + - name: use purge rules to remove the WAF Regional first rule + aws_waf_web_acl: + name: "{{ resource_prefix }}_web_acl" + rules: + - name: "{{ resource_prefix }}_rule_2" + priority: 2 + action: allow + purge_rules: yes + default_action: block + state: present + region: "{{ aws_region }}" + waf_regional: true + register: waf_regional_web_acl_add_rule + + - name: check that rule was removed from the WAF Regional web acl + assert: + that: + - waf_regional_web_acl_add_rule.changed + - waf_regional_web_acl_add_rule.web_acl.rules|length == 1 + + - name: swap two WAF Regional rules of same priority + aws_waf_web_acl: + name: "{{ resource_prefix }}_web_acl" + rules: + - name: "{{ resource_prefix }}_rule" + priority: 2 + action: allow + purge_rules: yes + default_action: block + state: present + region: "{{ aws_region }}" + waf_regional: true + register: waf_regional_web_acl_swap_rule + + - name: attempt to delete the WAF Regional inuse first rule + aws_waf_rule: + name: "{{ resource_prefix }}_rule" + state: absent + region: "{{ aws_region }}" + waf_regional: true + ignore_errors: yes + register: remove_waf_regional_inuse_rule + + - name: check that removing WAF Regional in-use rule fails + assert: + that: + - remove_waf_regional_inuse_rule.failed + + - name: delete the WAF Regional web acl + aws_waf_web_acl: + name: "{{ resource_prefix }}_web_acl" + state: absent + region: "{{ aws_region }}" + waf_regional: true + register: delete_waf_regional_web_acl + + - name: check that WAF Regional web acl was deleted + assert: + that: + - delete_waf_regional_web_acl.changed + - not delete_waf_regional_web_acl.web_acl + + - name: delete the no longer in use WAF Regional first rule + aws_waf_rule: + name: "{{ resource_prefix }}_rule" + state: absent + region: "{{ aws_region }}" + waf_regional: true + + ################################################## + # TEARDOWN + ################################################## + + always: + - debug: + msg: "****** TEARDOWN STARTS HERE ******" + + - name: delete the web acl + aws_waf_web_acl: + name: "{{ resource_prefix }}_web_acl" + state: absent + purge_rules: yes + ignore_errors: yes + + - name: remove second WAF rule + aws_waf_rule: + name: "{{ resource_prefix }}_rule_2" + state: absent + purge_conditions: yes + ignore_errors: yes + + - name: remove WAF rule + aws_waf_rule: + name: "{{ resource_prefix }}_rule" + state: absent + purge_conditions: yes + ignore_errors: yes + + - name: remove XSS condition + aws_waf_condition: + name: "{{ resource_prefix }}_xss_condition" + type: xss + state: absent + ignore_errors: yes + + - name: remove SQL condition + aws_waf_condition: + name: "{{ resource_prefix }}_sql_condition" + type: sql + state: absent + ignore_errors: yes + + - name: remove size condition + aws_waf_condition: + name: "{{ resource_prefix }}_size_condition" + type: size + state: absent + ignore_errors: yes + + - name: remove geo condition + aws_waf_condition: + name: "{{ resource_prefix }}_geo_condition" + type: geo + state: absent + ignore_errors: yes + + - name: remove byte condition + aws_waf_condition: + name: "{{ resource_prefix }}_byte_condition" + type: byte + state: absent + ignore_errors: yes + + - name: remove ip address condition + aws_waf_condition: + name: "{{ resource_prefix }}_ip_condition" + type: ip + state: absent + ignore_errors: yes + + - name: remove regex part 2 condition + aws_waf_condition: + name: "{{ resource_prefix }}_regex_condition_part_2" + type: regex + state: absent + ignore_errors: yes + + - name: remove first regex condition + aws_waf_condition: + name: "{{ resource_prefix }}_regex_condition" + type: regex + state: absent + ignore_errors: yes + + - name: delete the WAF Regional web acl + aws_waf_web_acl: + name: "{{ resource_prefix }}_web_acl" + state: absent + purge_rules: yes + region: "{{ aws_region }}" + waf_regional: true + ignore_errors: yes + + - name: remove second WAF Regional rule + aws_waf_rule: + name: "{{ resource_prefix }}_rule_2" + state: absent + purge_conditions: yes + region: "{{ aws_region }}" + waf_regional: true + ignore_errors: yes + + - name: remove WAF Regional rule + aws_waf_rule: + name: "{{ resource_prefix }}_rule" + state: absent + purge_conditions: yes + region: "{{ aws_region }}" + waf_regional: true + ignore_errors: yes + + - name: remove WAF Regional XSS condition + aws_waf_condition: + name: "{{ resource_prefix }}_xss_condition" + type: xss + state: absent + region: "{{ aws_region }}" + waf_regional: true + ignore_errors: yes + + - name: remove WAF Regional SQL condition + aws_waf_condition: + name: "{{ resource_prefix }}_sql_condition" + type: sql + state: absent + region: "{{ aws_region }}" + waf_regional: true + ignore_errors: yes + + - name: remove WAF Regional size condition + aws_waf_condition: + name: "{{ resource_prefix }}_size_condition" + type: size + state: absent + region: "{{ aws_region }}" + waf_regional: true + ignore_errors: yes + + - name: remove WAF Regional geo condition + aws_waf_condition: + name: "{{ resource_prefix }}_geo_condition" + type: geo + state: absent + region: "{{ aws_region }}" + waf_regional: true + ignore_errors: yes + + - name: remove WAF Regional byte condition + aws_waf_condition: + name: "{{ resource_prefix }}_byte_condition" + type: byte + state: absent + region: "{{ aws_region }}" + waf_regional: true + ignore_errors: yes + + - name: remove WAF Regional ip address condition + aws_waf_condition: + name: "{{ resource_prefix }}_ip_condition" + type: ip + state: absent + region: "{{ aws_region }}" + waf_regional: true + ignore_errors: yes + + - name: remove WAF Regional regex part 2 condition + aws_waf_condition: + name: "{{ resource_prefix }}_regex_condition_part_2" + type: regex + state: absent + region: "{{ aws_region }}" + waf_regional: true + ignore_errors: yes + + - name: remove first WAF Regional regex condition + aws_waf_condition: + name: "{{ resource_prefix }}_regex_condition" + type: regex + state: absent + region: "{{ aws_region }}" + waf_regional: true + ignore_errors: yes diff --git a/ansible_collections/community/aws/tests/integration/targets/wafv2/aliases b/ansible_collections/community/aws/tests/integration/targets/wafv2/aliases new file mode 100644 index 000000000..7ee94625a --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/wafv2/aliases @@ -0,0 +1,8 @@ +cloud/aws +# reason: Tests broken - https://github.com/ansible-collections/community.aws/issues/985 +disabled + +wafv2_resources +wafv2_resources_info +wafv2_web_acl +wafv2_web_acl_info diff --git a/ansible_collections/community/aws/tests/integration/targets/wafv2/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/wafv2/defaults/main.yml new file mode 100644 index 000000000..ac23638ca --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/wafv2/defaults/main.yml @@ -0,0 +1,11 @@ +--- +web_acl_name: '{{ tiny_prefix }}-web-acl' +rule_group_name: '{{ tiny_prefix }}-rule-group' +alb_name: "my-alb-{{ tiny_prefix }}" +tg_name: "my-tg-{{ tiny_prefix }}" +cidr: + main: 10.228.228.0/22 + a: 10.228.228.0/24 + b: 10.228.229.0/24 + c: 10.228.230.0/24 + d: 10.228.231.0/24 diff --git a/ansible_collections/community/aws/tests/integration/targets/wafv2/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/wafv2/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/wafv2/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/wafv2/tasks/alb.yml b/ansible_collections/community/aws/tests/integration/targets/wafv2/tasks/alb.yml new file mode 100644 index 000000000..32aeb376a --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/wafv2/tasks/alb.yml @@ -0,0 +1,105 @@ +############################################ +# create ALB and necessary dependencies +# to test wafv2_resources +############################################ +- name: create VPC + ec2_vpc_net: + cidr_block: "{{ cidr.main }}" + name: '{{ resource_prefix }}_vpc' + state: present + register: vpc + +- name: create internet gateway + ec2_vpc_igw: + vpc_id: '{{ vpc.vpc.id }}' + state: present + tags: + Name: '{{ resource_prefix }}' + register: igw + +- name: create public subnet + ec2_vpc_subnet: + cidr: '{{ item.cidr }}' + az: '{{ aws_region}}{{ item.az }}' + vpc_id: '{{ vpc.vpc.id }}' + state: present + tags: + Public: '{{ item.public|string }}' + Name: '{{ item.public|ternary(''public'', ''private'') }}-{{ item.az }}' + with_items: + - cidr: "{{ cidr.a }}" + az: a + public: 'True' + - cidr: "{{ cidr.b }}" + az: b + public: 'True' + - cidr: "{{ cidr.c }}" + az: a + public: 'False' + - cidr: "{{ cidr.d }}" + az: b + public: 'False' + register: subnets + +- ec2_vpc_subnet_info: + filters: + vpc-id: '{{ vpc.vpc.id }}' + register: vpc_subnets + +- name: create list of subnet ids + set_fact: + alb_subnets: "{{ (vpc_subnets.subnets| selectattr('tags.Public', 'equalto', 'True')| map(attribute='id')| list) }}" + private_subnets: "{{ (vpc_subnets.subnets| selectattr('tags.Public', 'equalto', 'False')| map(attribute='id')| list) }}" + +- name: create a route table + ec2_vpc_route_table: + vpc_id: '{{ vpc.vpc.id }}' + tags: + Name: igw-route + Created: '{{ resource_prefix }}' + subnets: '{{ alb_subnets + private_subnets }}' + routes: + - dest: 0.0.0.0/0 + gateway_id: '{{ igw.gateway_id }}' + register: route_table + +- ec2_group: + name: '{{ resource_prefix }}' + description: security group for Ansible ALB integration tests + state: present + vpc_id: '{{ vpc.vpc.id }}' + rules: + - proto: tcp + from_port: 1 + to_port: 65535 + cidr_ip: 0.0.0.0/0 + register: sec_group + +- name: create a target group for testing + elb_target_group: + name: '{{ tg_name }}' + protocol: http + port: 80 + vpc_id: '{{ vpc.vpc.id }}' + state: present + register: tg + +- name: create ALB with a listener + elb_application_lb: + name: "{{ alb_name }}" + subnets: "{{ alb_subnets }}" + security_groups: "{{ sec_group.group_id }}" + state: present + wait: yes + listeners: + - Protocol: HTTP + Port: 80 + DefaultActions: + - Type: forward + TargetGroupName: "{{ tg_name }}" + register: alb + +- assert: + that: + - alb.listeners|length == 1 + - alb.listeners[0].rules|length == 1 diff --git a/ansible_collections/community/aws/tests/integration/targets/wafv2/tasks/create_webacl.yml b/ansible_collections/community/aws/tests/integration/targets/wafv2/tasks/create_webacl.yml new file mode 100644 index 000000000..cf7dad3ec --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/wafv2/tasks/create_webacl.yml @@ -0,0 +1,272 @@ +####################### +## Create web acl +####################### + +- block: + + - name: check_mode create web acl + wafv2_web_acl: + name: "{{ web_acl_name }}" + state: present + description: hallo eins + scope: REGIONAL + default_action: Allow + sampled_requests: no + cloudwatch_metrics: yes + metric_name: blub + rules: + - name: zwei + priority: 2 + action: + block: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: ddos + statement: + xss_match_statement: + field_to_match: + body: {} + text_transformations: + - type: NONE + priority: 0 + - name: admin_protect + priority: 1 + override_action: + none: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: fsd + statement: + managed_rule_group_statement: + vendor_name: AWS + name: AWSManagedRulesAdminProtectionRuleSet + custom_response_bodies: + too_many_requests: + content_type: APPLICATION_JSON + content: '{ message: "Your request has been blocked due to too many HTTP requests coming from your IP" }' + tags: + A: B + C: D + register: out + check_mode: yes + + - name: check_mode verify create + assert: + that: + - out is changed + + - name: Create web acl with custom response bodies + wafv2_web_acl: + name: "{{ resource_prefix }}-acl-with-response-body" + state: present + description: foo + scope: REGIONAL + default_action: Allow + sampled_requests: no + cloudwatch_metrics: no + rules: + - name: rate-limit-per-IP + priority: 1 + action: + block: + custom_response: + response_code: 429 + custom_response_body_key: too_many_requests + statement: + rate_based_statement: + limit: 1000 + aggregate_key_type: IP + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: no + metric_name: unused + custom_response_bodies: + too_many_requests: + content_type: APPLICATION_JSON + content: '{ message: "Your request has been blocked due to too many HTTP requests coming from your IP" }' + register: acl_with_response_body + + - name: Web acl with custom response bodies verify create + assert: + that: + - acl_with_response_body is changed + - acl_with_response_body.web_acl.rules | count == 1 + - acl_with_response_body.web_acl.custom_response_bodies.too_many_requests is defined + + - name: Update web acl with custom response bodies to remove custom response + wafv2_web_acl: + name: "{{ resource_prefix }}-acl-with-response-body" + state: present + scope: REGIONAL + description: foo + default_action: Allow + sampled_requests: no + cloudwatch_metrics: no + rules: + - name: rate-limit-per-IP + priority: 1 + action: + block: {} + statement: + rate_based_statement: + limit: 1000 + aggregate_key_type: IP + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: no + metric_name: unused + custom_response_bodies: {} + + # unfortunately the wafv2_web_acl does not return the ACL structure after an update + # hence we have to do another task here using the info module to retrieve the latest state + # of the ACL and then to check it + - name: check if custom response body was really removed + wafv2_web_acl_info: + name: "{{ resource_prefix }}-acl-with-response-body" + scope: REGIONAL + register: acl_without_response_bodies + + - name: Web acl with custom response bodies verify removal of custom response + assert: + that: + - acl_without_response_bodies.custom_response_bodies is undefined + + - name: create web acl + wafv2_web_acl: + name: "{{ web_acl_name }}" + state: present + description: hallo eins + scope: REGIONAL + default_action: Allow + sampled_requests: no + cloudwatch_metrics: yes + metric_name: blub + rules: + - name: zwei + priority: 2 + action: + block: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: ddos + statement: + xss_match_statement: + field_to_match: + body: {} + text_transformations: + - type: NONE + priority: 0 + - name: admin_protect + priority: 1 + override_action: + none: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: fsd + statement: + managed_rule_group_statement: + vendor_name: AWS + name: AWSManagedRulesAdminProtectionRuleSet + - name: rate-limit-per-IP + priority: 3 + action: + block: + custom_response: + response_code: 429 + custom_response_body_key: too_many_requests + statement: + rate_based_statement: + limit: 5000 + aggregate_key_type: IP + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: waf-acl-rule-rate-limit-per-IP + custom_response_bodies: + too_many_requests: + content_type: APPLICATION_JSON + content: '{ message: "Your request has been blocked due to too many HTTP requests coming from your IP" }' + tags: + A: B + C: D + register: ACL + + - name: verify create + assert: + that: + - ACL is changed + - ACL.web_acl.name == web_acl_name + - not ACL.web_acl.visibility_config.sampled_requests_enabled + - ACL.web_acl.rules | count == 3 + - ACL.web_acl.description == 'hallo eins' + + - name: immutable create web acl + wafv2_web_acl: + name: "{{ web_acl_name }}" + state: present + description: hallo eins + scope: REGIONAL + default_action: Allow + sampled_requests: no + cloudwatch_metrics: yes + metric_name: blub + rules: + - name: zwei + priority: 2 + action: + block: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: ddos + statement: + xss_match_statement: + field_to_match: + body: {} + text_transformations: + - type: NONE + priority: 0 + - name: admin_protect + priority: 1 + override_action: + none: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: fsd + statement: + managed_rule_group_statement: + vendor_name: AWS + name: AWSManagedRulesAdminProtectionRuleSet + - name: rate-limit-per-IP + priority: 3 + action: + block: + custom_response: + response_code: 429 + custom_response_body_key: too_many_requests + statement: + rate_based_statement: + limit: 5000 + aggregate_key_type: IP + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: waf-acl-rule-rate-limit-per-IP + custom_response_bodies: + too_many_requests: + content_type: APPLICATION_JSON + content: '{ message: "Your request has been blocked due to too many HTTP requests coming from your IP" }' + tags: + A: B + C: D + register: out + + - name: verify create + assert: + that: + - out is not changed diff --git a/ansible_collections/community/aws/tests/integration/targets/wafv2/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/wafv2/tasks/main.yml new file mode 100644 index 000000000..547c4c151 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/wafv2/tasks/main.yml @@ -0,0 +1,217 @@ +--- +- module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + + block: + - include_tasks: alb.yml + - include_tasks: create_webacl.yml + - include_tasks: rule_group.yml + - include_tasks: test_webacl.yml + - include_tasks: waf_resources.yml + + ############################## + # test delete wafv2 resources + ############################## + - name: remove rule group + wafv2_rule_group: + name: "{{ rule_group_name }}" + state: absent + scope: REGIONAL + register: out + + - name: verify change + assert: + that: + - out is changed + + - name: immutable remove rule group + wafv2_rule_group: + name: "{{ rule_group_name }}" + state: absent + scope: REGIONAL + register: out + + - name: verify no change + assert: + that: + - out is not changed + + - name: delete web acl + wafv2_web_acl: + name: "{{ web_acl_name }}" + state: absent + scope: REGIONAL + register: out + + - name: verify change + assert: + that: + - out is changed + + + - name: immutable delete web acl + wafv2_web_acl: + name: "{{ web_acl_name }}" + state: absent + scope: REGIONAL + register: out + + - name: verify not change + assert: + that: + - out is not changed + + always: + ################################### + # always delete wafv2 components + ################################### + - name: remove test alb from waf web acs + wafv2_resources: + name: "{{ web_acl_name }}" + scope: REGIONAL + state: absent + arn: "{{ alb.load_balancer_arn }}" + ignore_errors: true + + - name: always delete web acl + wafv2_web_acl: + name: "{{ web_acl_name }}" + state: absent + scope: REGIONAL + ignore_errors: true + + - name: take care rule group is removed + wafv2_rule_group: + name: "{{ rule_group_name }}" + state: absent + scope: REGIONAL + ignore_errors: true + + - name: Ensure ACL with response body is removed + wafv2_web_acl: + name: "{{ resource_prefix }}-acl-with-response-body" + state: absent + scope: REGIONAL + ignore_errors: true + + ######################### + # remove alb and its deps + ######################### + - name: destroy ALB + elb_application_lb: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token }}' + region: '{{ aws_region }}' + name: '{{ alb_name }}' + state: absent + wait: true + wait_timeout: 600 + ignore_errors: true + + - name: destroy target group if it was created + elb_target_group: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token }}' + region: '{{ aws_region }}' + name: '{{ tg_name }}' + protocol: http + port: 80 + vpc_id: '{{ vpc.vpc.id }}' + state: absent + wait: true + wait_timeout: 600 + register: remove_tg + retries: 5 + delay: 3 + until: remove_tg is success + when: tg is defined + ignore_errors: true + + - name: destroy sec group + ec2_group: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token }}' + region: '{{ aws_region }}' + name: '{{ sec_group.group_name }}' + description: security group for Ansible ALB integration tests + state: absent + vpc_id: '{{ vpc.vpc.id }}' + register: remove_sg + retries: 10 + delay: 5 + until: remove_sg is success + ignore_errors: true + + - name: remove route table + ec2_vpc_route_table: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token }}' + region: '{{ aws_region }}' + vpc_id: '{{ vpc.vpc.id }}' + route_table_id: '{{ route_table.route_table.route_table_id }}' + lookup: id + state: absent + register: remove_rt + retries: 10 + delay: 5 + until: remove_rt is success + ignore_errors: true + + - name: destroy subnets + ec2_vpc_subnet: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token }}' + region: '{{ aws_region }}' + cidr: '{{ item.cidr }}' + vpc_id: '{{ vpc.vpc.id }}' + state: absent + register: remove_subnet + retries: 10 + delay: 5 + until: remove_subnet is success + with_items: + - cidr: 10.228.228.0/24 + - cidr: 10.228.229.0/24 + - cidr: 10.228.230.0/24 + - cidr: 10.228.231.0/24 + ignore_errors: true + + - name: destroy internet gateway + ec2_vpc_igw: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token }}' + region: '{{ aws_region }}' + vpc_id: '{{ vpc.vpc.id }}' + tags: + Name: '{{ resource_prefix }}' + state: absent + register: remove_igw + retries: 10 + delay: 5 + until: remove_igw is success + ignore_errors: true + + - name: destroy VPC + ec2_vpc_net: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token }}' + region: '{{ aws_region }}' + cidr_block: 10.228.228.0/22 + name: '{{ resource_prefix }}_vpc' + state: absent + register: remove_vpc + retries: 10 + delay: 5 + until: remove_vpc is success + ignore_errors: true diff --git a/ansible_collections/community/aws/tests/integration/targets/wafv2/tasks/rule_group.yml b/ansible_collections/community/aws/tests/integration/targets/wafv2/tasks/rule_group.yml new file mode 100644 index 000000000..6ec46f5dd --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/wafv2/tasks/rule_group.yml @@ -0,0 +1,681 @@ +#################################### +# Create and test rule group +#################################### +- name: check_mode create rule group + wafv2_rule_group: + name: "{{ rule_group_name }}" + state: present + description: hallo eins + scope: REGIONAL + capacity: 500 + rules: + - name: eins + priority: 1 + action: + allow: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: fsd + statement: + byte_match_statement: + search_string: ansible.com + positional_constraint: CONTAINS + field_to_match: + single_header: + name: host + text_transformations: + - type: LOWERCASE + priority: 0 + cloudwatch_metrics: yes + tags: + A: B + C: D + register: out + check_mode: yes + +- name: check_mode verify create + assert: + that: + - out is changed + +- name: create rule group + wafv2_rule_group: + name: "{{ rule_group_name }}" + state: present + description: hallo eins + scope: REGIONAL + capacity: 500 + rules: + - name: eins + priority: 1 + action: + allow: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: fsd + statement: + byte_match_statement: + search_string: ansible.com + positional_constraint: CONTAINS + field_to_match: + single_header: + name: host + text_transformations: + - type: LOWERCASE + priority: 0 + cloudwatch_metrics: yes + tags: + A: B + C: D + register: out + +- name: verify create + assert: + that: + - out is changed + +- name: rule group info + wafv2_rule_group_info: + name: "{{ rule_group_name }}" + state: present + scope: REGIONAL + register: out + +- name: verify one rule + assert: + that: + - out.rules | count == 1 + +- name: immutable create rule group + wafv2_rule_group: + name: "{{ rule_group_name }}" + state: present + description: hallo eins + scope: REGIONAL + capacity: 500 + rules: + - name: eins + priority: 1 + action: + allow: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: fsd + statement: + byte_match_statement: + search_string: ansible.com + positional_constraint: CONTAINS + field_to_match: + single_header: + name: host + text_transformations: + - type: LOWERCASE + priority: 0 + cloudwatch_metrics: yes + tags: + A: B + C: D + register: out + +- name: verify immutable create + assert: + that: + - out is not changed + +- name: change description + wafv2_rule_group: + name: "{{ rule_group_name }}" + state: present + description: hallo eins zwei + scope: REGIONAL + capacity: 500 + rules: + - name: eins + priority: 1 + action: + allow: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: fsd + statement: + byte_match_statement: + search_string: ansible.com + positional_constraint: CONTAINS + field_to_match: + single_header: + name: host + text_transformations: + - type: LOWERCASE + priority: 0 + cloudwatch_metrics: yes + tags: + A: B + C: D + register: out + +- name: verify change + assert: + that: + - out is changed + +- name: iummutable change description + wafv2_rule_group: + name: "{{ rule_group_name }}" + state: present + description: hallo eins zwei + scope: REGIONAL + capacity: 500 + rules: + - name: eins + priority: 1 + action: + allow: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: fsd + statement: + byte_match_statement: + search_string: ansible.com + positional_constraint: CONTAINS + field_to_match: + single_header: + name: host + text_transformations: + - type: LOWERCASE + priority: 0 + cloudwatch_metrics: yes + tags: + A: B + C: D + register: out + +- name: verify no change + assert: + that: + - out is not changed + +- name: add rule + wafv2_rule_group: + name: "{{ rule_group_name }}" + state: present + description: hallo eins zwei + scope: REGIONAL + capacity: 500 + rules: + - name: eins + priority: 1 + action: + allow: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: fsd + statement: + byte_match_statement: + search_string: ansible.com + positional_constraint: CONTAINS + field_to_match: + single_header: + name: host + text_transformations: + - type: LOWERCASE + priority: 0 + - name: zwei + priority: 2 + action: + block: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: ddos + statement: + or_statement: + statements: + - byte_match_statement: + search_string: ansible.com + positional_constraint: CONTAINS + field_to_match: + single_header: + name: host + text_transformations: + - type: LOWERCASE + priority: 0 + - xss_match_statement: + field_to_match: + body: {} + text_transformations: + - type: NONE + priority: 0 + cloudwatch_metrics: yes + tags: + A: B + C: D + register: out + +- name: verify change + assert: + that: + - out is changed + +- name: immutable add rule + wafv2_rule_group: + name: "{{ rule_group_name }}" + state: present + description: hallo eins zwei + scope: REGIONAL + capacity: 500 + rules: + - name: eins + priority: 1 + action: + allow: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: fsd + statement: + byte_match_statement: + search_string: ansible.com + positional_constraint: CONTAINS + field_to_match: + single_header: + name: host + text_transformations: + - type: LOWERCASE + priority: 0 + - name: zwei + priority: 2 + action: + block: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: ddos + statement: + or_statement: + statements: + - byte_match_statement: + search_string: ansible.com + positional_constraint: CONTAINS + field_to_match: + single_header: + name: host + text_transformations: + - type: LOWERCASE + priority: 0 + - xss_match_statement: + field_to_match: + body: {} + text_transformations: + - type: NONE + priority: 0 + cloudwatch_metrics: yes + tags: + A: B + C: D + register: out + +- name: verify no change + assert: + that: + - out is not changed + +- name: change rule + wafv2_rule_group: + name: "{{ rule_group_name }}" + state: present + description: hallo eins zwei + scope: REGIONAL + capacity: 500 + rules: + - name: eins + priority: 1 + action: + allow: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: fsd + statement: + byte_match_statement: + search_string: ansible.com + positional_constraint: CONTAINS + field_to_match: + single_header: + name: host + text_transformations: + - type: LOWERCASE + priority: 0 + - name: zwei + priority: 3 + action: + block: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: ddos + statement: + or_statement: + statements: + - byte_match_statement: + search_string: ansible.com + positional_constraint: CONTAINS + field_to_match: + single_header: + name: host + text_transformations: + - type: LOWERCASE + priority: 0 + - xss_match_statement: + field_to_match: + body: {} + text_transformations: + - type: NONE + priority: 0 + cloudwatch_metrics: yes + tags: + A: B + C: D + register: out + +- name: verify change + assert: + that: + - out is changed + +- name: immutable change rule + wafv2_rule_group: + name: "{{ rule_group_name }}" + state: present + description: hallo eins zwei + scope: REGIONAL + capacity: 500 + rules: + - name: eins + priority: 1 + action: + allow: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: fsd + statement: + byte_match_statement: + search_string: ansible.com + positional_constraint: CONTAINS + field_to_match: + single_header: + name: host + text_transformations: + - type: LOWERCASE + priority: 0 + - name: zwei + priority: 3 + action: + block: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: ddos + statement: + or_statement: + statements: + - byte_match_statement: + search_string: ansible.com + positional_constraint: CONTAINS + field_to_match: + single_header: + name: host + text_transformations: + - type: LOWERCASE + priority: 0 + - xss_match_statement: + field_to_match: + body: {} + text_transformations: + - type: NONE + priority: 0 + cloudwatch_metrics: yes + tags: + A: B + C: D + register: out + +- name: verify no change + assert: + that: + - out is not changed + +- name: change rule again + wafv2_rule_group: + name: "{{ rule_group_name }}" + state: present + description: hallo eins zwei + scope: REGIONAL + capacity: 500 + rules: + - name: zwei + priority: 1 + action: + block: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: ddos + statement: + or_statement: + statements: + - byte_match_statement: + search_string: ansible.com + positional_constraint: CONTAINS + field_to_match: + single_header: + name: host + text_transformations: + - type: LOWERCASE + priority: 0 + - xss_match_statement: + field_to_match: + body: {} + text_transformations: + - type: NONE + priority: 0 + - name: eins + priority: 2 + action: + allow: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: fsd + statement: + byte_match_statement: + search_string: ansible.com + positional_constraint: CONTAINS + field_to_match: + single_header: + name: host + text_transformations: + - type: LOWERCASE + priority: 0 + cloudwatch_metrics: yes + tags: + A: B + C: D + register: out + +- name: verify change + assert: + that: + - out is changed + +- name: add one rule + wafv2_rule_group: + name: "{{ rule_group_name }}" + state: present + description: hallo eins zwei + scope: REGIONAL + capacity: 500 + purge_rules: no + rules: + - name: allow-admin-svg + priority: 3 + action: + allow: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: allow-admin-svg + statement: + byte_match_statement: + search_string: admin.svg + positional_constraint: CONTAINS + field_to_match: + uri_path: {} + text_transformations: + - type: LOWERCASE + priority: 0 + cloudwatch_metrics: yes + tags: + A: B + C: D + register: out + +- name: verify change + assert: + that: + - out is changed + +- name: rule group info + wafv2_rule_group_info: + name: "{{ rule_group_name }}" + state: present + scope: REGIONAL + register: out + +- name: verify create + assert: + that: + - out.rules | count == 3 + +- name: immutable add one rule + wafv2_rule_group: + name: "{{ rule_group_name }}" + state: present + description: hallo eins zwei + scope: REGIONAL + capacity: 500 + purge_rules: no + rules: + - name: allow-admin-svg + priority: 3 + action: + allow: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: allow-admin-svg + statement: + byte_match_statement: + search_string: admin.svg + positional_constraint: CONTAINS + field_to_match: + uri_path: {} + text_transformations: + - type: LOWERCASE + priority: 0 + cloudwatch_metrics: yes + tags: + A: B + C: D + register: out + +- name: verify no change + assert: + that: + - out is not changed + +- name: purge rules + wafv2_rule_group: + name: "{{ rule_group_name }}" + state: present + description: hallo eins zwei + scope: REGIONAL + capacity: 500 + purge_rules: yes + rules: + - name: allow-admin-svg + priority: 3 + action: + allow: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: allow-admin-svg + statement: + byte_match_statement: + search_string: admin.svg + positional_constraint: CONTAINS + field_to_match: + uri_path: {} + text_transformations: + - type: LOWERCASE + priority: 0 + cloudwatch_metrics: yes + tags: + A: B + C: D + register: out + +- name: verify change + assert: + that: + - out is changed + +- name: absent one rule rules + wafv2_rule_group: + name: "{{ rule_group_name }}" + state: absent + description: hallo eins zwei + scope: REGIONAL + rules: + - name: allow-admin-svg + priority: 3 + action: + allow: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: allow-admin-svg + statement: + byte_match_statement: + search_string: admin.svg + positional_constraint: CONTAINS + field_to_match: + uri_path: {} + text_transformations: + - type: LOWERCASE + priority: 0 + register: out + +- name: verify change + assert: + that: + - out is changed + +- name: rule group info + wafv2_rule_group_info: + name: "{{ rule_group_name }}" + state: present + scope: REGIONAL + register: out + +- name: verify change + assert: + that: + - out.rules | count == 0 diff --git a/ansible_collections/community/aws/tests/integration/targets/wafv2/tasks/test_webacl.yml b/ansible_collections/community/aws/tests/integration/targets/wafv2/tasks/test_webacl.yml new file mode 100644 index 000000000..997e2fc7d --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/wafv2/tasks/test_webacl.yml @@ -0,0 +1,230 @@ +############################### +# test web acl +############################### +- name: get web acl + wafv2_web_acl_info: + name: "{{ web_acl_name }}" + scope: REGIONAL + register: out + +- name: verify rules + assert: + that: + - out.rules | count == 3 + +- name: change web acl description + wafv2_web_acl: + name: "{{ web_acl_name }}" + state: present + description: hallo eins drei + scope: REGIONAL + default_action: Allow + sampled_requests: no + cloudwatch_metrics: yes + metric_name: blub + rules: + - name: zwei + priority: 2 + action: + block: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: ddos + statement: + xss_match_statement: + field_to_match: + body: {} + text_transformations: + - type: NONE + priority: 0 + - name: admin_protect + priority: 1 + override_action: + none: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: fsd + statement: + managed_rule_group_statement: + vendor_name: AWS + name: AWSManagedRulesAdminProtectionRuleSet + tags: + A: B + C: D + register: out + +- name: verify change + assert: + that: + - out is changed + + +- name: add 1 rules + wafv2_web_acl: + name: "{{ web_acl_name }}" + state: present + description: hallo eins drei + scope: REGIONAL + default_action: Allow + sampled_requests: no + cloudwatch_metrics: yes + metric_name: blub + purge_rules: no + rules: + - name: bla + priority: 8 + override_action: + none: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: fsd + statement: + managed_rule_group_statement: + vendor_name: AWS + name: AWSManagedRulesAdminProtectionRuleSet + tags: + A: B + C: D + register: out + +- name: verify change + assert: + that: + - out is changed + +- name: get web acl + wafv2_web_acl_info: + name: "{{ web_acl_name }}" + scope: REGIONAL + register: out + +- name: verify rules + assert: + that: + - out.rules | count == 3 + +- name: reduce rules to 1 + wafv2_web_acl: + name: "{{ web_acl_name }}" + state: present + description: hallo eins drei + scope: REGIONAL + default_action: Allow + sampled_requests: no + cloudwatch_metrics: yes + metric_name: blub + purge_rules: yes + rules: + - name: admin_protect + priority: 1 + override_action: + none: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: admin_protect + statement: + managed_rule_group_statement: + vendor_name: AWS + name: AWSManagedRulesAdminProtectionRuleSet + tags: + A: B + C: D + register: out + +- name: verify change + assert: + that: + - out is changed + +- name: get web acl + wafv2_web_acl_info: + name: "{{ web_acl_name }}" + scope: REGIONAL + register: out + +- name: verify rules + assert: + that: + - out.rules | count == 1 + +- name: immutable change web acl + wafv2_web_acl: + name: "{{ web_acl_name }}" + state: present + description: hallo eins drei + scope: REGIONAL + default_action: Allow + sampled_requests: no + cloudwatch_metrics: yes + metric_name: blub + rules: + - name: admin_protect + priority: 1 + override_action: + none: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: admin_protect + statement: + managed_rule_group_statement: + vendor_name: AWS + name: AWSManagedRulesAdminProtectionRuleSet + tags: + A: B + C: D + register: out + +- name: verify no change + assert: + that: + - out is not changed + +- name: test geo match statement + wafv2_web_acl: + name: "{{ web_acl_name }}" + state: present + description: hallo eins drei + scope: REGIONAL + default_action: Allow + sampled_requests: no + cloudwatch_metrics: yes + metric_name: blub + purge_rules: yes + rules: + - name: block-germany + priority: 1 + action: + block: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: block-germany + statement: + geo_match_statement: + country_codes: + - DE + tags: + A: B + C: D + register: out + +- name: verify change + assert: + that: + - out is changed + +- name: re-read webacl + wafv2_web_acl_info: + name: "{{ web_acl_name }}" + scope: REGIONAL + register: out + +- name: verify geo match statement + assert: + that: + - out.rules[0].statement.geo_match_statement.country_codes[0] == 'DE' diff --git a/ansible_collections/community/aws/tests/integration/targets/wafv2/tasks/waf_resources.yml b/ansible_collections/community/aws/tests/integration/targets/wafv2/tasks/waf_resources.yml new file mode 100644 index 000000000..75cb2d29a --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/wafv2/tasks/waf_resources.yml @@ -0,0 +1,59 @@ +################################ +# test wafv2 resouces +# bind ALB to WAFv2 ACL +################################ +- name: check_mode add test alb to test waf + wafv2_resources: + name: "{{ web_acl_name }}" + scope: REGIONAL + state: present + arn: "{{ alb.load_balancer_arn }}" + check_mode: yes + register: out + +- name: verify check_mode create + assert: + that: + - out is changed + +- name: add test alb to test waf + wafv2_resources: + name: "{{ web_acl_name }}" + scope: REGIONAL + state: present + arn: "{{ alb.load_balancer_arn }}" + register: out + retries: 10 + delay: 15 + until: out.failed == false + +- name: verify create + assert: + that: + - out is changed + +- name: get web acl + wafv2_resources_info: + name: "{{ web_acl_name }}" + scope: REGIONAL + register: out + +- name: immutable add test alb from test waf + wafv2_resources: + name: "{{ web_acl_name }}" + scope: REGIONAL + state: present + arn: "{{ alb.load_balancer_arn }}" + register: out + +- name: verify immutable create + assert: + that: + - out is not changed + +- name: remove test alb from test waf + wafv2_resources: + name: "{{ web_acl_name }}" + scope: REGIONAL + state: absent + arn: "{{ alb.load_balancer_arn }}" diff --git a/ansible_collections/community/aws/tests/integration/targets/wafv2_ip_set/aliases b/ansible_collections/community/aws/tests/integration/targets/wafv2_ip_set/aliases new file mode 100644 index 000000000..8bdd6b5ee --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/wafv2_ip_set/aliases @@ -0,0 +1,4 @@ +cloud/aws + +wafv2_ip_set +wafv2_ip_set_info diff --git a/ansible_collections/community/aws/tests/integration/targets/wafv2_ip_set/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/wafv2_ip_set/defaults/main.yml new file mode 100644 index 000000000..11f2faed9 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/wafv2_ip_set/defaults/main.yml @@ -0,0 +1,2 @@ +--- +ip_set_name: '{{ resource_prefix }}-ipset' diff --git a/ansible_collections/community/aws/tests/integration/targets/wafv2_ip_set/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/wafv2_ip_set/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/wafv2_ip_set/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/wafv2_ip_set/tasks/description.yml b/ansible_collections/community/aws/tests/integration/targets/wafv2_ip_set/tasks/description.yml new file mode 100644 index 000000000..b44b4828d --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/wafv2_ip_set/tasks/description.yml @@ -0,0 +1,131 @@ +- name: Tests relating to setting descriptions on wavf2_ip_sets + vars: + description_one: 'a Description - {{ resource_prefix }}' + description_two: 'Another_Description - {{ resource_prefix }}' + # Mandatory settings + module_defaults: + community.aws.wafv2_ip_set: + name: '{{ ip_set_name }}' + state: present + scope: REGIONAL + ip_address_version: IPV4 + purge_addresses: no + addresses: [] + community.aws.wafv2_ip_set_info: + name: '{{ ip_set_name }}' + scope: REGIONAL + block: + + - name: test setting description wafv2_ip_set (check mode) + wafv2_ip_set: + description: '{{ description_one }}' + register: update_result + check_mode: yes + - name: assert that update succeeded + assert: + that: + - update_result is changed + + - name: test setting description wafv2_ip_set + wafv2_ip_set: + description: '{{ description_one }}' + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is changed + - update_result.description == description_one + + - name: test setting description wafv2_ip_set - idempotency (check mode) + wafv2_ip_set: + description: '{{ description_one }}' + register: update_result + check_mode: yes + - name: assert that update succeeded + assert: + that: + - update_result is not changed + + - name: test setting description wafv2_ip_set - idempotency + wafv2_ip_set: + description: '{{ description_one }}' + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is not changed + - update_result.description == description_one + + ### + + - name: test updating description on wafv2_ip_set (check mode) + wafv2_ip_set: + description: '{{ description_two }}' + register: update_result + check_mode: yes + - name: assert that update succeeded + assert: + that: + - update_result is changed + + - name: test updating description on wafv2_ip_set + wafv2_ip_set: + description: '{{ description_two }}' + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is changed + - update_result.description == description_two + + - name: test updating description on wafv2_ip_set - idempotency (check mode) + wafv2_ip_set: + description: '{{ description_two }}' + register: update_result + check_mode: yes + - name: assert that update succeeded + assert: + that: + - update_result is not changed + + - name: test updating description on wafv2_ip_set - idempotency + wafv2_ip_set: + description: '{{ description_two }}' + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is not changed + - update_result.description == description_two + + ### + + - name: test that wafv2_ip_set_info returns the description + wafv2_ip_set_info: + register: tag_info + - name: assert description present + assert: + that: + - tag_info.description == description_two + + ### + + - name: test no description param wafv2_ip_set (check mode) + wafv2_ip_set: {} + register: update_result + check_mode: yes + - name: assert no change + assert: + that: + - update_result is not changed + - update_result.description == description_two + + + - name: test no description param wafv2_ip_set + wafv2_ip_set: {} + register: update_result + - name: assert no change + assert: + that: + - update_result is not changed + - update_result.description == description_two diff --git a/ansible_collections/community/aws/tests/integration/targets/wafv2_ip_set/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/wafv2_ip_set/tasks/main.yml new file mode 100644 index 000000000..f7afc5b93 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/wafv2_ip_set/tasks/main.yml @@ -0,0 +1,215 @@ +--- +- module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + block: + - name: check_mode create ip set + wafv2_ip_set: + name: "{{ ip_set_name }}" + state: present + description: hallo eins + scope: REGIONAL + ip_address_version: IPV4 + addresses: + - 8.8.8.8/32 + - 8.8.4.4/32 + tags: + A: B + C: D + register: out + check_mode: yes + + - name: verify create + assert: + that: + - out is changed + + - name: create ip set + wafv2_ip_set: + name: "{{ ip_set_name }}" + state: present + description: hallo eins + scope: REGIONAL + ip_address_version: IPV4 + addresses: + - 8.8.8.8/32 + - 8.8.4.4/32 + tags: + A: B + C: D + register: out + + - name: verify create + assert: + that: + - out is changed + - "'8.8.8.8/32' in out.addresses" + - out.ip_address_version == 'IPV4' + - out.addresses | count == 2 + - out.description == 'hallo eins' + + - name: change ip set + wafv2_ip_set: + name: "{{ ip_set_name }}" + state: present + description: hallo eins + scope: REGIONAL + ip_address_version: IPV4 + addresses: + - 8.8.8.8/32 + - 8.8.4.4/32 + - 10.0.0.0/8 + tags: + A: B + C: D + register: out + + - name: verify create + assert: + that: + - out is changed + - "'10.0.0.0/8' in out.addresses" + + - name: test ip set immutable + wafv2_ip_set: + name: "{{ ip_set_name }}" + state: present + description: hallo eins + scope: REGIONAL + ip_address_version: IPV4 + addresses: + - 8.8.8.8/32 + - 8.8.4.4/32 + - 10.0.0.0/8 + tags: + A: B + C: D + register: out + + - name: verify immutable create + assert: + that: + - out is not changed + - out.addresses | count == 3 + + - name: add one ip + wafv2_ip_set: + name: "{{ ip_set_name }}" + state: present + description: hallo eins + scope: REGIONAL + ip_address_version: IPV4 + purge_addresses: no + addresses: + - 127.0.0.1/32 + register: out + + - name: verify change + assert: + that: + - out is changed + - out.addresses | count == 4 + - "'127.0.0.1/32' in out.addresses" + + + - name: remove one ip + wafv2_ip_set: + name: "{{ ip_set_name }}" + state: absent + description: hallo eins + scope: REGIONAL + ip_address_version: IPV4 + purge_addresses: yes + addresses: + - 127.0.0.1/32 + register: out + + - name: verify change + assert: + that: + - out is changed + - out.addresses | count == 3 + - "'127.0.0.1/32' not in out.addresses" + - "'8.8.8.8/32' in out.addresses" + + - name: get ip set info + wafv2_ip_set_info: + name: "{{ ip_set_name }}" + scope: REGIONAL + register: out + + - name: verify rules + assert: + that: + - out.addresses | count == 3 + + + - name: purge all but one + wafv2_ip_set: + name: "{{ ip_set_name }}" + state: present + description: hallo eins + scope: REGIONAL + ip_address_version: IPV4 + purge_addresses: yes + addresses: + - 127.0.0.1/32 + register: out + + - name: verify change + assert: + that: + - out is changed + - out.addresses | count == 1 + + - name: get ip set info + wafv2_ip_set_info: + name: "{{ ip_set_name }}" + scope: REGIONAL + register: out + + - name: verify rules + assert: + that: + - out.addresses | count == 1 + + - include_tasks: 'tagging.yml' + - include_tasks: 'description.yml' + + - name: delete ip set + wafv2_ip_set: + name: "{{ ip_set_name }}" + state: absent + scope: REGIONAL + ip_address_version: IPV4 + register: out + + - name: verify delete + assert: + that: + - out is changed + + - name: delete ip set immutable + wafv2_ip_set: + name: "{{ ip_set_name }}" + state: absent + scope: REGIONAL + ip_address_version: IPV4 + register: out + + - name: verify immutable delete + assert: + that: + - out is not changed + + + always: + - name: always delete ip set + wafv2_ip_set: + name: "{{ ip_set_name }}" + state: absent + scope: REGIONAL + ip_address_version: IPV4 diff --git a/ansible_collections/community/aws/tests/integration/targets/wafv2_ip_set/tasks/tagging.yml b/ansible_collections/community/aws/tests/integration/targets/wafv2_ip_set/tasks/tagging.yml new file mode 100644 index 000000000..d6125b32e --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/wafv2_ip_set/tasks/tagging.yml @@ -0,0 +1,256 @@ +- name: Tests relating to tagging wavf2_ip_sets + 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 + # Mandatory settings + module_defaults: + community.aws.wafv2_ip_set: + name: '{{ ip_set_name }}' + state: present + scope: REGIONAL + ip_address_version: IPV4 + purge_addresses: no + addresses: [] + community.aws.wafv2_ip_set_info: + name: '{{ ip_set_name }}' + scope: REGIONAL + block: + + ### + + - name: test adding tags to wafv2_ip_set (check mode) + wafv2_ip_set: + tags: '{{ first_tags }}' + purge_tags: True + register: update_result + check_mode: yes + - name: assert that update succeeded + assert: + that: + - update_result is changed + + - name: test adding tags to wafv2_ip_set + wafv2_ip_set: + tags: '{{ first_tags }}' + purge_tags: True + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is changed + - update_result.tags == first_tags + + - name: test adding tags to wafv2_ip_set - idempotency (check mode) + wafv2_ip_set: + tags: '{{ first_tags }}' + purge_tags: True + register: update_result + check_mode: yes + - name: assert that update succeeded + assert: + that: + - update_result is not changed + + - name: test adding tags to wafv2_ip_set - idempotency + wafv2_ip_set: + tags: '{{ first_tags }}' + purge_tags: True + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is not changed + - update_result.tags == first_tags + + ### + + - name: test updating tags with purge on wafv2_ip_set (check mode) + wafv2_ip_set: + tags: '{{ second_tags }}' + purge_tags: True + register: update_result + check_mode: yes + - name: assert that update succeeded + assert: + that: + - update_result is changed + + - name: test updating tags with purge on wafv2_ip_set + wafv2_ip_set: + tags: '{{ second_tags }}' + purge_tags: True + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is changed + - update_result.tags == second_tags + + - name: test updating tags with purge on wafv2_ip_set - idempotency (check mode) + wafv2_ip_set: + tags: '{{ second_tags }}' + purge_tags: True + register: update_result + check_mode: yes + - name: assert that update succeeded + assert: + that: + - update_result is not changed + + - name: test updating tags with purge on wafv2_ip_set - idempotency + wafv2_ip_set: + tags: '{{ second_tags }}' + purge_tags: True + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is not changed + - update_result.tags == second_tags + + ### + + - name: test updating tags without purge on wafv2_ip_set (check mode) + wafv2_ip_set: + tags: '{{ third_tags }}' + purge_tags: False + register: update_result + check_mode: yes + - name: assert that update succeeded + assert: + that: + - update_result is changed + + - name: test updating tags without purge on wafv2_ip_set + wafv2_ip_set: + tags: '{{ third_tags }}' + purge_tags: False + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is changed + - update_result.tags == final_tags + + - name: test updating tags without purge on wafv2_ip_set - idempotency (check mode) + wafv2_ip_set: + tags: '{{ third_tags }}' + purge_tags: False + register: update_result + check_mode: yes + - name: assert that update succeeded + assert: + that: + - update_result is not changed + + - name: test updating tags without purge on wafv2_ip_set - idempotency + wafv2_ip_set: + tags: '{{ third_tags }}' + purge_tags: False + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is not changed + - update_result.tags == final_tags + + ### + + - name: test that wafv2_ip_set_info returns the tags + wafv2_ip_set_info: + register: tag_info + - name: assert tags present + assert: + that: + - tag_info.tags == final_tags + + ### + + - name: test no tags param wafv2_ip_set (check mode) + wafv2_ip_set: {} + register: update_result + check_mode: yes + - name: assert no change + assert: + that: + - update_result is not changed + - update_result.tags == final_tags + + + - name: test no tags param wafv2_ip_set + wafv2_ip_set: {} + register: update_result + - name: assert no change + assert: + that: + - update_result is not changed + - update_result.tags == final_tags + + ### + + - name: test removing tags from wafv2_ip_set (check mode) + wafv2_ip_set: + tags: {} + purge_tags: True + register: update_result + check_mode: yes + - name: assert that update succeeded + assert: + that: + - update_result is changed + + - name: test removing tags from wafv2_ip_set + wafv2_ip_set: + tags: {} + purge_tags: True + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is changed + - update_result.tags == {} + + - name: test removing tags from wafv2_ip_set - idempotency (check mode) + wafv2_ip_set: + tags: {} + purge_tags: True + register: update_result + check_mode: yes + - name: assert that update succeeded + assert: + that: + - update_result is not changed + + - name: test removing tags from wafv2_ip_set - idempotency + wafv2_ip_set: + tags: {} + purge_tags: True + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is not changed + - update_result.tags == {} diff --git a/ansible_collections/community/aws/tests/integration/targets/wafv2_rule_group/aliases b/ansible_collections/community/aws/tests/integration/targets/wafv2_rule_group/aliases new file mode 100644 index 000000000..2c61ab765 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/wafv2_rule_group/aliases @@ -0,0 +1,3 @@ +cloud/aws + +wafv2_rule_group_info diff --git a/ansible_collections/community/aws/tests/integration/targets/wafv2_rule_group/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/wafv2_rule_group/defaults/main.yml new file mode 100644 index 000000000..ac23638ca --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/wafv2_rule_group/defaults/main.yml @@ -0,0 +1,11 @@ +--- +web_acl_name: '{{ tiny_prefix }}-web-acl' +rule_group_name: '{{ tiny_prefix }}-rule-group' +alb_name: "my-alb-{{ tiny_prefix }}" +tg_name: "my-tg-{{ tiny_prefix }}" +cidr: + main: 10.228.228.0/22 + a: 10.228.228.0/24 + b: 10.228.229.0/24 + c: 10.228.230.0/24 + d: 10.228.231.0/24 diff --git a/ansible_collections/community/aws/tests/integration/targets/wafv2_rule_group/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/wafv2_rule_group/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/wafv2_rule_group/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/wafv2_rule_group/tasks/description.yml b/ansible_collections/community/aws/tests/integration/targets/wafv2_rule_group/tasks/description.yml new file mode 100644 index 000000000..e20eed58e --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/wafv2_rule_group/tasks/description.yml @@ -0,0 +1,131 @@ +- name: Tests relating to setting the descriptions of wavf2_ip_sets + vars: + description_one: 'a Description - {{ resource_prefix }}' + description_two: 'Another_Description - {{ resource_prefix }}' + # Mandatory settings + module_defaults: + community.aws.wafv2_rule_group: + name: '{{ rule_group_name }}' + state: present + scope: REGIONAL + purge_rules: no + rules: [] + capacity: 500 + community.aws.wafv2_rule_group_info: + name: '{{ rule_group_name }}' + scope: REGIONAL + block: + + - name: test setting description wafv2_rule_group (check mode) + wafv2_rule_group: + description: '{{ description_one }}' + register: update_result + check_mode: yes + - name: assert that update succeeded + assert: + that: + - update_result is changed + + - name: test setting description wafv2_rule_group + wafv2_rule_group: + description: '{{ description_one }}' + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is changed + - update_result.description == description_one + + - name: test setting description wafv2_rule_group - idempotency (check mode) + wafv2_rule_group: + description: '{{ description_one }}' + register: update_result + check_mode: yes + - name: assert that update succeeded + assert: + that: + - update_result is not changed + + - name: test setting description wafv2_rule_group - idempotency + wafv2_rule_group: + description: '{{ description_one }}' + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is not changed + - update_result.description == description_one + + ### + + - name: test updating description on wafv2_rule_group (check mode) + wafv2_rule_group: + description: '{{ description_two }}' + register: update_result + check_mode: yes + - name: assert that update succeeded + assert: + that: + - update_result is changed + + - name: test updating description on wafv2_rule_group + wafv2_rule_group: + description: '{{ description_two }}' + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is changed + - update_result.description == description_two + + - name: test updating description on wafv2_rule_group - idempotency (check mode) + wafv2_rule_group: + description: '{{ description_two }}' + register: update_result + check_mode: yes + - name: assert that update succeeded + assert: + that: + - update_result is not changed + + - name: test updating description on wafv2_rule_group - idempotency + wafv2_rule_group: + description: '{{ description_two }}' + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is not changed + - update_result.description == description_two + + ### + + - name: test that wafv2_rule_group_info returns the description + wafv2_rule_group_info: + register: tag_info + - name: assert description present + assert: + that: + - tag_info.description == description_two + + ### + + - name: test no description param wafv2_rule_group (check mode) + wafv2_rule_group: {} + register: update_result + check_mode: yes + - name: assert no change + assert: + that: + - update_result is not changed + - update_result.description == description_two + + + - name: test no description param wafv2_rule_group + wafv2_rule_group: {} + register: update_result + - name: assert no change + assert: + that: + - update_result is not changed + - update_result.description == description_two diff --git a/ansible_collections/community/aws/tests/integration/targets/wafv2_rule_group/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/wafv2_rule_group/tasks/main.yml new file mode 100644 index 000000000..630d5de29 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/wafv2_rule_group/tasks/main.yml @@ -0,0 +1,731 @@ +--- +- module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + block: + #################################### + # Create and test rule group + #################################### + - name: check_mode create rule group + wafv2_rule_group: + name: "{{ rule_group_name }}" + state: present + description: hallo eins + scope: REGIONAL + capacity: 500 + rules: + - name: eins + priority: 1 + action: + allow: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: fsd + statement: + byte_match_statement: + search_string: ansible.com + positional_constraint: CONTAINS + field_to_match: + single_header: + name: host + text_transformations: + - type: LOWERCASE + priority: 0 + cloudwatch_metrics: yes + tags: + A: B + C: D + register: out + check_mode: yes + + - name: check_mode verify create + assert: + that: + - out is changed + + - name: create rule group + wafv2_rule_group: + name: "{{ rule_group_name }}" + state: present + description: hallo eins + scope: REGIONAL + capacity: 500 + rules: + - name: eins + priority: 1 + action: + allow: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: fsd + statement: + byte_match_statement: + search_string: ansible.com + positional_constraint: CONTAINS + field_to_match: + single_header: + name: host + text_transformations: + - type: LOWERCASE + priority: 0 + cloudwatch_metrics: yes + tags: + A: B + C: D + register: out + + - name: verify create + assert: + that: + - out is changed + + - name: rule group info + wafv2_rule_group_info: + name: "{{ rule_group_name }}" + state: present + scope: REGIONAL + register: out + + - name: verify one rule + assert: + that: + - out.rules | count == 1 + + - name: immutable create rule group + wafv2_rule_group: + name: "{{ rule_group_name }}" + state: present + description: hallo eins + scope: REGIONAL + capacity: 500 + rules: + - name: eins + priority: 1 + action: + allow: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: fsd + statement: + byte_match_statement: + search_string: ansible.com + positional_constraint: CONTAINS + field_to_match: + single_header: + name: host + text_transformations: + - type: LOWERCASE + priority: 0 + cloudwatch_metrics: yes + tags: + A: B + C: D + register: out + + - name: verify immutable create + assert: + that: + - out is not changed + + - name: change description + wafv2_rule_group: + name: "{{ rule_group_name }}" + state: present + description: hallo eins zwei + scope: REGIONAL + capacity: 500 + rules: + - name: eins + priority: 1 + action: + allow: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: fsd + statement: + byte_match_statement: + search_string: ansible.com + positional_constraint: CONTAINS + field_to_match: + single_header: + name: host + text_transformations: + - type: LOWERCASE + priority: 0 + cloudwatch_metrics: yes + tags: + A: B + C: D + register: out + + - name: verify change + assert: + that: + - out is changed + + - name: iummutable change description + wafv2_rule_group: + name: "{{ rule_group_name }}" + state: present + description: hallo eins zwei + scope: REGIONAL + capacity: 500 + rules: + - name: eins + priority: 1 + action: + allow: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: fsd + statement: + byte_match_statement: + search_string: ansible.com + positional_constraint: CONTAINS + field_to_match: + single_header: + name: host + text_transformations: + - type: LOWERCASE + priority: 0 + cloudwatch_metrics: yes + tags: + A: B + C: D + register: out + + - name: verify no change + assert: + that: + - out is not changed + + - name: add rule + wafv2_rule_group: + name: "{{ rule_group_name }}" + state: present + description: hallo eins zwei + scope: REGIONAL + capacity: 500 + rules: + - name: eins + priority: 1 + action: + allow: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: fsd + statement: + byte_match_statement: + search_string: ansible.com + positional_constraint: CONTAINS + field_to_match: + single_header: + name: host + text_transformations: + - type: LOWERCASE + priority: 0 + - name: zwei + priority: 2 + action: + block: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: ddos + statement: + or_statement: + statements: + - byte_match_statement: + search_string: ansible.com + positional_constraint: CONTAINS + field_to_match: + single_header: + name: host + text_transformations: + - type: LOWERCASE + priority: 0 + - xss_match_statement: + field_to_match: + body: {} + text_transformations: + - type: NONE + priority: 0 + cloudwatch_metrics: yes + tags: + A: B + C: D + register: out + + - name: verify change + assert: + that: + - out is changed + + - name: immutable add rule + wafv2_rule_group: + name: "{{ rule_group_name }}" + state: present + description: hallo eins zwei + scope: REGIONAL + capacity: 500 + rules: + - name: eins + priority: 1 + action: + allow: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: fsd + statement: + byte_match_statement: + search_string: ansible.com + positional_constraint: CONTAINS + field_to_match: + single_header: + name: host + text_transformations: + - type: LOWERCASE + priority: 0 + - name: zwei + priority: 2 + action: + block: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: ddos + statement: + or_statement: + statements: + - byte_match_statement: + search_string: ansible.com + positional_constraint: CONTAINS + field_to_match: + single_header: + name: host + text_transformations: + - type: LOWERCASE + priority: 0 + - xss_match_statement: + field_to_match: + body: {} + text_transformations: + - type: NONE + priority: 0 + cloudwatch_metrics: yes + tags: + A: B + C: D + register: out + + - name: verify no change + assert: + that: + - out is not changed + + - name: change rule + wafv2_rule_group: + name: "{{ rule_group_name }}" + state: present + description: hallo eins zwei + scope: REGIONAL + capacity: 500 + rules: + - name: eins + priority: 1 + action: + allow: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: fsd + statement: + byte_match_statement: + search_string: ansible.com + positional_constraint: CONTAINS + field_to_match: + single_header: + name: host + text_transformations: + - type: LOWERCASE + priority: 0 + - name: zwei + priority: 3 + action: + block: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: ddos + statement: + or_statement: + statements: + - byte_match_statement: + search_string: ansible.com + positional_constraint: CONTAINS + field_to_match: + single_header: + name: host + text_transformations: + - type: LOWERCASE + priority: 0 + - xss_match_statement: + field_to_match: + body: {} + text_transformations: + - type: NONE + priority: 0 + cloudwatch_metrics: yes + tags: + A: B + C: D + register: out + + - name: verify change + assert: + that: + - out is changed + + - name: immutable change rule + wafv2_rule_group: + name: "{{ rule_group_name }}" + state: present + description: hallo eins zwei + scope: REGIONAL + capacity: 500 + rules: + - name: eins + priority: 1 + action: + allow: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: fsd + statement: + byte_match_statement: + search_string: ansible.com + positional_constraint: CONTAINS + field_to_match: + single_header: + name: host + text_transformations: + - type: LOWERCASE + priority: 0 + - name: zwei + priority: 3 + action: + block: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: ddos + statement: + or_statement: + statements: + - byte_match_statement: + search_string: ansible.com + positional_constraint: CONTAINS + field_to_match: + single_header: + name: host + text_transformations: + - type: LOWERCASE + priority: 0 + - xss_match_statement: + field_to_match: + body: {} + text_transformations: + - type: NONE + priority: 0 + cloudwatch_metrics: yes + tags: + A: B + C: D + register: out + + - name: verify no change + assert: + that: + - out is not changed + + - name: change rule again + wafv2_rule_group: + name: "{{ rule_group_name }}" + state: present + description: hallo eins zwei + scope: REGIONAL + capacity: 500 + rules: + - name: zwei + priority: 1 + action: + block: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: ddos + statement: + or_statement: + statements: + - byte_match_statement: + search_string: ansible.com + positional_constraint: CONTAINS + field_to_match: + single_header: + name: host + text_transformations: + - type: LOWERCASE + priority: 0 + - xss_match_statement: + field_to_match: + body: {} + text_transformations: + - type: NONE + priority: 0 + - name: eins + priority: 2 + action: + allow: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: fsd + statement: + byte_match_statement: + search_string: ansible.com + positional_constraint: CONTAINS + field_to_match: + single_header: + name: host + text_transformations: + - type: LOWERCASE + priority: 0 + cloudwatch_metrics: yes + tags: + A: B + C: D + register: out + + - name: verify change + assert: + that: + - out is changed + + - name: add one rule + wafv2_rule_group: + name: "{{ rule_group_name }}" + state: present + description: hallo eins zwei + scope: REGIONAL + capacity: 500 + purge_rules: no + rules: + - name: allow-admin-svg + priority: 3 + action: + allow: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: allow-admin-svg + statement: + byte_match_statement: + search_string: admin.svg + positional_constraint: CONTAINS + field_to_match: + uri_path: {} + text_transformations: + - type: LOWERCASE + priority: 0 + cloudwatch_metrics: yes + tags: + A: B + C: D + register: out + + - name: verify change + assert: + that: + - out is changed + + - name: rule group info + wafv2_rule_group_info: + name: "{{ rule_group_name }}" + state: present + scope: REGIONAL + register: out + + - name: verify create + assert: + that: + - out.rules | count == 3 + + - name: immutable add one rule + wafv2_rule_group: + name: "{{ rule_group_name }}" + state: present + description: hallo eins zwei + scope: REGIONAL + capacity: 500 + purge_rules: no + rules: + - name: allow-admin-svg + priority: 3 + action: + allow: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: allow-admin-svg + statement: + byte_match_statement: + search_string: admin.svg + positional_constraint: CONTAINS + field_to_match: + uri_path: {} + text_transformations: + - type: LOWERCASE + priority: 0 + cloudwatch_metrics: yes + tags: + A: B + C: D + register: out + + - name: verify no change + assert: + that: + - out is not changed + + - name: purge rules + wafv2_rule_group: + name: "{{ rule_group_name }}" + state: present + description: hallo eins zwei + scope: REGIONAL + capacity: 500 + purge_rules: yes + rules: + - name: allow-admin-svg + priority: 3 + action: + allow: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: allow-admin-svg + statement: + byte_match_statement: + search_string: admin.svg + positional_constraint: CONTAINS + field_to_match: + uri_path: {} + text_transformations: + - type: LOWERCASE + priority: 0 + cloudwatch_metrics: yes + tags: + A: B + C: D + register: out + + - name: verify change + assert: + that: + - out is changed + + - name: absent one rule rules + wafv2_rule_group: + name: "{{ rule_group_name }}" + state: absent + description: hallo eins zwei + scope: REGIONAL + rules: + - name: allow-admin-svg + priority: 3 + action: + allow: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: allow-admin-svg + statement: + byte_match_statement: + search_string: admin.svg + positional_constraint: CONTAINS + field_to_match: + uri_path: {} + text_transformations: + - type: LOWERCASE + priority: 0 + register: out + + - name: verify change + assert: + that: + - out is changed + + - name: rule group info + wafv2_rule_group_info: + name: "{{ rule_group_name }}" + state: present + scope: REGIONAL + register: out + + - name: verify change + assert: + that: + - out.rules | count == 0 + + - include_tasks: 'tagging.yml' + - include_tasks: 'description.yml' + + ############################## + # test delete wafv2 resources + ############################## + - name: remove rule group + wafv2_rule_group: + name: "{{ rule_group_name }}" + state: absent + scope: REGIONAL + register: out + + - name: verify change + assert: + that: + - out is changed + + - name: immutable remove rule group + wafv2_rule_group: + name: "{{ rule_group_name }}" + state: absent + scope: REGIONAL + register: out + + - name: verify no change + assert: + that: + - out is not changed + + always: + ################################### + # always delete wafv2 components + ################################### + + - name: take care rule group is removed + wafv2_rule_group: + name: "{{ rule_group_name }}" + state: absent + scope: REGIONAL + ignore_errors: true diff --git a/ansible_collections/community/aws/tests/integration/targets/wafv2_rule_group/tasks/tagging.yml b/ansible_collections/community/aws/tests/integration/targets/wafv2_rule_group/tasks/tagging.yml new file mode 100644 index 000000000..31a6674ca --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/wafv2_rule_group/tasks/tagging.yml @@ -0,0 +1,256 @@ +- name: Tests relating to tagging wavf2_ip_sets + 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 + # Mandatory settings + module_defaults: + community.aws.wafv2_rule_group: + name: '{{ rule_group_name }}' + state: present + scope: REGIONAL + purge_rules: no + rules: [] + capacity: 500 + community.aws.wafv2_rule_group_info: + name: '{{ rule_group_name }}' + scope: REGIONAL + block: + + ### + + - name: test adding tags to wafv2_rule_group (check mode) + wafv2_rule_group: + tags: '{{ first_tags }}' + purge_tags: True + register: update_result + check_mode: yes + - name: assert that update succeeded + assert: + that: + - update_result is changed + + - name: test adding tags to wafv2_rule_group + wafv2_rule_group: + tags: '{{ first_tags }}' + purge_tags: True + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is changed + - update_result.tags == first_tags + + - name: test adding tags to wafv2_rule_group - idempotency (check mode) + wafv2_rule_group: + tags: '{{ first_tags }}' + purge_tags: True + register: update_result + check_mode: yes + - name: assert that update succeeded + assert: + that: + - update_result is not changed + + - name: test adding tags to wafv2_rule_group - idempotency + wafv2_rule_group: + tags: '{{ first_tags }}' + purge_tags: True + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is not changed + - update_result.tags == first_tags + + ### + + - name: test updating tags with purge on wafv2_rule_group (check mode) + wafv2_rule_group: + tags: '{{ second_tags }}' + purge_tags: True + register: update_result + check_mode: yes + - name: assert that update succeeded + assert: + that: + - update_result is changed + + - name: test updating tags with purge on wafv2_rule_group + wafv2_rule_group: + tags: '{{ second_tags }}' + purge_tags: True + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is changed + - update_result.tags == second_tags + + - name: test updating tags with purge on wafv2_rule_group - idempotency (check mode) + wafv2_rule_group: + tags: '{{ second_tags }}' + purge_tags: True + register: update_result + check_mode: yes + - name: assert that update succeeded + assert: + that: + - update_result is not changed + + - name: test updating tags with purge on wafv2_rule_group - idempotency + wafv2_rule_group: + tags: '{{ second_tags }}' + purge_tags: True + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is not changed + - update_result.tags == second_tags + + ### + + - name: test updating tags without purge on wafv2_rule_group (check mode) + wafv2_rule_group: + tags: '{{ third_tags }}' + purge_tags: False + register: update_result + check_mode: yes + - name: assert that update succeeded + assert: + that: + - update_result is changed + + - name: test updating tags without purge on wafv2_rule_group + wafv2_rule_group: + tags: '{{ third_tags }}' + purge_tags: False + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is changed + - update_result.tags == final_tags + + - name: test updating tags without purge on wafv2_rule_group - idempotency (check mode) + wafv2_rule_group: + tags: '{{ third_tags }}' + purge_tags: False + register: update_result + check_mode: yes + - name: assert that update succeeded + assert: + that: + - update_result is not changed + + - name: test updating tags without purge on wafv2_rule_group - idempotency + wafv2_rule_group: + tags: '{{ third_tags }}' + purge_tags: False + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is not changed + - update_result.tags == final_tags + + ### + + - name: test that wafv2_rule_group_info returns the tags + wafv2_rule_group_info: + register: tag_info + - name: assert tags present + assert: + that: + - tag_info.tags == final_tags + + ### + + - name: test no tags param wafv2_rule_group (check mode) + wafv2_rule_group: {} + register: update_result + check_mode: yes + - name: assert no change + assert: + that: + - update_result is not changed + - update_result.tags == final_tags + + + - name: test no tags param wafv2_rule_group + wafv2_rule_group: {} + register: update_result + - name: assert no change + assert: + that: + - update_result is not changed + - update_result.tags == final_tags + + ### + + - name: test removing tags from wafv2_rule_group (check mode) + wafv2_rule_group: + tags: {} + purge_tags: True + register: update_result + check_mode: yes + - name: assert that update succeeded + assert: + that: + - update_result is changed + + - name: test removing tags from wafv2_rule_group + wafv2_rule_group: + tags: {} + purge_tags: True + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is changed + - update_result.tags == {} + + - name: test removing tags from wafv2_rule_group - idempotency (check mode) + wafv2_rule_group: + tags: {} + purge_tags: True + register: update_result + check_mode: yes + - name: assert that update succeeded + assert: + that: + - update_result is not changed + + - name: test removing tags from wafv2_rule_group - idempotency + wafv2_rule_group: + tags: {} + purge_tags: True + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is not changed + - update_result.tags == {} diff --git a/ansible_collections/community/aws/tests/integration/targets/wafv2_web_acl/aliases b/ansible_collections/community/aws/tests/integration/targets/wafv2_web_acl/aliases new file mode 100644 index 000000000..8a25f857b --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/wafv2_web_acl/aliases @@ -0,0 +1,3 @@ +cloud/aws + +wafv2_web_acl_info diff --git a/ansible_collections/community/aws/tests/integration/targets/wafv2_web_acl/defaults/main.yml b/ansible_collections/community/aws/tests/integration/targets/wafv2_web_acl/defaults/main.yml new file mode 100644 index 000000000..804bf5573 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/wafv2_web_acl/defaults/main.yml @@ -0,0 +1,2 @@ +--- +web_acl_name: '{{ tiny_prefix }}-web-acl' diff --git a/ansible_collections/community/aws/tests/integration/targets/wafv2_web_acl/meta/main.yml b/ansible_collections/community/aws/tests/integration/targets/wafv2_web_acl/meta/main.yml new file mode 100644 index 000000000..32cf5dda7 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/wafv2_web_acl/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/ansible_collections/community/aws/tests/integration/targets/wafv2_web_acl/tasks/description.yml b/ansible_collections/community/aws/tests/integration/targets/wafv2_web_acl/tasks/description.yml new file mode 100644 index 000000000..4650d75e4 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/wafv2_web_acl/tasks/description.yml @@ -0,0 +1,131 @@ +- name: Tests relating to setting descriptions on wavf2_web_acl + vars: + description_one: 'a Description - {{ resource_prefix }}' + description_two: 'Another_Description - {{ resource_prefix }}' + # Mandatory settings + module_defaults: + community.aws.wafv2_web_acl: + name: '{{ web_acl_name }}' + state: present + scope: REGIONAL + purge_rules: no + rules: [] + default_action: Allow + community.aws.wafv2_web_acl_info: + name: '{{ web_acl_name }}' + scope: REGIONAL + block: + + - name: test setting description wafv2_web_acl (check mode) + wafv2_web_acl: + description: '{{ description_one }}' + register: update_result + check_mode: yes + - name: assert that update succeeded + assert: + that: + - update_result is changed + + - name: test setting description wafv2_web_acl + wafv2_web_acl: + description: '{{ description_one }}' + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is changed + - update_result.description == description_one + + - name: test setting description wafv2_web_acl - idempotency (check mode) + wafv2_web_acl: + description: '{{ description_one }}' + register: update_result + check_mode: yes + - name: assert that update succeeded + assert: + that: + - update_result is not changed + + - name: test setting description wafv2_web_acl - idempotency + wafv2_web_acl: + description: '{{ description_one }}' + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is not changed + - update_result.description == description_one + + ### + + - name: test updating description on wafv2_web_acl (check mode) + wafv2_web_acl: + description: '{{ description_two }}' + register: update_result + check_mode: yes + - name: assert that update succeeded + assert: + that: + - update_result is changed + + - name: test updating description on wafv2_web_acl + wafv2_web_acl: + description: '{{ description_two }}' + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is changed + - update_result.description == description_two + + - name: test updating description on wafv2_web_acl - idempotency (check mode) + wafv2_web_acl: + description: '{{ description_two }}' + register: update_result + check_mode: yes + - name: assert that update succeeded + assert: + that: + - update_result is not changed + + - name: test updating description on wafv2_web_acl - idempotency + wafv2_web_acl: + description: '{{ description_two }}' + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is not changed + - update_result.description == description_two + + ### + + - name: test that wafv2_web_acl_info returns the description + wafv2_web_acl_info: + register: tag_info + - name: assert description present + assert: + that: + - tag_info.description == description_two + + ### + + - name: test no description param wafv2_web_acl (check mode) + wafv2_web_acl: {} + register: update_result + check_mode: yes + - name: assert no change + assert: + that: + - update_result is not changed + - update_result.description == description_two + + + - name: test no description param wafv2_web_acl + wafv2_web_acl: {} + register: update_result + - name: assert no change + assert: + that: + - update_result is not changed + - update_result.description == description_two diff --git a/ansible_collections/community/aws/tests/integration/targets/wafv2_web_acl/tasks/main.yml b/ansible_collections/community/aws/tests/integration/targets/wafv2_web_acl/tasks/main.yml new file mode 100644 index 000000000..9d44e2b77 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/wafv2_web_acl/tasks/main.yml @@ -0,0 +1,566 @@ +--- +- module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + block: + + ####################### + ## Create web acl + ####################### + + - name: check_mode create web acl + wafv2_web_acl: + name: "{{ web_acl_name }}" + state: present + description: hallo eins + scope: REGIONAL + default_action: Allow + sampled_requests: no + cloudwatch_metrics: yes + metric_name: blub + rules: + - name: zwei + priority: 2 + action: + block: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: ddos + statement: + xss_match_statement: + field_to_match: + body: {} + text_transformations: + - type: NONE + priority: 0 + - name: admin_protect + priority: 1 + override_action: + none: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: fsd + statement: + managed_rule_group_statement: + vendor_name: AWS + name: AWSManagedRulesAdminProtectionRuleSet + custom_response_bodies: + too_many_requests: + content_type: APPLICATION_JSON + content: '{ message: "Your request has been blocked due to too many HTTP requests coming from your IP" }' + tags: + A: B + C: D + register: out + check_mode: yes + + - name: check_mode verify create + assert: + that: + - out is changed + + - name: Create web acl with custom response bodies + wafv2_web_acl: + name: "{{ resource_prefix }}-acl-with-response-body" + state: present + description: foo + scope: REGIONAL + default_action: Allow + sampled_requests: no + cloudwatch_metrics: no + rules: + - name: rate-limit-per-IP + priority: 1 + action: + block: + custom_response: + response_code: 429 + custom_response_body_key: too_many_requests + statement: + rate_based_statement: + limit: 1000 + aggregate_key_type: IP + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: no + metric_name: unused + custom_response_bodies: + too_many_requests: + content_type: APPLICATION_JSON + content: '{ message: "Your request has been blocked due to too many HTTP requests coming from your IP" }' + register: acl_with_response_body + + - name: Web acl with custom response bodies verify create + assert: + that: + - acl_with_response_body is changed + - acl_with_response_body.web_acl.rules | count == 1 + - acl_with_response_body.web_acl.custom_response_bodies.too_many_requests is defined + + - name: Update web acl with custom response bodies to remove custom response + wafv2_web_acl: + name: "{{ resource_prefix }}-acl-with-response-body" + state: present + scope: REGIONAL + description: foo + default_action: Allow + sampled_requests: no + cloudwatch_metrics: no + rules: + - name: rate-limit-per-IP + priority: 1 + action: + block: {} + statement: + rate_based_statement: + limit: 1000 + aggregate_key_type: IP + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: no + metric_name: unused + custom_response_bodies: {} + + # unfortunately the wafv2_web_acl does not return the ACL structure after an update + # hence we have to do another task here using the info module to retrieve the latest state + # of the ACL and then to check it + - name: check if custom response body was really removed + wafv2_web_acl_info: + name: "{{ resource_prefix }}-acl-with-response-body" + scope: REGIONAL + register: acl_without_response_bodies + + - name: Web acl with custom response bodies verify removal of custom response + assert: + that: + - acl_without_response_bodies.custom_response_bodies is undefined + + - name: create web acl + wafv2_web_acl: + name: "{{ web_acl_name }}" + state: present + description: hallo eins + scope: REGIONAL + default_action: Allow + sampled_requests: no + cloudwatch_metrics: yes + metric_name: blub + rules: + - name: zwei + priority: 2 + action: + block: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: ddos + statement: + xss_match_statement: + field_to_match: + body: {} + text_transformations: + - type: NONE + priority: 0 + - name: admin_protect + priority: 1 + override_action: + none: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: fsd + statement: + managed_rule_group_statement: + vendor_name: AWS + name: AWSManagedRulesAdminProtectionRuleSet + - name: rate-limit-per-IP + priority: 3 + action: + block: + custom_response: + response_code: 429 + custom_response_body_key: too_many_requests + statement: + rate_based_statement: + limit: 5000 + aggregate_key_type: IP + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: waf-acl-rule-rate-limit-per-IP + custom_response_bodies: + too_many_requests: + content_type: APPLICATION_JSON + content: '{ message: "Your request has been blocked due to too many HTTP requests coming from your IP" }' + tags: + A: B + C: D + register: ACL + + - name: verify create + assert: + that: + - ACL is changed + - ACL.web_acl.name == web_acl_name + - not ACL.web_acl.visibility_config.sampled_requests_enabled + - ACL.web_acl.rules | count == 3 + - ACL.web_acl.description == 'hallo eins' + + - name: immutable create web acl + wafv2_web_acl: + name: "{{ web_acl_name }}" + state: present + description: hallo eins + scope: REGIONAL + default_action: Allow + sampled_requests: no + cloudwatch_metrics: yes + metric_name: blub + rules: + - name: zwei + priority: 2 + action: + block: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: ddos + statement: + xss_match_statement: + field_to_match: + body: {} + text_transformations: + - type: NONE + priority: 0 + - name: admin_protect + priority: 1 + override_action: + none: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: fsd + statement: + managed_rule_group_statement: + vendor_name: AWS + name: AWSManagedRulesAdminProtectionRuleSet + - name: rate-limit-per-IP + priority: 3 + action: + block: + custom_response: + response_code: 429 + custom_response_body_key: too_many_requests + statement: + rate_based_statement: + limit: 5000 + aggregate_key_type: IP + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: waf-acl-rule-rate-limit-per-IP + custom_response_bodies: + too_many_requests: + content_type: APPLICATION_JSON + content: '{ message: "Your request has been blocked due to too many HTTP requests coming from your IP" }' + tags: + A: B + C: D + register: out + + - name: verify create + assert: + that: + - out is not changed + + ############################### + # test web acl + ############################### + - name: get web acl + wafv2_web_acl_info: + name: "{{ web_acl_name }}" + scope: REGIONAL + register: out + + - name: verify rules + assert: + that: + - out.rules | count == 3 + + - name: change web acl description + wafv2_web_acl: + name: "{{ web_acl_name }}" + state: present + description: hallo eins drei + scope: REGIONAL + default_action: Allow + sampled_requests: no + cloudwatch_metrics: yes + metric_name: blub + rules: + - name: zwei + priority: 2 + action: + block: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: ddos + statement: + xss_match_statement: + field_to_match: + body: {} + text_transformations: + - type: NONE + priority: 0 + - name: admin_protect + priority: 1 + override_action: + none: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: fsd + statement: + managed_rule_group_statement: + vendor_name: AWS + name: AWSManagedRulesAdminProtectionRuleSet + tags: + A: B + C: D + register: out + + - name: verify change + assert: + that: + - out is changed + + + - name: add 1 rules + wafv2_web_acl: + name: "{{ web_acl_name }}" + state: present + description: hallo eins drei + scope: REGIONAL + default_action: Allow + sampled_requests: no + cloudwatch_metrics: yes + metric_name: blub + purge_rules: no + rules: + - name: bla + priority: 8 + override_action: + none: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: fsd + statement: + managed_rule_group_statement: + vendor_name: AWS + name: AWSManagedRulesAdminProtectionRuleSet + tags: + A: B + C: D + register: out + + - name: verify change + assert: + that: + - out is changed + + - name: get web acl + wafv2_web_acl_info: + name: "{{ web_acl_name }}" + scope: REGIONAL + register: out + + - name: verify rules + assert: + that: + - out.rules | count == 3 + + - name: reduce rules to 1 + wafv2_web_acl: + name: "{{ web_acl_name }}" + state: present + description: hallo eins drei + scope: REGIONAL + default_action: Allow + sampled_requests: no + cloudwatch_metrics: yes + metric_name: blub + purge_rules: yes + rules: + - name: admin_protect + priority: 1 + override_action: + none: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: admin_protect + statement: + managed_rule_group_statement: + vendor_name: AWS + name: AWSManagedRulesAdminProtectionRuleSet + tags: + A: B + C: D + register: out + + - name: verify change + assert: + that: + - out is changed + + - name: get web acl + wafv2_web_acl_info: + name: "{{ web_acl_name }}" + scope: REGIONAL + register: out + + - name: verify rules + assert: + that: + - out.rules | count == 1 + + - name: immutable change web acl + wafv2_web_acl: + name: "{{ web_acl_name }}" + state: present + description: hallo eins drei + scope: REGIONAL + default_action: Allow + sampled_requests: no + cloudwatch_metrics: yes + metric_name: blub + rules: + - name: admin_protect + priority: 1 + override_action: + none: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: admin_protect + statement: + managed_rule_group_statement: + vendor_name: AWS + name: AWSManagedRulesAdminProtectionRuleSet + tags: + A: B + C: D + register: out + + - name: verify no change + assert: + that: + - out is not changed + + - name: test geo match statement + wafv2_web_acl: + name: "{{ web_acl_name }}" + state: present + description: hallo eins drei + scope: REGIONAL + default_action: Allow + sampled_requests: no + cloudwatch_metrics: yes + metric_name: blub + purge_rules: yes + rules: + - name: block-germany + priority: 1 + action: + block: {} + visibility_config: + sampled_requests_enabled: yes + cloud_watch_metrics_enabled: yes + metric_name: block-germany + statement: + geo_match_statement: + country_codes: + - DE + tags: + A: B + C: D + register: out + + - name: verify change + assert: + that: + - out is changed + + - name: re-read webacl + wafv2_web_acl_info: + name: "{{ web_acl_name }}" + scope: REGIONAL + register: out + + - name: verify geo match statement + assert: + that: + - out.rules[0].statement.geo_match_statement.country_codes[0] == 'DE' + + - include_tasks: 'description.yml' + - include_tasks: 'tags.yml' + + - name: re-read webacl + wafv2_web_acl_info: + name: "{{ web_acl_name }}" + scope: REGIONAL + register: out + + - name: verify geo match statement + assert: + that: + - out.rules[0].statement.geo_match_statement.country_codes[0] == 'DE' + + - name: delete web acl + wafv2_web_acl: + name: "{{ web_acl_name }}" + state: absent + scope: REGIONAL + register: out + + - name: verify change + assert: + that: + - out is changed + + - name: immutable delete web acl + wafv2_web_acl: + name: "{{ web_acl_name }}" + state: absent + scope: REGIONAL + register: out + + - name: verify not change + assert: + that: + - out is not changed + + always: + ################################### + # always delete wafv2 components + ################################### + - name: always delete web acl + wafv2_web_acl: + name: "{{ web_acl_name }}" + state: absent + scope: REGIONAL + ignore_errors: true + + - name: Ensure ACL with response body is removed + wafv2_web_acl: + name: "{{ resource_prefix }}-acl-with-response-body" + state: absent + scope: REGIONAL + ignore_errors: true diff --git a/ansible_collections/community/aws/tests/integration/targets/wafv2_web_acl/tasks/tags.yml b/ansible_collections/community/aws/tests/integration/targets/wafv2_web_acl/tasks/tags.yml new file mode 100644 index 000000000..406fa5a15 --- /dev/null +++ b/ansible_collections/community/aws/tests/integration/targets/wafv2_web_acl/tasks/tags.yml @@ -0,0 +1,254 @@ +- name: Tests relating to setting tags on wavf2_web_acl + 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 + # Mandatory settings + module_defaults: + community.aws.wafv2_web_acl: + name: '{{ web_acl_name }}' + state: present + scope: REGIONAL + purge_rules: no + rules: [] + default_action: Allow + community.aws.wafv2_web_acl_info: + name: '{{ web_acl_name }}' + scope: REGIONAL + block: + + - name: test adding tags to wafv2_web_acl (check mode) + wafv2_web_acl: + tags: '{{ first_tags }}' + purge_tags: True + register: update_result + check_mode: yes + - name: assert that update succeeded + assert: + that: + - update_result is changed + + - name: test adding tags to wafv2_web_acl + wafv2_web_acl: + tags: '{{ first_tags }}' + purge_tags: True + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is changed + - update_result.tags == first_tags + + - name: test adding tags to wafv2_web_acl - idempotency (check mode) + wafv2_web_acl: + tags: '{{ first_tags }}' + purge_tags: True + register: update_result + check_mode: yes + - name: assert that update succeeded + assert: + that: + - update_result is not changed + + - name: test adding tags to wafv2_web_acl - idempotency + wafv2_web_acl: + tags: '{{ first_tags }}' + purge_tags: True + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is not changed + - update_result.tags == first_tags + + ### + + - name: test updating tags with purge on wafv2_web_acl (check mode) + wafv2_web_acl: + tags: '{{ second_tags }}' + purge_tags: True + register: update_result + check_mode: yes + - name: assert that update succeeded + assert: + that: + - update_result is changed + + - name: test updating tags with purge on wafv2_web_acl + wafv2_web_acl: + tags: '{{ second_tags }}' + purge_tags: True + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is changed + - update_result.tags == second_tags + + - name: test updating tags with purge on wafv2_web_acl - idempotency (check mode) + wafv2_web_acl: + tags: '{{ second_tags }}' + purge_tags: True + register: update_result + check_mode: yes + - name: assert that update succeeded + assert: + that: + - update_result is not changed + + - name: test updating tags with purge on wafv2_web_acl - idempotency + wafv2_web_acl: + tags: '{{ second_tags }}' + purge_tags: True + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is not changed + - update_result.tags == second_tags + + ### + + - name: test updating tags without purge on wafv2_web_acl (check mode) + wafv2_web_acl: + tags: '{{ third_tags }}' + purge_tags: False + register: update_result + check_mode: yes + - name: assert that update succeeded + assert: + that: + - update_result is changed + + - name: test updating tags without purge on wafv2_web_acl + wafv2_web_acl: + tags: '{{ third_tags }}' + purge_tags: False + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is changed + - update_result.tags == final_tags + + - name: test updating tags without purge on wafv2_web_acl - idempotency (check mode) + wafv2_web_acl: + tags: '{{ third_tags }}' + purge_tags: False + register: update_result + check_mode: yes + - name: assert that update succeeded + assert: + that: + - update_result is not changed + + - name: test updating tags without purge on wafv2_web_acl - idempotency + wafv2_web_acl: + tags: '{{ third_tags }}' + purge_tags: False + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is not changed + - update_result.tags == final_tags + + ### + + - name: test that wafv2_web_acl_info returns the tags + wafv2_web_acl_info: + register: tag_info + - name: assert tags present + assert: + that: + - tag_info.tags == final_tags + + ### + + - name: test no tags param wafv2_web_acl (check mode) + wafv2_web_acl: {} + register: update_result + check_mode: yes + - name: assert no change + assert: + that: + - update_result is not changed + - update_result.tags == final_tags + + + - name: test no tags param wafv2_web_acl + wafv2_web_acl: {} + register: update_result + - name: assert no change + assert: + that: + - update_result is not changed + - update_result.tags == final_tags + + ### + + - name: test removing tags from wafv2_web_acl (check mode) + wafv2_web_acl: + tags: {} + purge_tags: True + register: update_result + check_mode: yes + - name: assert that update succeeded + assert: + that: + - update_result is changed + + - name: test removing tags from wafv2_web_acl + wafv2_web_acl: + tags: {} + purge_tags: True + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is changed + - update_result.tags == {} + + - name: test removing tags from wafv2_web_acl - idempotency (check mode) + wafv2_web_acl: + tags: {} + purge_tags: True + register: update_result + check_mode: yes + - name: assert that update succeeded + assert: + that: + - update_result is not changed + + - name: test removing tags from wafv2_web_acl - idempotency + wafv2_web_acl: + tags: {} + purge_tags: True + register: update_result + - name: assert that update succeeded + assert: + that: + - update_result is not changed + - update_result.tags == {} |