Advanced IAM: Custom Roles and IAM Conditions
Scenario
The predefined `roles/storage.admin` role grants too much power. You want a "Bucket Manager" role that can create and delete buckets, but cannot read or modify the actual data objects inside them. Furthermore, you want to grant this role to a developer, but implement a Time-Based IAM Condition so the permission automatically expires at the end of the work day (e.g. 5:00 PM on Friday).
- Create a google_project_iam_custom_role holding exactly these 3 permissions: `storage.buckets.get`, `storage.buckets.create`, `storage.buckets.delete`.
- Fetch a test user via variables, or provision a test google_service_account.
- Assign the custom role to the identity at the project level using google_project_iam_member.
- Inside the IAM binding, insert a condition block written in Common Expression Language (CEL) restricting the validity window using request.time < timestamp(...).
Additional Context
Custom Roles: Google cautions against Custom Roles unless absolutely necessary because they are not automatically updated when Google releases new GCP features. If a new API endpoint is created for Buckets, your Custom Role won't have it unless you manually add it.
IAM Conditions: These use the CEL language. They can evaluate time (`request.time`), resource names (`resource.name.startsWith`), and network IPs (for IAP). Note that not all GCP resource types fully support conditional IAM bindings yet.
variables.tf
variable "project_id" { type = string }main.tf
provider "google" {
project = var.project_id
}
# 1. The Custom IAM Role (Project-Level)
resource "google_project_iam_custom_role" "bucket_manager" {
role_id = "bucketManager" # Must be camelCase or snake_case, no dashes
title = "Bucket Infrastructure Manager"
description = "Can manage GCS buckets, but cannot read or edit objects."
permissions = [
"storage.buckets.get",
"storage.buckets.create",
"storage.buckets.delete",
"storage.buckets.update"
]
}
# 2. A test identity that needs this temporary access
resource "google_service_account" "temp_worker" {
account_id = "temporary-contractor"
display_name = "Third Party Contractor"
}
# 3. Project IAM Binding with a Condition
resource "google_project_iam_member" "conditional_binding" {
project = var.project_id
# Reference the custom role explicitly via its self_link or formatted name
role = google_project_iam_custom_role.bucket_manager.name
member = "serviceAccount:${google_service_account.temp_worker.email}"
# Time-based expiration condition using CEL
condition {
title = "expires_end_of_2025"
description = "Automatically revokes access at midnight Jan 1, 2026 UTC"
expression = "request.time < timestamp('2026-01-01T00:00:00Z')"
}
}Workflow Commands
terraform init
terraform apply -var="project_id=my-gcp-project-123"
# Verify the policy in the console or via CLI:
gcloud projects get-iam-policy my-gcp-project-123 \
--flatten="bindings[].condition" \
--format="json"
terraform destroy -var="project_id=my-gcp-project-123"
