Stage 4 · Provision
Modules & Composition
Module Upgrade Strategy
Version constraints, changelogs, deprecation windows, moved blocks, and upgrade playbooks — evolving modules safely.
Upgrade Challenges
Module upgrades are risky because they affect every consumer. A breaking change in a shared module can break dozens of environments simultaneously. Safe upgrades require planning, communication, and testing.
Version Constraints
# Production — exact version
module "vpc" {
source = "registry.example.com/org/vpc/aws"
version = "2.3.1"
}
# Staging — patch updates
module "vpc" {
source = "registry.example.com/org/vpc/aws"
version = "~> 2.3.1"
}
# Development — minor updates
module "vpc" {
source = "registry.example.com/org/vpc/aws"
version = "~> 2.3"
}Use exact versions in production for maximum safety. Use patch-level constraints in staging for automatic bug fixes. Use minor-level constraints in development for feature access.
Changelogs
# Changelog
## [2.4.0] - 2024-01-15
### Added
- Support for IPv6 CIDR blocks
- Flow log configuration variable
### Changed
- Default CIDR changed from 10.0.0.0/16 to 10.0.0.0/20
### Deprecated
- `enable_flow_logs` variable (use `flow_log_config` instead)
### Fixed
- Subnet route table association for multi-AZ deployments
## [2.3.1] - 2024-01-01
### Fixed
- NAT gateway route propagation for private subnets
## [2.3.0] - 2023-12-15
### Added
- `custom_tags` variable for additional resource tagsThe changelog follows Keep a Changelog format. Each version documents additions, changes, deprecations, fixes, and removals. Consumers can quickly assess upgrade impact.
Deprecation Windows
variable "enable_flow_logs" {
description = <<-EOT
DEPRECATED: Use flow_log_config instead.
This variable will be removed in v3.0.0.
EOT
type = bool
default = null
}
locals {
# Map deprecated variable to new variable
flow_log_enabled = var.enable_flow_logs != null ? var.enable_flow_logs : var.flow_log_config.enabled
}The deprecation warning in the description alerts consumers. The local variable maps the deprecated input to the new one. Consumers receive warnings during plan until they migrate.
Moved Blocks for Upgrades
# In the module, add moved blocks for renamed resources
moved {
from = aws_subnet.public
to = aws_subnet.public[0]
}
moved {
from = aws_subnet.private
to = aws_subnet.private[0]
}
# For renamed variables, use locals to bridge
locals {
old_to_new = {
instance_type = var.old_instance_type
min_count = var.old_min_count
}
}Moved blocks handle resource renames within the module. Local variables bridge renamed inputs. Both patterns allow consumers to upgrade without changing their configurations.
Upgrade Playbooks
# Module Upgrade Playbook
## Upgrade: vpc module 2.x to 3.x
### Prerequisites
- [ ] Review CHANGELOG.md for breaking changes
- [ ] Test upgrade in dev environment
- [ ] Back up state files
### Steps
1. Update version constraint to ~> 3.0
2. Run terraform plan and review changes
3. Update any deprecated variables
4. Apply to dev environment
5. Run tests
6. Apply to staging
7. Run tests
8. Apply to production
### Rollback
- Revert version constraint to ~> 2.3
- Run terraform plan to verify no destructive changes
- Apply rollback
### Post-Upgrade
- [ ] Update documentation
- [ ] Remove deprecated variable support
- [ ] Update CI pipeline if neededThe upgrade playbook provides step-by-step instructions. It includes prerequisites, steps, rollback procedure, and post-upgrade tasks. Each upgrade should have its own playbook.
Before upgrading a production module, test in an isolated environment. Create a temporary Terraform configuration, apply the upgrade, and verify behavior. Delete the test environment after validation.
Removing outputs, changing variable types, or renaming resources are breaking changes. These must increment the major version. Failing to do so breaks consumers who use ~> constraints.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.