diff --git a/changelogs/fragments/723-elasticache_subnet_group-boto3.yml b/changelogs/fragments/723-elasticache_subnet_group-boto3.yml new file mode 100644 index 00000000000..fc291737da9 --- /dev/null +++ b/changelogs/fragments/723-elasticache_subnet_group-boto3.yml @@ -0,0 +1,4 @@ +minor_changes: +- elasticache_subnet_group - module migrated to boto3 AWS SDK (https://github.com/ansible-collections/community.aws/pull/723). +- elasticache_subnet_group - add return values (https://github.com/ansible-collections/community.aws/pull/723). +- elasticache_subnet_group - add support for check_mode (https://github.com/ansible-collections/community.aws/pull/723). diff --git a/plugins/modules/elasticache_subnet_group.py b/plugins/modules/elasticache_subnet_group.py index 44a3e39ae6f..eda678205d0 100644 --- a/plugins/modules/elasticache_subnet_group.py +++ b/plugins/modules/elasticache_subnet_group.py @@ -12,34 +12,36 @@ version_added: 1.0.0 short_description: manage ElastiCache subnet groups description: - - Creates, modifies, and deletes ElastiCache subnet groups. This module has a dependency on python-boto >= 2.5. + - Creates, modifies, and deletes ElastiCache subnet groups. options: state: description: - Specifies whether the subnet should be present or absent. - required: true choices: [ 'present' , 'absent' ] + default: 'present' type: str name: description: - Database subnet group identifier. + - This value is automatically converted to lowercase. required: true type: str description: description: - - ElastiCache subnet group description. Only set when a new group is added. + - ElastiCache subnet group description. + - When not provided defaults to I(name) on subnet group creation. type: str subnets: description: - List of subnet IDs that make up the ElastiCache subnet group. + - At least one subnet must be provided when creating an ElastiCache subnet group. type: list elements: str -author: "Tim Mahoney (@timmahoney)" +author: + - "Tim Mahoney (@timmahoney)" extends_documentation_fragment: -- amazon.aws.aws -- amazon.aws.ec2 -requirements: -- boto >= 2.49.0 + - amazon.aws.aws + - amazon.aws.ec2 ''' EXAMPLES = r''' @@ -58,87 +60,195 @@ name: norwegian-blue ''' +RETURN = r''' +cache_subnet_group: + description: Description of the Elasticache Subnet Group. + returned: always + type: dict + contains: + arn: + description: The Amazon Resource Name (ARN) of the cache subnet group. + returned: when the subnet group exists + type: str + sample: arn:aws:elasticache:us-east-1:012345678901:subnetgroup:norwegian-blue + description: + description: The description of the cache subnet group. + returned: when the cache subnet group exists + type: str + sample: My Fancy Ex Parrot Subnet Group + name: + description: The name of the cache subnet group. + returned: when the cache subnet group exists + type: str + sample: norwegian-blue + vpc_id: + description: The VPC ID of the cache subnet group. + returned: when the cache subnet group exists + type: str + sample: norwegian-blue + subnet_ids: + description: The IDs of the subnets beloging to the cache subnet group. + returned: when the cache subnet group exists + type: list + elements: str + sample: + - subnet-aaaaaaaa + - subnet-bbbbbbbb +''' + try: - import boto - from boto.elasticache import connect_to_region - from boto.exception import BotoServerError + import botocore except ImportError: - pass # Handled by HAS_BOTO + pass # Handled by AnsibleAWSModule + +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict -from ansible.module_utils._text import to_native from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import HAS_BOTO -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import get_aws_connection_info +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry + + +def get_subnet_group(name): + try: + groups = client.describe_cache_subnet_groups( + aws_retry=True, + CacheSubnetGroupName=name, + )['CacheSubnetGroups'] + except is_boto3_error_code('CacheSubnetGroupNotFoundFault'): + return None + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except + module.fail_json_aws(e, msg="Failed to describe subnet group") + + if not groups: + return None + + if len(groups) > 1: + module.fail_aws( + msg="Found multiple matches for subnet group", + cache_subnet_groups=camel_dict_to_snake_dict(groups), + ) + + subnet_group = camel_dict_to_snake_dict(groups[0]) + + subnet_group['name'] = subnet_group['cache_subnet_group_name'] + subnet_group['description'] = subnet_group['cache_subnet_group_description'] + + subnet_ids = list(s['subnet_identifier'] for s in subnet_group['subnets']) + subnet_group['subnet_ids'] = subnet_ids + + return subnet_group + + +def create_subnet_group(name, description, subnets): + + if not subnets: + module.fail_json(msg='At least one subnet must be provided when creating a subnet group') + + if module.check_mode: + return True + + try: + if not description: + description = name + client.create_cache_subnet_group( + aws_retry=True, + CacheSubnetGroupName=name, + CacheSubnetGroupDescription=description, + SubnetIds=subnets, + ) + return True + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Failed to create subnet group") + + +def update_subnet_group(subnet_group, name, description, subnets): + update_params = dict() + if description and subnet_group['description'] != description: + update_params['CacheSubnetGroupDescription'] = description + if subnets: + old_subnets = set(subnet_group['subnet_ids']) + new_subnets = set(subnets) + if old_subnets != new_subnets: + update_params['SubnetIds'] = list(subnets) + + if not update_params: + return False + + if module.check_mode: + return True + + try: + client.modify_cache_subnet_group( + aws_retry=True, + CacheSubnetGroupName=name, + **update_params, + ) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Failed to update subnet group") + + return True + + +def delete_subnet_group(name): + + if module.check_mode: + return True + + try: + client.delete_cache_subnet_group( + aws_retry=True, + CacheSubnetGroupName=name, + ) + return True + except is_boto3_error_code('CacheSubnetGroupNotFoundFault'): + # AWS is "eventually consistent", cope with the race conditions where + # deletion hadn't completed when we ran describe + return False + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except + module.fail_json_aws(e, msg="Failed to delete subnet group") def main(): argument_spec = dict( - state=dict(required=True, choices=['present', 'absent']), + state=dict(default='present', choices=['present', 'absent']), name=dict(required=True), description=dict(required=False), subnets=dict(required=False, type='list', elements='str'), ) - module = AnsibleAWSModule(argument_spec=argument_spec, check_boto3=False) - if not HAS_BOTO: - module.fail_json(msg='boto required for this module') - - state = module.params.get('state') - group_name = module.params.get('name').lower() - group_description = module.params.get('description') - group_subnets = module.params.get('subnets') or {} + global module + global client - if state == 'present': - for required in ['name', 'description', 'subnets']: - if not module.params.get(required): - module.fail_json(msg=str("Parameter %s required for state='present'" % required)) - else: - for not_allowed in ['description', 'subnets']: - if module.params.get(not_allowed): - module.fail_json(msg=str("Parameter %s not allowed for state='absent'" % not_allowed)) + module = AnsibleAWSModule( + argument_spec=argument_spec, + supports_check_mode=True, + ) - # Retrieve any AWS settings from the environment. - region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module) + state = module.params.get('state') + name = module.params.get('name').lower() + description = module.params.get('description') + subnets = module.params.get('subnets') - if not region: - module.fail_json(msg=str("Either region or AWS_REGION or EC2_REGION environment variable or boto config aws_region or ec2_region must be set.")) + client = module.client('elasticache', retry_decorator=AWSRetry.jittered_backoff()) - """Get an elasticache connection""" - try: - conn = connect_to_region(region_name=region, **aws_connect_kwargs) - except boto.exception.NoAuthHandlerFound as e: - module.fail_json(msg=to_native(e)) + subnet_group = get_subnet_group(name) + changed = False - try: - changed = False - exists = False - - try: - matching_groups = conn.describe_cache_subnet_groups(group_name, max_records=100) - exists = len(matching_groups) > 0 - except BotoServerError as e: - if e.error_code != 'CacheSubnetGroupNotFoundFault': - module.fail_json(msg=e.error_message) - - if state == 'absent': - if exists: - conn.delete_cache_subnet_group(group_name) - changed = True - else: - if not exists: - new_group = conn.create_cache_subnet_group(group_name, cache_subnet_group_description=group_description, subnet_ids=group_subnets) - changed = True - else: - changed_group = conn.modify_cache_subnet_group(group_name, cache_subnet_group_description=group_description, subnet_ids=group_subnets) - changed = True - - except BotoServerError as e: - if e.error_message != 'No modifications were requested.': - module.fail_json(msg=e.error_message) + if state == 'present': + if not subnet_group: + result = create_subnet_group(name, description, subnets) + changed |= result else: - changed = False + result = update_subnet_group(subnet_group, name, description, subnets) + changed |= result + subnet_group = get_subnet_group(name) + else: + if subnet_group: + result = delete_subnet_group(name) + changed |= result + subnet_group = None - module.exit_json(changed=changed) + module.exit_json(changed=changed, cache_subnet_group=subnet_group) if __name__ == '__main__': diff --git a/tests/integration/targets/elasticache_subnet_group/tasks/main.yml b/tests/integration/targets/elasticache_subnet_group/tasks/main.yml index 3f2774b2b17..5814f9dc90d 100644 --- a/tests/integration/targets/elasticache_subnet_group/tasks/main.yml +++ b/tests/integration/targets/elasticache_subnet_group/tasks/main.yml @@ -13,6 +13,48 @@ 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 @@ -55,8 +97,26 @@ 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 @@ -72,11 +132,38 @@ 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 + - '"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: @@ -93,22 +180,51 @@ 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 + - '"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 }}' - subnets: - - '{{ subnet_id_a }}' - - '{{ subnet_id_b }}' + ## No longer mandatory + # subnets: + # - '{{ subnet_id_a }}' + # - '{{ subnet_id_b }}' register: update_description - name: Check result - Update Subnet Group Description @@ -116,20 +232,49 @@ 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 + - '"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 }}' - subnets: - - '{{ subnet_id_a }}' - - '{{ subnet_id_b }}' + ## No longer mandatory + # subnets: + # - '{{ subnet_id_a }}' + # - '{{ subnet_id_b }}' register: update_description - name: Check result - Update Subnet Group Description - idempotency @@ -137,19 +282,48 @@ 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 + - '"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 }}' - description: '{{ description_updated }}' + ## No longer mandatory + # description: '{{ description_updated }}' subnets: - '{{ subnet_id_c }}' - '{{ subnet_id_d }}' @@ -160,17 +334,46 @@ 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 + - '"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 }}' - description: '{{ description_updated }}' + ## No longer mandatory + # description: '{{ description_updated }}' subnets: - '{{ subnet_id_c }}' - '{{ subnet_id_d }}' @@ -181,14 +384,36 @@ 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 + - '"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 @@ -200,6 +425,18 @@ 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 @@ -211,6 +448,207 @@ 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: ################################################