Sharing Secrets Across an AWS Organization with SSM Parameter Store and Secrets Manager: RAM, KMS, and Regional Replication

A security agent baked into a golden Amazon Machine Image (AMI) often needs a registration token when a new Amazon EC2 instance first boots. The same problem appears with third-party license keys, package-repository credentials, webhook signing keys, and bootstrap credentials for shared internal services. The value is centrally governed, but workloads in many AWS accounts and AWS Regions must retrieve it without making it public, copying it into an AMI, or maintaining an unbounded list of account IDs.

AWS Systems Manager Parameter Store and AWS Secrets Manager can both provide this capability. Their architectures are not interchangeable, however. Cross-account authorization, encryption, resource discovery, regional availability, rotation, service integrations, quotas, and billing differ in ways that only become obvious when the complete access path is exercised.

This article builds and tests both designs. It uses only AWS CLI examples, anonymized identifiers, and synthetic secret values. The intended reader already understands IAM, AWS Organizations, AWS Key Management Service (AWS KMS), and regional AWS service endpoints.

Executive recommendation

Treat cross-account authorization and cross-Region availability as separate design problems:

  • Use Secrets Manager when the value is a true secret that benefits from managed rotation, native same-account regional replication, larger values, or broader service integrations.
  • Use Parameter Store Advanced parameters when the value is relatively static, no larger than 8 KB, the supported integrations are sufficient, and the platform team is prepared to own cross-Region replication and reconciliation.
  • Prefer per-account, per-environment, or per-site credentials over one organization-wide credential. If the external control plane supports AWS identity attestation or short-lived registration credentials, use that instead of distributing a static shared secret.
  • Never bake the secret into an AMI. Bake only nonsecret lookup metadata—such as a parameter ARN, secret ARN, or site identifier—and retrieve the value at first boot with the EC2 instance role.

For a registration-token use case with three security domains—for example, US production, US non-production, and international—create at least three tokens and scope each token to the corresponding OUs and workload roles. A rarely rotated token is still a bearer credential; segmentation limits the impact of disclosure and simplifies revocation.

Decision factor SSM Parameter Store Secrets Manager
Cross-account mechanism AWS Resource Access Manager (AWS RAM), backed by a parameter resource policy Secret resource policy; Secrets Manager is not shared through RAM
Shareable tier Advanced only All secrets, subject to policy and KMS requirements
Cross-account encryption Customer-managed KMS key required Customer-managed KMS key required
Native cross-Region replication No Yes, within the same AWS account
Rotation Custom Managed rotation for supported services or Lambda-based rotation
Maximum value size 8 KB for Advanced 65,536 bytes
Consumer lookup Full parameter ARN Full secret ARN
Hierarchical retrieval of shared resources GetParametersByPath is not in the RAM permissions Not applicable
Base storage price in US Regions USD 0.05 per Advanced parameter-month USD 0.40 per secret-month; every replica is another billed secret
Best fit Relatively static bootstrap values and configuration Credentials with lifecycle, rotation, or regional-resilience requirements

Prices and quotas change. Verify the linked AWS service pages before making a production cost decision.

First question: should this be one shared secret?

Central storage does not make a broadly shared bearer token low risk. If every workload in an organization receives the same token, compromise of one workload can expose a credential valid everywhere. Rotation also becomes an organization-wide event.

Evaluate alternatives in this order:

  1. AWS identity attestation or federation. The external system validates a signed AWS identity or an assumed role instead of accepting a static token.
  2. One-time bootstrap credentials. A narrowly authorized broker exchanges AWS identity for a short-lived, single-use registration credential.
  3. Per-account, per-environment, or per-site tokens. A compromised non-production token cannot register production workloads.
  4. One organization-wide static token. Use only when the control plane cannot support a narrower model, and document the resulting blast radius.

The storage service cannot compensate for an over-broad credential. Parameter Store and Secrets Manager control who can retrieve the token; they do not restrict what a caller can do after retrieving it.

The four gates in a cross-account read

A successful read from another AWS account must pass four independent gates:

  1. Consumer identity authorization. The workload role needs ssm:GetParameter or secretsmanager:GetSecretValue, plus kms:Decrypt for the owner account’s KMS key.
  2. Resource-side authorization. Parameter Store uses a RAM share/resource policy. Secrets Manager uses a resource policy attached to the secret.
  3. KMS authorization. The owner account’s KMS key policy must permit the external principal, and the external principal’s IAM policy must delegate the corresponding KMS permission.
  4. Regional routing. The AWS CLI or SDK client must call the endpoint in the Region encoded in the resource ARN. An ARN does not cause an AWS service endpoint to route the request to another Region.

An SCP, permissions boundary, session policy, VPC endpoint policy, or explicit deny can still reject the request after these allows are present.

Four authorization gates for cross-account secret retrieval

The examples in this article use the following anonymized values:

export ORG_MANAGEMENT_ACCOUNT_ID="999900001111"
export SECRET_OWNER_ACCOUNT_ID="111122223333"
export CONSUMER_ACCOUNT_ID="444455556666"

export ORG_ID="o-a1b2c3d4e5"
export ROOT_ID="r-abcd"
export TARGET_OU_ID="ou-abcd-12345678"

export PRIMARY_REGION="us-east-1"
export REPLICA_REGION="us-west-2"

export OWNER_PROFILE="secret-owner"
export CONSUMER_PROFILE="workload"
export ORG_PROFILE="org-management"

export PARAMETER_NAME="/org/bootstrap/security-agent/registration-token"
export SECRET_NAME="org/bootstrap/security-agent/registration-token"

The profile names are local AWS CLI profiles. They are not IAM principals and do not appear in policies.

Establish the AWS Organizations and RAM prerequisite

Sharing a parameter with an organization or OU requires RAM integration with AWS Organizations. Enable it once from the Organizations management account:

aws ram enable-sharing-with-aws-organization \
  --profile "${ORG_PROFILE}" \
  --region "${PRIMARY_REGION}"

This creates the AWSServiceRoleForResourceAccessManager service-linked role. Verify it without changing the configuration:

aws iam get-role \
  --profile "${ORG_PROFILE}" \
  --role-name AWSServiceRoleForResourceAccessManager \
  --query 'Role.[Arn,CreateDate]' \
  --output table

When Organizations sharing is enabled, principals inside the organization do not accept RAM invitations. External-account shares use a different trust workflow and are outside this article’s scope.

Construct the organization and OU principal ARNs with the Organizations management account ID, not the resource owner’s account ID:

export ORGANIZATION_ARN="arn:aws:organizations::${ORG_MANAGEMENT_ACCOUNT_ID}:organization/${ORG_ID}"
export TARGET_OU_ARN="arn:aws:organizations::${ORG_MANAGEMENT_ACCOUNT_ID}:ou/${ORG_ID}/${TARGET_OU_ID}"

Build regional KMS keys before sharing either service

The default keys aws/ssm and aws/secretsmanager are AWS managed keys. Their policies cannot be edited, so they cannot authorize a principal in another account. Cross-account retrieval therefore requires a customer-managed KMS key in every Region that contains a parameter or secret.

Create a symmetric key in the primary Region:

export PRIMARY_KEY_ID=$(
  aws kms create-key \
    --profile "${OWNER_PROFILE}" \
    --region "${PRIMARY_REGION}" \
    --description "Shared bootstrap secrets - ${PRIMARY_REGION}" \
    --key-usage ENCRYPT_DECRYPT \
    --key-spec SYMMETRIC_DEFAULT \
    --origin AWS_KMS \
    --tags \
      TagKey=Purpose,TagValue=SharedBootstrapSecrets \
      TagKey=Environment,TagValue=Example \
    --query 'KeyMetadata.KeyId' \
    --output text
)

export PRIMARY_KEY_ARN=$(
  aws kms describe-key \
    --profile "${OWNER_PROFILE}" \
    --region "${PRIMARY_REGION}" \
    --key-id "${PRIMARY_KEY_ID}" \
    --query 'KeyMetadata.Arn' \
    --output text
)

aws kms create-alias \
  --profile "${OWNER_PROFILE}" \
  --region "${PRIMARY_REGION}" \
  --alias-name alias/shared-bootstrap-secrets \
  --target-key-id "${PRIMARY_KEY_ID}"

A production key policy should preserve key administration and restrict external use by organization, service, Region, and encryption context. The following policy illustrates one regional key serving the two example resource prefixes. Separate keys per service or security domain provide stronger isolation at additional cost and operational complexity.

{
  "Version": "2012-10-17",
  "Id": "shared-bootstrap-secrets-us-east-1",
  "Statement": [
    {
      "Sid": "EnableOwnerAccountIAMPermissions",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::111122223333:root"
      },
      "Action": "kms:*",
      "Resource": "*"
    },
    {
      "Sid": "AllowOrganizationParameterDecryptThroughSSM",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "kms:Decrypt",
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "aws:PrincipalOrgID": "o-a1b2c3d4e5",
          "kms:ViaService": "ssm.us-east-1.amazonaws.com"
        },
        "StringLike": {
          "kms:EncryptionContext:PARAMETER_ARN": "arn:aws:ssm:us-east-1:111122223333:parameter/org/bootstrap/*"
        }
      }
    },
    {
      "Sid": "AllowOrganizationSecretDecryptThroughSecretsManager",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "kms:Decrypt",
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "aws:PrincipalOrgID": "o-a1b2c3d4e5",
          "kms:ViaService": "secretsmanager.us-east-1.amazonaws.com"
        },
        "StringLike": {
          "kms:EncryptionContext:SecretARN": "arn:aws:secretsmanager:us-east-1:111122223333:secret:org/bootstrap/*"
        }
      }
    }
  ]
}

Save this as kms-key-policy-us-east-1.json, review it through the organization’s normal change process, and apply it:

aws kms put-key-policy \
  --profile "${OWNER_PROFILE}" \
  --region "${PRIMARY_REGION}" \
  --key-id "${PRIMARY_KEY_ID}" \
  --policy-name default \
  --policy file://kms-key-policy-us-east-1.json

Do not blindly replace an existing key policy. Preserve designated key administrators, break-glass access, and any controls required by the organization. The account-root statement in this example delegates key management to IAM policies in the owner account; it does not give every identity automatic access.

The two external-use statements use different encryption-context keys:

  • Parameter Store supplies PARAMETER_ARN.
  • Secrets Manager supplies SecretARN.

Both statements use kms:ViaService, so the permission cannot be used for a direct kms:Decrypt request. They also use aws:PrincipalOrgID; principals that leave the organization stop matching the condition. The consumer still needs an IAM allow for the exact KMS key.

Create an independent key and equivalent regional policy in us-west-2 before creating regional copies. A primary key’s policy is never a substitute for a replica Region’s key policy.

Grant the consumer role its side of the authorization

Assume an existing workload role named SharedSecretReader. Attach only the service and KMS actions it needs. Use the exact ARNs returned by resource creation; the secret ARN includes a six-character suffix.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ReadSharedParameter",
      "Effect": "Allow",
      "Action": [
        "ssm:GetParameter",
        "ssm:GetParameters"
      ],
      "Resource": "arn:aws:ssm:us-east-1:111122223333:parameter/org/bootstrap/security-agent/registration-token"
    },
    {
      "Sid": "ReadSharedSecret",
      "Effect": "Allow",
      "Action": [
        "secretsmanager:DescribeSecret",
        "secretsmanager:GetSecretValue"
      ],
      "Resource": "arn:aws:secretsmanager:us-east-1:111122223333:secret:org/bootstrap/security-agent/registration-token-AbCdEf"
    },
    {
      "Sid": "DecryptSharedValues",
      "Effect": "Allow",
      "Action": "kms:Decrypt",
      "Resource": "arn:aws:kms:us-east-1:111122223333:key/12345678-1234-1234-1234-123456789012"
    }
  ]
}

Save it as shared-secret-reader-policy.json and attach it in the consumer account:

aws iam put-role-policy \
  --profile "${CONSUMER_PROFILE}" \
  --role-name SharedSecretReader \
  --policy-name ReadCentralBootstrapSecrets \
  --policy-document file://shared-secret-reader-policy.json

Repeat with the replica Region’s parameter, secret, and KMS key ARNs when local regional copies exist. Wildcarding the account, Region, or complete secret path makes future operations easier but expands the blast radius. Prefer generating exact policies from an approved inventory.

Option 1: share an SSM Parameter Store SecureString

Create an Advanced SecureString

Parameter Store supports cross-account sharing only for Advanced parameters. The value must use a customer-managed KMS key.

Avoid putting a production value directly into shell history. For a controlled administrative session, disable tracing, read the value silently, and clear the variable immediately after the request:

set +x
read -r -s -p "Synthetic test token: " REGISTRATION_TOKEN
printf '\n'

aws ssm put-parameter \
  --profile "${OWNER_PROFILE}" \
  --region "${PRIMARY_REGION}" \
  --name "${PARAMETER_NAME}" \
  --description "Security agent bootstrap token" \
  --type SecureString \
  --tier Advanced \
  --key-id "${PRIMARY_KEY_ARN}" \
  --value "${REGISTRATION_TOKEN}" \
  --tags \
    Key=Purpose,Value=SecurityAgentBootstrap \
    Key=SecurityDomain,Value=USNonProduction

unset REGISTRATION_TOKEN

The secret is still briefly present in the local process environment and AWS CLI argument memory. For production provisioning, inject it from an approved secrets workflow on a hardened runner rather than typing it interactively.

Get the canonical ARN:

export PARAMETER_ARN=$(
  aws ssm get-parameter \
    --profile "${OWNER_PROFILE}" \
    --region "${PRIMARY_REGION}" \
    --name "${PARAMETER_NAME}" \
    --query 'Parameter.ARN' \
    --output text
)

An Advanced SecureString uses envelope encryption. Parameter Store generates a data key, encrypts the value with that data key, and encrypts the data key under the configured KMS key. The parameter ARN is cryptographically bound through the PARAMETER_ARN encryption context.

Create a resource share for the target OU. Specifying the managed permission explicitly makes the history decision visible in review:

export PARAMETER_SHARE_ARN=$(
  aws ram create-resource-share \
    --profile "${OWNER_PROFILE}" \
    --region "${PRIMARY_REGION}" \
    --name shared-security-agent-token-us-nonprod \
    --resource-arns "${PARAMETER_ARN}" \
    --principals "${TARGET_OU_ARN}" \
    --permission-arns \
      arn:aws:ram::aws:permission/AWSRAMDefaultPermissionSSMParameterReadOnly \
    --no-allow-external-principals \
    --query 'resourceShare.resourceShareArn' \
    --output text
)

RAM accepts the request before every asynchronous association has necessarily completed. Check the resource and principal associations:

aws ram get-resource-share-associations \
  --profile "${OWNER_PROFILE}" \
  --region "${PRIMARY_REGION}" \
  --association-type RESOURCE \
  --resource-share-arns "${PARAMETER_SHARE_ARN}" \
  --query 'resourceShareAssociations[].{Resource:associatedEntity,Status:status,Message:statusMessage}' \
  --output table

aws ram get-resource-share-associations \
  --profile "${OWNER_PROFILE}" \
  --region "${PRIMARY_REGION}" \
  --association-type PRINCIPAL \
  --resource-share-arns "${PARAMETER_SHARE_ARN}" \
  --query 'resourceShareAssociations[].{Principal:associatedEntity,Status:status,Message:statusMessage}' \
  --output table

A resource share can target an individual account ID, an OU ARN, or the organization ARN. Prefer the narrowest stable boundary. OU sharing adapts as accounts move into or out of the OU, but account movement is therefore an access-control event that should be monitored and reviewed.

Choose whether consumers may read history

Parameter Store exposes two RAM managed permissions:

RAM permission Allowed SSM actions
AWSRAMDefaultPermissionSSMParameterReadOnly DescribeParameters, GetParameter, GetParameters
AWSRAMPermissionSSMParameterReadOnlyWithHistory The default actions plus GetParameterHistory

A previous token version can remain valid in an external control plane even after Parameter Store is updated. Grant history only when consumers have an explicit operational requirement for old values.

List the currently available permissions instead of hard-coding assumptions:

aws ram list-permissions \
  --profile "${OWNER_PROFILE}" \
  --region "${PRIMARY_REGION}" \
  --resource-type ssm:Parameter \
  --query 'permissions[].{Name:name,Arn:arn,Version:version,Default:isResourceTypeDefault}' \
  --output table

Neither permission includes GetParametersByPath. Shared Parameter Store hierarchies cannot be consumed as if they were a local recursive namespace. Share and retrieve explicit parameter ARNs.

Retrieve the shared parameter

The consuming account must use the full owner-account ARN:

aws ssm get-parameter \
  --profile "${CONSUMER_PROFILE}" \
  --region "${PRIMARY_REGION}" \
  --name "${PARAMETER_ARN}" \
  --with-decryption \
  --query 'Parameter.Value' \
  --output text

Using only /org/bootstrap/security-agent/registration-token makes the service search the consumer account and returns ParameterNotFound.

Shared parameters are not shown as the consumer’s own parameters. With an explicit RAM share—or a promoted implicit share—the consumer can inventory them with:

aws ssm describe-parameters \
  --profile "${CONSUMER_PROFILE}" \
  --region "${PRIMARY_REGION}" \
  --shared \
  --query 'Parameters[].{Name:Name,Type:Type,Tier:Tier,Version:Version}' \
  --output table

Without --with-decryption, GetParameter can return the encrypted envelope to an authorized SSM caller even if that caller cannot use the KMS key. That does not expose plaintext, but it demonstrates why SSM authorization and KMS authorization are separate controls.

Implicit sharing with PutResourcePolicy

Parameter Store also supports attaching a resource policy directly. This creates a RAM resource share with featureSet set to CREATED_FROM_POLICY.

A policy intended to match the default RAM permission must contain all three actions:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowConsumerAccountRead",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::444455556666:root"
      },
      "Action": [
        "ssm:DescribeParameters",
        "ssm:GetParameter",
        "ssm:GetParameters"
      ],
      "Resource": "arn:aws:ssm:us-east-1:111122223333:parameter/org/bootstrap/security-agent/registration-token"
    }
  ]
}

The SSM PutResourcePolicy API expects the policy as a nonempty string. Load the JSON with Bash command substitution before passing it; command substitution removes trailing newlines and avoids the trailing-newline validation behavior observed in CLI testing without adding another command-line dependency:

export SSM_RESOURCE_POLICY="$(<ssm-parameter-resource-policy.json)"

aws ssm put-resource-policy \
  --profile "${OWNER_PROFILE}" \
  --region "${PRIMARY_REGION}" \
  --resource-arn "${PARAMETER_ARN}" \
  --policy "${SSM_RESOURCE_POLICY}" \
  --query '[PolicyId,PolicyHash]' \
  --output table

unset SSM_RESOURCE_POLICY

The consumer can retrieve the parameter by full ARN immediately, but the parameter does not appear in describe-parameters --shared, and RAM’s normal consumer inventory does not expose it yet. Promote the policy-created share to a standard share:

aws ram get-resource-shares \
  --profile "${OWNER_PROFILE}" \
  --region "${PRIMARY_REGION}" \
  --resource-owner SELF \
  --query "resourceShares[?featureSet=='CREATED_FROM_POLICY' && status=='ACTIVE'].{Arn:resourceShareArn,Name:name}" \
  --output table

aws ram promote-resource-share-created-from-policy \
  --profile "${OWNER_PROFILE}" \
  --region "${PRIMARY_REGION}" \
  --resource-share-arn "arn:aws:ram:us-east-1:111122223333:resource-share/EXAMPLE"

Identify the share by its resource association rather than selecting the first result in an account with multiple implicit shares.

In my testing, promotion failed with UnmatchedPolicyPermissionException when the custom policy allowed only GetParameter and GetParameters. Adding DescribeParameters made the action set match AWSRAMDefaultPermissionSSMParameterReadOnly, after which promotion succeeded. Updating the policy replaced the policy-created RAM share ARN, so automation must rediscover the active share before promotion.

Use explicit RAM shares unless an existing policy-management workflow requires PutResourcePolicy. Explicit shares are easier to discover, review, and operate.

Parameter Store is regional and has no native replication

Calling the us-west-2 SSM endpoint with a us-east-1 parameter ARN fails with an incorrect-Region validation error. A workload in us-west-2 has two choices:

  1. Deliberately call the us-east-1 endpoint and accept the cross-Region dependency, latency, and data-transfer path.
  2. Read a separately created us-west-2 parameter encrypted under a us-west-2 KMS key and shared through a us-west-2 RAM share.

Parameter Store does not synchronize those resources. In my testing, updating the east parameter to version 3 left the independently created west parameter at version 1.

A production replication mechanism therefore needs more than an EventBridge rule:

  • Parameter-change events are best effort.
  • The replicator must fetch the source value with decryption and write it with the destination Region’s KMS key.
  • The destination needs its own tags, policies, KMS policy, and RAM share.
  • Replication must be idempotent and must prevent update loops.
  • A scheduled reconciler must compare approved source versions or hashes against every destination, repairing missed events.
  • Replication state and failures need alarms.
  • The primary Region and conflict policy must be explicit; do not allow independent writes in every Region.

Do not implement replication by placing plaintext values on an EventBridge event bus. Events and CloudTrail records are not secret-transport channels.

Shared-parameter integration constraints

AWS supports shared parameters in these integrations:

  • CloudFormation template parameters
  • AWS Parameters and Secrets Lambda Extension
  • EC2 launch templates
  • ImageId values supplied to EC2 RunInstances
  • Systems Manager Automation runbooks

AWS does not support shared parameters in these scenarios:

  • Systems Manager Run Command parameter references
  • CloudFormation dynamic references
  • CodeBuild environment variables
  • App Runner environment variables
  • ECS secret values

A direct SDK or AWS CLI GetParameter call can still work when an integration does not. Do not assume a service feature that accepts a local parameter name also accepts a cross-account parameter ARN.

Option 2: share a Secrets Manager secret

Secrets Manager does not use RAM for this design. Authorization is expressed directly in a secret resource policy, plus the consumer identity policy and KMS key policy.

Create the secret with a customer-managed key

set +x
read -r -s -p "Synthetic test token: " REGISTRATION_TOKEN
printf '\n'

export SECRET_ARN=$(
  aws secretsmanager create-secret \
    --profile "${OWNER_PROFILE}" \
    --region "${PRIMARY_REGION}" \
    --name "${SECRET_NAME}" \
    --description "Security agent bootstrap token" \
    --kms-key-id "${PRIMARY_KEY_ARN}" \
    --secret-string "${REGISTRATION_TOKEN}" \
    --tags \
      Key=Purpose,Value=SecurityAgentBootstrap \
      Key=SecurityDomain,Value=USNonProduction \
    --query 'ARN' \
    --output text
)

unset REGISTRATION_TOKEN

Capture the complete ARN returned by CreateSecret. Secrets Manager appends six characters to the name in the ARN. Partial ARNs are error-prone, particularly when a secret name itself ends with a hyphen followed by six characters.

Attach an OU-scoped resource policy

The following policy allows only principals that are:

  • Members of the expected organization
  • In the selected OU or one of its descendants
  • Using a role named SharedSecretReader under the expected role path

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowTargetOUWorkloadRoles",
      "Effect": "Allow",
      "Principal": "*",
      "Action": [
        "secretsmanager:DescribeSecret",
        "secretsmanager:GetSecretValue"
      ],
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "aws:PrincipalOrgID": "o-a1b2c3d4e5"
        },
        "ForAnyValue:StringLike": {
          "aws:PrincipalOrgPaths": "o-a1b2c3d4e5/r-abcd/ou-abcd-11111111/ou-abcd-12345678/*"
        },
        "ArnLike": {
          "aws:PrincipalArn": "arn:aws:iam::*:role/workload/SharedSecretReader"
        }
      }
    }
  ]
}

aws:PrincipalOrgPaths is multivalued, which is why the policy uses a set operator. Include the complete organization path, not only the target OU ID. To share with the entire organization, remove the OU-path condition but retain aws:PrincipalOrgID and a workload-role restriction.

Save the policy as secret-resource-policy.json, validate it, and then attach it with public-policy blocking enabled:

aws secretsmanager validate-resource-policy \
  --profile "${OWNER_PROFILE}" \
  --region "${PRIMARY_REGION}" \
  --secret-id "${SECRET_ARN}" \
  --resource-policy file://secret-resource-policy.json \
  --query '{Passed:PolicyValidationPassed,Errors:ValidationErrors}' \
  --output json

aws secretsmanager put-resource-policy \
  --profile "${OWNER_PROFILE}" \
  --region "${PRIMARY_REGION}" \
  --secret-id "${SECRET_ARN}" \
  --resource-policy file://secret-resource-policy.json \
  --block-public-policy

In my testing, policies constrained by either aws:PrincipalOrgID or aws:PrincipalOrgPaths passed BlockPublicPolicy validation and worked cross-account. An unrestricted Principal: "*" policy failed with BlockPublicPolicyCheck.

The wildcard principal is safe only because fixed organization, OU, and role conditions constrain it. Keep --block-public-policy in deployment and update workflows; do not treat one successful validation as permanent approval for future policy changes.

Retrieve the shared secret

Use the complete owner-account ARN:

aws secretsmanager get-secret-value \
  --profile "${CONSUMER_PROFILE}" \
  --region "${PRIMARY_REGION}" \
  --secret-id "${SECRET_ARN}" \
  --version-stage AWSCURRENT \
  --query 'SecretString' \
  --output text

A short name makes Secrets Manager search the consumer account and returns ResourceNotFoundException. Shared secrets also do not appear in the consumer’s ListSecrets results. Maintain an approved ARN catalog or inject the ARN as nonsecret deployment metadata.

Unlike Parameter Store, GetSecretValue has no return-ciphertext mode. Secrets Manager performs the KMS operation before returning SecretString or SecretBinary.

If the secret uses aws/secretsmanager, cross-account retrieval fails even when the secret resource policy allows the caller. The service returns an explicit error that the default KMS service key cannot be used for a secret accessed from another account.

Replicate the secret to another Region

Create a customer-managed KMS key in us-west-2, apply an equivalent regional key policy, and capture its ARN as REPLICA_KEY_ARN. Then ask Secrets Manager to create a read-only replica:

aws secretsmanager replicate-secret-to-regions \
  --profile "${OWNER_PROFILE}" \
  --region "${PRIMARY_REGION}" \
  --secret-id "${SECRET_ARN}" \
  --add-replica-regions \
    "Region=${REPLICA_REGION},KmsKeyId=${REPLICA_KEY_ARN}" \
  --query 'ReplicationStatus[].{Region:Region,Status:Status,KmsKeyId:KmsKeyId,Message:StatusMessage}' \
  --output table

Poll the primary until the replica reports InSync:

aws secretsmanager describe-secret \
  --profile "${OWNER_PROFILE}" \
  --region "${PRIMARY_REGION}" \
  --secret-id "${SECRET_ARN}" \
  --query 'ReplicationStatus[].{Region:Region,Status:Status,Message:StatusMessage}' \
  --output table

The replica ARN has the same account, name, and six-character suffix; only the Region changes:

export REPLICA_SECRET_ARN="arn:aws:secretsmanager:${REPLICA_REGION}:${SECRET_OWNER_ACCOUNT_ID}:secret:org/bootstrap/security-agent/registration-token-AbCdEf"

Secrets Manager copies the secret value, versions, tags, and resource policy. It does not copy or synchronize the destination KMS key policy. The consumer role also needs IAM permissions for the replica secret ARN and replica KMS key ARN.

Read through the local endpoint:

aws secretsmanager get-secret-value \
  --profile "${CONSUMER_PROFILE}" \
  --region "${REPLICA_REGION}" \
  --secret-id "${REPLICA_SECRET_ARN}" \
  --version-stage AWSCURRENT \
  --query 'SecretString' \
  --output text

Updates and rotation occur in the primary Region and propagate to replicas. A direct write to a replica fails with an operation-not-permitted error. If regional failover requires writes, promote the replica to an independent secret from the replica Region:

aws secretsmanager stop-replication-to-replica \
  --profile "${OWNER_PROFILE}" \
  --region "${REPLICA_REGION}" \
  --secret-id "${REPLICA_SECRET_ARN}"

Promotion changes the operating model. Define failback, conflict handling, rotation ownership, and resource-policy management before an incident.

Native replication is same-account only. The API’s replica structure accepts Region and KmsKeyId; it has no destination-account field. Cross-account copies require custom automation and become independent secrets with independent versions, rotation state, ARNs, policies, and billing.

It is also possible to call the primary Region from workloads in another Region without creating a replica. That reduces stored copies but creates a cross-Region runtime dependency. Use replicas for regional independence, not merely to reduce milliseconds of latency.

SSM Parameter Store and Secrets Manager regional sharing topologies

KMS multi-Region keys do not make either service global

A KMS multi-Region key consists of a primary key and one or more regional replica keys with the same mrk- key ID and cryptographic material. Ciphertext encrypted directly under one member can be decrypted by another member in a different Region.

Create and replicate an MRK with the CLI:

export MRK_PRIMARY_ARN=$(
  aws kms create-key \
    --profile "${OWNER_PROFILE}" \
    --region "${PRIMARY_REGION}" \
    --multi-region \
    --description "Shared bootstrap MRK primary" \
    --key-usage ENCRYPT_DECRYPT \
    --key-spec SYMMETRIC_DEFAULT \
    --query 'KeyMetadata.Arn' \
    --output text
)

aws kms replicate-key \
  --profile "${OWNER_PROFILE}" \
  --region "${PRIMARY_REGION}" \
  --key-id "${MRK_PRIMARY_ARN}" \
  --replica-region "${REPLICA_REGION}" \
  --query 'ReplicaKeyMetadata.{Arn:Arn,State:KeyState,Type:MultiRegionConfiguration.MultiRegionKeyType}' \
  --output table

However, every member is an independent regional KMS resource with its own:

  • Key ARN and policy
  • Aliases and grants
  • Description and tags
  • Enabled/disabled state
  • Deletion lifecycle
  • Monthly key charge

Policies and tags do not synchronize after replication. In my testing, the primary description contained a marker while a replica created without --description had an empty description.

For Parameter Store and Secrets Manager, MRKs do not remove the need for service-level replication. These services treat each member as a regional key and encrypt the regional resource under the regional key ARN. Use MRKs when portable ciphertext is an explicit requirement or when a common key ID provides operational value—not as a substitute for replicating the parameter or secret.

Deletion has another lifecycle trap. A scheduled replica enters PendingDeletion with a date. A primary with existing replicas enters PendingReplicaDeletion and has no deletion date; its waiting period begins only after every replica is deleted. Inventory all members before scheduling deletion.

Empirical test results

My testing used synthetic values in one owner sandbox account and one consumer sandbox account in the same organization. Resources were created in us-east-1 and us-west-2, exercised through separate AWS CLI profiles, and then deleted. Propagation intervals below are observations, not service-level commitments.

Test Observed result Architectural implication
Associate a Standard parameter with RAM Resource association FAILED Sharing requires Advanced tier
Share Advanced SecureString using aws/ssm Resource association FAILED A customer-managed key is a sharing prerequisite, not an optional hardening step
Retrieve shared parameter by short name ParameterNotFound Consumers must store/use the owner ARN
Retrieve encrypted parameter before KMS grant, without decryption Encrypted envelope returned SSM and KMS authorization are separate
Retrieve the same parameter with decryption before KMS grant InvalidKeyId RAM permission alone does not expose plaintext
Call us-west-2 SSM with a us-east-1 ARN ValidationException: Incorrect region Endpoint Region must match ARN Region
Call GetParameterHistory through default RAM permission AccessDeniedException History is a separate sharing decision
Call GetParametersByPath with a shared ARN path Validation failure; action absent from RAM permissions Do not design shared hierarchical enumeration
Read an implicit share before promotion Direct ARN read succeeded; shared inventory was empty Access and discoverability differ
Promote an implicit policy missing DescribeParameters UnmatchedPolicyPermissionException Promotion requires a standard managed-permission match
Share to an account, OU, and organization All associated; no in-organization invitations RAM tracks Organizations membership
Update east SSM parameter East became version 3; west remained version 1 Parameter Store has no native synchronization
Read a secret before attaching a resource policy AccessDeniedException Consumer IAM and KMS policies are insufficient by themselves
Read cross-account secret using aws/secretsmanager Explicit default-key cross-account error Customer-managed KMS key is mandatory
Retrieve shared secret by short name ResourceNotFoundException Use the complete owner ARN
Validate org- and OU-constrained wildcard principals Validation passed; live reads succeeded PrincipalOrgID/PrincipalOrgPaths can safely scale policy scope when combined with other restrictions
Validate unrestricted wildcard principal BlockPublicPolicyCheck failed Keep policy validation enabled
Replicate secret to second Region Status reached InSync; tags, policy, and version ID matched Native replication includes value and selected metadata
Write to replica Operation not permitted; call primary Region Replicas are read-only until promoted
Update primary secret Same new version ID appeared in replica within the five-second test interval Replication is asynchronous; do not treat the observed interval as an SLA
Supply AccountId to secret replication Client validation rejected the field Native replication cannot target another account
Remove RAM share Consumer read was denied within the four-second test interval Resource-side revocation is fast, but application caches can retain plaintext
Remove primary secret policy Reads failed in both Regions after policy propagation Replica resource policy follows the primary
Inspect CloudTrail Reads appeared in consumer; cross-account KMS decrypts appeared in caller and key-owner accounts Aggregate audit data from both sides
Schedule MRK deletion Replica: PendingDeletion; primary: PendingReplicaDeletion Delete/schedule replicas before expecting the primary countdown to begin

Architecture patterns and tradeoffs

Pattern A: central owner account with one local copy per Region

A security or shared-services account owns the parameter or secret. Workload accounts read it through OU-scoped authorization. Every active workload Region contains a regional resource and regional KMS key.

  • Secrets Manager: native replicas keep values, versions, tags, and resource policies synchronized.
  • Parameter Store: custom event-driven replication plus periodic reconciliation is required.

This is usually the best balance of central governance and regional availability. It avoids one copy per account while keeping runtime traffic in-Region.

Pattern B: central owner account with cross-Region reads

Only one regional copy exists. All workloads explicitly call that Region.

Advantages:

  • Fewer stored copies and KMS keys
  • One authoritative resource
  • No replication conflict

Disadvantages:

  • The primary Region is on every startup path
  • Cross-Region latency and transfer apply
  • Regional isolation and data-residency requirements may prohibit it
  • A VPC interface endpoint normally connects to its regional AWS service; cross-Region private connectivity requires additional network design

Use this only when startup can tolerate the dependency or has a controlled fallback.

Pattern C: independent copy in every workload account and Region

Each account owns its secret, policy, and KMS key. A central system distributes updates.

Advantages:

  • Strong account isolation
  • No cross-account runtime dependency
  • Local service integrations generally work without shared-resource restrictions

Disadvantages:

  • Copy count grows as accounts × Regions × security domains
  • Rotation and revocation become distributed transactions
  • Drift is inevitable without reconciliation
  • Storage, KMS, endpoint, and operational costs increase

Choose this when account autonomy and containment are more important than centralized lifecycle simplicity.

Pattern D: assume a role or call a credential broker

A workload assumes a narrowly scoped role in the owner account, or presents its AWS identity to a broker that returns a short-lived registration credential.

This avoids a resource policy on every value and can centralize contextual decisions, but it adds STS or broker availability, trust policies, session controls, and another audited service path. A broker is often the strongest design when the external control plane can mint single-use credentials.

Pattern E: one token per security domain

When the control plane organizes agents into sites or environments, align tokens with those boundaries. For example:

  • US production token → production OU paths only
  • US non-production token → non-production and sandbox OU paths only
  • International token → international OU paths only

Store each as a separate parameter or secret. Give each an independent resource policy, rotation runbook, and alerting context. A dedicated KMS key per domain gives the strongest cryptographic boundary; one tightly conditioned key per Region costs less and can still restrict each resource prefix through encryption context.

Private network access

Resource policies make access private in the authorization sense; they do not force packets to remain off public AWS service endpoints. Workloads without NAT or internet egress can use interface VPC endpoints with private DNS.

For Parameter Store retrieval, create an endpoint for the regional SSM service:

aws ec2 create-vpc-endpoint \
  --profile "${CONSUMER_PROFILE}" \
  --region "${PRIMARY_REGION}" \
  --vpc-id vpc-0123456789abcdef0 \
  --vpc-endpoint-type Interface \
  --service-name "com.amazonaws.${PRIMARY_REGION}.ssm" \
  --subnet-ids \
    subnet-11111111111111111 \
    subnet-22222222222222222 \
    subnet-33333333333333333 \
  --security-group-ids sg-0123456789abcdef0 \
  --private-dns-enabled

For Secrets Manager:

aws ec2 create-vpc-endpoint \
  --profile "${CONSUMER_PROFILE}" \
  --region "${PRIMARY_REGION}" \
  --vpc-id vpc-0123456789abcdef0 \
  --vpc-endpoint-type Interface \
  --service-name "com.amazonaws.${PRIMARY_REGION}.secretsmanager" \
  --subnet-ids \
    subnet-11111111111111111 \
    subnet-22222222222222222 \
    subnet-33333333333333333 \
  --security-group-ids sg-0123456789abcdef0 \
  --private-dns-enabled

Allow inbound TCP 443 to the endpoint ENIs only from approved workload security groups. Restrict the endpoint policy as another defense layer, but do not use it as a replacement for identity, resource, or KMS policies.

The workload does not need a KMS VPC endpoint for normal GetParameter --with-decryption or GetSecretValue operations. Parameter Store or Secrets Manager calls KMS on the principal’s behalf. A KMS endpoint is needed only when the workload makes direct KMS API calls.

Provision local endpoints in every Region used at runtime. PrivateLink endpoint-hour charges can exceed the storage cost of a small number of secrets, so include network architecture in the service comparison.

Golden AMI bootstrap design

The AMI should contain:

  • The agent binary and nonsecret configuration
  • A trusted first-boot service
  • A nonsecret site identifier or full regional parameter/secret ARN
  • No registration token, decrypted cache, or token-bearing user data

At launch:

  1. The instance obtains temporary role credentials through IMDSv2.
  2. Deployment metadata maps the account/environment/site to a regional resource ARN.
  3. The first-boot service retrieves the value from the local regional endpoint.
  4. The service supplies the token to the agent through standard input or another mechanism that does not expose it in process arguments or logs.
  5. The agent exchanges the bootstrap token for its normal registered identity, when supported.
  6. The bootstrap process clears in-memory variables and does not persist plaintext to the AMI, EBS volume, console output, or shell trace.

Secret-free golden AMI bootstrap sequence

Enforce IMDSv2 and restrict instance metadata access from untrusted containers or processes. Never place the secret in EC2 user data: user data is deployment metadata, not a secret store.

If the agent retrieves the token only once, avoid a long-lived local cache. If a workload reads a credential repeatedly, use a bounded client-side cache with a TTL shorter than the required revocation or rotation objective. Cache behavior is part of the security model: deleting a RAM share or resource policy cannot erase plaintext already held by a process.

Rotation and consistency

Parameter Store

Parameter Store retains versions but does not rotate an external credential. A complete rotation workflow must:

  1. Create or activate the new value in the external control plane.
  2. Update the primary parameter.
  3. Replicate and verify every regional parameter.
  4. Allow consumers to converge within a defined overlap window.
  5. Revoke the old value externally.
  6. Confirm no consumer is reading an old parameter version or cache entry.

Granting GetParameterHistory can undermine step 5 if the old external credential remains valid. Parameter history is operational metadata, not a rotation system.

Secrets Manager

Secrets Manager can coordinate rotation for supported AWS services and can invoke a Lambda rotation function for custom credentials. Regional replicas follow rotation performed in the primary Region. The external system must still support a safe rotation protocol—typically create, set, test, and finish—or an overlap window.

A rarely rotating registration token does not automatically justify Parameter Store. Rotation frequency is only one dimension; native regional replication, value size, policy operations, and integration support may still favor Secrets Manager.

Quotas and throughput

The most relevant defaults are:

Dimension Parameter Store Secrets Manager
Values per account and Region 10,000 Standard and 100,000 Advanced 500,000 secrets
Value size 4 KB Standard; 8 KB Advanced 65,536 bytes
Retained versions 100 100
Default read throughput 40 TPS shared by GetParameter, GetParameters, and GetParametersByPath 10,000 TPS for GetSecretValue; 100 TPS for BatchGetSecretValue
Resource-policy size Service-specific Parameter Store/RAM constraints 20,480 characters per secret resource policy

Parameter Store higher-throughput mode raises individual read limits and incurs additional charges. Shared-parameter throughput is enforced at the consumer account level, so one consuming account does not consume another account’s Parameter Store read quota.

Secrets Manager similarly attributes cross-account request throttling to the account of the calling identity, not the account that owns the secret. This reduces centralized noisy-neighbor coupling, but every workload still needs retries with exponential backoff and jitter.

Avoid updating a Secrets Manager value more frequently than once every ten minutes for a sustained period. Each update creates a version, and versions younger than 24 hours are not removed even when the normal 100-version cleanup threshold is exceeded.

Audit and detection

CloudTrail records Parameter Store, Secrets Manager, RAM, and KMS API activity. Query recent event history during validation:

aws cloudtrail lookup-events \
  --profile "${CONSUMER_PROFILE}" \
  --region "${PRIMARY_REGION}" \
  --lookup-attributes \
    AttributeKey=EventName,AttributeValue=GetParameter \
  --max-results 50 \
  --query 'Events[].{Time:EventTime,User:Username,EventId:EventId}' \
  --output table

aws cloudtrail lookup-events \
  --profile "${CONSUMER_PROFILE}" \
  --region "${PRIMARY_REGION}" \
  --lookup-attributes \
    AttributeKey=EventName,AttributeValue=GetSecretValue \
  --max-results 50 \
  --query 'Events[].{Time:EventTime,User:Username,EventId:EventId}' \
  --output table

For cross-account KMS operations, AWS records events in both the caller account and the key-owner account. Correlate them by time, key ARN, principal, encryption context, and request metadata. Use an organization trail or CloudTrail Lake for durable, centralized analysis; event history alone is not a retention strategy.

Alert on at least:

  • Reads by unexpected role paths, accounts, Regions, user agents, or VPC endpoints
  • Resource-policy and RAM-share changes
  • KMS key-policy, grant, state, rotation, and deletion changes
  • Secret replication failures or prolonged non-InSync status
  • Parameter replication drift
  • Use of AWSPREVIOUS or explicit old parameter versions
  • Account movement into an authorized OU
  • Retrieval spikes inconsistent with instance-launch volume

Do not put plaintext values in resource names, tags, policy conditions, CLI descriptions, or API request fields that CloudTrail records.

Cost model

Public prices as of September 2026:

Direct service charges

Item Price
Standard Parameter Store parameter No additional storage charge
Advanced Parameter Store parameter USD 0.05 per parameter-month, prorated hourly
Advanced Parameter Store API interactions USD 0.05 per 10,000 interactions
Secrets Manager secret USD 0.40 per secret-month, prorated hourly
Secrets Manager API calls USD 0.05 per 10,000 calls
Customer-managed KMS key USD 1.00 per key-month, prorated hourly
Symmetric KMS requests Typically USD 0.03 per 10,000 after the 20,000-request monthly free tier
AWS RAM No additional charge

Every Secrets Manager replica is billed as a separate secret. Every KMS key—including each member of a multi-Region key set—is billed as a separate key. The first and second automatic or on-demand KMS key rotations each add USD 1 per month; further rotations do not increase the monthly storage charge. Keys scheduled for deletion do not incur the key-storage charge during the waiting period.

Three tokens in two Regions

Assume three site-scoped registration tokens, two Regions, and one shared customer-managed KMS key per Region:

Parameter Store baseline

6 Advanced parameters × $0.05 = $0.30/month
2 regional KMS keys × $1.00 = $2.00/month
Baseline before requests and networking = $2.30/month

Secrets Manager baseline

6 primary/replica secrets × $0.40 = $2.40/month
2 regional KMS keys × $1.00 = $2.00/month
Baseline before requests and networking = $4.40/month

If policy requires one KMS key per site per Region, six keys cost USD 6 per month and dominate both totals. Conversely, a regional KMS key shared across hundreds of tightly related secrets amortizes its fixed cost.

At 10,000 instance launches per month with one retrieval per launch, either service adds approximately USD 0.05 in service API charges. At 7.2 million uncached reads per month, the service API charge is approximately USD 36, before KMS and networking. Retrieval frequency and cache strategy matter more than the storage-price difference at scale.

Costs that are easy to omit

  • Interface VPC endpoints are billed per endpoint ENI-hour and per GB. At an illustrative us-east-1 rate of USD 0.01 per hour, one endpoint deployed across three Availability Zones is approximately USD 21.90 per 730-hour month before data processing—more than the storage in the preceding example.
  • Custom Parameter Store replication incurs Lambda, EventBridge, logging, and operational costs.
  • Custom Secrets Manager rotation incurs Lambda charges unless managed rotation applies.
  • Cross-Region calls can incur data-transfer charges.
  • Additional CloudTrail copies, CloudTrail Lake ingestion, S3 storage, KMS encryption, and alarms can add charges.
  • Copies per account multiply secret/parameter and KMS-key inventory.

Cost is therefore an architecture property, not merely the advertised per-secret price.

Failure and revocation runbook

Document and test these cases before production:

Compromised consumer role

  1. Deny or disable the role in the consumer account.
  2. Remove the account/OU from the RAM share or secret policy if broader containment is required.
  3. Revoke the credential in the external control plane.
  4. Rotate the stored value and every regional copy.
  5. Investigate CloudTrail in both consumer and owner accounts.

Removing read access does not invalidate plaintext already retrieved. External revocation is mandatory for a bearer token.

Account moves between OUs

RAM OU shares and aws:PrincipalOrgPaths conditions follow Organizations membership. Treat MoveAccount as a privileged access-control operation. Alert on it, and test both access grant and access removal after moves.

Primary Region failure

  • Parameter Store: select a predesignated regional parameter, verify reconciliation state, and prevent conflicting writes during failover.
  • Secrets Manager: read the local replica. Promote it only if writes or rotation are required, and follow a defined failback process.

Owner-account closure or suspension

Centralized access depends on the owner account and its KMS keys. Parameter Store consumers lose access when the owner account closes; AWS documents recovery if the account is reopened during the post-closure period. Protect the owner account with appropriate governance and break-glass procedures.

KMS key disabled or scheduled for deletion

Monitor KMS key state in every Region. A disabled key breaks plaintext retrieval even when service policies remain correct. Multi-Region primaries remain in PendingReplicaDeletion until all replicas are gone, so deletion automation must understand the complete key set.

A production decision framework

Use the following sequence:

  1. Can AWS identity or a broker replace the shared bearer token? Use that design.
  2. Can the token be segmented by account, environment, or site? Segment it before selecting storage.
  3. Is native regional replication or coordinated rotation required? Prefer Secrets Manager.
  4. Is the value static, no larger than 8 KB, and supported by direct retrieval or the documented shared-parameter integrations? Parameter Store Advanced can be appropriate.
  5. Can workloads depend on another Region at runtime? If not, create local regional copies.
  6. Is account-level containment more important than centralized lifecycle? Consider copies per account despite the operational cost.
  7. Have identity, resource, KMS, endpoint, SCP, audit, caching, and revocation controls all been tested together? Do not approve the design based on policy inspection alone.

Decision tree for selecting a secret distribution model

Conclusion

SSM Parameter Store and Secrets Manager can both distribute a protected value across an AWS organization, but they solve the surrounding lifecycle differently.

Parameter Store sharing is a RAM feature available only to Advanced parameters. It is cost-effective for relatively static values, but consumers must use full ARNs, integration support is limited, history is a separate permission, and multi-Region synchronization is entirely the platform team’s responsibility. The implicit resource-policy path adds a promotion and discoverability lifecycle that explicit RAM shares avoid.

Secrets Manager uses secret resource policies rather than RAM. It costs more per stored value, but provides native same-account regional replicas, coordinated version propagation, and rotation capabilities. It still requires a customer-managed KMS key and independent KMS policy in every Region, and native replication cannot copy a secret into another account.

For organization-scale bootstrap credentials, the strongest practical design is usually a dedicated owner account, separate credentials for each security domain, local regional resources, OU- and role-scoped access, tightly conditioned regional KMS keys, private endpoints, and centralized audit. Where possible, replace the shared token with an identity-based or short-lived registration flow. The safest organization-wide secret is the one that no longer needs to be organization-wide.

References

Leave a Reply

Your email address will not be published. Required fields are marked *