Configure GCP Cloud Audit Logs for compliance. Set up log routing and BigQuery analysis. Use when auditing GCP activity.
npx skills add https://github.com/BagelHole/DevOps-Security-Agent-Skills --skill gcp-audit-logs
Audit GCP activity with Cloud Audit Logs for compliance, security investigation, and operational monitoring.
log_types:
admin_activity:
description: API calls that modify resource configuration or metadata
enabled: Always (cannot be disabled)
retention: 400 days (default)
cost: No charge
examples:
- Creating or deleting VM instances
- Changing IAM policies
- Modifying firewall rules
data_access:
description: API calls that read resource configuration, metadata, or user data
enabled: Must be explicitly enabled (except BigQuery)
retention: 30 days (default)
cost: Can be significant at high volume
subtypes:
ADMIN_READ: Read resource configuration/metadata
DATA_READ: Read user-provided data
DATA_WRITE: Write user-provided data
system_event:
description: Actions performed by GCP systems on behalf of resources
enabled: Always (cannot be disabled)
retention: 400 days (default)
cost: No charge
examples:
- Live migration of VM instances
- Automatic scaling events
policy_denied:
description: Actions denied by VPC Service Controls or organization policies
enabled: Always (cannot be disabled)
retention: 400 days (default)
cost: No charge
# Get current org IAM policy
gcloud organizations get-iam-policy ORG_ID --format=json > org-policy.json
# Add audit config to org-policy.json:
# {
# "auditConfigs": [
# {
# "service": "allServices",
# "auditLogConfigs": [
# {"logType": "ADMIN_READ"},
# {"logType": "DATA_READ"},
# {"logType": "DATA_WRITE"}
# ]
# }
# ],
# ...existing bindings...
# }
# Apply the updated policy
gcloud organizations set-iam-policy ORG_ID org-policy.json
# Enable data access logs for specific services at project level
gcloud projects get-iam-policy PROJECT_ID --format=json > project-policy.json
# Example: enable only for Cloud Storage and BigQuery
# {
# "auditConfigs": [
# {
# "service": "storage.googleapis.com",
# "auditLogConfigs": [
# {"logType": "DATA_READ"},
# {"logType": "DATA_WRITE"}
# ]
# },
# {
# "service": "bigquery.googleapis.com",
# "auditLogConfigs": [
# {"logType": "DATA_READ"},
# {"logType": "DATA_WRITE"}
# ]
# }
# ]
# }
gcloud projects set-iam-policy PROJECT_ID project-policy.json
# Create BigQuery dataset for audit log export
bq mk --dataset \
--description "Audit log export" \
--default_table_expiration 0 \
--location US \
PROJECT_ID:audit_logs
# Create organization-level log sink to BigQuery
gcloud logging sinks create org-audit-bigquery \
bigquery.googleapis.com/projects/PROJECT_ID/datasets/audit_logs \
--organization=ORG_ID \
--include-children \
--log-filter='logName:"cloudaudit.googleapis.com"'
# Get the sink writer identity and grant BigQuery access
SINK_SA=$(gcloud logging sinks describe org-audit-bigquery \
--organization=ORG_ID --format='value(writerIdentity)')
bq add-iam-policy-binding \
--member="$SINK_SA" \
--role="roles/bigquery.dataEditor" \
PROJECT_ID:audit_logs
# Create Cloud Storage sink for long-term archive
gsutil mb -l US -b on gs://org-audit-logs-archive
gsutil retention set 7y gs://org-audit-logs-archive
gcloud logging sinks create org-audit-storage \
storage.googleapis.com/org-audit-logs-archive \
--organization=ORG_ID \
--include-children \
--log-filter='logName:"cloudaudit.googleapis.com"'
STORAGE_SA=$(gcloud logging sinks describe org-audit-storage \
--organization=ORG_ID --format='value(writerIdentity)')
gsutil iam ch "$STORAGE_SA:objectCreator" gs://org-audit-logs-archive
# Create Pub/Sub sink for real-time streaming to SIEM
gcloud pubsub topics create audit-log-stream
gcloud logging sinks create org-audit-pubsub \
pubsub.googleapis.com/projects/PROJECT_ID/topics/audit-log-stream \
--organization=ORG_ID \
--include-children \
--log-filter='logName:"cloudaudit.googleapis.com" AND (protoPayload.methodName:"delete" OR protoPayload.methodName:"setIamPolicy" OR severity>=WARNING)'
PUBSUB_SA=$(gcloud logging sinks describe org-audit-pubsub \
--organization=ORG_ID --format='value(writerIdentity)')
gcloud pubsub topics add-iam-policy-binding audit-log-stream \
--member="$PUBSUB_SA" \
--role="roles/pubsub.publisher"
# View admin activity logs for the last 24 hours
gcloud logging read 'logName:"cloudaudit.googleapis.com/activity"
AND timestamp>="2024-01-01T00:00:00Z"' \
--project=PROJECT_ID \
--format=json \
--limit=100
# Find IAM policy changes
gcloud logging read 'logName:"cloudaudit.googleapis.com/activity"
AND protoPayload.methodName="SetIamPolicy"' \
--project=PROJECT_ID \
--freshness=7d
# Find resource deletions
gcloud logging read 'logName:"cloudaudit.googleapis.com/activity"
AND protoPayload.methodName=~"delete"
AND severity>=NOTICE' \
--project=PROJECT_ID \
--freshness=7d
# Data access audit log entries
gcloud logging read 'logName:"cloudaudit.googleapis.com/data_access"
AND protoPayload.serviceName="storage.googleapis.com"
AND protoPayload.methodName="storage.objects.get"' \
--project=PROJECT_ID \
--freshness=24h
# Failed authorization attempts
gcloud logging read 'logName:"cloudaudit.googleapis.com/policy"' \
--project=PROJECT_ID \
--freshness=7d
-- All destructive operations in the last 30 days
SELECT
timestamp,
protopayload_auditlog.authenticationInfo.principalEmail AS principal,
protopayload_auditlog.methodName AS method,
protopayload_auditlog.resourceName AS resource,
resource.labels.project_id AS project,
protopayload_auditlog.status.code AS status_code,
protopayload_auditlog.status.message AS status_message
FROM `project.audit_logs.cloudaudit_googleapis_com_activity_*`
WHERE _TABLE_SUFFIX >= FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY))
AND protopayload_auditlog.methodName LIKE '%delete%'
ORDER BY timestamp DESC
LIMIT 500;
-- IAM policy changes across the organization
SELECT
timestamp,
protopayload_auditlog.authenticationInfo.principalEmail AS changed_by,
resource.labels.project_id AS project,
protopayload_auditlog.resourceName AS resource,
protopayload_auditlog.servicedata_v1_iam.policyDelta.bindingDeltas
FROM `project.audit_logs.cloudaudit_googleapis_com_activity_*`
WHERE _TABLE_SUFFIX >= FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY))
AND protopayload_auditlog.methodName = 'SetIamPolicy'
ORDER BY timestamp DESC;
-- Activity per principal (detect anomalous usage)
SELECT
protopayload_auditlog.authenticationInfo.principalEmail AS principal,
COUNT(*) AS action_count,
COUNT(DISTINCT protopayload_auditlog.methodName) AS unique_methods,
COUNT(DISTINCT protopayload_auditlog.requestMetadata.callerIp) AS unique_ips,
MIN(timestamp) AS first_activity,
MAX(timestamp) AS last_activity
FROM `project.audit_logs.cloudaudit_googleapis_com_activity_*`
WHERE _TABLE_SUFFIX >= FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 7 DAY))
GROUP BY principal
ORDER BY action_count DESC
LIMIT 50;
-- Service account key creation events (security risk indicator)
SELECT
timestamp,
protopayload_auditlog.authenticationInfo.principalEmail AS created_by,
protopayload_auditlog.resourceName AS service_account,
protopayload_auditlog.requestMetadata.callerIp AS source_ip
FROM `project.audit_logs.cloudaudit_googleapis_com_activity_*`
WHERE _TABLE_SUFFIX >= FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 90 DAY))
AND protopayload_auditlog.methodName = 'google.iam.admin.v1.CreateServiceAccountKey'
ORDER BY timestamp DESC;
-- Data access patterns for sensitive Cloud Storage buckets
SELECT
timestamp,
protopayload_auditlog.authenticationInfo.principalEmail AS accessor,
protopayload_auditlog.resourceName AS object_path,
protopayload_auditlog.methodName AS access_type,
protopayload_auditlog.requestMetadata.callerIp AS source_ip
FROM `project.audit_logs.cloudaudit_googleapis_com_data_access_*`
WHERE _TABLE_SUFFIX >= FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 7 DAY))
AND protopayload_auditlog.resourceName LIKE '%sensitive-bucket%'
ORDER BY timestamp DESC
LIMIT 1000;
-- Failed operations indicating permission issues
SELECT
timestamp,
protopayload_auditlog.authenticationInfo.principalEmail AS principal,
protopayload_auditlog.methodName AS method,
protopayload_auditlog.status.code AS error_code,
protopayload_auditlog.status.message AS error_message,
protopayload_auditlog.requestMetadata.callerIp AS source_ip
FROM `project.audit_logs.cloudaudit_googleapis_com_activity_*`
WHERE _TABLE_SUFFIX >= FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 7 DAY))
AND protopayload_auditlog.status.code != 0
ORDER BY timestamp DESC
LIMIT 500;
# Alert on service account key creation
gcloud alpha monitoring policies create \
--display-name="SA Key Created" \
--condition-display-name="Service Account Key Creation" \
--condition-filter='resource.type="audited_resource" AND protoPayload.methodName="google.iam.admin.v1.CreateServiceAccountKey"' \
--condition-threshold-value=0 \
--condition-threshold-comparison=COMPARISON_GT \
--condition-threshold-duration=0s \
--notification-channels=projects/PROJECT_ID/notificationChannels/CHANNEL_ID \
--combiner=OR
# Create a log-based metric for IAM changes
gcloud logging metrics create iam-policy-changes \
--description="Count of IAM policy changes" \
--log-filter='logName:"cloudaudit.googleapis.com/activity" AND protoPayload.methodName="SetIamPolicy"'
# Create alerting policy using the log-based metric
gcloud alpha monitoring policies create \
--display-name="IAM Policy Changes" \
--condition-display-name="IAM Changes Detected" \
--condition-filter='metric.type="logging.googleapis.com/user/iam-policy-changes"' \
--condition-threshold-value=0 \
--condition-threshold-comparison=COMPARISON_GT \
--condition-threshold-duration=0s \
--notification-channels=projects/PROJECT_ID/notificationChannels/CHANNEL_ID
# Create log-based metric for firewall changes
gcloud logging metrics create firewall-rule-changes \
--description="Count of firewall rule changes" \
--log-filter='logName:"cloudaudit.googleapis.com/activity"
AND (protoPayload.methodName="v1.compute.firewalls.insert"
OR protoPayload.methodName="v1.compute.firewalls.delete"
OR protoPayload.methodName="v1.compute.firewalls.patch")'
# Create log-based metric for VPC network changes
gcloud logging metrics create vpc-network-changes \
--description="Count of VPC network changes" \
--log-filter='logName:"cloudaudit.googleapis.com/activity"
AND resource.type="gce_network"
AND (protoPayload.methodName=~"insert$" OR protoPayload.methodName=~"delete$")'
# Organization-level audit log sink to BigQuery
resource "google_logging_organization_sink" "audit_bigquery" {
name = "org-audit-bigquery"
org_id = var.org_id
destination = "bigquery.googleapis.com/projects/${var.project_id}/datasets/${google_bigquery_dataset.audit_logs.dataset_id}"
filter = "logName:\"cloudaudit.googleapis.com\""
include_children = true
bigquery_options {
use_partitioned_tables = true
}
}
resource "google_bigquery_dataset" "audit_logs" {
dataset_id = "audit_logs"
project = var.project_id
location = "US"
description = "Organization audit log export"
default_table_expiration_ms = null # No auto-expiry
access {
role = "WRITER"
user_by_email = google_logging_organization_sink.audit_bigquery.writer_identity
}
access {
role = "READER"
group_by_email = "[email protected]"
}
}
# Retention bucket with bucket lock
resource "google_storage_bucket" "audit_archive" {
name = "org-audit-logs-archive"
location = "US"
force_destroy = false
project = var.project_id
uniform_bucket_level_access = true
retention_policy {
is_locked = true
retention_period = 220752000 # 7 years in seconds
}
lifecycle_rule {
condition {
age = 90
}
action {
type = "SetStorageClass"
storage_class = "COLDLINE"
}
}
}
# Log-based alerting
resource "google_logging_metric" "iam_changes" {
name = "iam-policy-changes"
project = var.project_id
filter = "logName:\"cloudaudit.googleapis.com/activity\" AND protoPayload.methodName=\"SetIamPolicy\""
metric_descriptor {
metric_kind = "DELTA"
value_type = "INT64"
}
}
resource "google_monitoring_alert_policy" "iam_changes" {
display_name = "IAM Policy Changes Detected"
project = var.project_id
combiner = "OR"
conditions {
display_name = "IAM policy change count"
condition_threshold {
filter = "metric.type=\"logging.googleapis.com/user/iam-policy-changes\" AND resource.type=\"global\""
comparison = "COMPARISON_GT"
threshold_value = 0
duration = "0s"
}
}
notification_channels = [var.notification_channel_id]
}
gcp_audit_logs_checklist:
log_enablement:
- [ ] Admin activity logs verified active (always on)
- [ ] Data access logs enabled for sensitive services
- [ ] Data access exemptions configured to exclude high-volume, low-risk operations
- [ ] System event logs verified active (always on)
log_routing:
- [ ] Organization-level sink to BigQuery for analysis
- [ ] Organization-level sink to Cloud Storage for long-term archive
- [ ] Pub/Sub sink for real-time SIEM streaming (high severity events)
- [ ] Sink writer identities granted appropriate destination permissions
- [ ] Inclusion filters verified to capture all audit log types
storage_and_retention:
- [ ] BigQuery dataset created with appropriate access controls
- [ ] Cloud Storage bucket with retention policy and bucket lock
- [ ] Storage class lifecycle rules configured (Standard to Coldline)
- [ ] Default log retention in Cloud Logging extended if needed
alerting:
- [ ] Notification channels configured (email, PagerDuty, Slack)
- [ ] Log-based metric for IAM policy changes
- [ ] Log-based metric for firewall rule changes
- [ ] Log-based metric for service account key creation
- [ ] Alert policy for each critical metric
- [ ] Alert notification tested end-to-end
access_control:
- [ ] Logging Admin role restricted to security team
- [ ] BigQuery dataset read access granted to auditors only
- [ ] Storage bucket access restricted with IAM
- [ ] Sink configuration changes monitored via admin activity logs
Assess Kubernetes workloads and cluster configuration for AKS Automatic compatibility. Identifies incompatibilities, generates fixes, and guides migration from AKS Standard to AKS Automatic. WHEN: migrate to AKS Automatic, check AKS Automatic readiness, validate manifests for Automatic, assess cluster for Automatic compatibility, fix deployment for Automatic compatibility, identify AKS Automatic migration blockers, is my cluster ready for AKS Automatic.
Discovers available Azure OpenAI model capacity across regions and projects. Analyzes quota limits, compares availability, and recommends optimal deployment locations based on capacity requirements. USE FOR: find capacity, check quota, where can I deploy, capacity discovery, best region for capacity, multi-project capacity search, quota analysis, model availability, region comparison, check TPM availability. DO NOT USE FOR: actual deployment (hand off to preset or customize after discovery), quota increase requests (direct user to Azure Portal), listing existing deployments.
Interactive guided deployment flow for Azure OpenAI models with full customization control. Step-by-step selection of model version, SKU (GlobalStandard/Standard/ProvisionedManaged), capacity, RAI policy (content filter), and advanced options (dynamic quota, priority processing, spillover). USE FOR: custom deployment, customize model deployment, choose version, select SKU, set capacity, configure content filter, RAI policy, deployment options, detailed deployment, advanced deployment, PTU deployment, provisioned throughput. DO NOT USE FOR: quick deployment to optimal region (use preset).
Unified Azure OpenAI model deployment skill with intelligent intent-based routing. Handles quick preset deployments, fully customized deployments (version/SKU/capacity/RAI policy), and capacity discovery across regions and projects. USE FOR: deploy model, deploy gpt, create deployment, model deployment, deploy openai model, set up model, provision model, find capacity, check model availability, where can I deploy, best region for model, capacity analysis. DO NOT USE FOR: listing existing deployments (use foundry_models_deployments_list MCP tool), deleting deployments, agent creation (use agent/create), project creation (use project/create).
Intelligently deploys Azure OpenAI models to optimal regions by analyzing capacity across all available regions. Automatically checks current region first and shows alternatives if needed. USE FOR: quick deployment, optimal region, best region, automatic region selection, fast setup, multi-region capacity check, high availability deployment, deploy to best location. DO NOT USE FOR: custom SKU selection (use customize), specific version selection (use customize), custom capacity configuration (use customize), PTU deployments (use customize).
This skill should be used when working with LaminDB, an open-source data framework for biology that makes data queryable, traceable, reproducible, and FAIR. Use when managing biological datasets (scRNA-seq, spatial, flow cytometry, etc.), tracking computational workflows, curating and validating data with biological ontologies, building data lakehouses, or ensuring data lineage and reproducibility in biological research. Covers data management, annotation, ontologies (genes, cell types, diseases, tissues), schema validation, integrations with workflow managers (Nextflow, Snakemake) and MLOps platforms (W&B, MLflow), and deployment strategies.
Latch platform for bioinformatics workflows. Build pipelines with Latch SDK, @workflow/@task decorators, deploy serverless workflows, LatchFile/LatchDir, Nextflow/Snakemake integration.
Run Python code in the cloud with serverless containers, GPUs, and autoscaling. Use when deploying ML models, running batch processing jobs, scheduling compute-intensive tasks, or serving APIs that require GPU acceleration or dynamic scaling.
Take bagelhole/gcp-audit-logs from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.