GCP for DevOps — Resource Labels & Governance
Resource Labels & Governance
Labels are key-value pairs attached to GCP resources that enable cost allocation, automation, and governance. Combined with organization policies and asset inventory, labels form the backbone of cloud resource management at scale.
Labeling Strategy
Label Taxonomy:
┌──────────────────────────────────────────────────────────┐
│ Resource Labels │
│ │
│ Business Labels Technical Labels │
│ ┌──────────────────┐ ┌──────────────────────────┐ │
│ │ team=platform │ │ env=prod │ │
│ │ cost-center=1234 │ │ app=web-api │ │
│ │ owner=alice │ │ component=frontend │ │
│ │ department=eng │ │ managed-by=terraform │ │
│ └──────────────────┘ └──────────────────────────┘ │
│ │
│ Operational Labels Security Labels │
│ ┌──────────────────┐ ┌──────────────────────────┐ │
│ │ backup=daily │ │ data-class=confidential │ │
│ │ patch-group=a │ │ compliance=pci │ │
│ │ auto-delete=true │ │ encryption=cmek │ │
│ └──────────────────┘ └──────────────────────────┘ │
└──────────────────────────────────────────────────────────┘
Apply Labels to Resources
# Label a Compute Engine instance
$ gcloud compute instances update web-server-1 \
--zone=us-central1-a \
--update-labels=team=platform,env=prod,app=web-api,cost-center=eng-1234
Updated [web-server-1].
# Label a GKE cluster
$ gcloud container clusters update production \
--zone=us-central1-a \
--update-labels=team=platform,env=prod
Updating production...done.
# Label a Cloud Storage bucket
$ gcloud storage buckets update gs://my-data-bucket \
--update-labels=team=data,env=prod,data-class=internal
# Label a Cloud SQL instance
$ gcloud sql instances patch my-db \
--database-flags="" \
--update-labels=team=backend,env=prod
List Resources by Label
# Find all production VMs
$ gcloud compute instances list \
--filter="labels.env=prod" \
--format="table(name,zone,machineType,labels)"
NAME ZONE MACHINE_TYPE LABELS
web-server-1 us-central1-a e2-standard-2 env=prod,team=platform
api-server-1 us-central1-b e2-standard-4 env=prod,team=backend
# Find all resources owned by a team
$ gcloud compute instances list --filter="labels.team=platform"
$ gcloud container clusters list --filter="resourceLabels.team=platform"
$ gcloud sql instances list --filter="settings.userLabels.team=platform"
Enforce Labels with Organization Policies
Use a custom organization policy or a Cloud Function to enforce labeling:
# label_enforcer.py — Cloud Function triggered by Cloud Asset Inventory
from google.cloud import asset_v1
from google.cloud import compute_v1
REQUIRED_LABELS = ['team', 'env', 'cost-center']
def check_labels(event, context):
"""Check that new resources have required labels."""
asset = event.get('asset', {})
resource = asset.get('resource', {})
labels = resource.get('data', {}).get('labels', {})
missing = [l for l in REQUIRED_LABELS if l not in labels]
if missing:
resource_name = asset.get('name', 'unknown')
print(f"VIOLATION: {resource_name} missing labels: {missing}")
# Send Slack alert or create ticket
send_alert(resource_name, missing)
Cloud Asset Inventory for Governance
# Search for all resources without the 'team' label
$ gcloud asset search-all-resources \
--scope=projects/my-project \
--query="NOT labels.team:*" \
--asset-types="compute.googleapis.com/Instance" \
--format="table(name,assetType,labels)"
# Export all resources with their labels to BigQuery
$ gcloud asset export \
--project=my-project \
--output-bigquery-force \
--output-bigquery-dataset=asset_inventory \
--output-bigquery-table=resources \
--content-type=resource
Export in progress...done.
# Query unlabeled resources in BigQuery
# SELECT name, asset_type FROM asset_inventory.resources
# WHERE labels IS NULL OR ARRAY_LENGTH(labels) = 0
Label-Based Cost Reports
-- Cost by team label (BigQuery billing export)
SELECT
(SELECT value FROM UNNEST(labels) WHERE key = 'team') AS team,
(SELECT value FROM UNNEST(labels) WHERE key = 'env') AS environment,
ROUND(SUM(cost), 2) AS total_cost
FROM `billing_data.gcp_billing_export_v1_XXXXX`
WHERE invoice.month = FORMAT_DATE('%Y%m', CURRENT_DATE())
GROUP BY team, environment
ORDER BY total_cost DESC;
Label-Based Automation
# Auto-stop dev VMs at night (Cloud Scheduler + Function)
# The function stops all VMs with label env=dev
# Schedule: Every weekday at 8 PM
$ gcloud scheduler jobs create http stop-dev-vms \
--schedule="0 20 * * 1-5" \
--uri="https://REGION-PROJECT.cloudfunctions.net/stop-dev-vms" \
--http-method=POST \
--oidc-service-account-email=scheduler-sa@PROJECT.iam.gserviceaccount.com
# Schedule: Every weekday at 8 AM (restart)
$ gcloud scheduler jobs create http start-dev-vms \
--schedule="0 8 * * 1-5" \
--uri="https://REGION-PROJECT.cloudfunctions.net/start-dev-vms" \
--http-method=POST \
--oidc-service-account-email=scheduler-sa@PROJECT.iam.gserviceaccount.com
Labeling Best Practices
| Practice | Example | Benefit |
|---|---|---|
| Use lowercase keys | team not Team |
Consistency |
| Use hyphens in keys | cost-center not cost_center |
GCP convention |
| Keep values short | prod not production-environment |
Readability |
| Standardize values | prod/staging/dev |
Filtering accuracy |
| Document label taxonomy | Wiki/README | Team alignment |
| Automate enforcement | Org policy + Cloud Function | Compliance |
| Review regularly | Monthly audit | Remove stale labels |
Common Mistakes
| Mistake | Impact | Fix |
|---|---|---|
| No labeling standard | Inconsistent cost attribution | Document and enforce taxonomy |
| Labels applied inconsistently | Inaccurate reports | Automate with Terraform/IaC |
| Too many labels | Complexity, hard to manage | Limit to 5-8 required labels |
| Not labeling IaC resources | Terraform resources unlabeled | Add labels block to all resources |
Quick Reference
| Task | Command |
|---|---|
| Add labels to VM | gcloud compute instances update VM --update-labels=KEY=VALUE |
| Remove labels | gcloud compute instances update VM --remove-labels=KEY |
| Filter by label | gcloud compute instances list --filter="labels.KEY=VALUE" |
| Search unlabeled resources | gcloud asset search-all-resources --query="NOT labels.KEY:*" |
| Export assets to BigQuery | gcloud asset export --output-bigquery-dataset=DATASET |
Next → Practice Exercises