Store Secrets in AWS Secrets Manager
Learn to store Python app secrets in AWS Secrets Manager with a hands-on walkthrough, comparing options and troubleshooting edge cases.
Focus: store python app secrets in aws secrets manager
You’ve built a Python app, tested it locally, and now it’s time to deploy to AWS. But where do you put your database passwords, API keys, and OAuth tokens? Hardcoding them is a security disaster, and storing them in plain text files is only slightly better. In this lesson, you’ll learn how to store Python app secrets in AWS Secrets Manager, the AWS service designed to keep your sensitive data encrypted, rotated, and accessible to your code only when it needs it. By the end, you’ll be able to securely fetch secrets in Python, compare Secrets Manager with other options, and avoid common pitfalls.
The problem this lesson solves
Every application has secrets: database credentials, API keys, encryption keys, and more. If you hardcode them into your source code, they end up in your version control history, visible to anyone with repo access. If you put them in environment variables, they can leak through logs, CI pipelines, or developer laptops. And if you store them in plain text files like config.ini, a single misconfigured server can expose them to the world.
Pro tip: A secret leaked is a breach waiting to happen. Even if the secret is for a staging environment, treat it with the same care as production credentials.
AWS Secrets Manager solves this by centralizing secret storage, encrypting it at rest with AWS KMS, and providing an API to retrieve secrets on demand. You can also enable automatic rotation, so your database passwords change regularly without manual effort. This lesson focuses on how to use Secrets Manager from Python, but the concepts apply to any application running on AWS.
Core concept / mental model
Think of Secrets Manager as a vault with a keyhole. The vault (Secrets Manager) holds your secrets in encrypted form. The keyhole is the AWS SDK, which uses your IAM permissions to open the vault and hand you the secret — but only if you’re allowed. No secret is stored in your code, and no secret is readable by anyone without the right permissions.
Here’s a simple diagram in words:
Python app --> AWS SDK (boto3) --> Secrets Manager --> Returns secret as JSON
The SDK authenticates on your behalf using the credentials available in the environment (e.g., IAM role on EC2, access keys locally). Once authenticated, it calls get_secret_value and retrieves the secret as a string (usually JSON). Your app then parses that JSON to extract the fields it needs.
Key terms to remember:
- Secret: A key-value pair or arbitrary string stored in Secrets Manager.
- Secret ARN: The Amazon Resource Name that uniquely identifies the secret.
- Rotation: The process of automatically updating the secret value periodically.
- KMS key: The encryption key used to encrypt the secret at rest.
How it works step by step
Let’s walk through the lifecycle of using a secret with Secrets Manager:
- Create the secret — Use the AWS Console, CLI, or SDK to store your secret. For example, you might store a JSON object with
usernameandpasswordfor a database. - Grant IAM permissions — Your Python app needs an IAM role or user with permission to read the secret. The least-privilege policy would be
secretsmanager:GetSecretValueon the specific secret ARN. - Retrieve the secret in Python — Use
boto3to callget_secret_value(). The SDK handles authentication, then returns the secret string. - Parse and use — Convert the secret string to a Python dictionary and use the values in your database connection or API call.
- (Optional) Rotate the secret — Set up a Lambda function to update the secret value on a schedule. Your app just keeps fetching the latest value.
This flow ensures that secrets are never in code, and they can be updated without redeploying your application. The key insight is that you only fetch the secret when you need it, and you can cache it for performance if needed.
Hands-on walkthrough
Let’s get practical. You’ll need boto3 installed, and AWS credentials configured (either via AWS CLI or environment variables).
Step 1: Install boto3
pip install boto3
Step 2: Create a secret in AWS Secrets Manager
You can use the AWS CLI or the console. Here’s the CLI command to create a secret with a JSON value:
aws secretsmanager create-secret \
--name MyDatabaseSecret \
--secret-string '{"username":"admin","password":"SuperSecret!123"}'
Note the output includes the ARN — you’ll use that later.
Step 3: Write Python code to retrieve the secret
Create a Python script that fetches the secret and uses it.
import boto3
import json
from botocore.exceptions import ClientError
def get_secret(secret_name, region_name="us-east-1"):
session = boto3.session.Session()
client = session.client(
service_name='secretsmanager',
region_name=region_name
)
try:
response = client.get_secret_value(SecretId=secret_name)
# The secret is returned as a string (JSON usually)
secret_string = response.get('SecretString')
if secret_string:
return json.loads(secret_string)
else:
# If the secret is binary, handle it differently
return response['SecretBinary']
except ClientError as e:
error_code = e.response['Error']['Code']
if error_code == 'ResourceNotFoundException':
print("Secret not found")
elif error_code == 'AccessDeniedException':
print("Permission denied")
else:
raise
# Use it
db_creds = get_secret("MyDatabaseSecret")
print(f"Connecting to DB as {db_creds['username']}")
# This is where you would use the credentials, e.g.,
# engine = create_engine(f"postgresql://{db_creds['username']}:{db_creds['password']}@host/db")
Expected output:
Connecting to DB as admin
Step 4: Handle missing secrets gracefully
In production, your code should fail with a clear message if the secret is missing, but not print the secret itself.
try:
db_creds = get_secret("MyDatabaseSecret")
print("Secret retrieved successfully")
except Exception as e:
print("ERROR: Could not retrieve secret")
# Log the error for debugging, but not the secret
raise SystemExit(1)
Compare options / when to choose what
You have several ways to store secrets on AWS. Here’s a quick comparison to help you choose.
| Option | Best for | Pros | Cons |
|---|---|---|---|
| AWS Secrets Manager | Dynamic secrets, rotation, fine-grained IAM | Built-in rotation, automatic encryption, versioning | Slightly higher cost per secret per month |
| AWS Systems Manager Parameter Store | Simple key-value pairs, low cost | Free tier, supports secure strings, integrates with other AWS services | No built-in rotation, smaller size limits |
| Environment variables | Simple, quick, non-sensitive config | Easiest to use, no AWS API call | Not encrypted at rest, can leak |
| S3 with server-side encryption | Static files, large blobs | Cheap, scalable | Manual encryption management, not ideal for small secrets |
For most production applications, Secrets Manager is the recommended choice if you need rotation or have secrets that change. If you have a tight budget and need only static secrets, Parameter Store’s SecureString is a viable alternative. For everything else, use Secrets Manager.
Pro tip: Start with Secrets Manager even if you think you don’t need rotation. The cost is minimal for a handful of secrets, and you avoid migration later.
Troubleshooting & edge cases
Here are common issues you might face and how to solve them.
- AccessDeniedException: Your IAM role/user lacks
secretsmanager:GetSecretValuepermission. Attach a policy likearn:aws:iam::aws:policy/SecretsManagerReadWrite(or a custom one) to the role. - ResourceNotFoundException: You typed the secret name wrong. Use the full ARN if you have multiple regions.
- SecretString is None: The secret might be binary. Use
SecretBinaryinstead, but most use cases are strings. - Caching secrets for performance: Calling
get_secret_valueevery time adds latency. Cache the secret in memory for a few minutes, but be aware of rotation — if you cache too long, you might use a stale secret. - Region mismatch: Secrets are regional. Ensure your Python client uses the same region as the secret.
For example, here’s a simple cache implementation:
import time
import boto3
import json
_cache = {}
def get_secret_cached(secret_name, region_name):
now = time.time()
if secret_name in _cache and (now - _cache[secret_name]['timestamp'] < 300):
return _cache[secret_name]['data']
# Fetch from AWS
client = boto3.client('secretsmanager', region_name=region_name)
response = client.get_secret_value(SecretId=secret_name)
secret_data = json.loads(response['SecretString'])
_cache[secret_name] = {'data': secret_data, 'timestamp': now}
return secret_data
What you learned & what's next
You now know how to store Python app secrets in AWS Secrets Manager: you can create a secret, retrieve it securely in Python, and handle errors. You also learned when to prefer Secrets Manager over Parameter Store or environment variables. Finally, you saw how to cache secrets for performance while staying aware of rotation.
Now that secrets are secure, the next step in your AWS Cloud & DevOps with Python journey is learning how to deploy your Python app to AWS using services like EC2 or ECS, where you can attach IAM roles that grant permission to fetch these secrets automatically. That’s where the real power of secure cloud-native development comes together.
Practice recap
Create a second secret in Secrets Manager (e.g., an API key for a service) and write a Python script that fetches it, adds a small TTL cache, and prints a masked version (e.g., show only the first 4 characters). Then modify your IAM policy to deny access from a test user and confirm your script raises AccessDeniedException—this will solidify your understanding of least-privilege access.
Common mistakes
- Hardcoding secrets in the codebase, which exposes them to anyone with repo access
- Using environment variables for sensitive data that should be encrypted at rest
- Not granting least-privilege IAM policies, leading to either too much or too little access
- Calling get_secret_value on every request without caching, causing unnecessary latency
- Storing secrets as plain text instead of using JSON or binary types, making parsing harder
Variations
- Use AWS Systems Manager Parameter Store (SecureString) for static secrets at a lower cost
- Store secrets as binary data if you have keys or certificates that are not JSON-compatible
- Implement a custom caching wrapper (e.g., with TTL) to reduce API calls and improve performance
Real-world use cases
- A Django app on EC2 retrieves its PostgreSQL credentials from Secrets Manager at startup
- A serverless Lambda function gets an API key for a third-party service from Secrets Manager on each invocation
- A CI/CD pipeline rotates database passwords via Secrets Manager and the app automatically picks up the new secret
Key takeaways
- AWS Secrets Manager encrypts secrets at rest with KMS and provides fine-grained IAM access control
- Always retrieve secrets via the SDK at runtime, never hardcode them in code or config files
- Use least-privilege IAM policies—only allow GetSecretValue on the specific secret ARN
- Cache secrets in memory to reduce latency, but respect rotation by using a short TTL
- Compare Secrets Manager vs Parameter Store based on rotation needs, cost, and secret size
- Troubleshoot errors by checking IAM permissions, region, and secret name or ARN
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.