IAM & Access Control
TSC mapping: CC5 (Control Activities), CC6.1 (Logical Access Security), CC6.2 (Access Provisioning), CC6.3 (Access Removal)
IAM is the highest-evidence area for SOC 2 auditors on GCP. Auditors will pull IAM policy exports, review role assignments, verify 2SV enforcement, and check service account key ages. Get this layer right before anything else.
1. Super Admin Account Hardening
Super Admin accounts have unrestricted access to the entire Google Cloud organization and Workspace tenant. They must be locked down and used only in break-glass scenarios.
# List all users with the Super Admin role (requires gcloud with org admin)
gcloud organizations get-iam-policy <org-id> \
--flatten="bindings[].members" \
--filter="bindings.role:roles/resourcemanager.organizationAdmin" \
--format="table(bindings.members)"
# Verify 2-Step Verification enrollment for all users
gcloud beta identity groups memberships list \
--group-email=<group>@<domain>.com \
--format="table(preferredMemberKey.id)"
Best practices:
- Maintain 2–4 Super Admin accounts maximum; keep them separate from day-to-day Google Workspace accounts.
- Super Admins must use hardware security keys (Titan Security Key or FIDO2) — phishing-resistant 2SV.
- Enable the Advanced Protection Program for all Super Admin accounts.
- Super Admin access should be requested via Privileged Access Manager (PAM) for just-in-time elevation.
Reference: Super admin account best practices → · Advanced Protection Program →
2. 2-Step Verification Enforcement
Enforce 2SV for all users via the Cloud Identity / Google Workspace Admin Console. This is the GCP equivalent of MFA enforcement.
# Enforce 2SV at the organization level via Admin Console API
# (gcloud wraps the Directory API for user management)
# List users without 2SV enrolled
gcloud identity groups memberships list \
--group-email=all-users@<domain>.com \
--format=json | \
jq '.[] | select(.isEnrolledIn2Sv == false) | .preferredMemberKey.id'
# Check 2SV enforcement status for a specific OU
gcloud beta identity groups describe \
--group-email=<ou-group>@<domain>.com
Configure via Admin Console (admin.google.com → Security → 2-step verification):
- Set Allow users to turn on 2-Step Verification: Enabled
- Set Enforcement: On (for all users)
- Set Methods allowed: Any method for users, Security key only for admins
- Set Grace period: 1 week (for new users to enrol)
Context-Aware Access — step up for sensitive resources:
# Create an access level requiring 2SV + compliant device
gcloud access-context-manager levels create require-2sv-compliant \
--title "Require 2SV + Compliant Device" \
--basic-level-spec '
conditions:
- devicePolicy:
requireScreenlock: true
allowedEncryptionStatuses: [ENCRYPTED]
requireAdminApproval: false
requireCorpOwned: false
requiredAccessLevels: []
' \
--policy <access-policy-id>
Reference: Enforce 2-Step Verification → · Context-Aware Access →
3. Privileged Access Manager (PAM)
PAM enforces just-in-time (JIT) access to sensitive IAM roles across your GCP organization. Auditors expect privileged access to be time-limited, approved, and logged.
# Enable the Privileged Access Manager API
gcloud services enable privilegedaccessmanager.googleapis.com \
--project=<project-id>
# Create a PAM entitlement for the Security Admin role (max 4h, requires approval)
gcloud beta pam entitlements create security-admin-jit \
--project=<project-id> \
--location=global \
--requester-justification-config='notMandatory=false' \
--max-request-duration=14400s \
--privileged-access='{
"gcpIamAccess": {
"roleBindings": [{"role": "roles/iam.securityAdmin"}],
"resourceType": "cloudresourcemanager.googleapis.com/Project",
"resource": "//cloudresourcemanager.googleapis.com/projects/<project-id>"
}
}' \
--approval-workflow='{
"manualApprovals": {
"requireApproverJustification": true,
"steps": [{"approvers": {"principals": ["user:[email protected]"]}, "approvalsNeeded": 1}]
}
}'
# Request JIT access (done by the engineer)
gcloud beta pam grants create \
--entitlement=security-admin-jit \
--project=<project-id> \
--location=global \
--requested-duration=3600s \
--justification="Investigating SCC finding #12345"
Reference: Privileged Access Manager →
4. IAM Least Privilege — Role Assignments
Auditors will sample IAM policies. Primitive roles (roles/owner, roles/editor) granted at the project level are the most common GCP SOC 2 finding.
# Audit all IAM bindings for a project
gcloud projects get-iam-policy <project-id> \
--flatten="bindings[].members" \
--format="table(bindings.role,bindings.members)" | sort
# Find all primitive role assignments (Owner/Editor/Viewer at project level)
gcloud projects get-iam-policy <project-id> \
--flatten="bindings[].members" \
--filter="bindings.role:(roles/owner OR roles/editor)" \
--format="table(bindings.role,bindings.members)"
# Remove an overly permissive Editor binding
gcloud projects remove-iam-policy-binding <project-id> \
--member="user:[email protected]" \
--role="roles/editor"
# Add a least-privilege predefined role instead
gcloud projects add-iam-policy-binding <project-id> \
--member="user:[email protected]" \
--role="roles/run.developer"
Recommended role pattern:
| Persona | Role | Scope |
|---|---|---|
| All engineers (default) | roles/viewer | Project |
| Developers | roles/run.developer, roles/cloudfunctions.developer | Project |
| SRE/Ops | roles/compute.instanceAdmin.v1 | Project (via PAM) |
| Security team | roles/iam.securityReviewer | Organization |
| DBA | roles/cloudsql.editor | Project |
| Break-glass | roles/owner | Project (PAM, approval required) |
Use IAM Recommender to right-size permissions:
# List IAM recommendations for a project
gcloud recommender recommendations list \
--project=<project-id> \
--location=global \
--recommender=google.iam.policy.Recommender \
--format="table(name,stateInfo.state,primaryImpact.category)"
# Apply a recommendation
gcloud recommender recommendations mark-succeeded \
<recommendation-name> \
--project=<project-id> \
--location=global \
--recommender=google.iam.policy.Recommender \
--etag=<etag>
Reference: IAM best practices → · IAM Recommender →
5. Service Account Security
Service accounts are the GCP equivalent of IAM service roles. Service account keys are a chronic audit finding — avoid them entirely when possible.
# Audit service account keys across a project (flag all user-managed keys)
for sa in $(gcloud iam service-accounts list --project=<project-id> --format="value(email)"); do
keys=$(gcloud iam service-accounts keys list \
--iam-account=$sa \
--managed-by=user \
--format="table(name.basename(),validAfterTime,validBeforeTime)" 2>/dev/null)
[ -n "$keys" ] && echo "=== $sa ===" && echo "$keys"
done
# Disable a service account key
gcloud iam service-accounts keys disable <key-id> \
--iam-account=<sa>@<project>.iam.gserviceaccount.com
# Block creation of service account keys via Organization Policy
gcloud resource-manager org-policies set-policy \
--organization=<org-id> \
policy.yaml
# policy.yaml — disable service account key creation org-wide
name: organizations/<org-id>/policies/iam.disableServiceAccountKeyCreation
spec:
rules:
- enforce: true
Best practices:
- Use Workload Identity Federation for GitHub Actions, GitLab CI, and other OIDC-compatible CI/CD systems — eliminates service account keys entirely.
- Use Workload Identity (GKE) so Pods assume IAM roles without downloading keys.
- If a key is unavoidable, rotate it every 90 days and store it in Secret Manager.
# Configure Workload Identity Federation for GitHub Actions
gcloud iam workload-identity-pools create github-pool \
--project=<project-id> \
--location=global \
--display-name="GitHub Actions Pool"
gcloud iam workload-identity-pools providers create-oidc github-provider \
--project=<project-id> \
--location=global \
--workload-identity-pool=github-pool \
--display-name="GitHub OIDC Provider" \
--attribute-mapping="google.subject=assertion.sub,attribute.repository=assertion.repository" \
--issuer-uri="https://token.actions.githubusercontent.com"
Reference: Service account best practices → · Workload Identity Federation →
6. Quarterly Access Reviews
SOC 2 CC6.3 requires evidence that access is removed when no longer needed.
# Export all IAM bindings for a project (for review spreadsheet)
gcloud projects get-iam-policy <project-id> \
--format=json > iam-review-$(date +%Y-%m-%d).json
# List all users and their last login (via Cloud Identity / Workspace Admin SDK)
gcloud beta identity groups memberships list \
--group-email=all-users@<domain>.com \
--format="table(preferredMemberKey.id,roles[0].name)"
# Pull IAM Recommender findings (unused permissions)
gcloud recommender recommendations list \
--project=<project-id> \
--location=global \
--recommender=google.iam.policy.Recommender \
--filter="stateInfo.state=ACTIVE" \
--format="table(primaryImpact.category,description)"
Review process:
- Export IAM policy for all projects to a spreadsheet.
- Cross-reference with IAM Recommender — flag roles with no usage in 90+ days.
- Send to team leads for confirmation.
- Remove unjustified bindings; document the outcome.
- Retain exported IAM policy and review outcome for audit evidence.
SOC 2 Evidence Checklist for IAM
| Evidence item | How to export |
|---|---|
| Project IAM policy | gcloud projects get-iam-policy <project-id> --format=json |
| Organization IAM policy | gcloud organizations get-iam-policy <org-id> |
| Service account key list | gcloud iam service-accounts keys list per SA |
| 2SV enforcement status | Admin Console → Security → 2-step verification (screenshot) |
| PAM entitlement and grant history | GCP Console → IAM → Privileged Access Manager |
| IAM Recommender findings and actions | gcloud recommender recommendations list |
| Quarterly access review records | Retained internally (spreadsheet, ticketing system) |
| Workload Identity pool configuration | gcloud iam workload-identity-pools list |
Official references: