# gcloud CLI Cheat Sheet — Logging, Monitoring & Governance

> **Tool:** Google Cloud CLI (gcloud)
> **Category:** Cloud CLIs
> **Verified against:** Google Cloud SDK 553.0.0, flags verified via `gcloud logging read --help`, `gcloud
> logging sinks create --help`, `gcloud logging metrics create --help`, `gcloud monitoring policies create
> --help`, `gcloud monitoring dashboards create --help`, `gcloud monitoring uptime create --help`, `gcloud
> beta monitoring channels create --help`, `gcloud beta error-reporting --help`, `gcloud org-policies
> set-policy --help`, `gcloud resource-manager org-policies enable-enforce --help`, `gcloud resource-manager
> folders create --help`, `gcloud billing accounts list --help`, and `gcloud asset search-all-resources
> --help` run locally, 2026-09-17
> **Official docs:** https://cloud.google.com/sdk/gcloud/reference/logging, https://cloud.google.com/sdk/gcloud/reference/monitoring, and https://cloud.google.com/sdk/gcloud/reference/org-policies

## What it is and where it fits 🎯

This page bundles two things that share an audience more than a mechanism: **observability** (Cloud
Logging, Cloud Monitoring, Error Reporting, what's happening in your workloads right now) and
**governance** (Organization Policy, folders, billing, Cloud Asset Inventory, what's allowed to exist and
who's paying for it across an entire organization). Both are the operator's-eye-view layer sitting above
the individual services covered on pages `02`-`05`, the same combined "monitoring and governance" split this
site's Azure CLI page `04` uses. This page assumes the project/org/IAM hierarchy diagram from page `01`; a
folder or organization referenced below is the same resource hierarchy introduced there.

## Core concepts: where an org policy actually applies

```mermaid
flowchart TD
    Org["Organization"] --> FolderA["Folder: engineering"]
    Org --> FolderB["Folder: sandbox"]
    FolderA --> ProjA["Project: prod-api"]
    FolderA --> ProjB["Project: prod-web"]
    FolderB --> ProjC["Project: dev-experiments"]
    Policy["Org policy:<br/>restrict public IPs"] -.->|"set at org,<br/>inherited everywhere"| Org
    Override["Project-level override:<br/>allow public IPs"] -.->|"narrower scope wins"| ProjC

    classDef crit fill:#fbe8e6,stroke:#b3261e,color:#10161c
    classDef info fill:#e5f0fa,stroke:#1d6fb8,color:#10161c
    classDef ok fill:#e5f5ea,stroke:#1f8a4c,color:#10161c
    class Policy crit
    class Override ok
    class Org,FolderA,FolderB info
```

An Organization Policy set at the organization node is inherited by every folder and project underneath it,
the same inheritance shape IAM role bindings have on page `01`. Unlike most IAM bindings, a policy can be
narrowed or overridden at a lower node (a `sandbox` folder explicitly allowing something the org otherwise
denies), which is the deliberate design for "safe by default, opt out where genuinely needed" rather than
"deny with no escape hatch."

## Cloud Logging: reading log entries

```bash
gcloud logging read "resource.type=gce_instance AND severity>=ERROR" --limit=20 --freshness=1d
gcloud logging read "resource.type=k8s_container AND resource.labels.namespace_name=default" --limit=50
gcloud logging read 'timestamp>="2026-09-17T00:00:00Z"' --project=my-project-id --format=json
```

The filter syntax is the same Cloud Logging query language the Console's own Logs Explorer uses, worth
learning once since it transfers directly between the CLI and the UI. `--freshness` (default `1d`) bounds
how far back gcloud searches before giving up, worth widening for an incident investigation into something
that happened further back than a day ago, and narrowing for a tight, fast query against very recent
activity.

## Cloud Logging: sinks (routing logs somewhere else)

```bash
gcloud logging sinks create audit-to-bigquery \
  bigquery.googleapis.com/projects/my-project-id/datasets/audit_logs \
  --log-filter='protoPayload.serviceName="iam.googleapis.com"' \
  --project=my-project-id

gcloud logging sinks create org-security-logs \
  storage.googleapis.com/security-log-archive \
  --organization=<organization-id> --include-children \
  --log-filter='severity>=WARNING'

gcloud logging sinks list --project=my-project-id
gcloud logging sinks describe audit-to-bigquery --project=my-project-id --format="value(writerIdentity)"
```

A sink routes logs matching its filter to BigQuery, Cloud Storage, or Pub/Sub, useful for long-term
retention beyond Cloud Logging's own retention window, or for feeding a SIEM. `--include-children` on an
org- or folder-level sink is what actually makes it apply to every project underneath, an org-level sink
created without it captures only organization-level log entries themselves, not the projects' logs, a
genuinely easy mistake to make. `sinks describe`'s `writerIdentity` field is the service account the sink
publishes as, that identity needs write access on the destination (a BigQuery dataset, a bucket) or the sink
silently fails to deliver anything.

## Cloud Logging: logs-based metrics

```bash
gcloud logging metrics create failed-login-count \
  --description="Count of failed login attempts" \
  --log-filter='jsonPayload.event="login_failed"'

gcloud logging metrics list
gcloud logging metrics describe failed-login-count
```

A logs-based metric turns a log filter into a real Cloud Monitoring metric, counting matching entries over
time, which can then back an alerting policy the same way any other Monitoring metric can. This is the
bridge between "I can see this in the logs" and "I get paged when this happens too often", without needing
the application itself to emit a custom metric for something already visible in its logs.

## Cloud Monitoring: alerting policies

```bash
gcloud monitoring policies create \
  --display-name="High error rate: checkout-api" \
  --condition-display-name="5xx rate > 5%" \
  --condition-filter='resource.type="cloud_run_revision" AND resource.labels.service_name="checkout-api" AND metric.type="run.googleapis.com/request_count" AND metric.labels.response_code_class="5xx"' \
  --aggregation='{"alignmentPeriod": "300s", "perSeriesAligner": "ALIGN_RATE"}' \
  --duration=300s --if=any --notification-channels=<channel-id>

gcloud monitoring policies list
gcloud monitoring policies describe <policy-id>
```

`--condition-filter` is a Monitoring Query Language filter identifying the exact metric/resource
combination to watch, `--duration` is how long the condition must hold true before the policy actually
fires (avoiding a single noisy blip triggering a page), and `--notification-channels` is where the alert
actually goes, which has to already exist as a channel, created separately below.

## Cloud Monitoring: notification channels, dashboards, and uptime checks

```bash
gcloud beta monitoring channels create \
  --display-name="Platform team PagerDuty" --type=pagerduty \
  --channel-labels=service_key=<pagerduty-integration-key>

gcloud monitoring dashboards create --config-from-file=checkout-dashboard.json

gcloud monitoring uptime create checkout-api-uptime \
  --resource-type=uptime-url --resource-labels=host=checkout.example.com \
  --protocol=https --path=/healthz --period=60
```

> [!NOTE]
> `gcloud monitoring channels create` is currently only available under `alpha`/`beta` release tracks (not
> yet GA at the time of writing), confirmed against a real `gcloud monitoring channels` invocation returning
> "available in one or more alternate release tracks." `gcloud monitoring policies create`,
> `gcloud monitoring dashboards create`, and `gcloud monitoring uptime create` are all GA, no prefix needed.
> Run `gcloud components install beta` first if `beta monitoring channels` reports a missing component.

A dashboard's `--config-from-file` takes the same JSON layout the Console's "Dashboard JSON editor" exports,
the practical workflow is usually build one visually in the Console once, export it, then check that JSON
into version control and manage it via this command from then on rather than hand-writing the layout.

## Error Reporting

```bash
gcloud beta error-reporting events list --project=my-project-id --limit=20
gcloud beta error-reporting events list --service=checkout-api --limit=20
```

Error Reporting automatically groups and surfaces exceptions from Cloud Logging entries that look like
stack traces, no separate SDK integration required for most runtimes. Its `gcloud` surface is currently
beta-only and read-focused, for anything beyond listing recent grouped errors, the Console's Error
Reporting view or the underlying API directly is the more complete interface.

## Organizations, folders, and billing

```bash
gcloud organizations list
gcloud organizations describe <organization-id>

gcloud resource-manager folders create --display-name=engineering --organization=<organization-id>
gcloud resource-manager folders list --organization=<organization-id>
gcloud resource-manager folders move <folder-id> --organization=<organization-id>

gcloud billing accounts list
gcloud billing projects link my-project-id --billing-account=<billing-account-id>
gcloud billing projects describe my-project-id --format="value(billingEnabled,billingAccountName)"
```

Folders exist purely for organizing projects (and other folders) under shared IAM/policy inheritance, per
the core-concepts diagram, they hold no resources of their own. A project with `billingEnabled: false` is
the single most common reason a brand-new project can't create billable resources at all, `billing projects
describe` is the fast way to confirm that before spending time debugging what looks like a permissions
error but is actually a missing billing link.

## Organization Policy: constraining what's allowed

```bash
cat > deny-public-ip.yaml <<'EOF'
name: projects/my-project-id/policies/compute.vmExternalIpAccess
spec:
  rules:
  - denyAll: true
EOF
gcloud org-policies set-policy deny-public-ip.yaml

gcloud org-policies describe compute.vmExternalIpAccess --project=my-project-id --effective
gcloud org-policies delete compute.vmExternalIpAccess --project=my-project-id
```

```bash
# The older, simpler form for a boolean (on/off) constraint — still valid, less flexible than the YAML form
gcloud resource-manager org-policies enable-enforce compute.disableSerialPortAccess --project=my-project-id
```

`org-policies set-policy` (no `resource-manager` prefix) is the current API surface, taking a full YAML/JSON
policy spec that supports allow lists, deny lists, and conditional rules; `resource-manager org-policies
enable-enforce`/`disable-enforce` is the older, boolean-constraint-only shortcut, still functional but
unable to express anything beyond a flat on/off. `--effective` on `describe` shows the policy actually in
force after inheritance from any parent folder/org is applied, which can differ from what's set directly on
the project itself, exactly the override shape in the core-concepts diagram.

> [!IMPORTANT]
> **A newly-created project inherits every org policy already set above it in the hierarchy, immediately,
> with no grace period.** A team that provisions a project expecting to configure it freely before any
> guardrails apply is a common source of "why is this VM creation being denied, I haven't set any policy on
> this project" confusion, the policy was never on the project, it's inherited from the folder or
> organization the project was created under.

## Cloud Asset Inventory: searching resources across a whole scope

```bash
gcloud asset search-all-resources \
  --scope=organizations/<organization-id> \
  --asset-types=compute.googleapis.com/Instance \
  --query="state:RUNNING"

gcloud asset search-all-iam-policies \
  --scope=organizations/<organization-id> \
  --query="policy:roles/owner"
```

`search-all-resources`/`search-all-iam-policies` query a maintained inventory rather than calling every
service's own list API across every project live, meaningfully faster for an org-wide question ("every
running Compute instance across all 40 projects", "every IAM binding granting `roles/owner` anywhere in the
org") than scripting a per-project loop over `gcloud compute instances list`. Both require the
`cloudasset.assets.searchAll*` permission on the scope queried, typically granted at the organization level
to a platform/security team rather than per-project.

## Real-world scenario: org-wide public-IP lockdown with a documented sandbox exception

A platform team needs to deny public IPs on Compute instances org-wide, except in an explicitly designated
`sandbox` folder where experimentation needs unrestricted internet-facing test VMs:

```bash
cat > deny-public-ip-org.yaml <<'EOF'
name: organizations/<organization-id>/policies/compute.vmExternalIpAccess
spec:
  rules:
  - denyAll: true
EOF
gcloud org-policies set-policy deny-public-ip-org.yaml

cat > allow-public-ip-sandbox.yaml <<'EOF'
name: folders/<sandbox-folder-id>/policies/compute.vmExternalIpAccess
spec:
  rules:
  - allowAll: true
EOF
gcloud org-policies set-policy allow-public-ip-sandbox.yaml

gcloud org-policies describe compute.vmExternalIpAccess --folder=<sandbox-folder-id> --effective
```

The final `describe --effective` call confirms the override actually took, rather than trusting that setting
the folder-level policy alone was sufficient, worth doing before telling the sandbox team it's safe to
proceed.

## Real-world scenario: incident response — finding every resource an over-privileged service account touched

A service account was discovered with an unexpectedly broad `roles/editor` binding at the organization
level; the incident response team needs to scope the actual blast radius before revoking it:

- [ ] `gcloud asset search-all-iam-policies --scope=organizations/<org-id> --query="policy:serviceAccount:<sa-email>"`
      to confirm every resource where that identity holds a binding, not just the one that was found
- [ ] `gcloud logging read 'protoPayload.authenticationInfo.principalEmail="<sa-email>"' --organization=<org-id> --freshness=30d`
      to see what it actually *did* with that access, not just what it was theoretically allowed to do
- [ ] Narrow or revoke the binding only after both queries complete, revoking first risks losing the audit
      trail needed to fully understand the exposure
- [ ] File a logs-based metric + alerting policy for future bindings matching the same overly-broad
      pattern, so the next occurrence pages someone instead of being found by accident

## CI/CD integration recipe: failing a pipeline on a new org-policy violation

```yaml
# .github/workflows/policy-check.yml
name: Org policy compliance check
on:
  pull_request:
    paths: ["terraform/**"]

permissions:
  id-token: write
  contents: read

jobs:
  policy-check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: google-github-actions/auth@v2
        with:
          workload_identity_provider: projects/<project-number>/locations/global/workloadIdentityPools/github-pool/providers/github-provider
          service_account: policy-checker@my-project-id.iam.gserviceaccount.com
      - uses: google-github-actions/setup-gcloud@v2
      - run: |
          gcloud asset search-all-resources --scope=projects/my-project-id \
            --asset-types=compute.googleapis.com/Instance --query="state:RUNNING" \
            --format="value(name)" | tee running-instances.txt
          echo "Instance count: $(wc -l < running-instances.txt)"
```

A pipeline like this is a starting point for a broader policy-as-code gate, real enforcement of Terraform
plans against org policy typically layers a dedicated tool (Conftest/OPA, covered on this site's security &
compliance cheat sheets) on top of `gcloud`'s own read commands rather than scripting the comparison logic
by hand.

## Common pitfalls

- **Creating an org/folder-level log sink without `--include-children`.** It captures only that node's own
  log entries, not the projects underneath it, a near-empty sink is the usual symptom.
- **Assuming a project starts with no org policy applied.** It inherits everything set above it immediately,
  see the IMPORTANT callout, `describe --effective` is how to see what's actually in force.
- **Forgetting a dead sink's `writerIdentity` needs its own IAM grant on the destination.** A sink that
  exists but was never granted write access on its BigQuery dataset/bucket fails silently, logged nowhere a
  casual look would find.
- **Reaching for `resource-manager org-policies enable-enforce` when a conditional or list-based constraint
  is actually needed.** It only expresses a flat boolean; use `org-policies set-policy` with a full YAML
  spec for anything more nuanced.
- **Provisioning a project with no billing account linked and assuming resource creation failures are a
  permissions problem.** Check `billing projects describe` first.

## Exit codes

`0` success, non-zero on any API/validation error, an org-policy violation encountered while creating a
resource (an org policy denying public IPs, say) surfaces as a normal non-zero exit from the *resource
creation* command itself (`gcloud compute instances create`), not from any command on this page, the
denial happens at the point something violates the policy, not when the policy is set.

## When to reach for something else

For declarative, reviewable org-policy, folder, and billing-link provisioning across an entire organization,
prefer Terraform's `google_org_policy_policy`/`google_folder`/`google_billing_project_info` resources over a
growing shell script of the commands on this page, consistent with the IaC guidance on every earlier page.
For a full-featured SIEM or long-term security analytics beyond what a logs-based metric and a BigQuery sink
can reasonably cover, export logs into a dedicated security platform rather than trying to make Cloud
Logging alone serve that role.
