DevOps on Google Cloud, part 4: VPC, service accounts, and Secret Manager — the security skeleton
Part 4from the DevOps on Google Cloud series · 6 parts in all
Part 4: the security skeleton. GCP's two standout pieces here are the global VPC (subnets are regional, but the VPC and its firewall rules are global objects) and service accounts as first-class identities for workloads — plus the least- promoted feature in cloud security: IAM Conditions.
VPC and firewall rules: global by default
A GCP VPC spans all regions; a subnet is regional. Firewall rules are VPC-level, applied by tags and service accounts rather than subnet membership — which makes the classic three-tier chain cleaner to express:
# The chain: db <- app <- lb. Tags target workloads, not subnets.
gcloud compute networks create maplecart-vnet --subnet-mode=custom
gcloud compute networks subnets create app-useast1 \
--network=maplecart-vnet --region=us-east1 --range=10.0.2.0/24
gcloud compute firewall-rules create allow-lb-to-app \
--network=maplecart-vnet --direction=INGRESS \
--allow=tcp:8080 --source-ranges=130.211.0.0/22,35.191.0.0/16 \ # Google LB health ranges
--target-tags=web # the web-tier tag
gcloud compute firewall-rules create allow-app-to-db \
--network=maplecart-vnet --direction=INGRESS \
--allow=tcp:5432 \
--source-service-accounts=maplecart-web@maplecart.iam.gserviceaccount.com \ # identity, not IP!
--target-tags=db
That last rule is the one to notice: the database allows connections from a service account, not a CIDR. Renaming an instance or scaling the app fleet doesn't touch firewall rules — the identity is the anchor.
Service accounts and Workload Identity: the keyless default
Every workload runs as a service account; Cloud Run, GKE Workload Identity, and Cloud Functions attach it at deploy time. The rule mirrors the other series: no downloaded JSON keys — keys exist only for third parties that can't do federation:
# The app's identity and its least-privilege grants
gcloud iam service-accounts create maplecart-web
gcloud iam service-accounts add-iam-policy-binding [email protected] \
--member=serviceAccount:[email protected] --role=roles/run.invoker
gcloud projects add-iam-policy-binding maplecart \
--member=serviceAccount:maplecart-web@... --role=roles/secretmanager.secretAccessor \
--condition='expression=resource.name.startsWith("projects/123/secrets/maplecart-"),title=db-only'
# ^ IAM Conditions: the SA can read ONLY secrets prefixed "maplecart-" - least privilege
# with conditions, not with dozens of bespoke roles.
Secret Manager: the audit-friendly vault
gcloud secrets create maplecart-db-password --replication-policy=user-managed \
--locations=us-east1
echo -n "$PASSWORD" | gcloud secrets versions add maplecart-db-password --data-file=-
# Access from the app: the SA above + one line of SDK
from google.cloud import secretmanager
client = secretmanager.SecretManagerServiceClient()
name = f"projects/maplecart/secrets/maplecart-db-password/versions/latest"
password = client.access_secret_version(name=name).payload.data.decode()
Versioning is built in (rotate = add version; rollback = point at previous), every access is logged, and the IAM Conditions from above can scope which secrets a service may read. With network-by-identity, keyless workloads, and conditioned secret access, this is the tightest default skeleton of the three clouds. Next: delivery and observability.