# Terraform Secrets Management: Keep Passwords Out of Code and State

> How to handle secrets in Terraform and OpenTofu: sensitive variables, AWS Secrets Manager, SSM, generated passwords, ephemeral values and state encryption.

- Source: https://www.itwonderlab.com/terraform-secrets-management/
- Published: 2026-07-05
- Updated: 2026-07-05
- Author: Javier Ruiz Jiménez (https://www.javierruizjimenez.com/)
- Site: IT Wonder Lab (https://www.itwonderlab.com/)

---

## Secrets and the Terraform state

The main issue with secrets in Terraform is that **anything a resource needs ends up in the state file in plain text**: database passwords, generated keys, certificate private keys. Hiding the value in the console output does not remove it from the [state](https://www.itwonderlab.com/terraform-state/). A good strategy has four parts.

### 1. Never put secrets in the code or in Git

Do not write passwords in `.tf` or `.tfvars` files that are committed. Scanners such as [Trivy and Checkov](https://www.itwonderlab.com/terraform-security-scanning-tflint-checkov-trivy/) detect many of them. Add `*.tfvars` with secrets and `.terraform/` to `.gitignore`.

### 2. Mark variables and outputs as sensitive

```hcl title="variables.tf"
variable "db_password" {
  type      = string
  sensitive = true
}

output "db_connection" {
  value     = "postgres://admin:${var.db_password}@${aws_db_instance.main.endpoint}/app"
  sensitive = true
}
```

This only redacts the value in the plan and output. Provide it at runtime: `TF_VAR_db_password` from the CI/CD secret store, never from a file in the repository.

### 3. Let AWS generate and store the secret

For RDS, the best option is to ask AWS to manage the master password in [Secrets Manager](https://www.itwonderlab.com/aws-secrets-manager/): Terraform never sees it.

```hcl title="rds.tf"
resource "aws_db_instance" "main" {
  identifier                  = "ditwl-pro-db"
  engine                      = "postgres"
  instance_class              = "db.t4g.micro"
  allocated_storage           = 20
  username                    = "app"
  manage_master_user_password = true
  storage_encrypted           = true
}
```

The secret ARN is in `aws_db_instance.main.master_user_secret[0].secret_arn`, and the application reads it at runtime with its [IAM role](https://www.itwonderlab.com/aws-iam/).

When you must create a secret yourself, create the container with Terraform and set the value outside of it:

```hcl title="secret.tf"
resource "aws_secretsmanager_secret" "api_key" {
  name       = "pro/app/api-key"
  kms_key_id = aws_kms_key.secrets.arn
}
```

Then run `aws secretsmanager put-secret-value` from a secure process. That way, the value never enters the state.

### 4. Ephemeral values and write-only arguments

Recent versions of Terraform (1.10 and later) and OpenTofu add **ephemeral resources** and **write-only arguments**: values that are used during the run but are never saved in the plan or state. This is the direction to follow for generated passwords and data read from secret stores. Support depends on the provider resource, so check the documentation for your versions before relying on it.

### 5. Protect the state itself

Whatever you do, assume the state contains sensitive data:

- Use a remote [backend](https://www.itwonderlab.com/terraform-backend/) with encryption at rest ([S3 with KMS](https://www.itwonderlab.com/aws-terraform-tutorial-terraform-backends/)), versioning and strict IAM access.
- In OpenTofu, enable [state encryption](https://www.itwonderlab.com/terraform-state-file-encryption/), which encrypts the state and the plan before they are written.
- Restrict who can run `terraform state pull` or read the bucket. Read access to the state is read access to every secret in it.
- Avoid [terraform_remote_state](https://www.itwonderlab.com/terraform-data-sources-remote-state/) to share values: it requires reading the whole state.

### Avoid long-lived credentials for Terraform itself

Do not store AWS access keys in the pipeline. Use short-lived credentials from [OIDC](https://www.itwonderlab.com/terraform-github-actions-aws-oidc/) or an instance role.

### Checklist

- [ ] No secrets in Git
- [ ] Sensitive variables and outputs marked
- [ ] Database passwords managed by AWS (`manage_master_user_password`) or Secrets Manager
- [ ] State in an encrypted, private, versioned backend
- [ ] State encryption enabled in OpenTofu when possible
- [ ] Short-lived credentials in CI/CD
