File: AzureKubernetesEnvironmentResource.cs
Web Access
Project: src\src\Aspire.Hosting.Azure.Kubernetes\Aspire.Hosting.Azure.Kubernetes.csproj (Aspire.Hosting.Azure.Kubernetes)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
#pragma warning disable ASPIREAZURE003 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
#pragma warning disable ASPIREPIPELINES001
#pragma warning disable ASPIREAZURE001
 
using Aspire.Hosting.Kubernetes;
using Aspire.Hosting.Pipelines;
 
namespace Aspire.Hosting.Azure.Kubernetes;
 
/// <summary>
/// Represents an Azure Kubernetes Service (AKS) environment resource that provisions
/// an AKS cluster and serves as a compute environment for Kubernetes workloads.
/// </summary>
public partial class AzureKubernetesEnvironmentResource :
    AzureProvisioningResource,
    IAzureComputeEnvironmentResource,
    IAzureNspAssociationTarget
{
    /// <summary>
    /// Initializes a new instance of the <see cref="AzureKubernetesEnvironmentResource"/> class.
    /// </summary>
    /// <param name="name">The name of the resource.</param>
    /// <param name="configureInfrastructure">Callback to configure the Azure infrastructure.</param>
    public AzureKubernetesEnvironmentResource(
        string name,
        Action<AzureResourceInfrastructure> configureInfrastructure)
        : base(name, configureInfrastructure)
    {
        // Add pipeline step annotation to register per-environment AKS steps:
        //   - prepare-aks-{name}: applies node-pool/workload-identity annotations to compute
        //     resources targeted at this AKS env. Runs before BeforeStart so the inner
        //     KubernetesEnvironmentResource's prepare-deployment-targets-{k8s-name} step
        //     can observe the annotations.
        //   - aks-get-credentials-{name}: fetches AKS credentials into an isolated
        //     kubeconfig file after AKS is provisioned, before Helm prepare runs.
        Annotations.Add(new PipelineStepAnnotation(_ =>
        {
            var k8sEnv = KubernetesEnvironment;
 
            var prepareStep = new PipelineStep
            {
                Name = $"prepare-aks-{Name}",
                Description = $"Prepares Azure Kubernetes Service environment {Name}.",
                Action = ctx => PrepareAksEnvironmentAsync(ctx),
                DependsOnSteps = [WellKnownPipelineSteps.ValidateComputeEnvironments],
                RequiredBySteps =
                [
                    WellKnownPipelineSteps.BeforeStart,
                    // Ensure this runs before the inner K8s env materializes service resources,
                    // because that step reads node-pool and workload-identity annotations
                    // applied by PrepareAksEnvironmentAsync.
                    $"prepare-deployment-targets-{k8sEnv.Name}"
                ]
            };
 
            var getCredentialsStep = new PipelineStep
            {
                Name = $"aks-get-credentials-{Name}",
                Description = $"Fetches AKS credentials for {Name}",
                Action = ctx => GetAksCredentialsAsync(ctx),
                // Run after ALL Azure infrastructure is provisioned (including the AKS cluster).
                DependsOnSteps = [AzureEnvironmentResource.ProvisionInfrastructureStepName],
                // Must complete before the Helm prepare step on the inner K8s env.
                RequiredBySteps = [$"prepare-{k8sEnv.Name}"]
            };
 
            return Task.FromResult<IEnumerable<PipelineStep>>([prepareStep, getCredentialsStep]);
        }));
    }
 
    /// <summary>
    /// Gets the underlying Kubernetes environment resource used for Helm-based deployment.
    /// </summary>
    internal KubernetesEnvironmentResource KubernetesEnvironment { get; set; } = default!;
 
    /// <summary>
    /// Gets the resource ID of the AKS cluster.
    /// </summary>
    public BicepOutputReference Id => new("id", this);
 
    /// <summary>
    /// Gets the fully qualified domain name of the AKS cluster.
    /// </summary>
    public BicepOutputReference ClusterFqdn => new("clusterFqdn", this);
 
    /// <summary>
    /// Gets the OIDC issuer URL for the AKS cluster, used for workload identity federation.
    /// </summary>
    public BicepOutputReference OidcIssuerUrl => new("oidcIssuerUrl", this);
 
    /// <summary>
    /// Gets the object ID of the kubelet managed identity.
    /// </summary>
    public BicepOutputReference KubeletIdentityObjectId => new("kubeletIdentityObjectId", this);
 
    /// <summary>
    /// Gets the name of the node resource group.
    /// </summary>
    public BicepOutputReference NodeResourceGroup => new("nodeResourceGroup", this);
 
    /// <summary>
    /// Gets the name output reference for the AKS cluster.
    /// </summary>
    public BicepOutputReference NameOutputReference => new("name", this);
 
    /// <summary>
    /// Gets or sets the Kubernetes version for the AKS cluster.
    /// </summary>
    internal string? KubernetesVersion { get; set; }
 
    /// <summary>
    /// Gets or sets whether OIDC issuer is enabled on the cluster.
    /// </summary>
    internal bool OidcIssuerEnabled { get; set; } = true;
 
    /// <summary>
    /// Gets or sets whether workload identity is enabled on the cluster.
    /// </summary>
    internal bool WorkloadIdentityEnabled { get; set; } = true;
 
    /// <summary>
    /// Gets or sets the Log Analytics workspace resource for monitoring.
    /// </summary>
    internal AzureLogAnalyticsWorkspaceResource? LogAnalyticsWorkspace { get; set; }
 
    /// <summary>
    /// Gets or sets whether Container Insights is enabled.
    /// </summary>
    internal bool ContainerInsightsEnabled { get; set; }
 
    /// <summary>
    /// Gets the node pool configurations.
    /// </summary>
    internal List<AksNodePoolConfig> NodePools { get; } =
    [
        new AksNodePoolConfig("system", "Standard_D2s_v5", 1, 3, AksNodePoolMode.System)
    ];
 
    /// <summary>
    /// Gets the per-node-pool subnet overrides. Key is the pool name.
    /// </summary>
    internal Dictionary<string, BicepOutputReference> NodePoolSubnets { get; } = [];
 
    /// <summary>
    /// Gets the workload identity mappings. Key is the resource name, value is the identity resource.
    /// Used to generate federated identity credentials in Bicep.
    /// </summary>
    internal Dictionary<string, IAppIdentityResource> WorkloadIdentities { get; } = [];
 
    /// <summary>
    /// Gets or sets the network profile for the AKS cluster.
    /// </summary>
    internal AksNetworkProfile? NetworkProfile { get; set; }
 
    /// <summary>
    /// Gets or sets whether the cluster should be private.
    /// </summary>
    internal bool IsPrivateCluster { get; set; }
 
    /// <summary>
    /// Gets or sets the default container registry auto-created for this AKS environment.
    /// </summary>
    internal AzureContainerRegistryResource? DefaultContainerRegistry { get; set; }
 
    /// <summary>
    /// Gets the load balancer resources registered against this AKS environment via
    /// <see cref="AzureKubernetesEnvironmentExtensions.AddLoadBalancer"/>. Used by
    /// the Bicep emission to synthesize per-LB role assignments granting the
    /// AKS-auto-created AGC controller identity permission to join each LB subnet.
    /// </summary>
    internal List<AzureKubernetesLoadBalancerResource> LoadBalancers { get; } = [];
 
    /// <summary>
    /// Gets or sets whether the AKS managed Gateway API installation is enabled on the
    /// cluster. Toggled internally by <see cref="AzureKubernetesEnvironmentExtensions.AddLoadBalancer"/>;
    /// not exposed as a public extension because it's only useful in combination with the
    /// AGC ALB controller add-on (<see cref="ApplicationLoadBalancerEnabled"/>) today.
    /// </summary>
    internal bool GatewayApiEnabled { get; set; }
 
    /// <summary>
    /// Gets or sets whether the Azure Application Gateway for Containers (AGC) ALB
    /// controller add-on is enabled on the cluster. Toggled internally by
    /// <see cref="AzureKubernetesEnvironmentExtensions.AddLoadBalancer"/>.
    /// </summary>
    internal bool ApplicationLoadBalancerEnabled { get; set; }
 
    /// <summary>
    /// Whether the cluster needs to be emitted using a preview Bicep API version because
    /// it depends on <c>ingressProfile.gatewayAPI</c> or <c>ingressProfile.applicationLoadBalancer</c>,
    /// neither of which is in any stable AKS API version yet (latest stable
    /// <c>2026-01-01</c> doesn't have them; <c>gatewayAPI</c> first appears in
    /// <c>2025-08-02-preview</c>, <c>applicationLoadBalancer</c> in
    /// <c>2025-09-02-preview</c>).
    /// </summary>
    internal bool RequiresPreviewIngressApi => GatewayApiEnabled || ApplicationLoadBalancerEnabled;
}