3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Azure リソースを Terraform で管理するなら AVM を使ってみよう

Last updated at Posted at 2025-12-05

この記事は terraform Advent Calendar 2025 6 日目の記事です。

Terraform × Azure なら AVM

みなさんは AVM (Azure Verified Modules) というものをご存知でしょうか?
従来、Terraform で Azure リソースを管理する場合は AzureRMAzAPI を使用するのが通例でした。ただし、これらはそれぞれ利用に関して課題があります。詳細は、以前に私が書いたブログを参照いただければと思います。

そこで、AVM (Azure Verified Modules) です。AVM とは Microsoft が公式に提供する Infrastructure as Code (IaC) モジュールの標準化イニシアティブになります。Azure リソースの構築・管理を効率化し、一貫性・信頼性・ベストプラクティス準拠を実現することを目的にされています。よって、「とにかく早く、安全に、Azureのベストプラクティスに沿った構成を作りたい!」という場合は、AVM をまず使用することが推奨となります。

AVM は WAF 準拠

AVM は Azure Well-Architected Framework (WAF) に準拠しています。Azure Well-Architected Framework (WAF) とは、Azure クラウド上で高品質なシステムを設計・運用するためのベストプラクティスと設計指針をまとめたものです。クラウドアーキテクトや開発者が、Azure 上でアプリケーションやサービスを構築する際の品質を評価・改善するための指針として使われています。

つまり、AVM を使用すれば、必然的に Microsoft が推奨するベストプラクティスに沿ったサービス構築ができるということです。その点で Azure リソースを IaC で管理する際に使用を検討する価値があります。

他プロバイダーとの比較

AVM は、Microsoft のエンジニアによって開発・保守されています。Microsoft が公式としてサポートし、継続してメンテナンスされることが明記されています。その点でまず、利用についての安心が出ます。

AzureRM に関しては、メンテナーがコミュニティのため、Microsoft Azure に精通していないユーザーによるプロバイダーを使用するという運用面でのリスクがあります。新機能に対しての対応は比較的遅いですし、任意設定項目のデフォルト値が WAF に準拠しない、セキュリティ的に NG な内容だったりすることも多いです。そのため、WAF への準拠のために設定を行いたい際、任意設定項目なのに明示的に指定しないといけなかったり、細かい部分まで手が届かなかったりと、辛い思いをすることもあります。

AzAPI は Microsoft によってサポートがされているものではありますが、そもそもの作りに課題があります。
AzAPI は Azure Resource Manager (ARM) の REST API 上に構築されているレイヤーになるので、他に比べてコードの記述量が多くなり可読性の低下が懸念されます。また、ARM REST API のラッパー的なもののため、使用する API バージョン指定などが必要になることから、将来的な互換性リスクや API 仕様変更に弱く、型チェック面でも課題があります。
さらに、REST API に依存する関係で必然的に JSON 構造の記述が多くなることから、tf.state ファイルの差分検出部分に懸念があり、未変更なのに意図しない再作成が起きるなどのリスクもあります。

正直なところ、上述のような AzureRM と AzAPI それぞれの課題を解決したモジュールが AVM というのが私の認識です。

実際に AVM を使ってみる

以降では、実際に AVM でどのように Terraform のコード記述をするのかについて紹介します。

AzureRM や AzAPI と比較した際のポイントとしては、以下があります。

  • resource 定義を作成する必要がない
  • ベストプラクティスの構成に沿った設定値変更を自前で実装する必要がない

以下は、Azure Functions の Flex Consumption の環境を AVM で書いてみた例です。

main.tf
#--- Resource Group ---#
module "resource_group" {
 source   = "Azure/avm-res-resources-resourcegroup/azurerm"
 location = var.location_japan_east
 name     = var.resource_group_name
 tags     = var.common_tags
}

output "resource_group_name" {
 value = module.resource_group.name
}

#--- API Management Service ---#
module "apim" {
 source = "Azure/avm-res-apimanagement-service/azurerm"
 # Required
 location            = var.location_japan_east
 name                = var.apim_name
 publisher_email     = var.apim_publisher_email
 resource_group_name = module.resource_group.name
 # Optional
 sku_name = var.apim_sku_name
 tags     = var.common_tags
}

#--- Storage Account ---#
module "storage_account" {
 source = "Azure/avm-res-storage-storageaccount/azurerm"
 # Required
 location            = var.location_japan_east
 resource_group_name = module.resource_group.name
 name                = var.storage_account_name
 # Optional
 account_replication_type      = var.storage_account_account_replication_type
 containers                    = var.storage_account_containers
 network_rules                 = var.storage_account_network_rules
 tags                          = var.common_tags
}

output "storage_account_razure_api_containers" {
 value = module.storage_account_razure_api.containers
}
output "storage_account_razure_api_name" {
 value = module.storage_account_razure_api.name
}
output "storage_account_razure_api_resource_id" {
 value = module.storage_account_razure_api.resource_id
}
output "storage_account_razure_api_containers_app_package_endpoint" {
 value = format("https://%s.blob.core.windows.net/%s",
   module.storage_account.name,
   var.storage_account_containers["app-package"].name
 )
}

#--- App Service Plan ---#
module "service_plan" {
 source = "Azure/avm-res-web-serverfarm/azurerm"
 # Required
 location            = var.location_japan_east
 name                = var.service_plan_name
 os_type             = var.os_type_linux
 resource_group_name = module.resource_group.name
 # Optional
 sku_name               = var.service_plan_sku_name
 tags                   = var.common_tags
}

output "service_plan_resource_id" {
 value = module.service_plan.resource_id
}

#--- Azure Function App (Flex Consumption) ---#
locals {
 app_settings = merge(
   var.function_app_app_settings,
   {
     AzureWebJobsStorage__accountName     = module.storage_account.name
     AzureWebJobsStorage__blobServiceUri  = "https://${module.storage_account.name}.blob.core.windows.net/"
     AzureWebJobsStorage__queueServiceUri = "https://${module.storage_account.name}.queue.core.windows.net/"
     AzureWebJobsStorage__tableServiceUri = "https://${module.storage_account.name}.table.core.windows.net/"
   }
 )
}

module "function_app" {
 source = "Azure/avm-res-web-site/azurerm"
 # Required
 kind                     = var.function_app_kind
 location                 = var.location_japan_east
 name                     = var.function_app_name
 os_type                  = var.os_type_linux
 resource_group_name      = module.resource_group.name
 service_plan_resource_id = module.service_plan.resource_id
 # Optional
 app_settings = local.app_settings
 application_insights = {
   #--- Application Insights ---#
   location                      = var.location_japan_east
   name                          = var.aai_name
   resource_group_name           = module.resource_group.name
   workspace_id                  = var.aai_workspace_id
   application_type              = var.aai_application_type
   local_authentication_disabled = var.aai_local_authentication_disabled
   sampling_percentage           = var.aai_sampling_percentage
   tags                          = var.common_tags
 }
 fc1_runtime_name            = var.function_app_fc1_runtime_name
 fc1_runtime_version         = var.function_app_fc1_runtime_version
 function_app_uses_fc1       = var.function_app_razure_api_uses_fc1
 https_only                  = var.function_app_https_only
 managed_identities          = var.function_app_managed_identities
 storage_account_name        = module.storage_account.name
 storage_authentication_type = var.function_app_storage_authentication_type
 storage_container_endpoint = format("https://%s.blob.core.windows.net/%s",
   module.storage_account.name,
   var.storage_account_containers["app-package"].name
 )
 storage_uses_managed_identity                  = var.function_app_storage_uses_managed_identity
 tags                                           = var.common_tags
 webdeploy_publish_basic_authentication_enabled = var.function_app_webdeploy_publish_basic_authentication_enabled
}

output "function_app_application_insights" {
 value     = module.function_app.application_insights
 sensitive = true
}
output "function_app_system_assigned_managed_identity_principal_id" {
 value = module.function_app.system_assigned_mi_principal_id
}
output "function_app_resource_id" {
 value = module.function_app.resource_id
}

#--- Role Assignment ---#
# Storage Blob Data Contributor
module "role_assignment_storage_blob_data_contributor" {
 source = "Azure/avm-res-authorization-roleassignment/azurerm"
 # Optional
 role_assignments_azure_resource_manager = {
   storage_blob_data_contributor = {
     role_definition_name = "Storage Blob Data Contributor"
     principal_id         = module.function_app.system_assigned_mi_principal_id
     scope                = module.storage_account.resource_id
     principal_type       = "ServicePrincipal"
   }
 }
}

# Website Contributor
module "role_assignment_website_contributor" {
 source = "Azure/avm-res-authorization-roleassignment/azurerm"
 # Optional
 role_assignments_azure_resource_manager = {
   website_contributor = {
     role_definition_name = "Website Contributor"
     principal_id         = var.principal_deployment_id
     scope                = module.function_app.resource_id
     principal_type       = "ServicePrincipal"
   }
 }
}

例えば、Blob Storage の access_tier、account_tier や min_tls_version の値は記述していませんし、App Service Plan の worker_count や zone_balancing_enabled のような値も記述していません。明示的に定義しない場合、これらが全て AVM の推奨値で自動構成されます。

Blob Storage のコンテナー (var.storage_account_containers) に関しては、variables.tf にて型の定義を書き、terraform.tfvars にて Production/Development 環境別に作成するよう対応しています。
設計都合やシステム構成の都合により、AVM のデフォルト設定から変更したい場合は、従来同様変数設定もできます。

AVM で担保される推奨設定値から逸脱させるものに限って、いわゆるパラメータシート的な形で管理するようになるため、運用負荷もかなり軽減されます。

variables.tf (抜粋)
variable "storage_account_containers" {
  type = map(object({
    name               = string
    public_access_type = string
    cors_rules         = any
  }))
  description = "ストレージアカウント内に作成するコンテナ一覧"
  default     = {}
}
envs/production/terraform.tfvars (抜粋)
storage_account_containers = {
  "hosts" = {
    name = "azure-webjobs-hosts"
    public_access_tier = "private"
    cors_rules = []
  }
  "secrets" = {
    name = "azure-webjobs-secrets"
    public_access_tier = "private"
    cors_rules = []
  }
  "app-package" = {
    name = "app-package"
    public_access_tier = "private"
    cors_rules = []
  }
}

Terraform × Azure を始めるならまずは AVM

正直な話、IaC を初めて学ぶ人にとって Terraform は学習コストが低いとは言えません。そういった点においても、Terraform × Azure の場合は、AVM から始めてみるのが非常におすすめです。

最近は Microsoft Foundry (旧 Azure AI Foundry) や Azure OpenAI をきっかけに Microsoft Azure へ入門する人もかなり増え、実務で IaC に取り組む人も増えたと思います。
アプリケーションコードだけでなく、ぜひこの機会に AVM でインフラの IaC にも取り組み、良い CI/CD ライフを過ごしていただければと思います。

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?