Modern cloud applications rely on secrets like API keys, database passwords, and encryption tokens. Storing these in plaintext or local files creates security risks. AWS Secrets Manager provides an encrypted solution, but integrating it into infrastructure requires careful setup.
This guide covers creating secrets, retrieving them via Terraform, and deploying them securely to EC2 instances. Follow along to implement a scalable secrets management workflow that meets enterprise security standards.
Why AWS Secrets Manager Matters for Cloud Applications
Environment variables and configuration files are common ways to pass secrets to applications. However, these methods expose sensitive data in logs, version control systems, or instance metadata. AWS Secrets Manager addresses this by:
- Automatically rotating credentials without downtime
- Encrypting secrets at rest using AWS KMS
- Providing fine-grained IAM access controls
- Centralizing secrets management across multiple services
For infrastructure-as-code deployments, integrating Secrets Manager ensures secrets remain secure throughout the application lifecycle.
Creating and Updating Secrets via CLI
Secrets can be created through the AWS Console or programmatically using the AWS CLI. The CLI approach works well for automation pipelines and infrastructure code. Here’s how to create a new secret:
aws secretsmanager create-secret \
--name "my_app/v1/credentials" \
--description "Application credentials for production environment" \
--secret-string '{
"api_key": "your-actual-api-key-here",
"database_password": "secure-password-123",
"encryption_key": "aes-256-key-value"
}'The command returns metadata about the secret, including its Amazon Resource Name (ARN) and unique version identifier. This metadata is crucial for later retrieval in infrastructure code.
Automating Secret Updates with a Script
Manually updating secrets via the AWS Console becomes tedious at scale. A Bash script can read from a local .env file, convert its contents to JSON, and update the secret in AWS Secrets Manager. The script handles both creation and updates:
#!/bin/bash
if [ ! -f .env ]; then
echo "Error: .env file not found in the current directory!"
exit 1
fi
# Initialize JSON string
SECRET_JSON="{"
FIRST_ENTRY=true
# Read .env file line by line
while IFS='=' read -r key value; do
# Skip empty lines and comments
[[ -z "$key" || "$key" =~ ^# ]] && continue
# Remove surrounding quotes from value
value=$(echo "$value" | sed 's/^"//;s/"$//')
# Add comma separator if not the first entry
if [ "$FIRST_ENTRY" = true ]; then
FIRST_ENTRY=false
else
SECRET_JSON="$SECRET_JSON,"
fi
# Add key-value pair to JSON string
SECRET_JSON="$SECRET_JSON\"$key\": \"$value\""
done < .env
SECRET_JSON="$SECRET_JSON}"
# Check if secret exists
if aws secretsmanager describe-secret --secret-id "my_app/v1/credentials" >/dev/null 2>&1; then
# Update existing secret
aws secretsmanager update-secret \
--secret-id "my_app/v1/credentials" \
--secret-string "$SECRET_JSON"
echo "✅ Secret updated successfully!"
else
# Create new secret if it doesn't exist
aws secretsmanager create-secret \
--name "my_app/v1/credentials" \
--description "Application credentials" \
--secret-string "$SECRET_JSON"
echo "✅ New secret created successfully!"
fiA sample .env file might look like:
api_key=abc123xyz456
database_password=secure-pass-789
db_host=production-db.cluster-xyz.us-east-1.rds.amazonaws.comRunning the script updates the secret in AWS. The JSON structure preserves the original key-value pairs, making it easy to reference specific values later.
Integrating Secrets Manager with Terraform Infrastructure
Terraform becomes more powerful when it can dynamically fetch secrets from AWS Secrets Manager. This approach avoids hardcoding sensitive values in configuration files. The integration requires two key components in your Terraform modules.
Retrieving Secret Metadata
First, define a data source to fetch the secret’s metadata, including its ARN. This doesn’t retrieve the actual secret value but provides the identifier needed for subsequent operations:
# modules/secrets/main.tf
data "aws_caller_identity" "current" {}
data "aws_secretsmanager_secret" "app_secrets" {
name = var.credentials_name
}The aws_caller_identity data source provides context about the current AWS account and IAM principal making the API calls. This is useful for logging and debugging.
Fetching the Actual Secret Value
Next, retrieve the secret value itself using its ARN. This data source fetches the latest version of the secret by default:
# modules/secrets/main.tf
data "aws_secretsmanager_secret_version" "current" {
secret_id = data.aws_secretsmanager_secret.app_secrets.id
}The secret value is stored as a JSON string. Terraform’s jsondecode function converts this into a map that can be referenced in other resources. For example, to expose the secret to an EC2 instance:
# modules/secrets/outputs.tf
output "secrets" {
value = jsondecode(data.aws_secretsmanager_secret_version.current.secret_string)
sensitive = true
}Setting sensitive = true ensures the values aren’t displayed in Terraform logs or outputs.
Deploying Secrets to EC2 Instances
With the secrets mapped in Terraform, they can be injected into EC2 instances during provisioning. Here’s how to pass the api_key as an environment variable to a Docker container running on the instance:
# main.tf
module "secrets" {
source = "./modules/secrets"
credentials_name = "my_app/v1/credentials"
}
module "ec2_instance" {
source = "./modules/ec2"
instance_ami = var.instance_ami
instance_type = var.instance_type
security_group_id = module.security_group.security_group_id
subnet_id = module.network.public_subnet_ids[0]
ssh_public_key = var.ssh_public_key
user_data = <<-EOF
#!/bin/bash
sudo apt-get update -y
sudo apt-get install -y docker.io
sudo systemctl start docker
sudo systemctl enable docker
sudo usermod -aG docker $USERNAME
# Start container with secrets as environment variables
sudo docker run -d -p 80:80 \
-e API_KEY=${module.secrets.secrets.api_key} \
-e DB_PASSWORD=${module.secrets.secrets.database_password} \
nginx
EOF
}This approach keeps secrets out of version control while maintaining their security during deployment. The values are only available to the instance at runtime.
Best Practices for Secure Secret Management
Implementing AWS Secrets Manager is just the first step. Follow these practices to maximize security:
- Use IAM roles instead of access keys for Terraform to interact with Secrets Manager
- Enable automatic secret rotation for credentials
- Restrict access using IAM policies tied to specific secret ARNs
- Monitor secret access with AWS CloudTrail
- Rotate secrets regularly, especially after personnel changes
- Avoid logging secret values in Terraform state files
As cloud architectures grow more complex, centralized secrets management becomes essential. AWS Secrets Manager provides the foundation for secure, scalable secret handling that integrates seamlessly with infrastructure-as-code tools like Terraform.
AI summary
Learn to integrate AWS Secrets Manager with Terraform for secure secret retrieval and management. Step-by-step guide to creating, updating, and deploying secrets in cloud infrastructure.