👉
TL;DR: Use a secrets manager and variables—never hardcode secrets. Mark outputs sensitive and store state remotely with encryption and strict access. Traditional data sources can leak to state; use Terraform 1.10 ephemeral resources to fetch/generate secrets at apply time without persisting them.

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:

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:

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:

The Promise and Pitfalls of Ephemeral Identities
Short-lived credentials reduce exposure – but they aren’t secure by default. Here’s what ephemeral identity gets right, and where it can fail.
How to Handle Secrets in Terraform
DevOps engineers must handle secrets with care. In this series, we summarize best practices for leveraging secrets with your everyday tools.