The management of sensitive data—database credentials, API keys, and authentication tokens—requires a strategic approach to both storage and lifecycle management. AWS Secrets Manager provides a robust framework for this, but the intersection of infrastructure-as-code (IaC) and secret management often introduces complexity. In the Terraform ecosystem, the distinction between the aws_secretsmanager_secret resource and the aws_secretsmanager_secret_version resource is critical for any DevOps engineer or cloud architect. While the former defines the metadata and container of the secret, the latter manages the actual sensitive payload and its specific iterations.
Understanding the Secret vs. Secret Version Dichotomy
A common point of confusion for practitioners new to the AWS provider is the relationship between a secret and its version. In AWS Secrets Manager, a secret is essentially a metadata container. When you define a resource using aws_secretsmanager_secret, you are establishing the name, description, tags, and recovery window of the secret. However, creating the secret container does not automatically create a secret value.
To populate a secret with actual data, you must utilize the aws_secretsmanager_secret_version resource. This resource allows for the management of the secret string, which can be a simple plaintext string or a JSON-encoded object containing multiple key-value pairs. This decoupling is intentional; it allows administrators to update the value of a secret (by creating a new version) without altering the metadata or the resource identity of the secret itself.
Deep Dive into awssecretsmanagersecret_version Configuration
The aws_secretsmanager_secret_version resource is the primary mechanism for injecting sensitive data into AWS. It requires a link to a parent secret and the actual value to be protected.
Argument Reference and Implementation
The resource accepts several critical arguments that determine how the secret value is stored and tagged within AWS.
| Argument | Requirement | Description |
|---|---|---|
secret_id |
Required | The ID (usually the ARN) of the secret to which the version is attached. |
secret_string |
Optional/Implicit | The sensitive string value to store. This can be plaintext or JSON. |
version_stages |
Optional | A list of staging labels to attach to this version. |
Implementing Simple String Values
For basic use cases, such as a single API token, a simple string is sufficient. This approach is straightforward and requires minimal overhead.
```hcl
resource "awssecretsmanagersecret" "example" {
name = "my-simple-api-token"
}
resource "awssecretsmanagersecretversion" "example" {
secretid = awssecretsmanagersecret.example.id
secret_string = "example-string-to-protect"
}
```
Managing Key-Value Pairs via JSON
Modern applications often require multiple credentials for a single service (e.g., username, password, host, and port for a database). Secrets Manager supports JSON objects for these scenarios. In Terraform, the most efficient way to handle this is by using the jsonencode() function, which converts a Terraform map into a JSON string.
```hcl
variable "db_credentials" {
type = map(string)
default = {
key1 = "value1"
key2 = "value2"
}
}
resource "awssecretsmanagersecret" "database" {
name = "production/database/credentials"
}
resource "awssecretsmanagersecretversion" "database" {
secretid = awssecretsmanagersecret.database.id
secretstring = jsonencode(var.dbcredentials)
}
```
Advanced Versioning and Staging Labels
AWS Secrets Manager uses staging labels to help applications retrieve the correct version of a secret without needing to know the specific version ID. The most significant label is AWSCURRENT.
The Role of AWSCURRENT
By default, if no staging label is specified during the creation of a new secret version, AWS automatically assigns the AWSCURRENT label to that version. This informs any consuming application that this specific version is the active, authoritative value.
Staging labels must be unique. If you manually assign a label that already exists on another version of the same secret, AWS automatically removes that label from the old version and attaches it to the new one.
Managing Version Stages in Terraform
When using the version_stages argument, precision is required to avoid "perpetual diffs"—a state where Terraform constantly wants to update the resource even when no changes were made.
If version_stages is configured, you must include the AWSCURRENT label if:
- This is the only version of the secret.
- The label is already present on this version.
Failure to do so will cause Terraform to detect a difference between the local configuration and the remote state during every terraform plan cycle.
Deletion Nuances and the AWSCURRENT Label
Deleting a secret version through Terraform requires caution. If the AWSCURRENT staging label is present during the deletion of the aws_secretsmanager_secret_version resource, that label cannot be removed. To prevent critical errors during the deletion process, Terraform will skip the removal of this label.
Consequently, the secret version will remain active in AWS even after the resource has been removed from the Terraform state. To fully trigger version deprecation and ensure the version is inactive, you must move the AWSCURRENT label to another version before deleting the resource.
Integrating Random Password Generation
A best practice in secure infrastructure is to avoid hardcoding passwords in configuration files or version control. Terraform's random_password resource can be integrated directly with aws_secretsmanager_secret_version to automate the creation of high-entropy credentials.
```hcl
resource "randompassword" "database" {
length = 32
special = true
overridespecial = "!#$%&*()-_=+[]{}<>:?"
}
resource "awssecretsmanagersecret" "database" {
name = "production/database/credentials"
description = "Database credentials for the production environment"
recoverywindowin_days = 30
tags = {
Environment = "production"
ManagedBy = "terraform"
}
}
resource "awssecretsmanagersecretversion" "database" {
secretid = awssecretsmanagersecret.database.id
secretstring = jsonencode({
username = "admin"
password = randompassword.database.result
engine = "postgres"
host = "db.example.com"
port = 5432
dbname = "myapp"
})
}
```
Retrieving and Consuming Secrets
Once a secret version is established, other resources must be able to consume that data. This is achieved using data sources. This is particularly useful when secrets are managed in a separate Terraform workspace or were created manually in the AWS Console.
The Retrieval Workflow
Retrieving a secret is a two-step process: first, you reference the secret metadata, and second, you fetch the specific version's value.
- Reference the Secret: Use
data "aws_secretsmanager_secret"to find the ARN. - Fetch the Version: Use
data "aws_secretsmanager_secret_version"using that ARN. - Parse the Data: Use
jsondecode()to convert the secret string back into a usable map.
```hcl
data "awssecretsmanagersecret" "database" {
name = "production/database/credentials"
}
data "awssecretsmanagersecretversion" "database" {
secretid = data.awssecretsmanagersecret.database.id
}
locals {
dbcredentials = jsondecode(data.awssecretsmanagersecretversion.database.secret_string)
}
resource "awsdbinstance" "main" {
engine = "postgres"
instanceclass = "db.t3.medium"
username = local.dbcredentials["username"]
password = local.dbcredentials["password"]
dbname = local.db_credentials["dbname"]
# Other configuration...
}
```
Utilizing High-Level Modules
For organizations that prefer a standardized approach, community-maintained modules can simplify the deployment of Secrets Manager. The terraform-aws-modules/secrets-manager/aws module abstracts the complexity of managing separate secret and version resources.
These modules often integrate policy creation and random password generation into a single block. For instance, the module can handle the creation of the IAM policy required for a Lambda function to perform secret rotation by granting permissions for secretsmanager:DescribeSecret, GetSecretValue, PutSecretValue, and UpdateSecretVersionStage.
Example Module Implementation
```hcl
module "secrets_manager" {
source = "terraform-aws-modules/secrets-manager/aws"
nameprefix = "example"
description = "Example Secrets Manager secret"
recoverywindowindays = 30
createpolicy = true
blockpublic_policy = true
policy_statements = {
read = {
sid = "AllowAccountRead"
principals = [{ type = "AWS", identifiers = ["arn:aws:iam::1234567890:root"] }]
actions = ["secretsmanager:GetSecretValue"]
resources = ["*"]
}
}
createrandompassword = true
randompasswordlength = 64
randompasswordoverridespecial = "!@#$%^&*()+"
tags = {
Environment = "Development"
Project = "Example"
}
}
```
Attribute Reference and State Management
When managing secret versions, understanding the output attributes is essential for linking resources.
| Attribute | Description |
|---|---|
id |
A pipe-delimited combination of the secret ID and the version ID. |
version_id |
The unique identifier of the specific secret version. |
State Security Warning
It is imperative to remember that secret values managed via aws_secretsmanager_secret_version are stored in the Terraform state file in plaintext. To mitigate this risk, you must:
- Use a secure remote backend (such as S3 with encryption and strict IAM policies).
- Ensure state files are not committed to version control.
- Use write-only attributes where supported by the provider.
Importing Existing Secret Versions
If a secret version was created manually or by another tool, it can be brought under Terraform management using the terraform import command. The import process requires both the secret ARN and the version ID, separated by a pipe symbol.
Command format:
terraform import aws_secretsmanager_secret_version.example arn:aws:secretsmanager:region:account:secret:name-id|version-id
Example:
bash
terraform import aws_secretsmanager_secret_version.example arn:aws:secretsmanager:us-east-1:123456789012:secret:example-123456|xxxxx-xxxxxxx-xxxxxxx-xxxxx
Conclusion
The aws_secretsmanager_secret_version resource is a pivotal component for implementing a secure, automated credential management strategy within AWS. By separating the secret container (aws_secretsmanager_secret) from the secret value (aws_secretsmanager_secret_version), AWS allows for granular control over versioning, rotation, and retrieval.
The critical technical hurdles—such as managing the AWSCURRENT staging label and avoiding perpetual diffs—can be overcome by adhering to the strict configuration rules outlined. Integrating random_password resources ensures that credentials are never leaked in code, while the use of jsonencode and jsondecode provides a flexible way to handle complex credential sets. Whether using standalone resources for maximum control or high-level modules for rapid deployment, the key to a successful implementation is a rigorous approach to state security and a deep understanding of the AWS versioning lifecycle.
Sources
- typeerror.org/docs/terraform/providers/aws/r/secretsmanagersecretversion
- oneuptime.com/blog/post/2026-02-23-how-to-use-aws-secrets-manager-with-terraform/view
- awsfundamentals.com/terraform/secretsmanager/secretsmanager-secret-version-data
- support.hashicorp.com/hc/en-us/articles/37042317589651-AWS-Secrets-Manager-creates-secret-version-whereas-creating-secret-through-terraform-using-resource-aws-secretsmanager-secret-does-not-create-secret-version
- github.com/terraform-aws-modules/terraform-aws-secrets-manager