Skip to main content

S3 Bucket Cross-Region Replication

Overview

This check verifies that Amazon S3 buckets have cross-region replication (CRR) configured. Cross-region replication automatically copies objects from a source bucket in one AWS Region to a destination bucket in a different Region.

Without CRR, your data exists only in a single region, making it vulnerable to regional outages or disasters.

Risk

If this check fails, your S3 data faces these risks:

  • Regional outages can make your data completely inaccessible
  • Longer recovery times during disasters since there is no replica to fail over to
  • Data loss exposure if corruption or accidental deletion occurs without a copy elsewhere
  • Service disruption for applications that depend on multi-region availability

Remediation Steps

Prerequisites

  • Access to the AWS Console with permissions to modify S3 buckets and create IAM roles
  • A destination bucket in a different AWS Region (you can create one during setup)
Required IAM permissions

To configure replication, your IAM user or role needs:

  • s3:PutReplicationConfiguration on the source bucket
  • s3:GetReplicationConfiguration on the source bucket
  • iam:PassRole to assign the replication role
  • s3:CreateBucket if creating a new destination bucket

AWS Console Method

  1. Open the S3 Console
  2. Click on your source bucket name
  3. Go to the Properties tab
  4. Scroll to Bucket Versioning and click Edit
    • Select Enable and click Save changes (versioning is required for replication)
  5. Go to the Management tab
  6. Scroll to Replication rules and click Create replication rule
  7. Enter a rule name (e.g., "Replicate to us-west-2")
  8. For Status, keep it set to Enabled
  9. Under Source bucket, choose Apply to all objects in the bucket (or filter by prefix/tags if needed)
  10. Under Destination:
    • Choose Browse S3 and select a bucket in a different region, or create a new one
    • Ensure the destination bucket also has versioning enabled
  11. Under IAM role, select Create new role (recommended)
  12. Click Save

AWS will create the necessary IAM role automatically. New objects uploaded after this point will replicate to the destination.

AWS CLI (optional)

Step 1: Enable versioning on the source bucket

aws s3api put-bucket-versioning \
--bucket <your-source-bucket> \
--versioning-configuration Status=Enabled \
--region us-east-1

Step 2: Create the replication IAM role

First, create the trust policy file:

cat > /tmp/trust-policy.json << 'EOF'
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "s3.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF

Create the role:

aws iam create-role \
--role-name S3ReplicationRole \
--assume-role-policy-document file:///tmp/trust-policy.json \
--region us-east-1

Step 3: Attach the replication policy

Create the permissions policy file (replace placeholders):

cat > /tmp/replication-policy.json << 'EOF'
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetReplicationConfiguration",
"s3:ListBucket"
],
"Resource": "arn:aws:s3:::<your-source-bucket>"
},
{
"Effect": "Allow",
"Action": [
"s3:GetObjectVersionForReplication",
"s3:GetObjectVersionAcl",
"s3:GetObjectVersionTagging"
],
"Resource": "arn:aws:s3:::<your-source-bucket>/*"
},
{
"Effect": "Allow",
"Action": [
"s3:ReplicateObject",
"s3:ReplicateDelete",
"s3:ReplicateTags"
],
"Resource": "arn:aws:s3:::<your-destination-bucket>/*"
}
]
}
EOF

Attach the policy:

aws iam put-role-policy \
--role-name S3ReplicationRole \
--policy-name S3ReplicationPolicy \
--policy-document file:///tmp/replication-policy.json \
--region us-east-1

Step 4: Configure replication

Create the replication configuration file:

cat > /tmp/replication-config.json << 'EOF'
{
"Role": "arn:aws:iam::<your-account-id>:role/S3ReplicationRole",
"Rules": [
{
"ID": "ReplicateAllObjects",
"Status": "Enabled",
"Priority": 1,
"Filter": {},
"Destination": {
"Bucket": "arn:aws:s3:::<your-destination-bucket>",
"StorageClass": "STANDARD"
},
"DeleteMarkerReplication": {
"Status": "Disabled"
}
}
]
}
EOF

Apply the configuration:

aws s3api put-bucket-replication \
--bucket <your-source-bucket> \
--replication-configuration file:///tmp/replication-config.json \
--region us-east-1
CloudFormation (optional)
AWSTemplateFormatVersion: '2010-09-09'
Description: S3 bucket with cross-region replication enabled

Parameters:
SourceBucketName:
Type: String
Description: Name of the source S3 bucket
DestinationBucketArn:
Type: String
Description: ARN of the destination bucket in another region

Resources:
ReplicationRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: s3.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: S3ReplicationPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- s3:GetReplicationConfiguration
- s3:ListBucket
Resource: !Sub 'arn:aws:s3:::${SourceBucketName}'
- Effect: Allow
Action:
- s3:GetObjectVersionForReplication
- s3:GetObjectVersionAcl
- s3:GetObjectVersionTagging
Resource: !Sub 'arn:aws:s3:::${SourceBucketName}/*'
- Effect: Allow
Action:
- s3:ReplicateObject
- s3:ReplicateDelete
- s3:ReplicateTags
Resource: !Sub '${DestinationBucketArn}/*'

SourceBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Ref SourceBucketName
VersioningConfiguration:
Status: Enabled
ReplicationConfiguration:
Role: !GetAtt ReplicationRole.Arn
Rules:
- Id: ReplicateAllObjects
Status: Enabled
Destination:
Bucket: !Ref DestinationBucketArn
StorageClass: STANDARD

Outputs:
SourceBucketArn:
Description: ARN of the source bucket
Value: !GetAtt SourceBucket.Arn
ReplicationRoleArn:
Description: ARN of the replication IAM role
Value: !GetAtt ReplicationRole.Arn

Deploy the stack:

aws cloudformation deploy \
--template-file template.yaml \
--stack-name s3-cross-region-replication \
--parameter-overrides \
SourceBucketName=<your-source-bucket> \
DestinationBucketArn=arn:aws:s3:::<your-destination-bucket> \
--capabilities CAPABILITY_IAM \
--region us-east-1
Terraform (optional)
variable "source_bucket_name" {
description = "Name of the source S3 bucket"
type = string
}

variable "destination_bucket_arn" {
description = "ARN of the destination bucket in another region"
type = string
}

# IAM role for S3 replication
resource "aws_iam_role" "replication" {
name = "${var.source_bucket_name}-replication-role"

assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
Service = "s3.amazonaws.com"
}
Action = "sts:AssumeRole"
}
]
})
}

resource "aws_iam_role_policy" "replication" {
name = "${var.source_bucket_name}-replication-policy"
role = aws_iam_role.replication.id

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

# Source bucket with versioning and replication
resource "aws_s3_bucket" "source" {
bucket = var.source_bucket_name
}

resource "aws_s3_bucket_versioning" "source" {
bucket = aws_s3_bucket.source.id
versioning_configuration {
status = "Enabled"
}
}

resource "aws_s3_bucket_replication_configuration" "replication" {
depends_on = [aws_s3_bucket_versioning.source]

role = aws_iam_role.replication.arn
bucket = aws_s3_bucket.source.id

rule {
id = "replicate-all-objects"
status = "Enabled"

destination {
bucket = var.destination_bucket_arn
storage_class = "STANDARD"
}
}
}

output "source_bucket_arn" {
description = "ARN of the source bucket"
value = aws_s3_bucket.source.arn
}

output "replication_role_arn" {
description = "ARN of the replication IAM role"
value = aws_iam_role.replication.arn
}

Apply the configuration:

terraform init
terraform apply -var="source_bucket_name=<your-source-bucket>" \
-var="destination_bucket_arn=arn:aws:s3:::<your-destination-bucket>"

Verification

After enabling replication, verify it is working:

  1. In the S3 Console, go to your source bucket
  2. Click the Management tab
  3. Under Replication rules, confirm your rule shows Enabled
  4. Upload a test file to the source bucket
  5. Wait a few minutes, then check the destination bucket for the replicated file
CLI verification

Check the replication configuration:

aws s3api get-bucket-replication \
--bucket <your-source-bucket> \
--region us-east-1

Expected output shows your replication rules with Status: Enabled.

Re-run the Prowler check to confirm remediation:

prowler aws --checks s3_bucket_cross_region_replication

Additional Resources

Notes

  • Versioning is required: Both source and destination buckets must have versioning enabled for replication to work.
  • Existing objects are not replicated: Only new objects uploaded after enabling replication will be copied. Use S3 Batch Replication to replicate existing objects.
  • Costs: You will incur S3 storage costs in both regions, plus data transfer costs for replication.
  • Replication lag: Objects typically replicate within 15 minutes, but can take longer for large files. Consider Replication Time Control (RTC) for stricter SLAs.
  • Delete markers: By default, delete markers are not replicated. Enable delete marker replication if you want deletes to sync across regions.
  • KMS-encrypted objects: If your objects use KMS encryption, additional configuration is required to replicate the encryption keys.