LAB41_GCP_Advanced IAM: Custom Roles and IAM Conditions

Advanced IAM: Custom Roles and IAM Conditions

☁ GCP 🔥 Intermediate IAM Custom Roles Conditions Security

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).

Your Objectives
  • 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"