Version 6 of the AWS provider for Terraform is available in beta.
The biggest change in this version is that it supports multiple AWS regions in the same provider instance.
Previously you would instead do something like this to work with multiple (two) regions:
provider "aws" {
region = "eu-west-1"
}
provider "aws" {
region = "eu-west-2"
alias = "london"
}
resource "aws_s3_bucket" "source" {
bucket_prefix = "source"
}
resource "aws_s3_bucket" "destination" {
provider = aws.london
bucket_prefix = "destination"
}
The provider with no explicit alias is the default provider which will be used unless the provider argument is explicitly used to select a different provider alias.
In this blog post I will show how you can set up S3 bucket cross-region replication with version 6 of the AWS provider for Terraform.
Configure the provider#
Specify that your Terraform configuration will use the current beta version of the AWS provider (at the time of writing it is 6.0.0-beta2
):
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "6.0.0-beta2"
}
}
}
You can still include a default configuration for your provider if you wish, for instance you could add the following provider
block:
provider "aws" {
region = "eu-west-1"
# other configuration options left out ...
}
In the following I will skip configuring the provider with any defaults, just to highlight how I configure different regions for different resources. This, I leave my provider configuration empty (I could also have left it out completely):
provider "aws" {}
I want to set up replication between Ireland (eu-west-1
) and London (eu-west-2
). I define local values for the two regions:
locals {
source_region = "eu-west-1"
destination_region = "eu-west-2"
}
Configure the source bucket#
Resources that are regional now supports the region
argument. The source S3 bucket is configured as follows:
resource "aws_s3_bucket" "source" {
region = local.source_region
bucket_prefix = "source"
}
resource "aws_s3_bucket_versioning" "source" {
bucket = aws_s3_bucket.source.bucket
region = local.source_region
versioning_configuration {
status = "Enabled"
}
}
You must enable bucket versioning when setting up cross-region replication.
Configure the destination bucket#
The destination S3 bucket is configured in a similar fashion, with the notable difference being that it is placed in a different region:
resource "aws_s3_bucket" "destination" {
region = local.destination_region
bucket_prefix = "destination"
}
resource "aws_s3_bucket_versioning" "destination" {
bucket = aws_s3_bucket.destination.bucket
region = local.destination_region
versioning_configuration {
status = "Enabled"
}
}
Set up IAM resources#
S3 requires an IAM role that it can assume to perform the object replication:
data "aws_iam_policy_document" "assumerole" {
statement {
effect = "Allow"
principals {
type = "Service"
identifiers = ["s3.amazonaws.com"]
}
actions = ["sts:AssumeRole"]
}
}
resource "aws_iam_role" "replication" {
name_prefix = "s3-replication"
assume_role_policy = data.aws_iam_policy_document.assumerole.json
}
You must also create a replication IAM policy and attach it to the new IAM role:
data "aws_iam_policy_document" "replication" {
statement {
effect = "Allow"
actions = [
"s3:GetReplicationConfiguration",
"s3:ListBucket",
]
resources = [
aws_s3_bucket.source.arn,
]
}
statement {
effect = "Allow"
actions = [
"s3:GetObjectVersionForReplication",
"s3:GetObjectVersionAcl",
"s3:GetObjectVersionTagging",
]
resources = [
"${aws_s3_bucket.source.arn}/*",
]
}
statement {
effect = "Allow"
actions = [
"s3:ReplicateObject",
"s3:ReplicateDelete",
"s3:ReplicateTags",
]
resources = [
"${aws_s3_bucket.destination.arn}/*",
]
}
}
resource "aws_iam_policy" "replication" {
name_prefix = "replication"
policy = data.aws_iam_policy_document.replication.json
}
resource "aws_iam_role_policy_attachment" "replication" {
role = aws_iam_role.replication.name
policy_arn = aws_iam_policy.replication.arn
}
Configure the cross-region replication#
Finally, create the replication configuration:
resource "aws_s3_bucket_replication_configuration" "source_to_destination" {
bucket = aws_s3_bucket.source.bucket
role = aws_iam_role.replication.arn
region = local.source_region
rule {
status = "Enabled"
delete_marker_replication {
status = "Enabled"
}
filter {
prefix = "backup/"
}
destination {
bucket = aws_s3_bucket.destination.arn
}
}
depends_on = [
aws_iam_role_policy_attachment.replication,
aws_s3_bucket_versioning.source,
aws_s3_bucket_versioning.destination,
]
}
The important pieces of this configuration is to get the source and destination buckets correct. Also make sure to add a depends_on
for the IAM role as well as the versioning configurations for the two buckets. If you don’t, the apply might fail.
I have added a filter
that makes sure that only objects starting with backup/
will be replicated (e.g. backup/file.txt
or backup/2025/05/27/test.log
).
That’s it! All in all, the difference here is not substantial. When you work with more than two regions and you use many modules where you pass provider instances then you will start to notice more of a difference (for the better!)