Repository: ansible-collections/community.aws

Issue:
aws_cloudformation_stackset: adding new accounts doesn't create new stack instances
### Summary

Hi,
I have noticed that the `cloudformation_stack_set` module doesn't seem to support adding new accounts.
The stack set is initially provisioned with a list of accounts, however, on subsequent runs that have more accounts, the module doesn't add these new accounts.

### Issue Type

Bug Report

### Component Name

`cloudformation_stack_set`

### Ansible Version

```console (paste below)
ansible 2.10.17
  config file = None
  configured module search path = ['/home/rmahroua/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/local/lib/python3.9/site-packages/ansible
  executable location = /usr/local/bin/ansible
  python version = 3.9.13 (main, May 18 2022, 00:00:00) [GCC 11.3.1 20220421 (Red Hat 11.3.1-2)]
```


### Collection Versions

```console (paste below)
Collection    Version
------------- -------
amazon.aws    5.0.2  
awx.awx       21.7.0 
community.aws 5.0.0  

# /usr/local/lib/python3.9/site-packages/ansible_collections
Collection                    Version
----------------------------- -------
amazon.aws                    1.5.0  
```


### AWS SDK versions

```console (paste below)
Name: boto
Version: 2.49.0
Summary: Amazon Web Services Library
Home-page: https://github.com/boto/boto/
Author: Mitch Garnaat
Author-email: mitch@garnaat.com
License: MIT
Location: /home/rmahroua/.local/lib/python3.9/site-packages
Requires: 
Required-by: aws-automation
---
Name: boto3
Version: 1.24.89
Summary: The AWS SDK for Python
Home-page: https://github.com/boto/boto3
Author: Amazon Web Services
Author-email: 
License: Apache License 2.0
Location: /usr/local/lib/python3.9/site-packages
Requires: botocore, jmespath, s3transfer
Required-by: aws-automation, aws-shell
---
Name: botocore
Version: 1.27.89
Summary: Low-level, data-driven core of boto 3.
Home-page: https://github.com/boto/botocore
Author: Amazon Web Services
Author-email: 
License: Apache License 2.0
Location: /usr/local/lib/python3.9/site-packages
Requires: jmespath, python-dateutil, urllib3
Required-by: aws-automation, awscli, boto3, s3transfer
```


### Configuration

```console (paste below)
$ ansible-config dump --only-changed

```


### OS / Environment

Fedora release 34

### Steps to Reproduce

<!--- Paste example playbooks or commands between quotes below -->
```yaml (paste below)
# First run to deploy the stack set in two accounts
- name: Deploy AWS Config StackSet
  community.aws.cloudformation_stack_set:
    name: "My StackSet"
    description: "descripion"
    state: present
    template: "{{ stackset.config.template | default(omit) }}"
    accounts: [1234, 5678]
    region: us-east-1
    regions: us-east-1    

# Second run with an extra account
- name: Deploy AWS Config StackSet
  community.aws.cloudformation_stack_set:
    name: "My StackSet"
    description: "descripion"
    state: present
    template: "{{ stackset.config.template | default(omit) }}"
    accounts: [1234, 5678, 910112]
    region: us-east-1
    regions: us-east-1
```




### Expected Results

When adding an account to the `accounts` parameter, the module should deploy a new stack instance for that account.


### Actual Results

Currently, the new account isn't being added.

### Code of Conduct

- [X] I agree to follow the Ansible Code of Conduct
aws_cloudformation_stackset: adding new accounts doesn't create new stack instances
### Summary

Hi,
I have noticed that the `cloudformation_stack_set` module doesn't seem to support adding new accounts.
The stack set is initially provisioned with a list of accounts, however, on subsequent runs that have more accounts, the module doesn't add these new accounts.

### Issue Type

Bug Report

### Component Name

`cloudformation_stack_set`

### Ansible Version

```console (paste below)
ansible 2.10.17
  config file = None
  configured module search path = ['/home/rmahroua/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/local/lib/python3.9/site-packages/ansible
  executable location = /usr/local/bin/ansible
  python version = 3.9.13 (main, May 18 2022, 00:00:00) [GCC 11.3.1 20220421 (Red Hat 11.3.1-2)]
```


### Collection Versions

```console (paste below)
Collection    Version
------------- -------
amazon.aws    5.0.2  
awx.awx       21.7.0 
community.aws 5.0.0  

# /usr/local/lib/python3.9/site-packages/ansible_collections
Collection                    Version
----------------------------- -------
amazon.aws                    1.5.0  
```


### AWS SDK versions

```console (paste below)
Name: boto
Version: 2.49.0
Summary: Amazon Web Services Library
Home-page: https://github.com/boto/boto/
Author: Mitch Garnaat
Author-email: mitch@garnaat.com
License: MIT
Location: /home/rmahroua/.local/lib/python3.9/site-packages
Requires: 
Required-by: aws-automation
---
Name: boto3
Version: 1.24.89
Summary: The AWS SDK for Python
Home-page: https://github.com/boto/boto3
Author: Amazon Web Services
Author-email: 
License: Apache License 2.0
Location: /usr/local/lib/python3.9/site-packages
Requires: botocore, jmespath, s3transfer
Required-by: aws-automation, aws-shell
---
Name: botocore
Version: 1.27.89
Summary: Low-level, data-driven core of boto 3.
Home-page: https://github.com/boto/botocore
Author: Amazon Web Services
Author-email: 
License: Apache License 2.0
Location: /usr/local/lib/python3.9/site-packages
Requires: jmespath, python-dateutil, urllib3
Required-by: aws-automation, awscli, boto3, s3transfer
```


### Configuration

```console (paste below)
$ ansible-config dump --only-changed

```


### OS / Environment

Fedora release 34

### Steps to Reproduce

<!--- Paste example playbooks or commands between quotes below -->
```yaml (paste below)
# First run to deploy the stack set in two accounts
- name: Deploy AWS Config StackSet
  community.aws.cloudformation_stack_set:
    name: "My StackSet"
    description: "descripion"
    state: present
    template: "{{ stackset.config.template | default(omit) }}"
    accounts: [1234, 5678]
    region: us-east-1
    regions: us-east-1    

# Second run with an extra account
- name: Deploy AWS Config StackSet
  community.aws.cloudformation_stack_set:
    name: "My StackSet"
    description: "descripion"
    state: present
    template: "{{ stackset.config.template | default(omit) }}"
    accounts: [1234, 5678, 910112]
    region: us-east-1
    regions: us-east-1
```




### Expected Results

When adding an account to the `accounts` parameter, the module should deploy a new stack instance for that account.


### Actual Results

Currently, the new account isn't being added.

### Code of Conduct

- [X] I agree to follow the Ansible Code of Conduct


Discussion:
Files identified in the description:
* [`plugins/modules/cloudformation_stack_set.py`](https://github.com/['ansible-collections/amazon.aws', 'ansible-collections/community.aws', 'ansible-collections/community.vmware']/blob/main/plugins/modules/cloudformation_stack_set.py)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @jillr @markuman @s-hertel @tremble
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
Based on my understanding of the API, the [`update_stack_instances`](https://github.com/ansible-collections/community.aws/blob/main/plugins/modules/cloudformation_stack_set.py#L662) call does not deploy new stack instances, it only updates parameters against existing stack instances.

To support real idempotency in this case, supplementary calls to the [`create_stack_instances`](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/cloudformation.html#CloudFormation.Client.create_stack_instances) is necessary.
The method takes in as parameter the name of an existing stack set to provision a new instance in new accounts.


``` python
response = client.create_stack_instances(
    StackSetName='string',
    Accounts=[
        'string',
    ],
    DeploymentTargets={
        'Accounts': [
            'string',
        ],
        'AccountsUrl': 'string',
        'OrganizationalUnitIds': [
            'string',
        ],
        'AccountFilterType': 'NONE'|'INTERSECTION'|'DIFFERENCE'|'UNION'
    },
    Regions=[
        'string',
    ],
    ParameterOverrides=[
        {
            'ParameterKey': 'string',
            'ParameterValue': 'string',
            'UsePreviousValue': True|False,
            'ResolvedValue': 'string'
        },
    ],
    OperationPreferences={
        'RegionConcurrencyType': 'SEQUENTIAL'|'PARALLEL',
        'RegionOrder': [
            'string',
        ],
        'FailureToleranceCount': 123,
        'FailureTolerancePercentage': 123,
        'MaxConcurrentCount': 123,
        'MaxConcurrentPercentage': 123
    },
    OperationId='string',
    CallAs='SELF'|'DELEGATED_ADMIN'
)
```

EDIT: happy to work on implementing this functionality if we wish to support adding new accounts -- if the goal of the module is to remain close to the original CloudFormation feature that consists in updating existing accounts only, then a new Ansible module is required since the current collection does not have any module for provisioning new stack instances for an existing stackset.
@Razique Thank you for reporting this. Feel free to open a PR to support this functionality.
Files identified in the description:
* [`plugins/modules/cloudformation_stack_set.py`](https://github.com/['ansible-collections/amazon.aws', 'ansible-collections/community.aws', 'ansible-collections/community.vmware']/blob/main/plugins/modules/cloudformation_stack_set.py)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @jillr @markuman @s-hertel @tremble
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
Based on my understanding of the API, the [`update_stack_instances`](https://github.com/ansible-collections/community.aws/blob/main/plugins/modules/cloudformation_stack_set.py#L662) call does not deploy new stack instances, it only updates parameters against existing stack instances.

To support real idempotency in this case, supplementary calls to the [`create_stack_instances`](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/cloudformation.html#CloudFormation.Client.create_stack_instances) is necessary.
The method takes in as parameter the name of an existing stack set to provision a new instance in new accounts.


``` python
response = client.create_stack_instances(
    StackSetName='string',
    Accounts=[
        'string',
    ],
    DeploymentTargets={
        'Accounts': [
            'string',
        ],
        'AccountsUrl': 'string',
        'OrganizationalUnitIds': [
            'string',
        ],
        'AccountFilterType': 'NONE'|'INTERSECTION'|'DIFFERENCE'|'UNION'
    },
    Regions=[
        'string',
    ],
    ParameterOverrides=[
        {
            'ParameterKey': 'string',
            'ParameterValue': 'string',
            'UsePreviousValue': True|False,
            'ResolvedValue': 'string'
        },
    ],
    OperationPreferences={
        'RegionConcurrencyType': 'SEQUENTIAL'|'PARALLEL',
        'RegionOrder': [
            'string',
        ],
        'FailureToleranceCount': 123,
        'FailureTolerancePercentage': 123,
        'MaxConcurrentCount': 123,
        'MaxConcurrentPercentage': 123
    },
    OperationId='string',
    CallAs='SELF'|'DELEGATED_ADMIN'
)
```

EDIT: happy to work on implementing this functionality if we wish to support adding new accounts -- if the goal of the module is to remain close to the original CloudFormation feature that consists in updating existing accounts only, then a new Ansible module is required since the current collection does not have any module for provisioning new stack instances for an existing stackset.
@Razique Thank you for reporting this. Feel free to open a PR to support this functionality.

Resolving patch:
diff --git a/plugins/modules/cloudformation_stack_set.py b/plugins/modules/cloudformation_stack_set.py
--- a/plugins/modules/cloudformation_stack_set.py
+++ b/plugins/modules/cloudformation_stack_set.py
@@ -182,9 +182,11 @@
     description: Test stack in two accounts
     state: present
     template_url: https://s3.amazonaws.com/my-bucket/cloudformation.template
-    accounts: [1234567890, 2345678901]
+    accounts:
+      - 123456789012
+      - 234567890123
     regions:
-    - us-east-1
+      - us-east-1
 
 - name: on subsequent calls, templates are optional but parameters and tags can be altered
   community.aws.cloudformation_stack_set:
@@ -195,9 +197,11 @@
     tags:
       foo: bar
       test: stack
-    accounts: [1234567890, 2345678901]
+    accounts:
+      - 123456789012
+      - 234567890123
     regions:
-    - us-east-1
+     - us-east-1
 
 - name: The same type of update, but wait for the update to complete in all stacks
   community.aws.cloudformation_stack_set:
@@ -209,7 +213,26 @@
     tags:
       foo: bar
       test: stack
-    accounts: [1234567890, 2345678901]
+    accounts:
+      - 123456789012
+      - 234567890123
+    regions:
+    - us-east-1
+
+- name: Register new accounts (create new stack instances) with an existing stack set.
+  community.aws.cloudformation_stack_set:
+    name: my-stack
+    state: present
+    wait: true
+    parameters:
+      InstanceName: my_restacked_instance
+    tags:
+      foo: bar
+      test: stack
+    accounts:
+      - 123456789012
+      - 234567890123
+      - 345678901234
     regions:
     - us-east-1
 """
@@ -655,6 +678,14 @@ def main():
                 stack_params["OperationPreferences"] = get_operation_preferences(module)
             changed |= update_stack_set(module, stack_params, cfn)
 
+            await_stack_set_operation(
+                module,
+                cfn,
+                operation_id=stack_params["OperationId"],
+                stack_set_name=stack_params["StackSetName"],
+                max_wait=module.params.get("wait_timeout"),
+            )
+
         # now create/update any appropriate stack instances
         new_stack_instances, existing_stack_instances, unspecified_stack_instances = compare_stack_instances(
             cfn,


Test patch:
diff --git a/tests/integration/targets/cloudformation_stack_set/tasks/main.yml b/tests/integration/targets/cloudformation_stack_set/tasks/main.yml
--- a/tests/integration/targets/cloudformation_stack_set/tasks/main.yml
+++ b/tests/integration/targets/cloudformation_stack_set/tasks/main.yml
@@ -14,9 +14,9 @@
       aws_secret_key: "{{ secondary_aws_secret_key }}"
       security_token: "{{ secondary_security_token }}"
       region: "{{ aws_region }}"
-  no_log: yes
+  no_log: true
 
-- name: cloudformation_stack_set tests 
+- name: cloudformation_stack_set tests
   collections:
     - amazon.aws