NTT DATA TECH
🐷

GitHub Copilot と Azure の新機能を使って Terraform コードの作成を効率化してみた

に公開

はじめに

生成AIの活用によってIaCのコード作成は大幅に効率化されてきているように思います。
特にTerraformにおいては、HashiCorp社からMCP(Model Context Protocol)Serverが提供されたことで、プロバイダやモジュールの情報取得精度が上がり、出力されるコードの品質が格段に向上したと感じています。

一方で、コード生成時に最も重要なのは「何を作りたいか」のコンテキストです。
特にIaCの場合はコードで様々なパラメータを定義していくため、期待するコードを得るには細かくコンテキストを指定する必要があります。IaCでコード化する予定のシステム(またはリソース)についてのドキュメントが用意されていれば良いですが、パラメータレベルで細かい情報が記載されたドキュメントは、IaCとしてコードで管理する前提とした場合には無いことも多いと思います。
したがって、手元のドキュメントを読み込ませてある程度コンテキストは与えつつも、足りない部分はチャットに記述したり、生成されたコードを手直ししたり、という作業が必要になると思います。

そこで今回は、Azureに新しく追加された機能を用いて、コンテキストを上手く渡してより精度の高いコードを作成する方法を紹介しようと思います。

Terraform MCP Serverの準備

まずは、Terraform MCP Serverを利用するための準備をしておきます。
具体的なセットアップ手順は提供元のGithubを参照してください。
https://github.com/hashicorp/terraform-mcp-server

Dockerを使う方法がメインで書かれていますが、ビルド済の実行ファイルも用意されています。
https://releases.hashicorp.com/terraform-mcp-server/0.2.0/
私はWindows環境なので以下をダウンロードしています。
https://releases.hashicorp.com/terraform-mcp-server/0.2.0/terraform-mcp-server_0.2.0_windows_amd64.zip
zipファイル内に「terraform-mcp-server.exe」が入っていますので、適当な場所に配置したうえで、mcp.jsonにファイルのパスを記述してあげればOKです。
MCPのバージョンを管理する上ではDockerを利用した方が便利かと思いますが、実行ファイルを置くだけなのでDockerが使えない場合でも利用できますし、手っ取り早く試すにはこの方が良いかと思います。

.vscode/mcp.jsonの内容は以下の通りです。
パスの部分は適宜読み替えてください。

.vscode/mcp.json
{
  "servers": {
    "terraform": {
      "command": "C:\\path\\to\\terraform-mcp-server.exe",
      "args": ["stdio"]
    }
  }
}

Azure Verified Module

Terraform MCP Serverを利用することで、Terraform Registryからプロバイダやモジュールを探し、さらにそれらのドキュメントを参照してコードを生成できるようになり十分精度の高いコードが出力されると思います。今回はより品質を高めるためにMicrosoftが公式にサポートしているTerraform ModuleであるAVM(Azure Verified Modules)を使用することにします。

https://azure.github.io/Azure-Verified-Modules/indexes/terraform/tf-resource-modules/

AVMは以下のような特徴があります。

  • Bicep 用と Terraform 用のモジュールが用意されている
  • リソースモジュールとパターンモジュールの2種類が用意されている
    • リソースモジュール:個別リソースのデプロイ用途
    • パターンモジュール:Azure Landing Zoneのような構成パターンをデプロイ
  • 一貫性を担保するための明確な仕様への準拠
    • Terraform のリソースモジュールの場合はこちら に記載された仕様に準拠
    • WAF(Well-Architected Framework)等のフレームワークで推奨されたパラメータを採用
  • Microsoftの AVMチームによるサポート

AVMはTerraform Registryに登録されているため、Terraform MCP Serverが探索するモジュールに含まれています。したがって、追加で何かをする必要はなくGitHub Copilotに「モジュールはAVMで」とお願いするだけです。

カスタムインストラクションの準備

続いて、GitHub Copilot側の準備です。AVMを使ってほしい旨をあらかじめカスタムインストラクションとして記述しておきます。
基本方針だけ記述して、残りは「TerraformのIaCコード作成用のカスタムインストラクションを作成して」とGitHub Copilotに作成をお願いしました。

.vscode/copilot-instructions.md
# IaCコード作成方針

## 基本方針
- Terraformのベストプラクティスに従い、フォルダ、ファイルを適切に分割してください。
- 作成する対象はAzureのリソースです。リソースの作成はAVM(Azure Verified Modules)を使用してください。
- AVMに無い場合のみ独自のモジュールをローカルに作成し、再利用性を高めてください。
- IaCコードを実行する環境はWindows、Powershellです。

## ファイル構成・命名規則
- 環境ごと(dev、staging、prod)にディレクトリを分離してください。
- リソース名はAzureのベストプラクティスに従った命名規則を適用し、環境やプロジェクト識別子を含めてください。
- モジュールはmodules/ディレクトリ配下に整理してください。

## コード品質・保守性
- 変数には適切なtype、description、validation ルールを定義してください。
- outputs.tfでリソース情報を適切に出力し、他のモジュールとの連携を考慮してください。
- locals.tfを活用して計算値や共通値を整理してください。
- data sourceは適切に活用し、既存リソースとの連携を図ってください。

## セキュリティ・運用
- sensitive = trueを適切に設定し、機密情報の漏洩を防いでください。
- azurerm providerのfeatures blockを適切に設定してください。
- リソースグループ、タグ付けの戦略を統一してください。
- tfstateファイルはAzure Storage Accountのremote backendで管理してください。

## バージョン管理・依存関係
- Terraform、providerのバージョンを固定してください。
- required_providersブロックでバージョン制約を明記してください。
- .terraform.lock.hclファイルをバージョン管理に含めてください。

## 実行・テスト
- terraform plan の実行結果を必ず確認してから apply してください。
- 各環境への適用前に terraform validate, terraform fmt を実行してください。
- 可能な限り terraform-docs でドキュメントを自動生成してください。

## メモリ機能・タスク管理
- GitHub Copilotのメモリ機能を活用して、プロジェクトの構成や設計方針を記録してください。
- 作成したリソースの依存関係や設定理由をメモリに保存し、後続の作業で参照できるようにしてください。
- 環境固有の設定値や制約事項は必ずメモリ機能に記録してください。
- 作業中のタスクや課題は明確に整理し、進捗状況を追跡できるようにしてください。
- インフラの変更履歴や影響範囲についてもメモリ機能で管理してください。
- 障害対応や運用手順についての知見も蓄積し、チーム内で共有できるようにしてください。
- 定期的にメモリ内容を見直し、最新の状態に更新してください。

### ファイル出力・参照管理
- メモリ情報は `.github/memory/` ディレクトリ配下に以下のファイルで管理してください :
  - `project-memory.md`: プロジェクト全体の構成・設計方針
  - `resource-dependencies.md`: リソース間の依存関係と設定理由
  - `environment-configs.md`: 環境固有の設定値・制約事項
  - `tasks-progress.md`: 作業中のタスク・課題・進捗状況
  - `change-history.md`: インフラ変更履歴・影響範囲
  - `operations-knowledge.md`: 障害対応・運用手順の知見
- 各ファイルは作業開始時に必ず確認し、最新情報を把握してください。
- 作業完了後は該当ファイルを更新し、次回作業時の参照に備えてください。
- ファイル更新時は日付とバージョン情報を記録してください。

## プロジェクト情報管理
- 各環境のリソース構成図や依存関係をメモリで管理してください。
- デプロイ手順や設定手順の詳細をタスクとして記録してください。
- セキュリティ要件や規制要件をメモリに保存し、常に参照できるようにしてください。
- 過去のトラブルシューティング事例や解決方法を蓄積してください。

### ドキュメント参照フロー
- 作業前: `.github/memory/` 配下の関連ファイルを確認
- 作業中: 新たな知見や変更点をメモとして記録
- 作業後: 該当するメモリファイルを更新・コミット
- 定期見直し: 週次でメモリファイルの整理・統合を実施

Azure ポータルからTerraform コードをエクスポート

最後に、コンテキストとして生成AIに提供する情報を準備します。IaCのコード作成を行う場合、まずはAzureポータル等で画面操作を行って仮のリソースを作成し、動作確認や仕様の把握などを行うケースが多いかと思います。
であれば、そうやって一度作ったリソースの情報をコンテキストとして渡してあげれば、より精度の高いコードを生成できるはずです。

Azureポータルには元々、ARMテンプレート、およびBicepのコードをエクスポート機能があります。ですので、ARMテンプレートやBicepのコードをエクスポートしてそれをコンテキストとするという方法が考えられます。

ただし、今回は新しく追加された機能であるTerraform コードをエクスポートして利用してみようと思います。
これまでにも aztfexport のようなTerraformのコードとしてエクスポートするツールはありましたが、ポータルだけで実施できるようになりました。
なお、Azure Updates ではそれらしい投稿は見当たらないのですが、以下のブログ記事でこの機能が紹介されています。
ポータルの画面には記載がありませんが、現時点ではパブリックプレビューのようです。
https://techcommunity.microsoft.com/blog/azuretoolsblog/announcing-public-preview-of-terraform-export-from-the-azure-portal/4409889

以下が実際のAzureポータルでTerraform コードを生成した画面です。

alt text

Azureポータルの左メニューのオートメーション > テンプレートのエクスポート 画面に遷移し、Terraform タブを選択します。
Terraform のコードは AzureRM と AzAPI の2種類のプロバイダから選べるようになっています。
各プロバイダの違いは以下のドキュメント等を参照してください。
https://learn.microsoft.com/ja-jp/azure/developer/terraform/overview

今回はテスト用に作ったストレージアカウントのコードをエクスポートします。
個別のリソースを定義していく場合、通常は AzureRM プロバイダを使用することの方が多いと思いますので、今回は AzureRM でコードをエクスポートしてみます。

生成された ストレージアカウント の Terraform コード
terraform {
  required_providers {
    azurerm = {
      source  = "azurerm"
      version = "4.24.0"
    }
  }
}
provider "azurerm" {
  features {}
}
resource "azurerm_storage_account" "res-0" {
  access_tier                       = "Cold"
  account_kind                      = "StorageV2"
  account_replication_type          = "ZRS"
  account_tier                      = "Standard"
  allow_nested_items_to_be_public   = false
  allowed_copy_scope                = "AAD"
  cross_tenant_replication_enabled  = false
  default_to_oauth_authentication   = true
  dns_endpoint_type                 = "Standard"
  edge_zone                         = ""
  https_traffic_only_enabled        = true
  infrastructure_encryption_enabled = true
  is_hns_enabled                    = false
  large_file_share_enabled          = true
  local_user_enabled                = true
  location                          = "japaneast"
  min_tls_version                   = "TLS1_2"
  name                              = "storagegithubcopilottest"
  nfsv3_enabled                     = false
  primary_access_key                = "" # Masked sensitive attribute
  primary_blob_connection_string    = "" # Masked sensitive attribute
  primary_connection_string         = "" # Masked sensitive attribute
  public_network_access_enabled     = false
  queue_encryption_key_type         = "Account"
  resource_group_name               = "rg-test"
  secondary_access_key              = "" # Masked sensitive attribute
  secondary_blob_connection_string  = "" # Masked sensitive attribute
  secondary_connection_string       = "" # Masked sensitive attribute
  sftp_enabled                      = false
  shared_access_key_enabled         = true
  table_encryption_key_type         = "Account"
  tags                              = {}
  blob_properties {
    change_feed_enabled           = true
    change_feed_retention_in_days = 0
    default_service_version       = ""
    last_access_time_enabled      = false
    versioning_enabled            = true
    container_delete_retention_policy {
      days = 7
    }
    delete_retention_policy {
      days                     = 7
      permanent_delete_enabled = false
    }
    restore_policy {
      days = 6
    }
  }
  network_rules {
    bypass                     = ["AzureServices"]
    default_action             = "Deny"
    ip_rules                   = []
    virtual_network_subnet_ids = []
  }
  routing {
    choice                      = "InternetRouting"
    publish_internet_endpoints  = false
    publish_microsoft_endpoints = false
  }
  share_properties {
    retention_policy {
      days = 7
    }
  }
}

GitHub Copilotでコードを生成

それでは、GitHub Copilotでコードを作成していきます。
初期のディレクトリ構造は以下のような状態です。
AzureポータルからエクスポートしたTerraform コードはcontext.tfと名前を付けて保存しています。

IaC/
├── context.tf
|
├── .github/
|   └── copilot-instructions.md
|
└── .vscode/
    └── mcp.json

この状態で、GitHub Copilotをエージェントモードに変更して以下の指示を与えます。なお、モデルはClaude Sonnet 4を利用しました。

context.tfは作成予定のリソースのTerraform コードです。
カスタムインストラクションに従い、リファクタリングしてください。

実際に実行してみると、Terraform MCP Serverを使用してAzure Verified Moduleを探してモジュールのドキュメントの情報を取得しているのが分かります。

alt text

以下は自動生成されたREADME.mdに記載されていたプロジェクト構造です。

プロジェクト構造
project/
├── .github/
│   ├── copilot-instructions.md     # GitHub Copilot用の指示書
│   └── memory/                     # プロジェクトメモリ
│       ├── change-history.md
│       ├── environment-configs.md
│       ├── operations-knowledge.md
│       ├── project-memory.md
│       ├── resource-dependencies.md
│       └── tasks-progress.md
├── environments/                   # 環境別設定
│   ├── dev/                       # 開発環境
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   ├── outputs.tf
│   │   └── terraform.tfvars
│   ├── staging/                   # ステージング環境
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   ├── outputs.tf
│   │   └── terraform.tfvars
│   └── prod/                      # 本番環境
│       ├── main.tf
│       ├── variables.tf
│       ├── outputs.tf
│       └── terraform.tfvars
├── modules/                       # 再利用可能なモジュール
│   └── storage/                   # Storage Accountモジュール
│       ├── main.tf
│       ├── variables.tf
│       ├── outputs.tf
│       ├── versions.tf
│       └── README.md
├── main.tf                        # ルートレベル設定
├── locals.tf                      # 共通ローカル値
├── context.tf.backup              # 元の設定(バックアップ)
└── README.md                      # このファイル

Terraform のベストプラクティスに倣ったIaCらしいコードが出来上がっています。
また、project-memory.mdには、以下のようにコンテキストとして渡したcontext.tfで既存リソースの情報から設定すべき情報を抽出して記録出来ていました。もちろん、実際のコードにもこれらの情報が反映されています。

## セキュリティ要件
- パブリックネットワークアクセス: 無効
- 暗号化: 有効(インフラ暗号化含む)
- 最小TLSバージョン: 1.2
- 既定認証: OAuth
- ネットワーク制限: Azure Servicesのみ許可

## 運用要件
- 変更フィード: 有効
- バージョニング: 有効
- 削除保護: 7日間
- バックアップ/復元: 6日間

まとめ

本記事では、GitHub Copilot + Terraform MCP Server + AVM + AzureのExport機能を活用することで、再利用性や品質の高いIaCコードを簡単に生成できることをお伝えしました。
Terraformのコードを直接エクスポートできるようになったことで再利用性を考えなければそのままでも十分活用できると思いますが、生成AIをうまく活用してより品質の高いコードを作っていきましょう。

仲間募集

NTTデータ 金融高度技術本部では、以下の職種を募集しています。

金融系オープン系基盤エンジニア/基盤リーダ(ITアーキテクト)
NTT DATA TECH
NTT DATA TECH
設定によりコメント欄が無効化されています