Stage 4 · Provision
Validation, Policy & Secrets
Vault & Provider Secrets
Vault data sources, dynamic credentials, sensitive outputs, and avoiding plaintext tfvars — secrets management for Terraform.
Vault Overview
HashiCorp Vault is a secrets management tool that provides dynamic credentials, encryption as a service, and access control. The Vault provider lets Terraform read secrets from Vault and generate dynamic credentials.
provider "vault" {
address = "https://vault.example.com"
# Authentication handled by Vault method
}
# Or use Kubernetes auth
provider "vault" {
address = "https://vault.example.com"
auth_login {
path = "auth/kubernetes/login"
parameters = {
role = "terraform"
jwt = var.vault_jwt
}
}
}The Vault provider connects to a Vault server. Authentication uses tokens, Kubernetes service accounts, or other methods. The provider reads secrets and generates dynamic credentials.
Vault Data Source
data "vault_generic_secret" "database" {
path = "secret/data/prod/database"
}
resource "aws_db_instance" "main" {
username = data.vault_generic_secret.database.data["username"]
password = data.vault_generic_secret.database.data["password"]
}
# KV v2 secrets engine
data "vault_kv_secret_v2" "database" {
mount = "secret"
name = "prod/database"
}
# Specific field
output "db_password" {
value = data.vault_kv_secret_v2.database.data["password"]
sensitive = true
}The vault_generic_secret data source reads secrets from Vault. The path specifies the secret location. The data attribute contains the secret values. Use sensitive = true to prevent output logging.
Dynamic Credentials
data "vault_mount" "database" {
path = "database"
}
resource "vault_database_secret_backend_role" "terraform" {
backend = data.vault_mount.database.path
name = "terraform"
db_name = "postgres"
default_ttl = 3600
max_ttl = 86400
creation_statements = [
"CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}';",
"GRANT ALL PRIVILEGES ON DATABASE mydb TO \"{{name}}\";",
]
}
data "vault_generic_secret" "db_credentials" {
path = "database/creds/terraform"
}
resource "aws_db_instance" "main" {
username = data.vault_generic_secret.db_credentials.data["username"]
password = data.vault_generic_secret.db_credentials.data["password"]
}Vault generates short-lived database credentials. The TTL controls how long the credentials are valid. After the TTL, Vault revokes the credentials automatically. This eliminates long-lived credentials.
Sensitive Outputs
output "database_password" {
description = "Database password from Vault"
value = data.vault_generic_secret.database.data["password"]
sensitive = true
}
output "database_connection" {
description = "Database connection string"
value = "postgresql://${data.vault_generic_secret.database.data["username"]}:${data.vault_generic_secret.database.data["password"]}@${aws_db_instance.main.endpoint}"
sensitive = true
}
# Mark entire output as sensitive
output "all_secrets" {
value = {
db_password = data.vault_generic_secret.database.data["password"]
api_key = data.vault_generic_secret.api.data["key"]
}
sensitive = true
}sensitive = true prevents the value from appearing in plan or apply output. The value is still stored in state. Use sensitive for all secrets, even those from Vault.
Avoiding Plaintext
# Use Vault instead of variables for secrets
variable "db_password" {
type = string
sensitive = true
}
# Better: read from Vault
data "vault_generic_secret" "database" {
path = "secret/prod/database"
}
# Use the Vault value, not the variable
resource "aws_db_instance" "main" {
password = data.vault_generic_secret.database.data["password"]
}
# Or use the Vault provider directly
resource "vault_generic_secret" "config" {
path = "secret/config"
data_json = jsonencode({
api_key = var.api_key
})
}Never use .tfvars files for secrets. Use Vault or environment variables instead. The Vault data source reads secrets at plan time without storing them in files.
# Set VAULT_TOKEN environment variable
# export VAULT_TOKEN=hvs.CAESI...
# Or use AppRole
data "vault_approle_login" "terraform" {
role_id = var.vault_role_id
secret_id = var.vault_secret_id
}
provider "vault" {
address = "https://vault.example.com"
token = data.vault_approle_login.terraform.client_token
}Vault tokens can come from environment variables, AppRole, or Kubernetes auth. Never store Vault tokens in code or .tfvars files. Use the most secure method available for your environment.
Best Practices
- Use Vault for all secrets — never hardcode or use .tfvars.
- Use dynamic credentials with short TTLs.
- Mark all secret outputs as sensitive.
- Use Vault namespaces for environment isolation.
- Rotate Vault tokens regularly.
- Audit Vault access for compliance.
- Use AppRole or Kubernetes auth — not static tokens.
The Vault data source reads secrets during terraform plan. This means secrets are available for validation and output without storing them in files. This is the recommended approach for Terraform secrets.
Vault tokens used by the provider are stored in the state file. Use short-lived tokens and enable state encryption. Consider using a Vault token that has limited permissions.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.