While Terraform is great for deploying (and destroying!) resources, you occasionally run into a situation where you need Terraform to recreate an existing resource. Enter the Terraform taint and replace commands!

In this post, you will learn about using both of these commands, when you should use each one, and some examples of recreating resources.

Using Terraform taint Command

The terraform taint command instructed Terraform that a managed resource was degraded or damaged. Terraform then marked the object as “tainted” in the state file and replaced the object in the next plan you created.

The syntax for the command is terraform taint [options] address. The address argument indicates which resource to mark as tainted and is formatted in the resource address syntax. You find the resource address by using the terraform state list command. This command shows the deployed resources from the state file.

For example, here is a basic Terraform configuration file using the azurerm provider to deploy a resource group, a virtual network, three subnets, and a storage account.

terraform {
  required_providers {
    azurerm = {
      source = "hashicorp/azurerm"
    }
  }
}
provider "azurerm" {
  features {}
}
# Deploy resource group
resource "azurerm_resource_group" "rg" {
  name     = "rg-tfdemo"
  location = "WestUS2"
}
# Deploy virtual network
resource "azurerm_virtual_network" "vnet" {
  name                = "vnet-prod"
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
  address_space       = ["10.0.0.0/8"]
}
# Deploy subnets using count
resource "azurerm_subnet" "snets" {
  count                = 3
  name                 = "subnet${count.index + 1}"
  address_prefixes     = ["10.${count.index + 1}.0.0/16"]
  resource_group_name  = azurerm_resource_group.rg.name
  virtual_network_name = azurerm_virtual_network.vnet.name
}
# Deploy a storage account
resource "azurerm_storage_account" "sa" {
  name                     = "jbtterraformdemo3579"
  resource_group_name      = azurerm_resource_group.rg.name
  location                 = azurerm_resource_group.rg.location
  account_replication_type = "LRS"
  account_tier             = "Standard"
  access_tier              = "Hot"
}

When you deploy this configuration, the results of the terraform state list command are shown below with each resource’s address. The [0], [1],and [2] indicate each subnet created using the count argument.

terraform state list resource address
Viewing address resources in state file

To force recreate the storage account resource, copy the azurerm_stroage_account.sa address representing the storage account. Next, use this with the terraform taint command, like the example below. Once executed, Terraform will indicate that the resource is tainted

# Taint a resource for replacement
terraform taint azurerm_storage_account.sa
terraform taint
Marking a resource as tainted

Next, run terraform plan -out=deploy.tfplan, and Terraform displays that the storage account resource is tainted and will be replaced.

terraform taint resource
Terraform showing resource is tainted and will be replaced

Finally, run terraform apply deploy.tfplan to deploy the planned changes. Terraform destroys the storage account resource and creates a new one.

terraform tainted resource recreation
Terraform recreating tainted resource

Using Terraform replace command

HashiCorp deprecated the terraform taint command in v0.15.2. If you want to force replacement of an object even though there are no configuration changes, use the terraform plan or terraform apply command with the -replace option instead. If you are using an older version of Terraform, continue using the terraform taint command.

The “replace” option is superior to “taint” as you can see the full effect of the change before you take any action. When you use taint, someone else can create a new plan against your tainted object before you reviewed the consequences of the change yourself.

If you want to create an execution plan and mark an object for replacement, use the terraform plan command. Add the -out=FILE option to save the generated plan to a file, and add the -replace=ADDRESS option to indicate the object. Use the same resource address found in the previous section.

# Create an execution plan with the replaced object
terraform plan -out="deploy.tfplan" -replace="azurerm_storage_account.sa"
# Execute the plan to replace the object
terraform apply deploy.tfplan

If you don’t want to create a plan, you can also mark the object for replacement directly in the terraform apply command. This example marks one of the subnets for replacement.

terraform apply -replace="azurerm_subnet.snets[0]"

Terraform outputs that the subnet will be replaced (as requested), and you need to enter “yes” to approve the change.

terraform apply replace
Replacing object directly in terraform apply command

Closing

Terraform taint and replace provides multiple ways to replace managed objects depending on your version of Terraform. If you upgrade to a newer version, consider using the new replace method as this is the HashiCorp recommendation.

Enjoyed this article? Check out more of my Terraform content here!