Why a profile comes before any service
In L1 you saw the whole Google Cloud security map. With that many services, it's easy to pick one because it's familiar or because a vendor demo looked good. Production experience says otherwise. Teams that choose well first write down what the workload actually needs. They then hold every service up against that list.
That list is the standard security profile. Think of it as the requirements sheet a structural engineer fills in before choosing steel or concrete. You fill it in once per workload, keep it in Git next to the code and reuse it in every later lesson. Each service gets measured against the same ten headings.
The ten headings
- Identities, in three separate groups: workforce (staff and contractors), customer (end users of your app) and workload (machines: pipelines, VMs, pods, CI jobs)
- Privilege and access model: who changes what, and how admin access is granted
- Data classification: public, internal, confidential or regulated, and where each kind lives
- Encryption and key control: who holds the key and who can revoke it
- Compliance frameworks and residency
- Detection and response needs
- Logging and audit
- Zero trust posture
- Team skills
- Cost model
Gotcha: Keep the three identity groups apart. Workforce, customer and workload identities are served by different products. Google's own docs set Identity Platform (customer sign-in to your apps) against Workforce Identity Federation (employees and partners from an external IdP). Merge the groups and you will compare the wrong services.
Worked example: "ClaimsHub"
Our sample workload is ClaimsHub, an insurance-claims portal. It has 120 staff who sign in through Microsoft Entra ID and 400,000 policyholders. Batch jobs run in GitHub Actions. It processes EU customer data. Here is the profile as a YAML file in the repo:
# security-profile.yaml (v1, 2026-10-29, owner: platform team)
workload: claimshub
identities:
workforce: {count: 120, source: "Entra ID", mfa: required} # HARD
customer: {count: 400000, signin: [email, google], mfa: optional} # PREF
workload: [github-actions, cloud-run-api, nightly-batch] # HARD: no long-lived keys
privilege:
admin_access: "time-bound, approved, logged" # HARD
data_classes:
regulated: [claims_documents, medical_notes] # GDPR special category
confidential: [policy_records]
internal: [ops_metrics]
key_control:
regulated: "we hold the key outside Google and can revoke it" # HARD
confidential: "customer-managed key, we control rotation" # PREF
compliance:
frameworks: [GDPR, ISO27001]
residency: "EU regions only, data at rest" # HARD
detection:
needs: [misconfig findings, threat alerts]
response_sla: "triage within 4h business hours" # PREF
logging:
data_access_logs: [claims bucket, claims DB] # HARD
retention: "7 years for audit evidence" # HARD
zero_trust:
staff_access: "no VPN; device posture checked" # PREF
team_skills: {terraform: strong, kms: none, siem: basic}
cost_model: {security_budget_month_usd: 6000, prefer: "per-resource, predictable"}
Every line is either HARD (a pass/fail gate: a service that misses it is out) or PREF (a preference you will weight and score later). Mark these now, while the reasons are still fresh.
Turning vague needs into testable ones
"We need encryption" can't be tested. Every Google Cloud service already encrypts data at rest by default, with no setup. With that default, though, you can't view or manage the keys or review key usage. A testable need says who holds the key, who can revoke it, and what proof you need.
| Vague | Testable |
|---|---|
| "We need encryption" | "Regulated data uses a key held outside Google; revoking it makes the data unreadable within 1 hour" |
| "Keep data in Europe" | "Data at rest and support access stay in EU regions; a deploy outside the EU must fail" |
| "We need audit logs" | "Every read of claims documents is logged and kept 7 years" |
| "Secure admin access" | "No standing owner rights; admin access expires within 8 hours" |
The first row matters a lot. Customer-managed keys give you control over rotation. A key that lives outside Google, so that you can refuse to decrypt, is a different and much stricter need. Write down which one you mean.
Gather facts, don't guess
Fill the profile from what is actually running. A few read-only commands help:
# Which org and projects exist? (scopes the profile)
gcloud organizations list
gcloud projects list --filter="labels.app=claimshub"
# Who holds access today? Raw input for the privilege section
gcloud projects get-iam-policy claimshub-prod --format=json > iam-today.json
# How long are logs kept right now?
gcloud logging buckets describe _Default --location=global --project=claimshub-prod
# retentionDays: 30 <- compare with your 7-year need
That last output tells you something useful. Admin Activity audit logs are always written and kept 400 days in the _Required bucket. The _Default bucket keeps logs 30 days unless you change it (projects allow 1 to 3,650 days). Data Access audit logs are off by default, except for BigQuery, and turning them on adds log charges. So "every read is logged and kept 7 years" is a real gap. It also has a real cost, and that belongs in the cost model.
Best practices and trade-offs
- Fewer HARD lines is better. Each one removes options. If ten items are hard, ask "what happens if we miss this?" If the answer isn't a regulator, a contract or a breach, it's a preference.
- Record team skills honestly. If nobody on the team has run an external key manager, a HARD external-key requirement needs a staffing or partner plan. Otherwise you have a risk you haven't recorded.
- Date and version it. Rules and prices change. A profile dated 2026-10-29 can be checked again later.
- One profile per workload. A marketing site and a claims system should not share one.
Key takeaways
- The profile has ten headings, and every later service choice is measured against it.
- Split identities into workforce, customer and workload, because different products serve each.
- Rewrite vague needs as testable statements: who holds the key, where data lives, what is logged and for how long.
- Mark each line HARD (a pass/fail gate) or PREF (weighted later).
- Fill the profile from evidence (
gcloudoutput, current retention, current IAM), not memory. Defaults like 30-day_Defaultretention and Data Access logs being off often show the gaps.