GCP / Guide 3

Labels and governance

A label strategy finance can use, labels versus tags, organisation policy constraints, and labels in cost reports.

Labels answer the questions that come up every month: who owns this, what does it belong to, and who pays for it. In Google Cloud, labels flow straight into the billing export, so good labels mean cost reports by team and application with no extra work. Organisation policies and Cloud Asset Inventory make sure the rules are followed.

Labels and tags are different

Google Cloud has two similar-sounding features. Use both, for different jobs:

LabelsTags (Resource Manager tags)
What they areKey/value pairs on a resourceKey/value resources defined centrally, then bound to resources
Used forCost reports, filtering, automationAccess and policy: IAM Conditions, organisation policy conditions, firewall policies
Who can set themAnyone who can edit the resourceOnly people granted tagUser on the tag value
InheritanceNoneInherited down the hierarchy
In billing exportYesYes

Because anyone who can edit a resource can change its labels, never base security decisions on labels. That's what tags are for. And don't confuse either with network tags on VMs, which are plain strings used to target firewall rules (Guide 4).

A label strategy that finance can use

The same five as the Azure and AWS notes, adapted to Google's rules:

LabelExampleWhy
ownerjane_ravindran_inWho to ask before changing or deleting it
costcentercc-1042Who pays; used for showback and chargeback
environmentprod, test, devDifferent rules, schedules and priorities
applicationpayrollGroups resources across projects and services
dataclassificationconfidentialDrives security controls
  • Lowercase only. Keys and values can contain lowercase letters, digits, underscores and dashes; keys must start with a letter. No @ or ., which is why the owner is written jane_ravindran_in.
  • Limits: up to 64 labels per resource; keys and values up to 63 characters.
  • Label the project too. Project labels appear in the billing export alongside resource labels, so a one-workload-per-project layout gets most costs allocated even when a resource label is missing.
  • Not everything takes labels. VMs, disks, snapshots, buckets, Cloud SQL, BigQuery datasets, GKE clusters and Cloud Run services do; firewall rules and IAM don't.
  • Don't put secrets or personal data in labels. Many people and tools can read them.
# Label a VM and a bucket
gcloud compute instances add-labels vm-notes-test-01 --zone=us-east1-b `
    --labels=owner=jane_ravindran_in,costcenter=cc-1042,environment=test
gcloud storage buckets update gs://prj-notes-test-logs --update-labels=owner=jane_ravindran_in,costcenter=cc-1042

# Label the project
gcloud projects update prj-notes-test --update-labels=costcenter=cc-1042,environment=test

Find what is missing labels

Google Cloud has no built-in "require this label" policy for every service. Check instead, with Cloud Asset Inventory:

# Resources in the organisation with no costcenter label
gcloud asset search-all-resources --scope=organizations/123456789012 `
    --asset-types=compute.googleapis.com/Instance,compute.googleapis.com/Disk,storage.googleapis.com/Bucket `
    --query="NOT labels:costcenter" --format=json | ConvertFrom-Json |
    Select-Object assetType, displayName, project, location

Get-GcpUnlabeledResources.ps1 does this for a list of required labels across the common billable resource types, and reports near misses such as cost_center or cost-center. To enforce labels at creation, use custom organisation policy constraints for the services that support them (for example Compute Engine instances and GKE clusters), or require labels in your Terraform modules.

Organisation policy constraints worth setting

Beyond the secure-by-default set (Guide 2):

ConstraintWhy
gcp.resourceLocationsKeep resources in approved regions (data residency and support)
compute.skipDefaultNetworkCreationNew projects don't get the default network with its permissive firewall rules
compute.vmExternalIpAccessVMs can't have public IPs, except those you list
compute.requireOsLoginSSH access controlled by IAM, not by keys in metadata
compute.requireShieldedVmSecure boot and integrity monitoring on every VM
storage.publicAccessPreventionNo bucket can be made public
sql.restrictPublicIpCloud SQL instances on private IPs only
iam.allowedPolicyMemberDomainsStop grants to personal Gmail accounts and other outside domains

Test before you enforce. Organisation policies support dry run: the policy is evaluated and violations are logged, but nothing is blocked. Run it for a couple of weeks, check the logs, fix or exempt what breaks, then enforce. Set exceptions on specific projects or with tag conditions rather than turning a constraint off higher up.

# Enforce a boolean constraint on a folder
gcloud resource-manager org-policies enable-enforce compute.skipDefaultNetworkCreation --folder=111122223333

# Or set a list constraint from a YAML file (supports dryRunSpec for testing)
gcloud org-policies set-policy .\resource-locations.yaml

Labels in cost reports

  • Billing reports in the console group and filter by label and project label straight away; nothing to activate, unlike AWS.
  • Export billing data to BigQuery (standard and detailed usage cost) from day one. The export isn't retroactive, and it's where showback reports, dashboards and anomaly checks come from (Guide 10).
  • Labels apply to cost from the moment they're added. Costs from before a label was set stay unlabelled, so label at creation.
-- Last month's cost by costcenter label (BigQuery billing export)
SELECT
  (SELECT value FROM UNNEST(labels) WHERE key = 'costcenter') AS costcenter,
  ROUND(SUM(cost) + SUM(IFNULL((SELECT SUM(c.amount) FROM UNNEST(credits) c), 0)), 2) AS net_cost
FROM `prj-billing-export.billing.gcp_billing_export_v1_012345_6789AB_CDEF01`
WHERE invoice.month = FORMAT_DATE('%Y%m', DATE_SUB(CURRENT_DATE(), INTERVAL 1 MONTH))
GROUP BY costcenter
ORDER BY net_cost DESC