Terraform State 生成 (import/aztfexport/番外編: Data Sources) at Azure
はじめに
Terraform の State ファイルを生成する方法についてまとめた記事です。
なお、初学者なため手法や手順についてはもっと良い方法があるかもしれません。
既に構築済みの環境や VNET/VM 等ある程度のリソースが払い出される環境、手出しできない環境と共存している等 Terraform で構築していない環境において、後から Terraform で管理する場合 リソース定義である tf ファイルとともに tfstate (State) ファイルも必要になります。
State ファイルを生成する方法である Import コマンド、Import ブロック、Azure Export for Terraform (aztfexport) の3つの使用方法と使用してみた感想を記述します。
番外編として、そもそも手出しできない環境やリソースを含むならば Data Sources (data ブロック) で参照するが適当かと思うのでその方法についても記載します。
IaC とは、、、というのは一旦おいておきます。
なお、aztfexport は Bicep 生成とともに Azure ポータルと統合されるんじゃないかと思っています。
import
以下の terraform import コマンドを使用する。
terraform import [Terraformリソース定義.変数名] [リソースID]
- import コマンドにより、バックエンドに tfstate (State) が生成される。
- よって、生成された tfstate ファイルを確認し、必要な値を抜き出し、tf ファイルを完成させなければならない。
- import コマンドはリソース一つ一つに対して実行しなければならない。
- つらい。そもそも公式では import blocks があるよと記載されている。以下略。
▼ 公式ドキュメントと分かりやすい解説記事
import block
import コマンドでは一つ一つコマンドを入力しなければならなかったが、import block では任意 tf ファイルに複数記述でき、且つ tf ファイル自体を生成できる (不要なプロパティも生成される)。
Terraform v1.5.0 以降 import block が使えるが、import block 自体は Experimental で生成される構文については将来変更される可能性がある。
- 任意 tf ファイルに import {} で複数リソースを定義しておきコマンド実行する。
import {
id = リソース ID
to = Terraform リソース名.変数名
}
-
terraform plan -generate-config-out="import-main.tf"を実行すると、指定したファイル名で tf ファイルが生成される。- import コマンドのように手書きではなく、記載した分の resource ブロックを自動生成してくれる機能。
- 指定したファイル名で生成されるので、同名のファイルがあった場合エラーとなる。
- リソース毎に import block の記述が必要になる。
resource "azurerm_resource_group" "rg" {
name = "rg-name"
location ="japanwest"
# (other resource arguments...)
}
例
以下リソースグループ内のリソースを import block を使用して tf/tfstate ファイルを生成する。

- import block 用のファイルとして、importblock-main.tf を用意する
- 上記ファイルにリソース分の import block を記述する
・Azure のリソース ID を調査しておく。
・Terraform に該当するリソース名と変数名を指定する。
※VM の OS Disk は不要だった。Extension も import 可能。
# Resource Group
import {
id = "/subscriptions/xxxxxxxxxx/resourceGroups/rg-win-vm-iis-monkfish"
to = azurerm_resource_group.rg
}
# VNet
import {
id = "/subscriptions/xxxxxxxxxx/resourceGroups/rg-win-vm-iis-monkfish/providers/Microsoft.Network/virtualNetworks/
win-vm-iis-monkfish-vnet"
to = azurerm_virtual_network.my_vnet
}
# Subnet
import {
id = "/subscriptions/xxxxxxxxxx/resourceGroups/rg-win-vm-iis-monkfish/providers/Microsoft.Network/virtualNetworks/
win-vm-iis-monkfish-vnet/subnets/win-vm-iis-monkfish-subnet"
to = azurerm_subnet.my_subnet
}
~略~
# VM Extension
import {
id = "/subscriptions/xxxxxxxxxx/resourceGroups/rg-win-vm-iis-monkfish/providers/Microsoft.Compute/virtualMachines/win-vm-iis-vm/extensions/win-vm-iis-monkfish-wsi"
to = azurerm_virtual_machine_extension.web_server_install
}
-
terraform plan -generate-config-out="import-main.tf"を実行する

-
tf ファイルが生成されるが、CLI にエラーが表示されているので修正する
-
terraform planでエラーが出なくなるまで修正する = tf ファイルが完成する
※VM パスワードを修正、VNETからサブネットを除外等




-
terraform applyで tfstate ファイルを生成する


-
"importblock-main.tf" を削除する
所感
- 任意のリソース分生成されるのは良い場面があると想定される (大量リソースなど)。
- リソースの変数名が指定できて良い。
- 各リソースのプロパティが大量に出力されるため、必須/不要の編集に工数がかかる。
- 変数化し別ファイルにするためには工数がかかる。
- もちろん tf ファイルに設定変更や新規リソース追加することも出来る (こっちが本題であるが…)。
aztfexport
Microsoft が提供する Terraform 移行ツール。2025/01 時点のバージョンは v0.15。
Azure Export for Terraform を使用すると、単一のコマンドで Azure リソースを Terraform に移行できます。
単一のコマンドで、ユーザー指定のリソース セットを Terraform HCL コードと状態にエクスポートします。
上記の通り、aztfexport コマンドで tf/tfstate ファイル双方を生成できる。
単一リソース、リソースグループ、Resource Graphの3種類を指定して生成可能。
例
ここでは、import block でも使用したリソースグループを対象として tf/tfstate ファイルを生成する。

- Azure Export for Terraform (aztfexport.exe) をダウンロードしてPATHを通す
- aztfexport でエクスポートする
aztfexport resource-group rg-win-vm-iis-monkfish
・このとき、空のディレクトリにて実行する (ファイルやフォルダがあると警告が出る)。
・CUI が表示され、リソースグループ内のリソース一覧が表示される。
・フィルターや取り込まないようスキップすることが可能。

・w でインポート開始する。数秒で完了した。


・以下のファイルが生成された。このとき、ローカルに tfstate が生成される。

※aztfexport コマンドの引数にバックエンドを設定することでリモートバックエンドにエクスポート可能。
- 生成されたファイルを確認する。
・terraform.tf
terraform {
backend "local" {}
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "3.99.0"
}
}
}
・provider.tf
provider "azurerm" {
features {
}
skip_provider_registration = true
subscription_id = "xxxxxxxxxx"
environment = "public"
use_msi = false
use_cli = true
use_oidc = false
}
・main.tf:変数名は「res-数字」となる。
resource "azurerm_resource_group" "res-0" {
location = "japanwest"
name = "rg-win-vm-iis-monkfish"
}
resource "azurerm_windows_virtual_machine" "res-1" {
admin_password = "ignored-as-imported"
admin_username = "azureuser"
location = "japanwest"
name = "win-vm-iis-vm"
network_interface_ids = ["/subscriptions/xxxxxxxxxx/resourceGroups/rg-win-vm-iis-monkfish/providers/Microsoft.Network/networkInterfaces/win-vm-iis-monkfish-nic"]
resource_group_name = "rg-win-vm-iis-monkfish"
size = "Standard_DS1_v2"
boot_diagnostics {
storage_account_uri = "https://xxxxxxxxxx.blob.core.windows.net/"
}
os_disk {
caching = "ReadWrite"
storage_account_type = "Premium_LRS"
}
source_image_reference {
offer = "WindowsServer"
publisher = "MicrosoftWindowsServer"
sku = "2022-datacenter-azure-edition"
version = "latest"
}
depends_on = [
azurerm_network_interface.res-3,
]
}
resource "azurerm_virtual_machine_extension" "res-2" {
auto_upgrade_minor_version = true
name = "win-vm-iis-monkfish-wsi"
publisher = "Microsoft.Compute"
settings = jsonencode({
commandToExecute = "powershell -ExecutionPolicy Unrestricted Install-WindowsFeature -Name Web-Server -IncludeAllSubFeature -IncludeManagementTools"
})
type = "CustomScriptExtension"
type_handler_version = "1.8"
virtual_machine_id = "/subscriptions/xxxxxxxxxx/resourceGroups/rg-win-vm-iis-monkfish/providers/Microsoft.Compute/virtualMachines/win-vm-iis-vm"
depends_on = [
azurerm_windows_virtual_machine.res-1,
]
}
~略~
・aztfexportResourceMapping.json:リソースIDとTerraformの変数名のマッピングが記載されている。
{
"/subscriptions/xxxxxxxxxx/resourceGroups/rg-win-vm-iis-monkfish": {
"resource_id": "/subscriptions/xxxxxxxxxx/resourceGroups/rg-win-vm-iis-monkfish",
"resource_type": "azurerm_resource_group",
"resource_name": "res-0"
},
"/subscriptions/xxxxxxxxxx/resourceGroups/rg-win-vm-iis-monkfish/providers/Microsoft.Compute/virtualMachines/win-vm-iis-vm": {
"resource_id": "/subscriptions/xxxxxxxxxx/resourceGroups/rg-win-vm-iis-monkfish/providers/Microsoft.Compute/virtualMachines/win-vm-iis-vm",
"resource_type": "azurerm_windows_virtual_machine",
"resource_name": "res-1"
},
"/subscriptions/xxxxxxxxxx/resourceGroups/rg-win-vm-iis-monkfish/providers/Microsoft.Compute/virtualMachines/win-vm-iis-vm/extensions/win-vm-iis-monkfish-wsi": {
"resource_id": "/subscriptions/xxxxxxxxxx/resourceGroups/rg-win-vm-iis-monkfish/providers/Microsoft.Compute/virtualMachines/win-vm-iis-vm/extensions/win-vm-iis-monkfish-wsi",
"resource_type": "azurerm_virtual_machine_extension",
"resource_name": "res-2"
},
~略~
・aztfexportSkippedResources.txt:CUIでスキップしたリソースが記載されている。
Following resources are marked to be skipped:
- /subscriptions/xxxxxxxxxx/resourceGroups/rg-win-vm-iis-monkfish/providers/Microsoft.Network/networkSecurityGroups/win-vm-iis-monkfish-nsg/securityRules/web
- /subscriptions/xxxxxxxxxx/resourceGroups/rg-win-vm-iis-monkfish/providers/Microsoft.Storage/storageAccounts/xxxxxxxxxx/blobServices/default
~略~
・terraform.tfstate

-
terraform init / terraform plan してみる
・VM パスワードの要件のみエラーとなったため、パスワードをダミーに修正する。
・リモートバックエンドに修正する。

-
再度 terraform init / terraform plan してみる
・backend が変わった旨エラーとなっている。

-
既存のステートをリモートに持っていきたいので、
terraform init -migrate-stateを実行する

- terraform plan を実行する。何もエラーは発生しなかった。
所感
- tf ファイルを用意等せず、コマンド 1 行で各ファイル毎に分割して生成されるのは import block と比較してかなり便利。
- CUI でフィルターできる、取り込まないよう選択できるのも便利。
- 出力されるプロパティが最小限に見える。編集の時間も削減できそう。
- リソースの変数名が指定できなかった点や変数化し別ファイル化にするには工数がかかる。
- もちろん tf ファイルに設定変更や新規リソース追加することも出来る (こっちが本題であるが…)。
▼ 分かりやすい解説記事
Data Sources
番外編として、そもそも手出しできない環境やリソースを含むならば Data Sources (data ブロック) で参照する方が適当かと思うのでその方法についても記載します。
- Data Sources とは
- 既存リソースを data block にて定義し参照する
- 既存リソースの tfstate ファイルを生成する必要がない。
- 既存リソースが管理できないリソースであって設定が一部変わったとしても、ほぼ関係ない (と思われる)。
- 既存リソースの tfstate を保持管理していないので terraform apply でコケない (と思われる)。
- 新規リソースの追加に必要な既存リソースの参照を data block にて行う。
- 既存リソースを data block にて定義し参照する
- 後は通常通り新規リソースの定義を行い、
terraform plan、terraform applyする
▼ 公式ドキュメント
例
先程と同様に、import block でも使用したリソースグループを対象として新規リソースの追加を行う。

-
main.tf に既存リソースの data block を用意する (必要な参照のみ且つ一意になるように指定する)
data "azurerm_resource_group" "res-rg" { name = "rg-win-vm-iis-monkfish" } data "azurerm_virtual_network" "res-vnet" { resource_group_name = data.azurerm_resource_group.res-rg.name name = "win-vm-iis-monkfish-vnet" } data "azurerm_network_interface" "res-nic" { resource_group_name = data.azurerm_resource_group.res-rg.name name = "win-vm-iis-monkfish-nic" } -
main.tf に Application Gateway の追加を行う (data block を参照する)
variable "backend_address_pool_name" { default = "myBackendPool" } variable "frontend_port_name" { default = "myFrontendPort" } variable "frontend_ip_configuration_name" { default = "myAGIPConfig" } variable "http_setting_name" { default = "myHTTPsetting" } variable "listener_name" { default = "myListener" } variable "request_routing_rule_name" { default = "myRoutingRule" } resource "azurerm_subnet" "frontend" { name = "myAGSubnet" resource_group_name = data.azurerm_resource_group.res-rg.name virtual_network_name = data.azurerm_virtual_network.res-vnet.name address_prefixes = ["10.0.10.0/24"] } resource "azurerm_public_ip" "pip" { name = "myAGPublicIPAddress" resource_group_name = data.azurerm_resource_group.res-rg.name location = data.azurerm_resource_group.res-rg.location allocation_method = "Static" sku = "Standard" } resource "azurerm_application_gateway" "main" { name = "myAppGateway" resource_group_name = data.azurerm_resource_group.res-rg.name location = data.azurerm_resource_group.res-rg.location sku { name = "Standard_v2" tier = "Standard_v2" capacity = 2 } gateway_ip_configuration { name = "my-gateway-ip-configuration" subnet_id = azurerm_subnet.frontend.id } frontend_port { name = var.frontend_port_name port = 80 } frontend_ip_configuration { name = var.frontend_ip_configuration_name public_ip_address_id = azurerm_public_ip.pip.id } backend_address_pool { name = var.backend_address_pool_name } backend_http_settings { name = var.http_setting_name cookie_based_affinity = "Disabled" port = 80 protocol = "Http" request_timeout = 60 } http_listener { name = var.listener_name frontend_ip_configuration_name = var.frontend_ip_configuration_name frontend_port_name = var.frontend_port_name protocol = "Http" } request_routing_rule { name = var.request_routing_rule_name rule_type = "Basic" http_listener_name = var.listener_name backend_address_pool_name = var.backend_address_pool_name backend_http_settings_name = var.http_setting_name priority = 1 } } resource "azurerm_network_interface_application_gateway_backend_address_pool_association" "nic-assoc" { network_interface_id = data.azurerm_network_interface.res-nic.id ip_configuration_name = "terraform_work3_nic_configuration" backend_address_pool_id = one(azurerm_application_gateway.main.backend_address_pool).id } -
terraform planを実行する

-
terraform applyを実行する

-
リソースが作成された

-
tfstate を確認する

-
Application Gateway の Public IP に HTTP でアクセスすると IIS が動作していることが確認できる
所感
- 管理できないリソースの tfstate は作成してもハマるだけと思われる
- よって Data Sources を利用することが望ましいと思われる
- 大規模な場合、tfstate を分割して terraform_remote_state で参照することで衝突を回避するのが解決策なようである
この記事が参考になれば幸甚です。
Discussion