Skip to content

tamer84/aws_management

Repository files navigation

Automated Control Tower managed OU and account provisioning

This repo provides an example solution enabling the provisioning of Control Tower-integrated AWS OUs and accounts via GitHub Actions. The main focus of this repo is the OU and account vending automation.
To see more details about the Backstage integration or the automated resource provisioning, see the repo aws_environments.

Solution overview

SolutionOverview

The solution is split across two GitHub repos:

Both repos have GitHUb actions which use Terraform to automate OU/account creation and resource provisioning.
Detailed explanations are available in the relevant sections in the README files.

The solution uses the following AWS Services:

As part of the initial setup, the Management account requires an IAMRole and setup of GitHub as a trusted OIDC provider.
This is described below in Management resources setup.

Getting started

With Terraform and the aws-cli included in the ubuntu-2204 GitHub image, only an AWS account is required to proceed. Fork this repo and follow the steps below to use it in your own AWS management account. All you need outside of this repo is an AWS account with an IAMUser/IAMRole with the AdministratorAccess managed policy attached.

AWS setup

The repo is designed to work in a Control Tower managed AWS setup.
The first step would be to enable Control Tower in your management account.

The solution uses CloudFormation StackSets to inject the IAM roles necessary for further automation.
The second step therefore, is to enable Cloud Formation trusted access for AWS Organizations.

Optionally, a Route53 Hosted Zone can be used to allow DNS Delegation to be performed into the newly created accounts.
For this the account would need a Route53 Hosted Zone configured.
If you want to skip the Route53 step then comment out account_resources/dns.tf.

Terraform setup

Directory _terraform contains a simple declaration to provision an S3 bucket and DynamodDB lock table for Terraform.
The S3 Bucket it creates is shared between the Terraform projects, management_resources, ou_resources and account_resources.
A partial backend file is also generated, and used in the three above-mentioned repos, with independent keys.

This should be applied once by an account user with permissions to assume the AWSAdministratorAccess role.
The default workspace can be used.

Management resources setup

Contains the resources to enable the automation within the central management account. GitHub is configured as an OpenID Connect (OIDC) Provider, to avoid storage of AWS credentials. The IAMRole cicd_role grants permissions for Control Tower, Service Catalog, and other automation needs. Finally, the IAMRole [cicd_role] is added as a principal to the service catalog Control Tower portfolio.

InitialSetup

This should be applied once by an account user with permissions to assume the AWSAdministratorAccess role.
The default workspace can be used.

Backstage Setup

Backstage Install

Backstage sources are not part of this repo, and detailed setup instructions are also not provided.
If you want to industrialize the setup after trying it out, there are deployment instructions available at the official Backstage documentation.

For this simple example, it's enough to launch Backstage locally.
Just follow the getting-started instructions provided by Backstage to get the sources.
Before you start Backstage continue with the configuration here.

Backstage Configuration

This setup requires GitHub actions to be installed, so that the GitHub MR can be created.

From backstage root dir call:
yarn --cwd packages/backend add @backstage/plugin-scaffolder-backend-module-github

and add to /packages/backend/src/index.ts:
backend.add(import('@backstage/plugin-scaffolder-backend-module-github'));

GitHub access token

To be able to discover the Catalog Entities from GitHub (and to trigger the eventual MR), Backstage requires a Token with the scopes (repo, workflow) for GitHub.
For this simple scenario, it is enough to create a PAT and set it as an environment variable (GITHUB_TOKEN).

Management account configuration

There is a special catalog entry for the management account, so that the automation knows where Control Tower and the root OU are.
Update the contents of the file to correctly reflect your management account ID and root OU.

app-config.yaml

Finally, copy over the example app-config.yaml from this repo.

catalog:
  orphanStrategy: delete
  processingInterval: { minutes: 3 }
  import:
    entityFilename: catalog-info.yaml
    pullRequestBranchName: backstage-integration
  rules:
    - allow: [Component, System, API, Resource, Location, Domain, Template]
  locations:
    - type: url
      target: https://github.com/<<YOUR_GITHUB_ORG>>/aws_management/blob/main/catalog-info.yaml
    - type: url
      target: https://github.com/<<YOUR_GITHUB_ORG>>/aws_environments/blob/main/catalog-info.yaml

Make sure you modify the catalog location URLs to point to your own GitHub clones/forks of these repos.

Launch Backstage

Its time to start Backstage.

cd my-backstage-app # your app name
yarn dev

Backstage catalog

If everything has gone to plan, you should see Backstage starting up and available on your localhost with two entries in the catalog, AWS Management and AWS Environments. To understand what the Catalog is, please check the Backstage documentation.

Ecosystem Model

The OUs and accounts created in the management repo are represented in the Backstage Catalog based on the Backstage System Model.

Domains

Any OU you create in AWS via Backstage will have a catalog entry here.

Systems

Any account you create in AWS via Backstage will have a catalog entry here.

Templates

There are four templates ready to use in the two repositories.

OU and Account automation
Part of this repo and used to provision new OUs and accounts.

Environment automation
Part of the aws_environments repository, and will be described in detail in that repositories README.mdhttps://github.com/tamer84/aws_environments/README.md).

Usage

Interacting with the example is via Backstage Templates, which trigger GitHub Actions.
The templates can be accessed in the local running instance of Backstage.

Workflows

The two workflows (OU and account) are defined in the .github/workflows directory.

Each workflow requires input variables, which are in turn passed to terraform as variables for the provisioning.
Before Terraform can be executed, the Runner needs to assume the cicd_role defined in management_resources.
For this the GitHub action aws-actions/configure-aws-credentials is used as follows:

    - name: configure aws credentials
    uses: aws-actions/configure-aws-credentials@v4.0.2
    with:
        role-to-assume: arn:aws:iam::${{ env.ACCOUNT }}:role/service-role/cicd_role
        role-session-name: GitHub_to_AWS_via_FederatedOIDC
        aws-region: ${{ env.AWS_REGION }}

The role is confirmed using AWS STS:

    - name: Sts GetCallerIdentity
    run: |
      aws sts get-caller-identity

Once the IAMRole is assumed terraform can be executed, for example the account:

    - name: Create new Account via Terraform
    run: |
      cd account_resources
      terraform init -backend-config=../_terraform/remote_backend.hcl
      terraform workspace select -or-create $OU-$ENVIRONMENT
      terraform apply --auto-approve -var parent_ou_id=$OU -var domain=$DOMAIN -var environment=$ENVIRONMENT -var sso_email=$OWNER

Control Tower OU and account Terraform projects

The logic in these two directories is based on the AWS YouTube video Programmatically Create an AWS Account with AWS Control Tower | Amazon Web Services .

ou_resources

Responsible for creating new Control Tower managed OUs.
OuCreation

ou.tf

An OU is provisioned using the Terraform resource aws_organizations_organizational_unit.
The provisioned OU is not by default under Control Tower management, it needs to be explicitly brought under management.
For this, a Terraform null_resource resource is declared, to allow usage of aws-cli.

stackset.tf

The accounts vended later within this new OU will be automatically injected with an IAMRole/IAMRolePolicy to allow automated environment provisioning.
This is achieved by creating a CloudFormation StackSet and setting it to auto deploy to new accounts within the OU.
The StackSet contains the IAMRole and configures GitHub as a trusted OIDC provider.

account_resources

Responsible for creating new Control Tower managed accounts within managed OUs.
AccountCreation

account.tf

An account is provisioned using the Terraform null_resource resource, to allow usage of aws-cli servicecatalog commands.
Details about the process can be seen at the linked YouTube video.

dns.tf

Optionally, dns.tf creates a dynamic DNS entry for the new account. This is supported if there is a Route53 Hosted Zone in the management account.
It should be commented out if your test AWS Management account does not include a DNS Hosted Zone.

account_alias.tf

To demonstrate provisioning resources in the newly vended account using the IAMRole injected via the OU StackSet, the account-alias is set.

resource "aws_iam_account_alias" "alias" {
  provider      = aws.workload
  account_alias = "${local.ou_name}-${terraform.workspace}-${data.aws_caller_identity.current.account_id}"
}

Notice the provider is specified as aws.workload.

This is defined in the providers.tf file as follows:

provider "aws" {
  region = var.region
  alias  = "workload"
  assume_role {
    # The role ARN for CICD
    role_arn = "arn:aws:iam::${data.external.get_account_id.result.Id}:role/service-role/cicd_role"
  }
  default_tags {
    tags = {
      Terraform = "true"
    }
  }
}

The alias is created by assuming the IAMRole cicd_role within the newly provisioned account.

Backstage catalog entries

For each provisioned OU or account, Terraform generates a Backstage Catalog Entity file.
The files are committed by the GitHub action at the end of a successful run into the .backstage directory.

At the root of the repo is a file called catalog-info.yaml, which contains a Backstage Entity of type Location:

    apiVersion: backstage.io/v1alpha1
    kind: Location
    metadata:
      name: iac-management-files
      description: IAC systems collection
    spec:
      targets:
        - .backstage/*/*.yaml

This tells Backstage to scan the .backstage directory for Catalog Entities.

Next steps

Now that Control Tower OUs and accounts can be provisioned via GutHub actions, it's time to provision resources into the accounts via Backstage.
Go see the repo aws_environments to learn how to configure resources for environments.