Importing Terraform State in Azure

Wanting to import into a Terraform state file – unsure how? This blog will assist you with with that importing!

In this blog, I am going to show you how to import Azure resources into a terraform state file; this can be done locally or else initialising a Terraform state file in a remote local; such as an Azure Storage Account

In this example, I am going to import:-

  • Resource Group
  • Virtual Network
  • Subnet from the Virtual Network

My folder structure for this example

Terraform-Edit-State
    └──main.tf
    └──provider.tf
        

main.tf

# Create Resource Group
resource "azurerm_resource_group" "tamops" {
  name     = "tamops"
  location = "eastus2"
}

# Create Virtual Network
resource "azurerm_virtual_network" "vnet" {
  name                = "tamops-vnet"
  address_space       = ["192.168.0.0/16"]
  location            = "eastus2"
  resource_group_name = azurerm_resource_group.tamops.name
}

# Create Subnet 1
resource "azurerm_subnet" "subnet1" {
  name                 = "subnet1"
  resource_group_name  = azurerm_resource_group.tamops.name
  virtual_network_name = azurerm_virtual_network.vnet.name
  address_prefix       = "192.168.1.0/24"
}

Terraform Init

Firstly lets look at initialising Terraform, as mentioned this can be locally or retrieving a remote state

terraform init locally with the provider.tf

provider.tf

terraform {
  required_version = ">= 0.13.0"
  backend "local" {}
}

terraform init

terraform remote state in Azure

provider.tf

terraform {
  required_version = ">= 0.13.0"
  backend "azure" {}
}

We can grab the remote state file using various ways, in this I will show how to do via AZ CLI and additional just having an updated provider.tf with required prosperities to retrieve the remote state

terraform init -backend-config storage_account_name=tfstatedevops -backend-config container_name=terraformstate -backend-config resource_group_name=tamopstfstates -backend-config key=terraform.tfstate

provider.tf updated

terraform {
  required_version = ">= 0.13.0"
  backend "azure" {
    resource_group_name  = "tamopstfstates"
    storage_account_name = "tfstatedevops"
    container_name       = "terraformstate"
    key                  = "terraform.tfstate"
  }
}

A successful terraform init with local or remote state, will display similar to this:-

tamops@Synth:/mnt/c/Users/thomast/Documents/thomasthorntoncloud-examples-new/Terraform-Edit-State$ terraform init

Initializing the backend...

Successfully configured the backend "azure"! Terraform will automatically
use this backend unless the backend configuration changes.

Initializing provider plugins...
- Finding latest version of hashicorp/azurerm...
- Installing hashicorp/azurerm v2.53.0...
- Installed hashicorp/azurerm v2.53.0 (signed by HashiCorp)

Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.

Terraform has been successfully initialized!

Terraform Plan and apply with none of the resources imported

Running a plan with none of the resources imported into terraform.tfstate will show that there will be 4 additions

tamops@Synth:/mnt/c/Users/thomast/Documents/thomasthorntoncloud-examples-new/Terraform-Edit-State$ terraform plan
Acquiring state lock. This may take a few moments...

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # azurerm_resource_group.tamops will be created
  + resource "azurerm_resource_group" "tamops" {
      + id       = (known after apply)
      + location = "eastus2"
      + name     = "tamops"
    }

  # azurerm_subnet.subnet1 will be created
  + resource "azurerm_subnet" "subnet1" {
      + address_prefix                                 = "192.168.1.0/24"
      + address_prefixes                               = (known after apply)
      + enforce_private_link_endpoint_network_policies = false
      + enforce_private_link_service_network_policies  = false
      + id                                             = (known after apply)
      + name                                           = "subnet1"
      + resource_group_name                            = "tamops"
      + virtual_network_name                           = "tamops-vnet"
    }

  # azurerm_virtual_network.vnet will be created
  + resource "azurerm_virtual_network" "vnet" {
      + address_space         = [
          + "192.168.0.0/16",
        ]
      + guid                  = (known after apply)
      + id                    = (known after apply)
      + location              = "eastus2"
      + name                  = "tamops-vnet"
      + resource_group_name   = "tamops"
      + subnet                = (known after apply)
      + vm_protection_enabled = false
    }

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

This is not what we want, as when trying to terraform apply, will error due to the resources already existing – I will show an output from terraform apply with that error

azurerm_resource_group.tamops: Creating...

Error: A resource with the ID "/subscriptions/xxxxxxxxxxxxxxxx/resourceGroups/tamops" already exists - to be managed via Terraform this resource needs to be imported into the State. Please see the resource documentation for "azurerm_resource_group" for more information.

  on main.tf line 2, in resource "azurerm_resource_group" "tamops":
   2: resource "azurerm_resource_group" "tamops" {

Time to terraform import into terraform.tfstate

terraform import requires the following

terraform import <resource or module> <name of resource or module> <Resource ID of the Azure resource>

In my example, I have only 3 resources to import; since its quite a small import – no need to create a script, in a following blog I will show can you can do this at scale by utilising the Az CLI!

Lets import the 3 resources

terraform import azurerm_resource_group.tamops /subscriptions/XXXXXXXXXXXXXXXXXXXXXXXX/resourceGroups/tamops

terraform import azurerm_virtual_network.vnet /subscriptions/XXXXXXXXXXXXXXXXXXXXXXXX/resourceGroups/tamops/providers/Microsoft.Network/virtualNetworks/tamops-vnet

terraform import azurerm_subnet.subnet1 /subscriptions/XXXXXXXXXXXXXXXXXXXXXXXX/resourceGroups/tamops/providers/Microsoft.Network/virtualNetworks/tamops-vnet/subnets/subnet1

Successful import of the resource group, to show a successful example

tamops@Synth:/mnt/c/Users/thomast/Documents/thomasthorntoncloud-examples-new/Terraform-Edit-State$ terraform import azurerm_resource_group.tamops /subscriptions/XXXXXXXXXXXXXXXXXXXXXXXX/resourceGroups/tamops
Acquiring state lock. This may take a few moments...
azurerm_resource_group.tamops: Importing from ID "/subscriptions/XXXXXXXXXXXXXXXXXXXXXXXX/resourceGroups/tamops"...
azurerm_resource_group.tamops: Import prepared!
  Prepared azurerm_resource_group for import
azurerm_resource_group.tamops: Refreshing state... [id=/subscriptions/XXXXXXXXXXXXXXXXXXXXXXXX/resourceGroups/tamops]

Import successful!

The resources that were imported are shown above. These resources are now in
your Terraform state and will henceforth be managed by Terraform.

Once the other two resources are imported, time to test another terraform plan & terraform apply – no changes will be noted

terraform plan

terraform plan
Acquiring state lock. This may take a few moments...
azurerm_resource_group.tamops: Refreshing state... [id=/subscriptions/XXXXXXXXXXXXXXXXXXXXXXXX/resourceGroups/tamops]
azurerm_virtual_network.vnet: Refreshing state... [id=/subscriptions/XXXXXXXXXXXXXXXXXXXXXXXX/resourceGroups/tamops/providers/Microsoft.Network/virtualNetworks/tamops-vnet]
azurerm_subnet.subnet1: Refreshing state... [id=/subscriptions/XXXXXXXXXXXXXXXXXXXXXXXX/resourceGroups/tamops/providers/Microsoft.Network/virtualNetworks/tamops-vnet/subnets/subnet1]

No changes. Infrastructure is up-to-date.

This means that Terraform did not detect any differences between your
configuration and real physical resources that exist. As a result, no
actions need to be performed.

terraform apply

terraform apply
Acquiring state lock. This may take a few moments...
azurerm_resource_group.tamops: Refreshing state... [id=/subscriptions/XXXXXXXXXXXXXXXXXXXXXXXX/resourceGroups/tamops]
azurerm_virtual_network.vnet: Refreshing state... [id=/subscriptions/XXXXXXXXXXXXXXXXXXXXXXXX/resourceGroups/tamops/providers/Microsoft.Network/virtualNetworks/tamops-vnet]
azurerm_subnet.subnet1: Refreshing state... [id=/subscriptions/XXXXXXXXXXXXXXXXXXXXXXXX/resourceGroups/tamops/providers/Microsoft.Network/virtualNetworks/tamops-vnet/subnets/subnet1]

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

Awesome – hopefully you’ve enjoyed this blog post and now have an understanding of importing into a Terraform state file, I will be creating an additional blog to show how you can import into terraform state at scale!

GitHub repository for Terraform Code

Leave a Reply