Are you thinking about migrating from Terraform to OpenTofu? What are the possible benefits? What are the drawbacks? And can you migrate back? I’ll try to answer those questions in this blog post.
If you’d prefer your information in video format, check out the Terraform Tuesday video instead.
In August of 2023, HashiCorp decided to change the licensing for their core products from Mozilla Public License (MPL) 2.0 to Business Source License (BSL) 1.1. The change was introduced via a commit on each GitHub repository and took effect with the next subsequent release of each product. Terraform was included in the list of impacted products and some folks were nonplussed about the change.
I’m not here to re-litigate the whole thing. This is not a blog for a bunch of drama. If you want to hear people spilling the tea on HashiCorp and the state of open source in general, there’s plenty of folks happy to ramble at you. Just scoot on over to reddit or the bird site.
HashiCorp’s interpretation of BSL was simply that you could continue to use their community products for free if you weren’t creating a product that competed with their paid solutions. And the use had to be like for like. So you couldn’t use Vault community edition to create a competitor to HCP Vault or Vault Enterprise. And you couldn’t use Terraform community edition to create a competitor to Terraform Enterprise or HCP Terraform (previously known as Terraform Cloud). You could, however, use Terraform community edition to create a competitor to HCP Consul.
If you really want to dig into the details, you can check out their blog post and BSL FAQs. And if that isn’t adequate, there’s a licensing questions email address you can ping for your unique situation. For 99% of you, the license change doesn’t require any change from you. If licensing is your only concern, you’re probably in the clear (#notalawyer).
Terraform is by far HashiCorp’s most popular software, and many companies and projects have worked hard to fill in the gaps not covered by the community version of Terraform. Whether that was automation, state management, policy enforcement, or testing, there is a rich ecosystem of Terraform adjacent projects and products that suddenly felt under threat by the change to BSL.
At the same time, there were some open-source purists who rightly identified that BSL is not true open-source software (OSS), and now Terraform was under a source-available license. Organizations like the CNCF, that require their projects only use OSS tools and components found themselves in a jam.
A group of folks in the IaC ecosystem got together and wrote a manifesto demanding that HashiCorp revert back to an OSS license or they would fork Terraform. HashiCorp was disinclined to acquiesce to their request, and thus OpenTofu was born.
Like I said before, there is a non-zero amount of drama surrounding the birth of OpenTofu, HashiCorp’s response, and general internet sturm and drang. I am not going to get into it. I’ve said my piece elsewhere and I stand by it.
OpenTofu was forked from the latest version of Terraform that still carried the previous MPL license, equating to roughly 1.5.something. The current version of OpenTofu as of this post is 1.7.3, so everything I’m going to mention is about that version. Likewise, the current version of Terraform right now is 1.9.2, so when I make comparisons between the two that is the version I’m referring to.
The folks at OpenTofu have been trying to keep feature parity with newer releases of Terraform, but that is starting to diverge a bit. Any configuration that was valid before Terraform 1.6, should work just fine with OpenTofu. If you’re on Terraform 1.6 or newer, then you may have to do some small amount of rework depending on what features you’ve leveraged in the newer versions.
I have a blog post I’ve tried to keep up to date as new minor versions of each project are released. If that’s tl;dr, here are the big highlights.
Both Terraform and OpenTofu have introduced a testing framework that makes use of run
blocks, .tftest.hcl
files, and a test
command. Terraform includes mock data, which OpenTofu doesn’t yet. Other than that, the two are basically the same.
Both Terraform and OpenTofu introduced removed
blocks, looping for import
blocks, and provider defined functions. The removed
block syntax is a little different, but otherwise I believe all of these features are identical.
OpenTofu released state and plan encryption with 1.7. This feature does not exist in Terraform, and I doubt it ever will. There’s an interesting WIP about ephemeral resources and values for Terraform that will allow you to omit certain values from state and plans, but I wouldn’t expect any implementation of that until 1.10
at the earliest.
Version 1.9 of Terraform introduce the ability to include other object values in an input variable validation
block. As far as I can tell, this feature is not planned for OpenTofu at the moment.
Version 1.8 of OpenTofu will introduce support for .tofu
files to enable a configuration to support both platforms while allowing you to take advantage of new features in either. The core idea is to have two files with the same name, but one ends in .tf
and the other ends in .tofu
. Terraform will ignore the .tofu
file, and OpenTofu will ignore the .tf
file. You can leverage new Terraform functionality in the .tf
file and new OpenTofu stuff in the .tofu
file. It’s a neat idea!
While there has been some divergence, OpenTofu and Terraform are largely the same from an operational standpoint. The code sitting behind it will be slightly different, but unless you’re a developer trying to contribute, that shouldn’t concern you too much.
That’s enough chit-chat. How about we check out a migration?
The steps to perform the migration are as follows:
tofu init
to switch providers and modulestofu plan
to verify no changestofu apply
to update state dataI’m going to start with a simple project that deploys an Azure resource group and virtual network using Azure storage as the backend. Looking inside the configuration, we’ve got a terraform.tf
file that defines the required providers and backend:
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~>3.0"
}
}
backend "azurerm" {
resource_group_name = "tacoTruck"
storage_account_name = "opentofu1313"
container_name = "tfstate"
key = "terraform.tfstate"
}
}
And there’s a main.tf
that defines the azurerm
provider, resource group, and uses a module from the public registry to deploy a Virtual Network.
provider "azurerm" {
features {}
}
resource "azurerm_resource_group" "test" {
name = "opentofu-test"
location = "East US"
tags = {
managed_by = "Terraform"
}
}
module "vnet" {
source = "Azure/vnet/azurerm"
version = "4.1.0"
resource_group_name = azurerm_resource_group.test.name
use_for_each = true
vnet_location = azurerm_resource_group.test.location
address_space = ["10.0.0.0/16"]
subnet_names = ["subnet1", "subnet2"]
subnet_prefixes = ["10.0.0.0/24", "10.0.1.0/24"]
}
Let’s assume I’ve already deployed this configuration to Azure using Terraform. I can pull up the terminal and run terraform state list
to see the resources under management.
$ terraform state list
azurerm_resource_group.test
module.vnet.azurerm_subnet.subnet_for_each["subnet1"]
module.vnet.azurerm_subnet.subnet_for_each["subnet2"]
module.vnet.azurerm_virtual_network.vnet
The good folks at OpenTofu have published a migration guide that walks you through the process depending on which version of Terraform you’re currently using. I’m on version 1.9.2, so I’m following those directions.
The first step is to make sure there are no pending changes from the Terraform side. I’m going to run terraform plan
to make sure.
$ terraform plan
azurerm_resource_group.test: Refreshing state...
...
No changes. Your infrastructure matches the configuration.
Terraform has compared your real infrastructure against your configuration and found no differences, so no
changes are needed.
It comes back as no changes, so we’re good there. Had there been pending changes, we would want to apply those before attempting a migration. The goal is to switch to OpenTofu without impacting the target environment(s) in any way.
Next up, I am going to back up my state file just in case something goes sideways. If you’re using Azure storage, make sure you have versioning enabled and maybe even take a snapshot. You can also run terraform state pull
and direct it to a file for a quick and dirty backup, but I wouldn’t recommend it for production workloads where there might be sensitive information in the state data.
We can use this copy of the state data for comparison as well once we migrate to OpenTofu.
You’re going to want to check your code for any features that OpenTofu doesn’t support. Here are the ones I know of at the moment:
As always, you should check the release notes from the most recent version of OpenTofu. Version 1.8 is going to add support for mock data.
OpenTofu had to set up their own registry for providers and modules because HashiCorp changed the licensing terms of their public registry to prevent non-Terraform binaries from accessing it. Again, I’m not here to point fingers or get catty. The practical upshot for you is that moving to OpenTofu means using their registry instead of the Terraform public registry.
The Terraform public registry is essentially an abstraction over the provider and module repositories on GitHub. It adds support for version constraints, supplies nicely formatted documentation, and allows for easy discovery. But the actual provider binaries and module files live on GitHub. The OpenTofu registry points at the exact same GitHub repositories without violating the Terraform registry license.
When you define a source for your provider or module, you can choose to use a shorthand like hashicorp/aws
or the longer form registry.terraform.io/hashicorp/aws
. When using the shorthand form, Terraform prepends registry.terraform.io
automatically. OpenTofu does the same, but prepends registry.opentofu.org
instead.
If any of the providers or modules in your configuration are using the longer form, you’ll need to switch to the short form. That includes modules and providers inside of child modules as well. You only need to do this for modules and providers from the Terraform public registry.
OpenTofu will still work if you pull providers from the Terraform public registry, but you might technically be violating the Terms and Conditions of the registry. And you’re not a rule breaker, are you?
tofu init
to switch providers and modulesWith that out of the way, it’s time to initialize the configuration with OpenTofu. From the terminal, I’ll run tofu init
:
$ tofu init
Initializing the backend...
Initializing modules...
Downloading registry.opentofu.org/Azure/vnet/azurerm 4.1.0 for vnet...
- vnet in .terraform\modules\vnet
Initializing provider plugins...
- Finding hashicorp/azurerm versions matching "~> 3.0, >= 3.11.0, < 4.0.0"...
- Installing hashicorp/azurerm v3.113.0...
- Installed hashicorp/azurerm v3.113.0 (signed, key ID 0C0AF313E5FD9F80)
Providers are signed by their developers.
If you'd like to know more about provider signing, you can read about it here:
https://opentofu.org/docs/cli/plugins/signing/
OpenTofu has made some changes to the provider dependency selections recorded
in the .terraform.lock.hcl file. Review those changes and commit them to your
version control system if they represent changes you intended to make.
OpenTofu has been successfully initialized!
The output shows that the vnet
module is being pulled from registry.opentofu.org/Azure/vnet/azurerm
. Looking at the updated .terraform.lock.hcl
file:
provider "registry.opentofu.org/hashicorp/azurerm" {
version = "3.113.0"
constraints = "~> 3.0, >= 3.11.0, < 4.0.0"
hashes = [
"h1:beBFmLUcm/WPYeAX+E40sP3g6kn4Flpv2kO8DIwxj6c=",
...]
}
The azurerm
provider is also pointing at the OpenTofu registry. Additionally, in the .terraform
directory, there are now two folders for the azurerm
provider:
$ tree .terraform/providers
.
├───registry.opentofu.org
│ └───hashicorp
│ └───azurerm
│ └───3.113.0
│ └───windows_amd64
└───registry.terraform.io
└───hashicorp
└───azurerm
└───3.113.0
└───windows_amd64
You may want to go back after the migration and clean out the provider plugins from the Terraform public registry if you’re cramped for space.
Let’s check out the state data too, I’ll run tofu state pull
and that will print the state to the terminal.
$ tofu state pull
{
"version": 4,
"terraform_version": "1.7.3",
"serial": 11,
...
Looking at the terraform_version
, it says 1.7.3. However the source for the azurerm
providers still says registry.terraform.io
:
"resources": [
{
"mode": "managed",
"type": "azurerm_resource_group",
"name": "test",
"provider": "provider[\"registry.terraform.io/hashicorp/azurerm\"]",
...
Once we run a successful tofu apply
, that will update as well.
tofu plan
To Verify No ChangesWith the initialization is done, I’m going to run a tofu validate
to make sure OpenTofu is cool with my config and then run tofu plan
. If any issues occur it might come down to the slight differences in implementation of features.
$ tofu validate
Success! The configuration is valid.
My validate comes back clean, so I’m ready to run plan to see if any changes are listed. There shouldn’t be- we did check with Terraform before switching- but I want to make sure.
$ tofu plan
azurerm_resource_group.test: Refreshing state...
...
No changes. Your infrastructure matches the configuration.
OpenTofu has compared your real infrastructure against your configuration and found no
differences, so no changes are needed.
My plan comes back with no changes, but if you do see changes, you may want to roll back to Terraform or try and troubleshoot what is different about the configuration. I’ll address rolling back later in the post.
tofu apply
to update state dataAlthough OpenTofu did change the Terraform version logged in state data, it left it otherwise unchanged. There are no pending changes for our actual infrastructure, so all tofu apply
will do is alter the registry references in state data.
$ tofu apply -auto-approve
tofu apply -auto-approve
azurerm_resource_group.test: Refreshing state...
...
No changes. Your infrastructure matches the configuration.
OpenTofu has compared your real infrastructure against your configuration and found no
differences, so no changes are needed.
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
As predicted, there are no changes to our resources, but there should be some changes in state. I’ll run tofu state pull
to check:
{
"version": 4,
"terraform_version": "1.7.3",
"serial": 12,
"lineage": "a084a4f3-7aee-5943-dc21-613e4d1a88c1",
"outputs": {},
"resources": [
{
"mode": "managed",
"type": "azurerm_resource_group",
"name": "test",
"provider": "provider[\"registry.opentofu.org/hashicorp/azurerm\"]",
The serial has incremented from 11 to 12 and the provider reference now points to registry.opentofu.org
. Otherwise, the actual resources are exactly the same.
If I want to test out a change, I can update the managed_by
tag on the resource group from Terraform
to OpenTofu
and run an apply.
resource "azurerm_resource_group" "test" {
name = "opentofu-test"
location = "East US"
tags = {
managed_by = "OpenTofu"
}
}
This will prove that OpenTofu is now managing our infrastructure.
$ tofu apply -auto-approve
azurerm_resource_group.test: Refreshing state...
...
...
Apply complete! Resources: 0 added, 1 changed, 0 destroyed.
After a few moments the apply is complete and now our infra is managed with OpenTofu.
Not too tough eh?
The above migration process is cautious and methodical. For anything production-ish, I’d recommend that approach. But if you’re moving development stuff that you don’t really care about, the whole thing can be boiled down to two steps:
tofu init
tofu apply -auto-approve
And that’s it. The rest of the steps are about protecting the state and preventing issues. While I can’t recommend the YOLO approach, it really can be that simple to migrate.
In case you were wondering, migrating back to Terraform is essentially the same process. Run terraform init, plan, and then apply and you’ll be back where you started. If you’ve started using new OpenTofu features, that might complicate things, especially the state data encryption. You’ll need to remove that before migrating back.
Your actual infrastructure in either case isn’t altered by the migration, and I think that’s the most important part. If you want to experiment with OpenTofu and then move back to Terraform, you can! No harm, no foul.
This is a tougher question, and it’s one that I struggle to answer because honestly, and I know this is trite, IT DEPENDS™. So rather than giving you a straight answer, since I don’t think there’s a one size fits all response, instead here are some questions to consider:
You can also opt to stay on Terraform pre-BSL changes and just excuse yourself from the whole debate. You won’t be getting any security patches- that ended on December 31st 2023- but my point is that you can sit on 1.5 and wait to see what happens with the whole IBM acquiring HashiCorp thing, which is another can of worms I don’t want to get into right now.
I hope in this post I’ve shown you how easy it is to move from Terraform to OpenTofu. While its not quite a drop-in replacement- you will need to do a little homework and analysis- it’s also a relatively low-risk and reversible operation. You could easily pilot OpenTofu on some development environments and see if it meets your needs.
That being said, my bottom line is that I would stick with the status quo unless there is some compelling reason to migrate. Most engineers and admins I know already have enough on their plate, and migrating to a new tool (even one as simple as Terraform to OpenTofu) is just one more thing to worry about. Unless there’s a clear business benefit or legal compulsion, I wouldn’t make the move.
That’s not to denigrate or cheapen what the OpenTofu folks have done. They’ve successfully forked an incredibly popular project and made the migration process as easy as possible. Kudos to everyone working on the project! As both projects evolve and inevitably diverge, I’d like to revisit the topic and see if my thinking has changed.
October 18, 2024
What's New in the AzureRM Provider Version 4?
August 27, 2024
Debugging the AzureRM Provider with VSCode
August 20, 2024
State Encryption with OpenTofu
August 1, 2024