| name | terraform-modernization |
| description | Step-by-step Terraform upgrade and modernization guide. Use when migrating a Terraform project across major versions (0.12 → 0.13 → 0.14 → 0.15 → 1.0 → 1.7.x), fixing breaking changes, or adopting modern HCL patterns. Never skip a major version step. Use when this capability is needed. |
| metadata | {"author":"comeredon"} |
Terraform Modernization Skill
Upgrade Path
Only ever migrate one major version at a time:
0.12.x → 0.13.x → 0.14.x → 0.15.x → 1.0.x → 1.7.x
Before Every Version Step
- Back up state (PowerShell):
Copy-Item terraform.tfstate "terraform.tfstate.bak-$(Get-Date -Format 'yyyyMMdd-HHmmss')" -ErrorAction SilentlyContinue
- Tell the user exactly what version you are switching to and what will change.
- Ask for explicit confirmation before proceeding.
Per-Version Actions
0.12 → 0.13
terraform 0.13upgrade
terraform init -upgrade
terraform validate
terraform plan -out=tfplan-013
Breaking changes to fix:
0.13 → 0.14
terraform init -upgrade
terraform validate
terraform plan -out=tfplan-014
Actions:
0.14 → 0.15
terraform init -upgrade
terraform validate
terraform plan -out=tfplan-015
Breaking changes to fix:
| Deprecated pattern | Replacement |
|---|
list(string) type constraint shorthand list | list(string) (explicit) |
map shorthand | map(string) (explicit) |
null_resource without triggers | Add triggers = {} explicitly |
0.15 → 1.0
terraform init -upgrade
terraform validate
terraform plan -out=tfplan-100
Actions:
- Update
required_version = ">= 1.0".
- This version is intentionally backward-compatible with 0.15 — the step is mostly a version constraint bump.
- Review plan output carefully; no resource should be destroyed unless expected.
1.0 → 1.7.x
terraform init -upgrade
terraform validate
terraform plan -out=tfplan-173
Actions:
-
Update required_version = ">= 1.7" (or pin: "= 1.7.3").
-
Optional new features to adopt (non-breaking):
import block (GA) — declarative resource import:
import {
to = aws_s3_bucket.my_bucket
id = "my-existing-bucket"
}
removed block — remove from state without destroying:
removed {
from = aws_instance.old_server
lifecycle { destroy = false }
}
terraform test framework — write .tftest.hcl files:
run "s3_bucket_exists" {
command = plan
assert {
condition = aws_s3_bucket.main.bucket == "my-app-bucket"
error_message = "Unexpected bucket name."
}
}
Provider-defined functions — call functions shipped by providers:
output "arn_region" {
value = provider::aws::arn_parse(aws_s3_bucket.main.arn).region
}
Modern HCL Patterns (apply at any version ≥ 1.0)
Explicit required_providers with version constraints
terraform {
required_version = ">= 1.7"
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 3.0"
}
}
}
Variable validation
variable "environment" {
type = string
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "Must be dev, staging, or prod."
}
}
for_each over count for named resources
# Prefer this
resource "azurerm_resource_group" "envs" {
for_each = toset(["dev", "staging", "prod"])
name = "rg-${each.key}"
location = var.location
}
# Avoid this (index-based, brittle on deletion)
resource "azurerm_resource_group" "envs" {
count = 3
name = "rg-${count.index}"
location = var.location
}
Locals for computed values
locals {
common_tags = {
project = var.project_name
environment = var.environment
managed_by = "terraform"
}
}
Moved blocks (1.1+) — rename resources without destroy
moved {
from = azurerm_resource_group.old_name
to = azurerm_resource_group.new_name
}
State Safety Rules
Upgrade Checklist
Before marking a version step complete, verify all of the following:
Tooling Recommendations
| Tool | Purpose |
|---|
tfenv / tfswitch | Manage multiple Terraform binary versions side-by-side |
terraform fmt | Auto-format HCL to canonical style |
terraform validate | Catch syntax and type errors without a backend |
tflint | Provider-aware linting (e.g., deprecated arguments) |
checkov | Static security/compliance scanning of .tf files |
infracost | Cost estimation from plan output |
Anti-Patterns to Avoid
- Skipping a major version in the upgrade path
- Running
terraform apply before reviewing terraform plan
- Hard-coding credentials in
.tf files — use environment variables or a secrets backend
- Using
count for named resources (prefer for_each)
- Ignoring
.terraform.lock.hcl in version control
- Storing state locally in production
- Using
terraform taint (deprecated in 1.0 — use terraform apply -replace instead)
Source: comeredon/mymcp — distributed by TomeVault.