Azure Export for Terraform で既存の Azure 環境を Terraform にエクスポートする
はじめに
Azure の既存リソースは、[テンプレートのエクスポート] メニューから ARM テンプレートの形でダウンロードすることができます。テンプレートと言いつつそのまま流用できるわけではないので、転用する場合には編集が必要なのですが、エクスポート自体はできます。ふと、Terraform でエクスポートするツールはないのか探していたら、Azure Export for Terraform[1] というツールを見つけたので試してみます。
Azure Export for Terraform
このツールはこちらの GitHub リポジトリ[2] で公開されているので、環境に合わせてインストールします。
使用方法
Terraform コードとしてエクスポートする範囲を「リソースグループ/リソース/リソースグラフのクエリ結果」から選択できます。シンプルなのはリソースグループ単位でしょう。この場合、次のように記述します。
自前の環境に適用してみる
ハンズオン用に作っていた VNet に VM がいるだけのシンプルなリソースグループがあったので、そのリソースグループから Terraform を作成してみます。
既存環境のインポート
作業ディレクトリ単位で Terraform のプロジェクトが initialize されるので、専用のディレクトリを用意してそこで実行します。手元の環境は Winodws なので PowerShell から実行します。
aztfexport resource-group 202401-handson
このような形で抽出された情報が確認されます。w
キー押下にてインポートに進みます。
Microsoft Azure Export for Terraform
20240401-handson
10 items
│ 💡/subscriptions/xxxx/resourceGroups/20240401-HANDSON/providers/Microsoft.Compute/v
│ azurerm_virtual_machine_extension.res-0
💡/subscriptions/xxxx/resourceGroups/20240401-HANDSON/providers/Microsoft.Compute/vi
azurerm_virtual_machine_extension.res-1
💡/subscriptions/xxxx/resourceGroups/20240401-handson
azurerm_resource_group.res-2
💡/subscriptions/xxxx/resourceGroups/20240401-handson/providers/Microsoft.Compute/vi
azurerm_windows_virtual_machine.res-3
💡/subscriptions/xxxx/resourceGroups/20240401-handson/providers/Microsoft.Network/ne
azurerm_network_interface.res-4
💡/subscriptions/xxxx/resourceGroups/20240401-handson/providers/Microsoft.Network/ne
azurerm_network_security_group.res-5
••
↑/k up • ↓/j down • / filter • delete skip • e show error • r show recommendation • w import • s save • q
w
押下後、Importing ~
というメッセージが表示されます。
Microsoft Azure Export for Terraform
⣻ Importing /subscriptions/xxxx/resourceGroups/20240401-HANDSON/providers/Microsoft.
しばらく待つと、ローカルの Terraform プロジェクトに対する Azure 環境のインポートが終了します。
Microsoft Azure Export for Terraform
Terraform state and the config are generated at: C:<PATH_TO_WORKING_DIR>
Press any key to quit
code .
で VSCode を開いて中身を確認してみます。以下のようなファイルに分割されて作成されています。
このまま plan をしてみましょう。
$ terraform plan
╷
│ Error: "admin_password" has to fulfill 3 out of these 4 conditions: Has lower characters, Has upper characters, Has a digit, Has a special character other than "_", fullfiled only 2 conditions
│
│ with azurerm_windows_virtual_machine.res-3,
│ on main.tf line 37, in resource "azurerm_windows_virtual_machine" "res-3":
│ 37: admin_password = "ignored-as-imported"
VM の管理者パスワードはセキュアな文字列であるため、Terraform 側にインポートされておらず、仮置きの文字列(ignored-as-imported
)が入っていました。そして、その文字列が、VM に設定できる管理者パスワードの要件を満たしていなかったわけですね。一旦エラーの解消のためにパスワードをべた書きで設定します。
# admin_password = "ignored-as-imported"
admin_password = "AzureAdmin123!"
この状態で plan してみましょう。現在の Azure 環境と差分がない状態になりました。一応これで既存の Azure インフラをローカルの Terraform コードとして表現できている状態にはなったわけですね。
$ terraform plan
No changes. Your infrastructure matches the configuration.
管理者パスワードのべた書きが気になるので、一旦変数として読み込ませる形にしました。
# admin_password = "ignored-as-imported"
admin_password = var.admin_password
variable vm_admin_password {
sensitive = true
}
この状態で再度 plan してみましょう。差分があるといわれました。ちゃんと読んでいないのですが、Microsoft Q&A のこのあたり[3] が参考になるかもしれません。
$ terraform plan
var.vm_admin_password
Enter a value:
...
following symbols:
~ update in-place
Terraform will perform the following actions:
# azurerm_windows_virtual_machine.res-3 will be updated in-place
~ resource "azurerm_windows_virtual_machine" "res-3" {
id = "/subscriptions/xxxx/resourceGroups/20240401-handson/providers/Microsoft.Compute/virtualMachines/vm-module03"
name = "vm-module03"
tags = {}
# (26 unchanged attributes hidden)
# (2 unchanged blocks hidden)
}
Plan: 0 to add, 1 to change, 0 to destroy.
ちなみに、.tfstate 側では、パスワードはこのように認識されています。
...(略)
{
"mode": "managed",
"type": "azurerm_windows_virtual_machine",
"name": "res-3",
"provider": "provider[\"registry.terraform.io/hashicorp/azurerm\"]",
"instances": [
{
"schema_version": 0,
"attributes": {
"additional_capabilities": [],
"additional_unattend_content": [],
"admin_password": "ignored-as-imported",
"admin_username": "AzureAdmin",
...(略)
パスワード部分の書き方の影響かと思われるためそのまま apply します。
$ terraform apply
Apply complete! Resources: 0 added, 1 changed, 0 destroyed.
apply 後も .tfstate
側の admin_password
は変更されませんでした。そして、apply 時に渡した admin_password
で Azure VM に接続しに行ってもはじかれました。Azure portal 側での VM 作成時に指定した admin_password
で認証するとログインできました。パスワードのリセットは、Azure portal でも特別なメニューとして用意されているため、単にリソースのプロパティで変更するだけでは、管理者ユーザーのパスワードは変更できない可能性があります(要確認)。
リソースを追加してみる
既存環境が手元に用意できたので、ここにリソースを追加してみます。たとえば、ストレージアカウントを追加してみましょう。
resource "azurerm_storage_account" "test" {
name = "zukakostrgaccttest"
resource_group_name = azurerm_resource_group.res-2.name
location = azurerm_resource_group.res-2.location
account_tier = "Standard"
account_replication_type = "LRS"
}
plan してみます。追加したストレージ アカウントが差分として確認できました。
$ terraform plan
Terraform will perform the following actions:
# azurerm_storage_account.test will be created
+ resource "azurerm_storage_account" "test" {
+ access_tier = (known after apply)
+ account_kind = "StorageV2"
+ account_replication_type = "LRS"
+ account_tier = "Standard"
+ allow_nested_items_to_be_public = true
+ cross_tenant_replication_enabled = true
+ default_to_oauth_authentication = false
+ enable_https_traffic_only = true
+ id = (known after apply)
+ infrastructure_encryption_enabled = false
+ is_hns_enabled = false
+ large_file_share_enabled = (known after apply)
+ location = "japaneast"
+ min_tls_version = "TLS1_2"
+ name = "zukakostrgaccttest"
+ nfsv3_enabled = false
+ primary_access_key = (sensitive value)
+ primary_blob_connection_string = (sensitive value)
+ primary_blob_endpoint = (known after apply)
+ primary_blob_host = (known after apply)
+ primary_connection_string = (sensitive value)
+ primary_dfs_endpoint = (known after apply)
+ primary_dfs_host = (known after apply)
+ primary_file_endpoint = (known after apply)
+ primary_file_host = (known after apply)
+ primary_location = (known after apply)
+ primary_queue_endpoint = (known after apply)
+ primary_queue_host = (known after apply)
+ primary_table_endpoint = (known after apply)
+ primary_table_host = (known after apply)
+ primary_web_endpoint = (known after apply)
+ primary_web_host = (known after apply)
+ public_network_access_enabled = true
+ queue_encryption_key_type = "Service"
+ resource_group_name = "20240401-handson"
+ secondary_access_key = (sensitive value)
+ secondary_blob_connection_string = (sensitive value)
+ secondary_blob_endpoint = (known after apply)
+ secondary_blob_host = (known after apply)
+ secondary_connection_string = (sensitive value)
+ secondary_dfs_endpoint = (known after apply)
+ secondary_dfs_host = (known after apply)
+ secondary_file_endpoint = (known after apply)
+ secondary_file_host = (known after apply)
+ secondary_location = (known after apply)
+ secondary_queue_endpoint = (known after apply)
+ secondary_queue_host = (known after apply)
+ secondary_table_endpoint = (known after apply)
+ secondary_table_host = (known after apply)
+ secondary_web_endpoint = (known after apply)
+ secondary_web_host = (known after apply)
+ sftp_enabled = false
+ shared_access_key_enabled = true
+ table_encryption_key_type = "Service"
}
Plan: 1 to add, 0 to change, 0 to destroy.
問題なさそうなので apply します。
$ terraform apply
Plan: 1 to add, 0 to change, 0 to destroy.
azurerm_storage_account.test: Creating...
azurerm_storage_account.test: Still creating... [11s elapsed]
azurerm_storage_account.test: Still creating... [21s elapsed]
azurerm_storage_account.test: Creation complete after 28s [id=/subscriptions/42edd95d-ae8d-41c1-ac55-40bf336687b4/resourceGroups/20240401-handson/providers/Microsoft.Storage/storageAccounts/zukakostrgaccttest]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
リソースグループにストレージ アカウントが追加されました!
おわりに
Azure Export for Terraform を使って現行リソースの Terraform 表現を手元に持ってきて、必要に応じて変更を加えていくという流れを試してみました。別環境に構築するためのリードタイム短縮的な使い方をする場合でも、ARM テンプレートよりは可読性が高いため、利用できるユースケースは多いかもしれません。
Discussion