fix(deploy): manage Cloud Resource Manager API enablement in Pulumi#1213
Merged
fix(deploy): manage Cloud Resource Manager API enablement in Pulumi#1213
Conversation
The IAM bindings added in #1211 fail on a fresh GCP project because they depend on the Cloud Resource Manager API being enabled, which isn't a default for new projects. Staging deploy hit this directly; the manual `gcloud services enable cloudresourcemanager.googleapis.com` command unblocks it but isn't drift-protected. - Add a projects.Service resource for cloudresourcemanager.googleapis.com with DisableOnDestroy=false so Pulumi adopts the API enablement and re-enables it on accidental disable, but never tears it down on its own. - Make the node-SA IAM bindings explicitly DependsOn the API service so the dependency is encoded rather than implicit. - Replace organizations.LookupProject (CRM-dependent) with compute.GetDefaultServiceAccount, which uses the Compute API (already required for GKE) and returns the SA email directly. Removes the bootstrap chicken-and-egg between the API enablement and the project number lookup that the original code performed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extends the CRM API enablement to also encode container, compute, logging, and monitoring as Pulumi-managed projects.Service resources, all with DisableOnDestroy=false so a Pulumi destroy never disables a shared API. Same drift-protection / new-project bootstrap reasoning as CRM. Refactors the API enablement into a small ensureRequiredAPIs helper that returns the CRM resource so the IAM bindings can DependsOn it explicitly. Also updates deploy/README.md to: - List `roles/resourcemanager.projectIamAdmin` as a required Pulumi SA role. Without it the deploy fails with "Error retrieving IAM policy ... forbidden" when Pulumi tries to create the node-SA IAM bindings. This was the second prereq we hit after the CRM API enablement. - Reduce the bootstrap API list to the three actually required to first reach Pulumi (storage for the state bucket, cloudresourcemanager for the IAM bindings, container for the GKE cluster). The rest are adopted by ensureRequiredAPIs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Addresses review feedback on the ensureRequiredAPIs helper: - Create the CRM service explicitly outside the loop instead of detecting it via a string match on the resource name. The previous shape silently broke the IAM-binding DependsOn if anyone renamed "crm-api" — extracting it makes the data-flow obvious and removes the accumulator variable. - Reword the helper doc to acknowledge that bootstrap APIs (storage, cloudresourcemanager, container) are adopted *after* their consumers run, since those resources are created earlier in the stack. The previous wording overclaimed that the helper ensures-then-uses every API, which only matches reality for logging/monitoring. - Drop the misleading "rough dependency order" sentence — the loop resources have no DependsOn between them, so there's no order to speak of. No functional change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
tadasant
approved these changes
Apr 27, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Follow-up to #1211 to encode the GCP API and IAM dependencies its node-SA bindings rely on. After #1211 merged, the staging deploy hit two undocumented prereqs in sequence — Cloud Resource Manager API not enabled (because
projects.NewIAMMembercalls SetIamPolicy through CRM), then the Pulumi service account missingroles/resourcemanager.projectIamAdmin. Both fixed manually to unblock; this PR makes them explicit so they don't bite again.Changes
ensureRequiredAPIsadopts five GCP APIs as Pulumi-managedprojects.Serviceresources, all withDisableOnDestroy: falseandDisableDependentServices: falseso a Pulumi destroy/refactor can never disable a shared API:cloudresourcemanagerprojects.NewIAMMember→ SetIamPolicycomputecompute.GetDefaultServiceAccountinvoke + GKEcontainerloggingmonitoringCRM is created explicitly (not in the loop) so callers get a direct reference for
pulumi.DependsOn. Storage is intentionally not Pulumi-managed — Pulumi state itself lives in a GCS bucket, chicken-and-egg.grantNodeServiceAccountRolesnowDependsOnthe CRMprojects.Servicefor the four IAM bindings, and usescompute.GetDefaultServiceAccountto derive the SA email (Compute API was already required for GKE — avoids a CRM-dependent project-number lookup).deploy/README.md— addsroles/resourcemanager.projectIamAdminto the Pulumi SA's required roles and clarifies which APIs must be enabled before the firstpulumi up(storage, cloudresourcemanager, container) vs. whichensureRequiredAPIsadopts.Notes
projects.Serviceis idempotent against already-enabled APIs — on existing projects (staging/prod), Pulumi adopts the live enablement into state without re-enabling. This was confirmed live: staging deploy went green after the manual unblocks above, validating the runtime model.Test plan
go build ./...clean fordeploy/golangci-lint runclean fordeploy/pkg/providers/gcp/...(one pre-existingnilnilat line 61, unrelated)🤖 Generated with Claude Code