Terraform setup, Providers and Multi-Region Deployments
Setting up Terraform providers and profiles for multi-region deployments
Updated April, 2024
This is part of a collection of articles covering an Introduction to Advanced Terraform, it is deliberately of a certain style, you can find out more in the first post.
Introduction
Terraform is a very widely used Infrastructure as Code (IaC) tool, there is good reason for this. It's fairly easy to read (as far as code goes), so you should be able to understand at least some of what you're reading without really knowing much about it, and it's easy to steal a bit of code off GitHub or some blog post like this one and get it to work for you. It allows you to define and provision infrastructure using a declarative configuration language. Declarative just means you define what you want, and Terraform figures out how to make it happen.
Multi-Region deployments may not be important to you right now, but you might not know if that will still be the case a few years from now, and changing from code that isn't named appropriately and doesn't filter by region to code that does could be a chore you don't need, so I recommend getting this set up from the off, even if you don't plan on using it straight away.
In this article I'll give you an example setup for Terraform providers and profiles that you could use for a multi-region deployment.
Setting up your provider
I was once told that I should make my default profile into something bogus, so you don't mistakenly run code on the wrong account, this is great advice, and I've done it ever since, it forces you to set a named profile when running your code which I think is a great habit to get into.
To set up your profile, you can use the AWS CLI, but you don't need to. You can just create files at ~/.aws/credentials
and ~/.aws/config
and add your profiles in there. Here's an example of what those files might look like:
[default]
aws_access_key_id = SOME_BOGUS_ACCESS_KEY
aws_secret_access_key = SOME_BOGUS_SECRET_KEY
[arryw-dev]
aws_access_key_id = YOUR_ACCESS_KEY
aws_secret_access_key = YOUR_SECRET
You can see that I have a default profile, and a arryw-dev
profile. The default profile is bogus, the arryw-dev
profile is the one I want to use. You can add as many profiles as you like, and you can name them whatever you like, but they mustn't contain any spaces or special characters.
You can also set up your region in the ~/.aws/config
file, and I always set things to output in json, here's an example of what that file might look like:
[default]
region = eu-west-1
output = json
[arryw-dev]
region = eu-west-1
output = json
Terraform main.tf and Providers
To start with you should have a main.tf
file in your Terraform project. This is the main entry point for your Terraform configuration. In this file you define the providers that you want to use.
Terraform providers are the plugins that Terraform uses to interact with the APIs of the various cloud providers. You can think of them as the drivers that Terraform uses to talk to the cloud. You can see a list of all the providers that Terraform supports here.
To use a provider in your Terraform configuration, you need to define it. Here's an example of how you would define the AWS provider:
terraform {
required_version = ">= 1.7.5"
}
provider "aws" {
region = "eu-west-1"
shared_credentials_files = ["~/.aws/credentials"]
profile = "arryw-dev"
}
provider "aws" {
alias = "dublin"
region = "eu-west-1"
profile = "arryw-dev"
}
provider "aws" {
alias = "virginia"
region = "us-east-1"
profile = "arryw-dev"
}
provider "aws" {
alias = "mumbai"
region = "ap-south-1"
profile = "arryw-dev"
}
Using your providers
You can add some other terraform code now if you want, or you could just test things out by running terraform init
and terraform plan
to see if your providers are working as expected.
terraform init
terraform plan
You should see that Terraform is able to connect to the AWS API and that it doesn't want to create any resources.
Now that you have defined your providers, the following is an example of how you could deploy an EC2 instance in each region.
Note that the provider
attribute is used to specify which provider to use for each resource. this attribute cannot be dynamically set, a block of terraform code can only use one provider, this is why it's important to set up your code blocks and name them appropriately. The resource identifier will be used to identify the resource in the state file, this is the part that, if you need to change it in the future, could cause headaches that could have been avoided.
resource "aws_instance" "example_dublin" {
provider = aws.dublin
ami = "ami-0d812345cbexample"
instance_type = "t4g.small"
tags = {
Name = "example-dublin"
}
}
resource "aws_instance" "example_virginia" {
provider = aws.virginia
ami = "ami-0c55b15example1f0"
instance_type = "t4g.small"
tags = {
Name = "example-virginia"
}
}
MFA, Role switching & temporary credentials
If you're authenticating to one account and then switching roles to another account, chances are you're probably using MFA too (if you're not, you should be). When this is the case there are a couple of extra steps and some more entries in your ~/.aws/config
and ~/.aws/credentials
files.
Here are the steps you should take:
- Generate temporary credentials, replacing account number/token code and user/profile name etc
$ aws sts get-session-token --serial-number arn:aws:iam::999999999999:mfa/me@arrywalker.com --profile arryw-dev --duration-seconds 129600 --token-code 999999
- Add new profiles to your
~/.aws/credentials
~/.aws/credentials
[arryw-iam]
aws_access_key_id = YOUR_ACCESS_KEY
aws_secret_access_key = YOUR_SECRET
[arryw-iam-temp]
aws_access_key_id = ASIAINVALIDINVALID5A
aws_secret_access_key = ExINVALIDINVALIDINVALIDINVALIDVXJC+BYnSH
aws_session_token = 292~ChaRacTerS|Of/$tuff
- Add a new profile(s) to your
~/.aws/config
file, referencing the temporary credentials you created in the previous step.
~/.aws/config
[profile arryw-staging-temp]
role_arn = arn:aws:iam::111111111111:role/ARRYW-STAGING-ROLE
source_profile = arryw-iam-temp
[profile arryw-prod-temp]
role_arn = arn:aws:iam::222222222222:role/ARRYW-PROD-ROLE
source_profile = arryw-iam-temp
- Use one of the new profiles as your base profile in your Terraform code. note that the aliased providers wouldn't change in this scenario.
provider "aws" {
region = "eu-west-1"
shared_credentials_files = ["~/.aws/credentials"]
profile = "arryw-staging-temp"
}
Conclusion
And there you have it, there are other situations where you might need to set up your providers differently, like SSO for instance, in that case you wouldn't normally switch roles once authenticated, so you would still use temporary credentials but you would have a temp credentials entry for each account you need to access, and you would set up your providers to use the appropriate profile for each account.