24
votes

I've been looking for a way to be able to deploy to multiple AWS accounts simultaneously in Terraform and coming up dry. AWS has the concept of doing this with Stacks but I'm not sure if there is a way to do this in Terraform? If so what would be some solutions?

You can read more about the Cloudformation solution here.

2

2 Answers

33
votes

You can define multiple provider aliases which can be used to run actions in different regions or even different AWS accounts.

So to perform some actions in your default region (or be prompted for it if not defined in environment variables or ~/.aws/config) and also in US East 1 you'd have something like this:

provider "aws" {
  # ...
}

# Cloudfront ACM certs must exist in US-East-1
provider "aws" {
  alias  = "cloudfront-acm-certs"
  region = "us-east-1"
}

You'd then refer to them like so:

data "aws_acm_certificate" "ssl_certificate" {
  provider    = aws.cloudfront-acm-certs
  ...
}

resource "aws_cloudfront_distribution" "cloudfront" {
  ...
  viewer_certificate {
    acm_certificate_arn = data.aws_acm_certificate.ssl_certificate.arn
    ...
  }
}

So if you want to do things across multiple accounts at the same time then you could assume a role in the other account with something like this:

provider "aws" {
  # ...
}

# Assume a role in the DNS account so we can add records in the zone that lives there
provider "aws" {
  alias   = "dns"
  assume_role {
    role_arn     = "arn:aws:iam::ACCOUNT_ID:role/ROLE_NAME"
    session_name = "SESSION_NAME"
    external_id  = "EXTERNAL_ID"
  }
}

And refer to it like so:

data "aws_route53_zone" "selected" {
  provider     = aws.dns
  name         = "test.com."
}

resource "aws_route53_record" "www" {
  provider = aws.dns
  zone_id  = data.aws_route53_zone.selected.zone_id
  name     = "www.${data.aws_route53_zone.selected.name"
  ...
}

Alternatively you can provide credentials for different AWS accounts in a number of other ways such as hardcoding them in the provider or using different Terraform variables, AWS SDK specific environment variables or by using a configured profile.

3
votes

I would recommend also combining your solution with Terraform workspaces:

Named workspaces allow conveniently switching between multiple instances of a single configuration within its single backend. They are convenient in a number of situations, but cannot solve all problems.

A common use for multiple workspaces is to create a parallel, distinct copy of a set of infrastructure in order to test a set of changes before modifying the main production infrastructure. For example, a developer working on a complex set of infrastructure changes might create a new temporary workspace in order to freely experiment with changes without affecting the default workspace.

Non-default workspaces are often related to feature branches in version control. The default workspace might correspond to the "master" or "trunk" branch, which describes the intended state of production infrastructure. When a feature branch is created to develop a change, the developer of that feature might create a corresponding workspace and deploy into it a temporary "copy" of the main infrastructure so that changes can be tested without affecting the production infrastructure. Once the change is merged and deployed to the default workspace, the test infrastructure can be destroyed and the temporary workspace deleted.

AWS S3 is in the list of the supported backends.

It is very easy to use (similar to working with git branches) and combine it with the selected AWS account.

terraform workspace list
   dev
 * prod
   staging

A few references regarding configuring the AWS provider to work with multiple account:

  1. https://terragrunt.gruntwork.io/docs/features/work-with-multiple-aws-accounts/

  2. https://assets.ctfassets.net/hqu2g0tau160/5Od5r9RbuEYueaeeycUIcK/b5a355e684de0a842d6a3a483a7dc7d3/devopscon-V2.1.pdf