Managing AWS Secrets Manager with Terraform
A comprehensive guide to setting up AWS Secrets Manager using Terraform Infrastructure as Code
Managing AWS Secrets Manager with Terraform
AWS Secrets Manager helps you protect access to your applications, services, and IT resources. This guide shows how to set up Secrets Manager using Terraform.
Prerequisites
- AWS CLI configured
- Terraform installed
- Understanding of secret management
- Basic knowledge of KMS
Project Structure
aws-secrets-manager-terraform/
├── main.tf
├── variables.tf
├── outputs.tf
└── terraform.tfvars
Basic Secrets Manager Configuration
# main.tf
provider "aws" {
region = var.aws_region
}
# KMS Key for Encryption
resource "aws_kms_key" "secrets" {
description = "KMS key for Secrets Manager"
deletion_window_in_days = 7
enable_key_rotation = true
tags = {
Environment = var.environment
}
}
resource "aws_kms_alias" "secrets" {
name = "alias/${var.project_name}-secrets"
target_key_id = aws_kms_key.secrets.key_id
}
# Secret
resource "aws_secretsmanager_secret" "main" {
name = "${var.project_name}/${var.environment}/application"
description = "Application secrets for ${var.project_name}"
kms_key_id = aws_kms_key.secrets.arn
tags = {
Environment = var.environment
}
}
# Secret Version
resource "aws_secretsmanager_secret_version" "main" {
secret_id = aws_secretsmanager_secret.main.id
secret_string = jsonencode({
username = var.db_username
password = var.db_password
host = var.db_host
port = var.db_port
})
}
# Secret Policy
resource "aws_secretsmanager_secret_policy" "main" {
secret_arn = aws_secretsmanager_secret.main.arn
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "AllowAccessFromAccount"
Effect = "Allow"
Principal = {
AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"
}
Action = ["secretsmanager:GetSecretValue"]
Resource = "*"
}
]
})
}
Variables Configuration
# variables.tf
variable "aws_region" {
description = "AWS region"
type = string
default = "us-west-2"
}
variable "project_name" {
description = "Project name"
type = string
}
variable "environment" {
description = "Environment name"
type = string
default = "dev"
}
variable "db_username" {
description = "Database username"
type = string
sensitive = true
}
variable "db_password" {
description = "Database password"
type = string
sensitive = true
}
variable "db_host" {
description = "Database host"
type = string
}
variable "db_port" {
description = "Database port"
type = number
default = 5432
}
Best Practices
-
Secret Management
- Use meaningful names
- Implement rotation
- Version control secrets
- Regular secret reviews
-
Security
- Enable encryption
- Use proper IAM roles
- Implement secret policies
- Regular security reviews
-
Cost Optimization
- Monitor API calls
- Clean up unused secrets
- Use appropriate retention
- Regular cost reviews
-
Performance
- Cache secret values
- Use regional endpoints
- Monitor API limits
- Regular performance reviews
Automatic Secret Rotation
# Lambda Function for Rotation
resource "aws_lambda_function" "rotation" {
filename = "rotation_function.zip"
function_name = "${var.project_name}-secret-rotation"
role = aws_iam_role.rotation.arn
handler = "index.handler"
runtime = "nodejs14.x"
timeout = 30
environment {
variables = {
SECRETS_MANAGER_ENDPOINT = "https://secretsmanager.${var.aws_region}.amazonaws.com"
}
}
tags = {
Environment = var.environment
}
}
# Rotation Schedule
resource "aws_secretsmanager_secret_rotation" "main" {
secret_id = aws_secretsmanager_secret.main.id
rotation_lambda_arn = aws_lambda_function.rotation.arn
rotation_rules {
automatically_after_days = 30
}
}
# IAM Role for Lambda
resource "aws_iam_role" "rotation" {
name = "${var.project_name}-rotation-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "lambda.amazonaws.com"
}
}
]
})
}
# IAM Policy for Lambda
resource "aws_iam_role_policy" "rotation" {
name = "${var.project_name}-rotation-policy"
role = aws_iam_role.rotation.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"secretsmanager:DescribeSecret",
"secretsmanager:GetSecretValue",
"secretsmanager:PutSecretValue",
"secretsmanager:UpdateSecretVersionStage"
]
Resource = aws_secretsmanager_secret.main.arn
},
{
Effect = "Allow"
Action = [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
]
Resource = "arn:aws:logs:*:*:*"
}
]
})
}
Cross-Account Access
# Secret Policy for Cross-Account Access
resource "aws_secretsmanager_secret_policy" "cross_account" {
secret_arn = aws_secretsmanager_secret.main.arn
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "AllowCrossAccountAccess"
Effect = "Allow"
Principal = {
AWS = var.cross_account_arns
}
Action = [
"secretsmanager:GetSecretValue"
]
Resource = "*"
}
]
})
}
# IAM Role in Target Account
resource "aws_iam_role" "secret_access" {
provider = aws.target
name = "${var.project_name}-secret-access"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
AWS = var.source_account_arn
}
}
]
})
}
Monitoring Configuration
# CloudWatch Dashboard
resource "aws_cloudwatch_dashboard" "secrets" {
dashboard_name = "${var.project_name}-secrets-dashboard"
dashboard_body = jsonencode({
widgets = [
{
type = "metric"
x = 0
y = 0
width = 12
height = 6
properties = {
metrics = [
["AWS/SecretsManager", "GetSecretValue", "SecretId", aws_secretsmanager_secret.main.name],
["AWS/SecretsManager", "RotationSucceeded", "SecretId", aws_secretsmanager_secret.main.name]
]
period = 300
stat = "Sum"
region = var.aws_region
title = "Secrets Manager Activity"
}
}
]
})
}
# CloudWatch Alarms
resource "aws_cloudwatch_metric_alarm" "rotation_failed" {
alarm_name = "${var.project_name}-rotation-failed"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = "1"
metric_name = "RotationFailed"
namespace = "AWS/SecretsManager"
period = "300"
statistic = "Sum"
threshold = "0"
alarm_description = "This metric monitors secret rotation failures"
alarm_actions = [var.sns_topic_arn]
dimensions = {
SecretId = aws_secretsmanager_secret.main.name
}
}
Deployment Steps
- Initialize Terraform:
terraform init
- Plan the deployment:
terraform plan
- Apply the configuration:
terraform apply
Clean Up
Remove all resources when done:
terraform destroy
Common Use Cases
- Database Credentials
resource "aws_secretsmanager_secret" "database" {
name = "${var.project_name}/${var.environment}/database"
description = "Database credentials"
kms_key_id = aws_kms_key.secrets.arn
tags = {
Environment = var.environment
Type = "database"
}
}
resource "aws_secretsmanager_secret_version" "database" {
secret_id = aws_secretsmanager_secret.database.id
secret_string = jsonencode({
engine = "postgresql"
host = aws_db_instance.main.address
port = aws_db_instance.main.port
username = var.db_master_username
password = random_password.db_master_password.result
})
}
- API Keys
resource "aws_secretsmanager_secret" "api_keys" {
name = "${var.project_name}/${var.environment}/api-keys"
description = "API keys for external services"
kms_key_id = aws_kms_key.secrets.arn
tags = {
Environment = var.environment
Type = "api-keys"
}
}
resource "aws_secretsmanager_secret_version" "api_keys" {
secret_id = aws_secretsmanager_secret.api_keys.id
secret_string = jsonencode({
stripe_secret_key = var.stripe_secret_key
sendgrid_api_key = var.sendgrid_api_key
twilio_account_sid = var.twilio_account_sid
twilio_auth_token = var.twilio_auth_token
})
}
Advanced Features
# Replicate Secrets to Other Regions
resource "aws_secretsmanager_secret_replica" "replica" {
provider = aws.replica
primary_secret_arn = aws_secretsmanager_secret.main.arn
tags = {
Environment = var.environment
Type = "replica"
}
}
# Resource Policy for VPC Endpoint
resource "aws_secretsmanager_secret_policy" "vpc_endpoint" {
secret_arn = aws_secretsmanager_secret.main.arn
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "AllowVPCEndpointAccess"
Effect = "Allow"
Principal = "*"
Action = "secretsmanager:GetSecretValue"
Resource = "*"
Condition = {
StringEquals = {
"aws:SourceVpc": var.vpc_id
}
}
}
]
})
}
Conclusion
This setup provides a comprehensive foundation for deploying Secrets Manager using Terraform. Remember to:
- Plan your secret structure carefully
- Implement proper security measures
- Monitor secret usage and rotation
- Keep your configurations versioned
- Test thoroughly before production deployment
The complete code can be customized based on your specific requirements and use cases.