The scanner runs in a host project you choose, as Cloud Run Jobs with a keyless service account. Cloud Scheduler starts the coordinator job every 15 minutes, and the coordinator starts the worker job with as many parallel tasks as the backlog needs, up to max_workers. The scanner reads Cloud Storage, BigQuery, Cloud SQL, AlloyDB, Filestore, IAM policies, Vertex AI inventory and Data Access audit logs of the projects in scope. Only findings, classifications, counts, identifiers and run status leave.

Before you start

  • Terraform 1.11 or later and hashicorp/google 7.x or 8.x.
  • In the host project, the identity applying the module must be able to create service accounts, custom roles, Pub/Sub, Firestore, Secret Manager, Cloud Run and Cloud Scheduler resources, and enable APIs.
  • On every scanned project, folder or organization, it must be able to set IAM policy (resourcemanager.projects.setIamPolicy or the folder and organization equivalents).
  • An organization API key (tg_). See Before you deploy.

Deploy with Terraform

1

Start the wizard

In TigerGate, go to DSPM → Deployments → Add deployment and choose Google Cloud project. Review the roles the module grants.
2

Enter the project and network

Enter the name, the Project id (the host project), the Region the scanner runs in and the TigerGate region. To reach private databases, enter a Serverless VPC connector. Choose the profile, schedule, max workers and whether to share masked samples, then select Create deployment and enrollment token.
3

Copy the token and the Terraform

Copy the enrollment token (shown once), choose the organization API key, and copy main.tf and terraform.tfvars. Add more projects to scan_project_ids, or use folders or the whole organization (see below).
4

Apply

gcloud config get-value project # the project you are about to deploy into
terraform init
terraform plan
terraform apply
5

Register

The coordinator registers within 15 minutes. To start it now, run the command that terraform output -raw run_now_command prints (gcloud run jobs execute ...).
provider "google" {
  project = "security-tools-123"
}

module "tigergate_dspm" {
  source = "https://releases.tigergate.dev/dspm/<version>/tigergate-dspm-gcp.zip"

  tigergate_api_url = "https://api.tigergate.dev"
  tigergate_api_key = var.tigergate_api_key # tg_..., your organization's API key
  enrollment_token  = var.enrollment_token  # tgdspm_..., from the console
  project_id        = "security-tools-123"
  region            = "us-central1"
  scan_project_ids  = ["security-tools-123", "data-prod-456"]
}
The module writes the token and the key to Secret Manager as write-only values. To keep the key out of Terraform, pass an existing secret as tigergate_api_key_secret_id (projects/<project>/secrets/<name>); the module grants the scanner secretAccessor on it. The token takes enrollment_token_secret_id the same way.

What the module creates

ResourcePurpose
Service account <prefix>-scannerThe identity of both jobs. It has no key.
Service account <prefix>-triggerLets Cloud Scheduler start the coordinator job. No other access.
Custom role <prefix>_discoveryExactly the metadata reads the scanner makes.
Pub/Sub topics and subscriptions <prefix>-shards and <prefix>-shards-deadThe work queue and its dead letters.
Firestore database <prefix>-stateIncremental scan state, with a time-to-live policy.
Secret Manager secrets<prefix>-enrollment-token, <prefix>-api-key and <prefix>-scanner-credential.
Cloud Run jobs <prefix>-coordinator and <prefix>-workerThe coordinator, and the workers it starts.
Cloud Scheduler jobRuns the coordinator job every 15 minutes (schedule_cron).

Permissions

RoleWhy
Custom discovery role (or roles/viewer with discovery_role = "viewer")Inventory, posture and lineage, with no object or row access.
roles/storage.objectViewerRead objects. Granted per bucket when content_bucket_names is set, otherwise on the scanned scopes.
roles/bigquery.dataViewer, plus roles/bigquery.jobUser on the host projectRead table data. Query jobs are billed to the host project.
roles/cloudsql.client, roles/cloudsql.instanceUserConnect to Cloud SQL with IAM database authentication.
roles/spanner.databaseReader, roles/datastore.viewer, roles/bigtable.readerGranted, but Spanner, Firestore and Bigtable rows are not read yet: those stores get inventory and posture only.
roles/iam.securityReviewerRead IAM policies, so findings show who can reach each store.
roles/logging.privateLogViewerRead Data Access audit logs.
roles/aiplatform.viewerVertex AI inventory.
On its own resources the scanner gets Pub/Sub publisher and subscriber, roles/datastore.user on the state database only, Secret Manager access to its three secrets, permission to start the worker job, and roles/monitoring.viewer on the host project to read the queue backlog. Nothing grants write access to your data. Databases: the scanner signs in to Cloud SQL and AlloyDB for PostgreSQL as the service account’s IAM database user. Create that user in each database with read-only grants. Other engines need a Secret Manager secret holding {"username": "...", "password": "..."}, named on the deployment’s Configuration tab under Database access.

Projects, folders and organizations

  • scan_project_ids lists projects. Empty means the host project.
  • scan_folder_ids, with organization_id, covers every project below the folders, including new ones.
  • scan_whole_organization = true, with organization_id, covers the organization.
One deployment scans every location of every project in scope from its region.

Networking

  • Default: Cloud Run’s managed egress to the internet.
  • Private databases: set vpc_network and vpc_subnetwork for Direct VPC egress, or vpc_connector for an existing Serverless VPC Access connector. vpc_egress = "ALL_TRAFFIC" sends everything through the VPC, for Cloud NAT or an egress firewall. Allow the TigerGate API host and the Google APIs.
  • VPC Service Controls: a perimeter around the scanned projects must include the host project, or allow the scanner service account with an ingress rule. Otherwise reads fail with access_denied.
  • No internet egress: copy the image to Artifact Registry and set scanner_image.

Edge cases

  • APIs: calls made by the scanner’s service account are attributed to the host project, so the APIs it reads through must be enabled there. enable_apis = true (the default) does that. With enable_apis = false, a missing API shows as a preflight failure that names it.
  • Firestore location: a Firestore database’s location cannot change. Set firestore_location before the first apply if region is not the location you want.
  • Custom roles blocked: if your organization blocks a permission in custom roles, set discovery_role = "viewer".
  • CMEK: Cloud Storage and BigQuery decrypt with their own service agents, so the scanner needs no Cloud KMS role. A disabled or destroyed key makes the data unreadable, and the store reports kms_denied.
  • IAM deny policies that cover the scanner’s service account override the grants. The store reports access_denied, never clean.
  • Requester Pays buckets of another project show as access_denied.
  • Archive storage class objects are readable, with retrieval charges billed to the bucket’s project.

Cost

Cloud Run Jobs bill per vCPU-second and GiB-second while tasks run. Cloud Scheduler uses one job, within the free tier. Pub/Sub, Firestore and Secret Manager cost cents a month when idle. Reads are billed on the scanned projects: Cloud Storage operations, BigQuery bytes scanned (on the host project) and cross-region egress.

Operate the deployment

  • Rotate the API key: add a version to the secret in the api_key_secret_id output, or apply a new tigergate_api_key with tigergate_api_key_version raised by one. No redeploy is needed.
  • Logs: run the command that terraform output -raw logs_command prints.
  • Max workers: on Google Cloud the coordinator starts at most the console’s Max workers tasks, within the module’s max_workers.
  • Remove: select Revoke on the deployment, then terraform destroy.