Terraform 1.9: A Maturity Release
Terraform 1.9, released in June 2025, is one of the most significant releases since 1.0. HashiCorp responded to long-standing community requests: native secret management without state leakage, a more powerful built-in test framework, and better dependency management between stacks. For serious IaC teams, this is an update worth fast-tracking.
Ephemeral Variables: No More Secrets in State
This is the most anticipated feature of Terraform 1.9. Until now, every value used in a resource was persisted in the state file — including passwords, API keys, and certificates. Ephemeral variables are never written to state and are redacted in plans and logs.
variable "db_password" {
type = string
sensitive = true
ephemeral = true # Never written to state
}
resource "aws_db_instance" "main" {
password = var.db_password # Will not appear in state
}
# Ephemeral resource for dynamic secret retrieval
ephemeral "aws_secretsmanager_secret_version" "db_creds" {
secret_id = "prod/rds/credentials"
}
locals {
db_password = jsondecode(
ephemeral.aws_secretsmanager_secret_version.db_creds.secret_string
).password
}
This fundamentally changes the IaC security posture: no more manual state encryption specifically to protect secrets (though encryption remains recommended), and no risk of leakage when sharing plans.
Enhanced Terraform Test Framework
Terraform 1.9 significantly extends the test framework introduced in 1.6. Tests can now validate real resources created in an ephemeral AWS account, with automatic cleanup after the test run.
# tests/vpc_module.tftest.hcl
variables {
vpc_cidr = "10.0.0.0/16"
env_name = "test"
aws_region = "eu-west-1"
}
run "vpc_creation" {
command = apply
assert {
condition = aws_vpc.main.cidr_block == var.vpc_cidr
error_message = "VPC CIDR does not match parameter"
}
}
run "subnets_in_different_azs" {
command = apply
assert {
condition = length(distinct([
aws_subnet.private[0].availability_zone,
aws_subnet.private[1].availability_zone,
aws_subnet.private[2].availability_zone
])) == 3
error_message = "Private subnets must be in 3 different AZs"
}
}
Improved Check Blocks
Check blocks validate post-apply conditions without blocking the deployment — they emit warnings instead. In Terraform 1.9, they can reference data sources and perform complex assertions.
check "rds_backup_enabled" {
data "aws_db_instance" "check" {
db_instance_identifier = aws_db_instance.main.id
}
assert {
condition = data.aws_db_instance.check.backup_retention_period >= 7
error_message = "RDS backup retention must be at least 7 days"
}
}
Recommended Module Structure in 2025
modules/
terraform-aws-ecs-service/ # Naming: terraform-PROVIDER-RESOURCE
├── main.tf # Core resources
├── variables.tf # Variables with validation + ephemeral for secrets
├── outputs.tf # Outputs (ephemeral if sensitive)
├── versions.tf # Provider constraints
├── README.md # Generated by terraform-docs
└── tests/
├── basic.tftest.hcl # Plan-only tests
└── complete.tftest.hcl # Full apply + assertions
CI/CD Pipeline with GitHub Actions
- name: Terraform Test (plan only on PRs)
if: github.event_name == 'pull_request'
run: terraform test -filter=tests/basic.tftest.hcl
- name: Terraform Plan
run: terraform plan -out=tfplan
- name: Terraform Apply (main branch only)
if: github.ref == 'refs/heads/main'
run: terraform apply tfplan
State Management Best Practices
terraform {
backend "s3" {
bucket = "company-tfstate"
key = "prod/network/terraform.tfstate"
region = "eu-west-1"
dynamodb_table = "terraform-lock"
encrypt = true
}
}
Conclusion
Terraform 1.9 simultaneously strengthens security (ephemeral variables), reliability (enhanced tests), and governance (check blocks). For IaC teams managing critical infrastructure, the upgrade is well worth the effort. Our recommendation: adopt ephemeral variables immediately for any new code handling secrets, and plan the migration of existing modules over two sprints.
