Skip to main content

What's new in OpenTofu 1.9?

This page will run you through the most important changes in OpenTofu 1.9:

New features

Provider iteration (for_each)

One of the biggest challenges keeping the code clean in a multi-region or multi-zone deployment is the sheer number of providers and related configurations you have to juggle. Essentially, you will have to create duplicated code for each region or zone.

With provider iteration, you can now get rid of your code duplications by iterating over a set of providers instead of the duplicated code. If you have a small-scale setup with one of the cloud providers offering multiple zones, start by adding two variables:

Code Block
variable "regions" {
description = "A list of regions that should have a deployment."
type = set(string)
}

variable "disabled_regions" {
description = "A list of regions that should be disabled and all resources removed."
type = set(string)
default = []
}

The regions variable serves as a list of regions you will want to deploy to. The disabled_regions variable will allow you to remove a region later without removing the provider. This feature is extremely important because if you remove the provider without removing its resources, OpenTofu will be unable to remove the resources in that region.

As a next step, you can create your provider declarations. As you can see, this is only a single declaration. The only restriction worth mentioning here is the alias. Due to technical limitations, in this version you can only use for_each in conjunction with alias.

Code Block
provider "aws" {
alias = "by_region"
region = each.value
for_each = var.regions
}

Finally, you can use the provider set to call a submodule. Note that in contrast to the provider block above, the for_each iterates only over the providers that are not disabled. This lets you deprovision a region properly and later completely remove the provider.

Code Block
module "deploy" {
source = "./deploy"
providers = {
aws = aws.by_region[each.key]
}
for_each = setsubtract(var.regions, var.disabled_regions)
}

As this feature is a major departure how OpenTofu and its predecessor worked, there are a few limitations in place you may want to observe:

  1. You can only use for_each on variables and locals that can be obtained statically. Expressions that rely on data sources or resources are currently not usable.
  2. If you have an already-deployed infrastructure, don't simply remove a provider from the list as this will make it impossible for OpenTofu to destroy the infrastructure in this region. Instead, you will need to implement removing that infrastructure first and then remove the provider from the list. See the disabled_regions variable for an example above. OpenTofu will attempt to warn you if you don't use separate variables for provider and resource iterations, we strongly recommend you heed this warning.
  3. In this version each provider used in a for_each must have an alias. Providers without aliases are not supported for now due to internal technical reasons.
  4. There is currently no way to pass a set of providers to a module, you can only pass individual providers.

For more details, please read the full documentation.

The -exclude flag

OpenTofu already has a -target option to only plan or apply certain resources. Why not apply everything except a certain resource? This was the thought behind the -exclude flag and, in fact, this was one of the highest upvoted feature requests for this release.

You can test it by creating the following simple configuration:

Code Block
resource "local_file" "a" {
filename = "a.txt"
content = "a"
}

resource "local_file" "b" {
filename = "b.txt"
content = "b"
}

resource "local_file" "c" {
filename = "c.txt"
content = "c"
}

If you run tofu apply -exclude local_file.b, you will see that OpenTofu creates a.txt and c.txt, but not b.txt.

Improvements to existing features

Early evaluation improvements

When using early evaluation introduced in OpenTofu 1.8, OpenTofu will now prompt you for the variables needed for evaluation.

Encryption improvements

The new encrypted_metadata_alias option for state encryption

One of the tricky parts when using state encryption in previous versions is that OpenTofu needs to store the name of the key provider into the encrypted data alongside some metadata needed for decryption. This meant that changing the key provider name would involve using a fallback block and key provider naming for remote state data sources would have to be synchronized. With OpenTofu 1.9, we are introducing a new encrypted_metadata_alias option you can use to explicitly set the ID stored with the encrypted data:

Code Block
terraform {
encryption {
key_provider "pbkdf2" "mykey" {
passphrase = "OpenTofu has encryption"
# Note the fixed encrypted_metadata_alias here:
encrypted_metadata_alias = "certificates"
}
method "aes_gcm" "mymethod" {
keys = key_provider.pbkdf2.mykey
}
state {
method = method.aes_gcm.mymethod
}
}
}

Auto-applying encryption

In the previous OpenTofu version, changing the encryption configuration would not apply the encryption if no resource changes were detected. In this new version, OpenTofu applies the encryption configuration even if no resource changes are in the plan.

Testing and validation improvements

  • tofu test now throws errors instead of warnings for invalid override and mock fields.
  • tofu test now supports override_resource and override_data blocks in the scope of a single mock_provider.
  • References to variables, data, etc. are now usable in variable validation.
  • You can now use the -consolidate-warnings and -consolidate-errors options to consolidate similar warnings and errors together.

Backend improvements

  • The HTTP backend now supports extended trace logging, including request and response bodies.
  • The AzureRM backend now supports the timeout_seconds option with default timeout of 300 seconds.

CLI improvements

  • tofu console now accepts expressions split over multiple lines, when the newline characters appear inside bracketing pairs or when they are escaped using a backslash.
  • -show-sensitive option causes tofu plan, tofu apply, and other commands that can return data from the configuration or state to unmask sensitive values.

Performance improvements

With the help of the community, we have made several performance improvements for large graphs and large amounts of submodules.

Deprecations

Using ghcr.io/opentofu/opentofu as a base image

Using the ghcr.io/opentofu/opentofu image as a base image for custom images is deprecated and this will be removed in OpenTofu 1.10. The reason behind this change is security: while we regularly update OpenTofu itself when security-relevant updates arise, providing a base image would require an update roughly once a week, which we are currently not equipped to do.

However, we have added instructions on building your own base image in the containerized installation instructions.

Bugfixes

  • templatefile no longer crashes if the given filename is derived from a sensitive value. (#1801)
  • Configuration loading no longer crashes when a module block lacks the required source argument. (#1888)
  • The tofu force-unlock command now returns a relevant error when used with a backend that is not configured to support locking. (#1977)
  • Configuration generation during import no longer crashes if an imported object includes sensitive values. (#1986, #2077)
  • .tfvars files from the tests directly are no longer incorrectly loaded for non-test commands. (#2039)
  • tofu console's interactive mode now handles the special exit command correctly. (#2086)
  • Provider-contributed functions are now called correctly when used in the validation block of an input variable declaration. (#2052)
  • Sensitive values are now prohibited in early evaluation of backend configuration and module source locations, because otherwise they would be exposed as a side effect of initializing the backend or installing a module. (#2045)
  • tofu providers mirror no longer crashes when the dependency lock file has missing or invalid entries. (#1985)
  • OpenTofu now respects a provider-contributed functions' request to be called only when its arguments are fully known, for compatibility with functions that cannot handle unknown values themselves. (#2127)
  • tofu init no longer duplicates diagnostic messages produced when evaluating early-evaluation expressions. (#1890)
  • tofu plan change description now includes information about configuration blocks generated using a dynamic block with an unknown for_each value. (#1948)
  • Error message about a provider type mismatch now correctly identifies which module contains the problem. (#1991)
  • The yamldecode function's interpretation of scalars as numbers now conforms to the YAML 1.2 specification. In particular, the scalar value + is now interpreted as the string "+" rather than returning a parse error trying to interpret it as an integer. (#2044)
  • A module block's version argument now accepts prerelease version selections using a "v" prefix before the version number. Previously this was accepted only for non-prerelease selections. ([#2124])(https://github.com/opentofu/opentofu/issues/2124)
  • The tofu test command doesn't try to validate mock provider definition by its underlying provider schema now. (#2140)
  • Type validation for mocks and overrides are now less strict in tofu test. (#2144)
  • Skip imports blocks logic on tofu destroy (#2214)
  • Updated github.com/golang-jwt/jwt/v4 from 4.4.2 to 4.5.1 to make security scanners happy (no vulnerability, see #2179)
  • tofu test is now setting nulls for dynamic type when generating mock values. (#2245)
  • Variables declared in test files are now taking into account type default values. (#2244)
  • tofu test now removes outputs of destroyed modules between different test runs. (#2274)
  • Changes in create_before_destroy for resources which require replacement are now properly handled when refresh is disabled. (#2248)
  • tofu init command does not attempt to read encryption keys when -backend=false flag is set. (https://github.com/opentofu/opentofu/pull/2293)