February 14, 2023

Updating Terraform State, with Great Power comes Great Responsibility

Brendan Thompson

Now that we have gained an understanding about what Terraform State is how do we manipulate it, and when should we? In most scenarios it is actually not recommended to manipulate our state in any way as that is the responsibility of our Terraform code. However, in some situations we don’t have a choice, or perhaps we want to test a particular scenario that’s when state manipulation comes into play.

First lets check out the terraform state CLI command, it comes with 7 subcommands:

  • list - list all resources within the state file
  • mv - move an item within the state file
  • pull - pull the current state file and output it to our terminal
  • push - update remote state from a local state file
  • replace-provider - replace a provider within the state file
  • rm - remove a resource from our state file
  • show - show a single resource within the state file

Generally I use list and show fairly frequently to interrogate what’s in the state file, especially if it is a new codebase that I am looking at.

The rm subcommand can also be very useful if someone has been unkind enough to remove a resource (e.g. from the Azure portal). This allows us to remove that reference in state so that Terraform won’t complain, although if we don’t also remove it from the code Terraform will try to create on next apply.

Let’s dive into some scenarios and then how we would resolve them:

Force replacement/recreation of a single resource within state

This is actually pretty common during the development phase with larger projects. Let’s say you’ve built an entire cloud environment but you want to recreate a network. taint allows us to do this, otherwise we might need to delete the resource manually or comment the code out, neither are good solutions.

# Find the resource in state
terraform state list

# Inspect the resource
terraform state show azurerm_virtual_network.this

# Taint the resource
terraform taint azurerm_virtual_network.this

On the next terraform apply the azurerm_virtual_network.this will be recreated. If you change your mind and no longer require the resource to be tainted then you can use the aptly named untaint command.

terraform untaint azurerm_virtual_network.this

Import an existing resource into our state file

Sometimes we define all of our resources in our Terraform code but for some reason the resource is manually created. This leaves us in a state where Terraform can see the resource on the remote end and in its code but not in state. In order to fix that we need to import the resource into state this is where the import command comes into play.

As an example we are going to import the above virtual network into our state file.

terraform import azurerm_virtual_network.this /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/Resource_Group/providers/Microsoft.Network/virtualNetworks/Virtual Network 

As you can see from the above the import process is pretty simple, we simply need to pass in the address in Terraform state and the ID that is used for the resource. This will be different between providers.

Move a resource from a single resource into and set of resources, or into a module

This happens as our Terraform code grows, we might start with a single instance of something and then move to multiple instance through the use of the for expression or through creation of a module. To tell Terraform that we want to move our currently existing resource into one of those constructs we can either use the moved block or the Terraform mv command. Let’s take a look at an example.

# `for` expression
terraform mv azurerm_virtual_network.this azurerm_virtual_network.this["primary"]

# Module
terraform mv azurerm_virtual_network.this module.virtual_networks.azurerm_virtual_network.this

Update your state with the latest from your live resources

In this instance we want to force our state to go and check what the real state of our resources are and update state with any changes. This can be done with a very simple command.

terraform refresh

Closing out

In this post we went through the basics of working with Terraform state. How to taint and untaint a resource, how to move a resource to a new address within state, importing existing resources into state and refreshing state from the resource APIs. This really just touches the surface of working with state but in reality it is also about as much as you should be doing in state for 95% of cases. As a general rule of thumb if you’re modifying a state file by hand you’re doing something wrong.

Note: While this blog references Terraform, everything mentioned in here also applies to OpenTofu. New to OpenTofu? It is a fork of Terraform 1.5.7 as a result of the license change from MPL to BUSL by HashiCorp. OpenTofu is an open-source alternative to Terraform that is governed by the Linux Foundation. All features available in Terraform 1.5.7 or earlier are also available in OpenTofu. Find out the history of OpenTofu here.

Start using the Terraform platform of the future.

A screenshot of the modules page in the Scalr Platform