GCP IAM: Same Name, Different Game
Spending a day getting a Rails app onto Google Cloud Run turned into an unexpected deep dive into Google Cloud's identity and access management. The concepts aren't hard on their own—it's the terminology that keeps tripping you up. If you're coming from AWS, the word "role" means something entirely different in GCP.
AWS and GCP: Translation Guide
Years of AWS IAM conditioning don't help much here. In AWS, the workflow is clean: create an IAM role, attach permissions to that role, and you're done. Google Cloud splits that idea into two separate concepts that don't map one-to-one:
- A GCP IAM role is closer to an AWS permission. For example,
roles/cloudsql.clientgrants access to SQL instances. It doesn't define an identity. - A GCP service account is the identity, roughly analogous to an AWS IAM role. It's the thing that acts on behalf of your service.
So when someone says "IAM role" in GCP, they're talking about a bundle of permissions, not a deployable identity. It's a small vocabulary shift with big practical consequences.
Binding Roles to Identities Isn't One-Size-Fits-All
In AWS, attaching a permission to an identity is uniform—one Terraform resource handles it. In GCP, there's no single "attach this role to this service account" operation. Instead, the method varies by resource type, which became painfully clear while trying to grant a service account access to a secret.
The gcloud command pattern follows a predictable formula for most resource types:
gcloud GROUP add-iam-policy-binding RESOURCE --member=MEMBER --role=ROLE-ID
Applied to Secret Manager, that becomes:
gcloud beta secrets add-iam-policy-binding rails-master-key
--member=serviceAccount:[email protected]
--role=roles/secretmanager.secretAccessor ```
And the equivalent Terraform is:
resource "google_secret_manager_secret_iam_binding" "binding" {
project = "refrigerator-poetry"
secret_id = google_secret_manager_secret.railsmaster.secret_id
role = "roles/secretmanager.secretAccessor"
members = [
"serviceAccount:${google_service_account.fridge.email}"
]
}
That pattern works—until it doesn't.
SQL Instances Break the Pattern
Attempting to use the same approach for a Cloud SQL database hits a dead end: gcloud sql instances add-iam-policy-binding doesn't exist, and neither does gcloud sql add-iam-policy-binding. SQL instances require a completely different mechanism for access control.
Thoroughly stuck late at night, the pragmatic escape hatch was granting the service account roles/editor at the project level:
gcloud projects add-iam-policy-binding PROJECT_NAME \
--member=serviceAccount:account-email@whatever --role=roles/editor
That got things running, but it's a blunt instrument. The proper path for scoping SQL instance access remains unclear—it's clearly a separate workflow, just not one that was discoverable from the familiar add-iam-policy-binding pattern.
Terraform Proliferation: ERB Behind the Scenes
The Terraform GCP provider reveals how deep this fragmentation goes. AWS has three core resources (aws_iam_role, aws_iam_policy, aws_iam_role_policy). GCP has dozens of near-identical resources—one for each resource type—each combining role assignment and policy management:
google_folder_iam_binding
google_healthcare_dataset_iam_binding
google_healthcare_dicom_store_iam_binding
google_healthcare_fhir_store_iam_binding
google_iap_tunnel_iam_binding
google_iap_tunnel_instance_iam_binding
google_iap_web_backend_service_iam_binding
google_iap_web_iam_binding
google_iap_web_type_compute_iam_binding
google_kms_crypto_key_iam_binding
google_kms_key_ring_iam_binding
google_project_iam_binding
Want to give a service account access to a google_iap_web_backend? Use a google_iap_web_backend_service_iam_binding. The pattern holds, but the proliferation is staggering.
The generation method is the interesting part. All these resources don't come from hand-written Go—they're templated. A provider.go.erb template in the magic-modules repository generates the giant Go program that defines this entire family of resources. ERB, a Ruby templating language, is doing the heavy lifting for a Go codebase.
Still Searching for the Big Picture
Four hours of hands-on time isn't enough to fairly judge GCP's model—there may well be architectural benefits that only become clear with more experience. But from where it stands, the approach feels scattered. A single source of truth listing everything a service account can do would go a long way. So far, that doesn't appear to exist. Maybe it will surface with more familiarity—or maybe the answer is just accepting that GCP's identity model demands a different way of thinking.



