When testing out the new CloudFormation StackSet implementation (thank you for that!), I found that adding an account to an OU which is the deployment target of CloudFormation StackSet instances with -auto-deployment Enabled=true set does not deploy the stackset to the new account/region. If the account was already part of the OU at the time of creating the stackset instances, it works as expected, but the ongoing changes aren't reflected.
Here is a quick & dirty example of the issue:
#!/usr/bin/env bash
FAKECLOUD_ENDPOINT=http://localhost:4566
AWS_REGION=us-east-1
export AWS_CONFIG_FILE="$PWD/config"
export AWS_SHARED_CREDENTIALS_FILE="$PWD/credentials"
function save_aws_creds() {
# save_creds <profile> <akid> <secret> <region>
[ -n "$AWS_SHARED_CREDENTIALS_FILE" ] || return 1;
[ -z "$1" ] && :> "$AWS_SHARED_CREDENTIALS_FILE";
cat >>"$AWS_SHARED_CREDENTIALS_FILE" <<EOF
[${1:-"default"}]
region=${4:-"us-east-1"}
endpoint_url=${FAKECLOUD_ENDPOINT}
aws_access_key_id=${2:-"test"}
aws_secret_access_key=${3:-"test"}
EOF
}
cat >stackset_demo.yml <<EOF
AWSTemplateFormatVersion: 2010-09-09
Description: Configure a Cross-Account IAM Role
Parameters:
UserName:
Type: 'String'
Default: 'root'
AllowedPattern: '[A-Za-z0-9-_]+'
Description: Enter the trusted user name.
UserAccount:
Type: 'String'
Default: '123456789012'
AllowedPattern: '[0-9]{12}'
Description: Enter the account id where the trusted user lives.
ReadOnlyRoleName:
Type: 'String'
Default: 'readonly-role'
AllowedPattern: '[A-Za-z0-9-_]+'
Description: Enter the name of the read only role.
Resources:
ReadOnlyExecutionRole:
Type: AWS::IAM::Role
Properties:
Description: Allows ReadOnly Access.
RoleName: !Ref ReadOnlyRoleName
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
AWS:
- !Sub arn:aws:iam::\${UserAccount}:user/\${UserName}
Action:
- sts:AssumeRole
Path: /
ManagedPolicyArns:
- arn:aws:iam::aws:policy/ReadOnlyAccess
Tags:
- Key: Name
Value: !Ref ReadOnlyRoleName
EOF
# launch fakecloud
docker run --detach --name fc --publish 127.0.0.1:4566:4566 ghcr.io/faiscadev/fakecloud:latest; sleep 2;
# create the default credentials (deleted any preexisting credentials)
save_aws_creds
# create the organization and fetch the root OU
aws organizations create-organization --feature-set ALL
OU_ROOT=$(aws organizations list-roots | jq -r '.Roots[0].Id'); echo $OU_ROOT
# enable the necessary service principal and confirm it was added successfully
aws organizations enable-aws-service-access --service-principal member.org.stacksets.cloudformation.amazonaws.com
aws organizations list-aws-service-access-for-organization
# create the first child account (pre cloudformation stackset deployment)
account="$(curl -sf -X POST -H 'Content-Type: application/json' -d '{"accountId": "111111111111", "userName": "root"}' "$FAKECLOUD_ENDPOINT/_fakecloud/iam/create-admin")"
# save the credentials as the child1 profile
save_aws_creds child1 $(echo $account | jq -r '[.accessKeyId, .secretAccessKey] | join(" ")')
# create the cloudformation stackset
aws cloudformation create-stack-set --stack-set-name auto-deployment-failure --template-body file://stackset_demo.yml --permission-model SERVICE_MANAGED --capabilities CAPABILITY_NAMED_IAM -
-auto-deployment Enabled=true,RetainStacksOnAccountRemoval=false
aws cloudformation create-stack-instances --stack-set-name auto-deployment-failure --deployment-targets OrganizationalUnitIds=$OU_ROOT --regions us-east-1 --operation-preferences FailureTole
ranceCount=0,MaxConcurrentCount=10; sleep 1
# create the second child account (post cloudformation stackset deployment)
account="$(curl -sf -X POST -H 'Content-Type: application/json' -d '{"accountId": "222222222222", "userName": "root"}' "$FAKECLOUD_ENDPOINT/_fakecloud/iam/create-admin")"
# save the credentials as the child2 profile
save_aws_creds child2 $(echo $account | jq -r '[.accessKeyId, .secretAccessKey] | join(" ")')
echo 'Checking for role deployed in management account; expect failure'
aws --profile default iam get-role --role-name readonly-role
echo 'Checking for role deployed in child1 account; expect success'
aws --profile child1 iam get-role --role-name readonly-role
echo 'Checking for role deployed in child2 account; expect success, but fails'
aws --profile child2 iam get-role --role-name readonly-role
When testing out the new CloudFormation StackSet implementation (thank you for that!), I found that adding an account to an OU which is the deployment target of CloudFormation StackSet instances with
-auto-deployment Enabled=trueset does not deploy the stackset to the new account/region. If the account was already part of the OU at the time of creating the stackset instances, it works as expected, but the ongoing changes aren't reflected.Here is a quick & dirty example of the issue: