Skip to content
Linuxbeast
  • Home
  • Today in Tech
  • Who is Hiring?
  • About Me
  • Work With Me
  • Tools
    • DevOps Onboarding
    • AWS VPC Subnet Planner
    • Tag Network
    • Pag-IBIG Housing Loan Calculator
  • Contact
How to Access AWS Secrets Manager from Another Account

How to Access AWS Secrets Manager from Another Account

March 9, 2026August 16, 2024 by Linuxbeast
6 min read

Sometimes you need a Lambda function or EC2 instance in one AWS account to read a secret stored in a different account. This is a common setup in multi-account environments — you might keep shared credentials (database passwords, API keys) in a central account and have workloads in other accounts pull them as needed.

This guide covers how to set up cross-account access to AWS Secrets Manager using the AssumeRole pattern. We’ll configure both accounts, handle KMS encryption, and test it with Python and the AWS CLI.

How It Works

Two accounts are involved:

  • Account A (111111111111) — owns the secret in Secrets Manager
  • Account B (222222222222) — has the EC2 instance or Lambda that needs the secret

The recommended approach is the AssumeRole pattern: create an IAM role in Account A that Account B can assume. The flow goes like this:

  1. Account B’s Lambda/EC2 calls sts:AssumeRole to assume a role in Account A
  2. STS returns temporary credentials scoped to Account A
  3. Using those credentials, the code calls secretsmanager:GetSecretValue in Account A
  4. The secret is returned

You could also do this with just a resource-based policy on the secret (no role assumption), but the AssumeRole approach gives you better audit trails in CloudTrail and finer-grained control. If you’re new to AssumeRole, check out How to Set Up Cross-Account Access in AWS with AssumeRole for the fundamentals.

Prerequisites

  • Two AWS accounts with admin or IAM management access
  • A secret already created in Account A’s Secrets Manager
  • AWS CLI configured — see How to Install AWS CLI v2 on Ubuntu 22.04 if needed

Step 1: Create the Cross-Account Role in Account A

In Account A, create an IAM role that Account B is allowed to assume. This role will have permissions to read the secret.

Trust Policy (Who Can Assume This Role)

This trust policy allows a specific role in Account B to assume the role. Replace the account ID and role name with your own:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::222222222222:role/AccountB-LambdaRole"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "StringEquals": {
          "aws:PrincipalOrgID": "o-your-org-id"
        }
      }
    }
  ]
}

The aws:PrincipalOrgID condition is optional but recommended if both accounts are in the same AWS Organization. It prevents any account outside your org from assuming the role, even if the account ID somehow gets into the trust policy by mistake.

Permissions Policy (What the Role Can Do)

Attach this inline or managed policy to the role. It grants permission to read a specific secret:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "secretsmanager:GetSecretValue",
      "Resource": "arn:aws:secretsmanager:ap-southeast-1:111111111111:secret:my-shared-secret-*"
    }
  ]
}

Note the -* at the end of the secret ARN — Secrets Manager appends a random suffix to secret ARNs, so you need the wildcard to match it. You can find the full ARN in the Secrets Manager console.

Step 2: Update the KMS Key Policy (If Using a Custom Key)

If your secret is encrypted with the default aws/secretsmanager key, you can skip this step — AWS-managed keys handle cross-account access automatically when using the AssumeRole pattern (since you’re operating as a role within Account A).

But if you’re using a customer-managed KMS key (CMK) to encrypt the secret, you need to update the key policy to allow the cross-account role to decrypt:

{
  "Sid": "AllowCrossAccountDecrypt",
  "Effect": "Allow",
  "Principal": {
    "AWS": "arn:aws:iam::111111111111:role/CrossAccountSecretsRole"
  },
  "Action": [
    "kms:Decrypt",
    "kms:DescribeKey"
  ],
  "Resource": "*"
}

Add this as a statement to your existing KMS key policy in Account A. The Resource: "*" here is fine because this policy is attached to the key itself — it’s saying “allow this role to use this key,” not “allow access to all keys.”

Step 3: Configure the Role in Account B

The EC2 instance or Lambda function in Account B needs an IAM role with permission to assume the cross-account role in Account A.

Attach this policy to the Account B role (the one assigned to your Lambda or EC2 instance profile):

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "sts:AssumeRole",
      "Resource": "arn:aws:iam::111111111111:role/CrossAccountSecretsRole"
    }
  ]
}

This is the only permission Account B needs. The actual Secrets Manager access comes from the assumed role in Account A.

Step 4: Retrieve the Secret with Python

Here’s a Python function that assumes the cross-account role and retrieves the secret. This works the same in Lambda, on EC2, or locally with configured credentials:

import boto3
import json
import os

def get_cross_account_secret():
    role_arn = os.environ["ROLE_ARN"]
    secret_name = os.environ["SECRET_NAME"]
    region = os.environ.get("AWS_REGION", "ap-southeast-1")

    # Step 1: Assume the cross-account role
    sts_client = boto3.client("sts")
    assumed = sts_client.assume_role(
        RoleArn=role_arn,
        RoleSessionName="cross-account-secrets"
    )
    credentials = assumed["Credentials"]

    # Step 2: Create a Secrets Manager client using the assumed credentials
    sm_client = boto3.client(
        "secretsmanager",
        region_name=region,
        aws_access_key_id=credentials["AccessKeyId"],
        aws_secret_access_key=credentials["SecretAccessKey"],
        aws_session_token=credentials["SessionToken"],
    )

    # Step 3: Get the secret
    response = sm_client.get_secret_value(SecretId=secret_name)
    secret = json.loads(response["SecretString"])

    return secret


if __name__ == "__main__":
    secret = get_cross_account_secret()
    print("Secret keys:", list(secret.keys()))

Set the environment variables before running:

export ROLE_ARN="arn:aws:iam::111111111111:role/CrossAccountSecretsRole"
export SECRET_NAME="my-shared-secret"
python3 get_secret.py

The script prints the secret’s keys (not the values) to verify it works without exposing anything in logs. In production, you’d obviously use the values directly in your application instead of printing them.

Testing with the AWS CLI

You can also test this flow from the CLI without writing any code. First, assume the role:

aws sts assume-role \
  --role-arn "arn:aws:iam::111111111111:role/CrossAccountSecretsRole" \
  --role-session-name "test-session"

This returns temporary credentials. Export them:

export AWS_ACCESS_KEY_ID="returned-access-key"
export AWS_SECRET_ACCESS_KEY="returned-secret-key"
export AWS_SESSION_TOKEN="returned-session-token"

Now fetch the secret:

aws secretsmanager get-secret-value \
  --secret-id "my-shared-secret" \
  --region ap-southeast-1

If you get the secret back, your cross-account setup is working. Don’t forget to unset the environment variables after testing so you switch back to your default credentials:

unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN

Troubleshooting

AccessDeniedException on AssumeRole

Check two things: the trust policy on the Account A role must list Account B’s role ARN as a principal, and Account B’s role must have sts:AssumeRole permission targeting the Account A role ARN. Both sides need to agree.

AccessDeniedException on GetSecretValue

The assumed role probably doesn’t have secretsmanager:GetSecretValue permission, or the secret ARN in the policy doesn’t match. Double-check the wildcard suffix (-*) on the ARN.

KMS Decrypt Error

If you see AccessDeniedException mentioning KMS, the secret uses a customer-managed key and the key policy is missing the decrypt permissions. Go back to Step 2 and add the KMS policy statement.

Summary of What Goes Where

Component Account What It Does
Cross-account IAM role A Allows Account B to assume it; has permission to read the secret
KMS key policy update A Allows the cross-account role to decrypt (only if using CMK)
Lambda/EC2 role policy B Allows sts:AssumeRole targeting the Account A role
Application code B Assumes role, then calls GetSecretValue with temporary credentials

Conclusion

Cross-account Secrets Manager access comes down to three pieces: a role in Account A that Account B can assume, the right permissions on that role, and KMS key access if you’re using a customer-managed key. Once it’s set up, the actual code is a handful of boto3 calls.

If you’re working on more cross-account patterns, How to Copy S3 Bucket Objects Across AWS Accounts uses a similar AssumeRole setup for S3. And if you need to send emails cross-account, check out How to Send Email Using AWS SES with Cross-Account Secrets Manager — it builds directly on what we covered here.

Categories AWS Tags AssumeRole, AWS, Cross-Account, IAM, KMS, Python, Secrets Manager
How to Use SQL Joins and Conditional Statements in PostgreSQL
How to Connect AWS Lambda to Microsoft SQL Server Using PyODBC
← PreviousHow to Use SQL Joins and Conditional Statements in PostgreSQLNext →How to Connect AWS Lambda to Microsoft SQL Server Using PyODBC

Related Articles

How to Install AWS CLI v2 on Ubuntu 22.04
AWS

How to Install AWS CLI v2 on Ubuntu 22.04

How to Add and Delete Users on Ubuntu (EC2, WSL, or Any Server)
AWS

How to Add and Delete Users on Ubuntu (EC2, WSL, or Any Server)

Solve 'yum command not found' in AWS Lambda Python 3.12 base image
AWS

Solve ‘yum: command not found’ in AWS Lambda Python 3.12+ Base Image

© 2026 Linuxbeast • Built with GeneratePress
Manage Consent
To provide the best experiences, we use technologies like cookies to store and/or access device information. Consenting to these technologies will allow us to process data such as browsing behavior or unique IDs on this site. Not consenting or withdrawing consent, may adversely affect certain features and functions.
Functional Always active
The technical storage or access is strictly necessary for the legitimate purpose of enabling the use of a specific service explicitly requested by the subscriber or user, or for the sole purpose of carrying out the transmission of a communication over an electronic communications network.
Preferences
The technical storage or access is necessary for the legitimate purpose of storing preferences that are not requested by the subscriber or user.
Statistics
The technical storage or access that is used exclusively for statistical purposes. The technical storage or access that is used exclusively for anonymous statistical purposes. Without a subpoena, voluntary compliance on the part of your Internet Service Provider, or additional records from a third party, information stored or retrieved for this purpose alone cannot usually be used to identify you.
Marketing
The technical storage or access is required to create user profiles to send advertising, or to track the user on a website or across several websites for similar marketing purposes.
  • Manage options
  • Manage services
  • Manage {vendor_count} vendors
  • Read more about these purposes
View preferences
  • {title}
  • {title}
  • {title}