Terraform Ansible inventory 自動生成
【連携編】Google Cloud + Terraform + Ansible 環境ガイド
WSL2 + Google Cloud + Terraform + Ansible 連携
前回のガイドで、WSL2 (Ubuntu) 上に環境は整いました。ですので今回はより具体的な連携について記述していきたいと思います。
本稿では、Terraformが定義したインフラの「完成図」を、Ansibleが解釈可能な「名簿(Inventory)」へ自動翻訳する仕組みを構築します。これにより、IPアドレスの手動転記という不条理な作業を排除し、構成情報の「唯一の真実(SSOT)」を維持します。

graph TD
WSL2[WSL2 / Ansible] -- "gcloud IAP Tunnel" --> IAP[Identity-Aware Proxy]
IAP -- "Port 22 (Internal)" --> GCE[Compute Engine]
GCE -- "Verify" --> OSLogin[OS Login / IAM]
subgraph "IaC Control Plane"
TF[Terraform] -- "1. Define Output" --> Out[Output JSON]
Out -- "2. Parse" --> Inv[Dynamic Inventory]
endこの構成が解決する課題
- 踏み台サーバーの完全排除: 外部IPを持たないインスタンスへ、IAP経由で直接Ansibleを流し込みます。
- SSH鍵の管理放棄: OS Loginの有効化により、公開鍵の手動登録を不要にします。
- 動的スケーリングへの追従: 台数変更時もAnsible側の設定変更は一切不要です。
1. Terraform × Ansible 連携の設計パターン比較
「動けばいい」ではなく、運用に耐えうる接続方式を論理的に選定します。
| 方式 | 評価 | 採用可否の理由 |
| tfstate 直接参照 | ❌ | 状態ファイルの破壊リスクと、ツール間の密結合を招くため非推奨。 |
| GCP Inventory Plugin | △ | 正統派だが、IAM設計とサービスアカウントの権限管理が複雑化しやすい。 |
| Terraform Output 連携 | ◎ | 本ガイドで採用。明示的なインターフェースにより、疎結合で堅牢。 |
💡 LOGIC:
インフラの「出力(Output)」を契約としてAnsibleに渡すことで、Terraform内部の複雑なリソース構造からAnsibleを解放します。
2. Terraform実装:セキュアなOS Login構成
外部IPを完全に排除し、Google Cloudの認証基盤を利用する「本番準拠」の構成をとります。
[PATH] terraform/main.tf
resource "google_compute_instance" "web" {
count = 1
name = "web-server-${count.index}"
machine_type = "e2-micro"
metadata = {
# 💡 LOGIC: OS Loginを有効化し、SSH鍵管理をGoogle Cloud側に委譲
enable-oslogin = "TRUE"
}
network_interface {
network = "default"
# ⚠️ CAUTION: 本番環境準拠。外部IP(access_config)は付与せず、IAP経由の接続を前提とする
}
}
output "ansible_inventory_data" {
value = {
web_servers = [
for instance in google_compute_instance.web : {
name = instance.name
# 💡 LOGIC: 外部IPを持たないため、内部IPのみを出力
private_ip = instance.network_interface[0].network_ip
zone = instance.zone
}
]
}
}3. Ansible実装:Dynamic Inventory によるデータ変換
TerraformのJSON出力をパースし、Ansibleが解釈できる形式へ変換します。
[PATH] ansible/inventory/tf_inventory.py
#!/usr/bin/env python3
import json
import subprocess
def get_tf_output():
# terraform outputをJSON形式で取得
result = subprocess.check_output(["terraform", "-chdir=../../terraform", "output", "-json"])
return json.loads(result)
def build_inventory(tf_data):
# 💡 LOGIC: このインベントリは Ansible 実行時に「毎回オンメモリで生成」されます。
# Terraform apply 後にファイルを手動更新したり、Gitコミットしたりする必要はありません。
raw_data = tf_data.get("ansible_inventory_data", {}).get("value", {})
inventory = {"_meta": {"hostvars": {}}}
for group, hosts in raw_data.items():
inventory[group] = {"hosts": []}
for host in hosts:
hostname = host["name"]
inventory[group]["hosts"].append(hostname)
# 💡 LOGIC: ansible_hostにはインスタンス名を指定。
# IAPトンネルのProxyCommandが名前解決をGCE側で行うため、本構成で疎通可能です。
inventory["_meta"]["hostvars"][hostname] = {
"ansible_host": hostname,
"internal_ip": host["private_ip"],
"gcp_zone": host["zone"]
}
return inventory
if __name__ == "__main__":
print(json.dumps(build_inventory(get_tf_output())))4. 実装の急所:IAPトンネルと OS Login ユーザー
OS Login環境ではユーザー名が動的に決まるため、固定値を避け、gcloudコマンドの結果を利用します。
[PATH] ansible/ansible.cfg
[defaults]
inventory = ./inventory/tf_inventory.py
host_key_checking = False
[ssh_connection]
# 💡 LOGIC: ProxyCommand内の %h はAnsibleインベントリの ansible_host(インスタンス名)に置換されます。
# ※ IAPトンネルの確立には、WSL2側に gcloud CLI がインストールされている必要があります。
ssh_args = -o ProxyCommand="gcloud compute start-iap-tunnel %h 22 --listen-on-stdin --project=[PROJECT_ID] --zone=[ZONE]"5. ⚠️ 実務上の失敗回避:運用アンチパターン
セキュアなインフラ構成は、わずかな理解不足で「接続不能」の迷宮に陥ります。実務で遭遇しやすい3つの罠を整理します。
罠①:IAP と OS Login の「役割」を混同する
「必要な権限を付与したのにログインできない」トラブルの多くは、IAP と OS Login の責務を混同していることに起因します。
| 項目 | 役割(責務) | 失敗時の典型的な挙動 |
| IAP 権限 | VMまでの「通路」を確保(ネットワーク層) | 接続タイムアウト / Connection refused |
| OS Login | Linuxユーザーとして認証(OS層) | Permission denied (publickey) |
💡 LOGIC
IAP はネットワークのゲートウェイを開くだけです。VM 内でコマンドを実行するには、OS Login によるユーザー識別と SSH 鍵検証が別途必要です。
罠②:名前解決ができないと誤解する
WSL2 のローカル環境から ping [インスタンス名] を実行しても応答はありませんが、Ansible 経由の SSH は正常に動作します。
💡 LOGIC
ansible.cfg の ProxyCommand に指定した gcloud compute start-iap-tunnel が、Google Cloud API を通じてインスタンス名の解決とトンネル確立を同時に実行します。
罠③:OS Login 有効化直後の「反映ラグ」
Terraform で enable-oslogin = “TRUE” を適用した直後、数分間は SSH 接続が拒否される場合があります。
⚠️ CAUTION
これは Google Cloud 内部の同期遅延です。設定ミスを疑う前に、深呼吸して3分待つことが実務上の正解です。
6. IAM最小権限のTerraform定義
最小権限の原則(Least Privilege)に基づき、3つの Role を Terraform で定義します。
[PATH] terraform/iam.tf
# 1. IAPトンネルを通るための「通路」権限
resource "google_project_iam_member" "iap_accessor" {
project = var.project_id
role = "roles/iap.tunnelResourceAccessor"
member = "user:${var.admin_email}"
}
# 2. OS Loginでログインするための「鍵」権限
# ※ 本ガイドでは権限を最小化するため、sudo権限のない roles/compute.osLogin を採用しています。
resource "google_project_iam_member" "os_login" {
project = var.project_id
role = "roles/compute.osLogin"
member = "user:${var.admin_email}"
}
# 3. インスタンスのサービスアカウントを操作する権限
# 💡 LOGIC: IAP/OS Loginはプロジェクト全体へ作用するため project IAM、
# ServiceAccountUserは特定のSAに対する操作に限定するため SA IAM を適用し、スコープを最小化します。
resource "google_service_account_iam_member" "sa_user" {
service_account_id = google_service_account.instance_sa.name
role = "roles/iam.serviceAccountUser"
member = "user:${var.admin_email}"
}7. deploy.sh:一気通貫の実行スクリプト
ここまでの設計・IAM・接続を一つのコマンドに束ね、成功体験を固定化します。
[PATH] deploy.sh
#!/bin/bash
set -e
# 💡 LOGIC: 環境変数による OS Login ユーザー名の動的解決
export OS_LOGIN_USER=$(gcloud compute os-login describe-profile --format='value(posixAccounts[0].username)')
echo "--- Step 1: Terraform によるインフラ構築と権限付与 ---"
cd terraform
terraform apply -auto-approve
echo "--- Step 2: Ansible による構成管理の実行 ---"
cd ../ansible
# OS Loginの反映ラグ(罠③)を考慮し、ConnectionAttemptsでリトライを担保
ansible-playbook -i inventory/tf_inventory.py site.yml \
-e "ansible_user=${OS_LOGIN_USER}" \
--ssh-common-args="-o ConnectionAttempts=5"
echo "--- Deployment Successful! ---"Google Cloud 公式リファレンス
- GCS バックエンドを使用した Terraform 状態管理: https://cloud.google.com/docs/terraform/resource-management/store-state
- OS Login の仕組みと構成手順: https://cloud.google.com/compute/docs/oslogin
- IAP を使用した TCP 転送(SSH 接続)の概要: https://cloud.google.com/iap/docs/using-tcp-forwarding
- IAP 経由の接続に必要な IP アドレス範囲と FW 設定: https://cloud.google.com/iap/docs/using-tcp-forwarding#create-firewall-rule
Terraform / Ansible 公式リファレンス
- Terraform: GCS バックエンドの構成リファレンス: https://developer.hashicorp.com/terraform/language/settings/backends/gcs
- Ansible: ネットワーク制限下での ProxyCommand 実行(ssh_common_args): https://docs.ansible.com/ansible/latest/inventory_guide/intro_inventory.html#list-of-behavioral-inventory-parameters



コメントを残す