GCP Serverless Scanner
Deploy Mondoo's serverless scanner into your own GCP project with Terraform to continuously scan an organization or folder using Cloud Run, including options for CMEK, air-gapped installs, and restricted org policies.
The GCP serverless scanner runs entirely inside your GCP project. A Cloud Run service (the orchestrator) discovers the projects in a configured organization or folder on a schedule, then fans out one Cloud Run Job execution per project to run cnspec and ship results to Mondoo. Nothing scans from Mondoo's side, and no long-lived service account key is stored when you use Workload Identity Federation.
Looking for the platform-managed option instead? The GCP integration has Mondoo scan your projects for you. The serverless scanner is the self-hosted alternative: you deploy and own the scanning infrastructure with Terraform, which suits large organizations and restricted environments where scanning must originate from inside the tenancy.
What gets deployed
A single terraform apply of the serverless-scanner-gcp module
(deploy/terraform) creates three Cloud Run resources plus the Pub/Sub, Cloud
Storage, IAM, and (optionally) Cloud Scheduler scaffolding that connects them:
- Orchestrator, a Cloud Run service that receives schedule ticks, enumerates the projects in scope, and dispatches one scan task per project.
- Scanner, a Cloud Run Job. The orchestrator launches one execution per project; each execution runs cnspec against that project. The Job uses a Google Cloud SDK base image that downloads a per-execution scan script from the orchestrator bucket and runs it, so there is no cnspec image to pull.
- Updater, a Cloud Run Job that polls Mondoo's registry for a newer orchestrator image and rolls the service to it, so you do not have to re-apply after each release.
Prerequisites
- A dedicated GCP host project (separate from the projects you scan) with these
APIs enabled:
run,pubsub,iam,cloudresourcemanager,logging,secretmanager,storage, andmonitoring. Addcloudscheduleronly if you keep the built-in scheduler (seeenable_scheduler). The module verifies the APIs read-only and stops the plan if one is missing; it never enables them for you. - A Mondoo GCP serverless integration created in the console, which gives you an integration MRN and a credential (a Workload Identity Federation config or a service account JSON).
- An Artifact Registry repository the module's images are served from.
- The runtime service account's read access to the organization or folder you
scan. The module can grant this, or you can grant it out-of-band (see
manage_scope_iam).
Usage
module "serverless_scanner" {
source = "github.com/mondoohq/serverless-scanner-gcp//deploy/terraform"
project_id = "my-scanner-host"
region = "europe-west3"
image = "europe-west3-docker.pkg.dev/my-scanner-host/mondoo/orchestrator:prod"
# What to scan and where results go.
scope = "organizations/123456789012" # or folders/<id>
mondoo_integration_mrn = "//integrations.api.mondoo.com/spaces/<space>/integrations/<id>"
mondoo_config_base64 = filebase64("mondoo.json") # WIF config or SA JSON
}Configuration reference
Core inputs
| Input | Description |
|---|---|
project_id | The GCP host project the scanner runs in. |
region | Region for the Cloud Run service, Jobs, and Pub/Sub. |
image | Orchestrator (and updater) container image URI. |
scope | The org or folder to discover and scan, as organizations/<id> or folders/<id>. |
mondoo_integration_mrn | The integration MRN results report against. |
mondoo_config_base64 | Base64-encoded Mondoo credential (WIF config preferred, or a service account JSON). |
service_account_email | An existing runtime service account to use. Empty makes the module create one. |
name_prefix | Prefix for resource names, so one project can host multiple deployments. |
Schedules
The orchestrator is driven by four triggers, each a cron expression interpreted
in UTC: scan_schedule (the scan pass, every 12 hours by default),
metrics_schedule (telemetry), sync_schedule (backend-config sync), and
checkin_schedule (a liveness ping, every 10 minutes by default).
auto_update_schedule controls the updater (hourly by default).
Options for restricted environments
The following inputs exist so the module can deploy into sovereign, Assured Workloads, or otherwise policy-restricted projects. Each defaults to the unrestricted behavior, so leaving them unset changes nothing.
| Input | Default | Purpose |
|---|---|---|
kms_key_name | "" | Customer-managed encryption for all data-plane resources. |
installer_url | "" | Internal mirror for cnspec and provider downloads. |
scanner_image | Google Cloud SDK | Override the scanner Job base image. |
enable_scheduler | true | Create the Cloud Scheduler jobs, or drive triggers externally. |
manage_scope_iam | true | Grant the runtime SA its org/folder scan roles, or do it out-of-band. |
manage_project_iam | true | Grant the runtime SA its host-project roles, or do it out-of-band. |
Restricted and sovereign environments
Locked-down GCP organizations (sovereign clouds, Assured Workloads) enforce organization policies that reject the module's default resources. The options below make each requirement satisfiable.
Customer-managed encryption (CMEK)
Where constraints/gcp.restrictNonCmekServices is enforced, resources created
without a customer-managed key are rejected. Set kms_key_name to a Cloud KMS
key and the module encrypts the orchestrator bucket, every Pub/Sub topic, the
Cloud Run service and Jobs, and the config secret with it. The secret switches to
regional (user_managed) replication in region, since automatic replication
cannot use a single regional key.
kms_key_name = "projects/<key-project>/locations/europe-west3/keyRings/<ring>/cryptoKeys/<key>"The module does not manage IAM on the key. The Cloud Storage, Pub/Sub, Cloud Run, and Secret
Manager service agents of project_id must hold roles/cloudkms.cryptoKeyEncrypterDecrypter on
the key before the apply, or resource creation fails with a KMS permission error. Grant this
out-of-band, since the key commonly lives in a separate, centrally-managed project. Also confirm
constraints/gcp.restrictCmekCryptoKeyProjects allows the key's project, and that the key's
location matches region.
Air-gapped scan workloads
When the scan workloads cannot reach the public internet, set installer_url to
an internal mirror. The scan then fetches the cnspec binary from
<installer_url>/package/... and its providers from <installer_url>/providers/...,
so the mirror must serve both path layouts under one base URL.
installer_url = "https://install.mirror.example.internal"If the host project also blocks Cloud Run from pulling the default gcr.io
scanner base image, mirror a Google Cloud SDK image into your registry and set
scanner_image to it.
Driving triggers without Cloud Scheduler
Where Cloud Scheduler is unavailable (for example, org policy blocks the App
Engine service family), set enable_scheduler = false. The module then creates
no scheduler jobs and does not require the cloudscheduler API. The scan-trigger
Pub/Sub topic still exists, so an external driver, such as a Kubernetes CronJob,
can publish the typed trigger payloads to it on your own schedule.
With enable_scheduler = false, nothing in the module is granted publish access on the trigger
topic. Grant your external publisher roles/pubsub.publisher on the topic (exposed as the
module's scan_trigger_topic output).
Delegating IAM when the deployer cannot set it
In delegated-admin setups, the identity running Terraform may not set folder, organization, or project IAM, even though it can create the resources themselves. Two flags move those grants out-of-band:
manage_scope_iam = false: the module does not grant the runtime SA its org/folder scan roles. Grant them yourself at the scope node.manage_project_iam = false: the module does not grant the runtime SA (or the Cloud Run service agent) their host-project roles. Grant them yourself in the host project.
When manage_project_iam = false, grant these in the host project out-of-band:
| Member | Roles |
|---|---|
| Runtime service account | roles/browser, roles/viewer, roles/iam.securityReviewer, roles/resourcemanager.tagViewer, roles/logging.viewer, roles/monitoring.viewer, roles/run.viewer, roles/run.developer, roles/artifactregistry.reader |
Cloud Run service agent (service-<project-number>@serverless-robot-prod.iam.gserviceaccount.com) | roles/artifactregistry.reader |
The service agent's roles/artifactregistry.reader is required for the first revision to pull its
image. If you set manage_project_iam = false and forget it, the orchestrator, scanner, and
updater fail to deploy with a generic image pull error.
Resource-level IAM (Pub/Sub publisher, Cloud Run invoker, Secret Manager accessor) is always managed by the module and is unaffected by these flags.
Scan Continuously
Configure the Mondoo GCP integration to continuously scan your Google Cloud projects and organizations using one-click Workload Identity Federation or a service account key.
Overview
Secure your Kubernetes clusters by scanning nodes, workloads, and images for misconfigurations and vulnerabilities.