The purpose of this post is to offer a detailed, step-by-step guide to setting up an example of S3 Cross-Account Replication between buckets.
Requirements
1- Two AWS accounts (Account A and Account B).
2- Two Amazon S3 buckets (one in each account).
3- Appropriate IAM policies for access control.
4- IAM roles with necessary trust relationships and permissions.
5- Terraform for infrastructure as code deployment.
Scenario
We have two AWS accounts: Account A (111111111111) and Account B (222222222222). Our objective is to replicate objects from the source bucket, gerx24-source-bucket (in Account A), to the destination bucket, gerx24-destination-bucket (in Account B), using Amazon S3 Cross-Account Replication.
Additionally, we want to apply a lifecycle policy to buckets to automatically expire replicated objects after 10 days.
Folder Structure
Terraform files [Module]
Reusable Terraform module for provisioning Amazon S3 buckets.
main.tf
module "s3_bucket" {
source = "terraform-aws-modules/s3-bucket/aws"
version = "4.9.0"
bucket = var.bucket_name
control_object_ownership = true
object_ownership = "BucketOwnerPreferred"
acl = "private"
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
versioning = {
status = "Enabled"
}
lifecycle_rule = var.lifecycle_rules
server_side_encryption_configuration = {
rule = {
apply_server_side_encryption_by_default = {
sse_algorithm = "AES256"
}
}
}
}
outputs.tf
output "s3_bucket_id" {
description = "Bucket ID"
value = module.s3_bucket.s3_bucket_id
}
output "s3_bucket_arn" {
description = "Bucket ARN"
value = module.s3_bucket.s3_bucket_arn
}
variables.tf
variable "environment" {
default = null
description = "Environment"
type = string
}
variable "bucket_name" {
default = null
description = "Bucket name"
type = string
}
variable "lifecycle_rules" {
default = {}
description = "Lifecycle rules object"
type = any
}
Terraform files [Source Bucket]
S3 Cross-Account Replication source bucket.
1- Creates bucket [gerx24-source-bucket].
2- Creates a Role replication_role [source-replication-role].
3- Creates replication_policy [source-replication-policy].
4- Creates replication_bucket_config and bind it with [gerx24-source-bucket] bucket.
5- Includes in [gerx24-source-bucket] a lifecycle_rules to expire files after 10 days.
main.tf
module "source_replication_bucket" {
source = "../module/bucket"
environment = "dev"
bucket_name = "gerx24-source-bucket"
lifecycle_rules = [
{
id = "Delete files older than 10 days"
enabled = true
expiration = {
days = 10
}
}
]
}
locals {
source_replication_bucket_arn = module.source_replication_bucket.s3_bucket_id
bucket_name = module.source_replication_bucket.s3_bucket_arn
destination_bucket_arn = "arn:aws:s3:::gerx24-destination-bucket"
source_replication_policy_name = "source-replication-policy"
source_replication_role_name = "source-replication-role"
common_tags = {
environment = "dev"
maintainer = "gerx24"
}
}
resource "aws_iam_role" "replication_role" {
name = local.source_replication_role_name
assume_role_policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Action = "sts:AssumeRole",
Principal = {
Service = "s3.amazonaws.com"
},
Effect = "Allow",
Sid = ""
}
]
})
tags = local.common_tags
}
resource "aws_iam_policy" "replication_policy" {
name = local.source_replication_policy_name
policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Effect = "Allow",
Action = [
"s3:GetReplicationConfiguration",
"s3:ListBucket"
],
Resource = [
"${local.source_replication_bucket_arn}/*",
"${local.source_replication_bucket_arn}",
]
},
{
Effect = "Allow",
Action = [
"s3:GetObjectVersionForReplication",
"s3:GetObjectVersionAcl",
"s3:GetObjectVersionTagging"
],
Resource = [
"${local.source_replication_bucket_arn}/*",
"${local.source_replication_bucket_arn}"
]
},
{
Effect = "Allow",
Action = [
"s3:ReplicateObject",
"s3:ReplicateDelete",
"s3:ReplicateTags",
"s3:ObjectOwnerOverrideToBucketOwner"
],
Resource = [
"${local.destination_bucket_arn}/*",
"${local.destination_bucket_arn}"
]
}
]
})
}
resource "aws_iam_role_policy_attachment" "replication" {
role = aws_iam_role.replication_role.name
policy_arn = aws_iam_policy.replication_policy.arn
}
resource "aws_s3_bucket_replication_configuration" "replication_bucket_config" {
depends_on = [module.source_replication_bucket]
role = aws_iam_role.replication_role.arn
bucket = local.bucket_name
rule {
id = "cross-replication"
delete_marker_replication {
status = "Disabled"
}
source_selection_criteria {
replica_modifications {
status = "Enabled"
}
}
filter {
prefix = ""
}
status = "Enabled"
destination {
bucket = local.destination_bucket_arn
storage_class = "STANDARD"
access_control_translation {
owner = "Destination"
}
account = "222222222222"
metrics {
status = "Enabled"
event_threshold {
minutes = 15
}
}
replication_time {
status = "Enabled"
time {
minutes = 15
}
}
}
}
}
provider.tf
provider "aws" {
region = "us-east-1"
default_tags {
tags = {
terraform = "true"
application = "replication"
environment = "dev"
}
}
}
Terraform files [Destination Bucket]
S3 Cross-Account Replication destination bucket.
1- Creates bucket [gerx24-destination-bucket].
2- Creates destination_bucket_replication_policy and attach it to [gerx24-destination-bucket] bucket.
main.tf
module "destination_replication_bucket" {
source = "../module/bucket"
bucket_name = "gerx24-destination-bucket"
environment = "dev"
lifecycle_rules = [
{
id = "Delete files older than 10 days"
enabled = true
expiration = {
days = 10
}
}
]
}
locals {
destination_bucket_name = module.destination_replication_bucket.s3_bucket_id
source_replication_role = "arn:aws:iam::111111111111:role/source-replication-role"
}
resource "aws_s3_bucket_policy" "destination_bucket_replication_policy" {
bucket = local.destination_bucket_name
policy = jsonencode({
Version = "2012-10-17"
Id = ""
Statement = [
{
Sid = "AllowReplicationfromSourceBucket"
Effect = "Allow"
Principal = {
AWS = local.source_replication_role
}
Action = [
"s3:ReplicateObject",
"s3:ReplicateDelete",
"s3:ReplicateTags",
"s3:ObjectOwnerOverrideToBucketOwner",
"s3:List*",
"s3:GetBucketVersioning",
"s3:PutBucketVersioning"
]
Resource = [
"arn:aws:s3:::${local.destination_bucket_name}/*",
"arn:aws:s3:::${local.destination_bucket_name}"
]
}
]
})
}
provider.tf
provider "aws" {
region = "us-east-1"
default_tags {
tags = {
terraform = "true"
application = "replication"
environment = "dev"
}
}
}
Top comments (0)