TDD-driven infrastructure for a local Azure cloud stack: floci-az as Azure emulator.
| Module | Purpose | Tests |
|---|---|---|
resource-group |
Azure Resource Group | 3 |
storage |
Blob Storage (account + container) | 2 |
key-vault |
Key Vault + secrets | 2 |
container-registry |
Azure Container Registry (ACR) | 2 |
cosmos-db |
Cosmos DB account | 2 |
local env |
Full stack integration | 1 |
12 test runs. All passing. Via the Dagger pipeline: dagger call test-modules from pipeline/.
- floci-az (port 4577) — Azure emulator (Resource Manager, Storage, Key Vault, ACR, Cosmos DB)
# Start the stack
./scripts/up.sh
# Run the full CI pipeline (Dagger Go module)
cd pipeline
dagger call fmt-check
dagger call validate-modules
dagger call test-modules
dagger call integration-planThe Dagger pipeline at pipeline/ runs OpenTofu format check, schema validation, contract tests, and infrastructure plan — all inside isolated containers. Written in Go using the Dagger SDK v0.21.7.
The modules are tested against floci-az (an Azure emulator) but are provider-agnostic — they work with real Azure unchanged.
- An Azure subscription with a Service Principal (client ID + client secret)
- Azure CLI configured (
az login)
-
Copy the real Azure environment template:
cp -r infrastructure/opentofu/environments/real-azure infrastructure/opentofu/environments/staging
-
Edit
environments/staging/main.tf— uncomment the backend section and set your state storage account. -
Plan against real Azure (credentials never touch your filesystem — passed as Dagger secrets):
cd pipeline dagger call azure-plan \ --azure-client-id=env:ARM_CLIENT_ID \ --azure-client-secret=env:ARM_CLIENT_SECRET \ --azure-tenant-id=env:ARM_TENANT_ID \ --azure-subscription-id=env:ARM_SUBSCRIPTION_ID \ --env=staging -
Review the plan output. If it looks correct, apply:
dagger call azure-apply \ --azure-client-id=env:ARM_CLIENT_ID \ --azure-client-secret=env:ARM_CLIENT_SECRET \ --azure-tenant-id=env:ARM_TENANT_ID \ --azure-subscription-id=env:ARM_SUBSCRIPTION_ID \ --env=staging
Important: azure-apply is intentionally gated — it never runs automatically in CI. Plans are read-only; apply requires an explicit local dagger call with human confirmation.
The pipeline functions accept Azure credentials as Dagger Secret objects — they are never written to disk, never logged, and never exposed in the container's environment to other processes. The same modules, the same tests, the same pipeline. Only the environment directory and provider config change.
The Philosophical Developer — local cloud chapters: