Skip to content
You are reading dev: the documentation of main. The last release, 0.1.0, predates this site.

Foundations

opentofu/azure builds a resource group, a VNet, an AKS cluster with no CNI and no workload, and its identity, and outputs what the bootstrap needs to install Cilium and Flux.

Enforced: no variable. Default: a variable changes it.

Position
AKS Standard, Node Auto-Provisioning, never Automaticenforced
A system pool of 2 × Standard_D2s_v5 over zones 1–3, tainted CriticalAddonsOnly; NAP provisions the restdefault
stable upgrade channelenforced
Two maintenance windows of 4 to 24 hours, no defaultrequired
KubernetesOfficial support, no LTSenforced
Disk and File CSI drivers, AKS-managedenforced
Container Insights (90 days) and Managed Prometheusenforced, superseded
Entra Workload ID and the OIDC issuerenforced
Microsoft-managed keys for etcd, disks and logsenforced
network_plugin = "none": Cilium comes with the bootstrapenforced
pod_cidr 10.244.0.0/16, passed to Cilium as an outputdefault
Private cluster, no public FQDNenforced
One VNet 10.0.0.0/16, one node subnet, one NAT gatewaydefault
Egress through the NAT gateway (userAssignedNATGateway)enforced
owner, environment, socle-version tags on every resourceenforced
Under the hood
  • The private DNS zone is system-managed.
  • Service range 10.1.0.0/16, DNS service 10.1.0.10: defaults AKS needs.
  • Kubernetes RBAC on, system-assigned cluster identity: AKS’s defaults, declared.
  • upgrade_settings.max_surge = "10%" repeats AKS’s server-side default so the plan converges.
  • kubernetes_version has no default and accepts any major.minor.
  • Deployment Safeguards: proposed, no azurerm 4.x attribute. azure_policy_enabled = true installs the add-on and assigns nothing.
  • pod_cidr on the cluster, a network_policy: neither exists under BYO CNI; Cilium holds both.
  • Defender for Containers: the client’s subscription setting.
  • Any workload identity, Crossplane’s included: bind one to the oidc_issuer_url output.
  • Entra ID authentication on the API server: local accounts stay on (limits).
  • Any credential as input, any key as output: cluster_ca_certificate is the only certificate, sensitive.
  • Windows pools, IPv6: NAP supports neither.
  • Azure service metrics readers: proposed.
  • 2026-10-05, OpenTofu 1.12.6, plan only: the bootstrap with cloud = "azure" and no aws provider block fails with Provider "registry.opentofu.org/hashicorp/aws" requires explicit configuration. With a placeholder aws provider, the plan is 4 to add (Cilium, the operator, the instance, the socle release). The quickstart carries that block.
NameVersion
terraform>= 1.13
azurerm>= 4.0, < 5.0

No modules.

NameType
azurerm_kubernetes_cluster.socleresource
azurerm_log_analytics_workspace.container_insightsresource
azurerm_monitor_data_collection_rule.prometheusresource
azurerm_monitor_data_collection_rule_association.prometheusresource
azurerm_monitor_workspace.socleresource
azurerm_nat_gateway.socleresource
azurerm_nat_gateway_public_ip_association.socleresource
azurerm_public_ip.natresource
azurerm_resource_group.socleresource
azurerm_subnet.noderesource
azurerm_subnet_nat_gateway_association.noderesource
azurerm_virtual_network.socleresource
NameDescriptionTypeDefaultRequired
cluster_nameName shared by the resource group (when created), the VNet, the AKS cluster and every resource this module creates around them.stringn/ayes
environmentStamped on every billable resource. Also what the factory's upgrade rings (dev/staging/prod) key off.stringn/ayes
kubernetes_versionAKS control plane version. Required, no default: the module accepts whatever version it is given rather than enforcing a version policy itself — that policy is decided and bumped by the socle Kargo pipelines, not by this module.stringn/ayes
locationAzure region for every resource this module creates. Required, no default — there is no globally correct region to pick on a client's behalf.stringn/ayes
maintenance_window_auto_upgradeWindow Kubernetes version auto-upgrades (stable channel) are allowed to run in. At least 4 hours, per AKS's own constraint.
object({
frequency = string
interval = number
duration = number
day_of_week = optional(string)
start_time = optional(string)
utc_offset = optional(string)
})
n/ayes
maintenance_window_node_osWindow node OS security patches are allowed to run in. Separate schedule from maintenance_window_auto_upgrade, so node patching and Kubernetes upgrades never have to share a window.
object({
frequency = string
interval = number
duration = number
day_of_week = optional(string)
start_time = optional(string)
utc_offset = optional(string)
})
n/ayes
ownerStamped on every billable resource so cost can be attributed and orphans can be found.stringn/ayes
resource_group_nameName of the resource group. Used as the name to create when create_resource_group is true, or the name of an existing one to attach to when false.stringn/ayes
additional_tagsExtra tags merged onto every resource this module creates, on top of owner/environment/socle-version.map(string){}no
create_nat_gatewayCreate one NAT Gateway for the VNet's node subnet. Not a toggle for
disabling egress outright — the node subnet has no public IP of its
own, so without this its nodes reach nothing. Exists only for the
create_vnet = false case, where the consumer's existing VNet already
manages its own NAT or an alternate egress path.
booltrueno
create_resource_groupCreate the resource group, or attach to one the consumer already manages.booltrueno
create_vnetCreate the VNet and its node subnet, or attach to a subnet the consumer already manages.booltrueno
dns_service_ipIP address within service_cidr used for cluster service discovery (kube-dns).string"10.1.0.10"no
log_retention_daysRetention for the Container Insights Log Analytics workspace this module creates.number90no
node_subnet_idExisting node subnet ID to attach to. Required when create_vnet is false — this module carves its own subnet out of vnet_cidr only when it also creates the VNet.stringnullno
pod_cidrRange Cilium allocates pod addresses from, as the cluster pool of the
Cilium the bootstrap module installs (docs/architecture/cilium-before-flux.md). Not set on
the cluster: azurerm refuses pod_cidr under network_plugin = "none" (see
cluster.tf), so this module only carries the value to the bootstrap
through its output. Must not overlap the VNet, service_cidr or any
connected network. The default is AKS's own pod range, which the chart's
default (10.0.0.0/8) would not be: that one contains the VNet.
string"10.244.0.0/16"no
service_cidrCIDR for Kubernetes service IPs. Must not overlap the VNet or any connected network, and be smaller than /12 — an AKS constraint independent of the BYO CNI choice below.string"10.1.0.0/16"no
system_node_pool_node_countNode count for the mandatory system node pool. Small and fixed rather than autoscaled: this pool exists to satisfy AKS's structural minimum, not to run workloads.number2no
system_node_pool_vm_sizeVM size for the mandatory system node pool. This is a structural AKS requirement, not a Karpenter/NAP-managed pool — kept small and tainted for-system-only by default (only_critical_addons_enabled), since NAP provisions everything workload-shaped.string"Standard_D2s_v5"no
vnet_cidrCIDR for the VNet — and its single node subnet, which covers the whole range — when this module creates it. Arbitrary default (not a research decision), sized generously since it only ever needs to fit nodes: Cilium's own IPAM owns pod addressing entirely, decoupled from the VNet.string"10.0.0.0/16"no
vnet_nameExisting VNet name to attach to. Required when create_vnet is false, and incoherent to set when create_vnet is true — this module cannot both create a VNet and attach to a different one.stringnullno
zonesAvailability zones the default system node pool spreads across. Azure subnets aren't zone-scoped — zone placement happens on the node pool itself.list(string)
[
"1",
"2",
"3"
]
no
NameDescription
cluster_ca_certificateBase64-encoded cluster CA certificate, for building a kubeconfig.
cluster_endpointThe control plane's private FQDN — the only reachable endpoint, since this module always disables the public one.
cluster_nameName of the AKS cluster.
helm_kubernetesDrop-in value for the helm provider's kubernetes attribute, so a root configures it in one line. Carries no credential: kubelogin obtains a short-lived Entra token from the caller's Azure CLI login at call time; the server ID is AKS's well-known Entra application. PRECONDITION NOT YET MET BY THIS MODULE: the exec only authenticates against a cluster with Entra ID authentication enabled (azure_active_directory_role_based_access_control), which this module does not configure yet — local accounts remain its auth path. Until it is enabled, this output is the shape a root will consume, not a working login (docs/clouds/azure/limits.md).
locationRegion the cluster and its resources were created in.
node_subnet_idID of the node subnet.
oidc_issuer_urlThe cluster's OIDC issuer — what a workload identity federation binding built by the layer above (Crossplane's provider, or anything else) will need.
pod_cidrRange the bootstrap module hands Cilium as its cluster pool. Passed through, never set on the cluster: azurerm refuses pod_cidr under BYO CNI.
resource_group_nameName of the resource group the cluster and its resources live in, whether this module created it or not.
tagsThe standard tag set applied to every billable resource this module creates.
vnet_nameName of the VNet the cluster is attached to, whether this module created it or not.