Calling Amazon Bedrock APIs Securely in Dev and Prod
Use IAM Identity Center or Bedrock short-term API keys on your laptop and IAM roles on Amazon EC2, so you don't need long-term keys to call Amazon Bedrock
Every call to Amazon Bedrock needs credentials. The safest credentials are short-lived ones that you never write down. This post shows how to use temporary credentials on a developer laptop and IAM roles in production on Amazon EC2. The same Python code runs unchanged in both places.
| Environment | Recommended credential | Lifetime |
|---|---|---|
| Dev (laptop) | IAM Identity Center temporary credentials | Session duration (1–12 hours) |
| Dev (laptop, bearer-token tools) | Bedrock short-term API key | Up to 12 hours |
| Prod (EC2) | IAM role through an instance profile | Rotated automatically |
| Avoid | IAM user access keys or Bedrock long-term API keys | Days to never-expiring |
Prerequisites
- An AWS account with Amazon Bedrock model access in your Region (this post uses
us-west-2) - IAM Identity Center enabled, with a permission set that grants Bedrock access
- AWS CLI v2 and Python 3.9+
pip install boto3 aws-bedrock-token-generator
Why to avoid long-term credentials
IAM user access keys (
AKIA...) and Bedrock long-term API keys are static secrets. They stay valid until someone rotates or deletes them. A Bedrock long-term key can be set to never expire.The main risks are:
- Leak exposure. If keys get accidentally committed to Git, pasted into chat, baked into container images, or left in
.envfiles, then these leaked long-term key can be misued by an attacker (LLMjacking) until you notice and revoke it. - Broad blast radius. A long-term API key created in the Bedrock console automatically creates an IAM user (
BedrockAPIKey-xxx) with theAmazonBedrockLimitedAccessmanaged policy attached. That policy covers more than model invocation: it also includes some IAM, KMS, EC2, and Marketplace actions. - Weak attribution. A shared static key doesn't tell you which person or workload made a call.
- Operational burden. You must store, rotate, and audit the key yourself.
Temporary credentials remove most of this risk. They expire automatically and are tied to an identity you already manage.
Part 1: Dev environment on your laptop
Option A (recommended): IAM Identity Center temporary credentials
Configure an SSO profile:
bash aws configure sso --profile bedrock-dev Enter your start URL, SSO Region, account, and permission set when prompted. This writes a profile to ~/.aws/config:1
2
3
4
5
6
7
8
9
10
[profile bedrock-dev]
sso_session = my-sso
sso_account_id = 111122223333
sso_role_name = BedrockDeveloper
region = us-west-2
[sso-session my-sso]
sso_start_url = https://my-org.awsapps.com/start
sso_region = us-east-1
sso_registration_scopes = sso:account:accessSign in. This opens a browser and caches a temporary token:
1
aws sso login --profile bedrock-devPoint your shell at the profile and check your identity:
bash export AWS_PROFILE=bedrock-dev aws sts get-caller-identity .The ARN should look like
arn:aws:sts::111122223333:assumed-role/AWSReservedSSO_BedrockDeveloper_.../you@example.comNo secret is stored on disk. When the session expires, you run
aws sso login again.Option B: Bedrock short-term API keys
Alternatively, you can generate a short-term API key in the Amazon Bedrock console:
- Sign in AWS Console.
- Open the Amazon Bedrock console and select the Region you want to call, for example
us-west-2. - In the left navigation pane, choose API keys.
- On the Short-term API keys tab, choose Generate short-term API keys, and copy the key.
- Export the key in your terminal:
1
export AWS_BEARER_TOKEN_BEDROCK="<short-term-api-key>"A short-term key:
- Is valid until your console session expires, and never longer than 12 hours
- Inherits the permissions of the principal that generated it
- Works only in the Region where it was generated
Tip: To generate short-term keys from code, for example to refresh them automatically, use the aws-bedrock-token-generator library.
Note: IfAWS_BEARER_TOKEN_BEDROCKis set, boto3 sends the bearer token to Bedrock instead of SigV4-signed credentials. Rununset AWS_BEARER_TOKEN_BEDROCKwhen you want to go back to your profile credentials.
Part 2: Prod environment on Amazon EC2
In production, never copy keys to the instance. Use an IAM role instead.
- Create an IAM role with a least-privilege Bedrock policy. Cross-Region inference profiles need both the profile ARN and the underlying foundation model ARNs:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"bedrock:InvokeModel",
"bedrock:InvokeModelWithResponseStream"
],
"Resource": [
"arn:aws:bedrock:us-west-2:111122223333:inference-profile/global.openai.gpt-6-luna",
"arn:aws:bedrock:*::foundation-model/openai.gpt-6-luna*",
"arn:aws:bedrock:::foundation-model/openai.gpt-6-luna*"
]
}
]
}- Attach the role to your EC2 instance as an instance profile.
How the instance gets credentials: EC2 publishes temporary credentials for the role through the Instance Metadata Service (IMDS). The AWS SDK reads them automatically, with no configuration. They are rotated before they expire. Require IMDSv2 on the instance to protect against SSRF-style credential theft.
Part 3: One Python app for both environments
The application never handles credentials. It relies on the boto3 default credential chain:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# app.py
import boto3
MODEL_ID = "global.openai.gpt-6-luna"
REGION = "us-west-2"
def main():
session = boto3.Session(region_name=REGION)
# Show which identity is in use (helpful for debugging; remove in prod logs)
identity = session.client("sts").get_caller_identity()
print(f"Calling Bedrock as: {identity['Arn']}")
bedrock = session.client("bedrock-runtime")
response = bedrock.converse(
modelId=MODEL_ID,
messages=[{"role": "user", "content": [{"text": "Explain IAM roles in one sentence."}]}],
inferenceConfig={"maxTokens": 200},
)
print(response["output"]["message"]["content"][0]["text"])
if __name__ == "__main__":
main()Run locally:
1
2
3
aws sso login --profile bedrock-dev
AWS_PROFILE=bedrock-dev python app.py
# Calling Bedrock as: arn:aws:sts::111122223333:assumed-role/AWSReservedSSO_BedrockDeveloper_.../you@example.comRun on EC2:
1
2
python app.py
# Calling Bedrock as: arn:aws:sts::111122223333:assumed-role/BedrockAppRole/i-0123456789abcdef0There is no
aws_access_key_id in the code, in environment files, or on the instance.How boto3 picks credentials
Boto3 searches for credentials in a fixed order and stops at the first source it finds:
- Credentials passed as parameters to
boto3.client() - Credentials passed as parameters when you create a
Session - Environment variables (
AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY,AWS_SESSION_TOKEN) - Assume role provider
- Assume role with web identity provider
- IAM Identity Center credential provider
- Shared credentials file (
~/.aws/credentials) - Login with console credentials
- AWS config file (
~/.aws/config) - Boto2 config file (
/etc/boto.cfg,~/.boto) - Container credential provider (Amazon ECS, Amazon EKS Pod Identity)
- EC2 Instance Metadata Service (instance profile role)
On your laptop,
AWS_PROFILE=bedrock-dev selects the SSO profile, so step 6 supplies the credentials. On EC2 nothing earlier in the chain is set, so boto3 falls through to step 12 and uses the instance role.Keep production hosts free of static keys, and don't hardcode credentials in
boto3.client().Anti-pattern: Bedrock long-term API keys (Not Recommended)
These are the patterns to recognize and not use for production workloads as they carry the risk of accidental long-term credential leakage:
Example 1: Key hardcoded in the app (not recommended)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# hardcoded_key.py -- Not Recommended
import os
import boto3
REGION = "us-west-2"
MODEL_ID = "global.openai.gpt-6-luna"
# The secret is now in source control, code reviews, build artifacts, and
# every copy of the repo. It stays valid until someone revokes it.
os.environ["AWS_BEARER_TOKEN_BEDROCK"] = "<long-term-api-key>"
bedrock = boto3.client("bedrock-runtime", region_name=REGION)
response = bedrock.converse(
modelId=MODEL_ID,
messages=[{"role": "user", "content": [{"text": "Hello"}]}],
inferenceConfig={"maxTokens": 200},
)
print(response["output"]["message"]["content"][0]["text"])Example 2: Key read from an environment variable (not recommended)
1
2
# Typically set in a shell profile, a .env file
export AWS_BEARER_TOKEN_BEDROCK="<long-term-api-key>"1
2
3
4
5
6
7
8
9
10
11
12
13
14
# env_var_key.py -- NOT RECOMMENDED
import boto3
REGION = "us-west-2"
MODEL_ID = "global.openai.gpt-6-luna"
# No key in the code: boto3 picks up AWS_BEARER_TOKEN_BEDROCK automatically.
bedrock = boto3.client("bedrock-runtime", region_name=REGION)
response = bedrock.converse(
modelId=MODEL_ID,
messages=[{"role": "user", "content": [{"text": "Hello"}]}],
inferenceConfig={"maxTokens": 200},
)
print(response["output"]["message"]["content"][0]["text"])Summary
Use IAM Identity Center temporary credentials, or Bedrock short-term API keys, for local development, and use IAM roles for production workloads on EC2. Avoid IAM user access keys and Bedrock long-term API keys: they are static secrets that stay valid until revoked.
Clean up
- Detach the IAM role from the EC2 instance, and terminate the instance if you no longer need it.
- Delete the IAM role and its Bedrock policy.
- Deactivate and delete any Bedrock long-term API keys that you found.
References
- Boto3: Credentials
- Securing Amazon Bedrock API keys: Best practices for implementation and management
- LLMjacking: Stolen Cloud Credentials Used in New AI Attack (Sysdig)
- Generate an Amazon Bedrock API key
- Control permissions for generating and using Amazon Bedrock API keys
- aws-bedrock-token-generator (Python)
- Configuring IAM Identity Center authentication with the AWS CLI
- IAM roles for Amazon EC2
- Use IMDSv2
- Alternatives to long-term access keys
- AWS SDKs and Tools standardized credential providers
- Security best practices in IAM
- Amazon Bedrock pricing
Enjoyed reading this content? Let the author know!
Your likes, comments, shares, and saves help creators reach more builders.
Loading recommendations
Loading article