IAM & Organization Policies
Manage GCP IAM roles, service accounts, and organization policy constraints effectively.
Prerequisites
- GCP project with IAM permissions
- Understanding of GCP resource hierarchy (org, folders, projects)
Understanding GCP IAM Fundamentals
Google Cloud Identity and Access Management (IAM) controls who has what access to which resources. Unlike flat permission systems, GCP IAM uses a resource hierarchy: Organization → Folders → Projects → Resources. Permissions granted at a higher level are inherited by all child resources, which makes organizational design critically important. Every decision you make about where to place a role binding ripples downward through the hierarchy, affecting potentially hundreds of projects and thousands of resources.
IAM policies bind members (users, groups, service accounts, or domains) to roles, which are collections of individual permissions. A permission takes the form service.resource.verb, for example storage.objects.get grants read access to Cloud Storage objects. There are over 10,000 individual permissions across GCP services, which is why roles exist as a manageable abstraction layer.
There are three categories of roles, each suited to different scenarios:
| Role Type | Description | Example | When to Use |
|---|---|---|---|
| Basic (Primitive) | Broad access across all services (Owner, Editor, Viewer) | roles/editor | Development/sandbox only; never in production |
| Predefined | Fine-grained access for specific services | roles/storage.objectViewer | Most production workloads |
| Custom | User-defined with hand-picked permissions | projects/my-proj/roles/myRole | When predefined roles are too broad or too narrow |
Avoid Basic Roles in Production
The roles/editor role grants write access to almost every GCP service in the project. A single compromised service account with this role can modify Cloud Functions, read Secrets Manager values, and alter IAM bindings. Always prefer predefined or custom roles scoped to the minimum necessary permissions. Google's own security team recommends treating basic roles as a code smell in production environments.
The Resource Hierarchy in Depth
The resource hierarchy is more than just an organizational tool; it is the backbone of your entire access control strategy. When you bind a role at the organization level, every folder, project, and resource beneath it inherits that binding. When you bind a role at the project level, only resources within that project are affected. Understanding this inheritance model is essential for designing a least-privilege architecture.
The hierarchy has four levels, each with distinct governance responsibilities:
- Organization node: The root of your GCP hierarchy, tied to a Google Workspace or Cloud Identity domain. Organization-level bindings affect everything. Reserve this level for organization admins, billing admins, and security auditors only.
- Folders: Logical groupings that represent business units, environments (Production, Staging, Development), or teams. Folders can be nested up to 10 levels deep, though 2-3 levels is typical.
- Projects: The fundamental unit of resource isolation. Every GCP resource belongs to exactly one project. Projects provide billing boundaries, API enablement scopes, and default IAM boundaries.
- Resources: Individual GCP resources like VMs, buckets, and datasets. Resource-level IAM bindings are useful for sharing a specific resource across projects without granting broader access.
# View organization-level IAM policy
gcloud organizations get-iam-policy 123456789012 \
--format=json > org-policy.json
# View folder-level IAM policy
gcloud resource-manager folders get-iam-policy 987654321 \
--format=json > folder-policy.json
# View project-level IAM policy
gcloud projects get-iam-policy my-project \
--format=json > project-policy.json
# View resource-level IAM policy (e.g., a Cloud Storage bucket)
gcloud storage buckets get-iam-policy gs://my-bucket \
--format=json > bucket-policy.jsonIAM Policy Binding Mechanics
An IAM policy is a collection of bindings, where each binding associates a role with one or more members. When GCP evaluates an access request, it collects all policies from the resource itself up through the hierarchy to the organization, and grants access if any binding allows it. There is no explicit deny at the IAM level (deny policies are a separate feature), which means you cannot revoke a permission granted at a higher level by adding a different policy at a lower level. Inheritance is additive.
This additive model is why granting broad roles at the organization or folder level is dangerous: there is no way to “take it back” at the project level without using IAM deny policies, which add complexity.
IAM Deny Policies
IAM deny policies are a relatively recent addition that let you explicitly deny specific permissions, overriding any allow bindings. Deny policies are evaluated before allow policies. Use them sparingly for hard guardrails, for example preventing anyone except the security team from disabling audit logging. Deny policies are attached at the organization, folder, or project level.
Service Account Best Practices
Service accounts are identities for applications and workloads rather than humans. Each GCP project automatically gets a default Compute Engine service account and an App Engine service account, but relying on these defaults is an anti-pattern because they typically carry the broad Editor role. When a Cloud Function or Cloud Run service uses the default compute service account, it inherits permissions far beyond what it needs, creating a significant blast radius if compromised.
Instead, create dedicated service accounts per workload with the minimum permissions required. Follow these principles:
- One service account per microservice: never share service accounts across unrelated workloads. If your API service and your batch processing job share a service account, compromising either one compromises both.
- Avoid downloading keys: use Workload Identity Federation for external systems and the attached service account feature for GCP-native workloads. Downloaded keys are the number one source of credential leaks in cloud environments.
- Rotate keys if you must use them: set up automated key rotation with a maximum lifetime of 90 days. Use the organization policy
constraints/iam.disableServiceAccountKeyCreationto prevent key creation entirely where possible. - Disable unused service accounts: use IAM Recommender to find accounts that haven't authenticated in 90+ days. Disabled accounts can be re-enabled if needed, but they cannot be used by attackers.
- Limit service account impersonation: the
roles/iam.serviceAccountTokenCreatorrole allows generating tokens for a service account, which is effectively the same as having its permissions. Guard this role carefully.
# Create a dedicated service account
gcloud iam service-accounts create cloud-run-invoker \
--display-name="Cloud Run Invoker" \
--project=my-project
# Grant only the specific role needed
gcloud projects add-iam-policy-binding my-project \
--member="serviceAccount:cloud-run-invoker@my-project.iam.gserviceaccount.com" \
--role="roles/run.invoker" \
--condition='expression=resource.name.startsWith("projects/my-project/locations/us-central1/services/api-"),title=only-api-services'Service Account Key Management
If you absolutely must use service account keys (for legacy systems that cannot use Workload Identity Federation), implement strict key lifecycle management. Keys should be stored in a secrets manager, rotated automatically, and audited continuously.
# List all keys for a service account
gcloud iam service-accounts keys list \
--iam-account=my-sa@my-project.iam.gserviceaccount.com \
--format="table(name.basename(), validAfterTime, validBeforeTime, keyType)"
# Find keys older than 90 days across all service accounts
for sa in $(gcloud iam service-accounts list --format="value(email)" --project=my-project); do
echo "--- $sa ---"
gcloud iam service-accounts keys list \
--iam-account="$sa" \
--filter="validAfterTime<-P90D AND keyType=USER_MANAGED" \
--format="table(name.basename(), validAfterTime)"
done
# Create a new key (only if Workload Identity Federation is not possible)
gcloud iam service-accounts keys create key.json \
--iam-account=my-sa@my-project.iam.gserviceaccount.com
# Delete an old key after rotation
gcloud iam service-accounts keys delete KEY_ID \
--iam-account=my-sa@my-project.iam.gserviceaccount.comUse Short-Lived Credentials Instead of Keys
Instead of downloading long-lived keys, use gcloud auth print-access-token for development and Workload Identity Federation for CI/CD. For cross-project service account access, use service account impersonation, which generates short-lived tokens (default 1 hour) rather than permanent keys. This dramatically reduces the window of exposure if a credential is leaked.
Organization Policies
Organization Policies are guardrails enforced at the organization or folder level that restrict what resources can be created, regardless of a user's IAM permissions. While IAM answers “who can do what,” Organization Policies answer “what is allowed at all.” Even an Organization Admin cannot violate an active org policy constraint. This makes organization policies the strongest preventive control in GCP.
Organization policies use constraints, which are either boolean (on/off) or list-based (allow/deny specific values). Constraints are evaluated at the resource level, but policies can be set at the organization, folder, or project level with inheritance. A policy set at a higher level applies to all child resources unless explicitly overridden (which requires theorgpolicy.policyAdmin role).
Key organization policies every team should consider:
| Constraint | Purpose | Recommended Value |
|---|---|---|
constraints/compute.vmExternalIpAccess | Block VMs from having external IPs | Deny All (use Cloud NAT for egress) |
constraints/iam.disableServiceAccountKeyCreation | Prevent downloadable SA keys | Enforce (use Workload Identity instead) |
constraints/gcp.resourceLocations | Restrict resource regions for compliance | Allow only required regions (e.g., us-central1, us-east1) |
constraints/compute.requireShieldedVm | Ensure all VMs use Shielded VM features | Enforce |
constraints/sql.restrictPublicIp | Prevent Cloud SQL from having public IPs | Enforce |
constraints/storage.uniformBucketLevelAccess | Require uniform bucket-level access on all buckets | Enforce (disables ACLs, uses IAM only) |
constraints/iam.allowedPolicyMemberDomains | Restrict IAM bindings to your domain only | Allow only your organization's domain |
# Disable service account key creation across the org
gcloud resource-manager org-policies enable-enforce \
constraints/iam.disableServiceAccountKeyCreation \
--organization=123456789012
# Restrict resource locations to US regions only
cat > /tmp/location-policy.yaml << 'EOF'
constraint: constraints/gcp.resourceLocations
listPolicy:
allowedValues:
- in:us-locations
EOF
gcloud resource-manager org-policies set-policy \
/tmp/location-policy.yaml \
--organization=123456789012
# Restrict IAM bindings to your organization domain only
cat > /tmp/domain-policy.yaml << 'EOF'
constraint: constraints/iam.allowedPolicyMemberDomains
listPolicy:
allowedValues:
- C0xxxxxxx
EOF
gcloud resource-manager org-policies set-policy \
/tmp/domain-policy.yaml \
--organization=123456789012Use Dry Run Mode
Before enforcing a new organization policy, use the --dry-run flag or the Policy Simulator in the console. This shows which existing resources would violate the policy without actually blocking anything. It is invaluable for rolling out policies in brownfield environments where you may have legacy configurations that need to be remediated first.
Custom Organization Policy Constraints
Beyond the 80+ built-in constraints, GCP allows you to create custom organization policy constraints using Common Expression Language (CEL). Custom constraints can enforce rules that built-in constraints do not cover, such as requiring specific labels, enforcing naming conventions, or restricting machine type families.
# Define a custom constraint that requires a 'team' label on all GCE instances
cat > /tmp/custom-constraint.yaml << 'EOF'
name: organizations/123456789012/customConstraints/custom.requireTeamLabel
resourceTypes:
- compute.googleapis.com/Instance
methodTypes:
- CREATE
- UPDATE
condition: "resource.labels.exists(l, l == 'team')"
actionType: ALLOW
displayName: Require team label on VMs
description: All Compute Engine instances must have a 'team' label
EOF
gcloud org-policies set-custom-constraint /tmp/custom-constraint.yaml
# Enforce the custom constraint at the org level
cat > /tmp/enforce-label.yaml << 'EOF'
name: organizations/123456789012/policies/custom.requireTeamLabel
spec:
rules:
- enforce: true
EOF
gcloud org-policies set-policy /tmp/enforce-label.yamlWorkload Identity Federation
Workload Identity Federation allows external identities (from AWS, Azure, GitHub Actions, GitLab CI, or any OIDC/SAML provider) to impersonate GCP service accounts without downloading keys. This eliminates the most common source of credential leaks in CI/CD pipelines. Instead of storing a long-lived JSON key file as a CI/CD secret, the external workload exchanges its native identity token for a short-lived GCP access token.
The flow works as follows:
- Create a Workload Identity Pool that trusts your external provider.
- Create a Provider within the pool that maps external claims to Google attributes.
- Grant the external identity permission to impersonate a GCP service account.
- The external workload exchanges its token for a short-lived GCP access token (valid for 1 hour by default).
# Create the workload identity pool
gcloud iam workload-identity-pools create "github-pool" \
--location="global" \
--display-name="GitHub Actions Pool" \
--project=my-project
# Create the OIDC provider
gcloud iam workload-identity-pools providers create-oidc "github-provider" \
--location="global" \
--workload-identity-pool="github-pool" \
--display-name="GitHub Provider" \
--attribute-mapping="google.subject=assertion.sub,attribute.repository=assertion.repository" \
--issuer-uri="https://token.actions.githubusercontent.com" \
--project=my-project
# Allow a specific repo to impersonate the service account
gcloud iam service-accounts add-iam-policy-binding \
deployer@my-project.iam.gserviceaccount.com \
--role="roles/iam.workloadIdentityUser" \
--member="principalSet://iam.googleapis.com/projects/123456/locations/global/workloadIdentityPools/github-pool/attribute.repository/my-org/my-repo" \
--project=my-projectWorkload Identity Federation for AWS
If you have workloads running in AWS that need to access GCP resources, Workload Identity Federation can accept AWS STS tokens. This is particularly useful for multi-cloud architectures where data pipelines or applications span both providers.
# Create an AWS provider in your workload identity pool
gcloud iam workload-identity-pools providers create-aws "aws-provider" \
--location="global" \
--workload-identity-pool="multi-cloud-pool" \
--account-id="123456789012" \
--project=my-project
# Allow a specific AWS role to impersonate a GCP service account
gcloud iam service-accounts add-iam-policy-binding \
data-pipeline@my-project.iam.gserviceaccount.com \
--role="roles/iam.workloadIdentityUser" \
--member="principalSet://iam.googleapis.com/projects/123456/locations/global/workloadIdentityPools/multi-cloud-pool/attribute.aws_role/arn:aws:sts::123456789012:assumed-role/DataPipelineRole" \
--project=my-projectWorkload Identity for GKE
For workloads running on GKE, use Workload Identity (not Federation). Workload Identity maps Kubernetes service accounts to GCP service accounts, allowing pods to authenticate to GCP APIs without mounting keys. Enable it on the cluster with --workload-pool=PROJECT.svc.id.goog and annotate Kubernetes service accounts with the GCP service account email.
IAM Conditions and Context-Aware Access
IAM Conditions let you add conditional logic to role bindings. A binding only takes effect when the condition evaluates to true. Conditions can reference resource attributes (name, type, tags), request time, and access levels defined in Access Context Manager. This enables granular access control patterns that would otherwise require separate projects or complex service account chains.
Common condition patterns include:
- Time-based access: grant permissions only during business hours for break-glass scenarios. This is useful for on-call engineers who need elevated access only during incident response.
- Resource name prefixes: limit access to Cloud Storage buckets matching a naming convention. For example, allow a data science team to access only buckets starting with
ds-. - Tag-based access: use resource tags to control which environments a developer can modify. Tag production resources differently from staging, then write conditions that restrict access based on tags.
- IP-based access: restrict access to resources based on the source IP address using Access Context Manager access levels. This can enforce that sensitive operations only occur from corporate networks.
# Grant access only to dev-tagged resources
gcloud projects add-iam-policy-binding my-project \
--member="group:dev-team@example.com" \
--role="roles/compute.instanceAdmin.v1" \
--condition='expression=resource.matchTag("12345/environment", "dev"),title=dev-env-only,description=Only allow access to dev-tagged resources'
# Grant access only during business hours (UTC)
gcloud projects add-iam-policy-binding my-project \
--member="group:oncall@example.com" \
--role="roles/container.admin" \
--condition='expression=request.time.getHours("America/New_York") >= 9 && request.time.getHours("America/New_York") <= 17,title=business-hours-only'
# Grant access only to specific bucket name patterns
gcloud projects add-iam-policy-binding my-project \
--member="group:data-science@example.com" \
--role="roles/storage.objectViewer" \
--condition='expression=resource.name.startsWith("projects/_/buckets/ds-"),title=ds-buckets-only'Access Context Manager
Access Context Manager defines access levels based on client attributes such as IP address, device security posture, and identity. These access levels can be referenced in IAM conditions and VPC Service Controls perimeters to create context-aware access policies.
# Create an access policy (one per organization)
gcloud access-context-manager policies create \
--organization=123456789012 \
--title="Corporate Access Policy"
# Create an access level for corporate IP ranges
gcloud access-context-manager levels create corp-network \
--policy=POLICY_ID \
--title="Corporate Network" \
--basic-level-spec=corp-network-spec.yaml
# corp-network-spec.yaml contents:
# conditions:
# - ipSubnetworks:
# - 203.0.113.0/24
# - 198.51.100.0/24
# regions:
# - US
# - CACondition Evaluation Limits
IAM conditions use Common Expression Language (CEL) and have a maximum expression length of 300 characters. Complex conditions may need to be split across multiple bindings. Also note that not all resource types support conditions; check the documentation for your specific service. Conditions on basic roles (Owner, Editor, Viewer) are not supported.
Custom Roles
When predefined roles grant either too many or too few permissions, custom roles let you assemble exactly the permissions your workload needs. Custom roles can be created at the organization or project level. Organization-level custom roles can be granted across any project in the organization, while project-level custom roles are scoped to that project only.
Best practices for custom roles:
- Start with a predefined role and remove permissions rather than building from scratch. This ensures you do not miss permissions that are implicitly required.
- Test in development first: missing permissions cause runtime errors that may not surface until a specific code path is executed.
- Use the IAM Recommender to identify which permissions are actually being used by a service account, then create a custom role from the observed permissions.
- Set a launch stage (ALPHA, BETA, GA) to track the maturity of your custom roles.
# Step 1: Check what permissions a service account actually uses
gcloud recommender insights list \
--insight-type=google.iam.policy.Insight \
--project=my-project \
--location=global \
--filter="content.member:my-sa@my-project.iam.gserviceaccount.com" \
--format="json"
# Step 2: Create a custom role with only the needed permissions
gcloud iam roles create dataProcessorRole \
--project=my-project \
--title="Data Processor" \
--description="Custom role for data processing pipeline" \
--permissions="bigquery.datasets.get,bigquery.tables.getData,bigquery.jobs.create,storage.objects.get,storage.objects.list" \
--stage=GA
# Step 3: Bind the custom role to the service account
gcloud projects add-iam-policy-binding my-project \
--member="serviceAccount:my-sa@my-project.iam.gserviceaccount.com" \
--role="projects/my-project/roles/dataProcessorRole"| Custom Role Consideration | Organization Level | Project Level |
|---|---|---|
| Scope | Usable across all projects in the org | Usable only within the project |
| Limit | 300 custom roles per org | 300 custom roles per project |
| Permissions | Can include any permission | Cannot include org-level-only permissions |
| Best for | Shared roles used by multiple teams | Workload-specific roles |
Policy Intelligence Tools
GCP offers a suite of tools collectively known as Policy Intelligence that help you understand, audit, and optimize your IAM configuration. These tools are essential for maintaining least-privilege access over time, because permissions tend to accumulate (developers request access for one-time tasks and never relinquish it).
IAM Recommender
The IAM Recommender analyzes 90 days of permission usage data and suggests removing unused permissions or replacing broad roles with narrower ones. Recommendations are generated automatically and appear in the console, or you can query them via the Recommender API.
# List IAM role recommendations for a project
gcloud recommender recommendations list \
--recommender=google.iam.policy.Recommender \
--project=my-project \
--location=global \
--format="table(content.operationGroups[0].operations[0].pathFilters, priority, stateInfo.state)"
# Apply a recommendation (removes unused permissions)
gcloud recommender recommendations mark-claimed RECOMMENDATION_ID \
--recommender=google.iam.policy.Recommender \
--project=my-project \
--location=global \
--etag=ETAG_VALUEPolicy Analyzer
Policy Analyzer answers the question “who can access this resource and why?” It evaluates all IAM bindings across the hierarchy, including inherited permissions, conditional bindings, and group memberships. This is invaluable for incident response (“who had access to this bucket when the breach occurred?”) and compliance audits.
# Find all identities that can read a Cloud Storage bucket
gcloud asset analyze-iam-policy \
--organization=123456789012 \
--full-resource-name="//storage.googleapis.com/projects/_/buckets/sensitive-data" \
--permissions="storage.objects.get" \
--format="table(analysisResults.identityList.identities.name, analysisResults.accessControlLists.accesses.permission)"
# Find all resources a specific user can access
gcloud asset analyze-iam-policy \
--organization=123456789012 \
--identity="user:alice@example.com" \
--format="table(analysisResults.attachedResourceFullName, analysisResults.accessControlLists.accesses.role)"Policy Troubleshooter
When a user reports “access denied,” the Policy Troubleshooter explains exactly why the request was denied. It shows which policies were evaluated, which bindings were checked, and which conditions failed. This eliminates guesswork from access debugging.
Automate Policy Intelligence Reviews
Schedule a monthly Cloud Scheduler job that triggers a Cloud Function to pull IAM Recommender suggestions, aggregate them, and send a summary to your security team's Slack channel or email. This creates a regular cadence for tightening permissions without requiring manual effort to check the console.
Recommended IAM Architecture
A well-structured GCP IAM setup uses the resource hierarchy as a force-multiplier. Here is a proven layout that balances security, manageability, and developer productivity:
Organization Level
The organization level should have the tightest access controls. Only a small number of trusted administrators should have roles at this level. Typical bindings include:
- Organization Admin (2-3 people): manages the org node itself, sets up folders, and delegates administration.
- Security Admin (security team): manages organization policies, Security Command Center, and audit configurations.
- Billing Admin (finance/ops): manages billing accounts and cost controls.
- Audit Viewer (compliance team): read-only access to audit logs and IAM policies across the organization.
Folder Level
Create folders by environment (Production, Staging, Development) or by business unit. Grant team roles at the folder level so they automatically apply to all projects within. This is where most team-level access should be configured.
Project Level
Isolate workloads into separate projects. A microservice with its Cloud Run service, Cloud SQL database, and Pub/Sub topic should live in one project. Grant service-specific roles here. Service accounts should be created at this level with the minimum permissions needed for their specific workload.
Resource Level
Use resource-level IAM bindings only when you need to share a single resource across projects (e.g., a shared BigQuery dataset or a GCS bucket). Avoid excessive resource-level bindings as they are harder to audit than project or folder-level bindings.
# Folder structure
resource "google_folder" "production" {
display_name = "Production"
parent = "organizations/123456789012"
}
resource "google_folder" "staging" {
display_name = "Staging"
parent = "organizations/123456789012"
}
resource "google_folder" "development" {
display_name = "Development"
parent = "organizations/123456789012"
}
# Grant the dev team access at the Development folder level
resource "google_folder_iam_binding" "dev_team_editor" {
folder = google_folder.development.name
role = "roles/editor"
members = ["group:dev-team@example.com"]
}
# Grant the SRE team access at the Production folder level
resource "google_folder_iam_binding" "sre_team_admin" {
folder = google_folder.production.name
role = "roles/compute.admin"
members = ["group:sre-team@example.com"]
}
# Grant read-only access to production for developers
resource "google_folder_iam_binding" "dev_team_viewer" {
folder = google_folder.production.name
role = "roles/viewer"
members = ["group:dev-team@example.com"]
}Group-Based Access
Always assign roles to Google Groups rather than individual users. This makes access auditable, reviewable, and easy to revoke. When someone leaves a team, removing them from the group instantly removes all associated permissions across the entire hierarchy. Use Google Groups with Cloud Identity or Google Workspace, and require manager approval for group membership changes.
VPC Service Controls
VPC Service Controls (VPC-SC) create a security perimeter around GCP services that prevents data exfiltration even by users with valid IAM credentials. While IAM controls who can access a resource, VPC-SC controls the network context from which that access is allowed. A user might have roles/bigquery.dataViewer, but if they are outside the VPC Service Controls perimeter, their request is denied.
VPC-SC is essential for organizations handling sensitive data (PII, financial data, healthcare records) because it prevents a common attack pattern: an attacker obtains valid credentials and exfiltrates data to their own GCP project. With VPC-SC, the data cannot leave the perimeter regardless of the attacker's credentials.
# Create a service perimeter
gcloud access-context-manager perimeters create prod-perimeter \
--title="Production Perimeter" \
--resources="projects/111111,projects/222222" \
--restricted-services="bigquery.googleapis.com,storage.googleapis.com,secretmanager.googleapis.com" \
--access-levels="accessPolicies/POLICY_ID/accessLevels/corp-network" \
--policy=POLICY_ID
# Add an ingress rule to allow specific access patterns
gcloud access-context-manager perimeters update prod-perimeter \
--set-ingress-policies=ingress-policy.yaml \
--policy=POLICY_IDTest VPC-SC in Dry Run Mode First
VPC Service Controls can break legitimate access patterns if not configured carefully. Always start with a dry-run perimeter that logs violations without enforcing them. Monitor the dry-run logs for 2-4 weeks to identify all legitimate access patterns, then create ingress and egress rules for them before switching to enforcement mode.
Audit Logging and Monitoring
GCP Cloud Audit Logs capture three types of logs that are critical for security monitoring and compliance:
- Admin Activity logs: Record API calls that modify resources (create, update, delete). Always enabled, no additional cost, retained for 400 days.
- Data Access logs: Record API calls that read resource data or metadata. Must be explicitly enabled, can generate high volume and cost. Enable selectively for sensitive services.
- System Event logs: Record GCP system actions (like live migration of VMs). Always enabled, no additional cost.
# Enable data access logs for Cloud Storage, BigQuery, and Secret Manager
cat > /tmp/audit-policy.json << 'EOF'
{
"auditConfigs": [
{
"service": "storage.googleapis.com",
"auditLogConfigs": [
{ "logType": "DATA_READ" },
{ "logType": "DATA_WRITE" }
]
},
{
"service": "bigquery.googleapis.com",
"auditLogConfigs": [
{ "logType": "DATA_READ" },
{ "logType": "DATA_WRITE" }
]
},
{
"service": "secretmanager.googleapis.com",
"auditLogConfigs": [
{ "logType": "DATA_READ" },
{ "logType": "DATA_WRITE" }
]
}
]
}
EOF
# Apply the audit policy at the organization level
gcloud organizations set-iam-policy 123456789012 /tmp/audit-policy.json --merge
# Create a log sink to BigQuery for long-term analysis
gcloud logging sinks create iam-audit-sink \
bigquery.googleapis.com/projects/security-project/datasets/audit_logs \
--organization=123456789012 \
--include-children \
--log-filter='logName:"cloudaudit.googleapis.com"'IAM Security Checklist
Use this checklist to assess and improve your GCP IAM posture. Review it quarterly and track improvements over time:
| Category | Check | Priority |
|---|---|---|
| Roles | No basic roles (Owner/Editor/Viewer) in production projects | Critical |
| Service Accounts | No user-managed keys; using Workload Identity Federation | Critical |
| Org Policies | Key constraints enforced (no external IPs, no SA keys, location restrictions) | High |
| Groups | All IAM bindings use groups, not individual users | High |
| Audit | Data access logs enabled for sensitive services | High |
| Recommender | IAM Recommender suggestions reviewed monthly | Medium |
| VPC-SC | VPC Service Controls perimeter around sensitive projects | Medium |
| Conditions | Sensitive roles use IAM conditions (time, tag, or resource-based) | Medium |
IAM Is an Ongoing Practice
IAM configuration is never “done.” As teams grow, projects multiply, and workloads evolve, permissions drift from least-privilege. Build IAM hygiene into your operational rhythm: monthly recommender reviews, quarterly access audits, and annual architecture reviews. The cost of maintaining good IAM is far less than the cost of a breach caused by over-permissioned credentials.
Key Takeaways
- 1GCP IAM uses a resource hierarchy: Organization > Folders > Projects > Resources.
- 2Prefer predefined roles over basic (primitive) roles and never use roles/editor in production.
- 3Service accounts should be dedicated per workload with minimum necessary permissions.
- 4Workload Identity Federation eliminates the need for service account keys.
- 5Organization policies enforce guardrails regardless of IAM permissions.
- 6Use IAM Recommender and Policy Analyzer for continuous permission optimization.
Frequently Asked Questions
What is the difference between IAM and Organization Policies in GCP?
Should I use basic roles in GCP?
What is Workload Identity Federation?
How do I manage service accounts securely?
What organization policies should every GCP org enforce?
Written by CloudToolStack Team
Cloud engineers and architects with hands-on experience across AWS, Azure, and GCP. We write guides based on real-world production patterns, not just documentation rewrites.
Disclaimer: This guide is for educational purposes. Cloud services change frequently; always refer to official documentation for the latest information. AWS, Azure, and GCP are trademarks of their respective owners.