import { type ApplyResult } from "@intentius/chant/apply"; /** One ARM resource from a `deploymentTemplate.json` `resources[]`. */ export interface ArmResource { type: string; apiVersion: string; name: string; location?: string; properties?: unknown; sku?: unknown; kind?: unknown; tags?: Record; dependsOn?: unknown; } /** Injectable HTTP client — mirrors the GCP applier so tests avoid the network. */ export type AzHttp = (method: string, url: string, body?: unknown, signal?: AbortSignal) => Promise<{ status: number; text: string; }>; /** * Context for evaluating ARM template expressions. `deployed` holds the response * bodies of resources already applied this run (keyed by evaluated name) so * `reference()` resolves; `http`/`base` let `listKeys()` call the resource's * key action. */ export interface ArmEvalCtx { subscriptionId: string; resourceGroup: string; location: string; deployed: Map; http: AzHttp; base: string; signal?: AbortSignal; } /** Evaluate an ARM expression string (`"[...]"`); a plain string is returned as-is. */ export declare function evalArmString(s: string, ctx: ArmEvalCtx): Promise; /** Recursively evaluate every string in a value against the ARM context. */ export declare function evalArm(value: unknown, ctx: ArmEvalCtx): Promise; /** Resource names this resource references via `resourceId('type','name')` / `reference('name')`. Pure. */ export declare function armDependencies(resource: ArmResource, names: Set): string[]; /** * Topologically order ARM resources so a referenced resource is applied before * the resource that references it. Names that are expressions are ordered as-is * (they don't match a literal reference). Throws on a cycle. Pure. */ export declare function orderArmResources(resources: ArmResource[]): ArmResource[]; /** The ARM resource-ID PUT URL for a resource (name expression evaluated). */ export declare function armResourceUrl(resource: ArmResource, ctx: ArmEvalCtx): Promise; /** The ARM resource PUT body (location/properties/sku/kind/tags), expressions evaluated. */ export declare function armResourceBody(resource: ArmResource, ctx: ArmEvalCtx): Promise>; export interface AzApplyArgs { /** Path to a built ARM template (`deploymentTemplate.json`). */ templatePath: string; /** Resource group to deploy into. */ resourceGroup: string; /** Region for the resource group and `resourceGroup().location`. Default: `eastus`. */ location?: string; /** ARM endpoint override (e.g. floci-az `http://localhost:4577`). Default: real Azure. */ endpoint?: string; /** Subscription id. Default: floci-az's local subscription. */ subscriptionId?: string; /** * Delete chant-owned resources of a templated type that are no longer in the * template (owned-only prune). Destructive — off by default. Foreign * (non-chant) resources are never touched. */ prune?: boolean; } /** * The native Azure applier — read a built ARM template and PUT each resource * directly to the ARM resource-CRUD API, in dependency order, resolving ARM * expressions (including `reference()`/`listKeys()` against resources applied * earlier this run). The Azure twin of `gcpApply`: it targets floci-az (which * `az deployment` can't, floci-az having no deployments provider) or real Azure * by endpoint override; the resource group is ensured first. */ export declare function azApply(args: AzApplyArgs, signal?: AbortSignal, http?: AzHttp): Promise<{ applied: Array<{ type: string; name: string; }>; pruned: Array<{ type: string; name: string; deleted: boolean; }>; /** Owned, undeclared resources the prune could not delete (#1457). Empty when * `prune` is off, and empty on a clean prune. */ notPrunable: AzNotPrunable[]; }>; /** * The key this applier used before #1446, and still recognises. * * The lexicon declares its marker channel in `../../ownership.ts` * (`chant-managed-by`) — the key the serializer stamps, the plugin registers as * `ownershipChannel`, and `import/live-export.ts` filters on. This applier used * a bare `managed-by` instead, so the two never agreed. * * That is not cosmetic. Both are ordinary ARM tag keys on the same surface, so * nothing forced the difference — and it meant a resource carrying only the * SERIALIZER's marker was invisible to this prune. Anything deployed by a path * other than `azApply` reads as foreign and is never pruned, including * everything `ApplyOp` deployed before #1448 routed it here from * `az deployment --mode Complete`. * * (GCP has the same-looking split for a real reason: its serializer stamps a * Config Connector object, where `app.kubernetes.io/managed-by` is a legal * Kubernetes label, while `gcpApply` writes GCP REST labels, where it is not. * Two surfaces, two key vocabularies. Azure has one surface.) */ export declare const LEGACY_OWNERSHIP_TAG_KEY = "managed-by"; /** * The ownership tags azApply stamps on every resource it applies. * * Stamps the declared channel's key. The legacy key goes on too, so a resource * applied by this chant is still recognised by an older one mid-rollout; it is * transitional and can be dropped once no deployed estate predates #1446. * * Only the managed-by marker, not stack/env: this applier has no * {@link OwnershipMarker} to hand — the serializer stamps those from project * config. Same shape the fly serializer uses when no ownership context is set. */ export declare function chantOwnershipTags(): Record; /** * Whether a resource's tags mark it chant-owned. * * Resolves through core's `hasOwnershipMarker` against the lexicon's declared * channel (#1446), so this predicate and the serializer's stamp can no longer * drift — the arrangement the fly lexicon already had. * * The legacy key is accepted as well, and the union is deliberate: it is * strictly more than this predicate recognised before, so nothing that was * prunable stops being prunable, and resources the serializer marked become * prunable for the first time. */ export declare function isChantOwned(tags: Record | null | undefined): boolean; /** One resource from the ARM resource-group listing. */ export interface ArmListItem { id: string; name: string; type: string; tags?: Record; } /** List the resources in the group via the ARM resource-list endpoint. */ export declare function listGroupResources(ctx: ArmEvalCtx, http?: AzHttp, signal?: AbortSignal): Promise; /** Idempotently delete one ARM resource by type/name/apiVersion. A 404 means it is already gone. */ export declare function deleteArmResource(type: string, name: string, apiVersion: string, ctx: ArmEvalCtx, http?: AzHttp, signal?: AbortSignal): Promise<{ type: string; name: string; deleted: boolean; }>; /** * Normalize an azApply/azDelete result into core's apply envelope (#1446). * * The lexicon keeps its own shape — ARM `type`, per-resource applied/pruned — * and this projects it onto the shared tri-state so a caller can read any * applier's result the same way. */ export declare function toApplyResult(result: { applied?: Array<{ type: string; name: string; }>; pruned?: Array<{ type: string; name: string; deleted: boolean; }>; deleted?: Array<{ type: string; name: string; deleted: boolean; }>; notPrunable?: AzNotPrunable[]; }): ApplyResult; /** A chant-owned live resource an owned-only prune could not delete, and why (#1457). */ export interface AzNotPrunable { type: string; name: string; /** * `no-api-version` — the resource is owned and undeclared, but no apiVersion * could be found for its type: the current template does not declare the * type, the lexicon's pinned registry has no entry for it, and ARM's * provider metadata listed no apiVersion for it (or the call failed). * `deleteArmResource` cannot build a URL without one. */ reason: "no-api-version"; } /** * Resolve an apiVersion for a live resource type from ARM's provider metadata: * `GET /subscriptions/{sub}/providers/{namespace}?api-version=2021-04-01` * returns `resourceTypes[].apiVersions[]`. Picks the newest stable version, * falling back to the newest preview when no stable one exists. One call per * namespace per prune run; the result is memoised in `cache` so a group with * many orphans of one provider costs one round trip. Returns `undefined` when * the endpoint is unavailable (floci-az does not implement it) or the type is * not listed. */ export declare function providerApiVersion(resourceType: string, ctx: ArmEvalCtx, http?: AzHttp, signal?: AbortSignal, cache?: Map>): Promise; /** * Owned-only prune: delete every chant-owned live resource in the group whose * (evaluated) name is not declared in the template. Foreign (non-chant) * resources are never touched. * * ## Where the apiVersion comes from (#1457, #1472) * * `deleteArmResource` needs an `apiVersion`, and ARM's resource-group listing * does not return one per resource. The template is the obvious source, but it * only covers types it still declares: declare one storage account, apply, * delete it from source, and the template has zero resources of that type. * Keying the delete scope off the template alone made the last resource of a * type a permanent orphan (#1457 made that a reported skip rather than a * silent one). * * So the apiVersion is resolved in order: * * 1. the template, when it still declares the type; * 2. the lexicon's pinned per-provider registry (`lookupApiVersion`, the same * registry the serializer stamps into the template) — deterministic, no * extra call, covers every type the lexicon can emit; * 3. ARM's provider metadata (`providerApiVersion`) — covers a type the * lexicon has never heard of, at one extra call per namespace per run. * * Only when all three come up empty is the resource reported as * `notPrunable: no-api-version`. * * This matters more since #1448, which routed `ApplyOp`'s `arm` target through * `azApply` — before that, the composite reached `--mode Complete` and never * came here at all. */ export declare function pruneArmOrphans(desired: ArmResource[], ctx: ArmEvalCtx, http?: AzHttp, signal?: AbortSignal): Promise<{ pruned: Array<{ type: string; name: string; deleted: boolean; }>; notPrunable: AzNotPrunable[]; }>; /** * The inverse of {@link azApply} — read a built ARM template and delete the * resources it declares, in reverse dependency order (a referrer goes before the * resource it references). Idempotent: already-absent resources are a no-op. The * Azure twin of `gcpDelete`; `http` is injectable for tests. */ export declare function azDelete(args: AzApplyArgs, signal?: AbortSignal, http?: AzHttp): Promise<{ deleted: Array<{ type: string; name: string; deleted: boolean; }>; }>; //# sourceMappingURL=az-apply.d.ts.map