TrademarkTrademark
Features
Documentation

Getting Started with the Azure Terraform Export Tool

Learn all about the Azure Terraform Export Tool and how to use it.
Brendan ThompsonJanuary 11, 2024
Getting Started with the Azure Terraform Export Tool
Key takeaways
  • Microsoft's aztfexport tool brings existing Azure resources under Terraform management by automatically generating configuration and import blocks.
  • aztfexport runs through a Text User Interface where you select resources to import, and it offers resource, resource-group, query, and mapping-file export modes.
  • The tool generates six files, including main.tf with resource blocks, import.tf with import blocks, and aztfexportResourceMapping.json mapping Azure resources to Terraform properties.
  • The generated Terraform uses generic resource names like res-0, so refining names via the mapping file or move blocks is recommended for production use.

Most of us have hit the same wall: we want to manage existing infrastructure with Terraform, but turning that infrastructure into Terraform code by hand is tedious. A few tools automate the worst of it. This post looks at aztfexport by Microsoft, which brings your existing Azure resources under Terraform management.

The below diagram shows the Azure infrastructure that we will be exporting from Azure with aztfexport. It is a very simple setup of just a single resource group containing a storage account and the container app environment with a single container app deployed as well as a default log analytics workspace.

Example of export from a resource group

Example of export from a resource group

Let's start the process of generating our resources and then importing them into state with aztfexport.

  1. Firstly we need to execute the following command aztfexport rg rg-aue-dev-tf. This indicates to aztfexport that we are going to be exporting all resources within the rg-aue-dev-tf resource group on Azure
  2. We will be presented with a Text User Interface (TUI) which shows the process of importing items, once the items have been identified the following interface will be shown.

Example of items to import

Example of items to import

From here we are able to select/deselect items to import. For this demo we will be selecting the resource group and then pressing w on the keyboard to indicate an import.

  1. The following shows the importing/exporting process itself, from a visual perspective it is rather a black box.

Example of the importing processing

Example of the importing processing

  1. Once the import/export is complete we are shown the following line which gives you the directory of where the Terraform configuration is generated. By default this will be in the current working directory.

Example of imported state with folder

Example of imported state with folder

Before we move on to the exported Terraform, here are the other export modes aztfexport gives you:

  • resource: allows for exporting/importing a single resource.
  • resource-group: allows for exporting/importing a resource group as well as all resources within that resource group.
  • query: allows exporting/importing a set of resources as defined by an Azure Graph query, you can learn more about those queries at Starter query samples - Azure Resource Graph.
  • mapping-file: allows for exporting/importing resources based on a resource mapping file this is a json file containing the resource ID, type and name

Now that we've seen how the process works and what options are available, let's look at the generated Terraform. The export produces six files:

  • aztfexportResourceMapping.json: the mapping file between Azure resources and properties that will make up the Terraform resource.
  • aztfexportSkippedResources.txt: resources that will be skipped by the export/import process.
  • import.tf: import blocks for all resources.
  • main.tf: Terraform definitions for all resources within the export/import.
  • provider.tf: provider details.
  • terraform.tf: Terraform constraints.

Next we will look at an excerpt from the interesting files, namely aztfexportResourceMapping.json, main.tf, and import.tf we won't see the full content as its rather lengthy and aztfexport when exporting/importing a Log Analytics Workspace brings all the default queries with it which is rather messy. This first file, the aztfexportResourceMapping.json file contains all resources identified by aztfexport, and defines the Terraform properties required in order to produce resource blocks as well as the resource ID for the import blocks.

{
	"/subscriptions/ecf2fa19-c059-4906-933c-9ab85fb327f8/resourceGroups/rg-aue-dev-tf": {
		"resource_id": "/subscriptions/ecf2fa19-c059-4906-933c-9ab85fb327f8/resourceGroups/rg-aue-dev-tf",
		"resource_type": "azurerm_resource_group",
		"resource_name": "res-0"
	},
	"/subscriptions/ecf2fa19-c059-4906-933c-9ab85fb327f8/resourceGroups/rg-aue-dev-tf/providers/Microsoft.App/containerApps/ca-aue-dev-tf": {
		"resource_id": "/subscriptions/ecf2fa19-c059-4906-933c-9ab85fb327f8/resourceGroups/rg-aue-dev-tf/providers/Microsoft.App/containerApps/ca-aue-dev-tf",
		"resource_type": "azurerm_container_app",
		"resource_name": "res-1"
	},
	"/subscriptions/ecf2fa19-c059-4906-933c-9ab85fb327f8/resourceGroups/rg-aue-dev-tf/providers/Microsoft.App/managedEnvironments/cae-aue-dev-tf": {
		"resource_id": "/subscriptions/ecf2fa19-c059-4906-933c-9ab85fb327f8/resourceGroups/rg-aue-dev-tf/providers/Microsoft.App/managedEnvironments/cae-aue-dev-tf",
		"resource_type": "azurerm_container_app_environment",
		"resource_name": "res-2"
	},
	"/subscriptions/ecf2fa19-c059-4906-933c-9ab85fb327f8/resourceGroups/rg-aue-dev-tf/providers/Microsoft.OperationalInsights/workspaces/workspacergauedevtfb34b": {
		"resource_id": "/subscriptions/ecf2fa19-c059-4906-933c-9ab85fb327f8/resourceGroups/rg-aue-dev-tf/providers/Microsoft.OperationalInsights/workspaces/workspacergauedevtfb34b",
		"resource_type": "azurerm_log_analytics_workspace",
		"resource_name": "res-3"
	},
	"/subscriptions/ecf2fa19-c059-4906-933c-9ab85fb327f8/resourceGroups/rg-aue-dev-tf/providers/Microsoft.Storage/storageAccounts/saaurdevtf": {
		"resource_id": "/subscriptions/ecf2fa19-c059-4906-933c-9ab85fb327f8/resourceGroups/rg-aue-dev-tf/providers/Microsoft.Storage/storageAccounts/saaurdevtf",
		"resource_type": "azurerm_storage_account",
		"resource_name": "res-543"
	}
}

Let's quickly review those properties:

  • resource_id: the Azure Resource ID for the resource, this will act as the ID in Terraform state.
  • resource_type: the fully qualified resource type which will be used in the resource block.
  • resource_name: this will be used as the identifier for the given resource block.

Now we can look into the main.tf which takes the above resource properties and defines the resource blocks. As you can see the resource_type and resource_name are clearly visible. If you're using this for real, it's a good idea to either intercept the aztfexportResourceMapping.json file and give the resources more usable resource_name definitions, or use a move block after the export/import.

resource "azurerm_resource_group" "res-0" {
  location = "australiaeast"
  name     = "rg-aue-dev-tf"
}
resource "azurerm_container_app" "res-1" {
  container_app_environment_id = "/subscriptions/ecf2fa19-c059-4906-933c-9ab85fb327f8/resourceGroups/rg-aue-dev-tf/providers/Microsoft.App/managedEnvironments/cae-aue-dev-tf"
  name                         = "ca-aue-dev-tf"
  resource_group_name          = "rg-aue-dev-tf"
  revision_mode                = "Single"
  ingress {
    external_enabled = true
    target_port      = 80
    traffic_weight {
      latest_revision = true
      percentage      = 100
    }
  }
  template {
    container {
      cpu    = 0.25
      image  = "mcr.microsoft.com/k8se/quickstart:latest"
      memory = "0.5Gi"
      name   = "simple-hello-world-container"
    }
  }
  depends_on = [
    azurerm_container_app_environment.res-2,
  ]
}
resource "azurerm_container_app_environment" "res-2" {
  location            = "australiaeast"
  name                = "cae-aue-dev-tf"
  resource_group_name = "rg-aue-dev-tf"
}
resource "azurerm_log_analytics_workspace" "res-3" {
  location            = "australiaeast"
  name                = "workspacergauedevtfb34b"
  resource_group_name = "rg-aue-dev-tf"
}
resource "azurerm_storage_account" "res-543" {
  account_replication_type         = "RAGRS"
  account_tier                     = "Standard"
  allow_nested_items_to_be_public  = false
  cross_tenant_replication_enabled = false
  location                         = "australiaeast"
  name                             = "saaurdevtf"
  resource_group_name              = "rg-aue-dev-tf"
}

Now that we have our mappings and the generated Terraform, importing those into Terraform state is easy. You do it with the import block, which is great because it gives you tracking and auditability when you import resources. This is where the mapping file was used to generate all the attributes these blocks need to work.

import {
  id = "/subscriptions/ecf2fa19-c059-4906-933c-9ab85fb327f8/resourceGroups/rg-aue-dev-tf"
  to = azurerm_resource_group.res-0
}
import {
  id = "/subscriptions/ecf2fa19-c059-4906-933c-9ab85fb327f8/resourceGroups/rg-aue-dev-tf/providers/Microsoft.App/containerApps/ca-aue-dev-tf"
  to = azurerm_container_app.res-1
}
import {
  id = "/subscriptions/ecf2fa19-c059-4906-933c-9ab85fb327f8/resourceGroups/rg-aue-dev-tf/providers/Microsoft.App/managedEnvironments/cae-aue-dev-tf"
  to = azurerm_container_app_environment.res-2
}
import {
  id = "/subscriptions/ecf2fa19-c059-4906-933c-9ab85fb327f8/resourceGroups/rg-aue-dev-tf/providers/Microsoft.OperationalInsights/workspaces/workspacergauedevtfb34b"
  to = azurerm_log_analytics_workspace.res-3
}
import {
  id = "/subscriptions/ecf2fa19-c059-4906-933c-9ab85fb327f8/resourceGroups/rg-aue-dev-tf/providers/Microsoft.Storage/storageAccounts/saaurdevtf"
  to = azurerm_storage_account.res-543
}

This file can be deleted once the import is completed into state.

So that was a look at Microsoft's aztfexport utility. It makes it easy for us as engineers to get existing Azure assets into Terraform. The Terraform code it spits out won't always be the best, but it's a much better starting point than writing all of it by hand. I can see this being really useful for teams that started moving to the cloud before they started doing IaC.

Get started using Scalr by signing up today.

About the author
Brendan Thompsonsolutions engineer at Scalr
Brendan Thompson is a solutions engineer at Scalr, specializing in Terraform and cloud infrastructure.