Google Cloud Security Best Practices: IAM, VPC, and Monitoring
A deep-dive into securing Google Cloud Platform: organization policies, Workload Identity, VPC Service Controls, Security Command Center, IAM recommender, and Cloud Armor.
Google Cloud Platform has a rich set of security primitives that, when properly configured, make it one of the most defensible public clouds available. But many teams only scratch the surface — leaving default service account permissions, skipping VPC controls, and never touching Security Command Center. This guide covers the controls that matter most.
Organization Policies: Guardrails at Scale
Organization policies (part of the Resource Manager API) let you define constraints that apply across your entire GCP organization, folders, or projects — regardless of individual IAM bindings. Think of them as GCP's equivalent of AWS SCPs.
Critical organization policies to enable:
Require OS Login (replaces SSH key metadata):
gcloud resource-manager org-policies set-policy \
--organization=YOUR_ORG_ID \
constraints/compute.requireOsLogin.yaml
Prevent public IP assignment on VMs:
# no-public-ip.yaml
constraint: constraints/compute.vmExternalIpAccess
listPolicy:
allValues: DENY
Restrict which domains can be granted IAM roles (prevents adding personal Gmail accounts):
constraint: constraints/iam.allowedPolicyMemberDomains
listPolicy:
allowedValues:
- is:principalSet://iam.googleapis.com/organizations/YOUR_ORG_ID
Disable service account key creation (force Workload Identity instead):
constraint: constraints/iam.disableServiceAccountKeyCreation
booleanPolicy:
enforced: true
Apply these at the organization level and override selectively at the folder or project level only when a specific workload requires an exception.
Workload Identity for GKE
The most common GCP security mistake in Kubernetes deployments is mounting service account JSON keys as Kubernetes secrets. Keys don't expire, can be copied out of the cluster, and create a persistent foothold if stolen.
Workload Identity binds a Kubernetes service account to a Google service account, allowing pods to call GCP APIs using short-lived tokens fetched from the metadata server — with no JSON key ever stored.
Setup:
# Enable Workload Identity on the cluster
gcloud container clusters update my-cluster \
--workload-pool=PROJECT_ID.svc.id.goog \
--region=us-central1
# Create a Google service account
gcloud iam service-accounts create my-app-sa \
--display-name="My App Service Account"
# Grant the GCP service account roles
gcloud projects add-iam-policy-binding PROJECT_ID \
--member="serviceAccount:my-app-sa@PROJECT_ID.iam.gserviceaccount.com" \
--role="roles/storage.objectViewer"
# Bind the Kubernetes SA to the Google SA
gcloud iam service-accounts add-iam-policy-binding \
my-app-sa@PROJECT_ID.iam.gserviceaccount.com \
--role roles/iam.workloadIdentityUser \
--member "serviceAccount:PROJECT_ID.svc.id.goog[NAMESPACE/KSA_NAME]"
Then annotate your Kubernetes service account:
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-ksa
namespace: default
annotations:
iam.gke.io/gcp-service-account: my-app-sa@PROJECT_ID.iam.gserviceaccount.com
Pods using this service account will authenticate to GCP APIs without any key material in the cluster.
VPC Service Controls
VPC Service Controls create a security perimeter around GCP APIs to prevent data exfiltration — even if an attacker obtains valid IAM credentials. They work by restricting API access to requests originating from within your defined perimeter (specific VPCs, IP ranges, or identity conditions).
A service perimeter can restrict access to APIs like Cloud Storage, BigQuery, and Cloud KMS:
gcloud access-context-manager perimeters create my-perimeter \
--title="Production Perimeter" \
--resources=projects/PROJECT_NUMBER \
--restricted-services=storage.googleapis.com,bigquery.googleapis.com \
--policy=ACCESS_POLICY_NAME
Key use cases for VPC Service Controls:
- Prevent BigQuery data from being exfiltrated to a personal project by a malicious insider
- Restrict Cloud KMS key usage to requests from your corporate network
- Block Cloud Storage reads from outside your organization
Use dry-run mode first to identify what would be blocked before enforcing:
gcloud access-context-manager perimeters dry-run create ...
Security Command Center
Security Command Center (SCC) is GCP's native security posture management service. It aggregates findings from built-in detectors, integrated partner tools, and your own custom sources.
Built-in detectors include:
- Security Health Analytics: Misconfigured firewall rules, public GCS buckets, service accounts with owner roles, disabled audit logs
- Event Threat Detection: Detects threats in Cloud Logging data — including crypto mining, data exfiltration, brute force, and IAM anomalies
- Container Threat Detection: Runtime anomalies in GKE — unexpected process execution, suspicious network connections, binary tampering
Enable SCC at the organization level for full coverage:
gcloud services enable securitycenter.googleapis.com
Export findings to BigQuery for long-term retention and SIEM integration:
gcloud scc findings list projects/PROJECT_ID \
--filter="state=\"ACTIVE\"" \
--format=json | bq insert mydataset.scc_findings -
Set up Pub/Sub notifications for high-severity findings to feed your alerting pipeline.
IAM Recommender
IAM Recommender uses machine learning to analyze 90 days of activity logs and generate recommendations to reduce over-permissive bindings. It identifies roles that were granted but whose permissions were never exercised during the observation window.
View recommendations via CLI:
gcloud recommender recommendations list \
--project=PROJECT_ID \
--location=global \
--recommender=google.iam.policy.Recommender \
--format=json
Each recommendation includes:
- The current binding (principal + role)
- The suggested replacement (a more restrictive role or custom role)
- The permissions actually used during the observation period
Automate acceptance of low-risk recommendations (e.g., removing unused predefined roles) via the API, and route high-risk ones (owner/editor role removals) through a change approval workflow.
Audit Logs
GCP Audit Logs have three categories you should understand:
| Log Type | What It Captures | Default? |
|---|---|---|
| Admin Activity | IAM changes, resource creation/deletion | Always on |
| Data Access | API calls that read/write resource data | Off by default |
| System Event | GCP-generated admin actions | Always on |
Enable Data Access audit logs for sensitive services:
gcloud projects get-iam-policy PROJECT_ID --format=json > policy.json
# Add auditConfigs for storage.googleapis.com, bigquery.googleapis.com, etc.
gcloud projects set-iam-policy PROJECT_ID policy.json
Or set at organization level via Terraform:
resource "google_organization_iam_audit_config" "all_services" {
org_id = var.org_id
service = "allServices"
audit_log_config {
log_type = "ADMIN_READ"
}
audit_log_config {
log_type = "DATA_READ"
}
audit_log_config {
log_type = "DATA_WRITE"
}
}
Route all audit logs to Cloud Storage for long-term retention and to BigQuery for querying. Alert on critical events: project deletion, IAM owner grants, firewall rule changes, and disabling of audit logging itself.
Cloud Armor for DDoS and Application Protection
Cloud Armor is GCP's managed DDoS protection and WAF service, integrated with Cloud Load Balancing. It operates at the edge, before traffic reaches your backends.
Configure a security policy with key rules:
# Create a policy
gcloud compute security-policies create my-waf-policy \
--description="WAF policy for production"
# Enable the pre-configured OWASP Top 10 ruleset
gcloud compute security-policies rules create 1000 \
--security-policy=my-waf-policy \
--expression="evaluatePreconfiguredWaf('xss-v33-stable', {'sensitivity': 1})" \
--action=deny-403
# Rate limit by IP
gcloud compute security-policies rules create 2000 \
--security-policy=my-waf-policy \
--expression="true" \
--action=rate-based-ban \
--rate-limit-threshold-count=100 \
--rate-limit-threshold-interval-sec=60 \
--ban-duration-sec=300
# Attach to a backend service
gcloud compute backend-services update my-backend \
--security-policy=my-waf-policy \
--global
For adaptive protection against DDoS, enable it in the policy:
gcloud compute security-policies update my-waf-policy \
--enable-layer7-ddos-defense
Cloud Armor Adaptive Protection uses ML to detect volumetric attacks and automatically generates suggested rules to block attack traffic while preserving legitimate requests.
Service Account Hygiene
Beyond Workload Identity, apply these rules to all GCP service accounts:
- Never grant owner or editor roles to service accounts. Use fine-grained roles like
roles/storage.objectAdminscoped to specific resources. - Disable unused service accounts: audit with
gcloud iam service-accounts listand disable any not actively used. - Rotate user-managed keys every 90 days if you must use them, and prefer short-lived tokens generated via
gcloud auth print-access-token. - Use the principle of one service account per workload — never share a service account between unrelated services.
GCP's IAM Recommender and Policy Analyzer together give you a clear view of what permissions are granted vs. what is actually being used, making it straightforward to progressively tighten service account permissions without breaking applications.