Use AWS Secrets Manager for Keys
Use AWS Secrets Manager for keys — Cloud security essentials. Learn why and how, with a hands-on exercise.
Focus: use aws secrets manager for keys
You’ve built the infrastructure, configured IAM roles, and locked down S3 buckets — but somewhere in your codebase, there’s still a hardcoded API key scrolling past in a git diff. That key is a ticking time bomb. One accidental push, one shared screenshot, one over-permissive log — and your database credentials, third-party API keys, or signing secrets are in the wild. The pain is real: rotating secrets manually is error-prone, auditing who accessed what is nearly impossible, and a leaked secret can cost you far more than a late-night rollback. This lesson shows you how to use AWS Secrets Manager for keys — the service that centralizes secret storage, automates rotation, and gives you fine-grained access control — so you can sleep at night knowing your keys are managed, monitored, and rotated without a single hardcoded line.
The problem this lesson solves
Hardcoded secrets are the number one entry point for credential compromise. Every developer has done it: you need a quick connection string, so you paste it into your config file, commit it, and move on. Then it lands in a public repo, an internal wiki, or a CI log — and suddenly your entire environment is exposed.
But the problem goes deeper than just the initial leak. Even if you avoid committing secrets, you’re likely managing them in spreadsheets, environment files, or chat messages. Each of those approaches has the same fundamental issues:
- No rotation policy — secrets stay valid forever, so a leak is a permanent backdoor.
- No audit trail — you can’t tell who accessed what and when.
- No centralized control — every service has its own copy, making revocation a nightmare.
AWS Secrets Manager directly solves this. It gives you a single, auditable, encrypted store for all your secrets — API keys, database credentials, OAuth tokens, you name it. You can retrieve them programmatically, set automatic rotation, and control access with IAM policies. No more hunting through code for leaked keys; you just revoke and rotate.
The urgency is real: data breaches from exposed credentials are among the most common and costly incidents. By centralizing secret management, you shrink the blast radius of any single leak and dramatically reduce the operational overhead of keeping secrets fresh.
Core concept / mental model
Think of AWS Secrets Manager as a vault with a concierge. You deposit your secrets — the actual keys, passwords, or tokens — and the vault handles the rest: encryption at rest, automatic rotation, and access logging. You never touch the raw secret directly in your code; instead, you ask the vault for the current version whenever you need it.
A useful analogy: you wouldn’t put your house keys under the doormat and hope no one finds them. You’d use a key box with a combination that only you and trusted family members know. Secrets Manager is that key box, but with the ability to change the combination automatically every week and to log every time someone opens it.
Here’s how it works at a high level:
- Store — You create a secret, either via the console, CLI, or API. Secrets Manager encrypts it with a KMS key (by default, a service-managed key) and stores it in a highly available backend.
- Control — You attach an IAM policy to the secret that defines who can access it and under what conditions. You can restrict by source IP, VPC, or even specific API calls.
- Retrieve — Your application calls
GetSecretValuewith the secret’s ARN. Secret Manager returns the current version, which you use in your code — but never hardcode. - Rotate — You configure a rotation schedule (e.g., every 30 days). Secrets Manager creates a new version, updates the credential on the underlying service (if you set up Lambda rotation), and then deprecates the old version.
- Audit — Every API call that accesses the secret is logged in CloudTrail, so you can see exactly who pulled what and when.
The key mental shift: secrets are dynamic, not static. Your code should never assume a secret’s value stays the same. Instead, it should always fetch the current version at runtime. This way, rotation becomes a non-event.
How it works step by step
Let’s walk through the core workflow, from creating a secret to retrieving it in your application, and finally setting up rotation.
Step 1: Create a secret
You can create a secret via the AWS Management Console, the AWS CLI, or the SDK. The console is great for a one-off, but for automation, the CLI is your friend.
aws secretsmanager create-secret \
--name my-app-db-credentials \
--secret-string '{"username":"admin","password":"SuperSecret123"}'
The output includes the ARN — the unique identifier you’ll use everywhere.
Step 2: Set up IAM access
By default, only the secret’s owner can access it. To grant your application or EC2 instance access, you attach an IAM policy that allows secretsmanager:GetSecretValue. Here’s a minimal policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "secretsmanager:GetSecretValue",
"Resource": "arn:aws:secretsmanager:us-east-1:123456789012:secret:my-app-db-credentials-*"
}
]
}
Attach this policy to your EC2 instance role, ECS task role, or Lambda execution role. Now the service can retrieve the secret without storing it anywhere.
Step 3: Retrieve the secret in your code
Here’s the beauty: your application code never sees the raw secret until it’s already decrypted. In Python, using boto3, retrieving a secret is a single call:
import boto3
import json
session = boto3.session.Session()
client = session.client(service_name='secretsmanager', region_name='us-east-1')
try:
response = client.get_secret_value(SecretId='my-app-db-credentials')
secret = json.loads(response['SecretString'])
username = secret['username']
password = secret['password']
# Use the credentials to connect to your database
except Exception as e:
# Handle error (e.g., secret not found, no permission)
print(f"Failed to retrieve secret: {e}")
Pro tip: Never log the secret value. Even in error messages, redact it. A common trap is printing
secretduring debugging — that defeats the purpose.
Step 4: Rotate the secret (automatically)
Rotation is where Secrets Manager shines. You can schedule automatic rotation using a Lambda function that updates the secret on the underlying service. For example, for an RDS database, the rotation Lambda generates a new password, updates the DB user, and then updates the secret in Secrets Manager.
To enable rotation:
aws secretsmanager rotate-secret \
--secret-id my-app-db-credentials \
--rotation-lambda-arn arn:aws:lambda:us-east-1:123456789012:function:rotate-db-credentials \
--rotation-rules AutomaticallyAfterDays=30
Once enabled, Secrets Manager automatically invokes the Lambda on the schedule. Your application fetches the current version each time it connects, so the rotation is transparent — as long as you retrieve the secret at runtime rather than caching it indefinitely.
Step 5: Audit with CloudTrail
Every access to your secret is recorded. To see who accessed what, query CloudTrail:
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=ResourceName,AttributeValue=my-app-db-credentials \
--query "Events[].CloudTrailEvent"
This gives you a full audit trail — invaluable for compliance and incident response.
Hands-on walkthrough
Time to practice. We’ll create a secret, give an EC2 instance access, and retrieve it from Python. If you don’t have an EC2 instance handy, you can use a local environment with AWS credentials configured — the principle is the same.
Prerequisites
- AWS CLI installed and configured (
aws configure) - Python 3.8+ with
boto3installed (pip install boto3) - An IAM role with permissions to create secrets and attach policies (or admin access for this exercise)
Step 1: Create a secret (console or CLI)
Let’s create a test secret using the CLI, so you see the JSON response:
aws secretsmanager create-secret \
--name demo-api-key \
--secret-string '{"api_key":"AKIAIOSFODNN7EXAMPLE"}'
Expected output (truncated):
{
"ARN": "arn:aws:secretsmanager:us-east-1:123456789012:secret:demo-api-key-0a1b2c",
"Name": "demo-api-key",
"VersionId": "9f0a1b2c-..."
}
Step 2: Write a Python script to retrieve the secret
Create a file get_secret.py:
import boto3
import json
def get_secret(secret_name, region="us-east-1"):
client = boto3.client("secretsmanager", region_name=region)
try:
response = client.get_secret_value(SecretId=secret_name)
return json.loads(response["SecretString"])
except client.exceptions.ResourceNotFoundException:
return None
if __name__ == "__main__":
secret = get_secret("demo-api-key")
if secret:
print(f"Retrieved API key: {secret.get('api_key')}")
else:
print("Secret not found")
Run it with credentials that have GetSecretValue permission:
python get_secret.py
Expected output:
Retrieved API key: AKIAIOSFODNN7EXAMPLE
Step 3: Test rotation (simulated)
To avoid setting up a full Lambda, let’s manually rotate a version and see how the secret updates. You can update the secret with a new value, then retrieve it again:
aws secretsmanager put-secret-value \
--secret-id demo-api-key \
--secret-string '{"api_key":"NEWKEY12345"}'
Now re-run your Python script — it picks up the new value. That’s the core of rotation: your app should always fetch the latest version.
Pro tip: In production, enable rotation with a Lambda, and set
RotationRules.AutomaticallyAfterDaysto something like 30 or 90, depending on your compliance requirements.
Compare options / when to choose what
Secrets Manager is not the only secret store on AWS. Here’s a quick comparison to help you decide:
| Feature | AWS Secrets Manager | AWS SSM Parameter Store | HashiCorp Vault |
|---|---|---|---|
| Managed rotation | Yes (native + Lambda) | No (manual) | Yes (via plugins) |
| Audit trail | CloudTrail integration | CloudTrail (limited) | Built-in audit log |
| Access control | IAM policies | IAM policies | Policy engine |
| Cost | $0.40 per secret/month | Free (standard) | Cost of running cluster |
| Use case | Production secrets needing rotation | Non-secrets config parameters | Multi-cloud / on-prem |
- AWS Secrets Manager is the right choice when you need automatic rotation, strong auditability, and fine-grained access control, especially for database credentials or API keys.
- SSM Parameter Store is cheaper and fine for non-secret configuration (like feature flags), but lacks native rotation.
- HashiCorp Vault shines if you have a multi-cloud or on-prem environment and want a uniform secret management layer across all of them — but it comes with operational overhead.
For this lesson’s focus — use AWS Secrets Manager for keys — Secrets Manager is the recommended default due to its integration with AWS services and its rotation features.
Troubleshooting & edge cases
Error: AccessDeniedException when calling GetSecretValue
- Cause: The IAM role/user lacks secretsmanager:GetSecretValue permission.
- Fix: Attach the policy shown earlier, and make sure the resource ARN matches (including the -* suffix for some cases). Test with aws secretsmanager get-secret-value --secret-id your-secret after attaching.
Error: ResourceNotFoundException
- Cause: The secret name/ARN is incorrect, or you’re in the wrong region.
- Fix: Double-check the spellling, and verify the region in both your CLI config and your boto3 client.
Secret value is stale after rotation
- Cause: Your application caches the secret value and doesn’t fetch a new one on each connection.
- Fix: Always call GetSecretValue at runtime, or use short-lived caching with a refresh interval (e.g., 5 minutes). Never hardcode or cache indefinitely.
SecretString vs SecretBinary
- Cause: When storing binary secrets (like an SSL cert), you use SecretBinary. The Python code above assumes JSON, which is SecretString.
- Fix: For binary, use response['SecretBinary'] and base64-decode it. Check your use case.
Rotation Lambda fails
- Cause: The Lambda role doesn’t have permission to call secretsmanager:UpdateSecretVersionStage or the underlying service API.
- Fix: Review the rotation Lambda’s IAM role; it needs secretsmanager:GetSecretValue, secretsmanager:PutSecretValue, and access to the target service (e.g., RDS). Check CloudWatch logs for the Lambda for detailed errors.
What you learned & what's next
You now understand why hardcoded secrets are a security liability and how AWS Secrets Manager for keys centralizes storage, enforces rotation, and provides auditability. You’ve seen how to create a secret, grant IAM access, retrieve it from Python, and enable rotation — plus how to troubleshoot common issues. You’re ready to apply this in your own projects: stop pasting credentials into code, and start treating secrets as dynamic resources your application fetches at runtime.
Next in the Cloud security essentials track, you’ll build on this foundation — likely covering how to enforce secret hygiene across your CI/CD pipelines or how to integrate Secrets Manager with container workloads (like ECS or EKS). Keep practicing: the more you automate secret rotation and access control, the stronger your cloud security posture becomes.
Practice recap
Create your own test secret, grant a role access, and write a Python function that retrieves it. Then simulate rotation by updating the secret and confirming your app picks up the new value automatically. For extra credit, set up a rotation Lambda on a throwaway database and observe the version changes.
Common mistakes
- Hardcoding secrets in application code or configuration files, which defeats the purpose of Secrets Manager.
- Caching the secret value indefinitely instead of fetching it on each use, which prevents rotation from taking effect.
- Not attaching the correct IAM policy to the application role, leading to AccessDeniedException.
- Storing secrets in SSM Parameter Store without rotation when you really need Secrets Manager's rotation features.
Variations
- Using AWS SSM Parameter Store as a lower-cost alternative for non-secret configuration.
- Integrating with HashiCorp Vault for multi-cloud secret management.
- Using AWS Secrets Manager with Kubernetes External Secrets to inject secrets into pods.
Real-world use cases
- Storing and rotating RDS database credentials for a web application automatically every 30 days.
- Centralizing third-party API keys (e.g., Stripe, Twilio) for microservices, with per-service IAM access.
- Managing SSL/TLS private keys securely for a CDN or load balancer, with automatic rotation and audit logging.
Key takeaways
- Centralize all secrets in AWS Secrets Manager to avoid hardcoded credentials in code.
- Use IAM policies to control access; your app fetches the secret at runtime via GetSecretValue.
- Enable automatic rotation to keep secrets fresh and reduce blast radius of leaks.
- Audit all access via CloudTrail for compliance and incident response.
- Choose Secrets Manager over SSM Parameter Store when you need rotation and advanced features.
- Always retrieve latest version—never cache secret values indefinitely.
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.