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. In a production utilisation of this product it would be an idea to either intercept the aztfexportResourceMapping.json file and provide more usable resource_name definitions or utilise a move block post 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 it becomes a simple task to import those into Terraform state, this is done via the import block which is exceptionally powerful as it gives tracking and audibility to importing resources now. This is where the mapping file was used to generate all the relevant attributes for these blocks 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 into the aztfexport utility from Microsoft, we learned that it easily enables us as engineers to get pre-existing Azure assets into Terraform. Whilst the resultant Terraform code may not always be the best it is a supremely better starting point than having to manually craft the Terraform code by hand. I can see this being extremely useful to organisations who started their IaC journey after they had already started the journey into the cloud.

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.