Provisions an Azure Kubernetes Fleet Manager β the regional control-plane object that groups member AKS clusters for centralized, staged, safe multi-cluster upgrade governance. Targets
hashicorp/azurerm ~> 4.0.
- π°οΈ Creates a single
azurerm_kubernetes_fleet_managerβ the fleet control plane that AKS member clusters join. - π§ Provides the anchor for centralized, staged rollout of Kubernetes version and node-image upgrades across many clusters.
- π Defaults to a hubless (management-only) fleet: the smallest attack surface, with no hosted hub control plane to secure.
- π§± Standalone by design β it owns only the fleet; membership, update runs, and update strategies are separate sibling modules wired by
id. - π·οΈ Carries the universal
tagsandtimeoutstail; emits the fleetidfirst for downstream composition.
π‘ Why it matters: Operating dozens of AKS clusters means upgrades are the highest-risk recurring change you run. A Fleet Manager turns "upgrade every cluster by hand" into a governed, ordered, resumable rollout β canary a stage, verify, then promote β instead of an all-at-once change with no blast-radius control. This module gives you that anchor with a hardened, hubless default.
If this module saves you time, please consider supporting it:
- β Star the repository on GitHub so others can find it.
- π€ Connect on LinkedIn: linkedin.com/in/microsoftexpert
- β Buy me a coffee: buymeacoffee.com/microsoftexpert
flowchart TD
rg["terraform-azurerm-resource-group"]
fm["terraform-azurerm-kubernetes-fleet-manager"]
kfm["azurerm_kubernetes_fleet_manager"]
member["terraform-azurerm-kubernetes-fleet-member"]
aks["terraform-azurerm-kubernetes-cluster"]
run["azurerm_kubernetes_fleet_update_run"]
strat["terraform-azurerm-kubernetes-fleet-update-strategy"]
rg -->|"provides resource_group_name"| fm
fm -->|"creates"| kfm
fm -->|"emits fleet id"| member
aks -->|"emits cluster id"| member
member -->|"joins clusters into fleet"| kfm
strat -->|"orders staged rollout"| run
run -->|"upgrades members via fleet"| fm
classDef self fill:#0078D4,color:#ffffff,stroke:#004578;
classDef keystone fill:#004578,color:#ffffff,stroke:#002d4d;
classDef ext fill:#eef2f7,color:#1b2733,stroke:#c3ccd6;
class fm self;
class kfm keystone;
class rg,member,aks,run,strat ext;
This module owns the fleet control plane only. A resource group provides its placement; sibling modules join member AKS clusters, define update strategies, and execute update runs β each consuming this module's id.
flowchart LR
subgraph inputs["Inputs"]
n["name"]
rgn["resource_group_name"]
loc["location"]
hp["hub_profile (optional, deprecated)"]
tg["tags"]
to["timeouts"]
end
this["azurerm_kubernetes_fleet_manager.this"]
subgraph outputs["Outputs"]
oid["id"]
onm["name"]
ofqdn["hub_fqdn"]
over["hub_kubernetes_version"]
end
n -->|"required identity"| this
rgn -->|"required placement"| this
loc -->|"required region"| this
hp -->|"optional hub, hubless default"| this
tg -->|"resource tags"| this
to -->|"operation timeouts"| this
this -->|"resource id"| oid
this -->|"resource name"| onm
this -->|"computed hub fqdn"| ofqdn
this -->|"computed hub version"| over
classDef keystone fill:#004578,color:#ffffff,stroke:#002d4d;
classDef io fill:#eef2f7,color:#1b2733,stroke:#c3ccd6;
class this keystone;
class n,rgn,loc,hp,tg,to,oid,onm,ofqdn,over io;
Resource inventory
| Resource | Count | Role |
|---|---|---|
azurerm_kubernetes_fleet_manager.this |
1 | The fleet control plane (keystone) |
hub_profile (dynamic, optional) |
0β1 | Deprecated hub cluster profile; absent by default |
timeouts (dynamic, optional) |
0β1 | Per-operation timeout overrides |
| Requirement | Value |
|---|---|
| Terraform | >= 1.12.0 |
hashicorp/azurerm |
~> 4.0 |
| Provider block | None in this module β the caller configures provider "azurerm" { features {} }, auth, and subscription. |
Schema notes that bite (verified against the live provider schema):
- π
nameis force-new. Renaming the fleet replaces it and every membership/run attached to it. - π
resource_group_nameis force-new. Moving the fleet between resource groups replaces it. - π
locationis force-new. The fleet is a regional object; changing regions replaces it. β οΈ hub_profileis deprecated. The service team has deprecated the hub cluster profile and the provider no longer transmits it to the Azure API. Setting it has no effect and can cause a persistent plan diff; leave it null. Itsdns_prefixis create-time only.β οΈ hub_fqdnandhub_kubernetes_versionare always null β for every fleet, not just a hubless one. They are surfaced fromhub_profile, and the provider declareshub_profilebut never reads or writes it β it appears exactly once in the resource's source, in the schema declaration, and in neither the encode nor the decode path, so nothing ever populates them. They are retained because removing an output would strand references in consuming configurations.
- Least-privilege: a custom role granting
Microsoft.ContainerService/fleets/read,Microsoft.ContainerService/fleets/write, andMicrosoft.ContainerService/fleets/delete, scoped to the target resource group. - Built-in equivalent:
Azure Kubernetes Fleet Manager Contributor Role(orContributor) on the target resource group. - Joining member clusters and executing update runs require additional fleet-member and cluster-user actions on both the fleet and each member AKS cluster; those belong to the sibling modules, not to this one.
- An existing resource group in a supported US Azure region.
- The
Microsoft.ContainerServiceresource provider registered on the target subscription. - The caller configures the
provider "azurerm" { features {} }block, auth, and subscription; this module declares none of them.
terraform-azurerm-kubernetes-fleet-manager/
βββ providers.tf # required_version >= 1.12.0; azurerm ~> 4.0; no provider block
βββ variables.tf # deeply-typed inputs; hubless default; tags + timeouts tail
βββ main.tf # keystone azurerm_kubernetes_fleet_manager.this + dynamic blocks
βββ outputs.tf # id first, then name, then computed hub attributes
βββ README.md # this document
βββ SCOPE.md # the cross-module contract
βββ LICENSE # MIT, Copyright (c) 2026 Casey Wood
βββ .gitignore # canonical library ignore set
The smallest real call produces a hardened, hubless fleet:
module "fleet" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-kubernetes-fleet-manager.git?ref=v1.0.0"
name = "fleet-platform-prod"
resource_group_name = "rg-platform-prod"
location = "eastus"
}βΉοΈ The caller configures the provider, authentication, subscription, and the mandatory
features {}block. This module never declares aprovider {}block.
# In the caller's root module:
provider "azurerm" {
features {}
}Consumes
| Input | Type | Source module |
|---|---|---|
resource_group_name |
string |
terraform-azurerm-resource-group (name) |
location |
string |
caller / terraform-azurerm-resource-group (location) |
Emits
| Output | Description | Consumed by |
|---|---|---|
id |
Fleet Manager Resource ID (first) | fleet-member, fleet-update-run, fleet-update-strategy, diagnostics |
name |
Fleet Manager name | diagnostics / tagging |
resource_group_name |
Containing resource group | compositions / tagging |
location |
Fleet region | compositions / documentation |
hub_fqdn |
Always null β nothing populates it (see Architecture Notes) |
retained for compatibility only |
hub_kubernetes_version |
Always null β nothing populates it (see Architecture Notes) |
retained for compatibility only |
1 Β· Minimal hubless fleet (the secure default)
module "fleet" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-kubernetes-fleet-manager.git?ref=v1.0.0"
name = "fleet-platform-prod"
resource_group_name = "rg-platform-prod"
location = "eastus"
}π With no
hub_profile, the fleet is management-only: an orchestration surface with no hosted hub control plane and the smallest attack surface.
2 Β· Fleet with governance tags
module "fleet" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-kubernetes-fleet-manager.git?ref=v1.0.0"
name = "fleet-platform-prod"
resource_group_name = "rg-platform-prod"
location = "eastus2"
tags = {
environment = "prod"
owner = "platform-engineering"
cost_center = "cc-1042"
managed_by = "terraform"
}
}π‘ Tags flow straight to the fleet resource and are the anchor for cost and ownership reporting across every cluster the fleet governs.
3 Β· Custom operation timeouts
module "fleet" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-kubernetes-fleet-manager.git?ref=v1.0.0"
name = "fleet-platform-prod"
resource_group_name = "rg-platform-prod"
location = "westus2"
timeouts = {
create = "30m"
delete = "30m"
}
}βΉοΈ Only the operations you set are overridden; omitted operations use the provider defaults.
4 Β· Deprecated hub-based fleet (discouraged)
module "fleet" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-kubernetes-fleet-manager.git?ref=v1.0.0"
name = "fleet-legacy-hub"
resource_group_name = "rg-platform-prod"
location = "eastus"
hub_profile = {
dns_prefix = "fleet-legacy-hub"
}
}
β οΈ Thehub_profileargument is deprecated in the provider and is now inert: the provider declareshub_profilebut never reads or writes it β it appears exactly once in the resource's source, in the schema declaration, and in neither the encode nor the decode path, so setting it has no effect whatsoever on the created fleet. Note this is a deprecation of the argument, not of the capability β Microsoft documents hub clusters as current and required for workload placement, Managed Fleet Namespaces and DNS load balancing. If you need a hub, it cannot be configured through this resource. The block is retained only so configurations authored before the deprecation still parse.
5 Β· Stable naming convention across environments
locals {
env = "stage"
}
module "fleet" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-kubernetes-fleet-manager.git?ref=v1.0.0"
name = "fleet-platform-${local.env}"
resource_group_name = "rg-platform-${local.env}"
location = "centralus"
}π
name,resource_group_name, andlocationare all force-new. Fix your naming convention up front so a later rename does not silently replace the fleet and detach every member.
6 Β· Multi-region fleets with for_each at scale
variable "fleet_regions" {
type = map(object({
location = string
resource_group_name = string
}))
default = {
east = { location = "eastus", resource_group_name = "rg-fleet-eastus" }
west = { location = "westus2", resource_group_name = "rg-fleet-westus2" }
central = { location = "centralus", resource_group_name = "rg-fleet-centralus" }
}
}
module "fleet" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-kubernetes-fleet-manager.git?ref=v1.0.0"
for_each = var.fleet_regions
name = "fleet-platform-${each.key}"
resource_group_name = each.value.resource_group_name
location = each.value.location
tags = { region_key = each.key }
}π‘ Keying on a stable region label (not on the location string) keeps the set stable if a region is ever re-homed.
7 Β· Consuming the fleet id for a diagnostic setting
module "fleet" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-kubernetes-fleet-manager.git?ref=v1.0.0"
name = "fleet-platform-prod"
resource_group_name = "rg-platform-prod"
location = "eastus"
}
module "fleet_diagnostics" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-monitor-diagnostic-setting.git?ref=v1.0.0"
name = "fleet-diagnostics"
target_resource_id = module.fleet.id
log_analytics_workspace_id = var.log_analytics_workspace_id
}π‘ The fleet emits
idfirst precisely so cross-cutting modules like diagnostics can wire to it with one reference.
8 Β· Least-privilege role assignment scoped to the fleet
module "fleet" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-kubernetes-fleet-manager.git?ref=v1.0.0"
name = "fleet-platform-prod"
resource_group_name = "rg-platform-prod"
location = "eastus"
}
module "fleet_operators" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-role-assignments.git?ref=v1.0.0"
# `scope` is a required top-level argument; each entry may override it.
scope = module.fleet.id
role_assignments = {
fleet_admin = {
role_definition_name = "Azure Kubernetes Fleet Manager Contributor"
principal_id = var.platform_group_object_id
}
}
}π Grant fleet permissions at the fleet
idscope, not at the subscription, to keep the blast radius narrow.
9 Β· Note: joining a member AKS cluster (sibling module)
# Membership is owned by the fleet-member sibling module, not this one.
module "fleet" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-kubernetes-fleet-manager.git?ref=v1.0.0"
name = "fleet-platform-prod"
resource_group_name = "rg-platform-prod"
location = "eastus"
}
module "fleet_member_east" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-kubernetes-fleet-member.git?ref=v1.0.0"
name = "member-aks-east"
kubernetes_fleet_id = module.fleet.id
kubernetes_cluster_id = module.aks_east.id
group = "prod-east"
}βΉοΈ This module deliberately does not own membership. Keeping the fleet and its members as separate modules lets clusters join and leave without re-planning the fleet itself.
10 Β· Note: defining an update strategy (sibling module)
# Update strategies are owned by the fleet-update-strategy sibling module.
module "fleet_upgrade_strategy" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-kubernetes-fleet-update-strategy.git?ref=v1.0.0"
name = "canary-then-prod"
# NOTE the argument name: the strategy and the run say kubernetes_fleet_manager_id,
# while the member above says kubernetes_fleet_id. Same value, two spellings.
kubernetes_fleet_manager_id = module.fleet.id
# A LIST, and the order is the rollout order. The wait is in SECONDS.
stage = [
{
name = "canary"
after_stage_wait_in_seconds = 3600
group = [{ name = "prod-east" }]
},
{
name = "fleet"
group = [{ name = "prod-west" }, { name = "prod-central" }]
},
]
}π‘ A strategy encodes the order of a rollout (canary a group, wait, then promote). The fleet holds the members; the strategy holds the plan.
β οΈ The group names are matched against the members'groupvalues by exact string, with nothing validating them β an update group is not an Azure resource. Source them from one place.
11 Β· Note: executing a staged update run (sibling module)
# The update run is not yet wrapped as a module in this library, so it is shown as the
# resource. Note that kubernetes_version is nested inside managed_cluster_update.upgrade
# rather than being a top-level argument.
resource "azurerm_kubernetes_fleet_update_run" "upgrade_1_31" {
name = "upgrade-1-30-to-1-31"
kubernetes_fleet_manager_id = module.fleet.id
# Either reference a strategy OR declare stages inline -- the provider documents the
# two as mutually exclusive. Omitting both gives a one-by-one sequence.
fleet_update_strategy_id = module.fleet_upgrade_strategy.id
managed_cluster_update {
upgrade {
type = "Full"
kubernetes_version = "1.31.0"
}
}
}π‘ The run is the ordered execution. It references the fleet
idand, optionally, a strategyid.
β οΈ kubernetes_versionis required whenupgrade.typeisFull, and the two legal types areFullandNodeImageOnly.
12 Β· Fleet alongside its resource group
module "rg" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-resource-group.git?ref=v1.0.0"
name = "rg-platform-prod"
location = "eastus"
}
module "fleet" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-kubernetes-fleet-manager.git?ref=v1.0.0"
name = "fleet-platform-prod"
resource_group_name = module.rg.name
location = module.rg.location
}π‘ Referencing
module.rg.name/module.rg.locationgives Terraform an implicit dependency, so the resource group is created before the fleet without an explicitdepends_on.
13 Β· Reading the fleet outputs
module "fleet" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-kubernetes-fleet-manager.git?ref=v1.0.0"
name = "fleet-platform-prod"
resource_group_name = "rg-platform-prod"
location = "eastus"
}
output "fleet_id" {
value = module.fleet.id
}
output "fleet_hub_fqdn" {
value = module.fleet.hub_fqdn # always null -- nothing in the provider populates it
}βΉοΈ
hub_fqdnandhub_kubernetes_versionare null for the hubless default; they are populated only for legacy hub-based configurations.
14 Β· ποΈ End-to-end composition
Resource group β fleet manager β two AKS member clusters joined into the fleet via the fleet-member sibling:
provider "azurerm" {
features {}
}
module "rg" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-resource-group.git?ref=v1.0.0"
name = "rg-platform-prod"
location = "eastus"
}
module "fleet" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-kubernetes-fleet-manager.git?ref=v1.0.0"
name = "fleet-platform-prod"
resource_group_name = module.rg.name
location = module.rg.location
tags = {
environment = "prod"
managed_by = "terraform"
}
}
module "aks_east" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-kubernetes-cluster.git?ref=v1.0.0"
name = "aks-east"
resource_group_name = module.rg.name
location = "eastus"
dns_prefix = "aks-east"
}
module "aks_west" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-kubernetes-cluster.git?ref=v1.0.0"
name = "aks-west"
resource_group_name = module.rg.name
location = "westus2"
dns_prefix = "aks-west"
}
module "fleet_members" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-kubernetes-fleet-member.git?ref=v1.0.0"
for_each = {
east = { cluster_id = module.aks_east.id, group = "prod-east" }
west = { cluster_id = module.aks_west.id, group = "prod-west" }
}
name = "member-${each.key}"
kubernetes_fleet_id = module.fleet.id
kubernetes_cluster_id = each.value.cluster_id
group = each.value.group
}π‘ The fleet is the hub of the composition: the resource group provides placement, the AKS modules provide member cluster
ids, and the fleet-member sibling joins them using this module'sid. Update strategies and runs layer on top, all keyed to the sameid.
Primary identity & placement
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
name |
string |
β | β | Fleet Manager name (force-new). |
resource_group_name |
string |
β | β | Existing resource group (force-new). |
location |
string |
β | β | Azure region (force-new). |
Optional configuration
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
hub_profile |
object({ dns_prefix = string }) |
β | null |
Deprecated hub cluster profile; null yields a hubless fleet. |
tags |
map(string) |
β | {} |
Tags applied to the fleet. |
timeouts |
object({ create, read, update, delete }) |
β | null |
Per-operation timeout overrides. |
Full object() schemas
variable "name" {
type = string
}
variable "resource_group_name" {
type = string
}
variable "location" {
type = string
}
variable "hub_profile" {
type = object({
dns_prefix = string
})
default = null
}
variable "tags" {
type = map(string)
default = {}
}
variable "timeouts" {
type = object({
create = optional(string)
read = optional(string)
update = optional(string)
delete = optional(string)
})
default = null
}| Output | Description | Kind |
|---|---|---|
id |
Passthrough | |
name |
The name of the Kubernetes Fleet Manager | Passthrough |
resource_group_name |
The resource group that contains the Fleet Manager | Passthrough |
location |
Passthrough | |
tags |
Tags assigned to the Fleet Manager | Passthrough |
hub_fqdn |
Derived | |
hub_kubernetes_version |
Derived | |
hub_profile_configured_but_ignored |
Derived | |
hub_profile_argument_is_inert |
Constant | |
is_hubless |
Constant | |
hub_cluster_requires_out_of_band_creation |
Constant | |
supports_update_orchestration |
Constant | |
supports_workload_placement |
Constant | |
incurs_hub_cluster_cost |
Constant | |
hub_upgrade_is_one_way |
Constant | |
provider_exposes_managed_identity |
Constant | |
replacement_triggering_arguments |
Derived | |
only_mutable_argument |
Constant | Derived |
sibling_id_argument_names |
Derived | |
azure_api_provider_version |
Derived |
- The fleet is an identity you cannot move.
name,resource_group_name, andlocationare all force-new. A change to any of them replaces the fleet, which detaches every member and invalidates in-flight update runs. Treat the fleet's name and placement as permanent. - Hubless is the intended shape. The
main.tfrendershub_profileonly when the caller supplies it (for_each = var.hub_profile != null ? [var.hub_profile] : []), so the empty call is hubless. The hub profile is deprecated and no longer transmitted to the API; enabling it does not create a hub and can leave a persistent plan diff. - Computed hub attributes are guarded.
hub_fqdnandhub_kubernetes_versionare read throughtry(...), so they resolve tonullfor a hubless fleet rather than raising an index error on an absent block. - Ownership boundary is deliberate. This module owns only the fleet. Membership, strategies, and runs are separate lifecycles; keeping them in sibling modules means a cluster can join or leave, and a rollout can run, without re-planning the fleet.
features {}dependence. The provider will not initialize without a caller-sideprovider "azurerm" { features {} }block. A module that appears not to init in isolation is almost always missing that caller-side block.
Secure by default β the empty call produces the hardened resource; each relaxation is an explicit caller opt-in.
| Concern | Secure default (empty call) | Opt-out (caller must type it) |
|---|---|---|
| Hub cluster control plane | Hubless β hub_profile = null (management-only, smallest attack surface) |
supply a hub_profile (deprecated; discouraged) |
| Secret surface | None accepted, none emitted | β |
| Public/data-plane exposure | Not applicable β the fleet exposes no data plane toggle | β |
| Placement immutability | name / resource_group_name / location treated as permanent (force-new) |
rename/move (replaces the fleet) |
| Tagging | tags = {} (explicit, no hidden defaults) |
supply tags |
βΉοΈ Unlike storage or database primitives, the Fleet Manager schema exposes no public-network, TLS, or encryption toggle β its secure posture is the hubless default plus least-privilege RBAC on the caller identity.
terraform init -backend=false
terraform validate
terraform fmt -check- Pin the module with
?ref=v1.0.0β never a branch. - This library is plan-only during authoring; a human runs
terraform plan/applyfrom CI against real credentials.
The offline proof gate exercises everything that does not require Azure:
terraform init -backend=falseβ resolves the pinnedazurerm ~> 4.0provider with no backend.terraform validateβ proves the configuration is type-correct against the pinned provider schema; the deeply-typedobject()inputs surface a malformed call at parse time.terraform fmt -checkβ enforces canonical formatting.
What only plan (run by a human, from CI) exercises: the actual ARM create/read against a live subscription, force-new detection on a real name / location change, and the provider's drop of the deprecated hub_profile on apply.
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Outputs:
id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-platform-prod/providers/Microsoft.ContainerService/fleets/fleet-platform-prod"
name = "fleet-platform-prod"
resource_group_name = "rg-platform-prod"
location = "eastus"
hub_fqdn = null
hub_kubernetes_version = null
| Symptom | Cause | Fix |
|---|---|---|
Provider configuration not present / init fails in isolation |
No caller-side provider "azurerm" { features {} } block |
Add the provider + features {} block to the root module; this module never declares one. |
| Fleet is replaced on a small edit | You changed name, resource_group_name, or location β all force-new |
Revert the change, or accept replacement (which detaches members); settle naming/placement before first apply. |
Persistent diff on hub_profile after apply |
The block is deprecated; the provider no longer sends it to the API | Remove hub_profile and use the hubless default. |
hub_fqdn / hub_kubernetes_version are null |
The fleet is hubless (the default), or the hub profile was dropped as deprecated | Expected β these populate only for legacy hub-based configurations. |
AuthorizationFailed creating the fleet |
Caller identity lacks Microsoft.ContainerService/fleets/write at the resource group |
Grant the fleet contributor role at the resource group scope. |
| Members or runs missing after a fleet change | The fleet was replaced, detaching its dependents | Recreate membership/runs through their sibling modules against the new fleet id. |
- azurerm provider β
azurerm_kubernetes_fleet_manager - Azure Kubernetes Fleet Manager β Microsoft Learn
- Sibling modules:
terraform-azurerm-resource-group,terraform-azurerm-kubernetes-cluster,terraform-azurerm-kubernetes-fleet-member,terraform-azurerm-kubernetes-fleet-update-strategy - The update run is the resource
azurerm_kubernetes_fleet_update_run; it is not yet wrapped as a module in this library. - This module's
SCOPE.mdβ the cross-module contract.
π "Infrastructure as Code should be standardized, consistent, and secure."