Terraform refactoring - Renaming resources

Published on - Updated on

When you start working with Terraform, you often don’t see a need for renaming resources, but as time moves on and your infrastructure project grows, you will definitely encounter situations where you will need to change the Terraform name of a resource (for example, as part of an “Extract module” refactoring).

Creating our instance and IP address

Let’s say you have a Compute Engine instance and a static IP configured in your Google Cloud environment.

resource "google_compute_address" "ip_address" {
  name = "main-application"
}

resource "google_compute_instance" "default" {
  name         = "main-application"
  machine_type = "f1-micro"
  zone         = "europe-west1-b"

  boot_disk {
    initialize_params {
      image = "opensuse-cloud/opensuse-leap-15-5-v20230607-x86-64"
    }
  }

  network_interface {
    network = "default"

    access_config {
      nat_ip = google_compute_address.ip_address.address
    }
  }
}

I recommend that you run terraform init && terraform apply to deploy the above code in your environment so that you can follow along with the next steps in the post.

The need for a rename

At some point, you need to set up a second Compute Engine instance for a new service that you are developing. You open up the Terraform code and realize that you named the resource for the IP address of the original instance ip_address and the resource for the instance default (a copy-paste error from the Terraform documentation).

Before adding the new instance, we need to do some clean-up.

Let’s give our resources some better names.

variable "project" {}

resource "google_compute_address" "main_application" {
  project = var.project
  name    = "main-application"
  region  = "europe-west1"
}

resource "google_compute_instance" "main_application" {
  project      = var.project
  name         = "main-application"
  machine_type = "f1-micro"
  zone         = "europe-west1-b"

  boot_disk {
    initialize_params {
      image = "opensuse-cloud/opensuse-leap-15-5-v20230607-x86-64"
    }
  }

  network_interface {
    network = "default"

    access_config {
      nat_ip = google_compute_address.main_application.address
    }
  }
}

After renaming our resources, we can run terraform plan to ensure Terraform applies our changes correctly.

...
Plan: 2 to add, 0 to change, 2 to destroy.
...

According to the plan, Terraform wants to destroy our resources rather than update the names. If this happens, we will be in big trouble, as recreating the resources will result in production downtime. We can’t have our production server down because of an internal name change. In addition, deleting the IP address would mean we would get a new IP address. Often this is not what we want.

The reason for Terraform’s proposal comes from the fact that Terraform doesn’t know that the google_compute_instance.main_application and google_compute_instance.default resource names refer to the same Compute Engine instance.

Informing Terraform of the new resource names

In the past, you would have had to resort to a state migration to resolve this issue, but in later versions of Terraform, a moved block was added to the language that you can use to notify Terraform that a resource has moved to a new location.

moved {
  from = google_compute_address.ip_address
  to   = google_compute_address.main_application
}
moved {
  from = google_compute_instance.default
  to   = google_compute_instance.main_application
}

In the moved blocks, we specify the old and new names of the resources we want to move. Let’s run terraform plan one more time.

...
Plan: 0 to add, 0 to change, 0 to destroy.
...

We can see that Terraform now identifies the rename correctly and no longer plans to destroy any infrastructure. We can commit our code to version control, and on a future deployment, these changes will be applied to our production environment and our production Terraform state.