31

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.

ydaetskcoR
  • 53,225
  • 8
  • 158
  • 177
ehime
  • 8,025
  • 14
  • 51
  • 110
  • https://www.terraform.io/docs/configuration/providers.html#multiple-provider-instances – Moe Sep 06 '18 at 14:54

2 Answers2

47

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.

ydaetskcoR
  • 53,225
  • 8
  • 158
  • 177
  • awesome, you nailed it. I've been using a bash script up till now to rotate AWS keys and secrets and its hacky at best.... Plus 1, accepted and starred ;) – ehime Sep 06 '18 at 15:04
  • In terraform 0.12 or later, can use `provider = aws.dns` i.e. no quotes - which are deprecated. – Jon Burgess Jul 14 '21 at 00:40
  • 1
    @JonBurgess thanks for the comment. I've updated the answer to use HCL2 syntax now. In general I'm slowly going back through old answers and updating them to HCL2 where appropriate now that 0.12 came out more than 2 years ago and 1.0 is also out now but largely only where someone suggests it. Feel free to suggest HCL2 syntax edits on any old answers you see. – ydaetskcoR Jul 14 '21 at 11:20
  • What if I want to do this in a loop (for_each)? – Gill Bates Aug 03 '21 at 09:20
  • @GillBates you should ask a separate question about that and link back to this one as a reference. Make sure to show what you've tried so far (ideally as a [mcve] and any errors you got to help people be able to point you in the right direction. – ydaetskcoR Aug 03 '21 at 09:58
6

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

Rot-man
  • 18,045
  • 12
  • 118
  • 124