> For the complete documentation index, see [llms.txt](https://docs.heeler.com/mrecEO40m5D6bt7Pq5pE/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.heeler.com/mrecEO40m5D6bt7Pq5pE/get-started/cloud-and-runtime/google-cloud-platform/gcp-organization-setup.md).

# GCP Organization Setup

Onboard a GCP organization to Heeler with Workload Identity Federation and a read-only collector service account.

Set up a dedicated **`heeler-security`** project holding one **`heeler-collector`** service account with org-wide read access, then federate Heeler's identity to it with **Workload Identity Federation (WIF)**. You can do this with `gcloud` (below) or the Terraform **`gcp-wif`** module.

{% hint style="info" %}
Linking a project to billing needs `roles/billing.user` on the billing account (org-admin roles don't include it). Several APIs below are billable and won't activate without it. If you can't get that role, link an existing already-billed project instead of creating a new one. You can confirm billing at [console.cloud.google.com/billing](https://console.cloud.google.com/billing) under the **Projects** tab.
{% endhint %}

## Manual setup with `gcloud`

Before running the commands, open **Connections → Cloud Organizations → Add Organization**, choose **Google Cloud Platform**, and copy the environment-specific values shown on the **Setup** step. Set them in your shell exactly as Heeler displays them:

```bash
export HEELER_AWS_IAM_ROLE="arn:aws:iam::<account-id>:role/<role-name>"
export HEELER_AWS_ACCOUNT_ID="<account-id>"
export HEELER_AWS_ROLE_NAME="${HEELER_AWS_IAM_ROLE##*/}"
```

{% hint style="warning" %}
Do not reuse AWS values from another Heeler environment. `HEELER_AWS_IAM_ROLE` must be the IAM role ARN displayed in your Heeler setup screen (`arn:aws:iam::...:role/...`), not an STS session ARN. The manual provider condition below derives the matching STS assumed-role prefix because that is the identity GCP observes during federation.
{% endhint %}

{% stepper %}
{% step %}

### Create the project and link billing

```bash
gcloud projects create heeler-security
gcloud config set project heeler-security
gcloud billing accounts list
gcloud billing projects link heeler-security --billing-account=XXXXXX-XXXXXX-XXXXXX
```

{% endstep %}

{% step %}

### Enable the APIs

Enable the 11-API baseline used by both Heeler Terraform artifacts. This covers WIF, preflight, API visibility checks, and core inventory collection for both project and organization connections:

```bash
gcloud services enable artifactregistry.googleapis.com \
  cloudresourcemanager.googleapis.com compute.googleapis.com \
  container.googleapis.com iam.googleapis.com iamcredentials.googleapis.com \
  pubsub.googleapis.com serviceusage.googleapis.com sqladmin.googleapis.com \
  storage.googleapis.com sts.googleapis.com
```

For complete inventory coverage across every currently supported service, also enable the extended API set. These APIs are not enabled by either Terraform artifact:

```bash
gcloud services enable aiplatform.googleapis.com baremetalsolution.googleapis.com \
  bigquery.googleapis.com \
  cloudfunctions.googleapis.com cloudkms.googleapis.com dns.googleapis.com \
  eventarc.googleapis.com firestore.googleapis.com logging.googleapis.com \
  memcache.googleapis.com monitoring.googleapis.com orgpolicy.googleapis.com \
  redis.googleapis.com run.googleapis.com secretmanager.googleapis.com \
  securitycenter.googleapis.com vpcaccess.googleapis.com
```

{% endstep %}

{% step %}

### Create the collector service account

```bash
gcloud iam service-accounts create heeler-collector \
  --description="Service account used to collect inventory across GCP" \
  --display-name="Heeler Security Collector"
```

{% endstep %}

{% step %}

### Grant org-level read roles

Grant these to `heeler-collector@heeler-security.iam.gserviceaccount.com` at the organization (`gcloud organizations list` for the ID):

<table><thead><tr><th width="340">Role</th><th>Why</th></tr></thead><tbody><tr><td><code>roles/iam.securityReviewer</code></td><td>Broad read across every harvested service + IAM policy visibility.</td></tr><tr><td><code>roles/artifactregistry.reader</code></td><td>Pull images/packages from Artifact Registry for workload analysis. <strong>Optional</strong> — see below.</td></tr><tr><td><code>roles/resourcemanager.organizationViewer</code></td><td>Enumerate the organization.</td></tr><tr><td><code>roles/resourcemanager.folderViewer</code></td><td>Enumerate folders.</td></tr><tr><td><code>roles/iam.serviceAccountTokenCreator</code></td><td>Token creation for impersonation.</td></tr></tbody></table>

{% hint style="info" %}
`roles/artifactregistry.reader` is the only grant that goes beyond metadata reads, and it exists solely for **workload analysis**: pulling images and packages from Artifact Registry so Heeler can fingerprint what your workloads run (`securityReviewer` alone can list repositories but not pull from them — analysis fails with `DENIED: Permission 'artifactregistry.repositories.downloadArtifacts' denied`). If nothing in the organization is deployed from Artifact Registry, you can omit it — Heeler skips those scans and everything else works. GCP workload analysis needs no host-execution permission: Heeler never executes anything on Compute Engine instances.
{% endhint %}

{% hint style="info" %}
**GKE with DNS endpoints?** Also grant `roles/container.viewer` (adds `container.clusters.connect`). For private endpoints with authorized networks, add Heeler's IPs `44.221.229.40` and `52.73.231.96` to the cluster's authorized networks.
{% endhint %}
{% endstep %}

{% step %}

### Create the WIF pool, provider and binding

Create the pool and an AWS provider locked to the environment-specific Heeler assumed-role identity, then let that federated AWS identity impersonate the collector:

```bash
gcloud iam workload-identity-pools create heeler-aws-access --location="global" \
  --display-name="Heeler AWS"

gcloud iam workload-identity-pools providers create-aws heeler \
  --account-id="${HEELER_AWS_ACCOUNT_ID}" --location="global" \
  --workload-identity-pool="heeler-aws-access" \
  --attribute-mapping="google.subject=assertion.arn,attribute.aws_account=assertion.account,attribute.arn=assertion.arn" \
  --attribute-condition="assertion.arn.startsWith('arn:aws:sts::${HEELER_AWS_ACCOUNT_ID}:assumed-role/${HEELER_AWS_ROLE_NAME}/')"

PROJECT_NUMBER=$(gcloud projects describe heeler-security --format="value(projectNumber)")
gcloud iam service-accounts add-iam-policy-binding \
  heeler-collector@heeler-security.iam.gserviceaccount.com \
  --member="principalSet://iam.googleapis.com/projects/${PROJECT_NUMBER}/locations/global/workloadIdentityPools/heeler-aws-access/attribute.aws_account/${HEELER_AWS_ACCOUNT_ID}" \
  --role=roles/iam.workloadIdentityUser
```

{% endstep %}

{% step %}

### Generate the Workload Identity Configuration

```bash
gcloud iam workload-identity-pools create-cred-config \
  projects/${PROJECT_NUMBER}/locations/global/workloadIdentityPools/heeler-aws-access/providers/heeler \
  --service-account=heeler-collector@heeler-security.iam.gserviceaccount.com \
  --aws --output-file=heeler-wif-config.json
```

Paste the full contents of `heeler-wif-config.json` into Heeler next.
{% endstep %}
{% endstepper %}

## Configure the connection in Heeler

Open **Connections → Cloud Organizations** (or the settings icon at the top right → **Connections**), click **Add Organization**, and choose **Google Cloud Platform**.

<figure><img src="https://414480750-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXP3dp2kecwKA2KvYkntz%2Fuploads%2FalH3gxWFd0hdmmgB3iDC%2FConnections%20Cloud%20Organizations%20Add%20Organization%20gimp.png?alt=media&amp;token=e5887474-22d8-4899-b2d3-1d17b2ad7af4" alt="The Cloud Organizations list in Heeler with the Add Organization button."><figcaption><p>Start from Connections → Cloud Organizations → Add Organization.</p></figcaption></figure>

Then enter:

<figure><img src="https://414480750-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXP3dp2kecwKA2KvYkntz%2Fuploads%2Fgit-blob-bb3a800565efc8f76015713f4ab21f50af9dac52%2Fcc-gs-cloud-gcp-form.png?alt=media" alt="The Add GCP Organization wizard showing the Workload Identity Configuration, Project ID, and optional Folder/Project ID fields."><figcaption><p>Paste the Workload Identity Configuration JSON and your project details.</p></figcaption></figure>

<table><thead><tr><th width="280">Field</th><th>Value</th></tr></thead><tbody><tr><td><strong>Organization Name</strong></td><td>A friendly name (e.g. <code>Acme Corp</code>).</td></tr><tr><td><strong>Workload Identity Configuration</strong></td><td>The full JSON from the previous step (or <code>terraform output -raw workload_identity_config</code>).</td></tr><tr><td><strong>Project ID</strong></td><td>The dedicated project, e.g. <code>heeler-security</code>.</td></tr><tr><td><strong>Folder IDs</strong> (optional)</td><td><em>Included</em> folders — only projects within them are harvested. Blank = everything visible.</td></tr><tr><td><strong>Project IDs</strong> (optional)</td><td><em>Excluded</em> projects — skipped even if under an included folder.</td></tr></tbody></table>

Click **Next**. Heeler runs a **seven-step preflight** — loading the WIF config, federating to the service account, validating the Project ID, listing the organization, listing folders and projects, sampling a Compute Engine harvest, and checking IAM on a sampled project — each reporting inline. On success, click **Create connection**.

<figure><img src="https://414480750-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXP3dp2kecwKA2KvYkntz%2Fuploads%2Fgit-blob-aef845a712ab1dbd5fbbef4400a900b57ad5e616%2Fcc-cloud-gcporg-03.png?alt=media" alt="The GCP connection validation step showing the seven-row preflight checklist running and reporting results."><figcaption><p>The seven preflight checks run and report inline before the connection saves.</p></figcaption></figure>

{% hint style="warning" %}
A failed check names the step and surfaces the API error. Common causes: IAM-role propagation delay (wait 1–5 min and retry), a missing role binding (re-check the grants), or an API not enabled. The preflight samples a random project for the API-enablement check, so a project missing that API can trip it. Click **Previous** to return to the Details step, fix the issue, and submit again to re-run the preflight.
{% endhint %}

<figure><img src="https://414480750-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXP3dp2kecwKA2KvYkntz%2Fuploads%2FZzYc6e8Dy0s0qfJMS4zW%2FGCP%20Organization%20successfully%20added%20gimp.png?alt=media&amp;token=1b328600-3840-4828-9db1-b2452f949554" alt="Heeler confirming the GCP organization was added successfully."><figcaption><p>The organization is connected. Inventory collection starts in the background.</p></figcaption></figure>

Inventory collection begins in the background; the first round can take some time depending on the size of the GCP footprint.

{% hint style="info" %}
**Next step: Event Collection (optional).** Adding the organization enables inventory harvesting only. Near-real-time event collection is a separate, optional setup — until you complete it, the organization's **Event Collection** column shows `DISABLED`. See [GCP Event Collection](/mrecEO40m5D6bt7Pq5pE/get-started/cloud-and-runtime/google-cloud-platform/gcp-event-collection.md).
{% endhint %}

## Terraform

Use the public **`gcp-wif` 2.0.0 module** to configure organization-scoped Workload Identity Federation in either an existing project or a new project. For a single project connection, use [Connect a single project instead](#connect-a-single-project-instead); this module grants organization-level access.

<a href="https://registry.terraform.io/modules/Heeler-Security/heeler-wif/google/2.0.0" class="button primary">Open in Terraform Registry</a>

<a href="https://github.com/Heeler-Security/terraform-google-heeler-wif/tree/2.0.0" class="button secondary" data-icon="github">View module files</a>

{% stepper %}
{% step %}

### Copy the Heeler environment values

In Heeler, open **Connections → Cloud Organizations → Add Organization → Google Cloud Platform → Setup with Terraform**.

Copy these values exactly as displayed:

| Terraform input         | Requirement                                                 |
| ----------------------- | ----------------------------------------------------------- |
| `heeler_aws_iam_role`   | Must begin with `arn:aws:iam::`, not `arn:aws:sts::`.       |
| `heeler_aws_account_id` | Must come from the same Heeler environment as the IAM role. |

Do not reuse values from another Heeler environment.
{% endstep %}

{% step %}

### Choose an existing or new GCP project

Create a Terraform configuration with one of these module blocks.

{% tabs %}
{% tab title="Use an existing project (recommended)" %}
The project must already exist, belong to the organization you want to connect, and have billing enabled. Terraform configures Heeler's WIF and IAM resources without managing the project's name, placement, billing association, or deletion lifecycle.

```hcl
module "heeler" {
  source  = "Heeler-Security/heeler-wif/google"
  version = "2.0.0"

  project_id            = "<EXISTING_PROJECT_ID>"
  heeler_aws_iam_role   = "<VALUE_FROM_HEELER>"
  heeler_aws_account_id = "<VALUE_FROM_HEELER>"
}
```

{% endtab %}

{% tab title="Create a new project" %}
Project creation is opt-in. Supply the organization and billing account explicitly:

```hcl
module "heeler" {
  source  = "Heeler-Security/heeler-wif/google"
  version = "2.0.0"

  project_id            = "<NEW_PROJECT_ID>"
  heeler_aws_iam_role   = "<VALUE_FROM_HEELER>"
  heeler_aws_account_id = "<VALUE_FROM_HEELER>"

  create_project  = true
  project_name    = "Heeler Security"
  org_id          = "<ORGANIZATION_ID>"
  billing_account = "<BILLING_ACCOUNT_ID>"
}
```

The identity running Terraform needs permission to create the project and `roles/billing.user` on the selected billing account.
{% endtab %}
{% endtabs %}
{% endstep %}

{% step %}

### Initialize and review the plan

```bash
terraform init
terraform validate
terraform plan
```

For an existing project, confirm the plan contains **no `google_project` resource** and no changes to its name, organization placement, billing association, or deletion lifecycle.

For a new project, confirm the plan creates only the project you named and the expected Heeler WIF, service account, API, and IAM resources.
{% endstep %}

{% step %}

### Apply and confirm there is no drift

Apply the reviewed plan:

```bash
terraform apply
```

After it completes, run a second plan:

```bash
terraform plan -detailed-exitcode
```

Exit code `0` means the configuration is clean. Exit code `1` means Terraform encountered an error. Exit code `2` means Terraform still proposes changes; review and resolve them before continuing.
{% endstep %}

{% step %}

### Copy the Workload Identity Configuration

```bash
terraform output -raw workload_identity_config
```

Treat the output as connection configuration and do not commit it to source control. In Heeler's **Details** step, paste it into **Workload Identity Configuration** and enter the same `project_id` used by Terraform. Add any included **Folder IDs** or excluded **Project IDs** for the organization scope.
{% endstep %}

{% step %}

### Validate and create the connection

Click **Next**. Heeler validates federation, the project, organization and folder visibility, a sample inventory call, and IAM access. Resolve any failed check before clicking **Create connection**.
{% endstep %}
{% endstepper %}

{% hint style="info" %}
The module enables the [11-API baseline](#enable-the-apis). Enable the extended API set from that section separately when you want inventory from the corresponding services.
{% endhint %}

{% hint style="warning" %}
`terraform destroy` removes the module's WIF, service-account, and IAM resources, but it does not disable APIs. A project created with `create_project = true` uses `deletion_policy = "ABANDON"`, so destroy removes it from Terraform state without deleting the real project.
{% endhint %}

<details>

<summary>Version compatibility and upgrades</summary>

| Component                 | Supported range      | CI endpoints     |
| ------------------------- | -------------------- | ---------------- |
| Terraform                 | `>= 1.7.5, < 2.0.0`  | 1.7.5 and 1.16.1 |
| HashiCorp Google provider | `>= 6.22.0, < 9.0.0` | 6.22.0 and 8.1.0 |

Before upgrading within those ranges, run `terraform init -upgrade` and review `terraform plan`. Wait for a published compatibility update before adopting Google provider 9.x or Terraform 2.x.

Upgrading from module 1.0.2 changes project ownership behavior. Follow the module's [upgrade instructions](https://github.com/Heeler-Security/terraform-google-heeler-wif/tree/2.0.0#upgrade-from-102) before applying.

</details>

## Connect a single project instead

You can onboard one or more individual **projects** instead of a whole organization — useful for a proof-of-concept or a third-party project you want visibility into. The flow mirrors the org setup with three differences:

* **Grant the read roles at the project level**, scoped to each project you're onboarding, rather than at the organization. Run the same roles from the [Grant org-level read roles](#grant-org-level-read-roles) step against the target project:

```bash
gcloud projects add-iam-policy-binding TARGET_PROJECT_ID \
    --member=serviceAccount:heeler-collector@heeler-security.iam.gserviceaccount.com \
    --role=roles/iam.securityReviewer
# repeat for roles/artifactregistry.reader, roles/resourcemanager.folderViewer,
# roles/resourcemanager.organizationViewer, and roles/iam.serviceAccountTokenCreator
```

* **Use the same 11-API baseline.** Heeler harvests each project independently in both connection modes, so project and organization connections share the baseline above. Enable extended APIs only for the additional resource types you want harvested. Follow the manual `gcloud` steps for project-scoped IAM; the Terraform artifacts above grant organization-level access.
* **Configure it under Cloud Accounts.** Go to **Connections → Cloud Accounts → Add Cloud Account → Google Cloud Platform** and enter a **Project Name**, the **Workload Identity Configuration** JSON, and the **Project ID**, then click **Save**.

<figure><img src="https://414480750-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXP3dp2kecwKA2KvYkntz%2Fuploads%2Fgit-blob-e2db712c7ecddf7f719958e91414b8c6211b8106%2Fcc-cloud-gcpsingle-01.png?alt=media" alt="The Add GCP Cloud Account dialog in Heeler — Setup step — showing Terraform and manual setup choices. Use the manual path for project-scoped IAM."><figcaption><p>Adding a single GCP project under Connections → Cloud Accounts. Although the Setup step displays Terraform inputs, the current Terraform artifacts grant organization-level access; use the manual path for project-scoped onboarding.</p></figcaption></figure>

On success, inventory collection begins in the background.

## Troubleshooting

Skip this if the preflight passes — the commands below are diagnostic, for isolating which part of the setup failed.

### Isolating an org-level IAM failure

If the preflight's organization-listing step fails, you can confirm whether the problem is the service account's org-level IAM grant (independently of the WIF path) by impersonating the collector from your workstation:

```bash
gcloud organizations list \
    --impersonate-service-account=heeler-collector@heeler-security.iam.gserviceaccount.com
```

This does **not** exercise WIF — it uses your local gcloud identity to mint the SA token, bypassing federation — so it only tests org-level IAM. It also requires that *you* hold `roles/iam.serviceAccountTokenCreator` on the service account (`roles/owner` does not include this). If needed, grant it:

```bash
gcloud iam service-accounts add-iam-policy-binding \
    heeler-collector@heeler-security.iam.gserviceaccount.com \
    --member=user:$(gcloud config get-value account) \
    --role=roles/iam.serviceAccountTokenCreator
```

If the impersonated command then returns your organization, the SA's `roles/resourcemanager.organizationViewer` grant is working and the failure is somewhere in the WIF path (provider attribute condition, `principalSet` binding, or the AWS-side role). If it still returns "permission denied," re-check the org-level role grants.

## Related

* [GCP Event Collection](/mrecEO40m5D6bt7Pq5pE/get-started/cloud-and-runtime/google-cloud-platform/gcp-event-collection.md) — add near-real-time change detection.
* [Google Cloud Platform](/mrecEO40m5D6bt7Pq5pE/get-started/cloud-and-runtime/google-cloud-platform.md) — WIF overview and setup options.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.heeler.com/mrecEO40m5D6bt7Pq5pE/get-started/cloud-and-runtime/google-cloud-platform/gcp-organization-setup.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
