Managing Secrets with Secrets Manager
Complete the full lesson to earn 25 points
Work through each section, then tap “Mark as Complete” on the last one.
✦ Skip the page breaks and see fewer ads — read each lesson on a single page with Pro
Managing Secrets with AWS Secrets Manager in SDLC Automation
Introduction: The Critical Need for Secrets Management
In the modern software development lifecycle (SDLC), automation is the backbone of efficiency. When we build CI/CD pipelines using tools like AWS CodeBuild, we are constantly authenticating against various services—databases, third-party APIs, container registries, and internal microservices. These connections require credentials, such as API keys, database passwords, or private SSH keys. Historically, developers often stored these credentials in plain text within configuration files, environment variables in build scripts, or even hardcoded directly into the source code. This practice, while convenient in the short term, introduces massive security risks that can lead to data breaches, unauthorized system access, and catastrophic loss of intellectual property.
Secrets management is the discipline of protecting sensitive information by ensuring it is encrypted at rest, rotated regularly, and accessed only by authorized entities. AWS Secrets Manager provides a centralized, managed service that handles the lifecycle of these secrets. Instead of embedding a database password into your buildspec.yml file, you store that password in Secrets Manager and instruct your build process to fetch it dynamically at runtime. This shift from static configuration to dynamic retrieval is a fundamental pillar of secure DevOps. By adopting this approach, you reduce the blast radius of a potential credential leak, automate the tedious process of manual password updates, and maintain a clear audit trail of who accessed what secret and when.
In this lesson, we will explore how to integrate AWS Secrets Manager into your automated build workflows. We will move beyond the theory and look at how to architect your CI/CD pipelines to treat secrets as first-class, dynamic resources rather than static text files.
The Architecture of Secrets in CI/CD Pipelines
To understand how Secrets Manager functions within an SDLC, we must first look at the flow of data during a build process. When a developer pushes code to a repository, a trigger initiates a build container (such as AWS CodeBuild). This container needs to perform tasks that require privileged access.
Without a dedicated secrets manager, the build container typically relies on environment variables defined in the build project settings. While these are hidden from the console UI, they are often logged in build logs if not carefully masked, and they are static—meaning if a password changes, you must manually update the build project configuration in every environment.
The Dynamic Retrieval Model
When you integrate Secrets Manager, the architecture changes significantly. The build container is granted an IAM (Identity and Access Management) role that allows it to query the Secrets Manager API. During the build execution, the build script (or the CodeBuild configuration itself) requests the specific secret value. The service returns the secret over an encrypted channel, the build process uses it in memory to perform the required task (like pushing an image to a registry), and the secret is discarded when the build process completes.
Callout: Static vs. Dynamic Secrets Management Static secrets management involves hardcoding credentials or using environment variables that rarely change and are shared across many processes. This creates a "long-lived" credential that is highly vulnerable if leaked. Dynamic secrets management uses short-lived credentials or retrieves them from a secure vault at the exact moment of need. This minimizes the time a credential exists in a usable state, drastically reducing the impact of a potential compromise.
Setting Up AWS Secrets Manager: A Step-by-Step Guide
Before your build process can consume a secret, you must first store it securely. We will walk through the process of creating a secret for a database connection string.
Step 1: Create the Secret
- Navigate to the AWS Secrets Manager console.
- Click on "Store a new secret."
- Select "Other type of secret" (or choose "Credentials for Amazon RDS" if applicable).
- Enter the key-value pairs. For example, use
DB_USERNAMEas the key and your username as the value, then add another row forDB_PASSWORD. - Click "Next" and provide a descriptive name, such as
prod/myapp/db_credentials. - Configure the automatic rotation settings if your security policy requires it. For now, you can keep rotation disabled to focus on the retrieval mechanism.
- Click "Store."
Step 2: Granting IAM Permissions
An AWS CodeBuild project cannot read a secret just because it exists; it must have explicit permission. You must modify the IAM role associated with your CodeBuild project to include a policy that allows the secretsmanager:GetSecretValue action.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "secretsmanager:GetSecretValue",
"Resource": "arn:aws:secretsmanager:region:account-id:secret:prod/myapp/db_credentials-*"
}
]
}
Warning: Principle of Least Privilege Always restrict the
Resourcefield to the specific ARN of the secret you need. Avoid using a wildcard (*) for the resource. If you give a build process permission to access all secrets in your account, a compromise of the build container could lead to a total account takeover.
Consuming Secrets in AWS CodeBuild
There are two primary ways to bring secrets into your CodeBuild environment: using the env section of the buildspec.yml file or fetching them manually via the AWS CLI or SDK within your build commands.
Method 1: The env Block (Declarative)
The most efficient way to use Secrets Manager in CodeBuild is to define the secret directly in the buildspec.yml file. CodeBuild will automatically fetch the secret and inject it as an environment variable before any of your build commands run.
version: 0.2
env:
secrets-manager:
# The variable name that will be available in the build
DB_PASSWORD: "prod/myapp/db_credentials:password"
API_KEY: "prod/myapp/api_keys:external_service_key"
phases:
build:
commands:
- echo "Connecting to database..."
- ./run_migration.sh --password $DB_PASSWORD
How it works:
- CodeBuild interprets the
secrets-managersection during the initialization phase. - It makes an API call to Secrets Manager to resolve the provided paths.
- It maps the values to the environment variables
DB_PASSWORDandAPI_KEY. - These variables are now available to any shell command inside your build phases.
Method 2: Manual Retrieval via AWS CLI (Programmatic)
Sometimes you need more control, such as when you need to parse a complex JSON object stored in a secret or when you only need the secret for a specific sub-process. You can use the AWS CLI directly in your buildspec.yml.
phases:
install:
commands:
- apt-get update && apt-get install -y jq
build:
commands:
- # Fetch the JSON secret and use jq to extract a specific field
- SECRET_JSON=$(aws secretsmanager get-secret-value --secret-id prod/myapp/db_credentials --query SecretString --output text)
- DB_USER=$(echo $SECRET_JSON | jq -r .username)
- DB_PASS=$(echo $SECRET_JSON | jq -r .password)
- ./connect_db.sh -u $DB_USER -p $DB_PASS
This approach is highly flexible but requires you to manage the parsing logic manually. It is particularly useful if your secret contains multiple related values that you need to distribute to different tools during the build.
Comparison: Managing Secrets vs. Plain Environment Variables
It is helpful to compare the two methods to understand why Secrets Manager is the industry standard for production pipelines.
| Feature | Plain Environment Variables | AWS Secrets Manager |
|---|---|---|
| Storage | Plain text in Build Configuration | Encrypted at rest (KMS) |
| Access Control | IAM roles (broad) | IAM roles + Resource-based policies |
| Rotation | Manual update of build config | Automatic rotation supported |
| Auditability | Limited | Detailed logging via AWS CloudTrail |
| Exposure | Visible in console/logs if not masked | Never visible in plain text in console |
Note: Even when using Secrets Manager, you must still be cautious about logging. If your script
echoesthe environment variable to the build log, the secret will appear in plain text in your CI/CD history. Always use commands likeecho "Value loaded"rather thanecho $DB_PASSWORD.
Best Practices for Secure Secret Handling
Managing secrets is as much about process as it is about technology. Follow these best practices to maintain a hardened build environment.
1. Implement Secret Rotation
Secrets that never change are a liability. If a secret is leaked, it remains valid indefinitely. AWS Secrets Manager allows you to configure Lambda functions to automatically rotate passwords for supported services (like RDS). Even for third-party APIs, you can implement custom rotation logic that triggers a new key generation and updates the secret value automatically.
2. Use Separate Secrets for Environments
Never share the same secret between dev, staging, and prod. If a developer accidentally logs a secret while debugging a staging build, that same secret must be considered compromised for production. Use naming conventions like dev/myapp/db and prod/myapp/db to enforce strict isolation.
3. Audit Access with CloudTrail
Secrets Manager integrates with AWS CloudTrail. You should monitor GetSecretValue events to ensure that only expected build roles are accessing your secrets. If you see a developer's IAM user account calling GetSecretValue outside of a build context, it is a red flag that warrants investigation.
4. Mask Build Logs
CodeBuild provides a feature to mask sensitive information in logs. If a command happens to output a secret, CodeBuild can automatically replace it with ****. Ensure this is enabled in your project configuration, but do not rely on it as a primary defense—the best defense is ensuring the secret is never printed in the first place.
5. Avoid Storing Secrets in Version Control
This should go without saying, but it is the most common pitfall. Never commit .env files or hardcoded credentials to Git. Use pre-commit hooks to scan your repository for patterns that look like keys or passwords before they are pushed to the remote server.
Common Pitfalls and Troubleshooting
Pitfall 1: Incorrect IAM Permissions
The most common error is a 403 Access Denied when the build container tries to fetch a secret. This usually happens because the IAM role attached to the CodeBuild project does not have the secretsmanager:GetSecretValue permission for the specific resource ARN. Remember that if you use a KMS key to encrypt your secret, the IAM role must also have the kms:Decrypt permission for that specific key.
Pitfall 2: Environment Variable Collisions
If you define an environment variable in the CodeBuild project settings (via the AWS Console) and also try to inject it via the env block in buildspec.yml, the buildspec.yml value will typically take precedence. However, this can lead to confusing behavior where a developer changes the setting in the console but sees no change in the build. Always favor the buildspec.yml approach for consistency and version control.
Pitfall 3: The "Too Many Requests" Error
Secrets Manager has API rate limits. If you have a massive build system with thousands of concurrent builds all attempting to fetch the same secret at the exact same millisecond, you might hit throttling limits. In such cases, use caching mechanisms or ensure your build processes are staggered. For most standard CI/CD workloads, the default limits are more than sufficient.
Practical Example: Automating a Deployment with an API Key
Imagine you are deploying a frontend application to a CDN and you need to invalidate the cache using an API key from a service like Cloudflare or a similar provider.
The Workflow:
- Your
buildspec.ymldefines theAPI_KEYusing thesecrets-managerblock. - The
post_buildphase runs a command that calls the provider's API.
version: 0.2
env:
secrets-manager:
PROVIDER_API_KEY: "prod/cdn/provider_key:api_key"
phases:
post_build:
commands:
- echo "Invalidating CDN cache..."
- curl -X POST "https://api.provider.com/v1/purge" \
-H "Authorization: Bearer $PROVIDER_API_KEY" \
-H "Content-Type: application/json" \
--data '{"files": ["/*"]}'
By keeping the PROVIDER_API_KEY inside Secrets Manager, you can rotate that key in the provider's dashboard and update the secret in AWS without ever touching the source code or the build project settings. This decouples your security configuration from your deployment logic.
Advanced: Cross-Account Secret Access
In larger organizations, you might have a "Security Account" that centralizes all secrets and a "Development Account" where your CI/CD pipelines run. Secrets Manager supports cross-account access through resource-based policies.
To allow an IAM role in Account B (Development) to access a secret in Account A (Security):
- In Account A (Security): Attach a policy to the secret that grants
secretsmanager:GetSecretValueto the IAM role ARN from Account B. - In Account B (Development): Ensure the IAM role has the
secretsmanager:GetSecretValuepermission for the secret ARN in Account A.
This setup allows for a centralized "source of truth" for credentials across an entire enterprise. It simplifies auditing and compliance because you only have one location to review all secret usage across multiple AWS accounts.
Callout: Why Centralization Matters Centralizing secrets in a dedicated security account creates a single point of governance. Instead of having fragmented security policies scattered across dozens of accounts, your security team can enforce rotation policies, encryption standards, and access logs in one place. This significantly reduces the overhead of compliance audits (like SOC2 or PCI-DSS).
Frequently Asked Questions
Can I use Secrets Manager for non-AWS services?
Yes. Secrets Manager is simply a key-value store with access control. It has no knowledge of what the data represents. You can store database passwords, API keys for third-party SaaS, SSH private keys, or even configuration tokens for internal tools.
What happens if the secret is deleted?
If you delete a secret in Secrets Manager, it enters a "recovery window" (default is 30 days). During this time, the secret is inaccessible. If your build processes rely on that secret, they will fail immediately. Always be careful when deleting secrets and utilize the recovery window if you accidentally remove a production credential.
Does Secrets Manager cost money?
Yes. AWS charges a monthly fee per secret and a small fee per API call. While this is an additional cost, it is negligible compared to the cost of a credential leak or the man-hours required to manually rotate passwords across multiple environments.
Can I use Secrets Manager with Docker containers?
If you are running your build inside a container, the environment variable injection method works perfectly. The container runtime (CodeBuild) injects the variables into the shell environment, and your application code can then access them using standard methods (like os.getenv in Python or process.env in Node.js).
Key Takeaways
- Security First: Moving secrets out of source code and environment variables is the single most important step in securing your CI/CD pipeline. Secrets Manager provides the necessary encryption and access control to achieve this.
- Dynamic Retrieval: By retrieving secrets at build time, you ensure that your build process always has the most up-to-date credentials, enabling seamless rotation without downtime.
- IAM is Your Guardrail: The security of your secrets is only as good as your IAM policy configuration. Always adhere to the principle of least privilege, ensuring that only the specific build roles required have access to specific secrets.
- Audit and Monitor: Use CloudTrail to track secret access. Understanding who is accessing your secrets and when is critical for identifying potential security incidents before they escalate.
- Environment Isolation: Never share secrets across development, staging, and production environments. This prevents a compromise in a less secure environment from affecting your production systems.
- Masking is Not Enough: While log masking is a helpful feature in CodeBuild, it is a secondary safety net. Your primary goal should be to prevent sensitive data from ever hitting the logs through disciplined script writing.
- Infrastructure as Code: Treat your secret management configuration as part of your infrastructure. Use tools like Terraform or CloudFormation to manage the creation of secrets and their associated policies to ensure consistency across your environments.
By integrating Secrets Manager into your SDLC automation, you are building a robust and resilient pipeline that respects the sensitivity of your data. This approach not only protects your organization from external threats but also simplifies the operational burden of credential management, allowing your team to focus on shipping features rather than managing configuration headaches.
Continue the course
Enjoying the courses?
Everything stays free. Pro shows fewer ads, doubles your daily points limit so you progress twice as fast, and lets you read each lesson on one page.
- ✓ Fewer advertisements
- ✓ 2× daily points limit
- ✓ Distraction-free lessons