Terraform管理リソースの手動変更を検知する方法の調査と検証
Terraform で管理している GCP リソースへの手動変更を検知する方法を、Cloud Run サービスを用いて実証的に調査しました。
調査の背景と目的
Infrastructure as Code を実践する中で、緊急対応時に手動でリソースを変更することが避けられない場面があります。しかし、これらの手動変更が Terraform コードに反映されないと、設定の不整合が発生し、運用上のリスクとなります。
本調査では、Terraform がどのような手動変更を検知できるのか、検知できない変更は何か、そして完全な変更検知を実現する方法があるかを明らかにすることを目的としました。
調査方法
検証環境
検証は Google Cloud プロジェクト上で実施しました。Terraform は v1.5 以上を使用し、gcloud CLI と jq を併用して動作確認を行っています。検証対象のリソースとしては、手動変更が頻繁に発生しやすい Cloud Run サービスを選択しました。
検証手順の概要
まず最小限の定義で Cloud Run サービスを Terraform で作成し、その後 Cloud Console から手動で各種変更を加えます。そして terraform plan
を実行して変更の検知状況を確認し、検知できない変更については代替の検知方法を調査しました。
実証実験
Step 1. Terraform による初期リソースの作成
検証用に最小限の属性のみを定義した main.tf
ファイルを作成しました。
terraform {
required_version = ">= 1.5"
required_providers {
google = {
source = "hashicorp/google"
version = "~> 5.0"
}
}
}
provider "google" {
project = var.project_id
region = var.region
}
variable "project_id" {
description = "GCP Project ID"
type = string
}
variable "region" {
description = "GCP Region"
type = string
default = "asia-northeast1"
}
# 最小限の定義のみを持つ Cloud Run サービス
resource "google_cloud_run_service" "minimal_service" {
name = "hello-minimal"
location = var.region
template {
spec {
containers {
image = "gcr.io/cloudrun/hello"
# リソース制限のみ定義(環境変数、ポートなどは未定義)
resources {
limits = {
cpu = "1"
memory = "512Mi"
}
}
}
}
# メタデータは最小限のみ
metadata {
labels = {
"managed-by" = "terraform"
}
}
}
# トラフィック設定も最小限
traffic {
percent = 100
latest_revision = true
}
}
output "service_url" {
value = google_cloud_run_service.minimal_service.status[0].url
}
意図的に環境変数やタイムアウト設定(デフォルトの 300 秒を使用)、managed-by 以外の追加ラベルなどは定義から除外しました。これにより、Terraform が未定義属性の変更をどう扱うかを確認できます。
Step 2. 手動変更の実施
Cloud Console から様々な変更を加えました。
Terraform で定義済み属性の変更
まず CPU を 1 から 2 に、メモリを 512Mi から 1Gi に変更しました。また、managed-by ラベルを terraform
から console
に書き換えました。
Terraform で未定義属性の変更
環境変数として LOG_LEVEL=debug
と REGION=asia-northeast1
を追加し、タイムアウト設定をデフォルトの 300 秒から 60 秒に短縮しました。さらに、environment=production
と updated-by=manual
というラベルを追加しました。
Step 3. terraform plan による検知結果
terraform plan
実行結果
# google_cloud_run_service.minimal_service will be updated in-place
~ resource "google_cloud_run_service" "minimal_service" {
~ template {
~ metadata {
~ labels = {
- "client.knative.dev/nonce" = "c12f701b-d51a-470d-8333-a120eb867f13" -> null
- "environment" = "production" -> null
~ "managed-by" = "console" -> "terraform"
- "updated-by" = "manual" -> null
}
}
~ spec {
~ containers {
- env {
- name = "LOG_LEVEL" -> null
- value = "debug" -> null
}
- env {
- name = "REGION" -> null
- value = "asia-northeast1" -> null
}
~ resources {
~ limits = {
~ "cpu" = "2000m" -> "1"
~ "memory" = "1Gi" -> "512Mi"
}
}
}
}
}
}
調査結果
変更検知の結果まとめ
変更内容 | Terraform 定義 | 検知結果 | 検知内容 |
---|---|---|---|
CPU/メモリ | あり | ✅ 検出 | 変更前後の値が明確に表示 |
managed-by ラベル | あり | ✅ 検出 | 変更前後の値が明確に表示 |
環境変数 | なし | ✅ 検出 | 削除として検出(現在値も表示) |
追加ラベル | なし | ✅ 検出 | 削除として検出(現在値も表示) |
タイムアウト | なし | ❌ 未検出 | 検知されず |
未定義属性も多くは検知される
- Terraform で定義していない環境変数やラベルも「削除」として検出
- 削除される値も表示されるため、変更内容の把握は可能
検知されない属性も存在する
- タイムアウト設定(
timeoutSeconds
)は検知されない - Terraform の state に記録されていない属性が該当
検知の仕組み
- Terraform は実際のリソースの状態を取得し、定義と比較
- 定義にない属性は「削除すべきもの」として扱われる
代替検知方法の調査
Cloud Asset Inventory による包括的な検知
Terraform で検知できない変更を包括的に確認するため、Cloud Asset Inventory を調査しました。
Cloud Asset Inventory を使うと、GCP リソースの完全な状態をスナップショットとして取得できます。このデータを BigQuery にエクスポートすることで、SQL を使って詳細なリソース状態の分析が可能になります。タイムアウト設定のような Terraform で検知できない属性も含めて、すべての設定値を確認できるため、手動変更の完全な把握に役立ちます。
# BigQuery データセットの作成
bq mk --dataset --location=$REGION ${PROJECT_ID}:terraform_audit
# スナップショットのエクスポート
gcloud asset export \
--project=$PROJECT_ID \
--bigquery-table=projects/${PROJECT_ID}/datasets/terraform_audit/tables/full_snapshot \
--content-type=resource \
--snapshot-time=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
Terraformerによる検知
Terraformerが本命だったのですが、以下のコマンドでサポートリソースを確認したところ、Cloud Run がサポートされていなかったため断念しました。
terraformer import google list --projects=$PROJECT_ID
上記コマンドの出力結果に Cloud Run が含まれていなかったため、この方法は使用できませんでした。
推奨される対策
Terraform 定義を包括的にする
可能な限り多くの属性を明示的に定義することが重要です。Google Cloud では、ベストプラクティスに基づいた Terraform ブループリントとモジュールを提供しているので、これらを活用することで、網羅的な定義を効率的に実装できます。
即時反映を原則とする
手動変更が必要な場合は、即座に Terraform コードに反映することが重要です。
Discussion