Skip to content

Adding account to an Organizational Unit does not trigger CloudFormation StackSet deployment #2539

Description

@freakinhippie

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions