Local Testing — Azure (floci-az)
floci-az emulates Azure’s resource CRUD but not the Microsoft.Resources/deployments provider, so az deployment has nothing to talk to. azApply closes the gap: it reads the ARM template chant synthesizes and PUTs each resource straight to floci-az’s ARM resource endpoints, in dependency order, resolving ARM expressions from live responses as it goes.
The resource
Section titled “The resource”A storage account, described as data. It synthesizes to an ARM template:
// src/azure/infra.ts — StorageAccountSecure is a composite (secure defaults)export const { storageAccount } = StorageAccountSecure({ name: "triostore", location: "eastus", sku: "Standard_LRS", tags: { project: "cross-cloud-demo" },});The op
Section titled “The op”chant run azure boots floci-az, builds the ARM template, applies it with azApply, verifies the account over the ARM endpoint, and removes the container.
export default Op({ name: "azure", taskQueue: "trio-azure", phases: [ phase("Emulator", [flociAzUp()]), phase("Build", [build(".", { script: "build:azure" })]), phase("Apply", [azApply("dist/azure.json", { resourceGroup: "trio-rg", location: "eastus", endpoint: "http://localhost:4577" })]), phase("Verify", [httpCheck("http://localhost:4577/subscriptions/.../providers/Microsoft.Storage/storageAccounts/triostore?api-version=2025-06-01")]), phase("Teardown", [flociAzDown()]), ],});How azApply works
Section titled “How azApply works”az deployment submits a whole template to the deployments provider and lets Azure order and resolve it. floci-az has no such provider, so azApply does that work itself:
- Reads the ARM template chant emitted and ensures the resource group exists.
- Orders resources by their
resourceId/referencereferences — a referenced resource applies before the one that references it — and detects cycles. - Evaluates ARM expressions as it applies:
resourceGroup(),subscription(),concat,uniqueString,resourceId(...), plus the runtime-state functionsreference('name')andlistKeys(id, apiVersion), resolved from the live PUT responses of resources applied earlier in the run. - PUTs each resource to
{endpoint}{resourceId}?api-version=...and captures the response so later resources can reference it.
This is the same evaluation a real ARM deployment performs; azApply just runs it client-side against the emulator’s resource CRUD.
Run it
Section titled “Run it”Requires Docker and curl on PATH.
cd examples/local-cloud-trionpm installchant run azureDeleting and pruning
Section titled “Deleting and pruning”azDelete is the inverse of azApply: it reads the same template and deletes the resources it declares, in reverse dependency order (a referrer goes before what it references), idempotently.
azApply also takes prune: true for an owned-only prune — after applying, it deletes chant-managed resources of a templated type whose name is no longer in the template. chant stamps a managed-by: chant tag on everything it applies and only ever prunes tagged resources, so a foreign resource sharing the group is never touched.
From local to real cloud
Section titled “From local to real cloud”Drop the endpoint override and azApply targets real Azure ARM. The template, the ordering, and the expression evaluation are unchanged — only the resource-manager host differs.