Using Azure CLI and Powershell to create Kubernetes cluster in Azure
Tags: kubernetes, azure, azurecli, tutorial
Published: 2020-01-19 16:32:07

Here's a guide on how to set up a Azure Kubernetes Service cluster using Azure CLI and powershell. I use this environment to set up a default cluster for testing and it takes me about 3 minutes to set up a full basic environment for dev/test.

A tip, I use VSCode and the Azure CLI tool plugin with Powershell Core. This gives me almost the same functionality as I get through the Powershell ISE.

Requirements:
* Powershell ([Install instructions](https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell?view=powershell-7))
* Azure CLI ([Install instructions](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest))
* kubectl

Once you have Powershell and Azure CLI installed run 
```powershell
az aks install-cli
```
to install the kubectl client. Follow the instruction to set up the environment variable in system.

Open VSCode, or any other text editor and start building your script.

First thing first..
```powershell
#login to your azure subscripiton
az login

#see your subscriptions
az account list

#set active subscription if you have more than one
az account set --name "<name of your azure subscripiton>"
```

First we define the variables we need to use. change the value of the variables as you see fit. I am choosing location "North Europe" since I am in Norway but important part is that your AKS name must be unique within the location/region you choose and if you plan to deploy ACS also it's name must be globally unique.

```powershell
# general
$location = "northeurope"
$aksrg = "cluster_rg"

# VNET##
$vnet = "cluster-vnet"
$vnet_address_range = "10.0.0.0/16"

# aks subnet
$akssn = "aks-sn"
$aks_cidr = "10.0.2.0/23"

# aks
$aks = "<define a unique name for your cluster (unique within $location)>"
$aks_srv_cidr = "192.168.6.0/23"
$aks_dns_ip = "192.168.6.10"
$aks_pod_cidr = "192.168.8.0/21"
$aks_docker_bridge = "172.17.0.1/16"

# acr
$registry = "<define a globally unique name for your registry>"
```


Next we will create some resources. We start with our resource group and then create the virtual network and subnet.

```powershell
# create the resource group
az group create --name $aksrg --location $location

# define a VNET
az network vnet create --name $vnet --resource-group $aksrg --address-prefixes 
$vnet_address_range

# create the AKS subnet inside the vnet
az network vnet subnet create -g $aksrg --vnet-name $vnet -n $akssn --address-prefixes $aks_cidr

# get id of the vnet
$vnetid = $(az network vnet show -n $vnet -g $aksrg --query id --output tsv)

# get the id of the subnet
$akssnid = $(az network vnet subnet show -g $aksrg --vnet-name $vnet -n $akssn --query id --output tsv)
```

We also need to create a service principal that enables the AKS service to interact with the virtual network we created. One tip I have while working with Azure CLI and powershell is to install "jq". It is a lightweight and flexible command-line JSON processor that can be installed through [Chocolatey](https://chocolatey.org/packages/jq). All of the Azure CLI commands can output json response values by applying the switch "--output json" to the command. 
If you want your service principal to be a bit easier to spot in AAD you can attach a name to to the SP using the --name switch (ie: --name "${aks}_sp"). Also, if you attach name to the service principal and don't remove it in clean up, it'll be reused and patched the next time you run the script. 

```powershell
#create the service prinsipal and store the output in a json file
$outputfile = "aks_sp.json"
$filecontent = $(az ad sp create-for-rbac --skip-assignment --name "${aks}_sp" --output json)
$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False
[System.IO.File]::WriteAllLines($outputfile, $filecontent, $Utf8NoBomEncoding)


#read the values from the json file
$aks_appid = $(jq -r ".appId" $outputfile)
$aks_secret = $(jq -r ".password" $outputfile)
```
"jq" is a bit peckish about file encoding so to make sure it is stored in a compatible format we can force it to UTF8. On my laptop VScode integrated shell stores it as UTF-16 LE when I just dump the az ad sp create-for-rbac with "> $outputfile"

With the service principal created and loaded into the variables using jq we can assign the role of Contributor to the virtual network. Notice also the UTF8 converting. While I've been testing I've found that jq is a bit peckish on how it likes it's files encoded. Utf8 works fine so the converter just forces that.

NOTE!
If you run all of this as a single script you will most likely receive an error that "Principal \<guid\> does not exist in the directory \<guid\>". A "small hack" is to let the script sleep for about 10 seconds to give AAD time to create the service principal. I haven't found any better way to do this yet so if anyone have a "wait-for-it" command that works with "az ad" commands let me know in the comments :) 

```powershell
#ZzzzZZz
Start-Sleep -s 10
az role assignment create --assignee $aks_appid --scope $vnetid --role Contributor
```

Now everything is set up and we can create the Azure Kubernetes Service. Another service that can be useful to deploy with AKS is a container registry. Especially when I use this as part of setting up a CI/CD 

```powershell
# optional create Azure Container Registry
az acr create --name $registry --resource-group $aksrg --sku basic
```

If you didn't create an ACR, remove the --attach-acr parameter. ACR can also be attached later. 

NOTE!
This is a small cluster, with no autoscale and no real plan for networking. To create a production ready cluster you need a bit more planning  around ip/network etc. Look at the network requirements on the [Azure Doc](https://docs.microsoft.com/en-us/azure/aks/configure-kubenet) and plan your networking using the [Visual Subnet Calculator](https://dev.to/megatherion/tool-to-help-you-plan-subnets-in-networks-1mp). 

```powershell
#create the aks service (remove linebreaks before running
az aks create --resource-group $aksrg \
       --name $aks \
       --attach-acr $registry \
       --node-count 3 \
       --network-plugin kubenet \
       --service-cidr $aks_srv_cidr \
       --dns-service-ip $aks_dns_ip \
       --pod-cidr $aks_pod_cidr \
       --docker-bridge-address $aks_docker_bridge \
       --vnet-subnet-id $akssnid \
       --service-principal $aks_appid \
       --client-secret $aks_secret

# get the aks resource id
$aks_resourceId = $(az aks show -n $aks -g $aksrg --query id -o tsv)
# wait for aks to be up and running
az resource wait --exists --ids $aks_resourceId
```
Now the cluster is up and running. To set the context for kubectl to your newly created cluster run.

```powershell
az aks get-credentials --resource-group $aksrg --name $aks  --overwrite-existing
```
To verify that everything is up and running get the cluster info using kubectl
```powershell
kubectl cluster-info
```

It is important to understand that this is not a service that is designed and secured for production so do spend more time customizing and add security, network and capacity planning before deploying anything for production.

When you are done, running two simple commands will remove everything we have created.

```powershell
#cleanup
az group delete --name $aksrg --yes
az group delete --name "NetworkWatcherRG" --yes
az ad sp delete --id $aks_appid
Remove-Item –path $outputfile
```

To do this in bash should require minimal changes. All of the Azure CLI commands are cross platform equal.

Feel free to leave a comment if you have any optimization tips or feedback!