VPC Service Controls Enforced Mode移行時のトラブルシューティング完全ガイド

VPC Service Controls(VPC-SC)をEnforced Modeに移行する際、「Request is prohibited by organization’s policy」エラーに遭遇することは珍しくありません。本記事では、実際の移行プロジェクトで経験したトラブルシューティング手法と、体系的な問題解決アプローチを解説します。

典型的なエラーパターン

GitHub Actions実行時のエラー

1
2
Error 403: Request is prohibited by organization's policy. 
For more information on VPC Service Controls, see https://cloud.google.com/vpc-service-controls/docs

このエラーは、以下の操作で頻繁に発生します:

  • terraform init時のGCS state bucket アクセス
  • terraform plan/apply時の各種Google Cloud API呼び出し
  • BigQuery、Cloud Storage、Secret Manager等のリソース操作

段階的トラブルシューティング手法

ステップ1: Cloud Loggingでの詳細調査

まず、具体的にどのリクエストが拒否されているかをCloud Loggingで確認します:

1
2
3
4
# Cloud Loggingクエリ例
protoPayload.serviceName="vpcservicecontrols.googleapis.com"
protoPayload.methodName="google.cloud.vpcservicecontrols.v1.VpcServiceControlsServiceV1.CheckViolation"
severity="ERROR"

重要な確認ポイント:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
  "protoPayload": {
    "authenticationInfo": {
      "principalEmail": "github-actions-apply-dev@main-project-dev.iam.gserviceaccount.com"
    },
    "serviceName": "storage.googleapis.com",
    "methodName": "google.storage.objects.get",
    "resourceName": "projects/_/buckets/main-project-dev-tfstate/objects/terraform.tfstate"
  }
}

これらの情報から、誰がどのサービスのどのメソッドでアクセス拒否されているかを特定できます。

ステップ2: Access Level設定の確認

サービスアカウントがAccess Levelに含まれているかを確認:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# access_level定義の確認
locals {
  access_level = {
    service_accounts = {
      main_project = [
        # このリストに該当のサービスアカウントが含まれているか?
        var.service_accounts_main_project["github-actions-apply-${var.env}"].member,
        var.service_accounts_main_project["github-actions-plan-${var.env}"].member,
      ]
    }
  }
}

確認コマンド:

1
2
3
4
5
# Terraform planで実際に作成されるリソースを確認
terraform plan -target=module.access_level

# 特定のAccess Levelの内容を確認
gcloud access-context-manager levels describe [ACCESS_LEVEL_NAME] --policy=[POLICY_ID]

ステップ3: Service Perimeter設定の確認

Access LevelがService Perimeterに含まれているかを確認:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# service_perimeter設定の確認
locals {
  service_perimeter = {
    default = {
      access_levels = concat([
        var.access_level_office_and_vpn_ips.id,
        # GitHub Actions用のAccess Levelが含まれているか?
        var.access_level_service_accounts["main_project"].id,
        var.access_level_service_accounts["infrastructure_repo"].id,
      ])
      resources = [
        "projects/${var.google_projects["main-project-${var.env}"].number}",
        "projects/${var.google_projects["infrastructure-repo-${var.env}"].number}",
      ]
    }
  }
}

ステップ4: リソースとプロジェクトの対応確認

エラーログのresourceNameとService Perimeterの保護対象が一致しているかを確認:

1
2
3
4
5
# プロジェクト番号の確認
gcloud projects describe PROJECT_ID --format="value(projectNumber)"

# Service Perimeterの保護対象確認  
gcloud access-context-manager perimeters describe [PERIMETER_NAME] --policy=[POLICY_ID]

よくある設定ミスと解決法

1. Access Levelは定義されているがService Perimeterに含まれていない

症状: Access Level自体は作成されているが、アクセスが拒否される

原因: Service Perimeterのaccess_levelsリストにAccess Levelが含まれていない

解決法:

1
2
3
4
5
# Service Perimeterにaccess_levelを追加
access_levels = concat([
  var.access_level_office_and_vpn_ips.id,
+ var.access_level_service_accounts["infrastructure_repo"].id,
])

2. モジュール間の変数連携エラー

症状: 「Reference to undeclared input variable」エラー

原因:

  • access_levelモジュールでoutputが定義されていない
  • service_perimeterモジュールでvariableが宣言されていない
  • メインファイルでの変数受け渡しが漏れている

解決法: Terraformモジュール設計のベストプラクティスに従った変数連携の実装

3. プロジェクト番号とプロジェクトIDの混同

症状: 設定は正しく見えるがアクセスが拒否される

原因: VPC-SCはプロジェクト番号で動作するが、設定でプロジェクトIDを使用している

解決法:

1
2
3
4
5
# Good: プロジェクト番号を使用
resources = ["projects/${var.google_projects["infrastructure-repo-${var.env}"].number}"]

# Bad: プロジェクトIDを使用  
resources = ["projects/infrastructure-repo-dev"]

効果的なデバッグワークフロー

1. 段階的な権限追加

最初からすべての権限を設定せず、エラーログを見ながら段階的に追加:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# 最初は最小限の権限から開始
operations = {
  "storage.googleapis.com" = {
    methods = [
      "google.storage.objects.get",  # まずはこれだけ
    ]
  }
}

# エラーログを確認して必要な権限を追加
operations = {
  "storage.googleapis.com" = {
    methods = [
      "google.storage.objects.get",
      "google.storage.objects.list",    # 追加
      "google.storage.objects.create",  # 追加
    ]
  }
}

2. terraform planでの事前検証

1
2
3
4
5
6
7
# モジュールごとの検証
terraform plan -target=module.access_level
terraform plan -target=module.service_perimeter  
terraform plan -target=module.ingress_policy

# 全体の検証
terraform plan

3. リアルタイムログ監視

1
2
3
# Cloud Loggingでリアルタイム監視
gcloud logging tail "protoPayload.serviceName=vpcservicecontrols.googleapis.com" \
  --filter="severity=ERROR"

成功事例:実環境での解決プロセス

実際のプロジェクトでの解決プロセスを時系列で紹介:

1. 問題発生(Day 1)

  • GitHub Actions実行時に403エラー発生
  • すべてのTerraform operationが実行不可

2. 原因調査(Day 1-2)

  • Cloud Loggingでエラーの詳細を分析
  • 他環境との設定差分を特定
  • Access LevelがService Perimeterに含まれていないことを発見

3. 修正実装(Day 2-3)

  • 対象modulesにaccess_level_service_accountsのサポートを追加
  • Service PerimeterにAccess Levelを追加
  • モジュール間の変数連携を修正

4. 検証完了(Day 3)

  • terraform plan/apply が正常実行
  • VPC-SC違反ログが完全消滅
  • 全てのGitHub Actionsワークフローが正常化

まとめ

VPC Service Controls Enforced Mode移行時のトラブルシューティングでは、以下のアプローチが効果的です:

  1. Cloud Loggingによる詳細分析: エラーの根本原因を特定
  2. 段階的な設定確認: Access Level → Service Perimeter → リソース対応の順序で確認
  3. 体系的なデバッグ: モジュールごとの個別検証から全体検証へ
  4. エラーベースの権限追加: 必要最小限から段階的に権限を拡張

VPC-SCの設定は複雑ですが、体系的なアプローチを取ることで、効率的に問題を解決できます。特に、Cloud Loggingの活用は問題の早期発見と解決に不可欠です。

今後VPC-SC移行を予定している組織は、事前にこれらのトラブルシューティング手法を把握しておくことで、スムーズな移行を実現できるでしょう。

技術ネタ、趣味や備忘録などを書いているブログです
Hugo で構築されています。
テーマ StackJimmy によって設計されています。