Cross-Account CloudFormation Deployments

Attini supports deploying CloudFormation across AWS accounts. To enable cross-account deployments, we need to do the following:

  1. Create an IAM Role in the target account that the Attini Framework can assume.
  2. Configure the ExecutionRoleArn in the Attini deployment plan to reference the IAM Role from step 1.
  3. Apply an S3 bucket policy to the attini-artifact-store-{Region}-{AccountId} bucket that allows the IAM Role from step 1 to get the template from the bucket.

In the example below, the Attini Framework is deployed into eu-west-1 in account 111111111111, and we deploy a CloudFormation stack into account 222222222222.

Cross account cfn deploys Cross account cfn deploys


1. Create a custom execution IAM Role

First, we need to create an IAM Role in the target AWS account. This IAM Role requires permission to manage all the resources in your CloudFormation Stack OR the iam:PassRole permission if you are using a Stack role. It also needs permissions to deploy CloudFormation and accessing S3 Objects from attini-artifact-store-{Region**}-{AccountId*} bucket.

We must also ensure that the Attini Framework can assume the IAM Role. This is done by allowing the IAM Role to be assumed by the arn:aws:iam::{AccountId*}:role/attini/attini-action-role-{Region**} IAM Role.

* The AWS account id you are performing the deployment from.

** The region you are performing the deployment from.


The example IAM Roles below have full EC2 access. However, you should configure the IAM Role to only have access to resources the stack should manage.

Example role configuration
CreateS3ExecutionRole:
  Type: AWS::IAM::Role
  Properties:
    RoleName: custom-execution-role
    AssumeRolePolicyDocument:
      Statement:
        - Action: sts:AssumeRole
          Effect: Allow
          Principal:
            AWS: arn:aws:iam::111111111111:role/attini/attini-action-role-eu-west-1
      Version: 2012-10-17
    Policies:
      - PolicyName: !Ref AWS::StackName
        PolicyDocument:
          Version: 2012-10-17
          Statement:
            - Action: s3:GetObject
              Effect: Allow
              Resource: arn:aws:s3:::attini-artifact-store-eu-west-1-111111111111/*
            - Action:
                - cloudformation:Describe*
                - cloudformation:List*
                - cloudformation:Get*
                - cloudformation:ValidateTemplate
                - cloudformation:CreateStack
                - cloudformation:TagResource
                - cloudformation:UntagResource
                - cloudformation:CancelUpdateStack
                - cloudformation:CreateChangeSet
                - cloudformation:ContinueUpdateRollback
                - cloudformation:UpdateStack
                - cloudformation:DeleteStack
              Effect: Allow
              Resource: “*”
            - Action: ec2:*
              Effect: Allow
              Resource: “*”
{
  "Role": {
    "RoleName": "custom-execution-role",
    "Arn": "arn:aws:iam::222222222222:role/custom-execution-role",
    "AssumeRolePolicyDocument": {
      "Version": "2012-10-17",
      "Statement": [{
        "Effect": "Allow",
        "Principal": {
          "AWS": [
            "arn:aws:iam::111111111111:role/attini/attini-action-role-eu-west-1"
          ]
        },
        "Action": "sts:AssumeRole"
      }]
    },
    "Policies": [{
        "Effect": "Allow",
        "Action": "s3:GetObject",
        "Resource": "arn:aws:s3:::attini-artifact-store-eu-west-1-111111111111/*"
      },
      {
        "Effect": "Allow",
        "Action": [
          "cloudformation:Describe*",
          "cloudformation:List*",
          "cloudformation:Get*",
          "cloudformation:ValidateTemplate",
          "cloudformation:CreateStack",
          "cloudformation:TagResource",
          "cloudformation:UntagResource",
          "cloudformation:CancelUpdateStack",
          "cloudformation:CreateChangeSet",
          "cloudformation:ContinueUpdateRollback",
          "cloudformation:UpdateStack",
          "cloudformation:DeleteStack"
        ],
        "Resource": "*"
      },
      {
        "Effect": "Allow",
        "Action": "ec2:*",
        "Resource": "*"
      }
    ],
    "Description": "Cross account deployment role"
  }
}

2. Configure the step in the deployment plan

We need to configure the deployment plan to use our execution IAM Role when deploying the template. This is done by setting the ExecutionRoleArn property in the AttiniCfn step.

Example of a deployment plan step using an ExecutionRoleArn
Resources:
  CrossAccountDeploymentPlan:
    Type: Attini::Deploy::DeploymentPlan
    Properties:
      DeploymentPlan:
        StartAt: DeployVpc
        States:
          DeployVpc:
            Type: AttiniCfn
            Properties:
              Template: /network/vpc.yaml
              StackName: vpc
              ExecutionRoleArn: arn:aws:iam::222222222222:role/custom-execution-role
            End: true

3. Apply an s3 bucket policy

Because we are using a custom execution IAM Role we need to make sure that it's allowed to read the CloudFormation template from S3. This is easily done by applying an S3 Bucket Policy to the attini-artifact-store-{Region}-{AccountId} bucket in the account we are performing the deployment from.

Example of an S3 Bucket Policy
{
  "Version":"2012-10-17",
  "Statement": [
    {
      "Sid": "AllowCrossAccountAccess",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::222222222222:role/custom-execution-role"
      },
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::attini-artifact-store-eu-west-1-111111111111/*"
    }
  ]
}

That’s it! The deployment plan will now deploy the stack to the target account.