Terraform import blocks to import Azure resources into Terraform

In a recent announcement with Terraform v1.5.0, Terraform released the option to import your resources into Terraform using import blocks. This feature is a great addition to Terraform as it allows you to import existing resources into your Terraform state file, which can be useful when you have resources that were created outside of Terraform.

An interesting thought on why you may want to use this option instead of terraform import

Unlike the terraform import command, configuration-driven import using import blocks is predictable, works with CICD pipelines, and lets you preview an import operation before modifying state.

https://developer.hashicorp.com

Using import blocks with a sample Terraform configuration

Lets looking at import blocks with a simple Azure setup of:

  • Resource Group
  • Private DNS Zone
  • Zone Record

The import block, requires two values for each Azure resource:

  • id: the ID of the Azure resource
  • to: the Terraform resource type and name

The import block I will be using:

import {

  id = "/subscriptions/04109105-f3ca-44ac-a3a7-66b4936112c3/resourceGroups/examplerg"
  to = azurerm_resource_group.resource_group

}

import {

  id = "/subscriptions/04109105-f3ca-44ac-a3a7-66b4936112c3/resourceGroups/examplerg/providers/Microsoft.Network/privateDnsZones/thomasthornton.cloud"
  to = azurerm_private_dns_zone.dns_zone

}

import {

  id = "/subscriptions/04109105-f3ca-44ac-a3a7-66b4936112c3/resourceGroups/examplerg/providers/Microsoft.Network/privateDnsZones/thomasthornton.cloud/A/example"
  to = azurerm_private_dns_a_record.a_record

}

Running terraform plan -generate-config-out=generated.tf , will show me a plan of the import along with generating a terraform file with the required configuration.

Output of Terraform plan:

Terraform will perform the following actions:

  # azurerm_private_dns_a_record.a_record will be imported
  # (config will be generated)
    resource "azurerm_private_dns_a_record" "a_record" {
        fqdn                = "example.thomasthornton.cloud."
        id                  = "/subscriptions/04109105-f3ca-44ac-a3a7-66b4936112c3/resourceGroups/examplerg/providers/Microsoft.Network/privateDnsZones/thomasthornton.cloud/A/example"
        name                = "example"
        records             = [
            "1.2.3.4",
        ]
        resource_group_name = "examplerg"
        tags                = {}
        ttl                 = 3600
        zone_name           = "thomasthornton.cloud"
    }

  # azurerm_private_dns_zone.dns_zone will be imported
  # (config will be generated)
    resource "azurerm_private_dns_zone" "dns_zone" {
        id                                                    = "/subscriptions/04109105-f3ca-44ac-a3a7-66b4936112c3/resourceGroups/examplerg/providers/Microsoft.Network/privateDnsZones/thomasthornton.cloud"
        max_number_of_record_sets                             = 25000
        max_number_of_virtual_network_links                   = 1000
        max_number_of_virtual_network_links_with_registration = 100
        name                                                  = "thomasthornton.cloud"
        number_of_record_sets                                 = 2
        resource_group_name                                   = "examplerg"
        tags                                                  = {}

        soa_record {
            email         = "azureprivatedns-host.microsoft.com"
            expire_time   = 2419200
            fqdn          = "thomasthornton.cloud."
            host_name     = "azureprivatedns.net"
            minimum_ttl   = 10
            refresh_time  = 3600
            retry_time    = 300
            serial_number = 1
            tags          = {}
            ttl           = 3600
        }
    }

  # azurerm_resource_group.resource_group will be imported
  # (config will be generated)
    resource "azurerm_resource_group" "resource_group" {
        id       = "/subscriptions/04109105-f3ca-44ac-a3a7-66b4936112c3/resourceGroups/examplerg"
        location = "uksouth"
        name     = "examplerg"
        tags     = {
            "example1" = "tagvalue1"
            "example2" = "tagvalue2"
        }
    }

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

Notice the reference in the plan, that it is going to import 3 resources?

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

Reviewing the generated.tf that was generated with the above:

# __generated__ by Terraform
# Please review these resources and move them into your main configuration files.

# __generated__ by Terraform from "/subscriptions/04109105-f3ca-44ac-a3a7-66b4936112c3/resourceGroups/examplerg"
resource "azurerm_resource_group" "resource_group" {
  location = "uksouth"
  name     = "examplerg"
  tags = {
    example1 = "tagvalue1"
    example2 = "tagvalue2"
  }
}

# __generated__ by Terraform from "/subscriptions/04109105-f3ca-44ac-a3a7-66b4936112c3/resourceGroups/examplerg/providers/Microsoft.Network/privateDnsZones/thomasthornton.cloud/A/example"
resource "azurerm_private_dns_a_record" "a_record" {
  name                = "example"
  records             = ["1.2.3.4"]
  resource_group_name = "examplerg"
  tags                = {}
  ttl                 = 3600
  zone_name           = "thomasthornton.cloud"
}

# __generated__ by Terraform from "/subscriptions/04109105-f3ca-44ac-a3a7-66b4936112c3/resourceGroups/examplerg/providers/Microsoft.Network/privateDnsZones/thomasthornton.cloud"
resource "azurerm_private_dns_zone" "dns_zone" {
  name                = "thomasthornton.cloud"
  resource_group_name = "examplerg"
  tags                = {}
  soa_record {
    email        = "azureprivatedns-host.microsoft.com"
    expire_time  = 2419200
    minimum_ttl  = 10
    refresh_time = 3600
    retry_time   = 300
    tags         = {}
    ttl          = 3600
  }
}

generated.tf also found here in GitHub

The terraform it creates is pretty good, some values etc you can remove – but it will certainly deploy successfully :).

Very cool indeed..! If I run a Terraform apply, it will import these three resources into terraform state.

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

There is limitations

Using the setup to import resources in Terraform is pretty straightforward and super useful. However, there are some limitations to this feature. Depending on the Azure resources you want to import, you may come across some errors that the generated Terraform code cannot understand fully.

For example, if you try to import an Azure resource that has complex dependencies or relationships with other resources, Terraform may not be able to fully represent those dependencies in the generated configuration file. This can result in errors when you try to apply changes to the imported resource.

Such as this, during a virtual network import:

│ Error: Value for unconfigurable attribute
│ 
│   with azurerm_virtual_network.vnet,
│   on generated.tf line 9:
│   (source code not available)
│ 
│ Can't configure a value for "subnet.0.id": its value will be decided automatically based on the result of applying this configuration.

When you do receive an error such as the above currently, it does require some Terraform knowledge to resolve – but it still provides a super useful baseline to allow you to begin importing your Azure resources into Terraform state!

In summary, while the import feature in Terraform is a powerful tool, it does have some limitations. Depending on the Azure resources you want to import, you may need to manually modify the generated Terraform configuration file to include any missing dependencies or relationships. This can be a time-consuming process, but it is necessary to ensure that your Terraform configuration accurately represents the state of your infrastructure.

GitHub repo containing example code referenced here

Leave a Reply