APC 技術ブログ

株式会社エーピーコミュニケーションズの技術ブログです。

株式会社 エーピーコミュニケーションズの技術ブログです。

Terraform v1.5.0からの新機能:Import Block機能の紹介と既存ツールとの比較

はじめに

Terraform v1.5.0からの新機能として

  • インポートブロック
  • インポートしたリソースのコンフィグ自動生成(-generate-config-out)

が登場しました。
現在、v1.5.0-rc2が開発中の最新バージョンとなっており、近いうちにGAされるものと思われます。 本記事の投稿直後にv1.5.0がリリースされました。
記事内ではv1.5.0-rc2のインストール方法を紹介していますが、v1.5.0は以下のダウンロードサイトから取得可能です。

developer.hashicorp.com

従来ではインポート、つまり既存リソースはTerraform管理下に取り込むには、
terraformCLIのコマンドである terraform import [TerraformAddress] [ResourceId]コマンドを利用していましたが、
あくまでTerraformのStateにリソースを取り込むところをやってくれるまでです。
対応するHCLは自前で記述する必要があり、特にパラメータを既存リソースとHCLを合わせるのがかなり大変で、
Terraformで1からリソースを作るのと変わらない(あるいはそれ以上)の労力がかかってしまい、
インポートは結構な苦行だと思っていた方も少なくなかったと思います。

そんなインポート作業ですが、今回の新機能の登場で大分楽になることが期待できます。
さっそく使ってみて使用感を確認していきたいと思います。

実践

実験として、Azure PortalからAzure Functionsを作成して、リソースをHCL自動生成を利用してインポートしていきたいと思います。

Azure PortalからAzure Functionsを作成

import-test-funcを作成していこうと思います。
ついでにリソースグループやAppService PlanやStorage Accout等も一緒に作ってしまいましょう。

これで作成完了です。

Terraform v1.5.0 (rc2)のインストール

新しいImport機能を利用するにはTerraform v1.5.0以上が必要になるのでインストールします。
通常Terraformの実行ファイルはダウンロードページから取得できますが、betaやrcバージョンはこちらからは取得できません。
リリース自体はされているので以下のページから環境にあったファイルをダウンロードしましょう。

Terraform v1.5.0-rc2 Binaries | HashiCorp Releases

また、私はtfenvというTerraformのバージョン管理ツールを利用しているのですが、こちらからもrc2バージョンをインストール可能です。

$ tfenv install 1.5.0-rc2
Installing Terraform v1.5.0-rc2
############################ 100.0%
Downloading release tarball from https://releases.hashicorp.com/terraform/1.5.0-rc2/terraform_1.5.0-rc2_lin6SUMSnux_amd64.zip
############################ 100.0%
Downloading SHA hash file from https://releases.hashicorp.com/terraform/1.5.0-rc2/terraform_1.5.0-rc2_SHA256SUMS'
No keybase install found, skipping OpenPGP signature verification
Archive:  /tmp/tfenv_download.wIik8e/terraform_1.5.0-rc2_linux_amd64.zip
  inflating: /home/hoge/.tfenv/versions/1.5.0-rc2/terraform
Installation of terraform v1.5.0-rc2 successful. To make this your default version, run 'tfenv use 1.5.0-rcc2'                                                                  

$ tfenv use 1.5.0-rc2
Switching default version to v1.5.0-rc2
Switching completed

準備が整ったのでさっそくインポートしていこうと思います。

tfファイルの記述

それではtfファイルを記述していきます。
まずはいつも通りproviderブロックを書き、次にインポートブロックを書いていきます。

リソースグループのインポート

まとめてやるとややこしいのでまずは1リソースずつということでリソースグループをインポートします。

main.tf

provider "azurerm" {
    features {}
}

import {
    id = "/subscriptions/aaaaaaaaaaaaaaaaaaaaaaaaaaaaa/resourceGroups/import-test"
    to = azurerm_resource_group.main
}

インポートブロックには↑のように、idtoを指定する必要があります。
idにはクラウド側のリソースIDを、toにはTerraformにおけるアドレスを記述します。
AzureのリソースIDについては、大抵のリソースはプロパティ等から取得することができます。

tfファイルが書けたら、terraform initを実行した後、-generate-config-outを実行してみます。

$ terraform plan -generate-config-out=rg.tf
azurerm_resource_group.main: Preparing import... [id=/subscriptions/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/resourceGroups/import-test]
azurerm_resource_group.main: Refreshing state... [id=/subscriptions/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/resourceGroups/import-test]

Terraform will perform the following actions:

  # azurerm_resource_group.main will be imported
  # (config will be generated)
    resource "azurerm_resource_group" "main" {
        id       = "/subscriptions/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/resourceGroups/import-test"
        location = "japaneast"
        name     = "import-test"
        tags     = {}
    }

Plan: 1 to import, 0 to add, 0 to change, 0 to destroy.
╷
│ Warning: Config generation is experimental
│
│ Generating configuration during import is currently experimental, and the generated configuration format may 
│ change in future versions.
╵

────────────────────────────────────────────────────────────────────────────────────────────────────────────── 

Terraform has generated configuration and written it to rg.tf. Please review the configuration and edit it as  
necessary before adding it to version control.

Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these     
actions if you run "terraform apply" now.

コンフィグ自動生成機能はまだ実験段階という警告が出つつ、他にはエラーなく完了しているので生成されたファイルを見てみましょう。

rg.tf

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

# __generated__ by Terraform from "/subscriptions/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/resourceGroups/import-test"
resource "azurerm_resource_group" "main" {
  location = "japaneast"
  name     = "import-test"
  tags     = {}
}

イイ感じですね!
本番だったらvariableslocalsを使って変数化したものを代入するようにしますが一旦このままで進めようと思います。

terraform applyを実行してtfstateに取り込みを完了させましょう。

App Service Planのインポート

続いてApp Service Planをインポートします。
ちなみにリソースグループのインポートブロックは既に不要なので削除可能ですが、今回は書いたまま進めてます。

main.tf 追記分

import {
    id = "/subscriptions/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/resourceGroups/import-test/providers/Microsoft.Web/serverfarms/ASP-importtest-9781"
    to = azurerm_service_plan.main
}

今回も-generate-config-outオプションで自動生成を行いますが、ファイルが存在するとエラーになってしまうので別のファイルに出力させます。

$ terraform plan -generate-config-out=plan.tf
azurerm_service_plan.main: Preparing import... [id=/subscriptions/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/resourceGroups/import-test/providers/Microsoft.Web/serverfarms/ASP-importtest-9781]
azurerm_service_plan.main: Refreshing state... [id=/subscriptions/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/resourceGroups/import-test/providers/Microsoft.Web/serverfarms/ASP-importtest-9781]
azurerm_resource_group.main: Refreshing state... [id=/subscriptions/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/resourceGroups/import-test]

Planning failed. Terraform encountered an error while generating this plan.


│ Warning: Config generation is experimental

│ Generating configuration during import is currently experimental, and the generated configuration format may 
│ change in future versions.


│ Error: expected worker_count to be at least (1), got 0

│   with azurerm_service_plan.main,
│   on plan.tf line 12:
(source code not available)

今度は先ほどの警告に加えてエラーが出ました。
とはいえファイルは生成されているようなので先にファイルを見てみましょう。

plan.tf

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

# __generated__ by Terraform
resource "azurerm_service_plan" "main" {
  app_service_environment_id   = null
  location                     = "japaneast"
  maximum_elastic_worker_count = 1
  name                         = "ASP-importtest-9781"
  os_type                      = "Windows"
  per_site_scaling_enabled     = false
  resource_group_name          = "import-test"
  sku_name                     = "Y1"
  tags = {
    env = "test"
  }
  worker_count           = 0
  zone_balancing_enabled = false
}

plan.tfはしっかり生成されていますね。また、エラーメッセージと合わせてみると worker_countに不正な値が入っていることが読み取れます。
ようするに、コンフィグ生成には成功しているが、そのコンフィグを読み込んだterraform planでエラーが出た、という状態になります。

また、エラー箇所としてon plan.tf line 12という表記があり、これはファイルの行数のように見えますが、
どうやらリソースブロック内の行数を表しているようです。
resource "azurerm_service_plan" "main"というリソースブロックの開始が0行目、最初のパラメータが1行目、という数え方になるのでご注意ください。
ちなみにパラメータ名がアルファベット順にソートされて生成されるので、気になる方は配置しなおしましょう。
(私はnameresource_group_nameを最初に書きがちです)

今回エラーになったworker_countのパラメータはオプション設定であり、
今回のFunctionsは従量課金のサーバレスプランなので通常はworker_countは書く必要がないので、
値をnullに修正するか、コメントアウトしてしまいましょう。今回は値をnullに修正しました。

plan.tf 更新

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

# __generated__ by Terraform
resource "azurerm_service_plan" "main" {
  app_service_environment_id   = null
  location                     = "japaneast"
  maximum_elastic_worker_count = 1
  name                         = "ASP-importtest-9781"
  os_type                      = "Windows"
  per_site_scaling_enabled     = false
  resource_group_name          = "import-test"
  sku_name                     = "Y1"
  tags = {
    env = "test"
  }
  worker_count           = null
  zone_balancing_enabled = false
}

これでterraform planを実行してみましょう。

$ terraform plan
Terraform will perform the following actions:

  # azurerm_service_plan.main will be imported
    resource "azurerm_service_plan" "main" {
        id                           = "/subscriptions/daaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/resourceGroups/import-test/providers/Microsoft.Web/serverfarms/ASP-importtest-9781"
        kind                         = "functionapp"
        location                     = "japaneast"
        maximum_elastic_worker_count = 1
        name                         = "ASP-importtest-9781"
        os_type                      = "Windows"
        per_site_scaling_enabled     = false
        reserved                     = false
        resource_group_name          = "import-test"
        sku_name                     = "Y1"
        tags                         = {
            "env" = "test"
        }
        worker_count                 = 0
        zone_balancing_enabled       = false
    }

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

────────────────────────────────────────────────────────────────────────────────────────────────────────────── 

Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these     
actions if you run "terraform apply" now.

terraform planで差分なく実行できることが確認できたので、terraform applyでインポートを完了しましょう。

Azure Functions等のインポート

最後にもまとめてAzure Functions、Storage Accout、Application insightsをまとめてインポートしてみましょう。

main.tf 追記分

import {
    id = "/subscriptions/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/resourceGroups/import-test/providers/Microsoft.Storage/storageAccounts/importtest80af"
    to = azurerm_storage_account.main
}

import {
    id = "/subscriptions/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/resourceGroups/import-test/providers/microsoft.insights/components/import-test-func"
    to = azurerm_application_insights.main
}

import {
    id = "/subscriptions/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/resourceGroups/import-test/providers/Microsoft.Web/sites/import-test-func"
    to = azurerm_windows_function_app.main
}

Application Insightsは問題なくインポートできていそうですが、それ以外の部分で結構なエラーが出ました。

Terraform planned the following actions, but then encountered a problem:

  # azurerm_application_insights.main will be imported
  # (config will be generated)
    resource "azurerm_application_insights" "main" {
        app_id                                = "df2a0b0e-37bf-44f4-a778-91f491c9a5ca"
        application_type                      = "web"
        connection_string                     = (sensitive value)
        daily_data_cap_in_gb                  = 100
        daily_data_cap_notifications_disabled = false
        disable_ip_masking                    = false
        force_customer_storage_for_profiler   = false
        id                                    = "/subscriptions/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/resourceGroups/import-test/providers/microsoft.insights/components/import-test-func"
        instrumentation_key                   = (sensitive value)
        internet_ingestion_enabled            = true
        internet_query_enabled                = true
        local_authentication_disabled         = false
        location                              = "japaneast"
        name                                  = "import-test-func"
        resource_group_name                   = "import-test"
        retention_in_days                     = 90
        sampling_percentage                   = 0
        tags                                  = {
            "env" = "test"
        }
        workspace_id                          = "/subscriptions/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/resourceGroups/defaultresourcegroup-ejp/providers/Microsoft.OperationalInsights/workspaces/defaultworkspace-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-ejp"
    }

Plan: 1 to import, 0 to add, 0 to change, 0 to destroy.
╷
│ Warning: Config generation is experimental
│
│ Generating configuration during import is currently experimental, and the generated configuration format may 
│ change in future versions.
╵
╷
│ Error: parsing "SystemAssigned": parsing the UserAssignedIdentity ID: the number of segments didn't match    

│ Expected a UserAssignedIdentity ID that matched (containing 8 segments):

│ > /subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.ManagedIdentity/userAssignedIdentities/userAssignedIdentityValue

│ However this value was provided (which was parsed into 0 segments):

│ > SystemAssigned

│ The following Segments are expected:

│ * Segment 0 - this should be the literal value "subscriptions"
│ * Segment 1 - this should be the UUID of the Azure Subscription
│ * Segment 2 - this should be the literal value "resourceGroups"
│ * Segment 3 - this should be the name of the Resource Group
│ * Segment 4 - this should be the literal value "providers"
│ * Segment 5 - this should be the name of the Resource Provider [for example 'Microsoft.ManagedIdentity']     
│ * Segment 6 - this should be the literal value "userAssignedIdentities"
│ * Segment 7 - this should be the user specified value for this userAssignedIdentity [for example "userAssignedIdentityValue"]

│ The following Segments were parsed:

│ * Segment 0 - not found
│ * Segment 1 - not found
│ * Segment 2 - not found
│ * Segment 3 - not found
│ * Segment 4 - not found
│ * Segment 5 - not found
│ * Segment 6 - not found
│ * Segment 7 - not found


│   with azurerm_windows_function_app.main,
│   on func.tf line 11:
│   (source code not available)

エラーが長いので省略します。

エラー全体を見たい方はこちらをクリックしてください。

╵
╷
│ Error: expected blob_properties.0.change_feed_retention_in_days to be in the range (1 - 146000), got 0       
│
│   with azurerm_storage_account.main,
│   on func.tf line 29:
│   (source code not available)
│
╵
╷
│ Error: expected site_config.0.health_check_eviction_time_in_min to be in the range (2 - 10), got 0
│
│   with azurerm_windows_function_app.main,
│   on func.tf line 37:
│   (source code not available)
│
╵
╷
│ Error: Invalid combination of arguments
│
│   with azurerm_windows_function_app.main,
│   on func.tf line 54:
│   (source code not available)
│
│ "site_config.0.application_stack.0.dotnet_version": only one of
│ `site_config.0.application_stack.0.dotnet_version,site_config.0.application_stack.0.java_version,site_config.0.application_stack.0.node_version,site_config.0.application_stack.0.powershell_core_version,site_config.0.application_stack.0.use_custom_runtime`
│ can be specified, but
│ `site_config.0.application_stack.0.dotnet_version,site_config.0.application_stack.0.node_version,site_config.0.application_stack.0.use_custom_runtime`
│ were specified.
╵
╷
│ Error: Invalid combination of arguments
│
│   with azurerm_windows_function_app.main,
│   on func.tf line 55:
│   (source code not available)
│
│ "site_config.0.application_stack.0.java_version": only one of
│ `site_config.0.application_stack.0.dotnet_version,site_config.0.application_stack.0.java_version,site_config.0.application_stack.0.node_version,site_config.0.application_stack.0.powershell_core_version,site_config.0.application_stack.0.use_custom_runtime`
│ can be specified, but
│ `site_config.0.application_stack.0.dotnet_version,site_config.0.application_stack.0.node_version,site_config.0.application_stack.0.use_custom_runtime`
│ were specified.
╵
╷
│ Error: Invalid combination of arguments
│
│   with azurerm_windows_function_app.main,
│   on func.tf line 56:
│   (source code not available)
│
│ "site_config.0.application_stack.0.node_version": only one of
│ `site_config.0.application_stack.0.dotnet_version,site_config.0.application_stack.0.java_version,site_config.0.application_stack.0.node_version,site_config.0.application_stack.0.powershell_core_version,site_config.0.application_stack.0.use_custom_runtime`
│ can be specified, but
│ `site_config.0.application_stack.0.dotnet_version,site_config.0.application_stack.0.node_version,site_config.0.application_stack.0.use_custom_runtime`
│ were specified.
╵
╷
│ Error: Invalid combination of arguments
│
│   with azurerm_windows_function_app.main,
│   on func.tf line 57:
│   (source code not available)
│
│ "site_config.0.application_stack.0.powershell_core_version": only one of
│ `site_config.0.application_stack.0.dotnet_version,site_config.0.application_stack.0.java_version,site_config.0.application_stack.0.node_version,site_config.0.application_stack.0.powershell_core_version,site_config.0.application_stack.0.use_custom_runtime`
│ can be specified, but
│ `site_config.0.application_stack.0.dotnet_version,site_config.0.application_stack.0.node_version,site_config.0.application_stack.0.use_custom_runtime`
│ were specified.
╵
╷
│ Error: Invalid combination of arguments
│
│   with azurerm_windows_function_app.main,
│   on func.tf line 58:
│   (source code not available)
│
│ "site_config.0.application_stack.0.use_custom_runtime": only one of
│ `site_config.0.application_stack.0.dotnet_version,site_config.0.application_stack.0.java_version,site_config.0.application_stack.0.node_version,site_config.0.application_stack.0.powershell_core_version,site_config.0.application_stack.0.use_custom_runtime`
│ can be specified, but
│ `site_config.0.application_stack.0.dotnet_version,site_config.0.application_stack.0.node_version,site_config.0.application_stack.0.use_custom_runtime`
│ were specified.
╵
╷
│ Error: Conflicting configuration arguments
│
│   with azurerm_windows_function_app.main,
│   on func.tf line 59:
│   (source code not available)
│
│ "site_config.0.application_stack.0.use_dotnet_isolated_runtime": conflicts with
│ site_config.0.application_stack.0.node_version

各種エラー箇所を修正したHCLが以下になります。
修正前のパラメータはコメントアウトして残してあるものになります。

ここからわかる特徴としてはstorage_account_access_keyapplication_insights_connection_stringのようなSensitive属性を持つ値は、
null # sensitiveと書かれて自動的には入力されない点です。
おそらく確認せずにGitHub等に公開して情報漏洩しないようにしているのかな?と予想しています。

また、azurerm_service_planworker_countと同様に、azurerm_windows_function_appでもapplication_stackには1つの言語のバージョン設定にしか書けないのに複数言語の設定が書かれていたので、
Portalで設定したNode.js以外の設定はコメントアウトしています。

# __generated__ by Terraform
resource "azurerm_storage_account" "main" {
  access_tier                       = null
  account_kind                      = "Storage"
  account_replication_type          = "LRS"
  account_tier                      = "Standard"
  allow_nested_items_to_be_public   = true
  allowed_copy_scope                = null
  cross_tenant_replication_enabled  = true
  default_to_oauth_authentication   = true
  edge_zone                         = null
  enable_https_traffic_only         = true
  infrastructure_encryption_enabled = false
  is_hns_enabled                    = false
  large_file_share_enabled          = null
  location                          = "japaneast"
  min_tls_version                   = "TLS1_2"
  name                              = "importtest80af"
  nfsv3_enabled                     = false
  public_network_access_enabled     = true
  queue_encryption_key_type         = "Service"
  resource_group_name               = "import-test"
  sftp_enabled                      = false
  shared_access_key_enabled         = true
  table_encryption_key_type         = "Service"
  tags = {
    env = "test"
  }
  blob_properties {
    change_feed_enabled           = false
    change_feed_retention_in_days = null
    default_service_version       = null
    last_access_time_enabled      = false
    versioning_enabled            = false
  }
  network_rules {
    bypass                     = ["AzureServices"]
    default_action             = "Allow"
    ip_rules                   = []
    virtual_network_subnet_ids = []
  }
}

# __generated__ by Terraform
resource "azurerm_windows_function_app" "main" {
  app_settings                       = {}
  builtin_logging_enabled            = false
  client_certificate_enabled         = false
  client_certificate_exclusion_paths = null
  client_certificate_mode            = "Required"
  content_share_force_disabled       = false
  daily_memory_time_quota            = 0
  enabled                            = true
  functions_extension_version        = "~4"
  https_only                         = true
  key_vault_reference_identity_id    = null         # "SystemAssigned"
  location                           = "japaneast"
  name                               = "import-test-func"
  resource_group_name                = "import-test"
  service_plan_id                    = "/subscriptions/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/resourceGroups/import-test/providers/Microsoft.Web/serverfarms/ASP-importtest-9781"
  storage_account_access_key         = azurerm_storage_account.main.primary_access_key  # null # sensitive
  storage_account_name               = "importtest80af"
  storage_key_vault_secret_id        = null
  # storage_uses_managed_identity      = false
  tags = {
    env                                      = "test"
    "hidden-link: /app-insights-resource-id" = "/subscriptions/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/resourceGroups/import-test/providers/Microsoft.Insights/components/import-test-func"
  }
  virtual_network_subnet_id = null
  zip_deploy_file           = null
  site_config {
    always_on                              = false
    api_definition_url                     = null
    api_management_api_id                  = null
    app_command_line                       = null
    app_scale_limit                        = 200
    application_insights_connection_string = azurerm_application_insights.main.connection_string   # null # sensitive
    application_insights_key               = azurerm_application_insights.main.instrumentation_key # null # sensitive
    default_documents                      = ["Default.htm", "Default.html", "Default.asp", "index.htm", "index.html", "iisstart.htm", "default.aspx", "index.php"]
    elastic_instance_minimum               = 0
    ftps_state                             = "FtpsOnly"
    health_check_eviction_time_in_min      = null # 0
    health_check_path                      = null
    http2_enabled                          = false
    load_balancing_mode                    = "LeastRequests"
    managed_pipeline_mode                  = "Integrated"
    minimum_tls_version                    = "1.2"
    pre_warmed_instance_count              = 0
    remote_debugging_enabled               = false
    remote_debugging_version               = null
    runtime_scale_monitoring_enabled       = false
    scm_minimum_tls_version                = "1.2"
    scm_use_main_ip_restriction            = false
    use_32_bit_worker                      = true
    vnet_route_all_enabled                 = false
    websockets_enabled                     = false
    worker_count                           = 1
    application_stack {
      # dotnet_version              = "v6.0"
      # java_version                = null
      node_version                = "~18"
      # powershell_core_version     = null
      # use_custom_runtime          = false
      # use_dotnet_isolated_runtime = false
    }
    cors {
      allowed_origins     = ["https://portal.azure.com"]
      support_credentials = false
    }
  }
}

# __generated__ by Terraform from "/subscriptions/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/resourceGroups/import-test/providers/microsoft.insights/components/import-test-func"
resource "azurerm_application_insights" "main" {
  application_type                      = "web"
  daily_data_cap_in_gb                  = 100
  daily_data_cap_notifications_disabled = false
  disable_ip_masking                    = false
  force_customer_storage_for_profiler   = false
  internet_ingestion_enabled            = true
  internet_query_enabled                = true
  local_authentication_disabled         = false
  location                              = "japaneast"
  name                                  = "import-test-func"
  resource_group_name                   = "import-test"
  retention_in_days                     = 90
  sampling_percentage                   = 0
  tags = {
    env = "test"
  }
  workspace_id = "/subscriptions/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/resourceGroups/defaultresourcegroup-ejp/providers/Microsoft.OperationalInsights/workspaces/defaultworkspace-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-ejp"
}

所感

冒頭でも書いたように、従来のTerraformのインポート作業はなかなか厳しいものでした。
AzureはリソースIDがかなり長く、CLIでterraform importコマンドを打つと数行に渡ってしまって見づらく修正しにくかったので、HCLでコードとして書けるのは助かります。
特にパラメータを補完するのが大変でしたが、それを-generate-config-outで自動で生成してくれるのは非常に便利です!

ただ、ちょくちょくInvalidな書き方がされて結構なボリュームのエラーが出るため、
ある程度Terraformの知識を持っていないと修正が難しい部分はあります。
それでも体感80%前後ぐらいは補完してくれるので、これまでのインポート作業に比べるとかなり楽になったといって差し支えないと思います!

既存ツールとの比較

実は今回のインポート機能に近い機能を持つツールがAzure特化ではありますが以前から存在していました。
aztfexportというツールでMicrosoftの管理下で開発されています。(以前はaztfyterrafyという名前で個人プロジェクトだったような…と記憶しています)

GitHub - Azure/aztfexport: A tool to bring existing Azure resources under Terraform's management

こちらのツールは今回のインポート機能のような単一リソースのインポートだけでなく、
リソースグループ内の全リソースを取得したり、Azure Resource Graph クエリ構文を使用してフィルタリングして複数のリソースを取得することができます。
インポートするリソースを対話的に取捨選択することができるので、環境丸ごとのようなインポートができる点がかなりの強力なツールです。

aztfexportはセンシティブな値もすべて入力してくれて、ここは好みが分かれる部分ではありますが、
リソースIDとTerraform上のリソース名のマッピングファイルを生成・カスタマイズしてHCLを生成してインポートしてくれます。

aztfexportの利用方法についてはAzureのドキュメントに詳しく書かれています。

learn.microsoft.com

ニーズがありそうならaztfexportの使い方も紹介したいと思ってます。

おわりに

Terraform v1.5.0から登場するインポートブロックとコンフィグ生成機能を紹介させていただきました。

個人的には既存リソースをTerraformにインポートするハードルが1段下がった、という印象があります。

現時点ではaztfexportのほうが多機能な分優位性はあるかなとは思いますが、
Terraformのネイティブ機能なことと、まだベータに登場したばかりの新機能なので、今後のアップデートに期待したいと思います。


私達ACS事業部はAzure・AKSなどのクラウドネイティブ技術を活用した内製化のご支援をしております。

www.ap-com.co.jp

また、一緒に働いていただける仲間も募集中です!
今年もまだまだ組織規模拡大中なので、ご興味持っていただけましたらぜひお声がけください。

www.ap-com.co.jp

本記事の投稿者: 安藤 知樹
AzureとTerraformをメインにインフラ系のご支援を担当しています。 Shunsuke Yoshikawa - Credly