Menu

Virtual Geek

Tales from real IT system administrators world and non-production environment

Terraform refactoring moved block example

While working with Azure Terraform I converted/updated existing resource block to module (Removed resource block and added module replacing it). But after running terraform plan command I found that earlier created resource using resource block will be destroyed and it will be creating a complete new resource from module as tfstate doesn't allow it, because of old information came from resource block.

In this case, I simply wanted to just move existing deployed resource code structure without destroying it and update tfstate file. It should adopt the new change and update the tfstate file accordingly. To make this adoption your will need to use moved{} block in your tf configuration file script.

Download this tf configuration code Terraform moved block configuration here or it is also available on github.com.

In my example here to show demo from scratch I have created new Resource Group in Azure using Terraform with simple azurerm_resource_group resource block. 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
terraform {
  required_providers {
    azurerm = {
      source = "hashicorp/azurerm"
      #version = "=3.108.0"
    }
  }
}

# Configure the Microsoft Azure Provider
provider "azurerm" {
  features {}
}

variable "rginfo" {
  type = object({
    name     = string
    location = string
    tags     = map(any)
  })
  default = {
    name     = "moved-grp"
    location = "westus"
    tags = {
      name = "groupB"
    }
  }
  description = "value"
}

resource "azurerm_resource_group" "rg" {
  name     = var.rginfo.name
  location = var.rginfo.location
  tags     = var.rginfo.tags
}

After running plan and apply, I see new resource group is created successfully. As you can see in the last line of output. 

#First time deploy resource with simple resource block configuration file.
PS D:\Projects\Terraform\ \moved>terraform plan
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create
Terraform will perform the following actions:
  # azurerm_resource_group.rg will be created
  + resource "azurerm_resource_group" "rg" {
      + id       = (known after apply)
  + resource "azurerm_resource_group" "rg" {
      + id       = (known after apply)
  + resource "azurerm_resource_group" "rg" {
  + resource "azurerm_resource_group" "rg" {
      + id       = (known after apply)
      + location = "westus"
      + name     = "moved-grp"
      + tags     = {
          + "name" = "groupA"
        }
    }

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

────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 

Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now.

PS D:\Projects\Terraform\ \moved> terraform apply --auto-approve
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create
Terraform will perform the following actions:
  # azurerm_resource_group.rg will be created
  + resource "azurerm_resource_group" "rg" {
      + id       = (known after apply)
      + location = "westus"
      + name     = "moved-grp"
      + tags     = {
          + "name" = "groupA"
        }
    }

Plan: 1 to add, 0 to change, 0 to destroy.
azurerm_resource_group.rg: Creating...
azurerm_resource_group.rg: Still creating... [10s elapsed]
azurerm_resource_group.rg: Creation complete after 12s [id=/subscriptions/9e22xxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx/resourceGroups/moved-grp]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

After adding resource, I checked the state list and below is the output. All is looking good so far.

terraform state list
azurerm_resource_group.rg

Microsoft Azure Hashicorp IBM terraform configuration file tf terraflorm plan apply --auto-approve create resource azurerm_resource_group id location tags map execution plan refactoring moved block.png

I simply updated and replaced the code configuration to deploy same resource from module with same information. It is migration from resource block to module block. 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
terraform {
  required_providers {
    azurerm = {
      source = "hashicorp/azurerm"
      #version = "=2.91.0"
    }
  }
}

# Configure the Microsoft Azure Provider
provider "azurerm" {
  features {}
}

variable "rginfo" {
  type = object({
    name     = string
    location = string
    tags     = map(any)
  })
  default = {
    name     = "moved-grp"
    location = "westus"
    tags = {
      name = "groupB"
    }
  }
  description = "value"
}

module "rg" {
  source = "./module/"
  rginfo = {
    name     = var.rginfo.name
    location = var.rginfo.location
    tags     = var.rginfo.tags
  }
}

Main problem starts here, When I just run plan, in the output I see it will create and destroy the resource. What it is doing here is, already created resource with resource block will be destroyed and it will create complete new resource from module block. But it is not what I was looking, instead I wanted it should adopt and modify the existing resource without destroy if there are changes and update tfstate. To overcome this issue there is moved{} block you can use.

#Run the plan after replacing resource block with module for same resource.

terraform plan

azurerm_resource_group.rg: Refreshing state... [id=/subscriptions/9e22xxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx/resourceGroups/moved-grp]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create
  - destroy

Terraform will perform the following actions:
  # azurerm_resource_group.rg will be destroyed
  # (because azurerm_resource_group.rg is not in configuration)
  - resource "azurerm_resource_group" "rg" {
      - id         = "/subscriptions/9e22xxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx/resourceGroups/moved-grp" -> null
      - location   = "westus" -> null
      - name       = "moved-grp" -> null
      - tags       = {
          - "name" = "groupB"
        } -> null
        # (1 unchanged attribute hidden)
    }

  # module.rg.azurerm_resource_group.rg will be created
  + resource "azurerm_resource_group" "rg" {
      + id       = (known after apply)
      + location = "westus"
      + name     = "moved-grp"
      + tags     = {
          + "name" = "groupB"
        }
    }

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

────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 

Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now.

Below is the the moved block syntax you will need to add.

moved {
  from = old state
  to      = new state
}

Below is the complete code information.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
terraform {
  required_providers {
    azurerm = {
      source = "hashicorp/azurerm"
      #version = "=2.91.0"
    }
  }
}

# Configure the Microsoft Azure Provider
provider "azurerm" {
  features {}
}

variable "rginfo" {
  type = object({
    name     = string
    location = string
    tags     = map(any)
  })
  default = {
    name     = "moved-grp"
    location = "westus"
    tags = {
      name = "groupB"
    }
  }
  description = "value"
}

moved {
  from = azurerm_resource_group.rg
  to   = module.rg.azurerm_resource_group.rg
}

module "rg" {
  source = "./module/"
  rginfo = {
    name     = var.rginfo.name
    location = var.rginfo.location
    tags     = var.rginfo.tags
  }
}
#After adding moved{} block.
terraform apply --auto-approve
module.rg.azurerm_resource_group.rg: Refreshing state... [id=/subscriptions/9e22xxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx/resourceGroups/moved-grp]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:
  # module.rg.azurerm_resource_group.rg will be updated in-place
  # (moved from azurerm_resource_group.rg)
  ~ resource "azurerm_resource_group" "rg" {
        id       = "/subscriptions/9e22xxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx/resourceGroups/moved-grp"
        name     = "moved-grp"
      ~ tags     = {
          ~ "name" = "groupA" -> "groupB"
        }
        # (1 unchanged attribute hidden)
    }
Plan: 0 to add, 1 to change, 0 to destroy.

Changes to Outputs:
  - name = {
      - id         = "/subscriptions/9e22xxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx/resourceGroups/moved-grp"
      - location   = "westus"
      - managed_by = ""
      - name       = "moved-grp"
      - tags       = {
          - name = "groupA"
        }
      - timeouts   = null
    } -> null
module.rg.azurerm_resource_group.rg: Modifying... [id=/subscriptions/9e22xxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx/resourceGroups/moved-grp]
module.rg.azurerm_resource_group.rg: Modifications complete after 5s [id=/subscriptions/9e22xxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx/resourceGroups/moved-grp]

Apply complete! Resources: 0 added, 1 changed, 0 destroyed.

Here is the output now after applying new change. I see it is update in-place. It will update the state without destroying it and make the required change. After running state list I see new state from module.

terraform state list
module.rg.azurerm_resource_group.rg

Microsoft Azure terraform plan add change destory appy --auto-approve subscription update in-place hashicorp ibm hcl id name tags location moved block managed by module apply refactoring terraform.png

This information is for only one block, for every changed blocks code or migration, you will need to add moved block with old and new information state.

Below is the old way to move/migrate from old state to new state without adding moved{} block. You will need to run below command.

terraform state mv old_state new_state

PS D:\Projects\Terraform\\moved> terraform state mv azurerm_resource_group.rg module.rg.azurerm_resource_group.rg
Move "azurerm_resource_group.rg" to "module.rg.azurerm_resource_group.rg"
Successfully moved 1 object(s).
PS D:\Projects\Terraform\\moved> 
PS D:\Projects\Terraform\\moved> 
PS D:\Projects\Terraform\\moved> terraform plan
module.rg.azurerm_resource_group.rg: Refreshing state... [id=/subscriptions/9e22xxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/moved-grp]

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.
PS D:\Projects\Terraform\\moved> 

Useful Articles
Creating a Private Endpoint for Azure Storage Account with Terraform example 2
Terraform clone virtual machine template in VMware vSphere vCenter from CSV file
Terraform error retrieving storage account failure responding to request StatusCode 404 StorageAccountNotFound The storage account was not found
Terraform testing local variables and output csv file without resource Part 1
Terraform testing variable map object values without resource configuration part 2
Terraform foreach module output to show only required results
Terraform deploy create A Private DNS Record in Microsoft Azure from list of objects
Terraform clone virtual machine template in VMware vSphere vCenter Dynamic Content Part 2
Creating a Private Endpoint for Azure Storage Account with required sub services using Terraform
Terraform Azure Create Private Endpoint to existing Storage Account with Custom Private DNS zone record link
Using terraform to clone a virtual machine on VMware vSphere infrastructure
Terraform module clone VMware vSphere Linux and Windows virtual machine
Terraform VMware vSphere Virtual Machine customization clone failed on Windows
Terraform VMware vSphere Virtual Machine cloning Operating system not found

Go Back

Comment

Blog Search

Page Views

12279026

Follow me on Blogarama