Back to blog
Cloud SecurityGCPMonitoring & LoggingOperations & ComplianceStorage

GCP Storage Bucket Logging Not Enabled: Why It Matters and How to Fix It

Learn why GCP Storage buckets without access logging leave you blind during incidents, and how to enable logging with gcloud, Terraform, and policy-as-code.

TL;DR

This check flags GCP Storage buckets without access logging configured, which leaves you blind to who read or modified objects during an incident. Fix it by pointing usage and storage logs to a dedicated log bucket with gcloud storage buckets update.

When something goes wrong with a storage bucket, the first question anyone asks is "who touched it?" If access logging is off, you cannot answer that question. The data that would have told you which principal downloaded a file, when, and from where simply does not exist. This check, storage_nologging, looks for GCP Storage buckets that have no access logging configured and flags them so you can close the gap before you actually need the logs.


What this check detects

Google Cloud Storage supports usage logs and storage logs, delivered as CSV files to a destination bucket you nominate. Usage logs record individual request-level activity such as GET, PUT, and DELETE operations, including the requester, the operation type, and the response code. Storage logs record the amount of data stored over a 24-hour period.

The Lensix storage_nologging check inspects each bucket in your GCP project and reports any bucket where access logging has not been turned on. In practice that means the bucket has no logging configuration pointing to a log destination.

Note: GCP has two distinct ways of capturing storage activity. Bucket access logs (the CSV-based feature this check covers) and Cloud Audit Logs Data Access logs. They overlap but are not identical. Audit Logs are managed at the project level through IAM and Cloud Logging, while access logs are configured per bucket and write CSV files. Many teams run both.


Why it matters

Logging feels like paperwork until the day you need it. Here is where the absence of access logs hurts.

Incident investigation grinds to a halt

Suppose a credential leaks and an attacker pulls objects out of a bucket holding customer exports. Without logs, you cannot determine which objects were accessed, how many requests were made, or whether the activity was a quick smash-and-grab or a slow exfiltration over weeks. You are left guessing, and in a breach disclosure scenario, guessing means you have to assume the worst about every object in the bucket.

Compliance gaps

Frameworks like PCI DSS, HIPAA, SOC 2, and ISO 27001 expect you to record access to sensitive data. An auditor asking "show me read access to this bucket over the last 90 days" expects an answer, not a shrug. A bucket with no logging is a finding waiting to be written up.

No baseline means no anomaly detection

You cannot alert on unusual access patterns if you never recorded normal access patterns. Logging is the raw material for detection. Skip it and every downstream monitoring effort starts from nothing.

Warning: Access logs are generated on a best-effort basis and delivered roughly hourly. They are not a real-time alerting mechanism and they may occasionally be incomplete. For time-sensitive detection, pair them with Cloud Audit Data Access logs, which feed into Cloud Logging and can trigger log-based alerts.


How to fix it

Enabling access logging takes three steps: create a destination bucket for the logs, grant the Cloud Storage service the right to write there, then point your source bucket's logging at that destination.

Step 1: Create a log destination bucket

Keep your logs separate from the data they describe. A dedicated bucket makes lifecycle management and access control much cleaner.

gcloud storage buckets create gs://my-company-access-logs \
  --location=us-central1 \
  --uniform-bucket-level-access

Step 2: Grant the log delivery service account write access

Cloud Storage delivers logs using a Google-managed group. Grant it the legacy bucket writer role on the destination bucket.

gcloud storage buckets add-iam-policy-binding gs://my-company-access-logs \
  --member=group:[email protected] \
  --role=roles/storage.legacyBucketWriter

Step 3: Enable logging on the source bucket

gcloud storage buckets update gs://my-production-data \
  --log-bucket=gs://my-company-access-logs \
  --log-object-prefix=production-data/

The --log-object-prefix keeps logs from multiple source buckets organized inside one destination bucket. Verify the configuration took effect:

gcloud storage buckets describe gs://my-production-data \
  --format="json(logging_config)"

You should see the destination bucket and prefix reflected back.

Tip: Set a lifecycle rule on the log bucket so logs do not accumulate forever and rack up storage costs. A rule that deletes objects after 90 or 365 days usually balances retention requirements against spend.

Fixing it with Terraform

If you manage buckets as code, add a logging block rather than clicking through the console. The change becomes reviewable and repeatable.

resource "google_storage_bucket" "production_data" {
  name                        = "my-production-data"
  location                    = "US-CENTRAL1"
  uniform_bucket_level_access = true

  logging {
    log_bucket        = google_storage_bucket.access_logs.name
    log_object_prefix = "production-data/"
  }
}

resource "google_storage_bucket" "access_logs" {
  name                        = "my-company-access-logs"
  location                    = "US-CENTRAL1"
  uniform_bucket_level_access = true

  lifecycle_rule {
    condition {
      age = 365
    }
    action {
      type = "Delete"
    }
  }
}

Warning: Access logs consume storage in the destination bucket, and high-traffic buckets can generate a meaningful volume of log data. Factor this into your cost estimates, and use a cheaper storage class such as Nearline for older logs if you retain them long term.


How to prevent it from happening again

A one-time fix is worthless if the next bucket someone creates ships without logging. Push the requirement left into your provisioning workflow.

Enforce it in Terraform with a policy check

Use Open Policy Agent with conftest to reject any bucket that lacks a logging block before it ever reaches terraform apply.

package main

deny[msg] {
  resource := input.resource.google_storage_bucket[name]
  not resource.logging
  msg := sprintf("Storage bucket '%s' must have access logging enabled", [name])
}

Wire it into CI so the pipeline fails on a violation:

conftest test --policy ./policy main.tf.json

Use an Organization Policy where possible

GCP does not have a built-in org policy constraint that forces access logging directly, so the most reliable guardrail is policy-as-code in CI combined with periodic scanning. Run storage_nologging on a schedule so any bucket created out-of-band, through the console or a one-off script, gets caught quickly rather than sitting unlogged for months.

Tip: Wrap bucket creation in a reusable Terraform module that sets logging, uniform_bucket_level_access, and a sensible default lifecycle rule. When the secure configuration is the default and the only sanctioned path, teams stop having to remember it.


Best practices

  • Centralize log storage. Send access logs from many buckets into one or a few dedicated log buckets, ideally in a separate project with tighter IAM, so logs cannot be tampered with by anyone who can touch the source data.
  • Combine access logs with Cloud Audit Logs. Enable Data Access audit logs for Cloud Storage to get richer, real-time activity in Cloud Logging that you can alert on. Use CSV access logs for billing-style analysis and long retention.
  • Lock down the log bucket. Apply uniform bucket-level access and restrict read permissions. Logs often reveal sensitive object names and access patterns.
  • Set retention deliberately. Match log retention to your compliance obligations, then automate deletion with lifecycle rules so you neither lose required evidence nor pay to store logs forever.
  • Monitor for drift. Even with policy-as-code, run continuous checks. Configurations get changed by hand under pressure, and a scheduled scan is your safety net.

Logging is one of those controls that costs almost nothing to turn on and pays for itself the first time you have to reconstruct what happened. Enable it everywhere that holds data you would care about losing, and make it a default rather than an afterthought.

Fix GCP Storage Bucket Logging Not Enabled | Lensix | Lensix