> For the complete documentation index, see [llms.txt](https://docs.firefly.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.firefly.ai/detailed-guides/bulk-onboarding/google-cloud.md).

# Google Cloud

**Scope: CLI and API only.** This guide covers the `gcloud` CLI and the Firefly API for onboarding GCP projects without the console wizard. Firefly also ships a Terraform module for Google Cloud onboarding, documented separately.

## When to use this

Use this guide to bulk onboard GCP projects, drive onboarding from a CI/CD pipeline, or enable org-level discovery. For a single project, the console wizard (**Settings > Integrations > Add New > Google Cloud**) is faster.

## Prerequisites

* Admin access to the Firefly console
* `gcloud` CLI, authenticated
* GCP IAM permissions to create service accounts and grant roles
* Org-level IAM permissions if you want folder-tree discovery (optional)
* `curl` and `jq`

## Pre-flight: can you create service account keys?

Run this before committing to a timeline. This integration depends on a downloadable service account JSON key, and some organizations block key creation with an org policy.

```shell
gcloud resource-manager org-policies describe \
  constraints/iam.disableServiceAccountKeyCreation \
  --project="$PROJECT_ID" --effective
```

If the constraint is enforced, key creation will fail and no amount of IAM permission will fix it. Options, in order of preference:

1. **Request a scoped exception** for the Firefly project only. Most platform teams will grant this for a single, audited service account — a narrower ask than disabling the policy org-wide.
2. **Use a project that's out of scope** of the policy, if you have one designated for third-party integrations.
3. **Contact your Firefly representative** to confirm whether a keyless option (Workload Identity Federation) is currently available for this integration.

Two related constraints worth checking at the same time, since both cause confusing failures later:

```shell
gcloud resource-manager org-policies describe \
  constraints/iam.disableServiceAccountKeyUpload \
  --project="$PROJECT_ID" --effective

gcloud resource-manager org-policies describe \
  constraints/iam.allowedPolicyMemberDomains \
  --project="$PROJECT_ID" --effective
```

The second restricts which identities can be granted roles and is a common cause of "role binding rejected" errors that look like a permissions problem but aren't.

## Phase A — Authenticate to Firefly

Create the key pair under **Settings > Users > Create Key Pair**, then:

```shell
FIREFLY_API="https://prodapi.firefly.ai/api"

TOKEN=$(curl -sS -X POST "${FIREFLY_API}/account/access_keys/login" \
  -H "Content-Type: application/json" \
  -d "{\"accessKey\":\"${FIREFLY_ACCESS_KEY}\",\"secretKey\":\"${FIREFLY_SECRET_KEY}\"}" \
  | jq -r '.access_token // .accessToken')
```

## Phase B — Configure Google Cloud

### 1. Authenticate and set the project

```shell
gcloud auth login
gcloud config set project PROJECT_ID

PROJECT_ID=$(gcloud config get-value project)
```

### 2. Create the service account

```shell
gcloud iam service-accounts create firefly-sa \
  --description="Service Account for Firefly integration" \
  --display-name="Firefly Service Account"

SA_EMAIL="firefly-sa@${PROJECT_ID}.iam.gserviceaccount.com"
echo "$SA_EMAIL"
```

Use a dedicated service account rather than reusing an existing one — it keeps revocation and audit simple.

### 3. Grant project-level roles

```shell
for ROLE in \
  roles/viewer \
  roles/iam.securityReviewer \
  roles/logging.configWriter \
  roles/storage.bucketViewer
do
  gcloud projects add-iam-policy-binding "$PROJECT_ID" \
    --member="serviceAccount:${SA_EMAIL}" \
    --role="$ROLE" \
    --condition=None
done
```

| Role                         | Purpose                                                 |
| ---------------------------- | ------------------------------------------------------- |
| `roles/viewer`               | Read-only access for inventory discovery                |
| `roles/iam.securityReviewer` | IAM and security configuration, for compliance scanning |
| `roles/logging.configWriter` | Required for event-driven integration (log sinks)       |
| `roles/storage.bucketViewer` | Bucket metadata for IaC state discovery                 |

### 4. Grant conditional tfstate access

Object read access is scoped by an IAM condition so Firefly can only read objects whose name ends in `tfstate`.

```shell
gcloud projects add-iam-policy-binding "$PROJECT_ID" \
  --member="serviceAccount:${SA_EMAIL}" \
  --role="roles/storage.objectViewer" \
  --condition='title=TFStateSuffix,description=Limit access to tfstate objects only,expression=resource.name.endsWith("tfstate")'
```

**Use `add-iam-policy-binding`, never `set-iam-policy`, for this.** `set-iam-policy` overwrites the project's entire IAM policy document; `add-iam-policy-binding` is additive and safe.

### 5. Org-level folder discovery (optional)

Only needed if you want Firefly to discover the folder tree and auto-enroll projects. Requires org admin.

```shell
ORG_ID=$(gcloud organizations list --format="value(ID)")
ROLE_ID="fireflyFolderDiscovery"

gcloud iam roles create "$ROLE_ID" \
  --organization="$ORG_ID" \
  --title="Firefly Folder Discovery" \
  --description="Allows Firefly to discover the folder tree" \
  --permissions="resourcemanager.folders.get,resourcemanager.folders.list" \
  --stage="GA"

gcloud organizations add-iam-policy-binding "$ORG_ID" \
  --member="serviceAccount:${SA_EMAIL}" \
  --role="organizations/${ORG_ID}/roles/${ROLE_ID}"
```

For full org-wide project discovery, also grant `roles/viewer` at the organization scope. This is the main scale lever on GCP — with org-level viewer and auto-discovery enabled, you onboard one anchor project and Firefly enumerates the rest.

### 6. Generate the service account key

```shell
gcloud iam service-accounts keys create firefly-sa-key.json \
  --iam-account="$SA_EMAIL"
```

This file is a long-lived credential. Keep it out of version control, delete it from your workstation once uploaded, and agree a rotation schedule up front.

**Service account keys don't expire.** That's a common finding in security reviews — the key is scoped to a dedicated read-only service account, the tfstate binding is conditioned to a filename suffix, and rotation is a process you control. To rotate, create a second key, update the integration in the Firefly console, then delete the old key:

```shell
gcloud iam service-accounts keys list --iam-account="$SA_EMAIL"
gcloud iam service-accounts keys delete KEY_ID --iam-account="$SA_EMAIL"
```

Agree a rotation interval during onboarding rather than after the first audit.

### 7. Enable required APIs

```shell
gcloud services enable \
  logging.googleapis.com \
  admin.googleapis.com \
  appengine.googleapis.com \
  bigquery.googleapis.com \
  cloudbilling.googleapis.com \
  cloudfunctions.googleapis.com \
  cloudscheduler.googleapis.com \
  dataproc.googleapis.com \
  dns.googleapis.com \
  cloudresourcemanager.googleapis.com \
  sqladmin.googleapis.com \
  compute.googleapis.com \
  iam.googleapis.com \
  container.googleapis.com \
  servicemanagement.googleapis.com \
  serviceusage.googleapis.com \
  cloudasset.googleapis.com \
  redis.googleapis.com \
  storage.googleapis.com \
  groupssettings.googleapis.com \
  spanner.googleapis.com \
  file.googleapis.com \
  recommender.googleapis.com
```

Takes 2–3 minutes. `recommender.googleapis.com` powers Google Cloud Insights — leaving it out silently disables that feature.

### 8. Additional projects (optional)

The same service account can cover multiple projects. For each additional project, repeat steps 3, 4, and 7 with `PROJECT_ID` set to the new project and `SA_EMAIL` unchanged:

```shell
for P in project-b project-c project-d; do
  for ROLE in roles/viewer roles/iam.securityReviewer roles/logging.configWriter roles/storage.bucketViewer; do
    gcloud projects add-iam-policy-binding "$P" \
      --member="serviceAccount:${SA_EMAIL}" --role="$ROLE" --condition=None
  done
done
```

## Phase C — Register with Firefly

```shell
SA_KEY=$(jq -c . firefly-sa-key.json | jq -R .)

PAYLOAD=$(jq -n \
  --arg name "Production GCP" \
  --arg projectId "$PROJECT_ID" \
  --argjson serviceAccountKey "$SA_KEY" \
  '{
    name: $name,
    projectId: $projectId,
    serviceAccountKey: $serviceAccountKey,
    isPrimary: true,
    shouldAutoDiscoverProjects: true,
    isProd: true,
    isEventDrivenDisabled: false,
    isIacAutoDiscoverDisabled: false,
    regexExcludeProjectsDiscovery: []
  }')

curl -sS -X POST "${FIREFLY_API}/integrations/google" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  --data "$PAYLOAD" | jq .
```

| Parameter                       | Description                                                                               |
| ------------------------------- | ----------------------------------------------------------------------------------------- |
| `name`                          | Display name for the integration                                                          |
| `projectId`                     | The GCP project ID being integrated. This is the anchor project used to discover the rest |
| `serviceAccountKey`             | Full contents of `firefly-sa-key.json`, as a JSON string                                  |
| `isPrimary`                     | True if this is your primary GCP integration                                              |
| `shouldAutoDiscoverProjects`    | Discover all accessible projects automatically                                            |
| `isProd`                        | Marks the integration as production                                                       |
| `isEventDrivenDisabled`         | `false` enables real-time event detection                                                 |
| `isIacAutoDiscoverDisabled`     | `false` enables automatic IaC discovery                                                   |
| `regexExcludeProjectsDiscovery` | Regex list of projects to skip, e.g. `[".*-test$", "sandbox-.*"]`                         |

The initially integrated project is listed first in the console, and every subsequently discovered project hangs off it. Deleting that first project deletes the whole integration — worth planning around before you tidy up unused projects.

This call isn't documented as idempotent. If a registration partially fails, check the console before retrying rather than assuming a second call will update the integration in place.

## Verify

1. Firefly console > **Settings > Integrations > Google Cloud**
2. Confirm the project appears and the status is connected
3. **Inventory**, filtered to Google Cloud — allow 10–15 minutes for first discovery

## Troubleshooting

| Symptom                              | Likely cause                                                                                                         |
| ------------------------------------ | -------------------------------------------------------------------------------------------------------------------- |
| Key creation fails outright          | `constraints/iam.disableServiceAccountKeyCreation` is enforced — see the pre-flight check above                      |
| Role binding rejected                | Org policy restricting IAM grants (`allowedPolicyMemberDomains`), or missing `resourcemanager.projects.setIamPolicy` |
| API enablement fails                 | Billing not enabled on the project, or missing `serviceusage.services.enable`                                        |
| Registration returns 4xx             | Malformed `serviceAccountKey` (check the `jq -c . \| jq -R .` escaping), or an expired token                         |
| Some projects missing from Inventory | APIs not enabled on those projects, or they match an exclusion regex                                                 |
| No IaC state discovered              | The tfstate IAM condition wasn't applied, or state objects don't end in `tfstate`                                    |


---

# 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.firefly.ai/detailed-guides/bulk-onboarding/google-cloud.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.
