Infrastructure as code (IaC) brings automation, speed, and reusability. Still, it also introduces security challenges. Similar to managing application secrets in app code, managing secrets in infra code is crucial: A single leaked secret (like a database password or an API key) can compromise an entire environment.
1 A Brief Introduction to Terraform Secrets
Terraform manages infrastructure and resources, some of which require sensitive information to be managed, and this is where secrets are introduced. In Terraform, secrets are used to securely manage sensitive data such as passwords, API keys, TLS certificates, and other confidential information. Examples:
- When creating a database instance, you need to specify the password.
- When managing IAM roles, the access keys secrets are also a secret.
- When creating API keys for gateways, the API key is also a secret.
Secrets in Terraform sometimes are used as input to manage resources, other times, they are sensitive outputs that get used in other places.
Next, let's have a look at the similarities and differences between Terraform secrets and normal application secrets.
2 Similarities between Terraform Secrets and Application Secrets
2.1 Infrastructure as Code is Still Code
Since infrastructure code is still code, rule No.1 of managing secrets in code applies to Terraform as well: Do not store hard-coded secrets in code.
For example, when creating a database, DO NOT:
resource "aws_db_instance" "database" {
# ...
password = "notasecurepassword"
# ...
}
Instead, create a variables file and set the values via a .tfvars
file.
First, declare input variables for the password in variables.tf
:
variable "db_password" {
type = string
}
Then, create a secret.tfvars
file to assign values to the variable:
db_password = "insecurepassword"
So that when creating the resources, we can use the variable instead:
resource "aws_db_instance" "database" {
# ...
password = var.db_password
# ...
}
This removes the hard-coded secrets from the Terraform code, but still, remember that we need to make sure not to store secrets.tfvars
as clear text in the code repository. There are a couple of ways to do that, like file encryption, or set values when apply.
However, this is still some operational overhead. Since infrastructure code is still code, it's natural to think about using secrets managers like we do in application code.
2.2 Using Secrets Managers
With secrets managers, we can remove the sensitive values from the infrastructure code repo all together, and read from secrets managers when required.
For example, with AWS Secrets Manager, we can create a secret:
aws secretsmanager create-secret \
--name TestSecret \
--description "My test secret created with the CLI." \
--secret-string "{\"user\":\"testuser\",\"password\":\"testpassword123\"}"
Then in the Terraform code, we can read from it:
data "aws_secretsmanager_secret" "example" {
name = "TestSecret"
}
data "aws_secretsmanager_secret_version" "example" {
secret_id = data.aws_secretsmanager_secret.example.id
}
locals {
password = jsondecode(data.aws_secretsmanager_secret_version.example.secret_string)["password"]
}
Then we can simply use local.password
when needed.
See a working example of the demo above here.
3 Differences between Terraform Secrets and Application Secrets
If Terraform code were like a standard web application code, we would stop here, problems solved.
However, that isn't the case, and this is where Terraform code differs from normal application code.
3.1 Outputs
Unlike a normal application, Terraform manages infrastructure and resources, so it needs to output some critical information for us to use to continue managing those resources.
For example, I might need to know the password in the previous example, so I define an output:
output "password" {
value = local.password
}
And this is bad because when I apply, the password gets printed out on the screen. For long-time Terraform users who started years back, you may remember that there used to be no easy solution for this. Luckily, since Terraform 0.14 (Jan 2021), we have a new sensitive
flag that can protect sensitive information:
output "password" {
value = local.password
sensitive = true
}
Now when we apply this configuration, Terraform redacts the sensitive value from its console output.
When working with a sensitive field, it is best to enable the sensitive flag. This will prevent the field's values from showing up in CLI output (and in the cloud version of Terraform).
3.2 States and Plans
Terraform is a state machine, and this is also where Terraform code differs from normal application code. By definition, a state machine requires a state to work, and all information, including sensitive information, is stored in the state. Terraform requires careful handling of sensitive data to prevent exposure in plans and state files.
If we examine the state file from the previous example. We can see the value is still stored in the state as clear text:
{
...
"resources": [
...,
{
"type": "aws_secretsmanager_secret_version",
"name": "example",
"instances": [
{
"attributes": {
"secret_string": "{\"user\":\"testuser\",\"password\":\"testpassword123\"}",
...
},
}
]
}
],
...
}
Even with the sensitive
flag turned on for the output, the clear text secret is still there, because sensitive
will not encrypt or obscure the value in the state. Or, even without the output at all, the clear text secret is still there because it's also stored as part of the resource/data.
The same is true for Terraform plans. If we use terraform plan -out tfplan
to save the generated plan to a file on disk (useful when we want to pause/review before apply), which we can later execute by passing the file to terraform apply, then we can show the plan by terraform show -json tfplan
, and we can find out that the sensitive information is also stored as clear text in the plan.
For really long time Terraform users, there was a time when we used to encrypt/decrypt the state because back then local state was the only choice. However, encrypting State is discouraged now, instead, use remote backends that support encryption at rest. For example, an AWS S3 backend with encryption turned on.
So far, with secrets managers, the sensitive
flag, and the remote state, it is already orders of magnitude better than where we started in the very first example.
However, it's still not ideal, because those sensitive information is still stored in the state while it's not absolutely necessary. For example, why store the value of a secret from AWS Secrets Manager in the state when we can get it directly from AWS Secrets Manager? To solve this problem, we need ephemeral resources. Read on.
4 Ephemeral Resources
Ephemeral Resources are introduced in Terraform 1.10 (Nov 2024), and simply put, they allow Terraform to use the data while not storing that in the state (or plan). They can be used to perform tasks like grabbing secrets from secret managers, and other parts of the Terraform code can use the info they provide. Think of them as one-time tools that fetch or do something just in time, then disappear, leaving nothing in the state, which makes them perfect for managing sensitive information.
In the previous example, we used data
to fetch secrets from AWS Secrets Managers, but the secret value is still stored in the state as clear text. With ephemeral resources, we can achieve the same goal without storing any sensitive information in the state:
data "aws_secretsmanager_secret" "example" {
name = "TestSecret"
}
ephemeral "aws_secretsmanager_secret_version" "example" {
secret_id = data.aws_secretsmanager_secret.example.id
}
locals {
password = jsondecode(ephemeral.aws_secretsmanager_secret_version.example.secret_string)["password"]
}
We can inspect the state after applying to verify that ephemeral resources store nothing in the state:
{
...
"resources": [
{
...
"type": "aws_secretsmanager_secret",
"name": "example",
"instances": [
...
]
}
],
"check_results": null
}
We can see that the aws_secretsmanager_secret_version
ephemeral resource doesn't exist in the state, achieving a completely secure setup with no sensitive information stored anywhere.
See a working example of the demo above here.
Going one step forward, we can even use Terraform to:
- Create a secret in AWS Secrets Manager
- Generate a random password with ephemeral resources
- Store the generated password in the secret in AWS Secrets Manager
- Use ephemeral resources to access the password in the secret.
The following code achieves this:
resource "aws_secretsmanager_secret" "example" {
name = "TestSecret2"
}
ephemeral "aws_secretsmanager_random_password" "example" {}
resource "aws_secretsmanager_secret_version" "example" {
secret_id = aws_secretsmanager_secret.example.id
secret_string_wo = ephemeral.aws_secretsmanager_random_password.example.random_password
secret_string_wo_version = 1
}
ephemeral "aws_secretsmanager_secret_version" "example" {
depends_on = [aws_secretsmanager_secret_version.example]
secret_id = resource.aws_secretsmanager_secret.example.id
}
From here on, we can use ephemeral.aws_secretsmanager_secret_version
in other places, for example as the password of an RDS instance.
See a working example of the demo above here.
If you want to know more about ephemeral resources and how to use them, read Everything you Need to Know About Terraform's Ephemeral Resources.
5 Ephemeral Resources with Other Secrets Managers
While the previous sections and examples focus on AWS Secrets Manager, it's worth noting that those principles and Terraform ephemeral resources apply broadly across different cloud providers and different secrets managers.
For example, to read a secret from Infisical using ephemeral resources, all we have to do is:
ephemeral "infisical_secret" "example" {
name = "TestSecret"
env_slug = "dev"
workspace_id = "7d6a63e5-eb68-4bfd-8ca4-e0e289dcc4b9"
folder_path = "/"
}
locals {
password = ephemeral.infisical_secret.example.value
}
Then, similarly, we can use local.password
in other places when creating resources, leaving no trace of any sensitive information in the state.
Ready to secure your secrets with ephemeral resources? Try integrating Infisical or other secrets managers into your Terraform workflow today! Check out the working example on GitHub here (note that the example uses universal auth, read more about it here in the official documentation).
Keep your infrastructure secure and your state clean—start using ephemeral resources now!
6 Summary: Best Practices for Terraform Secrets Management
Managing secrets securely in Terraform is critical to protecting your infrastructure from exposure and breaches. Here's a concise summary of the best practices covered in this blog:
- Never Hardcode Secrets: Avoid storing sensitive data directly in Terraform files.
- Use variables (e.g.,
variables.tf
+.tfvars
to provide sensitive input) and ensure.tfvars
files are excluded from version control. - Secure Your Terraform State: Use secure remote backends (e.g., S3 with encryption), and consider restricting access to state files using IAM policies or equivalent role-based controls.
- Mark Sensitive Outputs: Always use
sensitive = true
for outputs containing secrets to prevent accidental exposure in logs or CLI output. - Leverage Secrets Managers: Use tools like AWS Secrets Manager, Infisical, or HashiCorp Vault to store and retrieve secrets dynamically when needed at runtime, reducing long-term exposures.
- Use Ephemeral Resources for Secrets: Ensure secrets are never stored in state, avoid Terraform state files persisting sensitive data.
Final thought: By combining secrets managers, ephemeral resources, and secure state management, you can maintain a robust secrets management workflow in Terraform and keep your infrastructure secure without compromising automation.
Ready to implement these best practices? Explore the full examples on GitHub:
- Use ephemeral resources to access AWS Secrets Manager
- Use ephemeral resources to create secrets
- Use ephemeral resources with Infisical
Secure your Terraform deployments today, and start using ephemeral resources and secrets managers!

FAQ
How do Terraform ephemeral resources improve secrets handling?
Ephemeral resources (Terraform 1.10+) let you use sensitive values during a plan/apply without persisting them in state or plans. Unlike traditional data sources that store fetched values in state, ephemeral blocks fetch secrets just-in-time and leave no trace after execution. This significantly reduces the risk of leaking passwords, API keys, or tokens via state files or saved plans.
Should I still use the sensitive flag if I use ephemeral resources?
Yes. The sensitive = true
flag prevents accidental exposure of values in CLI output and logs. Ephemeral resources stop secrets from being persisted in state and plans, while the sensitive flag protects runtime output. Use both together for defense-in-depth.
Does using AWS Secrets Manager mean secrets never appear in Terraform state?
Not by default. If you use standard data sources to read from AWS Secrets Manager, the retrieved secret can still land in Terraform state and plans. To avoid that, use ephemeral resources for fetching secrets and ensure you don’t expose values via outputs or locals that derive into persisted resources. Also store Terraform state in a secure, encrypted remote backend (e.g., S3 with encryption and strict IAM).
Can I generate and store a new password securely during apply?
Yes. You can use an ephemeral resource to generate a random password, write it into AWS Secrets Manager as a managed secret version, and then reference it via another ephemeral read—all without persisting the secret in Terraform state. This pattern enables secure bootstrapping for resources like RDS instances while keeping state clean.
Related reading:


