
Secrets Manager Now Supports Cross-Account Replication, So I Tried It Out
This page has been translated by machine translation. View original
Introduction
On November 13, 2026, AWS Secrets Manager added support for cross-account replication.
This feature synchronizes a primary secret to a replica in a different account. Because the replica uses a KMS key on the replica side, you can still read the replica even when the primary secret or the primary-side KMS key is unavailable.
This time, using two accounts, I tried setting up replication, propagating updates from the primary, the behavior when the permissions on the primary-side KMS key are removed, and disassociation and promotion.
Setup
I used AWS CLI 2.40.3. The region is ap-northeast-1. Both accounts are member accounts, and I specified --replica-restriction NoRestriction for the association. The primary secret is encrypted with a customer managed key (CMK) in the primary-side account.
| Where to configure | What to configure | Result if missing |
|---|---|---|
| Key policy of the replica-side CMK | Allow kms:Encrypt to the primary-side account | The association is rejected with AccessDeniedException |
| Key policy of the primary-side CMK | Allow kms:Decrypt to the replication role | Replication becomes Failed when the primary is updated |
| Resource policy of the replica | Attached automatically by Secrets Manager (you do not write it yourself) | — |
Replica side
In the replica-side account, create a CMK. The key policy at creation has just one statement that allows kms:* to the account root, and I saved it as replica-key-policy-initial.json.
aws --profile replica --region ap-northeast-1 kms create-key --description xacct-demo\ replica --tags TagKey=Name\,TagValue=xacct-demo-replica --policy file://replica-key-policy-initial.json
Next, create the replica by specifying the primary's ARN in --cross-account-primary-arn. No secret value is passed at creation.
aws --profile replica --region ap-northeast-1 secretsmanager create-secret --name xacct-demo-replica-secret --cross-account-primary-arn arn:aws:secretsmanager:ap-northeast-1:111122223333:secret:xacct-demo-primary-secret-AbCdEf --kms-key-id arn:aws:kms:ap-northeast-1:123456789012:key/abcd1234-ab12-cd34-ef56-abcdef123456 --tags Key=Name\,Value=xacct-demo-replica
Before association, the replica's replication status is Pending and it has no value.
The replica's resource policy was attached by Secrets Manager at the time of create-secret. If you run get-resource-policy with the policy name cross-account-replication, the body is returned.
aws --profile replica --region ap-northeast-1 secretsmanager get-resource-policy --secret-id xacct-demo-replica-secret --policy-name cross-account-replication
I have unescaped the ResourcePolicy string so that it can be read as JSON.
{
"Version" : "2012-10-17",
"Statement" : [ {
"Sid" : "CrossAccountReplication",
"Effect" : "Allow",
"Principal" : {
"AWS" : "arn:aws:iam::111122223333:root"
},
"Action" : "secretsmanager:AcceptCrossAccountReplication",
"Resource" : "*",
"Condition" : {
"ForAnyValue:StringEquals" : {
"aws:InvokedBy" : "secretsmanager.amazonaws.com"
},
"ArnEquals" : {
"secretsmanager:CrossAccountPrimaryARN" : "arn:aws:secretsmanager:ap-northeast-1:111122223333:secret:xacct-demo-primary-secret-AbCdEf"
}
}
} ]
}
Finally, add a kms:Encrypt permission for the primary-side account to the key policy of the replica-side CMK. Since the condition contains the replica's ARN, run this after creating the replica. Because put-key-policy replaces the entire key policy, replica-key-policy.json also includes the account root permission.
replica-key-policy.json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "EnableIAMPolicies",
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::123456789012:root" },
"Action": "kms:*",
"Resource": "*"
},
{
"Sid": "AllowCrossAccountReplication",
"Effect": "Allow",
"Principal": "*",
"Action": "kms:Encrypt",
"Resource": "*",
"Condition": {
"StringEquals": {
"kms:CallerAccount": "111122223333",
"kms:EncryptionContext:SecretARN": "arn:aws:secretsmanager:ap-northeast-1:123456789012:secret:xacct-demo-replica-secret-GhIjKl"
}
}
}
]
}
aws --profile replica --region ap-northeast-1 kms put-key-policy --key-id arn:aws:kms:ap-northeast-1:123456789012:key/abcd1234-ab12-cd34-ef56-abcdef123456 --policy-name default --policy file://replica-key-policy.json
Primary side
In the primary-side account, create the replication role. The role's permissions policy allows only kms:Encrypt on the replica-side CMK, and I placed the kms:Decrypt permission only in the key policy of the primary-side CMK.
Replication role definition
role-trust.json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": { "Service": "secretsmanager.amazonaws.com" },
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": { "aws:SourceAccount": "111122223333" }
}
}
]
}
role-permissions.json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "EncryptReplicaSecretKey",
"Effect": "Allow",
"Action": "kms:Encrypt",
"Resource": "arn:aws:kms:ap-northeast-1:123456789012:key/abcd1234-ab12-cd34-ef56-abcdef123456",
"Condition": {
"StringEquals": { "kms:EncryptionContext:SecretARN": "arn:aws:secretsmanager:ap-northeast-1:123456789012:secret:xacct-demo-replica-secret-GhIjKl" }
}
}
]
}
aws --profile primary --region ap-northeast-1 iam create-role --role-name xacct-demo-replication-role --assume-role-policy-document file://role-trust.json
aws --profile primary --region ap-northeast-1 iam put-role-policy --role-name xacct-demo-replication-role --policy-name replication --policy-document file://role-permissions.json
Add a kms:Decrypt permission for the role to the key policy of the primary-side CMK. Here too, replace it with the full text including the account root permission.
primary-key-policy.json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "EnableIAMPolicies",
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::111122223333:root" },
"Action": "kms:*",
"Resource": "*"
},
{
"Sid": "AllowReplicationRoleDecrypt",
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::111122223333:role/xacct-demo-replication-role" },
"Action": "kms:Decrypt",
"Resource": "*",
"Condition": {
"StringEquals": { "kms:EncryptionContext:SecretARN": "arn:aws:secretsmanager:ap-northeast-1:111122223333:secret:xacct-demo-primary-secret-AbCdEf" }
}
}
]
}
aws --profile primary --region ap-northeast-1 kms put-key-policy --key-id arn:aws:kms:ap-northeast-1:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab --policy-name default --policy file://primary-key-policy.json
Association
The association is performed from the primary-side account with associate-cross-account-replica. The IAM permissions required for the operator are described in the API reference. In this article, I ran it with administrator permissions.
aws --profile primary --region ap-northeast-1 secretsmanager associate-cross-account-replica --secret-id xacct-demo-primary-secret --cross-account-replica-arn arn:aws:secretsmanager:ap-northeast-1:123456789012:secret:xacct-demo-replica-secret-GhIjKl --replication-role-arn arn:aws:iam::111122223333:role/xacct-demo-replication-role --replica-restriction NoRestriction
After running it, the association started.
"ReplicationStatus": "InProgress"
Before reading the value, confirm that both sides are InSync with describe-secret.
Primary side:
"CrossAccountReplicas": [
{
"ReplicaSecretARN": "arn:aws:secretsmanager:ap-northeast-1:123456789012:secret:xacct-demo-replica-secret-GhIjKl",
"ReplicationRoleARN": "arn:aws:iam::111122223333:role/xacct-demo-replication-role",
"ReplicationStatus": "InSync"
}
]
Replica side:
"CrossAccountPrimary": {
"PrimarySecretARN": "arn:aws:secretsmanager:ap-northeast-1:111122223333:secret:xacct-demo-primary-secret-AbCdEf",
"ReplicationStatus": "InSync"
}
After it became InSync, running get-secret-value in the replica-side account returned the value. The VersionId is the same as the VersionId returned when the primary was created.
"VersionId": "EXAMPLE-VERSION-V1",
"SecretString": "value-v1",
Only one replica can be associated with one primary.
Replication behavior
Propagating updates
I updated the primary with put-secret-value and measured twice how long it took until get-secret-value in the replica-side account returned the new value. The start point is immediately before the put-secret-value call, and the end point is the time the replica returned the new value. In both cases the new value was returned on the first read, so the values in the table are upper bounds on the time the propagation took.
| Update | Start | End | Start to end |
|---|---|---|---|
| value-v2 | 11:53:00.383Z | 11:53:01.393Z | about 1.01 seconds |
| value-v3 | 11:53:09.431Z | 11:53:10.389Z | about 0.96 seconds |
When the permissions on the primary-side CMK are removed
I removed the kms:Decrypt permission for the replication role from the key policy of the primary-side CMK, returning it to a state with only the account root permission (primary-key-policy-initial.json).
aws --profile primary --region ap-northeast-1 kms put-key-policy --key-id arn:aws:kms:ap-northeast-1:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab --policy-name default --policy file://primary-key-policy-initial.json
In that state, I updated the primary to value-v4 and checked the replication status and the replica's value.
| Point in time | Replication status | Replica's get-secret-value |
|---|---|---|
| Immediately after removing the permission | Replica InSync | value-v3 |
| About 15 seconds after updating to value-v4 | Failed on both sides | value-v3 |
| About 5 seconds after restoring the permission and re-running the association | Primary InSync | value-v4 |
Even with the permission removed, put-secret-value on the primary and get-secret-value on the replica succeeded. Only replication failed, and the primary-side StatusMessage is as follows.
"StatusMessage": "Replication failed: User: arn:aws:sts::111122223333:assumed-role/xacct-demo-replication-role/SecretsManagerCrossAccountReplication-123456789012 is not authorized to perform: kms:Decrypt on resource: arn:aws:kms:ap-northeast-1:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab because no identity-based policy allows the kms:Decrypt action (Service: AWSKMS; Status Code: 400; Error Code: AccessDeniedException; Request ID: <Request ID>; Proxy: null)"
The replica-side StatusMessage was Replication failed in the primary account.
Disassociation and promotion
State by operation
To stop replication, there are disassociate-cross-account-replica on the primary side and stop-replication-to-replica on the replica side.
aws --profile primary --region ap-northeast-1 secretsmanager disassociate-cross-account-replica --secret-id xacct-demo-primary-secret --cross-account-replica-arn arn:aws:secretsmanager:ap-northeast-1:123456789012:secret:xacct-demo-replica-secret-GhIjKl
aws --profile replica --region ap-northeast-1 secretsmanager stop-replication-to-replica --secret-id xacct-demo-replica-secret-2
I ran each operation on an InSync replica and summarized the results in the following table. Since only one replica can be associated with one primary, after disassociating from the primary side, I created and associated a second replica and tried the replica-side operation.
| Item checked | Primary side disassociate-cross-account-replica | Replica side stop-replication-to-replica |
|---|---|---|
| describe-secret of the primary | CrossAccountReplicas disappears | CrossAccountReplicas remains with Detached and Replication removed by the replica secret |
| describe-secret of the replica | Detached and Replication removed by the primary secret | CrossAccountPrimary disappears |
| put-secret-value on the replica | Rejected | Succeeds |
With either operation, subsequent updates to the primary do not reach the replica.
State after promotion
When I ran stop-replication-to-replica on the first replica, which had become Detached through the primary-side disassociation, the replica was promoted. After promotion, get-secret-value returned value-v4 and put-secret-value succeeded. The resource policy for replication was also gone.
When promoted from the replica side, a record of the Detached replica remains on the primary. To remove the record, run disassociate-cross-account-replica on the primary side.
aws --profile primary --region ap-northeast-1 secretsmanager disassociate-cross-account-replica --secret-id xacct-demo-primary-secret --cross-account-replica-arn arn:aws:secretsmanager:ap-northeast-1:123456789012:secret:xacct-demo-replica-secret-2-MnOpQr
Summary
I set up cross-account replication for Secrets Manager and was able to confirm the behavior of the replica.
Even if the secret or CMK in the primary-side account becomes unreadable due to ransomware damage, cross-account replication may allow you to restore the secret.
The aws-samples repository published a mechanism that copies secrets using Lambda and EventBridge, but this is now available as a Secrets Manager feature.
If you use Secrets Manager automatic rotation and the like, the value after rotation cannot be reproduced from elsewhere. Please make use of this as a safeguard against the risk of losing access to your secrets.
