Terraform
Use write-only arguments
Write-only arguments let you securely pass temporary values to Terraform's managed resources during an operation without persisting those values to state or plan files. Use write-only arguments to handle sensitive data such as passwords, API tokens, and other secrets.
Background
Write-only arguments complement other ephemeral values in Terraform, letting you securely pass sensitive data throughout your configuration without ever storing it in Terraform's artifacts. For example, you can generate a random password using an ephemeral
resource then pass it to a write-only argument on another resource
block. The provider uses the write-only argument value to configure the resource, then Terraform discards the value without storing it.
Hands-on: Declare a write-only argument in the Upgrade RDS major version tutorial.
Unlike other ephemeral constructs in Terraform, such as ephemeral resources or variables, write-only arguments accept both ephemeral and non-ephemeral values.
Requirements
To use write-only arguments, you must use Terraform v.1.11 or later and use a resource that supports write-only arguments.
Declare a write-only argument
Providers indicate in the Terraform registry whether an argument is write-only. For example, the aws
provider's aws_db_instance
resource has a write-only password_wo
argument. The password_wo
argument accepts a value to use as the database password:
resource "aws_db_instance" "test" {
instance_class = "db.t3.micro"
allocated_storage = "5"
engine = "postgres"
username = "example"
skip_final_snapshot = true
password_wo = <ephemeral or non-ephemeral value>
password_wo_version = 1
}
Write-only arguments accept both ephemeral and non-ephemeral values. For example, you could also use a string as the value of a write-only argument:
resource "aws_db_instance" "test" {
instance_class = "db.t3.micro"
allocated_storage = "5"
engine = "postgres"
username = "example"
skip_final_snapshot = true
password_wo = "my-password-here"
password_wo_version = 1
}
However, we recommend using write-only arguments for passing ephemeral values to resources. For example, you can use an ephemeral
resource to generate a random password and pass it to the password_wo
write-only argument:
ephemeral "random_password" "db_password" {
length = 16
override_special = "!#$%&*()-_=+[]{}<>:?"
}
resource "aws_db_instance" "example" {
instance_class = "db.t3.micro"
allocated_storage = "5"
engine = "postgres"
username = "example"
skip_final_snapshot = true
password_wo = ephemeral.random_password.db_password.result
password_wo_version = 1
}
During a Terraform operation, the provider uses the password_wo
value to create the database instance, and then Terraform discards that value without storing it in the plan or state file.
Note that the way this is written, the password_wo
value is lost after Terraform generates unless we capture it in another resource or output. For an example of generating, storing, retrieving, and using an ephemeral password as a write-only argument, refer to the expanded example below.
Update write-only arguments with versions
Terraform does not store write-only arguments in state files, so Terraform has no way of knowing if a write-only argument value has changed. Because Terraform cannot track write-only argument values, it sends write-only arguments to the provider during every operation.
Terraform also cannot create plan diffs for write-only arguments because it does not store those values in plan files. However, providers typically include version arguments alongside write-only arguments. Terraform stores version arguments in state, and can track if a version argument changes.
Providers implement version arguments to let practitioners track write-only argument values and control when a provider uses those write-only arguments. The implementation of write-only arguments and their version arguments is provider-specific, so consult the Registry for more details about your specific provider.
For example, the aws_db_instance
resource has an accompanying password_wo_version
argument for the password_wo
write-only argument:
resource "aws_db_instance" "test" {
instance_class = "db.t3.micro"
allocated_storage = "5"
engine = "postgres"
username = "example"
skip_final_snapshot = true
password_wo = "old-password-here"
password_wo_version = 1
}
The provider uses the write-only argument value when creating the aws_db_instance
resource and Terraform stores the password_wo_version
argument value in state.
To trigger an update of a write-only argument, increment the version argument's value in your configuration:
resource "aws_db_instance" "main" {
instance_class = "db.t3.micro"
allocated_storage = "5"
engine = "postgres"
username = "example"
password_wo = "new-password-here"
password_wo_version = 2
}
When you increment the password_wo_version
argument, Terraform notices that change in its plan and notifies the aws
provider. The aws
provider then uses the new password_wo
value to update the aws_db_instance
resource.
Example
You can use an ephemeral
resource to generate a random password, store it in AWS Secrets Manager, and then retrieve it using another ephemeral
resource. Finally, you can pass the password to the password_wo
write-only argument of the aws_db_instance
resource:
ephemeral "random_password" "db_password" {
length = 16
override_special = "!#$%&*()-_=+[]{}<>:?"
}
resource "aws_secretsmanager_secret" "db_password" {
name = "db_password"
}
resource "aws_secretsmanager_secret_version" "db_password" {
secret_id = aws_secretsmanager_secret.db_password.id
secret_string_wo = ephemeral.random_password.db_password.result
secret_string_wo_version = 1
}
ephemeral "aws_secretsmanager_secret_version" "db_password" {
secret_id = aws_secretsmanager_secret_version.db_password.secret_id
}
resource "aws_db_instance" "example" {
instance_class = "db.t3.micro"
allocated_storage = "5"
engine = "postgres"
username = "example"
skip_final_snapshot = true
password_wo = ephemeral.aws_secretsmanager_secret_version.db_password.secret_string
password_wo_version = aws_secretsmanager_secret_version.db_password.secret_string_wo_version
}
In the above example, the ephemeral resource aws_secretsmanager_secret_version
references an argument that Terraform initially does not know. Terraform defers executing aws_secretsmanager_secret_version
until the apply stage, to ensure that Terraform evaluates the resource after it has the information it needs.
Terraform first creates the secret in AWS Secrets Manager using the ephemeral random_password
, then retrieve it using the ephemeral aws_secretsmanager_secret_version
resource, and finally write the password to the write-only password_wo
argument of the aws_db_instance
resource.