Skip to content

Latest commit

Β 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

☁️ Azure Kubernetes Fleet Manager Terraform Module

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.


Terraform azurerm module type resources


🧩 Overview

  • πŸ›°οΈ 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 tags and timeouts tail; emits the fleet id first 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.


❀️ Support this project

If this module saves you time, please consider supporting it:


πŸ—ΊοΈ Where this fits in the family

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;
Loading

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.


🧬 What this module builds

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;
Loading

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

βœ… Provider / Versions

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):

  • πŸ” name is force-new. Renaming the fleet replaces it and every membership/run attached to it.
  • πŸ” resource_group_name is force-new. Moving the fleet between resource groups replaces it.
  • πŸ” location is force-new. The fleet is a regional object; changing regions replaces it.
  • ⚠️ hub_profile is 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. Its dns_prefix is create-time only.
  • ⚠️ hub_fqdn and hub_kubernetes_version are always null β€” for every fleet, not just a hubless one. They are surfaced from hub_profile, and the provider declares hub_profile but 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.

πŸ”‘ Required Azure RBAC Roles / Permissions

  • Least-privilege: a custom role granting Microsoft.ContainerService/fleets/read, Microsoft.ContainerService/fleets/write, and Microsoft.ContainerService/fleets/delete, scoped to the target resource group.
  • Built-in equivalent: Azure Kubernetes Fleet Manager Contributor Role (or Contributor) 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.

Azure Prerequisites

  • An existing resource group in a supported US Azure region.
  • The Microsoft.ContainerService resource provider registered on the target subscription.
  • The caller configures the provider "azurerm" { features {} } block, auth, and subscription; this module declares none of them.

πŸ“ Module Structure

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

βš™οΈ Quick Start

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 a provider {} block.

# In the caller's root module:
provider "azurerm" {
  features {}
}

πŸ”Œ Cross-Module Contract

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

πŸ“š Example Library

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"
  }
}

⚠️ The hub_profile argument is deprecated in the provider and is now inert: the provider declares hub_profile but 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, and location are 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 id first 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 id scope, 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' group values 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 id and, optionally, a strategy id.

⚠️ kubernetes_version is required when upgrade.type is Full, and the two legal types are Full and NodeImageOnly.

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.location gives Terraform an implicit dependency, so the resource group is created before the fleet without an explicit depends_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_fqdn and hub_kubernetes_version are 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's id. Update strategies and runs layer on top, all keyed to the same id.


πŸ“₯ Inputs

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
}

🧾 Outputs

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

🧠 Architecture Notes

  • The fleet is an identity you cannot move. name, resource_group_name, and location are 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.tf renders hub_profile only 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_fqdn and hub_kubernetes_version are read through try(...), so they resolve to null for 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-side provider "azurerm" { features {} } block. A module that appears not to init in isolation is almost always missing that caller-side block.

🧱 Design Principles

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.


πŸš€ Runbook

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 / apply from CI against real credentials.

πŸ§ͺ Testing

The offline proof gate exercises everything that does not require Azure:

  • terraform init -backend=false β€” resolves the pinned azurerm ~> 4.0 provider with no backend.
  • terraform validate β€” proves the configuration is type-correct against the pinned provider schema; the deeply-typed object() 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.


πŸ’¬ Example Output

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

πŸ” Troubleshooting

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.

πŸ”— Related Docs


πŸ’™ "Infrastructure as Code should be standardized, consistent, and secure."