Security Command Center Compliance Manager のカスタムクラウドコントロールを Terraform で作って検出してみた

Security Command Center Compliance Manager のカスタムクラウドコントロールを Terraform で作って検出してみた

2026.11.15

Compliance Manager のカスタムクラウドコントロールを試してみました。

Terraformを使ってコントロールを作成して、テスト用のリソースに対して検出までをやってみました。

カスタムクラウドコントロールとは

カスタムクラウドコントロールは、自分で検出ルールを書けるコントロールです。
ルールは CEL で書き、対象のリソースタイプと組み合わせます。
CEL には「準拠している」条件を書きます。
式が true なら準拠、false なら finding になります。
カスタムクラウドコントロールは、SCC の Premium ティアと Enterprise ティアでだけ使えます。

https://docs.cloud.google.com/security-command-center/docs/compliance-manager-manage-cloud-controls

検出に使うには、コントロールをフレームワークに追加し、そのフレームワークを組織・フォルダ・プロジェクトにデプロイします。
デプロイすると Compliance Manager が対象のリソースを評価し、違反があれば finding が出ます。

フレームワークは、複数のコントロールをまとめたものです。
Compliance Manager には Security Essentials などの組み込みフレームワークがあります。
組み込みのフレームワークは編集できないので、カスタムクラウドコントロールは自分で作ったカスタムフレームワークに入れます。

https://docs.cloud.google.com/security-command-center/docs/compliance-manager-overview

やってみた

Terraformでリソースを作ってみます。

コントロール、フレームワーク、デプロイは、それぞれ次のリソースで作ります。

  • google_cloud_security_compliance_cloud_control
  • google_cloud_security_compliance_framework
  • google_cloud_security_compliance_framework_deployment

検出対象は、オブジェクトのバージョニングが無効の Cloud Storage バケットにしました。

versions.tf
terraform {
  required_version = ">= 1.9"

  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "~> 8.10"
    }
  }
}

provider "google" {
  project               = var.project_id
  user_project_override = true
  billing_project       = var.project_id
}
variables.tf
variable "org_id" {
  type = string
}

variable "project_id" {
  type = string
}
terraform.tfvars
org_id     = "<ORGANIZATION_ID>"
project_id = "<PROJECT_ID>"
main.tf
resource "google_cloud_security_compliance_cloud_control" "bucket_versioning" {
  parent           = "organizations/${var.org_id}"
  location         = "global"
  cloud_control_id = "custom-bucket-versioning"

  display_name              = "Bucket versioning must be enabled"
  description               = "Cloud Storage buckets must have versioning enabled"
  categories                = ["CC_CATEGORY_INFRASTRUCTURE"]
  severity                  = "HIGH"
  finding_category          = "BUCKET_VERSIONING_DISABLED"
  remediation_steps         = "Enable object versioning on the bucket."
  supported_cloud_providers = ["GCP"]

  rules {
    description       = "Versioning must be enabled"
    rule_action_types = ["RULE_ACTION_TYPE_DETECTIVE"]

    cel_expression {
      expression = "has(resource.data.versioning.enabled) && resource.data.versioning.enabled == true"
      resource_types_values {
        values = ["storage.googleapis.com/Bucket"]
      }
    }
  }
}

resource "google_cloud_security_compliance_framework" "custom" {
  parent       = "organizations/${var.org_id}"
  location     = "global"
  framework_id = "custom-storage-baseline"

  display_name = "Custom Storage Baseline"
  description  = "Custom framework for Cloud Storage checks"

  cloud_control_details {
    name              = google_cloud_security_compliance_cloud_control.bucket_versioning.name
    major_revision_id = google_cloud_security_compliance_cloud_control.bucket_versioning.major_revision_id
  }
}

resource "google_cloud_security_compliance_framework_deployment" "custom" {
  parent                  = "organizations/${var.org_id}"
  location                = "global"
  framework_deployment_id = "custom-storage-baseline"

  framework {
    framework         = google_cloud_security_compliance_framework.custom.name
    major_revision_id = google_cloud_security_compliance_framework.custom.major_revision_id
  }

  target_resource_config {
    existing_target_resource = "projects/${var.project_id}"
  }

  cloud_control_metadata {
    enforcement_mode = "DETECTIVE"

    cloud_control_details {
      name              = google_cloud_security_compliance_cloud_control.bucket_versioning.name
      major_revision_id = google_cloud_security_compliance_cloud_control.bucket_versioning.major_revision_id
    }
  }
}

デプロイ先は組織・フォルダ・プロジェクトから選べます。今回は影響範囲を絞るため、1つのプロジェクトにしました。

CEL の has() は、バージョニングを一度も設定していないバケットも検出するために付けています。
このようなバケットは、Cloud Asset Inventory 上で versioning が {} になり、enabled キーがありません。
has() なしの resource.data.versioning.enabled == true で試したときは、このバケットは2日以上待っても finding になりませんでした。
評価エラーは表示されず、キーがなくて式を評価できなかったためと推測しています(エラー自体は確認できていません)。
has() を付けた式では、このバケットも finding になりました。

terraform applyで上記のリソースをデプロイして、検出結果を確認します。

動作確認: 検出できるか試してみる

コンソールから「Security Command Center」 -> 「コンプライアンス」 -> 「<今回作成したフレームワーク>」の順に選択するとコントロールの状態が表示されます。

Notification_Center.png

現時点ではコントロールのステータスが合格になっていることを確認できます。

検出できるか確認するために、バージョニングを設定しないバケット cm-test-ver-off を gcloud で作りました。

gcloud storage buckets create gs://<BUCKET_NAME> \
  --project=<PROJECT_ID> --location=asia-northeast1

今回はデプロイ後すぐに検出されました。

ちなみに、公式ドキュメントには、デプロイ後に finding が出るまで最大6時間かかるとあるため、すぐ出ない場合は時間をおいて確認する必要がありそうです。

After you deploy a framework, it can take up to 6 hours before findings related to detective cloud controls appear.

https://docs.cloud.google.com/security-command-center/docs/compliance-manager-overview

検出後の画面は次のとおりです。

Notification_Center.png

finding の詳細は以下です。

Notification_Center.png

無事に検出することができました。

削除

検証が終わったらterraform destroy でリソースを削除します。特にエラーは発生せず削除できました。

削除後も、既存の finding は ACTIVE のまま残っていました。
公式ドキュメントには、フレームワークを外すと、ほとんどの finding が7日後に Inactive になるとあります。

When you remove a framework, the state of most of the related findings changes to Inactive after seven days.

https://docs.cloud.google.com/security-command-center/docs/compliance-manager-apply-framework

おわりに

Terraformで、コントロールの作成からデプロイまで一通り試しました。
未設定のときにキーごと省略されるプロパティは、CEL の式で has() を付けないと検出できないことがありました。

カスタムルールを作る時は、Cloud Asset Inventory 上のデータの形を先に確認しておくと楽そうです。

この記事をシェアする

関連記事