{"record":{"id":"d1e91063475c55d6","repo":"kubernetes/kops","slug":"cluster-has-no-subnets","errorCode":null,"errorMessage":"cluster has no subnets","messagePattern":"cluster has no subnets","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"upup/pkg/fi/cloudup/azure/cloudconfig.go","lineNumber":57,"sourceCode":"\tRouteTableName              string `json:\"routeTableName,omitempty\"`\n\tSecurityGroupName           string `json:\"securityGroupName,omitempty\"`\n\tUseInstanceMetadata         bool   `json:\"useInstanceMetadata,omitempty\"`\n\tDisableAvailabilitySetNodes bool   `json:\"disableAvailabilitySetNodes,omitempty\"`\n}\n\n// BuildCloudConfig assembles the Azure cloud provider configuration for a\n// cluster. kOps publishes the result in the azure-cloud-provider Secret, which\n// the cloud-controller-manager and CSI drivers load via the\n// --cloud-config-secret-name flag.\nfunc BuildCloudConfig(cluster *kops.Cluster) (*CloudConfig, error) {\n\tazure := cluster.Spec.CloudProvider.Azure\n\tif azure == nil {\n\t\treturn nil, fmt.Errorf(\"cluster is not an Azure cluster\")\n\t}\n\n\tsubnets := cluster.Spec.Networking.Subnets\n\tif len(subnets) == 0 {\n\t\treturn nil, fmt.Errorf(\"cluster has no subnets\")\n\t}\n\n\t// In kOps the virtual network and the network security group share a name.\n\tnetworkName := cluster.AzureNetworkSecurityGroupName()\n\n\treturn &CloudConfig{\n\t\tTenantID:                    azure.TenantID,\n\t\tSubscriptionID:              azure.SubscriptionID,\n\t\tUseManagedIdentityExtension: true,\n\t\tResourceGroup:               cluster.AzureResourceGroupName(),\n\t\tLocation:                    subnets[0].Region,\n\t\tVnetName:                    networkName,\n\t\tSubnetName:                  subnets[0].Name,\n\t\tRouteTableName:              cluster.AzureRouteTableName(),\n\t\tSecurityGroupName:           networkName,\n\t\tUseInstanceMetadata:         true,\n\t\tDisableAvailabilitySetNodes: true,\n\t}, nil","sourceCodeStart":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/upup/pkg/fi/cloudup/azure/cloudconfig.go#L39-L75","documentation":"BuildCloudConfig assembles the azure-cloud-provider CloudConfig consumed by the Azure cloud-controller-manager and CSI drivers. It requires cluster.Spec.Networking.Subnets to be non-empty because the config derives Location (region) and SubnetName from subnets[0]; with zero subnets there is no valid value for these required cloud-provider fields, so it fails fast with this error instead of publishing a broken secret.","triggerScenarios":"Calling kops.BuildCloudConfig(cluster) when cluster.Spec.Networking.Subnets is an empty slice or nil (e.g. the cluster spec was constructed programmatically, deserialized incompletely, or the networking section was omitted from the manifest).","commonSituations":"Hand-writing a minimal Cluster YAML without a networking.subnets section; a controller/automation tool that builds kops.Cluster structs in Go and forgets subnets; a partially applied or truncated cluster manifest where subnets were stripped by a mutating webhook or serialization bug.","solutions":["Add at least one subnet under spec.networking.subnets in the cluster manifest (with a name and region matching your Azure location) before running the operation.","If building the cluster spec in Go, populate Spec.Networking.Subnets before calling BuildCloudConfig.","Verify the cluster spec actually loaded: re-run `kops get cluster -o yaml` and confirm the subnets entries are present, then re-apply/update the cluster."],"exampleFix":"// before (incomplete cluster spec)\nspec:\n  cloudProvider:\n    azure: {}\n  networking:\n    nonMasqueradeCIDR: 100.64.0.0/10\n// after\nspec:\n  cloudProvider:\n    azure: {}\n  networking:\n    nonMasqueradeCIDR: 100.64.0.0/10\n    subnets:\n      - name: azure-us-east-1\n        type: Public\n        region: us-east-1","handlingStrategy":"validation","validationCode":"if cluster == nil || cluster.Spec.CloudProvider.Azure == nil {\n    return errors.New(\"not an Azure cluster\")\n}\nif len(cluster.Spec.Networking.Subnets) == 0 {\n    return errors.New(\"cluster spec must define at least one networking subnet before building Azure CloudConfig\")\n}","typeGuard":"func hasSubnets(c *kops.Cluster) bool {\n    return c != nil && c.Spec.CloudProvider.Azure != nil && len(c.Spec.Networking.Subnets) > 0\n}","tryCatchPattern":null,"preventionTips":["Always include spec.networking.subnets in Azure cluster manifests; use `kops get cluster -o yaml` to verify before mutating.","When generating Cluster specs in Go, add a validation step (kops/validation.ValidateCluster) before calling BuildCloudConfig.","Add a unit test asserting BuildCloudConfig fails fast on empty subnets so regressions surface early."],"tags":["azure","cluster-config","validation","subnets"],"backgroundTag":"missing-required-config-field","analyzedSha":"4c8573c808a73d578c5eadc86d410646ea0b0d73","analyzedAt":"2026-09-05T04:13:19.212Z","contentChangedAt":"2026-09-05T04:13:19.212Z","schemaVersion":2},"datasetVersion":"2026-09-12T07:17:12.445Z"}