Terraform provider dependency lock files
Terraform dependency lock files provide versioning guardrails for infrastructure code, ensuring consistent provider versions across teams and environments while enhancing security. Introduced in Terraform 0.14 (December 2020), these .terraform.lock.hcl
files track exact provider versions and verify package integrity through cryptographic checksums, solving critical challenges in infrastructure management.
What are Terraform provider dependency lock files and why they matter
Terraform provider dependency lock files are special HCL files (named .terraform.lock.hcl
) that record and lock the specific provider versions selected during initialization, along with cryptographic checksums to verify provider package integrity. They ensure consistent provider selection across different environments and team members, making infrastructure deployments more predictable and reproducible.
These lock files solve several critical infrastructure management challenges:
- Reproducibility and consistency - Without lock files, Terraform would select the latest provider version matching configuration constraints each time
terraform init
ran, potentially causing inconsistent behavior across environments or team members. - Supply chain security - Lock files include cryptographic checksums that Terraform verifies during installation, protecting against tampered or compromised provider packages.
- Cross-platform collaboration - They help ensure consistent provider selection regardless of platform (Linux, macOS, Windows), facilitating smooth collaboration in diverse teams.
Lock files operate at the root module level, applying to the entire configuration including all child modules. They're designed to be committed to version control alongside Terraform configuration files, enabling review of dependency changes through normal code review processes.
The anatomy of .terraform.lock.hcl files
The .terraform.lock.hcl
file uses HashiCorp Configuration Language (HCL) syntax but is not a Terraform configuration file (hence the .hcl
extension rather than .tf
). It primarily consists of provider
blocks that track selected versions and verification data.
A typical .terraform.lock.hcl
file has this structure:
# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.
provider "registry.terraform.io/hashicorp/aws" {
version = "4.5.0"
constraints = ">= 4.5.0"
hashes = [
"h1:6y12cTFaxpFv4qyU3gkV9M15eSBBrgInoKY1iaHuhvg=",
"zh:0573de96ba316d808be9f8d6fc8e8e68e0e6b614ed4d8d11eed83062c9b60714",
"zh:37560469042f5f43fdb961eb6c6b7f6e0bccec04c1c7cbf90f5d6d97893e6c3d",
"zh:44bb4725649b8784f5a2db4c3e5ef83c599ec3f4609db1c3de707c9339844e74",
# Additional hash entries...
]
}
Each provider block contains:
- A label with the provider's fully qualified address (e.g.,
registry.terraform.io/hashicorp/aws
) version
: The exact provider version Terraform selectedconstraints
: The version constraints from the configuration that were used to select this versionhashes
: A list of cryptographic checksums used to verify provider package integrity
The checksums use different prefixes to indicate the hashing scheme:
h1:
prefix: Hash scheme 1 (SHA256 hash of the provider package contents)zh:
prefix: Legacy "zip hash" scheme (SHA256 hash of the .zip package itself)
When lock files are created and updated
The .terraform.lock.hcl
file is created and updated during specific points in the Terraform workflow:
- Initial creation: When running
terraform init
for the first time in a directory, Terraform selects provider versions based on constraints in configuration and creates the lock file. - Normal updates: The lock file is updated when running
terraform init
and either:- New providers are added to the configuration
- The lock file doesn't exist
- The
-upgrade
flag is specified
- Opportunistic updates: Terraform may add new hashes to the file (without changing versions) when it installs a provider on a new platform and can verify it against existing hashes.
- Explicit updates: Using the
terraform providers lock
command to pre-populate hashes for specific platforms.
The lock file is not updated during:
terraform plan
operationsterraform apply
operationsterraform destroy
operations
These commands use the provider versions specified in the lock file but won't modify it.
How lock files work with provider versioning
Lock files form a critical part of Terraform's dependency management system by establishing a clear separation between version constraints and locked versions:
- In your Terraform configuration, you specify version constraints (e.g.,
>= 3.0.0, < 4.0.0
) that define a range of acceptable provider versions. - The lock file records the specific version that was selected from within that range (e.g.,
3.27.0
).
This separation allows for flexible constraints in the configuration while ensuring consistency in actual execution. When a provider has a selection recorded in the lock file, Terraform will install that exact version during initialization.
To upgrade providers, you run terraform init -upgrade
, which instructs Terraform to ignore the current lock file entries and select the newest versions that satisfy the configuration constraints. After an upgrade, Terraform updates the lock file with the new selections.
Handling platform-specific dependencies
Provider packages are platform-specific (OS and architecture), creating a challenge for cross-platform teams:
- By default, a newly created lock file only contains hashes for the current platform
- This can cause issues when the configuration is used on different platforms
To solve this, use the terraform providers lock
command to pre-populate hashes for multiple platforms:
terraform providers lock \
-platform=linux_amd64 \
-platform=darwin_amd64 \
-platform=windows_amd64
This command downloads provider packages for each specified platform and adds their hashes to the lock file, which can then be committed to version control for use across different platforms.
Best practices for working with lock files
Version control and collaboration
- Always commit lock files to version control alongside your Terraform configuration files
- Never add
.terraform.lock.hcl
to.gitignore
- ignoring lock files defeats their purpose - Review lock file changes before committing - verify that version changes align with expectations
- Include lock file changes in code reviews just like any other code change
- Document significant provider updates, especially major version upgrades
Workflow management
- Intentional provider updates - Use
terraform init -upgrade
when you intentionally want to update providers - Pre-populate hashes for all platforms your team uses to prevent lock file conflicts
- Never regenerate lock files in testing or production environments without propagating changes back to development
- Create a dedicated branch for provider updates to isolate changes
CI/CD pipeline strategies
- Include verification steps to ensure the lock file hasn't been modified unexpectedly
- Ensure CI/CD runners use the same Terraform version across all stages
- Save the lock file alongside the plan file as part of pipeline artifacts
- Propagate the same lock file through all environments (dev → test → prod)
Multi-environment management
- Lock files belong to the root module, not to individual modules within a configuration
- Each separate Terraform configuration has its own lock file
- Lock files are shared across all workspaces in a given Terraform configuration
- For different environments (dev/staging/prod), use separate directories with their own state and lock files rather than workspaces
Common issues and their solutions
Cross-platform challenges
Issue: Lock file generated on one platform fails on another Solution: Pre-populate checksums for all platforms your team uses:
terraform providers lock \
-platform=linux_amd64 \
-platform=darwin_amd64 \
-platform=darwin_arm64 \
-platform=windows_amd64
Checksum verification failures
Error: "Failed to install provider" / "doesn't match any of the checksums previously recorded in the dependency lock file" Solution: Generate platform-specific checksums using the command above or for your specific platform:
terraform providers lock -platform=$(terraform version -json | jq -r '.platform')
Unexpected provider upgrades
Issue: Silent provider upgrades causing unexpected behavior Solution: Always commit the lock file to version control and use terraform init -upgrade
when explicitly upgrading providers
CI/CD environment problems
Issue: CI pipelines failing due to missing platform checksums Solution: Add a step in your pipeline to handle this:
terraform init
terraform providers lock -platform=linux_amd64 -platform=darwin_amd64 -platform=windows_amd64
Version constraint issues
Issue: Breaking changes when upgrading providers Solution: Use sensible version constraints:
- For stability:
version = "3.4.5"
(exact version) - For patches only:
version = "~> 3.4.0"
(allows 3.4.x) - For minor updates:
version = "~> 3.0"
(allows 3.x) - Avoid:
version = ">= 3.0.0"
(too permissive)
Practical examples
Managing provider versions
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.0" # Allows any 4.x version
}
random = {
source = "hashicorp/random"
version = "3.1.0" # Pins to exact version
}
}
}
Upgrading provider versions
# Upgrade all providers to newest versions within constraints
terraform init -upgrade
# Upgrade only specific providers
terraform init -upgrade=hashicorp/aws
Working with private provider registries
# Configure the provider source in your Terraform code
terraform {
required_providers {
custom = {
source = "example.com/myorg/custom"
version = "~> 1.0"
}
}
}
Then configure Terraform to use your private registry:
# In .terraformrc or terraform.rc
provider_installation {
network_mirror {
url = "https://terraform.example.com/providers/"
}
direct {
exclude = ["example.com/*/*"]
}
}
# Initialize and create lock file
terraform init
# Add hashes for different platforms
terraform providers lock \
-net-mirror=https://terraform.example.com/providers/ \
-platform=linux_amd64 \
-platform=darwin_amd64 \
example.com/myorg/custom
Conclusion
Terraform provider dependency lock files are a crucial component in ensuring reliable, reproducible infrastructure deployments. They solve important challenges around versioning, security, and cross-platform collaboration that previous Terraform versions struggled with.
By properly understanding and managing these lock files—committing them to version control, pre-populating them for multiple platforms, and following best practices for updates—teams can avoid inconsistencies and ensure smooth deployments across environments.
The most important practices to remember are: always commit your lock files to version control, pre-populate checksums for all platforms your team uses, use terraform init -upgrade
for intentional updates, and treat lock file changes with the same care as any other code change. These habits will significantly improve your Terraform workflow and help prevent the "works on my machine" problems that often plague infrastructure deployments.