メインコンテンツへスキップ
  1. Blogs/

HCP Terraform運用の落とし穴トップ5

· loading · loading · ·
Terraform HCP Terraform 運用 ベストプラクティス
Nimbus Craftsman
著者
Nimbus Craftsman
目次

はじめに 👋
#

こんにちは、Nimbusです。

HCP Terraformを大規模環境で運用してきた中で、 「これは落とし穴だ!」と感じた運用上の課題をTop5形式でまとめました。

これからHCP Terraformを本格導入する方、すでに運用中だけど何か引っかかっている方の 参考になれば幸いです。それでは、Let’s Dive in! 🦄

落とし穴1:トークンの種類による権限の違いを知らない 🔑
#

何が起きたか
#

「Private Module RegistryにModuleをpublishしようとしたら、権限エラーで失敗…」

実はこれ、私が実際に遭遇した問題です。Organization Tokenを使って Private Module Registryへのpublishを試みたのですが、何度やっても unauthorizedエラーが返ってきました。

問題の本質
#

HCP Terraformには3種類のトークンが存在しますが、それぞれできることが異なります:

トークンの種類用途Private Module Registry publish
Organization Token組織全体の操作❌ できない
Team Tokenチーム単位の操作✅ できる
User Token個人ユーザーの操作✅ できる

特に厄介なのが、Organization TokenではPrivate Module Registryへのpublishができないこと。 これはTeam TokenまたはUser Tokenでないと実行できません。

UIからは問題なくpublishできたのに、Terraformから実行するとエラーになる… この原因が「使っているトークンの種類」だと気づくまで、かなり時間を要しました。

対策とベストプラクティス 💡
#

1. 用途に応じたトークンを使い分ける

# Private Module Registry操作にはTeam Token
provider "tfe" {
  token = var.team_token  # Organization Tokenではなく
}

2. トークンの管理ルールを明確にする

  • チームトークンの作成は手作業で実施
  • 発行したトークンはVariable SetsまたはHCP Vaultに保管
  • 有効期限は90日として、ライフサイクルはチームで管理

3. ドキュメント化する 各トークンで「できること・できないこと」を表にまとめてチーム共有しましょう。


落とし穴2:VCS連携の見えない依存関係 🔗
#

何が起きたか
#

ある日、後輩から「VCSプロバイダーからいくつかのリポジトリが見えなくなった」と連絡がありました。

状況整理:

  • 登場人物:私(GitHub OrganizationのOwner権限あり)と後輩(Owner権限なし)
  • 最初にVCSプロバイダーを作成したのは私
  • 作成直後は正常にOrganization内のリポジトリが確認できていた
  • 後輩が何かのタイミングでVCSプロバイダーの再認証を実施
  • 再認証後、いくつかのリポジトリが見えなくなった

問題の本質
#

HCP TerraformのVCS連携は、個人のGitHubアカウントと密に紐付いている

VCSプロバイダーを再認証した際、その操作を行ったユーザーのGitHub権限が VCSプロバイダーのアクセススコープに反映されます。

最初の状態:
VCSプロバイダー(私のOwner権限で作成)
  → Organization内の全リポジトリが見える ✅

後輩が再認証後:
VCSプロバイダー(後輩の権限で再認証)
  → 後輩がアクセスできるリポジトリのみ見える ⚠️
  → Owner限定のリポジトリが見えなくなる ❌

Owner権限を持つ私のユーザーで再認証したら、すべて正常に戻りました。

対策とベストプラクティス 💡
#

1. VCS接続の責任者を明確にする

  • 誰がVCSプロバイダーを管理するか文書化
  • 再認証は必ず管理者権限を持つユーザーが実施

2. 専用アカウントの作成(推奨)

私たちの組織では、個人アカウントに依存しない専用アカウントを作成しています:

専用アカウント:terraform-admin@xxxxx.com

【HCP Terraform側】
- 専用メールアドレスでユーザー登録
- Organization Owner権限を付与

【GitHub側】
- 同じメールアドレスでGitHubアカウント作成
- Organization Owner権限を付与
- このアカウントでVCSプロバイダーを作成・管理

このアプローチのメリット:

  • 個人に依存しない(誰かが退職しても影響を受けない)
  • 権限管理が明確(専用アカウントの権限のみ管理すればよい)
  • 再認証時のリスク低減(常に同じ権限で再認証される)
  • 監査が容易(VCS操作がすべてこのアカウント経由)

3. 定期的な権限確認

  • VCSプロバイダーでアクセスできるリポジトリ一覧を定期的に確認
  • 想定外のリポジトリが見えなくなっていないかチェック
  • 専用アカウントの認証情報を安全に管理(パスワード管理ツールなど)
注意: 「再認証」は単なる認証更新ではなく、アクセススコープの再定義です!

落とし穴3:WorkspaceのTerraform管理 - ガバナンス vs 利便性のジレンマ ⚖️
#

何が起きたか
#

「Workspaceの設定を少し変えたいだけなのに、いちいち管理者に依頼しないといけない…」 「自由に触らせたら、OIDC認証ではなくシークレットキーを直接入力された…」

WorkspaceをTerraformで管理するかどうかは、大規模運用では避けて通れない問題です。

問題の本質
#

アプローチ1:Terraformで厳格に管理
#

✅ メリット:
- ガバナンスを効かせられる(OIDC強制、命名規則統一など)
- 変更履歴がGitに残る
- Policy as Codeで設定を強制

❌ デメリット:
- 利用者の自由度が低い
- カスタマイズのたびに管理者へ依頼→ボトルネック化
- 「ちょっと試したい」ができない

アプローチ2:利用者に自由に触らせる
#

✅ メリット:
- 開発速度が上がる
- 管理者がボトルネックにならない
- 柔軟なカスタマイズが可能

❌ デメリット:
- OIDC使わずシークレット直接入力される
- 命名規則が守られない
- セキュリティリスク増大

対策とベストプラクティス 💡
#

私たちの組織の選択:厳格にTerraformで管理

私たちの組織では、組織の特性上、ガバナンスを優先し、すべてをTerraformで厳格に管理する方針を取っています。

1. 基盤設定はすべてTerraformで管理

# Workspace作成から設定まですべてコード化
resource "tfe_workspace" "example" {
  name         = var.workspace_name
  organization = var.organization
  
  # VCS連携を設定
  vcs_repo {
    identifier     = var.repository
    oauth_token_id = var.oauth_token_id
  }
}

2. Google Cloud認証はWorkload Identity(Dynamic Credentials)で統一

私たちの環境ではGoogle Cloud Workload Identityを使った動的クレデンシャルを採用しています。 これにより、シークレットキーの直接入力を根本的に防ぐことができます。

# Workload Identity Poolの設定
resource "google_iam_workload_identity_pool" "pool" {
  workload_identity_pool_id = "hcp-terraform-pool"
  display_name             = "HCP Terraform Pool"
  description              = "Pool for HCP Terraform workspaces"
}

# Workload Identity Providerの設定
resource "google_iam_workload_identity_pool_provider" "hcp_terraform" {
  workload_identity_pool_id = google_iam_workload_identity_pool.pool.workload_identity_pool_id
  
  workload_identity_pool_provider_id = "hcp-terraform-provider"
  display_name                       = "HCP Terraform Provider"
  
  oidc {
    issuer_uri = "https://app.terraform.io"
  }
  
  attribute_mapping = {
    "google.subject"                  = "assertion.sub"
    "attribute.terraform_workspace_id" = "assertion.terraform_workspace_id"
    "attribute.terraform_organization_id" = "assertion.terraform_organization_id"
  }
}

# サービスアカウントとの紐付け
resource "google_service_account_iam_binding" "workload_identity" {
  service_account_id = google_service_account.terraform_sa.name
  role               = "roles/iam.workloadIdentityUser"
  
  members = [
    "principalSet://iam.googleapis.com/${google_iam_workload_identity_pool.pool.name}/attribute.terraform_workspace_id/${var.workspace_id}"
  ]
}

3. Variable Setsで認証情報を一元管理

各WorkspaceにはVariable Setを通じてWorkload Identity設定を自動適用:

# Variable Set作成
resource "tfe_variable_set" "gcp_workload_identity" {
  name         = "gcp-workload-identity-credentials"
  description  = "Google Cloud Workload Identity settings"
  organization = var.organization
}

# Workload Identity有効化フラグ
resource "tfe_variable" "enable_gcp_provider_auth" {
  key             = "TFC_GCP_PROVIDER_AUTH"
  value           = "true"
  category        = "env"
  variable_set_id = tfe_variable_set.gcp_workload_identity.id
}

# Workload Providerの指定
resource "tfe_variable" "workload_provider_name" {
  key             = "TFC_GCP_WORKLOAD_PROVIDER_NAME"
  value           = google_iam_workload_identity_pool_provider.hcp_terraform.name
  category        = "env"
  variable_set_id = tfe_variable_set.gcp_workload_identity.id
}

# サービスアカウントの指定
resource "tfe_variable" "service_account_email" {
  key             = "TFC_GCP_RUN_SERVICE_ACCOUNT_EMAIL"
  value           = google_service_account.terraform_sa.email
  category        = "env"
  variable_set_id = tfe_variable_set.gcp_workload_identity.id
}

このアプローチで実現できること:

  • シークレットキーの直接入力を防止(そもそもできない仕組み)
  • 認証情報の一元管理(Variable Setで統一)
  • セキュリティの強化(短期トークンを自動生成)
  • 監査の容易化(誰がいつアクセスしたか追跡可能)
  • 設定の標準化(すべての設定がコードとして履歴に残る)

トレードオフ:得られたものと失ったもの

正直に言うと、この厳格な管理にはデメリットもあります:

✅ 得られたもの:
- 強固なガバナンス体制
- 設定ミスの大幅な削減
- セキュリティの向上
- 変更履歴の完全な可視化

❌ 失ったもの:
- 開発者の自由度
  → 変数の値をちょっと変えたいだけなのに管理者に依頼
  → 通知設定の変更すら自分でできない
- 開発スピード
  → 管理者がボトルネックになる
  → 「プロジェクト内だけでも自由にさせてほしい」という声

実際に開発者から聞いた声:

  • 「変数の値を更新するだけなのに、なぜPull Requestが必要なの?」
  • 「通知設定くらい自分で変えさせてほしい…」
  • 「プロジェクト内の設定くらいは自由にさせてほしい」

これらの声は十分理解できます。しかし、私たちの組織では、組織の特性上、ガバナンスを優先せざるを得ないという判断をしています。

組織によって優先順位は異なる

重要なのは、どちらが正解というわけではないということです:

優先事項推奨アプローチ
ガバナンス・監査対応が最優先Terraformで厳格に管理
開発スピード・柔軟性が最優先利用者に開放
両方のバランスを取りたい段階的管理(重要な設定はTerraform、それ以外は開放)

私たちのように金融・小売・医療など規制の厳しい業界では、前者を選ばざるを得ないことが多いです。 一方、スタートアップや開発スピード重視の組織では、後者が適しているでしょう。

判断のポイント:

  • 組織のコンプライアンス要件はどの程度厳しいか?
  • セキュリティインシデントが発生した場合の影響は?
  • 開発者の不便さと、ガバナンス強化、どちらを優先すべきか?
  • 管理者のリソースは十分にあるか?
ポイント: 完璧な解決策はありません。組織の特性に応じて、何を優先するかを明確にすることが重要です。

落とし穴4:Workspace設計ミス - ライフサイクルの混在 🔄
#

何が起きたか
#

「検証用のデータベースを削除したいのに、本番リソースと同じWorkspaceで管理してて消せない…」 「不要なリソースが残り続けて、毎月無駄なコストが発生している…」

問題の本質
#

ライフサイクルが異なるリソースを1つのWorkspaceに詰め込んでしまった

❌ 悪い例:
Workspace: myapp-prod
  ├─ 本番データベース(常時稼働・絶対に消せない)
  ├─ 検証用データベース(開発時のみ・消したい)
  └─ テスト用インスタンス(たまに使う・消したい)
  
→ 検証用だけ消したいのに、terraform destroyすると全部消える!
→ 仕方なく放置...コストだけがかさむ

これは単に「環境を分ける」だけでは解決しません。 同じ本番環境でも、常時稼働が必要なもの一時的なものは分離すべきです。

対策とベストプラクティス 💡
#

正しい設計:Volatility(変動性)でグルーピング

✅ 良い例:
Workspace: myapp-prod-core
  └─ 本番データベース(常時稼働・変更頻度:低)

Workspace: myapp-prod-testing
  └─ 検証用データベース(一時的・変更頻度:高)
  → 不要になったらWorkspaceごと削除可能!

Workspace: myapp-prod-temporary
  └─ テスト用インスタンス(スポット使用)
  → 使用後は即削除

Workspace設計の3つの軸:

  1. Environment(環境): dev / staging / prod
  2. Lifecycle(ライフサイクル): permanent / temporary / ephemeral
  3. Component(コンポーネント): network / compute / data

具体的な命名規則:

{プロジェクト}-{環境}-{ライフサイクル}-{コンポーネント}

例:
- payment-prod-permanent-database
- payment-prod-temporary-testing
- payment-dev-ephemeral-sandbox
コスト最適化のポイント: 一時的なリソースは専用Workspaceで管理し、使用後はWorkspaceごと削除!

さらに便利な機能:Automatically Destroy

HCP Terraformには、一時的なWorkspace向けのAutomatically Destroy機能があります(プランによって利用可否が異なります)。

resource "tfe_workspace" "temporary" {
  name         = "myapp-prod-temporary"
  organization = var.organization
  
  # 自動削除の設定
  auto_destroy_at = "2025-12-31T23:59:59Z"  # 指定日時に自動destroy
}

この機能のメリット:

  • 削除忘れを防止(指定日時に自動的にterraform destroy実行)
  • コスト削減の自動化(手動で削除する必要がない)
  • 一時的な検証環境に最適(検証期間終了後、自動的にクリーンアップ)

使用例:

検証用Workspace: myapp-prod-feature-test
  → auto_destroy_at: "2025-02-28T23:59:59Z"(2週間後に自動削除)
  → 検証終了後、自動的にリソースが削除される

UIからも設定可能で、Workspace設定画面から「Auto-destroy」のスケジュールを設定できます。


落とし穴5:Variable Setsのスコープがカオス化 📦
#

何が起きたか
#

「あれ?この変数どのWorkspaceに適用されてるんだっけ…?」 「本番環境なのに、開発用のGCP認証情報が適用されてる!」

大規模運用になると、Variable Setsの管理が一気にカオス化します。

問題の本質
#

Variable Setが乱立し、誰も全体像を把握していない

カオスの実例:

Variable Set: gcp-credentials
  → 適用先:全Workspace(本番も開発も!)😱

Variable Set: gcp-prod-only
  → 適用先:なぜか開発環境のWorkspaceにも適用されている...

Variable Set: common
  → 適用先:誰も把握していない
  → 中身:謎の変数が大量に入っている

Variable Set: gcp-creds-prod
Variable Set: gcp-credentials-production
Variable Set: prod-gcp-auth
  → 全部同じ用途なのに命名がバラバラ...

なぜこうなるか:

  • Variable Setsの命名規則がない
  • スコープ(どのWorkspaceに適用するか)の設計がない
  • 個人が勝手にVariable Setを作成してしまう
  • 不要になったVariable Setを削除していない

対策とベストプラクティス 💡
#

1. 命名規則を統一する

{環境}-{プロバイダー}-{用途}

例:
✅ prod-gcp-credentials
✅ dev-gcp-service-account
✅ staging-gcp-workload-identity
✅ global-common-vars(全環境共通)

2. タグベースでスコープを制御

# Workspaceにタグを付与
resource "tfe_workspace" "example" {
  name         = "myapp-prod"
  organization = var.organization
  
  tags = {
    environment = "prod"
    cloud       = "gcp"
    component   = "api"
  }
}

# Variable Setはタグで適用範囲を指定
resource "tfe_variable_set" "prod_gcp" {
  name         = "prod-gcp-credentials"
  organization = var.organization
}

# タグに基づいてVariable Setを適用
resource "tfe_workspace_variable_set" "prod_gcp_assignment" {
  for_each = {
    for ws in data.tfe_workspace_ids.prod_workspaces.ids : ws => ws
  }
  
  workspace_id    = each.value
  variable_set_id = tfe_variable_set.prod_gcp.id
}

data "tfe_workspace_ids" "prod_workspaces" {
  organization = var.organization
  tag_names    = ["environment:prod"]
}

3. Variable SetもTerraformで管理

# IaCで管理することで:
# - 誰が何を作ったか履歴に残る
# - レビュープロセスを挟める
# - 不要なVariable Setを検出しやすい
resource "tfe_variable_set" "example" {
  name         = var.variable_set_name
  description  = var.description
  organization = var.organization
}

4. 定期的な棚卸し

  • 月次でVariable Setの一覧を確認
  • 使われていないVariable Setを削除
  • 命名規則に従っていないものを修正

実装例:スコープ管理のマトリックス

Variable Set名適用タグ対象Workspace数用途
prod-gcp-credentialsenvironment:prod, cloud:gcp15本番GCP認証
dev-gcp-credentialsenvironment:dev, cloud:gcp30開発GCP認証
global-common-vars(全Workspace)195組織名など

まとめ 💡
#

HCP Terraformは強力なツールですが、大規模運用になると様々な落とし穴があります。 今回紹介した5つの落とし穴を意識することで、より安全で効率的な運用が可能になります。

運用の鉄則:
#

  1. トークンの種類と権限を理解する

    • 用途に応じた適切なトークンを選択
    • 管理ルールを明確にする
  2. VCS連携は組織アカウントで管理

    • 個人アカウントに依存しない
    • 再認証の影響範囲を理解する
  3. ガバナンスと利便性のバランスを取る

    • セキュリティは厳格に、開発効率は柔軟に
    • Variable Setsで共通設定を強制
  4. ライフサイクルでWorkspaceを分離

    • 常時稼働 vs 一時的で分ける
    • コスト最適化につながる
  5. Variable Setsは命名規則とタグで管理

    • カオス化を防ぐ仕組みを最初から作る
    • 定期的な棚卸しを忘れずに

おわりに ✨
#

大規模なHCP Terraform運用は試行錯誤の連続でした。 今回紹介した落とし穴は、すべて実際に遭遇し、痛い目を見た経験から学んだものです。

これらの経験が皆さんの運用改善に少しでも役立てば嬉しいです。

「こんな落とし穴もあった!」というご意見や質問があれば、 ぜひコメントやSNSで教えてください。

それでは、また次回お会いしましょう!See ya 👋🏻