sigs.k8s.io/cluster-api-provider-azure@v1.14.3/azure/services/managedclusters/spec.go (about) 1 /* 2 Copyright 2022 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package managedclusters 18 19 import ( 20 "context" 21 "encoding/base64" 22 "fmt" 23 "net" 24 25 asocontainerservicev1preview "github.com/Azure/azure-service-operator/v2/api/containerservice/v1api20230202preview" 26 asocontainerservicev1 "github.com/Azure/azure-service-operator/v2/api/containerservice/v1api20231001" 27 asocontainerservicev1hub "github.com/Azure/azure-service-operator/v2/api/containerservice/v1api20231001/storage" 28 "github.com/Azure/azure-service-operator/v2/pkg/genruntime" 29 "github.com/pkg/errors" 30 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 31 "k8s.io/apimachinery/pkg/runtime" 32 "k8s.io/utils/ptr" 33 infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1" 34 "sigs.k8s.io/cluster-api-provider-azure/azure" 35 "sigs.k8s.io/cluster-api-provider-azure/azure/converters" 36 "sigs.k8s.io/cluster-api-provider-azure/azure/services/agentpools" 37 "sigs.k8s.io/cluster-api-provider-azure/azure/services/aso" 38 "sigs.k8s.io/cluster-api-provider-azure/util/tele" 39 "sigs.k8s.io/cluster-api-provider-azure/util/versions" 40 "sigs.k8s.io/cluster-api/util/secret" 41 ) 42 43 // ManagedClusterSpec contains properties to create a managed cluster. 44 type ManagedClusterSpec struct { 45 // Name is the name of this AKS Cluster. 46 Name string 47 48 // ResourceGroup is the name of the Azure resource group for this AKS Cluster. 49 ResourceGroup string 50 51 // NodeResourceGroup is the name of the Azure resource group containing IaaS VMs. 52 NodeResourceGroup string 53 54 // ClusterName is the name of the owning Cluster API Cluster resource. 55 ClusterName string 56 57 // VnetSubnetID is the Azure Resource ID for the subnet which should contain nodes. 58 VnetSubnetID string 59 60 // Location is a string matching one of the canonical Azure region names. Examples: "westus2", "eastus". 61 Location string 62 63 // Tags is a set of tags to add to this cluster. 64 Tags map[string]string 65 66 // Version defines the desired Kubernetes version. 67 Version string 68 69 // LoadBalancerSKU for the managed cluster. Possible values include: 'Standard', 'Basic'. Defaults to Standard. 70 LoadBalancerSKU string 71 72 // NetworkPlugin used for building Kubernetes network. Possible values include: 'azure', 'kubenet'. Defaults to azure. 73 NetworkPlugin string 74 75 // NetworkPluginMode is the mode the network plugin should use. 76 NetworkPluginMode *infrav1.NetworkPluginMode 77 78 // NetworkPolicy used for building Kubernetes network. Possible values include: 'azure', 'calico', 'cilium'. 79 NetworkPolicy string 80 81 // NetworkDataplane used for building Kubernetes network. Possible values include: 'azure', 'cilium'. 82 NetworkDataplane *infrav1.NetworkDataplaneType 83 84 // OutboundType used for building Kubernetes network. Possible values include: 'loadBalancer', 'managedNATGateway', 'userAssignedNATGateway', 'userDefinedRouting'. 85 OutboundType *infrav1.ManagedControlPlaneOutboundType 86 87 // SSHPublicKey is a string literal containing an ssh public key. Will autogenerate and discard if not provided. 88 SSHPublicKey string 89 90 // GetAllAgentPools is a function that returns the list of agent pool specifications in this cluster. 91 GetAllAgentPools func() ([]azure.ASOResourceSpecGetter[genruntime.MetaObject], error) 92 93 // PodCIDR is the CIDR block for IP addresses distributed to pods 94 PodCIDR string 95 96 // ServiceCIDR is the CIDR block for IP addresses distributed to services 97 ServiceCIDR string 98 99 // DNSServiceIP is an IP address assigned to the Kubernetes DNS service 100 DNSServiceIP *string 101 102 // AddonProfiles are the profiles of managed cluster add-on. 103 AddonProfiles []AddonProfile 104 105 // AADProfile is Azure Active Directory configuration to integrate with AKS, for aad authentication. 106 AADProfile *AADProfile 107 108 // SKU is the SKU of the AKS to be provisioned. 109 SKU *SKU 110 111 // LoadBalancerProfile is the profile of the cluster load balancer. 112 LoadBalancerProfile *LoadBalancerProfile 113 114 // APIServerAccessProfile is the access profile for AKS API server. 115 APIServerAccessProfile *APIServerAccessProfile 116 117 // AutoScalerProfile is the parameters to be applied to the cluster-autoscaler when enabled. 118 AutoScalerProfile *AutoScalerProfile 119 120 // Identity is the AKS control plane Identity configuration 121 Identity *infrav1.Identity 122 123 // KubeletUserAssignedIdentity is the user-assigned identity for kubelet to authenticate to ACR. 124 KubeletUserAssignedIdentity string 125 126 // HTTPProxyConfig is the HTTP proxy configuration for the cluster. 127 HTTPProxyConfig *HTTPProxyConfig 128 129 // OIDCIssuerProfile is the OIDC issuer profile of the Managed Cluster. 130 OIDCIssuerProfile *OIDCIssuerProfile 131 132 // DNSPrefix allows the user to customize dns prefix. 133 DNSPrefix *string 134 135 // DisableLocalAccounts disables getting static credentials for this cluster when set. Expected to only be used for AAD clusters. 136 DisableLocalAccounts *bool 137 138 // AutoUpgradeProfile defines auto upgrade configuration. 139 AutoUpgradeProfile *ManagedClusterAutoUpgradeProfile 140 141 // SecurityProfile defines the security profile for the cluster. 142 SecurityProfile *ManagedClusterSecurityProfile 143 144 // Patches are extra patches to be applied to the ASO resource. 145 Patches []string 146 147 // Preview enables the preview API version. 148 Preview bool 149 } 150 151 // ManagedClusterAutoUpgradeProfile auto upgrade profile for a managed cluster. 152 type ManagedClusterAutoUpgradeProfile struct { 153 // UpgradeChannel defines the channel for auto upgrade configuration. 154 UpgradeChannel *infrav1.UpgradeChannel 155 } 156 157 // HTTPProxyConfig is the HTTP proxy configuration for the cluster. 158 type HTTPProxyConfig struct { 159 // HTTPProxy is the HTTP proxy server endpoint to use. 160 HTTPProxy *string `json:"httpProxy,omitempty"` 161 162 // HTTPSProxy is the HTTPS proxy server endpoint to use. 163 HTTPSProxy *string `json:"httpsProxy,omitempty"` 164 165 // NoProxy is the endpoints that should not go through proxy. 166 NoProxy []string `json:"noProxy,omitempty"` 167 168 // TrustedCA is the Alternative CA cert to use for connecting to proxy servers. 169 TrustedCA *string `json:"trustedCa,omitempty"` 170 } 171 172 // AADProfile is Azure Active Directory configuration to integrate with AKS, for aad authentication. 173 type AADProfile struct { 174 // Managed defines whether to enable managed AAD. 175 Managed bool 176 177 // EnableAzureRBAC defines whether to enable Azure RBAC for Kubernetes authorization. 178 EnableAzureRBAC bool 179 180 // AdminGroupObjectIDs are the AAD group object IDs that will have admin role of the cluster. 181 AdminGroupObjectIDs []string 182 } 183 184 // AddonProfile is the profile of a managed cluster add-on. 185 type AddonProfile struct { 186 Name string 187 Config map[string]string 188 Enabled bool 189 } 190 191 // SKU is an AKS SKU. 192 type SKU struct { 193 // Tier is the tier of a managed cluster SKU. 194 Tier string 195 } 196 197 // LoadBalancerProfile is the profile of the cluster load balancer. 198 type LoadBalancerProfile struct { 199 // Load balancer profile must specify at most one of ManagedOutboundIPs, OutboundIPPrefixes and OutboundIPs. 200 // By default the AKS cluster automatically creates a public IP in the AKS-managed infrastructure resource group and assigns it to the load balancer outbound pool. 201 // Alternatively, you can assign your own custom public IP or public IP prefix at cluster creation time. 202 // See https://learn.microsoft.com/azure/aks/load-balancer-standard#provide-your-own-outbound-public-ips-or-prefixes 203 204 // ManagedOutboundIPs are the desired managed outbound IPs for the cluster load balancer. 205 ManagedOutboundIPs *int 206 207 // OutboundIPPrefixes are the desired outbound IP Prefix resources for the cluster load balancer. 208 OutboundIPPrefixes []string 209 210 // OutboundIPs are the desired outbound IP resources for the cluster load balancer. 211 OutboundIPs []string 212 213 // AllocatedOutboundPorts are the desired number of allocated SNAT ports per VM. Allowed values must be in the range of 0 to 64000 (inclusive). The default value is 0 which results in Azure dynamically allocating ports. 214 AllocatedOutboundPorts *int 215 216 // IdleTimeoutInMinutes are the desired outbound flow idle timeout in minutes. Allowed values must be in the range of 4 to 120 (inclusive). The default value is 30 minutes. 217 IdleTimeoutInMinutes *int 218 } 219 220 // APIServerAccessProfile is the access profile for AKS API server. 221 type APIServerAccessProfile struct { 222 // AuthorizedIPRanges are the authorized IP Ranges to kubernetes API server. 223 AuthorizedIPRanges []string 224 // EnablePrivateCluster defines hether to create the cluster as a private cluster or not. 225 EnablePrivateCluster *bool 226 // PrivateDNSZone is the private dns zone for private clusters. 227 PrivateDNSZone *string 228 // EnablePrivateClusterPublicFQDN defines whether to create additional public FQDN for private cluster or not. 229 EnablePrivateClusterPublicFQDN *bool 230 } 231 232 // AutoScalerProfile parameters to be applied to the cluster-autoscaler when enabled. 233 type AutoScalerProfile struct { 234 // BalanceSimilarNodeGroups - Valid values are 'true' and 'false' 235 BalanceSimilarNodeGroups *string 236 // Expander - If not specified, the default is 'random'. See [expanders](https://github.com/kubernetes/autoscaler/blob/master/cluster-autoscaler/FAQ.md#what-are-expanders) for more information. 237 Expander *string 238 // MaxEmptyBulkDelete - The default is 10. 239 MaxEmptyBulkDelete *string 240 // MaxGracefulTerminationSec - The default is 600. 241 MaxGracefulTerminationSec *string 242 // MaxNodeProvisionTime - The default is '15m'. Values must be an integer followed by an 'm'. No unit of time other than minutes (m) is supported. 243 MaxNodeProvisionTime *string 244 // MaxTotalUnreadyPercentage - The default is 45. The maximum is 100 and the minimum is 0. 245 MaxTotalUnreadyPercentage *string 246 // NewPodScaleUpDelay - For scenarios like burst/batch scale where you don't want CA to act before the kubernetes scheduler could schedule all the pods, you can tell CA to ignore unscheduled pods before they're a certain age. The default is '0s'. Values must be an integer followed by a unit ('s' for seconds, 'm' for minutes, 'h' for hours, etc). 247 NewPodScaleUpDelay *string 248 // OkTotalUnreadyCount - This must be an integer. The default is 3. 249 OkTotalUnreadyCount *string 250 // ScanInterval - The default is '10s'. Values must be an integer number of seconds. 251 ScanInterval *string 252 // ScaleDownDelayAfterAdd - The default is '10m'. Values must be an integer followed by an 'm'. No unit of time other than minutes (m) is supported. 253 ScaleDownDelayAfterAdd *string 254 // ScaleDownDelayAfterDelete - The default is the scan-interval. Values must be an integer followed by an 'm'. No unit of time other than minutes (m) is supported. 255 ScaleDownDelayAfterDelete *string 256 // ScaleDownDelayAfterFailure - The default is '3m'. Values must be an integer followed by an 'm'. No unit of time other than minutes (m) is supported. 257 ScaleDownDelayAfterFailure *string 258 // ScaleDownUnneededTime - The default is '10m'. Values must be an integer followed by an 'm'. No unit of time other than minutes (m) is supported. 259 ScaleDownUnneededTime *string 260 // ScaleDownUnreadyTime - The default is '20m'. Values must be an integer followed by an 'm'. No unit of time other than minutes (m) is supported. 261 ScaleDownUnreadyTime *string 262 // ScaleDownUtilizationThreshold - The default is '0.5'. 263 ScaleDownUtilizationThreshold *string 264 // SkipNodesWithLocalStorage - The default is true. 265 SkipNodesWithLocalStorage *string 266 // SkipNodesWithSystemPods - The default is true. 267 SkipNodesWithSystemPods *string 268 } 269 270 // OIDCIssuerProfile is the OIDC issuer profile of the Managed Cluster. 271 type OIDCIssuerProfile struct { 272 // Enabled is whether the OIDC issuer is enabled. 273 Enabled *bool 274 } 275 276 // ManagedClusterSecurityProfile defines the security profile for the cluster. 277 type ManagedClusterSecurityProfile struct { 278 // AzureKeyVaultKms defines Azure Key Vault key management service settings for the security profile. 279 AzureKeyVaultKms *AzureKeyVaultKms 280 281 // Defender defines Microsoft Defender settings for the security profile. 282 Defender *ManagedClusterSecurityProfileDefender 283 284 // ImageCleaner settings for the security profile. 285 ImageCleaner *ManagedClusterSecurityProfileImageCleaner 286 287 // Workloadidentity enables Kubernetes applications to access Azure cloud resources securely with Azure AD. 288 WorkloadIdentity *ManagedClusterSecurityProfileWorkloadIdentity 289 } 290 291 // ManagedClusterSecurityProfileDefender defines Microsoft Defender settings for the security profile. 292 type ManagedClusterSecurityProfileDefender struct { 293 // LogAnalyticsWorkspaceResourceID is the ID of the Log Analytics workspace that has to be associated with Microsoft Defender. 294 // When Microsoft Defender is enabled, this field is required and must be a valid workspace resource ID. 295 LogAnalyticsWorkspaceResourceID *string 296 297 // SecurityMonitoring profile defines the Microsoft Defender threat detection for Cloud settings for the security profile. 298 SecurityMonitoring *ManagedClusterSecurityProfileDefenderSecurityMonitoring 299 } 300 301 // ManagedClusterSecurityProfileDefenderSecurityMonitoring settings for the security profile threat detection. 302 type ManagedClusterSecurityProfileDefenderSecurityMonitoring struct { 303 // Enabled enables Defender threat detection 304 Enabled *bool 305 } 306 307 // ManagedClusterSecurityProfileImageCleaner removes unused images from nodes, freeing up disk space and helping to reduce attack surface area. 308 type ManagedClusterSecurityProfileImageCleaner struct { 309 // Enabled enables Image Cleaner on AKS cluster. 310 Enabled *bool 311 312 // Image Cleaner scanning interval in hours. 313 IntervalHours *int 314 } 315 316 // ManagedClusterSecurityProfileWorkloadIdentity defines Workload identity settings for the security profile. 317 type ManagedClusterSecurityProfileWorkloadIdentity struct { 318 // Enabled enables workload identity. 319 Enabled *bool 320 } 321 322 // AzureKeyVaultKms Azure Key Vault key management service settings for the security profile. 323 type AzureKeyVaultKms struct { 324 // Enabled enables Azure Key Vault key management service. The default is false. 325 Enabled *bool 326 327 // KeyID defines the Identifier of Azure Key Vault key. 328 // When Azure Key Vault key management service is enabled, this field is required and must be a valid key identifier. 329 KeyID *string 330 331 // KeyVaultNetworkAccess defines the network access of key vault. 332 // The possible values are Public and Private. 333 // Public means the key vault allows public access from all networks. 334 // Private means the key vault disables public access and enables private link. The default value is Public. 335 KeyVaultNetworkAccess *infrav1.KeyVaultNetworkAccessTypes 336 337 // KeyVaultResourceID is the Resource ID of key vault. When keyVaultNetworkAccess is Private, this field is required and must be a valid resource ID. 338 KeyVaultResourceID *string 339 } 340 341 // buildAutoScalerProfile builds the AutoScalerProfile for the ManagedClusterProperties. 342 func buildAutoScalerProfile(autoScalerProfile *AutoScalerProfile) *asocontainerservicev1.ManagedClusterProperties_AutoScalerProfile { 343 if autoScalerProfile == nil { 344 return nil 345 } 346 347 mcAutoScalerProfile := &asocontainerservicev1.ManagedClusterProperties_AutoScalerProfile{ 348 BalanceSimilarNodeGroups: autoScalerProfile.BalanceSimilarNodeGroups, 349 MaxEmptyBulkDelete: autoScalerProfile.MaxEmptyBulkDelete, 350 MaxGracefulTerminationSec: autoScalerProfile.MaxGracefulTerminationSec, 351 MaxNodeProvisionTime: autoScalerProfile.MaxNodeProvisionTime, 352 MaxTotalUnreadyPercentage: autoScalerProfile.MaxTotalUnreadyPercentage, 353 NewPodScaleUpDelay: autoScalerProfile.NewPodScaleUpDelay, 354 OkTotalUnreadyCount: autoScalerProfile.OkTotalUnreadyCount, 355 ScanInterval: autoScalerProfile.ScanInterval, 356 ScaleDownDelayAfterAdd: autoScalerProfile.ScaleDownDelayAfterAdd, 357 ScaleDownDelayAfterDelete: autoScalerProfile.ScaleDownDelayAfterDelete, 358 ScaleDownDelayAfterFailure: autoScalerProfile.ScaleDownDelayAfterFailure, 359 ScaleDownUnneededTime: autoScalerProfile.ScaleDownUnneededTime, 360 ScaleDownUnreadyTime: autoScalerProfile.ScaleDownUnreadyTime, 361 ScaleDownUtilizationThreshold: autoScalerProfile.ScaleDownUtilizationThreshold, 362 SkipNodesWithLocalStorage: autoScalerProfile.SkipNodesWithLocalStorage, 363 SkipNodesWithSystemPods: autoScalerProfile.SkipNodesWithSystemPods, 364 } 365 if autoScalerProfile.Expander != nil { 366 mcAutoScalerProfile.Expander = ptr.To(asocontainerservicev1.ManagedClusterProperties_AutoScalerProfile_Expander(*autoScalerProfile.Expander)) 367 } 368 369 return mcAutoScalerProfile 370 } 371 372 // getManagedClusterVersion gets the desired managed k8s version. 373 // If autoupgrade channels is set to patch, stable or rapid, clusters can be upgraded to higher version by AKS. 374 // If autoupgrade is triggered, existing kubernetes version will be higher than the user desired kubernetes version. 375 // CAPZ should honour the upgrade and it should not downgrade to the lower desired version. 376 func (s *ManagedClusterSpec) getManagedClusterVersion(existing *asocontainerservicev1.ManagedCluster) string { 377 if existing == nil || existing.Status.CurrentKubernetesVersion == nil { 378 return s.Version 379 } 380 return versions.GetHigherK8sVersion(s.Version, *existing.Status.CurrentKubernetesVersion) 381 } 382 383 // ResourceRef implements azure.ASOResourceSpecGetter. 384 func (s *ManagedClusterSpec) ResourceRef() genruntime.MetaObject { 385 if s.Preview { 386 return &asocontainerservicev1preview.ManagedCluster{ 387 ObjectMeta: metav1.ObjectMeta{ 388 Name: s.Name, 389 }, 390 } 391 } 392 return &asocontainerservicev1.ManagedCluster{ 393 ObjectMeta: metav1.ObjectMeta{ 394 Name: s.Name, 395 }, 396 } 397 } 398 399 // Parameters returns the parameters for the managed clusters. 400 // 401 //nolint:gocyclo // Function requires a lot of nil checks that raise complexity. 402 func (s *ManagedClusterSpec) Parameters(ctx context.Context, existingObj genruntime.MetaObject) (params genruntime.MetaObject, err error) { 403 ctx, _, done := tele.StartSpanWithLogger(ctx, "managedclusters.Service.Parameters") 404 defer done() 405 406 // If existing is preview, convert to stable then back to preview at the end of the function. 407 var existing *asocontainerservicev1.ManagedCluster 408 var existingStatus asocontainerservicev1preview.ManagedCluster_STATUS 409 if existingObj != nil { 410 if s.Preview { 411 existingPreview := existingObj.(*asocontainerservicev1preview.ManagedCluster) 412 existingStatus = existingPreview.Status 413 hub := &asocontainerservicev1hub.ManagedCluster{} 414 if err := existingPreview.ConvertTo(hub); err != nil { 415 return nil, err 416 } 417 stable := &asocontainerservicev1.ManagedCluster{} 418 if err := stable.ConvertFrom(hub); err != nil { 419 return nil, err 420 } 421 existing = stable.DeepCopy() 422 } else { 423 existing = existingObj.(*asocontainerservicev1.ManagedCluster) 424 } 425 } 426 427 managedCluster := existing 428 if managedCluster == nil { 429 managedCluster = &asocontainerservicev1.ManagedCluster{ 430 Spec: asocontainerservicev1.ManagedCluster_Spec{ 431 Tags: infrav1.Build(infrav1.BuildParams{ 432 Lifecycle: infrav1.ResourceLifecycleOwned, 433 ClusterName: s.ClusterName, 434 Name: ptr.To(s.Name), 435 Role: ptr.To(infrav1.CommonRole), 436 // Additional tags managed separately 437 }), 438 }, 439 } 440 } 441 442 managedCluster.Spec.AzureName = s.Name 443 managedCluster.Spec.Owner = &genruntime.KnownResourceReference{ 444 Name: s.ResourceGroup, 445 } 446 managedCluster.Spec.Identity = &asocontainerservicev1.ManagedClusterIdentity{ 447 Type: ptr.To(asocontainerservicev1.ManagedClusterIdentity_Type_SystemAssigned), 448 } 449 managedCluster.Spec.Location = &s.Location 450 managedCluster.Spec.NodeResourceGroup = &s.NodeResourceGroup 451 managedCluster.Spec.EnableRBAC = ptr.To(true) 452 managedCluster.Spec.DnsPrefix = s.DNSPrefix 453 454 if kubernetesVersion := s.getManagedClusterVersion(existing); kubernetesVersion != "" { 455 managedCluster.Spec.KubernetesVersion = &kubernetesVersion 456 } 457 458 managedCluster.Spec.ServicePrincipalProfile = &asocontainerservicev1.ManagedClusterServicePrincipalProfile{ 459 ClientId: ptr.To("msi"), 460 } 461 managedCluster.Spec.NetworkProfile = &asocontainerservicev1.ContainerServiceNetworkProfile{ 462 NetworkPlugin: azure.AliasOrNil[asocontainerservicev1.NetworkPlugin](&s.NetworkPlugin), 463 LoadBalancerSku: azure.AliasOrNil[asocontainerservicev1.ContainerServiceNetworkProfile_LoadBalancerSku](&s.LoadBalancerSKU), 464 NetworkPolicy: azure.AliasOrNil[asocontainerservicev1.ContainerServiceNetworkProfile_NetworkPolicy](&s.NetworkPolicy), 465 } 466 if s.NetworkDataplane != nil { 467 managedCluster.Spec.NetworkProfile.NetworkDataplane = ptr.To(asocontainerservicev1.ContainerServiceNetworkProfile_NetworkDataplane(*s.NetworkDataplane)) 468 } 469 managedCluster.Spec.AutoScalerProfile = buildAutoScalerProfile(s.AutoScalerProfile) 470 471 var decodedSSHPublicKey []byte 472 if s.SSHPublicKey != "" { 473 decodedSSHPublicKey, err = base64.StdEncoding.DecodeString(s.SSHPublicKey) 474 if err != nil { 475 return nil, errors.Wrap(err, "failed to decode SSHPublicKey") 476 } 477 } 478 479 if decodedSSHPublicKey != nil { 480 managedCluster.Spec.LinuxProfile = &asocontainerservicev1.ContainerServiceLinuxProfile{ 481 AdminUsername: ptr.To(azure.DefaultAKSUserName), 482 Ssh: &asocontainerservicev1.ContainerServiceSshConfiguration{ 483 PublicKeys: []asocontainerservicev1.ContainerServiceSshPublicKey{ 484 { 485 KeyData: ptr.To(string(decodedSSHPublicKey)), 486 }, 487 }, 488 }, 489 } 490 } 491 492 if s.NetworkPluginMode != nil { 493 managedCluster.Spec.NetworkProfile.NetworkPluginMode = ptr.To(asocontainerservicev1.ContainerServiceNetworkProfile_NetworkPluginMode(*s.NetworkPluginMode)) 494 } 495 496 if s.PodCIDR != "" { 497 managedCluster.Spec.NetworkProfile.PodCidr = &s.PodCIDR 498 } 499 500 if s.ServiceCIDR != "" { 501 managedCluster.Spec.NetworkProfile.ServiceCidr = &s.ServiceCIDR 502 managedCluster.Spec.NetworkProfile.DnsServiceIP = s.DNSServiceIP 503 if s.DNSServiceIP == nil { 504 ip, _, err := net.ParseCIDR(s.ServiceCIDR) 505 if err != nil { 506 return nil, fmt.Errorf("failed to parse service cidr: %w", err) 507 } 508 // HACK: set the last octet of the IP to .10 509 // This ensures the dns IP is valid in the service cidr without forcing the user 510 // to specify it in both the Capi cluster and the Azure control plane. 511 // https://golang.org/src/net/ip.go#L48 512 ip[15] = byte(10) 513 dnsIP := ip.String() 514 managedCluster.Spec.NetworkProfile.DnsServiceIP = &dnsIP 515 } 516 } 517 518 // OperatorSpec defines how the Secrets generated by ASO should look for the AKS cluster kubeconfigs. 519 // There is no prescribed naming convention that must be followed. 520 managedCluster.Spec.OperatorSpec = &asocontainerservicev1.ManagedClusterOperatorSpec{ 521 Secrets: &asocontainerservicev1.ManagedClusterOperatorSecrets{ 522 AdminCredentials: &genruntime.SecretDestination{ 523 Name: adminKubeconfigSecretName(s.ClusterName), 524 Key: secret.KubeconfigDataName, 525 }, 526 }, 527 } 528 529 if s.AADProfile != nil { 530 managedCluster.Spec.AadProfile = &asocontainerservicev1.ManagedClusterAADProfile{ 531 Managed: &s.AADProfile.Managed, 532 EnableAzureRBAC: &s.AADProfile.EnableAzureRBAC, 533 AdminGroupObjectIDs: s.AADProfile.AdminGroupObjectIDs, 534 } 535 if s.DisableLocalAccounts != nil { 536 managedCluster.Spec.DisableLocalAccounts = s.DisableLocalAccounts 537 } 538 539 if ptr.Deref(s.DisableLocalAccounts, false) { 540 // admin credentials cannot be fetched when local accounts are disabled 541 managedCluster.Spec.OperatorSpec.Secrets.AdminCredentials = nil 542 } 543 if s.AADProfile.Managed { 544 managedCluster.Spec.OperatorSpec.Secrets.UserCredentials = &genruntime.SecretDestination{ 545 Name: userKubeconfigSecretName(s.ClusterName), 546 Key: secret.KubeconfigDataName, 547 } 548 } 549 } 550 551 for i := range s.AddonProfiles { 552 if managedCluster.Spec.AddonProfiles == nil { 553 managedCluster.Spec.AddonProfiles = map[string]asocontainerservicev1.ManagedClusterAddonProfile{} 554 } 555 item := s.AddonProfiles[i] 556 addonProfile := asocontainerservicev1.ManagedClusterAddonProfile{ 557 Enabled: &item.Enabled, 558 } 559 if item.Config != nil { 560 addonProfile.Config = item.Config 561 } 562 managedCluster.Spec.AddonProfiles[item.Name] = addonProfile 563 } 564 565 if s.SKU != nil { 566 tierName := asocontainerservicev1.ManagedClusterSKU_Tier(s.SKU.Tier) 567 managedCluster.Spec.Sku = &asocontainerservicev1.ManagedClusterSKU{ 568 Name: ptr.To(asocontainerservicev1.ManagedClusterSKU_Name("Base")), 569 Tier: ptr.To(tierName), 570 } 571 } 572 573 if s.LoadBalancerProfile != nil { 574 managedCluster.Spec.NetworkProfile.LoadBalancerProfile = s.GetLoadBalancerProfile() 575 } 576 577 if s.APIServerAccessProfile != nil { 578 managedCluster.Spec.ApiServerAccessProfile = &asocontainerservicev1.ManagedClusterAPIServerAccessProfile{ 579 EnablePrivateCluster: s.APIServerAccessProfile.EnablePrivateCluster, 580 PrivateDNSZone: s.APIServerAccessProfile.PrivateDNSZone, 581 EnablePrivateClusterPublicFQDN: s.APIServerAccessProfile.EnablePrivateClusterPublicFQDN, 582 } 583 584 if s.APIServerAccessProfile.AuthorizedIPRanges != nil { 585 managedCluster.Spec.ApiServerAccessProfile.AuthorizedIPRanges = s.APIServerAccessProfile.AuthorizedIPRanges 586 } 587 } 588 589 if s.OutboundType != nil { 590 managedCluster.Spec.NetworkProfile.OutboundType = ptr.To(asocontainerservicev1.ContainerServiceNetworkProfile_OutboundType(*s.OutboundType)) 591 } 592 593 if s.Identity != nil { 594 managedCluster.Spec.Identity, err = getIdentity(s.Identity) 595 if err != nil { 596 return nil, errors.Wrapf(err, "Identity is not valid: %s", err) 597 } 598 } 599 600 if s.KubeletUserAssignedIdentity != "" { 601 managedCluster.Spec.IdentityProfile = map[string]asocontainerservicev1.UserAssignedIdentity{ 602 kubeletIdentityKey: { 603 ResourceReference: &genruntime.ResourceReference{ 604 ARMID: s.KubeletUserAssignedIdentity, 605 }, 606 }, 607 } 608 } 609 610 if s.HTTPProxyConfig != nil { 611 managedCluster.Spec.HttpProxyConfig = &asocontainerservicev1.ManagedClusterHTTPProxyConfig{ 612 HttpProxy: s.HTTPProxyConfig.HTTPProxy, 613 HttpsProxy: s.HTTPProxyConfig.HTTPSProxy, 614 TrustedCa: s.HTTPProxyConfig.TrustedCA, 615 } 616 617 if s.HTTPProxyConfig.NoProxy != nil { 618 managedCluster.Spec.HttpProxyConfig.NoProxy = s.HTTPProxyConfig.NoProxy 619 } 620 } 621 622 if s.OIDCIssuerProfile != nil { 623 managedCluster.Spec.OidcIssuerProfile = &asocontainerservicev1.ManagedClusterOIDCIssuerProfile{ 624 Enabled: s.OIDCIssuerProfile.Enabled, 625 } 626 if ptr.Deref(s.OIDCIssuerProfile.Enabled, false) { 627 managedCluster.Spec.OperatorSpec.ConfigMaps = &asocontainerservicev1.ManagedClusterOperatorConfigMaps{ 628 OIDCIssuerProfile: &genruntime.ConfigMapDestination{ 629 Name: oidcIssuerURLConfigMapName(s.ClusterName), 630 Key: oidcIssuerProfileURL, 631 }, 632 } 633 } 634 } 635 636 if s.AutoUpgradeProfile != nil { 637 managedCluster.Spec.AutoUpgradeProfile = &asocontainerservicev1.ManagedClusterAutoUpgradeProfile{ 638 UpgradeChannel: (*asocontainerservicev1.ManagedClusterAutoUpgradeProfile_UpgradeChannel)(s.AutoUpgradeProfile.UpgradeChannel), 639 } 640 } 641 642 if s.SecurityProfile != nil { 643 securityProfile := &asocontainerservicev1.ManagedClusterSecurityProfile{} 644 if s.SecurityProfile.AzureKeyVaultKms != nil { 645 securityProfile.AzureKeyVaultKms = &asocontainerservicev1.AzureKeyVaultKms{ 646 Enabled: s.SecurityProfile.AzureKeyVaultKms.Enabled, 647 KeyId: s.SecurityProfile.AzureKeyVaultKms.KeyID, 648 } 649 if s.SecurityProfile.AzureKeyVaultKms.KeyVaultNetworkAccess != nil { 650 keyVaultNetworkAccess := string(*s.SecurityProfile.AzureKeyVaultKms.KeyVaultNetworkAccess) 651 securityProfile.AzureKeyVaultKms.KeyVaultNetworkAccess = ptr.To(asocontainerservicev1.AzureKeyVaultKms_KeyVaultNetworkAccess(keyVaultNetworkAccess)) 652 } 653 if s.SecurityProfile.AzureKeyVaultKms.KeyVaultResourceID != nil { 654 securityProfile.AzureKeyVaultKms.KeyVaultResourceReference = &genruntime.ResourceReference{ 655 ARMID: *s.SecurityProfile.AzureKeyVaultKms.KeyVaultResourceID, 656 } 657 } 658 } 659 if s.SecurityProfile.Defender != nil { 660 securityProfile.Defender = &asocontainerservicev1.ManagedClusterSecurityProfileDefender{ 661 LogAnalyticsWorkspaceResourceReference: &genruntime.ResourceReference{ 662 ARMID: *s.SecurityProfile.Defender.LogAnalyticsWorkspaceResourceID, 663 }, 664 } 665 if s.SecurityProfile.Defender.SecurityMonitoring != nil { 666 securityProfile.Defender.SecurityMonitoring = &asocontainerservicev1.ManagedClusterSecurityProfileDefenderSecurityMonitoring{ 667 Enabled: s.SecurityProfile.Defender.SecurityMonitoring.Enabled, 668 } 669 } 670 } 671 if s.SecurityProfile.ImageCleaner != nil { 672 securityProfile.ImageCleaner = &asocontainerservicev1.ManagedClusterSecurityProfileImageCleaner{ 673 Enabled: s.SecurityProfile.ImageCleaner.Enabled, 674 IntervalHours: s.SecurityProfile.ImageCleaner.IntervalHours, 675 } 676 } 677 if s.SecurityProfile.WorkloadIdentity != nil { 678 securityProfile.WorkloadIdentity = &asocontainerservicev1.ManagedClusterSecurityProfileWorkloadIdentity{ 679 Enabled: s.SecurityProfile.WorkloadIdentity.Enabled, 680 } 681 } 682 managedCluster.Spec.SecurityProfile = securityProfile 683 } 684 685 // Only include AgentPoolProfiles during initial cluster creation. Agent pools are managed solely by the 686 // AzureManagedMachinePool controller thereafter. 687 var prevAgentPoolProfiles []asocontainerservicev1preview.ManagedClusterAgentPoolProfile 688 managedCluster.Spec.AgentPoolProfiles = nil 689 if managedCluster.Status.AgentPoolProfiles == nil { 690 // Add all agent pools to cluster spec that will be submitted to the API 691 agentPoolSpecs, err := s.GetAllAgentPools() 692 if err != nil { 693 return nil, errors.Wrapf(err, "failed to get agent pool specs for managed cluster %s", s.Name) 694 } 695 696 scheme := runtime.NewScheme() 697 if err := asocontainerservicev1.AddToScheme(scheme); err != nil { 698 return nil, errors.Wrap(err, "error constructing scheme") 699 } 700 if err := asocontainerservicev1preview.AddToScheme(scheme); err != nil { 701 return nil, errors.Wrap(err, "error constructing scheme") 702 } 703 for _, agentPoolSpec := range agentPoolSpecs { 704 agentPool, err := aso.PatchedParameters(ctx, scheme, agentPoolSpec, nil) 705 if err != nil { 706 return nil, errors.Wrapf(err, "failed to get agent pool parameters for managed cluster %s", s.Name) 707 } 708 agentPoolSpecTyped := agentPoolSpec.(*agentpools.AgentPoolSpec) 709 if s.Preview { 710 agentPoolTyped := agentPool.(*asocontainerservicev1preview.ManagedClustersAgentPool) 711 agentPoolTyped.Spec.AzureName = agentPoolSpecTyped.AzureName 712 profile := converters.AgentPoolToManagedClusterAgentPoolPreviewProfile(agentPoolTyped) 713 prevAgentPoolProfiles = append(prevAgentPoolProfiles, profile) 714 } else { 715 agentPoolTyped := agentPool.(*asocontainerservicev1.ManagedClustersAgentPool) 716 agentPoolTyped.Spec.AzureName = agentPoolSpecTyped.AzureName 717 profile := converters.AgentPoolToManagedClusterAgentPoolProfile(agentPoolTyped) 718 managedCluster.Spec.AgentPoolProfiles = append(managedCluster.Spec.AgentPoolProfiles, profile) 719 } 720 } 721 } 722 723 if s.Preview { 724 hub := &asocontainerservicev1hub.ManagedCluster{} 725 if err := managedCluster.ConvertTo(hub); err != nil { 726 return nil, err 727 } 728 prev := &asocontainerservicev1preview.ManagedCluster{} 729 if err := prev.ConvertFrom(hub); err != nil { 730 return nil, err 731 } 732 if existing != nil { 733 prev.Status = existingStatus 734 } 735 if prevAgentPoolProfiles != nil { 736 prev.Spec.AgentPoolProfiles = prevAgentPoolProfiles 737 } 738 return prev, nil 739 } 740 741 return managedCluster, nil 742 } 743 744 // GetLoadBalancerProfile returns an asocontainerservicev1.ManagedClusterLoadBalancerProfile from the 745 // information present in ManagedClusterSpec.LoadBalancerProfile. 746 func (s *ManagedClusterSpec) GetLoadBalancerProfile() (loadBalancerProfile *asocontainerservicev1.ManagedClusterLoadBalancerProfile) { 747 loadBalancerProfile = &asocontainerservicev1.ManagedClusterLoadBalancerProfile{ 748 AllocatedOutboundPorts: s.LoadBalancerProfile.AllocatedOutboundPorts, 749 IdleTimeoutInMinutes: s.LoadBalancerProfile.IdleTimeoutInMinutes, 750 } 751 if s.LoadBalancerProfile.ManagedOutboundIPs != nil { 752 loadBalancerProfile.ManagedOutboundIPs = &asocontainerservicev1.ManagedClusterLoadBalancerProfile_ManagedOutboundIPs{Count: s.LoadBalancerProfile.ManagedOutboundIPs} 753 } 754 if len(s.LoadBalancerProfile.OutboundIPPrefixes) > 0 { 755 loadBalancerProfile.OutboundIPPrefixes = &asocontainerservicev1.ManagedClusterLoadBalancerProfile_OutboundIPPrefixes{ 756 PublicIPPrefixes: convertToResourceReferences(s.LoadBalancerProfile.OutboundIPPrefixes), 757 } 758 } 759 if len(s.LoadBalancerProfile.OutboundIPs) > 0 { 760 loadBalancerProfile.OutboundIPs = &asocontainerservicev1.ManagedClusterLoadBalancerProfile_OutboundIPs{ 761 PublicIPs: convertToResourceReferences(s.LoadBalancerProfile.OutboundIPs), 762 } 763 } 764 return 765 } 766 767 func convertToResourceReferences(resources []string) []asocontainerservicev1.ResourceReference { 768 resourceReferences := make([]asocontainerservicev1.ResourceReference, len(resources)) 769 for i := range resources { 770 resourceReferences[i] = asocontainerservicev1.ResourceReference{ 771 Reference: &genruntime.ResourceReference{ 772 ARMID: resources[i], 773 }, 774 } 775 } 776 return resourceReferences 777 } 778 779 func getIdentity(identity *infrav1.Identity) (managedClusterIdentity *asocontainerservicev1.ManagedClusterIdentity, err error) { 780 if identity.Type == "" { 781 return 782 } 783 784 managedClusterIdentity = &asocontainerservicev1.ManagedClusterIdentity{ 785 Type: ptr.To(asocontainerservicev1.ManagedClusterIdentity_Type(identity.Type)), 786 } 787 if ptr.Deref(managedClusterIdentity.Type, "") == asocontainerservicev1.ManagedClusterIdentity_Type_UserAssigned { 788 if identity.UserAssignedIdentityResourceID == "" { 789 err = errors.Errorf("Identity is set to \"UserAssigned\" but no UserAssignedIdentityResourceID is present") 790 return 791 } 792 managedClusterIdentity.UserAssignedIdentities = []asocontainerservicev1.UserAssignedIdentityDetails{ 793 { 794 Reference: genruntime.ResourceReference{ 795 ARMID: identity.UserAssignedIdentityResourceID, 796 }, 797 }, 798 } 799 } 800 return 801 } 802 803 func adminKubeconfigSecretName(clusterName string) string { 804 return secret.Name(clusterName+"-aso", secret.Kubeconfig) 805 } 806 807 func oidcIssuerURLConfigMapName(clusterName string) string { 808 return secret.Name(clusterName+"-aso", "oidc-issuer-profile") 809 } 810 811 func userKubeconfigSecretName(clusterName string) string { 812 return secret.Name(clusterName+"-user-aso", secret.Kubeconfig) 813 } 814 815 // WasManaged implements azure.ASOResourceSpecGetter. 816 func (s *ManagedClusterSpec) WasManaged(resource genruntime.MetaObject) bool { 817 // CAPZ has never supported BYO managed clusters. 818 return true 819 } 820 821 var _ aso.TagsGetterSetter[genruntime.MetaObject] = (*ManagedClusterSpec)(nil) 822 823 // GetAdditionalTags implements aso.TagsGetterSetter. 824 func (s *ManagedClusterSpec) GetAdditionalTags() infrav1.Tags { 825 return s.Tags 826 } 827 828 // GetDesiredTags implements aso.TagsGetterSetter. 829 func (s *ManagedClusterSpec) GetDesiredTags(resource genruntime.MetaObject) infrav1.Tags { 830 if s.Preview { 831 return resource.(*asocontainerservicev1preview.ManagedCluster).Spec.Tags 832 } 833 return resource.(*asocontainerservicev1.ManagedCluster).Spec.Tags 834 } 835 836 // SetTags implements aso.TagsGetterSetter. 837 func (s *ManagedClusterSpec) SetTags(resource genruntime.MetaObject, tags infrav1.Tags) { 838 if s.Preview { 839 resource.(*asocontainerservicev1preview.ManagedCluster).Spec.Tags = tags 840 return 841 } 842 resource.(*asocontainerservicev1.ManagedCluster).Spec.Tags = tags 843 } 844 845 var _ aso.Patcher = (*ManagedClusterSpec)(nil) 846 847 // ExtraPatches implements aso.Patcher. 848 func (s *ManagedClusterSpec) ExtraPatches() []string { 849 return s.Patches 850 }