Infrastructure as One Stack
Every Azure resource behind ALERTCalifornia — the web apps and function apps, the storage accounts, Cosmos and SQL, App Configuration and Key Vault, SignalR, Event Hubs, Front Door, and all the monitoring — is defined as code in this repository, and stamped out identically to five environments. Nothing is clicked into existence in the portal. This section is the guide to that code: how it's organized, how it deploys, and how to change it safely.
If you've worked in application repos, the mental shift is this: there are no "requests" here, only desired state. You describe what Azure should look like; Terraform computes the diff against reality and makes it so. Everything below serves that one idea.
The three pieces
The infrastructure is not one big file — it's three cooperating layers:
- The root stack (
alertca/) is the composition — it decides which resources exist, wired together with identities, roles, and configuration. It's organized by domain, not by resource type (more on that next). - The reusable modules (
Terraform.Module.Azure.*) are separate, version-pinned repositories that each know how to build one kind of Azure resource well. The root stack consumes them; it rarely writes rawazurerm_*resources itself. - The pipelines (
devops/) run Terraform against the stack for a chosen environment —init,plan, andapply— with approvals between environments.
Organized by domain, not by resource type
Open alertca/ and you won't find storage.tf, network.tf, compute.tf. You'll find
domain pairs — a locals.<domain>.tf that holds a domain's configuration as data, and a
main.<domain>.tf that turns that data into resources:
| Domain pair | Owns |
|---|---|
locals.backend.tf · main.backend.tf | The backend/admin APIs (web apps) |
locals.functions.tf · main.functions.tf | The Azure Function apps |
locals.storage.tf · main.storage.tf | Storage accounts, containers, queues, tables |
locals.databases.tf · main.database.tf | Cosmos DB and SQL |
locals.appconfig.tf · main.appconfig.tf | App Configuration keys and Key Vault references |
locals.identities.tf · main.roles.tf | Managed identities and RBAC |
| … and appinsights, eventhub, frontdoor, keyvault, monitor, mock, scheduler, signalr |
Cross-cutting files sit alongside them: locals.tf (naming and tags), variables.tf (the
typed input contract), data.*.tf (data sources), outputs.tf (values downstream systems
read), provider.tf, and terraform.tf (versions and the remote state backend).
The stack, deployed five ways
The same code produces five environments — hub, sbx, dev, uat, prd — using Terraform
workspaces over a single remote state backend, with per-environment envs/*.tfvars
overrides and one Azure subscription each. How that works, and how a change is promoted from
dev to production, is a lesson of its own.
The reading path
This section builds from "how it's shaped" to "how to change it safely":
- The Domain Pattern — how one domain wires config → resources → identity → RBAC → config.
- Reusable Modules — the module catalog, the contract, and how to add one.
- Environments & Deployment — workspaces, state, tfvars, and the pipeline.
- Adding a Component — the end-to-end workflow, step by step.
- Naming, Identity & RBAC — the conventions that keep 200+ resources coherent.
- State, Pinning & Gotchas — remote state, version pinning, and the sharp edges.
Wrapping up
The ALERTCalifornia infrastructure is one domain-organized Terraform stack that composes version-pinned reusable modules and deploys to five workspace-backed environments through Azure DevOps pipelines. The whole thing is desired state: you edit the description, Terraform reconciles Azure to match. Start with the pattern every domain follows: The Domain Pattern.