IAMをTerraform化してみた

こんにちは、SCSKの齋藤です。

Google Cloud Platform (GCP) の Identity and Access Management (IAM) は、クラウドリソースへのアクセス制御を行う重要な機能です。しかし、組織が大きくなり、複数のフォルダとプロジェクトを管理するようになると、IAMの管理は複雑化し、手動での運用では限界が見えてきます。

本記事では、実際にGoogle Cloud組織全体のIAMをTerraformで自動化し、組織レベルからフォルダ、プロジェクトまでを一元管理できるシステムを構築した経験を紹介します。

なぜIAMのTerraform化が必要なのか?

GCPをはじめとするクラウド環境では、リソースのデプロイや設定変更の速度が非常に速く、手動での変更管理では追いつかないのが現状です。これはIAM権限についても同様で、特に大規模な組織や多くのプロジェクトが稼働している環境では、権限管理の複雑性が増大し、以下のような課題に直面しがちです。

手動でのIAM管理が抱える課題

  1. 可視性の欠如:
    • 誰が、いつ、どのような権限を、どのリソースに対して付与したのかが、コンソールや監査ログを手動で確認するだけでは把握しにくい。
    • 複数のプロジェクトやフォルダにまたがる権限の全体像を俯瞰することが困難。
  2. 一貫性の欠如と設定ミス:
    • 手動での権限付与は、ヒューマンエラーを誘発しやすく、意図しない権限の付与や、必要な権限の漏れが発生しやすい。
    • プロジェクトや環境ごとに同様の権限設定が必要な場合でも、手動では一貫性を保つのが難しい。
  3. レビュープロセスの困難さ:
    • 変更内容を第三者がレビューする仕組みがない、あるいは機能しにくい。
    • 権限変更が組織のセキュリティポリシーに準拠しているかを確認するのに時間がかかる。
  4. 変更履歴の追跡とロールバックの難しさ:
    • 特定の変更がいつ、誰によって行われたのかを追跡するのが難しい。
    • 誤った変更があった場合に、迅速かつ安全に元の状態に戻す(ロールバックする)ことが困難。
  5. セキュリティドリフトの発生:
    • コードで管理されていない場合、一時的な手動変更が恒久化し、定義されたセキュリティポリシーと実際の環境との間に乖離(ドリフト)が生じやすい。これにより、意図せず広範な権限が付与されたままになる「過剰な権限」が発生する可能性があります。

これらの課題は、セキュリティリスクの増大だけでなく、運用の非効率性、コンプライアンス違反のリスク、そして監査対応時の大きな負担へと繋がります。

TerraformによるIAM管理のアーキテクチャと実装

ここでは、TerraformでIAMを管理するための推奨されるディレクトリ構造、GCPリソースの動的な取得、そしてYAMLファイルによる設定管理の具体例を紹介します。

ディレクトリ構造

TerraformでIAMを効果的に管理するためには、明確でスケーラブルなディレクトリ構造が重要です。以下にその一例を示します。

terraform-iam/
├── main.tf                    # メイン設定ファイル (モジュールの呼び出しなど)
├── variables.tf               # 変数定義ファイル
├── outputs.tf                 # 出力値定義ファイル
├── backend.tf                 # Terraform Stateのバックエンド設定
├── provider.tf                # GCPプロバイダー設定
├── terraform.tfvars           # 変数のデフォルト値または環境固有の値
├── modules/
│   └── folder_hierarchy/      # フォルダ・プロジェクト階層のIAM管理用モジュール
│       ├── main.tf            # モジュール本体
│       ├── variables.tf       # モジュール変数定義
│       └── outputs.tf         # モジュール出力値
└── iam_configs/              # IAM設定を記述するYAMLファイル群
    ├── organization/         # 組織レベルのIAM設定
    │   └── organization.yaml
    ├── folders/              # フォルダレベルのIAM設定(フォルダごとにファイル分割)
    │   ├── production.yaml
    │   ├── analytics.yaml
    │   └── development.yaml
    └── projects/             # プロジェクトレベルのIAM設定(特定のプロジェクト用)
        ├── project_alpha.yaml
        └── project_beta.yaml

この構造により、IAM設定の責任範囲を明確にし、再利用性を高め、管理の複雑性を低減できます。

GCP IAMとTerraformリソースの対応

GCP IAMは階層的な構造(組織 > フォルダ > プロジェクト > リソース)を持っています。Terraformでは、この階層に応じて異なるリソースタイプが提供されます。

  • 組織 (Organization) レベルgoogle_organization_iam_membergoogle_organization_iam_policy
  • フォルダ (Folder) レベルgoogle_folder_iam_membergoogle_folder_iam_policy
  • プロジェクト (Project) レベルgoogle_project_iam_membergoogle_project_iam_policy
  • 特定リソース (Resource) レベルgoogle_storage_bucket_iam_membergoogle_bigquery_dataset_iam_member など、各サービス固有のリソース。
  • サービスアカウント (Service Account) レベルgoogle_service_account_iam_membergoogle_service_account_iam_policy (サービスアカウント自身への権限)

承知いたしました。ご提供いただいた詳細なコードとディレクトリ構成、運用面の考慮事項などを盛り込み、「IAMをTerraform化してみた」の記事を再構築します。前回の記事との依存関係をなくし、TerraformによるIAM管理に特化した内容とします。


IAMをTerraform化してみた

こんにちは、SCSKの齋藤です。

クラウド環境、特にGoogle Cloud (GCP) におけるIdentity and Access Management (IAM) の管理は、組織のセキュリティと運用効率の要となります。しかし、多くの企業でIAM権限の管理は複雑化し、セキュリティリスクや運用上の課題を抱えがちです。手動での権限付与は、ヒューマンエラー、変更履歴の追跡困難、コンプライアンス遵守の課題など、様々な問題を引き起こす可能性があります。

本記事では、GCP環境におけるIAM権限の管理をより効率的かつ安全に行うため、TerraformによるInfrastructure as Code (IaC) を導入する具体的なアプローチについて解説します。IAM設定をコードとして管理することで、手動運用が抱える様々な課題を解決し、堅牢でスケーラブルなセキュリティ基盤を構築できます。

第1章:はじめに – なぜ今、IAMのTerraform化が必要なのか?

GCPをはじめとするクラウド環境では、リソースのデプロイや設定変更の速度が非常に速く、手動での変更管理では追いつかないのが現状です。これはIAM権限についても同様で、特に大規模な組織や多くのプロジェクトが稼働している環境では、権限管理の複雑性が増大し、以下のような課題に直面しがちです。

1.1. 手動でのIAM管理が抱える課題

  1. 可視性の欠如:
    • 誰が、いつ、どのような権限を、どのリソースに対して付与したのかが、コンソールや監査ログを手動で確認するだけでは把握しにくい。
    • 複数のプロジェクトやフォルダにまたがる権限の全体像を俯瞰することが困難。
  2. 一貫性の欠如と設定ミス:
    • 手動での権限付与は、ヒューマンエラーを誘発しやすく、意図しない権限の付与や、必要な権限の漏れが発生しやすい。
    • プロジェクトや環境ごとに同様の権限設定が必要な場合でも、手動では一貫性を保つのが難しい。
  3. レビュープロセスの困難さ:
    • 変更内容を第三者がレビューする仕組みがない、あるいは機能しにくい。
    • 権限変更が組織のセキュリティポリシーに準拠しているかを確認するのに時間がかかる。
  4. 変更履歴の追跡とロールバックの難しさ:
    • 特定の変更がいつ、誰によって行われたのかを追跡するのが難しい。
    • 誤った変更があった場合に、迅速かつ安全に元の状態に戻す(ロールバックする)ことが困難。
  5. セキュリティドリフトの発生:
    • コードで管理されていない場合、一時的な手動変更が恒久化し、定義されたセキュリティポリシーと実際の環境との間に乖離(ドリフト)が生じやすい。これにより、意図せず広範な権限が付与されたままになる「過剰な権限」が発生する可能性があります。

これらの課題は、セキュリティリスクの増大だけでなく、運用の非効率性、コンプライアンス違反のリスク、そして監査対応時の大きな負担へと繋がります。

1.2. IAMをTerraformで管理するメリット

Infrastructure as Code (IaC) ツールであるTerraformを利用してIAMを管理することで、上記の手動管理の課題を解決し、以下のようなメリットを享受できます。

  1. 可視性と一元管理:
    • IAMポリシーがコードとしてTerraformファイルに明示的に記述されるため、権限の付与状況が一目で把握できます。
    • 組織、フォルダ、プロジェクト、リソースレベルなど、すべてのIAM設定を一元的に管理できます。
  2. バージョン管理と変更履歴:
    • Gitなどのバージョン管理システムと連携することで、IAMポリシーのすべての変更が履歴として残り、誰が、いつ、何を、なぜ変更したのかを追跡できます。
    • 必要であれば、特定の時点の構成に迅速にロールバックすることも可能です。
  3. レビューと承認プロセス:
    • 権限の変更は、コードとしてプルリクエスト(Pull Request)を通じて提出され、チームメンバーやセキュリティ担当者によるコードレビューを受けることができます。
    • これにより、変更内容の妥当性、セキュリティポリシーへの準拠、ベストプラクティスとの整合性を確保できます。
  4. 自動化と再現性:
    • Terraformによって、IAMポリシーのデプロイや更新を自動化できます。
    • これにより、同じポリシーを複数の環境(開発、ステージング、本番)に一貫して適用し、環境間の差異をなくすことが容易になります。
  5. セキュリティドリフトの検出と修正:
    • Terraform Stateファイルと実際のクラウド環境の状態を比較することで、手動で行われた変更(ドリフト)を検出し、コードに定義された状態に戻すことが可能です。
    • これにより、常にコードが「真実の源 (Source of Truth)」となり、セキュリティポリシーが強制されます。
  6. コンプライアンスと監査対応の簡素化:
    • コード化されたポリシーは、監査証跡として機能し、特定の規制(GDPR、PCI DSSなど)への準拠状況を証明しやすくなります。
    • 監査担当者は、コードリポジトリを参照することで、権限設定の詳細を容易に確認できます。

第2章:TerraformによるIAM管理のアーキテクチャと実装

ここでは、TerraformでIAMを管理するための推奨されるディレクトリ構造、GCPリソースの動的な取得、そしてYAMLファイルによる設定管理の具体例を紹介します。

2.1. 推奨されるディレクトリ構造

TerraformでIAMを効果的に管理するためには、明確でスケーラブルなディレクトリ構造が重要です。以下にその一例を示します。

python

terraform-iam/
├── main.tf # メイン設定ファイル (モジュールの呼び出しなど)
├── variables.tf # 変数定義ファイル
├── outputs.tf # 出力値定義ファイル
├── backend.tf # Terraform Stateのバックエンド設定
├── provider.tf # GCPプロバイダー設定
├── terraform.tfvars # 変数のデフォルト値または環境固有の値
├── modules/
│ └── folder_hierarchy/ # フォルダ・プロジェクト階層のIAM管理用モジュール
│ ├── main.tf # モジュール本体
│ ├── variables.tf # モジュール変数定義
│ └── outputs.tf # モジュール出力値
└── iam_configs/ # IAM設定を記述するYAMLファイル群
├── organization/ # 組織レベルのIAM設定
│ └── organization.yaml
├── folders/ # フォルダレベルのIAM設定(フォルダごとにファイル分割)
│ ├── production.yaml
│ ├── analytics.yaml
│ └── development.yaml
└── projects/ # プロジェクトレベルのIAM設定(特定のプロジェクト用)
├── project_alpha.yaml
└── project_beta.yaml

この構造により、IAM設定の責任範囲を明確にし、再利用性を高め、管理の複雑性を低減できます。

2.2. GCP IAMとTerraformリソースの対応

GCP IAMは階層的な構造(組織 > フォルダ > プロジェクト > リソース)を持っています。Terraformでは、この階層に応じて異なるリソースタイプが提供されます。

  • 組織 (Organization) レベルgoogle_organization_iam_membergoogle_organization_iam_policy
  • フォルダ (Folder) レベルgoogle_folder_iam_membergoogle_folder_iam_policy
  • プロジェクト (Project) レベルgoogle_project_iam_membergoogle_project_iam_policy
  • 特定リソース (Resource) レベルgoogle_storage_bucket_iam_membergoogle_bigquery_dataset_iam_member など、各サービス固有のリソース。
  • サービスアカウント (Service Account) レベルgoogle_service_account_iam_membergoogle_service_account_iam_policy (サービスアカウント自身への権限)

特に、_iam_member リソースは既存ポリシーに影響を与えず追記・削除を行うため推奨されますが、_iam_policy は既存ポリシーを上書きするため注意が必要です。

組織配下のすべてのリソースを動的に取得する仕組み

大規模なGCP環境では、個々のフォルダやプロジェクトを手動で指定するのではなく、組織内の既存リソースをTerraformで動的に取得し、IAM管理の対象とすることが効率的です。dataブロックを利用してこれを実現します。

# main.tf (または適切な場所に配置)

# 組織配下の直属のフォルダを動的に取得
data "google_folders" "organization_folders" {
  parent_id = "organizations/${var.organization_id}"
}

# 組織配下のすべてのサブフォルダを再帰的に取得(各直属フォルダの子フォルダを対象)
# mapのkeyは直属フォルダのIDになります
data "google_folders" "all_subfolders" {
  for_each  = toset([for folder in data.google_folders.organization_folders.folders : folder.folder_id])
  parent_id = "folders/${each.key}" # 'each.key' は親フォルダのID
}

# 組織配下のすべてのプロジェクトを取得
data "google_projects" "organization_projects" {
  # フィルタで組織IDを直接の親、または祖先に持つプロジェクトを指定
  filter = "parent.id:${var.organization_id} OR ancestors.id:${var.organization_id}"
}

この設定により、TerraformはGCP環境から最新の組織、フォルダ、プロジェクトの情報を取得し、IAM設定の基盤として利用できるようになります。

ローカル値による論理構造の構築

取得した動的なデータを、Terraformのlocalsブロックを使って管理しやすい論理構造に変換します。これにより、YAMLファイルで定義するIAM設定と、Terraformリソースを柔軟に連携させることができます。

# main.tf (localsブロックの例)
locals {
  # 組織直下のフォルダ(IAM設定のターゲット指定用)
  root_folders = {
    for folder in data.google_folders.organization_folders.folders :
    folder.display_name => folder.folder_id
    if !contains(var.excluded_folders, folder.folder_id) # 除外対象フォルダは含めない
  }

  # 各ルートフォルダ配下のすべてのサブフォルダ(再帰的に取得した情報を統合)
  # root_name (例: "production") をキーに、その配下の全フォルダID (root自身含む) のリスト
  all_subfolders_by_root = {
    for root_name, root_id in local.root_folders : root_name => concat(
      [root_id], # ルートフォルダ自身もリストに含める
      flatten([
        # data.google_folders.all_subfolders[root_id].folders は map(object)
        # その中の各folderオブジェクトからfolder_idを取得しflatten
        for subfolder in try(data.google_folders.all_subfolders[root_id].folders, []) : [
          subfolder.folder_id,
        ]
      ])
    )
  }

  # 各ルートフォルダ配下のすべてのプロジェクト
  # root_name (例: "production") をキーに、その配下の全プロジェクトIDのリスト
  projects_by_root_folder = {
    for root_name, root_id in local.root_folders : root_name => [
      for project in data.google_projects.organization_projects.projects :
      project.project_id
      # プロジェクトのancestorsにルートフォルダIDが含まれ、かつ除外対象でないものを抽出
      if contains(project.ancestors, root_id) && !contains(var.excluded_projects, project.project_id)
    ]
  }

  # IAM設定ファイルを読み込み、localsに統合
  # ここでiam_configs/folders/以下のYAMLファイルを動的に読み込みます。
  # 各ルートフォルダ名(例: "production")に対応するYAMLファイルを検索し、
  # 存在すればその内容をyamldecodeでパースし、Terraformで扱いやすいマップ構造に変換します。
  folder_iam_configs = {
    for root_name, _ in local.root_folders : root_name => {
      # フォルダごとのIAMバインディング
      folder_bindings = fileexists("${path.module}/iam_configs/folders/${lower(root_name)}.yaml") ? {
        for binding in flatten([
          for iam_entry in yamldecode(file("${path.module}/iam_configs/folders/${lower(root_name)}.yaml")).folder_bindings : [ # YAML内の'folder_bindings'キーを想定
            for role in iam_entry.roles : {
              principal = "${iam_entry.principal_type}:${iam_entry.email}"
              role      = role
              condition = lookup(iam_entry, "condition", null) # Conditionもサポート
            }
          ]
        ]) : "${binding.role}_${binding.principal}" => binding # Keyとしてroleとprincipalを連結しユニーク化
      } : {}

      # プロジェクトごとのIAMバインディング (フォルダ配下のプロジェクトに適用したい場合)
      project_bindings = fileexists("${path.module}/iam_configs/folders/${lower(root_name)}.yaml") ? {
        for binding in flatten([
          for iam_entry in yamldecode(file("${path.module}/iam_configs/folders/${lower(root_name)}.yaml")).project_bindings : [ # YAML内の'project_bindings'キーを想定
            for role in iam_entry.roles : {
              principal = "${iam_entry.principal_type}:${iam_entry.email}"
              role      = role
              condition = lookup(iam_entry, "condition", null)
            }
          ]
        ]) : "${binding.role}_${binding.principal}" => binding
      } : {}

      folder_ids  = local.all_subfolders_by_root[root_name] # そのルートフォルダ配下の全フォルダID
      project_ids = local.projects_by_root_folder[root_name] # そのルートフォルダ配下の全プロジェクトID
    }
  }
}

このlocalsブロックは、動的に取得したGCPの階層情報と、YAMLファイルで定義されたIAM設定を組み合わせ、folder_iam_configsというマップ構造に変換します。これにより、後続のモジュール呼び出しで、各フォルダセット(例: productionフォルダとその配下のすべてのフォルダ・プロジェクト)に適用すべきIAMポリシーを動的に渡すことが可能になります。

YAML設定ファイルの読み込みと構造

IAM設定は、iam_configs/ ディレクトリ配下にYAMLファイルとして定義します。これにより、HCL(HashiCorp Configuration Language)に不慣れなチームメンバーでもIAM設定を記述しやすくなり、Terraformコード本体と設定データを分離できます。

YAML設定ファイルの例 (iam_configs/folders/production.yaml):

# production.yaml
folder_bindings:
  - principal_type: "user"
    email: "admin@example.com"
    roles:
      - "roles/resourcemanager.folderViewer"
      - "roles/resourcemanager.folderEditor"

  - principal_type: "group"
    email: "production-team@example.com"
    roles:
      - "roles/resourcemanager.folderViewer"

  - principal_type: "serviceAccount"
    email: "terraform@your-project.iam.gserviceaccount.com"
    roles:
      - "roles/resourcemanager.folderAdmin"
project_bindings:
  - principal_type: "group"
    email: "production-team@example.com"
    roles:
      - "roles/viewer"
      - "roles/container.developer" # 例えばGKE開発者ロール

YAMLファイルでは、folder_bindingsproject_bindingsをキーとして、それぞれのレベルで付与したいプリンシパル、そのタイプ、メールアドレス、そしてロールのリストを定義します。これにより、特定のフォルダやその配下のプロジェクトに対して、一貫したIAMポリシーを適用できます。

TerraformによるIAM運用ワークフロー

IAMをTerraformで管理することは、単にコードを書くだけでなく、そのコードをセキュアに運用するためのワークフローを確立することが重要です。

コードレビューの導入

IAMポリシーの変更は、セキュリティに直接影響を与えるため、厳格なコードレビュープロセスが不可欠です。

  • プルリクエスト (Pull Request) ベースの開発:
    • Terraformコードの変更はすべて、Gitリポジトリのプルリクエストとして提出します。
    • セキュリティチーム、アーキテクト、または経験豊富なチームメンバーがレビュー担当者となり、変更内容の妥当性、最小権限原則への準拠、セキュリティベストプラクティスとの整合性を確認します。
  • terraform plan の活用:
    • レビューの際には、必ずterraform planコマンドの出力を共有します。
    • terraform planは、Terraformが実行しようとしている変更の差分を詳細に表示するため、意図しない権限の追加や削除がないかを確認する上で極めて重要です。

CI/CDパイプラインとの連携と実行コマンド

TerraformによるIAMのデプロイは、CI/CDパイプラインに組み込むことで自動化と信頼性を向上させます。

  • 自動テストとポリシーチェック:
    • Terraformコードの変更がプルリクエストとして提出された際、自動的にterraform validateを実行し、構文エラーがないかを確認します。
    • さらに、terraform fmtでコードのフォーマットを統一します。
    • OPA (Open Policy Agent) やterraform-complianceなどのポリシーチェックツールを導入し、セキュリティポリシーやコンプライアンス要件に違反していないかを自動的に検証します。
  • 自動terraform plan実行:
    • プルリクエストが作成されるたびに、CIパイプラインで自動的にterraform planを実行し、その出力をプルリクエストコメントとして自動投稿します。これにより、レビュー担当者は変更のプレビューを容易に確認できます。
  • 自動デプロイとロールバック戦略:
    • レビューと承認が完了したプルリクエストは、自動的に本番環境にterraform applyを実行する設定を検討します。
    • 自動デプロイは強力ですが、万が一の事態に備え、迅速なロールバック戦略を準備しておくことが重要です。Gitリポジトリの過去のコミットにgit revertで戻し、再度terraform applyを実行するなどの手順を確立します。

実行コマンド例:

Terraformの実行は、対象スコープを限定することで、影響範囲を制御しながら段階的に適用することができます。

# 全体実行(組織 + 全フォルダ + 全プロジェクトのIAMを適用)
terraform plan
terraform apply

# 組織レベルのみのIAMを適用(特定のモジュールをターゲット指定)
terraform plan -target=module.organization_iam
terraform apply -target=module.organization_iam

# 特定フォルダ配下全体(フォルダ + サブフォルダ + プロジェクト)のIAMを適用
# local.folder_iam_configsをfor_eachで展開しているモジュール(例: module.iam_for_root_folder)に対してターゲットを指定
terraform plan -target=module.iam_for_root_folder["production"]
terraform apply -target=module.iam_for_root_folder["production"]

# 複数フォルダのIAMを同時に指定して適用
terraform plan -target=module.iam_for_root_folder["production"] -target=module.iam_for_root_folder["analytics"]
terraform apply -target=module.iam_for_root_folder["production"] -target=module.iam_for_root_folder["analytics"]

-targetオプションは、特定の変更を検証・適用する際に非常に有用です。

まとめ

本記事では、GCP環境におけるIAM管理をTerraformでコード化する具体的な方法と、そのメリット、考慮すべき点について解説しました。

IAMをTerraform化することは、手動管理の限界を乗り越え、GCP環境におけるセキュリティと運用ガバナンスを大きく向上させる強力な手段です。ヒューマンエラーのリスクを低減し、透明性のあるレビュープロセスを導入し、変更履歴を完全に追跡できるようになります。さらに、最小権限の原則をコードとして強制することで、セキュリティリスクを大幅に削減することが可能です。

初期の導入には手間と学習コストがかかるかもしれませんが、その投資は長期的なセキュリティ体制の強化と運用効率の向上として必ず報われます。Infrastructure as CodeによるIAM管理は、現代のクラウド運用において不可欠なアプローチです。

継続的な改善と、セキュリティベストプラクティスの適用を通じて、より堅牢で効率的なクラウドインフラを目指していきましょう。

参考

著者について

SCSK株式会社
ソリューション事業グループ
基盤ソリューション事業本部

齋藤雄太をフォローする

クラウドに強いによるエンジニアブログです。

SCSKクラウドサービス(Google Cloud)は、Google Cloudの多彩なAIや各種サービスを活用したワンストップソリューションを提供します。SCSKのノウハウや体制を有効活用し、業務課題の解決に必要な全体検討と組み合わせで、最適な業務実装まで支援します。

Google Cloudクラウド
シェアする
タイトルとURLをコピーしました