Deploy a Windows Server container on an Azure Kubernetes Service (AKS) cluster using Azure CLI

Azure Kubernetes Service (AKS) is a managed Kubernetes service that lets you quickly deploy and manage clusters. In this article, you use Azure CLI to deploy an AKS cluster that runs Windows Server containers. You also deploy an ASP.NET sample application in a Windows Server container to the cluster.

Note

To get started with quickly provisioning an AKS cluster, this article includes steps to deploy a cluster with default settings for evaluation purposes only. Before deploying a production-ready cluster, we recommend that you familiarize yourself with our [baseline reference architecture][baseline-reference-architecture] to consider how it aligns with your business requirements.

Before you begin

This quickstart assumes a basic understanding of Kubernetes concepts. For more information, see Kubernetes core concepts for Azure Kubernetes Service (AKS).

  • If you don't have an Azure subscription, create a trial account before you begin.

  • Prerequisites

    You can use the local Azure CLI.

  • This article requires version 2.0.64 or later of the Azure CLI.

  • Make sure that the identity you're using to create your cluster has the appropriate minimum permissions. For more details on access and identity for AKS, see Access and identity options for Azure Kubernetes Service (AKS).

  • If you have multiple Azure subscriptions, select the appropriate subscription ID in which the resources should be billed using the az account set command. For more information, see How to manage Azure subscriptions – Azure CLI.

  • If you're using --os-sku Windows2025, you need to install the aks-preview extension and register the preview flag. The minimum version is 18.0.0b40.

Install the aks-preview extension

Important

AKS preview features are available on a self-service, opt-in basis. Previews are provided "as is" and "as available," and they're excluded from the service-level agreements and limited warranty. AKS previews are partially covered by customer support on a best-effort basis. As such, these features aren't meant for production use. For more information, see the following support articles:

  1. Install the aks-preview Azure CLI extension using the az extension add command.
az extension add --name aks-preview
  1. Update to the latest version of the extension using the az extension update command. Windows Server 2025 requires a minimum of 18.0.0b40.
az extension update --name aks-preview

Register the AksWindows2025Preview feature flag

  1. Register the AksWindows2025Preview feature flag using the [az feature register][az-feature-register] command.
az feature register --name AksWindows2025Preview --namespace Microsoft.ContainerService
  1. Verify the registration status using the [az feature show][az-feature-show] command. It takes a few minutes for the status to show Registered.
az feature show --name AksWindows2025Preview --namespace Microsoft.ContainerService
  1. When the status reflects Registered, refresh the registration of the Microsoft.ContainerService resource provider using the [az provider register][az-provider-register] command.

    az provider register --namespace Microsoft.ContainerService
    

Create a resource group

An Azure resource group is a logical group in which Azure resources are deployed and managed. When you create a resource group, you're asked to specify a location. This location is where resource group metadata is stored and where your resources run in Azure if you don't specify another region during resource creation.

  • Create a resource group using the az group create command. The following example creates a resource group named myResourceGroup in the Chinanorth3 location.

    export RANDOM_SUFFIX=$(openssl rand -hex 3)
    export REGION="canadacentral"
    export MY_RESOURCE_GROUP_NAME="myAKSResourceGroup$RANDOM_SUFFIX"
    az group create --name $MY_RESOURCE_GROUP_NAME --location $REGION
    

    Results:

    {
      "id": "/subscriptions/xxxxx-xxxxx-xxxxx-xxxxx/resourceGroups/myResourceGroupxxxxx",
      "location": "WestUS2",
      "managedBy": null,
      "name": "myResourceGroupxxxxx",
      "properties": {
        "provisioningState": "Succeeded"
      },
      "tags": null,
      "type": "Microsoft.Resources/resourceGroups"
    }
    

Create an AKS cluster

In this section, we create an AKS cluster with the following configuration:

  • The cluster is configured with two nodes to ensure it operates reliably. A node is an Azure virtual machine (VM) that runs the Kubernetes node components and container runtime.
  • The --windows-admin-password and --windows-admin-username parameters set the administrator credentials for any Windows Server nodes on the cluster and must meet Windows Server password requirements.
  • The node pool uses VirtualMachineScaleSets.

Use the following steps to create the AKS cluster with Azure CLI:

  1. Create a username to use as administrator credentials for the Windows Server nodes on your cluster.

    export WINDOWS_USERNAME="winadmin"
    
  2. Create a password for the administrator username you created in the previous step. The password must be a minimum of 14 characters and meet the Windows Server password complexity requirements.

    export WINDOWS_PASSWORD=$(echo "P@ssw0rd$(openssl rand -base64 10 | tr -dc 'A-Za-z0-9!@#$%^&*()' | cut -c1-6)")
    
  3. Create your cluster using the az aks create command and specify the --windows-admin-username and --windows-admin-password parameters. The following example command creates a cluster using the values from WINDOWS_USERNAME and WINDOWS_PASSWORD you set in the previous commands. A random suffix is appended to the cluster name for uniqueness.

    export MY_AKS_CLUSTER="myAKSCluster$RANDOM_SUFFIX"
    az aks create \
        --resource-group $MY_RESOURCE_GROUP_NAME \
        --name $MY_AKS_CLUSTER \
        --node-count 2 \
        --enable-addons monitoring \
        --generate-ssh-keys \
        --windows-admin-username $WINDOWS_USERNAME \
        --windows-admin-password $WINDOWS_PASSWORD \
        --vm-set-type VirtualMachineScaleSets \
        --network-plugin azure
    

    After a few minutes, the command completes and returns JSON-formatted information about the cluster. Occasionally, the cluster can take longer than a few minutes to provision. Allow up to 10 minutes for provisioning.

    If you get a password validation error, and the password that you set meets the length and complexity requirements, try creating your resource group in another region. Then try creating the cluster with the new resource group.

    If you don't specify an administrator username and password when creating the node pool, the username is set to azureuser and the password is set to a random value. For more information, see the Windows Server FAQ

    You can't change the administrator username, but you can change the administrator password that your AKS cluster uses for Windows Server nodes using az aks update. For more information, see Windows Server FAQ.

    To run an AKS cluster that supports node pools for Windows Server containers, your cluster needs to use a network policy that uses Azure CNI (advanced) network plugin. The --network-plugin azure parameter specifies Azure CNI.

Add a node pool

By default, all AKS clusters are created with a node pool that can run Linux containers. You must add a Windows node pool that can run Windows Server containers alongside the Linux node pool. To check if you have a Windows node pool in your cluster, you can view the nodes on your cluster using the kubectl get nodes -o wide command.

To create a Windows node pool, you need to specify a supported OsType and OsSku. Use the information in the following table to determine which is appropriate for your cluster:

OsType OsSku Default Supported K8s versions Details
windows Windows2025 Currently in preview. Not default. 1.32+ Updated defaults: containerd 2.0, Generation 2 image is used by default.
windows Windows2022 Default in K8s 1.25-1.34 Not available in K8s 1.35+ Retires in March 2027. Updated defaults: FIPS is enabled by default.
windows Windows2019 Default in K8s 1.24 and below Not available in K8s 1.32+ Retires in March 2026.

Windows Server 2022 is the default operating system for Kubernetes versions 1.25-1.34. Windows Server 2019 is the default OS for earlier versions. If you don't specify a particular OS SKU, Azure creates the new node pool with the default SKU for the version of Kubernetes used by the cluster.

Note

  • Windows Server 2022 retires after Kubernetes version 1.34 reaches end of support and won't be supported in Kubernetes version 1.35 and above.
  • Windows Server 2019 retires after Kubernetes version 1.32 reaches end of support and won't be supported in Kubernetes version 1.33 and above.

For more information, see AKS release notes. To stay up to date on the latest Windows Server OS versions and learn more about our roadmap of what's planned for support on AKS, see our AKS public roadmap.

  • Add a Windows node pool using the [az aks nodepool add][az-aks-nodepool-add] command with a specified OsType and OsSku. If you don't specify a particular OS SKU, Azure creates the new node pool with the default SKU for the version of Kubernetes used by the cluster.

    az aks nodepool add \
      --resource-group $MY_RESOURCE_GROUP_NAME \
      --cluster-name $MY_AKS_CLUSTER \
      --os-type Windows \
      --os-sku Windows2022 \
      --name npwin \
      --node-count 1
    

    This command creates a new node pool named npwin and adds it to myAKSCluster. The command also uses the default subnet in the default virtual network created when running az aks create.

Connect to the cluster

You use kubectl, the Kubernetes command-line client, to manage your Kubernetes clusters. To you want to install kubectl locally, you can use the az aks install-cli command.

  1. Configure kubectl to connect to your Kubernetes cluster using the az aks get-credentials command. This command downloads credentials and configures the Kubernetes CLI to use them.

    az aks get-credentials --resource-group $MY_RESOURCE_GROUP_NAME --name $MY_AKS_CLUSTER
    
  2. Verify the connection to your cluster using the kubectl get command, which returns a list of the cluster nodes.

    kubectl get nodes -o wide
    

    The following example output shows all nodes in the cluster. Make sure the status of all nodes is Ready:

    NAME                                STATUS   ROLES   AGE   VERSION   INTERNAL-IP   EXTERNAL-IP   OS-IMAGE                         KERNEL-VERSION      CONTAINER-RUNTIME
    aks-nodepool1-20786768-vmss000000   Ready    agent   22h   v1.27.7   10.224.0.4    <none>        Ubuntu 22.04.3 LTS               5.15.0-1052-azure   containerd://1.7.5-1
    aks-nodepool1-20786768-vmss000001   Ready    agent   22h   v1.27.7   10.224.0.33   <none>        Ubuntu 22.04.3 LTS               5.15.0-1052-azure   containerd://1.7.5-1
    aksnpwin000000                      Ready    agent   20h   v1.27.7   10.224.0.62   <none>        Windows Server 2022 Datacenter   10.0.20348.2159     containerd://1.6.21+azure
    

    Note

    The container runtime for each node pool is shown under CONTAINER-RUNTIME. The container runtime values begin with containerd://, which means that they each use containerd for the container runtime.

Deploy the application

A Kubernetes manifest file defines a desired state for the cluster, such as what container images to run. In this article, you use a manifest to create all objects needed to run the ASP.NET sample application in a Windows Server container. This manifest includes a Kubernetes deployment for the ASP.NET sample application and an external Kubernetes service to access the application from the internet.

The ASP.NET sample application is provided as part of the .NET Framework Samples and runs in a Windows Server container. AKS requires Windows Server containers to be based on images of Windows Server 2019 or greater. The Kubernetes manifest file must also define a node selector to tell your AKS cluster to run your ASP.NET sample application's pod on a node that can run Windows Server containers.

  1. Create a file named sample.yaml and copy in the following YAML definition:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: sample
      labels:
        app: sample
    spec:
      replicas: 1
      template:
        metadata:
          name: sample
          labels:
            app: sample
        spec:
          nodeSelector:
            "kubernetes.io/os": windows
          containers:
          - name: sample
            image: mcr.azk8s.cn/dotnet/framework/samples:aspnetapp
            resources:
              limits:
                cpu: 1
                memory: 800M
            ports:
              - containerPort: 80
      selector:
        matchLabels:
          app: sample
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: sample
    spec:
      type: LoadBalancer
      ports:
      - protocol: TCP
        port: 80
      selector:
        app: sample
    

    For a breakdown of YAML manifest files, see Deployments and YAML manifests.

  2. Deploy the application using the kubectl apply command and specify the name of your YAML manifest.

    kubectl apply -f sample.yaml
    

    The following example output shows the deployment and service created successfully:

    {
      "deployment.apps/sample": "created",
      "service/sample": "created"
    }
    

Test the application

When the application runs, a Kubernetes service exposes the application front end to the internet. This process can take a few minutes to complete. Occasionally, the service can take longer than a few minutes to provision. Allow up to 10 minutes for provisioning.

  1. Check the status of the deployed pods using the kubectl get pods command. Make sure all pods are Running before proceeding.

    kubectl get pods
    
  2. Monitor progress using the kubectl get service command with the --watch argument.

    while true; do
      export EXTERNAL_IP=$(kubectl get service sample -o jsonpath="{.status.loadBalancer.ingress[0].ip}" 2>/dev/null)
      if [[ -n "$EXTERNAL_IP" && "$EXTERNAL_IP" != "<pending>" ]]; then
        kubectl get service sample
        break
      fi
      echo "Still waiting for external IP assignment..."
      sleep 5
    done
    

    Initially, the output shows the EXTERNAL-IP for the sample service as pending:

    NAME     TYPE           CLUSTER-IP     EXTERNAL-IP       PORT(S)        AGE
    sample   LoadBalancer   xx.xx.xx.xx    pending          xx:xxxx/TCP     2m
    
  3. When the EXTERNAL-IP address changes from pending to an actual public IP address, use CTRL-C to stop the kubectl watch process.

    The following example output shows a valid public IP address assigned to the service:

    {
      "NAME": "sample",
      "TYPE": "LoadBalancer",
      "CLUSTER-IP": "10.0.37.27",
      "EXTERNAL-IP": "52.179.23.131",
      "PORT(S)": "80:30572/TCP",
      "AGE": "2m"
    }
    
  4. See the sample app in action by opening a web browser to the external IP address of your service after a few minutes.

    Screenshot of browsing to ASP.NET sample application.

Next steps

In this quickstart, you deployed a Kubernetes cluster and then deployed an ASP.NET sample application in a Windows Server container to it. This sample application is for demo purposes only and doesn't represent all the best practices for Kubernetes applications.

To learn more about AKS, and to walk through a complete code-to-deployment example, continue to the Kubernetes cluster tutorial.