Skip to content

fix(api): extend federation source-identity fail-loud guard to Azure and GCP deployments #41

@cristim

Description

@cristim

Summary

The federation IaC handler has a fail-loud guard for empty AWS source account IDs but not for empty Azure/GCP source identities. When AZURE_SUBSCRIPTION_ID, AZURE_TENANT_ID, or GCP_PROJECT_ID are unset on the Lambda, the rendered IaC bundle ships with empty identity fields and customers hit a cryptic terraform apply error.

Captured in: known_issues/25_federation_azure_gcp_source_identity_failloud.md

Current behaviour

internal/api/handler.go::resolveSourceIdentity (≈line 446) reads Azure/GCP identity from environment variables. If any are unset, empty strings flow into the generated tfvars/scripts and the federation bundle downloads successfully. The apply-time failure is a blank client_id / missing project — the operator has no clear signal that CUDly's own env is misconfigured.

The AWS source path in internal/api/handler_federation.go::populateSourceAccountID (≈line 171) already fails loud:

func (h *Handler) populateSourceAccountID(ctx context.Context, source string, data *federationIaCData) error {
    if sourceCloud() != "aws" {
        return nil
    }
    data.SourceAccountID = h.resolveSourceAccountID(ctx)
    if source == "aws" && data.SourceAccountID == "" {
        return fmt.Errorf("federation iac: CUDly failed to resolve its own AWS account ID; ...")
    }
    return nil
}

The Azure/GCP equivalent is missing — populateSourceAccountID returns nil immediately for non-AWS source clouds, and nothing else validates the source identity before rendering the bundle.

Expected behaviour

The same fail-loud treatment for Azure and GCP deployments, naming the missing env var so the operator knows exactly what to fix.

Steps to reproduce

  1. Deploy CUDly on Azure (or GCP).
  2. Unset AZURE_SUBSCRIPTION_ID on the Lambda/Function App (or GCP_PROJECT_ID on Cloud Run).
  3. From the UI, download a federation bundle.
  4. Observe: bundle downloads 200 OK, terraform apply later fails with blank client_id / missing project — no actionable error at download time.

Proposed fix

Extend populateSourceAccountID (or add a sibling populateSourceIdentity) to cover Azure and GCP. Sketch:

if sourceCloud() == "azure" {
    id := h.resolveSourceIdentity(ctx)
    if id.SubscriptionID == "" || id.TenantID == "" {
        return fmt.Errorf("federation iac: AZURE_SUBSCRIPTION_ID or AZURE_TENANT_ID is not set; check the Lambda environment variables")
    }
}
if sourceCloud() == "gcp" {
    id := h.resolveSourceIdentity(ctx)
    if id.ProjectID == "" {
        return fmt.Errorf("federation iac: GCP_PROJECT_ID is not set; check the Lambda environment variables")
    }
}

Test plan

Four tests in internal/api/handler_federation_test.go that mock resolveSourceIdentity to return empty fields and assert a non-client-error 500 response is surfaced:

  • Azure: SubscriptionID="" → 500
  • Azure: TenantID="" → 500
  • GCP: ProjectID="" → 500
  • Positive control: all fields populated → 200

References

  • Companion commit: fix(api): pre-fill ContactEmail from Session.Email and fail loud on empty SourceAccountID (the AWS-only fix this extends).
  • Files: internal/api/handler_federation.go::populateSourceAccountID, internal/api/handler.go::resolveSourceIdentity.
  • Known-issue doc: known_issues/25_federation_azure_gcp_source_identity_failloud.md.

Severity

Medium — no customer data loss, but an operator misconfiguration leaks through to apply-time errors that look like a CUDly bundle bug.

Effort

Small — ~5 LOC + 4 tests.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions