Cloud services

Blog post

One quiet UUID holds a site together

Git, identity, storage, and Front Door all need a durable way to agree on “this site.” We use one project id as that join key—never as a password, and never as a substitute for real authorization.

A shared $web cabinet contains project UUID compartments, with xyz.tld and preview.xyz.tld each split into public and state siblings; Front Door routes only to public while a separate static-origin entrance remains directly reachable

A site’s visitor files live at $web/<PROJECT_ID>/<fqdn>/public/; workflow state lives beside them at $web/<PROJECT_ID>/<fqdn>/state/. Production and environment hostnames occupy separate pairs beneath the same project id. Front Door still matches inbound /*, but its fixed origin path is /<PROJECT_ID>/<fqdn>/public.

  1. Project id One immutable UUID minted with the site and kept in Terraform state.
  2. Hostname folders Production plus its provisioned environment hosts live together beneath that UUID; each has separate public/ and state/ children.
  3. Storage boundary Attribute-based access control permits the uploader to write, delete, add, and move only paths beginning with its UUID.

What the id does—and what it does not

The UUID gives an anonymous visitor one more unguessable segment on the public website endpoint, but that is only obscurity. It is not a credential. Storage authorization comes from the uploader’s federated identity and its $web/<PROJECT_ID>/* path condition. Purge authorization comes from platform-owned Front Door inventory: the caller repository and requested FQDN must both be custom domains on exact /<PROJECT_ID>/<fqdn>/public routes.

We still store PROJECT_ID as a GitHub secret so it is not casually printed as a workflow input. We also stamp the same id on the Entra application metadata, allowing operators to join identity back to storage without consulting tenant git.

Why it is not the client id

The application id describes an identity. The project id describes the site and its storage layout. Keeping those concepts separate makes replacement explicit: rebuilding the site creates a new uploader application and, for a genuinely new leaf, a new prefix. We do not bury files under an identity that may be replaced for unrelated reasons.

A secret also shapes the workflow boundary

Because the project id is stored as a GitHub secret, GitHub may suppress a job output whose value contains it. The durable-queue action therefore keeps storage blob names such as <PROJECT_ID>/<fqdn>/public/… inside one execution boundary: it lists origin, writes and verifies state, and deletes dropped durable blobs there. What leaves the action is the safe, FQDN-relative purge list—paths such as /index.html—plus local metadata that does not embed the id. Secret masking is log hygiene rather than authorization, but respecting it also makes the dataflow reliable.

Helpful glue, not magical isolation

A one-to-one relationship between repository, uploader, and prefix makes onboarding and teardown pleasantly mechanical. It does not partition the shared storage account or Front Door profile. The prefix-scoped storage condition and the platform-run route checks are the boundaries that matter. Tenant-writable folder names are never accepted as host authorization.

state/version.txt stores the UTC timestamp, git SHA, and deploy date outside the public route. state/to-purge-YYYYMMDD.txt makes unfinished invalidation durable; it keeps its original date, is cleared only after purge and verification succeed, and is never a lifecycle-cleanup target.

The storage condition covers write, delete, add, and move. The contributor role still includes broader read/list and container operations; narrowing those actions is part of the planned custom role. The UUID makes the condition simpler, but it does not make an over-broad role disappear.

Next: what deploys put inside the prefix, or return to all posts.