Adding SMB1 protocol support to Windows Server 2019

I realize this is not a very exciting post, especially compared to my other wonderful musing on this site, but I felt I really had to write it to share the pain!

A colleague I work with needed to enable this feature on an Azure Windows Server 2019 machine to communicate with some old system that only supports Server Message Block version 1 (SMB1). Easy enough to add that right?

Trying the installation

Let’s first get some information about the feature:

Get-WindowsOptionalFeature -Online -FeatureName SMB1Protocol

The output:

Notice the State property? The feature is disabled and the payload (installation files) are not on the Azure virtual machine.

When you try to install the feature, you get:

I guess I need the Windows Server 2019 sources!

Downloading Windows Server 2019

I downloaded Windows Server 2019 (November 2019 version) from https://my.visualstudio.com/Downloads?q=SQL%20Server%202019. I am not sure if you can use the evaluation version of Windows Server 2019 because I did not try that. I downloaded the ISO to the Azure virtual machine.

Mount ISO and copy install.wim

On recent versions of Windows, you can right click an ISO and mount it. In the mounted ISO, search for install.wim and copy that file to a folder on your C: disk like c:\wim. Under c:\wim, create a folder called mount and run the following command:

dism /mount-wim /wimfile:c:\wim\install.wim /index:4 /mountdir:c:\wim\mount /readonly

The contents of install.wim is now available in c:\wim\mount. Now don’t try to enable the feature by pointing to the sources with the -source parameter of Enable-WindowsOptionalFeature. It will not work yet!

Patch the mounted files

The Azure Windows Server 2019 image (at time of writing, June 2020) has a cumulative update installed: https://support.microsoft.com/en-us/help/4551853/windows-10-update-kb4551853. For this installatin to work, I needed to download the update from https://www.catalog.update.microsoft.com/home.aspx and put it somewhere like c:\patches.

Now we can update the mounted files offline with the following command:

Dism /Add-Package /Image:"C:\wim\mount" /PackagePath="c:\patches\windows10.0-kb4551853-x64_ce1ea7def481ee2eb8bba6db49ddb42e45cba54f.msu" 

It will take a while to update! You need to do this because the files mounted from the downloaded ISO do not match the version of the Windows Server 2019 image. Without this update, the installation of the SMB1 feature will not succeed.

Enable the SMB1 feature

Now we can enable the feature with the following command:

dism /online /Enable-Feature /FeatureName:SMB1Protocol /All /source:c:\wim\mount\windows\winsxs /limitaccess 

You will need to reboot. After rebooting, from a PowerShell prompt run the command below to check the installation:

Get-WindowsOptionalFeature -Online -FeatureName SMB1Protocol

The State property should say: Enabled

Conclusion

Something this trivial took me way too long. Is there a simpler way? Let me know! 👍

And by the way, don’t enable SMB1. It’s not secure at all. But in some cases, there’s just no way around it.

A look at Windows containers on AKS

Now that the public preview of Windows containers on AKS is available, let’s look at the basics. You need a couple of things to get started, including a couple of subscription-wide settings. I recommend using a subscription that is not used to roll out production AKS clusters. Make sure the Azure CLI (az) is homed to the subscription. Use Azure Cloud Shell to make your life easier:

  • Install the aks-preview extension
  • Register the Windows preview feature
  • Check that the feature is active; this will take a few minutes
  • Register the Microsoft.ContainerService resource provider again (only if the Windows preview feature is active)

The following commands make the above happen:

az extension add --name aks-preview

az feature register --name WindowsPreview --namespace Microsoft.ContainerService

az feature list -o table --query "[?contains(name, 'Microsoft.ContainerService/WindowsPreview')].{Name:name,State:properties.state}"

az provider register --namespace Microsoft.ContainerService

With that out of the way, deploy a new AKS cluster:

az aks create \
     --resource-group RESOURCEGROUP \
     --name winclu \
     --node-count 1 \
     --kubernetes-version 1.13.5 \
     --generate-ssh-keys \
     --windows-admin-password APASSWORDHERE \
     --windows-admin-username azureuser \
     --enable-vmss \
     --enable-addons monitoring \
     --network-plugin azure

Replace RESOURCEGROUP with an ARM resource group and replace APASSWORDHERE with a complex password. If you have ever deployed clusters that support multiple node pools with virtual machine scale sets, the above command will be very familiar. The only real difference here is –windows-admin-password and –windows-admin-username which are required to deploy the Windows hosts that will run your containers.

You can use the Windows user name and password to RDP into the Kubernetes nodes. You will need to deploy a jump host that has a route to the Kubernetes virtual network to make this happen as the Kubernetes hosts are not exposed with a public IP address. As they shouldn’t… 😉

Note that you need to deploy a node pool with Linux first (as in the above command). That is why the number of nodes has been set to the minimum. You cannot delete this node pool after adding a Windows node pool.

After deployment, you will see the cluster in the portal with the Linux node pool with one node:

node pool with one node

When you click Add node pool, you will be able to select the OS type of a new pool:

Both Linux and Windows as OS type for the node pool

We will add a Windows node pool via the CLI. The node pool will use the Standard_D2s_v3 virtual machine size by default, which is also the recommended minimum.

az aks nodepool add \
     --resource-group RESOURCEGROUP \
     --cluster-name winclu \
     --os-type Windows \
     --name winpl \
     --node-count 1 \
     --kubernetes-version 1.13.5

Note: the name of the Windows node pool cannot be longer than 6 characters

The node pool is now being added and will soon be ready:

windows node pool being added

When ready, you will see an additional scale set in the resource group that backs this AKS deployment:

additional scale set for the Windows node pool

We can now schedule pods on the Windows node pool. You can schedule a pod on a Windows node by adding a nodeSelector to the pod spec:

nodeSelector:         
  "beta.kubernetes.io/os": windows 

To try this, let’s deploy a Windows version of my realtime-go app with the following command. The gist contains the YAML required to deploy the app and a service. It uses the gbaeke/realtime-go-win image on Docker Hub. The base image is mcr.microsoft.com/windows/nanoserver:1809. You need to use the 1809 version because the hosts use 1809 as well. With Hyper-V isolation, the kernel match would not be required.

kubectl apply -f https://gist.githubusercontent.com/gbaeke/ed029e8ccbf345661ed7f07298a36c21/raw/02cedf88defa7a0a3dedff5e06f7e2fc5bbeccbe/realtime-go-win.yaml 

This should deploy the app but sadly, it will error out. It needs a running redis server. Let’s deploy that the quick and dirty way (command on one line below):

kubectl run redis --image=redis --replicas=1 --overrides='{ "spec": { "template": { "spec": { "nodeSelector": { "beta.kubernetes.io/os": "linux" } } } } }' --expose --port 6379

I realize it’s ugly with the override but it does the trick. The above command creates a deployment called redis that sets the nodeSelector to target Linux nodes. It also creates a service of type ClusterIP that exposes port 6379. The ClusterIP allows the realtime-go-win container to connect to redis over the Kubernetes network. Now delete the realtime-go container and recreate it:

kubectl delete -f https://gist.githubusercontent.com/gbaeke/ed029e8ccbf345661ed7f07298a36c21/raw/02cedf88defa7a0a3dedff5e06f7e2fc5bbeccbe/realtime-go-win.yaml

kubectl apply -f https://gist.githubusercontent.com/gbaeke/ed029e8ccbf345661ed7f07298a36c21/raw/02cedf88defa7a0a3dedff5e06f7e2fc5bbeccbe/realtime-go-win.yaml 

Note that I could not get DNS resolution to work in the Windows container. Normally, the realtime-go container should be able to find the redis service via the name redis or the complete FQDN of redis.default.svc.cluster.local. Because that did not work, the code in the realtime-go-win container was modified to use environment variables injected by Kubernetes:

redisHost := getEnv("REDISHOST", "")
if redisHost == "" {
    redisIP := getEnv("REDIS_SERVICE_HOST", "localhost")
    redisPort := getEnv("REDIS_SERVICE_PORT", "6379")
    redisHost = redisIP + ":" + redisPort
} 

Conclusion

Deploying an AKS cluster with both Linux and Windows node pools is a simple matter. Because you can now deploy both Windows and Linux containers, you have some additional work to make sure Windows containers go to Windows hosts and Linux containers to Linux hosts. Using a nodeSelector is an easy way to do that. There are other methods as well such as node taints. Sadly, I had an issue with Kubernetes DNS in the Windows container so I switched to injected environment variables.