Generally, when using HashiCorp Terraform to deploy and manage Amazon AWS resources, the Terraform project will be working with AWS resources in only a single AWS Account. However, it’s common to structure cloud deployments across multiple Amazon AWS Accounts in certain use cases. This could be to separate DEV and PROD environments, or to separate cloud resource managements across multiple AWS Accounts for several other reasons. Regardless of the actual reason to use multiple AWS Accounts, it really is possible to use a single Terraform project to manage all the cloud resources across multiple AWS Accounts without the need to split things out into multiple Terraform projects.
Let’s take a look at configuring the AWS Terraform Provider to enable the deployment and management of AWS resources across multiple AWS Accounts from a single Terraform project!
Table of Contents
Define Multiple AWS Providers
At these times, you will need to support multiple instances of the aws
Terraform Provider, each targeting a different AWS Account, within a single Terraform project. This can be done by using the alias
attribute on the provider
declaration in Terraform. This enables the ability to have multiple aws
providers configured for different AWS Account credentials.
This may become necessary when you have separate AWS Accounts, or even just separate AWS IAM credentials, that need to be used to deploy and manage specific Amazon AWS resources. Maybe the resources are managed across the different accounts, or maybe different security credentials are used for specific resource deployment needs. Either way, this technique will help you get the Terraform written the way you need it within a single Terraform project.
The following is an example of 2 different provider
blocks for the aws
Terraform provider with the alias
attribute set to differentiate between them since they are configured for different AWS Accounts:
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.0"
}
}
provider "aws" {
alias = "dev"
access_key = var.dev_aws_key
secret_key = var.dev_aws_secret
region = "us-east-1"
}
provider "aws" {
alias = "prod"
access_key = var.prod_aws_key
secret_key = var.prod_aws_secret
region = "us-west-2"
}
}
Keep in mind the previous example uses Input Variables passed to the Terraform project to define the values that are used to configure the AWS Accounts for each aws
Terraform provider instance.
Deploy to Multiple AWS Accounts within Terraform Project
Once you have multiple aws
Terraform providers configured in the Terraform project with the alias
attribute set on each, you can use the provider
attribute on resource
blocks to explicitly specific which provider to use for deploying each resource.
The following is an example of code from a single Terraform project that will deploy resources to multiple AWS Accounts using the aws
providers from the previous example:
resource aws_s3_bucket "dev_bucket_1" {
provider = "dev"
bucket = "my-tf-dev-bucket-1"
}
resource aws_s3_bucket "prod_bucket_1" {
provider = "prod"
bucket = "my-tf-prod-bucket-1"
}
With the resource.provider
attribute set, you’re telling the Terraform code which specific Terraform provider to use for deploying and managing that AWS resource. By defining multiple aws
providers with the alias
attribute, you are able to set up multiple instances of the aws
provider to be used within a single Terraform provider. This is supported for the aws
provider, as well as any other Terraform provider.
Passing Terraform Provider Alias to Modules
When writing Terraform Modules, for code reuse and organization within the Terraform project, the code within the module may be coded to just use the “default” aws
provider; without any provider.alias
expected. In these cases, you can explicitly set, or override, which aws
Terraform provider from the Terraform project will be used as the “default” within the Terraform Module.
This is done by using the providers
attribute on a module
block to explicitly configure the Terraform Module for which Terraform providers to pass it.
The following is an example of using a Terraform Module within a project and passing different aws
provider instances to it as the “default” provider. This will enable the same module, without code changes, to be used with multiple AWS Accounts within a single Terraform project.
module "sample-dev" {
source = "./sample-module"
providers = {
aws = aws.dev
}
}
module "sample-prod" {
source = "./sample-module"
providers = {
aws = aws.prod
}
}
When using the module.providers
attribute to tell the Terraform Module which Terraform provider instance to use, you are defining it using an object syntax that allows for multiple providers. This enables you to explicitly specify all the different Terraform providers that module needs to use, in case it’s coded for multiple providers as well.
Deploy to Multiple AWS Accounts with a “Default” aws
Provider
The previous examples all used named aws
providers using the alias
attribute on each provider. It is possible, and probably best practice, to use one aws
Terraform provider instance as the “default”, and then use the alias
attribute to configure multiple other providers to be used within the Terraform project as well.
The following it an alternative example that defines the dev
AWS provider instance as the “default” for the Terraform project, while also defining the prod
AWS provider instance as a named instance using the alias
attribute.
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.0"
}
}
# This is the "default" provider
provider "aws" {
access_key = var.dev_aws_key
secret_key = var.dev_aws_secret
region = "us-east-1"
}
# This provider uses the "prod" alias
provider "aws" {
alias = "prod"
access_key = var.prod_aws_key
secret_key = var.prod_aws_secret
region = "us-west-2"
}
}
# Use the "default" aws provider
resource aws_s3_bucket "dev_bucket_1" {
bucket = "my-tf-dev-bucket-1"
}
module "sample-dev" {
source = "./sample-module"
}
# Use the "prod" aliased aws provider
resource aws_s3_bucket "prod_bucket_1" {
provider = "prod"
bucket = "my-tf-prod-bucket-1"
}
module "sample-dev" {
source = "./sample-module"
providers = {
aws = aws.prod
}
}
Happy Terraform-ing!