Terraform 0.12 is presented as a foundational release that introduces major new language features and a more robust foundation for future development. The version is described as a major update that includes dozens of improvements and features spanning the breadth and depth of Terraform's functionality. After Terraform 0.11, the work started by listening to the community to better understand what are people’s challenges using Terraform, not only as beginners, but as they get to intermediate and more advanced usages. The release is positioned as one of the bigger releases in the Terraform history.
Core Language Upgrade
The core language received its most significant upgrade, moving from a basic configuration format to a more expressive programming language. This shift enables you to write more dynamic and reusable infrastructure code.
A major change is the introduction of first-class expressions. You no longer need to wrap expressions in interpolation syntax ${...} in most places. This makes the code cleaner and less cluttered. For example, instance_type = var.type now works directly.
The new generalized type system is a big deal. It supports rich data structures and allows for stricter validation, catching type errors at plan time instead of apply time.
The release introduces a new version of HCL with notable new features such as for-loops.
New Language Features
The key changes can be summarized as follows:
| Category | Key Changes |
|---|---|
| New Language Features | First-class expressions, generalized type system, conditional expressions, for expressions, for_each and dynamic blocks |
| Improvements | Improved interpolation, template syntax, plugin installation, and error messages |
| Backwards Incompatibilities | Required syntax changes for lists and maps, new keyword conflicts |
First-class expression syntax allows you to express references and expressions directly rather than using string interpolation syntax.
Generalized type system allows you to use lists and maps more freely, and use resources as object values.
Iteration constructs allow you to transform and filter one collection into another collection, and generate nested configuration blocks from collections.
Structural rendering of plans changes plan output so it now looks more like configuration making it easier to understand.
Context-rich error messages now include a highlighted snippet of configuration and often suggest exactly what needs to be changed to resolve them.
Dynamic blocks provide a mechanism to generate nested configuration blocks from collections. An example configuration showing new language features is:
data "consul_key_prefix" "environment" {
path = "apps/example/env"
}
resource "aws_elastic_beanstalk_environment" "example" {
name = "test_environment"
application = "testing"
setting {
namespace = "aws:autoscaling:asg"
name = "MinSize"
value = "1"
}
dynamic "setting" {
for_each = data.consul_key_prefix.environment.var
content {
namespace = "aws:elasticbeanstalk:application:environment"
name = setting.key
value = setting.value
}
}
}
output "environment" {
value = {
id =
Improvements and Developer Experience
Improvements in Terraform 0.12 cover interpolation, template syntax, plugin installation, and error messages.
Structural rendering of plans makes plan output more like configuration.
Context-rich error messages include a highlighted snippet of configuration and often suggest exactly what needs to be changed to resolve them.
The release also includes remote plan and apply, which keeps secrets off of CI systems and developer machines.
The blog series published as a preview to the new features and improvement in Terraform 0.12 contains code examples. Guidance emphasizes to read and read them again until lambs become lions. The upgrade to 0.12 guide is also referenced as important reading.
Breaking Changes and Migration Realities
Terraform 0.12 does have backward compatibility with Terraform 0.11 syntax, but all syntax from 0.12 is not backwards compatible with 0.11, so any deployments in Terraform must be done using Terraform 0.12.
The most frequent code breaking changes you will likely encounter are probably the attributes vs blocks code syntax. In Terraform 0.11, they could be used interchangeably without any problem. For example, with ocicoresecurity_list, defining the egress security rules block as below with the ‘=’ was acceptable:
egress_security_rules = [
{
protocol = "${local.all_protocols}"
destination = "${local.anywhere}"
},
]
With 0.12, the syntax is more strict. Attributes are specified with an ‘=’ and blocks without the ‘=’.
Required syntax changes for lists and maps and new keyword conflicts are listed as backwards incompatibilities.
Lessons learned while upgrading the terraform-oci-oke project include:
- Read the blog series
- Fix breaking changes first
- Start using first class expressions
- Keep interpolation syntax for string concatenation
- Use improved conditionals
- Introduce dynamic blocks to reduce code repetition
- Upgrade self-contained modules
The reason all syntax from 0.12 is not backwards compatible with 0.11 is that any deployments in Terraform must be done using Terraform 0.12.
Upgrade Guidance from Practice
Fix breaking changes first is presented as a primary step. The attribute vs block distinction is a common source of failures.
Start using first class expressions after breaking changes are fixed. First class expressions remove the need for wrapping expressions in interpolation syntax in most places.
Keep interpolation syntax for string concatenation where needed.
Use improved conditionals as part of the new language feature set.
Introduce dynamic blocks to reduce code repetition.
Upgrade self-contained modules to take advantage of the new language features and to avoid workarounds that no longer work.
Terraform brings new aspects to the table when it comes to infrastructure as code. Particularly with 0.12, it improved on its error messages, Terraform plans, and interpolation syntax, it is highly encouraged for teams to adopt the new version. Despite the large changes to the tool, the upgrade process has never been more streamlined. By following the steps above, you and your team will be more comfortable with the upgrade process.
Modules Providers and State Considerations
While Terraform 0.12 does have backward compatibility with Terraform 0.11 syntax, if you have access to the module’s source, it is highly recommended to upgrade the modules to 0.12 as well. This is especially apparent when the module utilizes workarounds in Terraform 0.11 that no longer work in Terraform 0.12.
If you are utilizing a public module, it is important to see which version of the module supports Terraform 0.12. If there is such module, upgrading the module to that new module version is critical. If there is no such version yet, consider opening up an issue to bring it to the attention of the maintainers. If all else fails, creating a fork of the module and upgrading it yourself is viable, but be warned of fragmentation of the module versions.
Provider Versions
Like the modules, this is an important aspect of a Terraform project to keep in check when upgrading. With a new version of Terraform, the providers also get updated to support the update as well. As such, it is just simply looking in the change-logs to find the optimal version to utilize for Terraform 0.12.
The state file
It is crucial to understand that state files made in Terraform 0.12 cannot be read with earlier Terraform versions. As such, it is important to have a backup of the state file before attempting the upgrade, just in case if anything goes wrong.
Conclusion
Terraform 0.12 represents a deliberate move from a basic configuration format to a more expressive programming language for infrastructure. The introduction of first-class expressions, a generalized type system, conditional expressions, for expressions, for_each and dynamic blocks gives teams tools to write more dynamic and reusable infrastructure code with stricter validation at plan time instead of apply time.
The improvements to interpolation, template syntax, plugin installation, and error messages, together with structural rendering of plans and context-rich error messages, reduce cognitive load and make plan output easier to understand. Remote plan and apply adds a security boundary by keeping secrets off of CI systems and developer machines.
The upgrade path is non-trivial because of required syntax changes for lists and maps, new keyword conflicts, and stricter attribute versus block syntax. The experience of upgrading real projects shows that breaking changes should be fixed first, then first class expressions adopted, interpolation retained for string concatenation, improved conditionals used, dynamic blocks introduced to reduce repetition, and self-contained modules upgraded.
Module and provider version alignment and state file backup are critical operational concerns. State files created with 0.12 cannot be read with earlier Terraform versions, creating a one-way upgrade boundary. Public modules may lag behind 0.12 support, requiring version checks, issue filing, or controlled forks with awareness of fragmentation.
Overall, the release improves error messages, Terraform plans, and interpolation syntax and is highly encouraged for adoption. The changes are large, but the upgrade process is described as streamlined when following structured steps, blog series review, and careful handling of modules, providers, and state.