Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions multiregion-s3/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Multiregion S3 bucket
Creates a bi-directionally replicated S3 bucket between 2 regions with the same suffix.
144 changes: 144 additions & 0 deletions multiregion-s3/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
# S3 Buckets
resource "aws_s3_bucket" "buckets" {
for_each = toset([var.Region1, var.Region2])
region = each.value
bucket = "${var.BucketPrefix}-${each.value}"
}

# Enable versioning (required for replication)
resource "aws_s3_bucket_versioning" "versioning" {
for_each = toset([var.Region1, var.Region2])
region = each.value
bucket = aws_s3_bucket.buckets[each.value].id

versioning_configuration {
status = "Enabled"
}
}

resource "aws_iam_role" "replication" {
name = "${var.BucketPrefix}-replication-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "s3.amazonaws.com"
}
}
]
})
}

resource "aws_iam_policy" "replication" {
name = "${var.BucketPrefix}-replication-policy"

policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = [
"s3:GetReplicationConfiguration",
"s3:ListBucket"
]
Effect = "Allow"
Resource = [
for bucket in aws_s3_bucket.buckets : bucket.arn
]
},
{
Action = [
"s3:GetObjectVersionForReplication",
"s3:GetObjectVersionAcl",
"s3:GetObjectVersionTagging"
]
Effect = "Allow"
Resource = [
for bucket in aws_s3_bucket.buckets : "${bucket.arn}/*"
]
},
{
Action = [
"s3:ReplicateObject",
"s3:ReplicateDelete",
"s3:ReplicateTags"
]
Effect = "Allow"
Resource = [
for bucket in aws_s3_bucket.buckets : "${bucket.arn}/*"
]
}
]
})
}

resource "aws_iam_role_policy_attachment" "replication" {
role = aws_iam_role.replication.name
policy_arn = aws_iam_policy.replication.arn
}

# Replication configuration: Region1 -> Region2
resource "aws_s3_bucket_replication_configuration" "region1_to_region2" {
region = var.Region1
role = aws_iam_role.replication.arn
bucket = aws_s3_bucket.buckets[var.Region1].id

rule {
id = "replicate-to-${var.Region2}"
status = "Enabled"
priority = 1

filter {}

destination {
bucket = aws_s3_bucket.buckets[var.Region2].arn
storage_class = "STANDARD"
}

delete_marker_replication {
status = "Enabled"
}
}

depends_on = [aws_s3_bucket_versioning.versioning]
}

# Replication configuration: Region2 -> Region1
resource "aws_s3_bucket_replication_configuration" "region2_to_region1" {
region = var.Region2
role = aws_iam_role.replication.arn
bucket = aws_s3_bucket.buckets[var.Region2].id

rule {
id = "replicate-to-${var.Region1}"
status = "Enabled"
priority = 1

filter {}

destination {
bucket = aws_s3_bucket.buckets[var.Region1].arn
storage_class = "STANDARD"
}

delete_marker_replication {
status = "Enabled"
}
}

depends_on = [aws_s3_bucket_versioning.versioning]
}

output "buckets_info" {
description = "Map of bucket information by region"
value = {
for region, bucket in aws_s3_bucket.buckets : region => {
arn = bucket.arn
bucket_regional_domain_name = bucket.bucket_regional_domain_name
bucket_domain_name = bucket.bucket_domain_name
id = bucket.id
}
}
}
28 changes: 28 additions & 0 deletions multiregion-s3/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
data "aws_regions" "current" {
all_regions = true
}

variable "Region1" {
type = string
description = "Region for bucket 1"

validation {
condition = contains(data.aws_regions.current.names, var.Region1)
error_message = "Region1 must be a valid AWS region. Available regions: ${join(", ", data.aws_regions.current.names)}"
}
}

variable "Region2" {
type = string
description = "Region for bucket 2"

validation {
condition = contains(data.aws_regions.current.names, var.Region2)
error_message = "Region1 must be a valid AWS region. Available regions: ${join(", ", data.aws_regions.current.names)}"
}
}

variable "BucketPrefix" {
type = string
description = "Prefix for the bucket. The created bucket names will be in the form of BucketPrefix-Region."
}