From a89b3b912223556f8e02286c49aa6ed3206fd63a Mon Sep 17 00:00:00 2001 From: Razique Mahroua Date: Mon, 14 Aug 2023 10:04:56 -0700 Subject: [PATCH 1/3] * Add new purge_stack_instances parameter --- .../20230814-cloudformation-stack-set.yml | 2 + plugins/modules/cloudformation_stack_set.py | 108 ++++++++++++------ 2 files changed, 74 insertions(+), 36 deletions(-) create mode 100644 changelogs/fragments/20230814-cloudformation-stack-set.yml diff --git a/changelogs/fragments/20230814-cloudformation-stack-set.yml b/changelogs/fragments/20230814-cloudformation-stack-set.yml new file mode 100644 index 00000000000..02b512f885e --- /dev/null +++ b/changelogs/fragments/20230814-cloudformation-stack-set.yml @@ -0,0 +1,2 @@ +minor_changes: + - cloudformation_stack_set - add new 'purge_stack_instances' module parameter to remove stack instances when accounts are not present in the list of accounts diff --git a/plugins/modules/cloudformation_stack_set.py b/plugins/modules/cloudformation_stack_set.py index 17e888b4f1b..8d8bdb23e0e 100644 --- a/plugins/modules/cloudformation_stack_set.py +++ b/plugins/modules/cloudformation_stack_set.py @@ -66,14 +66,19 @@ type: str purge_stacks: description: - - Only applicable when I(state=absent). Sets whether, when deleting a stack set, the stack instances should also be deleted. - - By default, instances will be deleted. To keep stacks when stack set is deleted set I(purge_stacks=false). + - Only applicable when I(state=absent). Sets whether, when deleting a stack set, the stack instances should also be deleted. + - By default, instances will be deleted. To keep stacks when stack set is deleted set I(purge_stacks=false). type: bool default: true + purge_stack_instances: + description: + - When set to I(True), instructs the module to delete stack instances that are not defined via the C(accounts) parameter. + type: bool + default: false wait: description: - - Whether or not to wait for stack operation to complete. This includes waiting for stack instances to reach UPDATE_COMPLETE status. - - If you choose not to wait, this module will not notify when stack operations fail because it will not wait for them to finish. + - Whether or not to wait for stack operation to complete. This includes waiting for stack instances to reach UPDATE_COMPLETE status. + - If you choose not to wait, this module will not notify when stack operations fail because it will not wait for them to finish. type: bool default: false wait_timeout: @@ -83,51 +88,51 @@ type: int capabilities: description: - - Capabilities allow stacks to create and modify IAM resources, which may include adding users or roles. - - Currently the only available values are 'CAPABILITY_IAM' and 'CAPABILITY_NAMED_IAM'. Either or both may be provided. - - > - The following resources require that one or both of these parameters is specified: AWS::IAM::AccessKey, - AWS::IAM::Group, AWS::IAM::InstanceProfile, AWS::IAM::Policy, AWS::IAM::Role, AWS::IAM::User, AWS::IAM::UserToGroupAddition + - Capabilities allow stacks to create and modify IAM resources, which may include adding users or roles. + - Currently the only available values are 'CAPABILITY_IAM' and 'CAPABILITY_NAMED_IAM'. Either or both may be provided. + - > + The following resources require that one or both of these parameters is specified: AWS::IAM::AccessKey, + AWS::IAM::Group, AWS::IAM::InstanceProfile, AWS::IAM::Policy, AWS::IAM::Role, AWS::IAM::User, AWS::IAM::UserToGroupAddition type: list elements: str choices: - - 'CAPABILITY_IAM' - - 'CAPABILITY_NAMED_IAM' + - 'CAPABILITY_IAM' + - 'CAPABILITY_NAMED_IAM' regions: description: - - A list of AWS regions to create instances of a stack in. The I(region) parameter chooses where the Stack Set is created, and I(regions) - specifies the region for stack instances. - - At least one region must be specified to create a stack set. On updates, if fewer regions are specified only the specified regions will + - A list of AWS regions to create instances of a stack in. The I(region) parameter chooses where the Stack Set is created, and I(regions) + specifies the region for stack instances. + - At least one region must be specified to create a stack set. On updates, if fewer regions are specified only the specified regions will have their stack instances updated. type: list elements: str accounts: description: - - A list of AWS accounts in which to create instance of CloudFormation stacks. - - At least one region must be specified to create a stack set. On updates, if fewer regions are specified only the specified regions will - have their stack instances updated. + - A list of AWS accounts in which to create instance of CloudFormation stacks. + - At least one region must be specified to create a stack set. On updates, if fewer regions are specified only the specified regions will + have their stack instances updated. type: list elements: str administration_role_arn: description: - - ARN of the administration role, meaning the role that CloudFormation Stack Sets use to assume the roles in your child accounts. - - This defaults to C(arn:aws:iam::{{ account ID }}:role/AWSCloudFormationStackSetAdministrationRole) where C({{ account ID }}) is replaced with the - account number of the current IAM role/user/STS credentials. + - ARN of the administration role, meaning the role that CloudFormation Stack Sets use to assume the roles in your child accounts. + - This defaults to C(arn:aws:iam::{{ account ID }}:role/AWSCloudFormationStackSetAdministrationRole) where C({{ account ID }}) is replaced with the + account number of the current IAM role/user/STS credentials. aliases: - - admin_role_arn - - admin_role - - administration_role + - admin_role_arn + - admin_role + - administration_role type: str execution_role_name: description: - - ARN of the execution role, meaning the role that CloudFormation Stack Sets assumes in your child accounts. - - This MUST NOT be an ARN, and the roles must exist in each child account specified. - - The default name for the execution role is C(AWSCloudFormationStackSetExecutionRole) + - ARN of the execution role, meaning the role that CloudFormation Stack Sets assumes in your child accounts. + - This MUST NOT be an ARN, and the roles must exist in each child account specified. + - The default name for the execution role is C(AWSCloudFormationStackSetExecutionRole) aliases: - - exec_role_name - - exec_role - - execution_role - type: str + - exec_role_name + - exec_role + - execution_role + type: str tags: description: - Dictionary of tags to associate with stack and its resources during stack creation. @@ -169,6 +174,7 @@ author: - "Ryan Scott Brown (@ryansb)" + - "Razique Mahroua (@razique)" extends_documentation_fragment: - amazon.aws.common.modules - amazon.aws.region.modules @@ -235,6 +241,23 @@ - 345678901234 regions: - us-east-1 + +- name: Delete untracked stack instances for an existing stack set. + community.aws.cloudformation_stack_set: + name: my-stack + state: present + purge_stack_instances: true + parameters: + InstanceName: my_restacked_instance + tags: + foo: bar + test: stack + accounts: + - 123456789012 + - 234567890123 + - 345678901234 + regions: + - us-east-1 """ RETURN = r""" @@ -459,10 +482,11 @@ def await_stack_instance_completion(module, cfn, stack_set_name, max_wait): pass time.sleep(15) - module.warn( - f"Timed out waiting for stack set {stack_set_name} instances {', '.join(s['StackId'] for s in to_await)} to" - f" complete after {max_wait} seconds. Returning unfinished operation" - ) + if to_await: + module.warn( + f"Timed out waiting for stack set {stack_set_name} instances {', '.join(s['StackId'] for s in to_await)} to" + f" complete after {max_wait} seconds. Returning unfinished operation" + ) def await_stack_set_exists(cfn, stack_set_name): @@ -539,6 +563,7 @@ def main(): wait_timeout=dict(type="int", default=900), state=dict(default="present", choices=["present", "absent"]), purge_stacks=dict(type="bool", default=True), + purge_stack_instances=dict(type="bool", default=False), parameters=dict(type="dict", default={}), template=dict(type="path"), template_url=dict(), @@ -705,10 +730,21 @@ def main(): changed = True cfn.create_stack_instances( StackSetName=module.params["name"], - Accounts=list(set(acct for acct, region in new_stack_instances)), - Regions=list(set(region for acct, region in new_stack_instances)), + Accounts=list(set(acct for acct, _ in new_stack_instances)), + Regions=list(set(region for _, region in new_stack_instances)), + OperationPreferences=get_operation_preferences(module), + OperationId=operation_ids[-1], + ) + elif unspecified_stack_instances and module.params.get("purge_stack_instances"): + operation_ids.append(f"Ansible-StackInstance-RemoveInstance-{operation_uuid}") + changed = True + cfn.delete_stack_instances( + StackSetName=module.params["name"], + Accounts=list(set(acct for acct, _ in unspecified_stack_instances)), + Regions=list(set(region for _, region in unspecified_stack_instances)), OperationPreferences=get_operation_preferences(module), OperationId=operation_ids[-1], + RetainStacks=not(module.params.get("purge_stacks")) ) else: operation_ids.append(f"Ansible-StackInstance-Update-{operation_uuid}") From 8a79f6a9da191ea65b3bdcf0081bae45991966fd Mon Sep 17 00:00:00 2001 From: Razique Mahroua Date: Wed, 30 Aug 2023 08:18:03 -0700 Subject: [PATCH 2/3] Fix indentation --- plugins/modules/cloudformation_stack_set.py | 32 ++++++++++----------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/plugins/modules/cloudformation_stack_set.py b/plugins/modules/cloudformation_stack_set.py index 8d8bdb23e0e..f60aa23aee2 100644 --- a/plugins/modules/cloudformation_stack_set.py +++ b/plugins/modules/cloudformation_stack_set.py @@ -132,7 +132,7 @@ - exec_role_name - exec_role - execution_role - type: str + type: str tags: description: - Dictionary of tags to associate with stack and its resources during stack creation. @@ -146,31 +146,31 @@ suboptions: fail_count: description: - - The number of accounts, per region, for which this operation can fail before CloudFormation - stops the operation in that region. - - You must specify one of I(fail_count) and I(fail_percentage). + - The number of accounts, per region, for which this operation can fail before CloudFormation + stops the operation in that region. + - You must specify one of I(fail_count) and I(fail_percentage). type: int fail_percentage: type: int description: - - The percentage of accounts, per region, for which this stack operation can fail before CloudFormation - stops the operation in that region. - - You must specify one of I(fail_count) and I(fail_percentage). + - The percentage of accounts, per region, for which this stack operation can fail before CloudFormation + stops the operation in that region. + - You must specify one of I(fail_count) and I(fail_percentage). parallel_percentage: type: int description: - - The maximum percentage of accounts in which to perform this operation at one time. - - You must specify one of I(parallel_count) and I(parallel_percentage). - - Note that this setting lets you specify the maximum for operations. - For large deployments, under certain circumstances the actual percentage may be lower. + - The maximum percentage of accounts in which to perform this operation at one time. + - You must specify one of I(parallel_count) and I(parallel_percentage). + - Note that this setting lets you specify the maximum for operations. + For large deployments, under certain circumstances the actual percentage may be lower. parallel_count: type: int description: - - The maximum number of accounts in which to perform this operation at one time. - - I(parallel_count) may be at most one more than the I(fail_count). - - You must specify one of I(parallel_count) and I(parallel_percentage). - - Note that this setting lets you specify the maximum for operations. - For large deployments, under certain circumstances the actual count may be lower. + - The maximum number of accounts in which to perform this operation at one time. + - I(parallel_count) may be at most one more than the I(fail_count). + - You must specify one of I(parallel_count) and I(parallel_percentage). + - Note that this setting lets you specify the maximum for operations. + For large deployments, under certain circumstances the actual count may be lower. author: - "Ryan Scott Brown (@ryansb)" From e9d837cfc0f11fef4a44db24328eda1e7b34ac0b Mon Sep 17 00:00:00 2001 From: Razique Mahroua Date: Wed, 13 Sep 2023 09:19:19 -0700 Subject: [PATCH 3/3] Fix indentation Address comments --- plugins/modules/cloudformation_stack_set.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/plugins/modules/cloudformation_stack_set.py b/plugins/modules/cloudformation_stack_set.py index f60aa23aee2..dcc54295ac9 100644 --- a/plugins/modules/cloudformation_stack_set.py +++ b/plugins/modules/cloudformation_stack_set.py @@ -31,7 +31,7 @@ type: dict state: description: - - If I(state=present), stack will be created. If I(state=present) and if stack exists and template has changed, it will be updated. + - If I(state=present), stack will be created. If I(state=present) and if stack exists and template has changed, it will be updated. If I(state=absent), stack will be removed. default: present choices: [ present, absent ] @@ -72,7 +72,7 @@ default: true purge_stack_instances: description: - - When set to I(True), instructs the module to delete stack instances that are not defined via the C(accounts) parameter. + - When set to C(True), instructs the module to delete stack instances that are not defined via the I(accounts) parameter. type: bool default: false wait: @@ -83,7 +83,7 @@ default: false wait_timeout: description: - - How long to wait (in seconds) for stacks to complete create/update/delete operations. + - How long to wait (in seconds) for stacks to complete create/update/delete operations. default: 900 type: int capabilities: @@ -103,7 +103,7 @@ - A list of AWS regions to create instances of a stack in. The I(region) parameter chooses where the Stack Set is created, and I(regions) specifies the region for stack instances. - At least one region must be specified to create a stack set. On updates, if fewer regions are specified only the specified regions will - have their stack instances updated. + have their stack instances updated. type: list elements: str accounts: @@ -140,7 +140,7 @@ type: dict failure_tolerance: description: - - Settings to change what is considered "failed" when running stack instance updates, and how many to do at a time. + - Settings to change what is considered "failed" when running stack instance updates, and how many to do at a time. type: dict default: {} suboptions: @@ -730,8 +730,8 @@ def main(): changed = True cfn.create_stack_instances( StackSetName=module.params["name"], - Accounts=list(set(acct for acct, _ in new_stack_instances)), - Regions=list(set(region for _, region in new_stack_instances)), + Accounts=list(set(acct for acct, dummy in new_stack_instances)), + Regions=list(set(region for dummy, region in new_stack_instances)), OperationPreferences=get_operation_preferences(module), OperationId=operation_ids[-1], ) @@ -740,11 +740,11 @@ def main(): changed = True cfn.delete_stack_instances( StackSetName=module.params["name"], - Accounts=list(set(acct for acct, _ in unspecified_stack_instances)), - Regions=list(set(region for _, region in unspecified_stack_instances)), + Accounts=list(set(acct for acct, dummy in unspecified_stack_instances)), + Regions=list(set(region for dummy, region in unspecified_stack_instances)), OperationPreferences=get_operation_preferences(module), OperationId=operation_ids[-1], - RetainStacks=not(module.params.get("purge_stacks")) + RetainStacks=not module.params.get("purge_stacks"), ) else: operation_ids.append(f"Ansible-StackInstance-Update-{operation_uuid}")