The Problem
You need every object written to a bucket in account A to appear in a bucket in account B, in a different Region, both sides encrypted with customer-managed KMS keys. Account B owns the destination bucket and its KMS key. Account A owns the replication role. Nothing works until every piece of policy on both sides agrees.
The Solution
Set up S3 replication on the source bucket with a rule that targets the destination bucket in account B. Wire up four things correctly:
- The IAM replication role in account A
- The source KMS key policy in account A
- The destination bucket policy in account B
- The destination KMS key policy in account B
Miss any one and replication silently fails.
How It Works
Prerequisites
- Versioning enabled on both buckets, since replication requires it
- Both buckets exist in their own accounts and Regions
- A KMS key in each account and each Region
The Replication Role (Account A)
Trust policy. S3 assumes this role to perform the replication:
1
2
3
4
5
6
7
8
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "Service": "s3.amazonaws.com" },
"Action": "sts:AssumeRole"
}]
}
Permissions policy. Read from source, write to destination, decrypt source KMS, encrypt with destination KMS:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetReplicationConfiguration",
"s3:ListBucket",
"s3:GetObjectVersionForReplication",
"s3:GetObjectVersionAcl",
"s3:GetObjectVersionTagging"
],
"Resource": [
"arn:aws:s3:::source-hadzimahmutovic",
"arn:aws:s3:::source-hadzimahmutovic/*"
]
},
{
"Effect": "Allow",
"Action": [
"s3:ReplicateObject",
"s3:ReplicateDelete",
"s3:ReplicateTags"
],
"Resource": "arn:aws:s3:::dest-clearview-team/*"
},
{
"Effect": "Allow",
"Action": "kms:Decrypt",
"Resource": "arn:aws:kms:eu-west-1:111111111111:key/source-key-id",
"Condition": {
"StringLike": {
"kms:ViaService": "s3.eu-west-1.amazonaws.com",
"kms:EncryptionContext:aws:s3:arn": "arn:aws:s3:::source-hadzimahmutovic/*"
}
}
},
{
"Effect": "Allow",
"Action": "kms:Encrypt",
"Resource": "arn:aws:kms:us-east-1:222222222222:key/dest-key-id",
"Condition": {
"StringLike": {
"kms:ViaService": "s3.us-east-1.amazonaws.com",
"kms:EncryptionContext:aws:s3:arn": "arn:aws:s3:::dest-clearview-team/*"
}
}
}
]
}
The Destination Bucket Policy (Account B)
Account B must explicitly grant the replication role from account A permission to write replicas:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::111111111111:role/S3ReplicationRole"
},
"Action": [
"s3:ReplicateObject",
"s3:ReplicateDelete",
"s3:ReplicateTags",
"s3:ObjectOwnerOverrideToBucketOwner"
],
"Resource": "arn:aws:s3:::dest-clearview-team/*"
}]
}
The Destination KMS Key Policy (Account B)
Grant kms:Encrypt (and helper actions) to the replication role from account A:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"Sid": "AllowSourceReplicationRole",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::111111111111:role/S3ReplicationRole"
},
"Action": [
"kms:Encrypt",
"kms:GenerateDataKey",
"kms:ReEncrypt*",
"kms:DescribeKey"
],
"Resource": "*"
}
The Source KMS Key Policy (Account A)
Grant kms:Decrypt to the replication role. If the role is in the same account, an IAM policy is usually enough, but adding an explicit key policy statement removes doubt:
1
2
3
4
5
6
7
8
9
{
"Sid": "AllowReplicationRoleDecrypt",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::111111111111:role/S3ReplicationRole"
},
"Action": "kms:Decrypt",
"Resource": "*"
}
The Replication Configuration
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
aws s3api put-bucket-replication \
--bucket source-hadzimahmutovic \
--replication-configuration '{
"Role": "arn:aws:iam::111111111111:role/S3ReplicationRole",
"Rules": [{
"ID": "replicate-to-b",
"Status": "Enabled",
"Priority": 1,
"Filter": { "Prefix": "" },
"DeleteMarkerReplication": { "Status": "Enabled" },
"Destination": {
"Bucket": "arn:aws:s3:::dest-clearview-team",
"Account": "222222222222",
"AccessControlTranslation": { "Owner": "Destination" },
"EncryptionConfiguration": {
"ReplicaKmsKeyID": "arn:aws:kms:us-east-1:222222222222:key/dest-key-id"
}
},
"SourceSelectionCriteria": {
"SseKmsEncryptedObjects": { "Status": "Enabled" }
}
}]
}'
AccessControlTranslation transfers ownership to account B. Without it, account B owns the bucket but account A owns the replicated objects, which breaks downstream access.
Verifying Replication
Upload a test object and check the replication status:
1
2
3
4
aws s3api head-object \
--bucket source-hadzimahmutovic \
--key test.txt \
--query ReplicationStatus
Values: PENDING, COMPLETED, FAILED. Enable S3 Replication Time Control (RTC) if you need a 15-minute SLA and per-object metrics in CloudWatch.
Why Not the Alternatives?
Same-account replication only — Solves nothing for the cross-account requirement. The destination bucket owner and the source bucket owner must be able to enforce policy independently.
Client-side script that copies objects — Fragile, no atomic ordering, no replication metrics, no delete marker handling, no retry on transient failures.
AWS DataSync between the two buckets — Batch and scheduled, not continuous. Not the right tool for real-time replication.
Multi-Region access points without replication — MRAPs route requests, they do not create replicas. You still need replication underneath if you want physical copies.
Key Takeaways
- Cross-account replication with KMS needs four aligned policies: role trust, role permissions, destination bucket policy, and both KMS key policies
- Use
AccessControlTranslationto give the destination account ownership of the replicas - Enable
SseKmsEncryptedObjectsinSourceSelectionCriteriaso KMS-encrypted objects actually replicate - Add S3 Replication Time Control for a 15-minute replication SLA with CloudWatch metrics
- Check
ReplicationStatuson the source object to confirm the flow works end to end
Never miss a story from us, subscribe to our newsletter