Referencing Secrets
This page explains how to reference existing AWS Secrets Manager secrets in your Terraform configuration.
Prerequisites
- Secret must already exist in AWS Secrets Manager (created manually or by customer)
- You must have the secret ARN
- Terraform execution role must have
secretsmanager:GetSecretValuepermission
Basic Configuration
Step 1: Define the Secret Reference
Add the secret reference to your secretsmanager_secrets variable:
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
arn |
String | Yes | Full ARN of the existing secret in Secrets Manager |
version_stage |
String | No | Version stage to retrieve (e.g., "AWSCURRENT", "AWSPENDING") |
version_id |
String | No | Specific version ID to retrieve |
Step 2: Use the Secret in Other Resources
The module makes the secret available for reference by other Terraform resources. The most common use case is domain join:
Version Management
Using Current Version (Default)
If no version is specified, the module retrieves the current version:
secretsmanager_secrets = {
my_secret = {
arn = "arn:aws:secretsmanager:us-east-2:123456789:secret:app/config-ABC123"
}
}
Using Specific Version Stage
Version stages are labels like "AWSCURRENT" or "AWSPENDING" that AWS manages:
secretsmanager_secrets = {
my_secret = {
arn = "arn:aws:secretsmanager:us-east-2:123456789:secret:app/config-ABC123"
version_stage = "AWSPENDING" # For testing new credential rotation
}
}
Using Specific Version ID
For precise control, use a specific version UUID:
secretsmanager_secrets = {
my_secret = {
arn = "arn:aws:secretsmanager:us-east-2:123456789:secret:app/config-ABC123"
version_id = "1234abcd-12ab-34cd-56ef-1234567890ab"
}
}
Multiple Secrets
You can reference multiple secrets in the same configuration:
secretsmanager_secrets = {
domain_join_prod = {
arn = "arn:aws:secretsmanager:us-east-2:123456789:secret:domain/prod-ABC123"
}
domain_join_nonprod = {
arn = "arn:aws:secretsmanager:us-east-2:123456789:secret:domain/nonprod-XYZ789"
}
api_key = {
arn = "arn:aws:secretsmanager:us-east-2:123456789:secret:app/apikey-DEF456"
}
}
Required IAM Permissions
The Terraform execution role needs permission to read the secret:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"secretsmanager:GetSecretValue",
"secretsmanager:DescribeSecret"
],
"Resource": "arn:aws:secretsmanager:us-east-2:271851283454:secret:sapphy/domainjoin-*"
}
]
}
Least Privilege Example
Restrict to specific secrets using wildcards:
{
"Effect": "Allow",
"Action": "secretsmanager:GetSecretValue",
"Resource": [
"arn:aws:secretsmanager:us-east-2:271851283454:secret:domain/*",
"arn:aws:secretsmanager:us-east-2:271851283454:secret:app/config-*"
]
}
Tag-Based Access Control
Restrict based on secret tags:
{
"Effect": "Allow",
"Action": "secretsmanager:GetSecretValue",
"Resource": "*",
"Condition": {
"StringEquals": {
"aws:ResourceTag/TerraformManaged": "true"
}
}
}
Security Best Practices
1. Never Store Secret Values in Terraform State
This module is designed to only reference secret ARNs, not retrieve values into state. The actual secret retrieval happens at runtime by EC2 instances or other AWS services.
❌ Don't do this:
# BAD - Exposes secret value in Terraform state
locals {
password = data.aws_secretsmanager_secret_version.my_secret.secret_string
}
✅ Do this instead:
# GOOD - Only reference the ARN, let services retrieve at runtime
domain_join = {
example = {
content = {
mainSteps = [{
inputs = {
secretArn = "arn:aws:secretsmanager:..."
}
}]
}
}
}
2. Use Separate Secrets Per Environment
secretsmanager_secrets = {
domain_join_dev = {
arn = "arn:aws:secretsmanager:us-east-2:111111111:secret:domain/dev-ABC"
}
domain_join_prod = {
arn = "arn:aws:secretsmanager:us-west-2:222222222:secret:domain/prod-XYZ"
}
}
3. Limit Terraform Role Permissions
Grant only the minimum required permissions:
secretsmanager:DescribeSecret- To get secret metadatasecretsmanager:GetSecretValue- Only if absolutely necessary (avoid if possible)
For domain join, EC2 instances retrieve secrets, not Terraform—so Terraform role doesn't need GetSecretValue.
4. Enable CloudTrail Logging
Monitor secret access with CloudTrail:
# Example CloudWatch alarm for unexpected secret access
resource "aws_cloudwatch_metric_alarm" "secret_access" {
alarm_name = "secrets-manager-unusual-access"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = "1"
metric_name = "SecretAccessCount"
namespace = "AWS/SecretsManager"
period = "300"
statistic = "Sum"
threshold = "10"
}
Troubleshooting
Secret Not Found
Error: ResourceNotFoundException: Secrets Manager can't find the specified secret
Solutions: - Verify the ARN is correct (check for typos) - Ensure the secret exists in the specified region - Check that you're using the correct AWS account
Access Denied
Error: AccessDeniedException: User is not authorized to perform: secretsmanager:GetSecretValue
Solutions:
- Verify Terraform execution role has secretsmanager:GetSecretValue permission
- Check IAM policy resource ARN matches the secret ARN
- Ensure there are no deny policies blocking access
Version Not Found
Error: ResourceNotFoundException: You provided a VersionId that does not exist
Solutions:
- Verify the version ID exists: aws secretsmanager list-secret-version-ids --secret-id <arn>
- Check version stage spelling (e.g., "AWSCURRENT" not "awscurrent")
- Ensure version hasn't been deleted
Common Patterns
Pattern 1: Domain Join (Primary Use Case)
# Reference the secret
secretsmanager_secrets = {
domain_creds = {
arn = "arn:aws:secretsmanager:us-east-2:123456789:secret:domain/join-ABC"
}
}
# Use in domain join
domain_join = {
CustomerDomain = {
ad_type = "self-managed"
content = {
mainSteps = [{
inputs = {
directoryName = "corp.example.com"
dnsIpAddresses = ["10.0.1.10", "10.0.1.11"]
secretArn = "arn:aws:secretsmanager:us-east-2:123456789:secret:domain/join-ABC"
}
}]
}
}
}
Pattern 2: Multi-Account Setup
# Secrets in different accounts for different environments
secretsmanager_secrets = {
dev_domain = {
arn = "arn:aws:secretsmanager:us-east-2:111111111:secret:domain/dev-ABC"
}
prod_domain = {
arn = "arn:aws:secretsmanager:us-west-2:222222222:secret:domain/prod-XYZ"
}
}
Pattern 3: Rotation Support
# Use AWSPENDING to test rotated credentials before promotion
secretsmanager_secrets = {
active_creds = {
arn = "arn:aws:secretsmanager:us-east-2:123456789:secret:app/db-ABC"
version_stage = "AWSCURRENT"
}
test_rotated_creds = {
arn = "arn:aws:secretsmanager:us-east-2:123456789:secret:app/db-ABC"
version_stage = "AWSPENDING"
}
}
Related Documentation
- Self-Managed AD Domain Join - Primary use case for this module
- Security Benefits - Credential delegation patterns