Getting Started with Terraform and AKS: a Step-by-Step Guide to Deploying Your First Cluster

Fairwinds
6 min readJul 23, 2020
Using AKS and Terraform

We are major advocates of using infrastructure as code to manage Kubernetes. Terraform is our tool of choice to manage the entire lifecycle of Kubernetes infrastructure. You can read about the benefits of Terraform here.

This blog provides a step-by-step guide on how to get started with Terraform and AKS by deploying your first cluster with infrastructure as code.

Prerequisites

If you want to follow along and create your own AKS Cluster in Terraform, follow these steps.

  • Create Azure Account and log in (there is a free tier).
  • Create a Subscription — confirm that the Subscription has Owner rights

Steps

Create a directory for the project like terraform-aks. Next, set up an ssh key pair in the directory with this command: ssh-keygen -t rsa -f ./aks-key. Then, run az login from the command line to log into your Azure account.

We will now set up several Terraform files to contain the various resource configurations. The first file will be named provider.tf. Create the file and add these lines of code:

provider "azurerm" {
version = "~> 2.5.0"
features {}
}
provider "azuread" {
version = "0.9.0"
}

Now, create a file called cluster.tf. This will include our modules for a virtual network, cluster and node pool. Now we can add a resource group, which is something Azure requires for resources to be created in.

## Create a resource group to place resources
resource "azurerm_resource_group" "aks" {
name = "myakscluster"
location = "centralus"
}

This code will set up the network for your AKS cluster, add this to cluster.tf as well.

## Create the virtual network for an AKS cluster
module "network" {
source = "git@github.com:FairwindsOps/azure-terraform-modules.git//virtual_network?ref=virtual_network-v0.6.0"
region = "centralus"
resource_group_name = azurerm_resource_group.aks.name
name = "myakscluster"
network_cidr_prefix = "10.64.0.0"
network_cidr_suffix = 10
subnets = [{
name = "aks-subnet"
cidr_block = 16
}]
}

You will notice in the source field that the network module is being pulled from our git repo: git@github.com:FairwindsOps/azure-terraform-modules.git//virtual_network.

If you explore that repo, under the aks_cluster directory, you'll notice the aad.tf file. This module supports an Azure Active Directory integration, but we will not be using that here. You can also see all the other resources that are being created by this module. Next, add this code to the cluster.tf file for the AKS cluster itself:

## Create the AKS cluster
module "cluster" {
source = "git@github.com:FairwindsOps/azure-terraform-modules.git//aks_cluster?ref=aks_cluster-v0.8.0"
region = "centralus"
cluster_name = "myakscluster"
kubernetes_version = "1.16.10"
resource_group_name = azurerm_resource_group.aks.name
node_subnet_id = module.network.subnet_ids[0] # use the subnet from the module above
network_plugin = "azure"
network_policy = "calico"
public_ssh_key_path = "aks-key.pub"
}

Note: This uses Kubernetes 1.16.10. You may need to check AKS release notes to confirm the latest supported version.

You will notice that certain values, like module.network.subnet_ids[``0``], are referenced from the network module. This feature of Terraform enables you to set values in on module or resource and using them in others. Lastly, add this code to cluster.tf to set up the node pool that will contain the AKS worker nodes:

## Create the node pool
module "node_pool" {
source = "git@github.com:FairwindsOps/azure-terraform-modules.git//aks_node_pool?ref=aks_node_pool-v0.4.0"
name = "myakspool"
kubernetes_version = "1.16.10"
aks_cluster_id = module.cluster.id
node_subnet_id = module.network.subnet_ids[0]
}

Note: This uses Kubernetes 1.16.10. You may need to check AKS release notes to confirm the latest supported version.

That’s it! Your Terraform files are all ready to go.

The next step is to initialize Terraform by running terraform init. Terraform will generate a directory named .terraform and download each module source declared in cluster.tf.

Initialization will pull in any providers required by these modules, in this example it will download the google provider. If configured, Terraform will also configure the backend for storing the state file.

terraform init
Initializing modules...
Downloading git@github.com:FairwindsOps/azure-terraform-modules.git for cluster...
- cluster in .terraform/modules/cluster/aks_cluster
Downloading git@github.com:FairwindsOps/azure-terraform-modules.git for network...
- network in .terraform/modules/network/virtual_network
Downloading git@github.com:hashicorp/terraform-cidr-subnets.git?ref=v1.0.0 for network.subnet_addrs...
- network.subnet_addrs in .terraform/modules/network.subnet_addrs
Downloading git@github.com:FairwindsOps/azure-terraform-modules.git for node_pool...
- node_pool in .terraform/modules/node_pool/aks_node_pool
Initializing the backend...Initializing provider plugins...
- Checking for available provider plugins...
- Downloading plugin for provider "azurerm" (hashicorp/azurerm) 2.5.0...
- Downloading plugin for provider "azuread" (hashicorp/azuread) 0.9.0...
- Downloading plugin for provider "kubernetes" (hashicorp/kubernetes) 1.11.3...
- Downloading plugin for provider "null" (hashicorp/null) 2.1.2...
The following providers do not have any version constraints in configuration, so the latest version was installed.To prevent automatic upgrades to new major versions that may contain breaking changes, it is recommended to add version = "..." constraints to the corresponding provider blocks in configuration, with the constraint strings suggested below.* provider.kubernetes: version = "~> 1.11"
* provider.null: version = "~> 2.1"
Terraform has been successfully initialized!You may now begin working with Terraform. Try running "terraform plan" to see any changes that are required for your infrastructure. All Terraform commands should now work.If you ever set or change modules or backend configuration for Terraform, rerun this command to reinitialize your working directory. If you forget, other commands will detect it and remind you to do so if necessary.

After Terraform has been successfully initialized, you should be able to run terraform plan. It is always a good idea to run terraform plan and review the output before allowing Terraform to make any changes.

terraform plan
Refreshing Terraform state in-memory prior to plan... The refreshed state will be used to calculate this plan, but will not be persisted to local or remote state storage.
module.cluster.data.azurerm_subscription.current: Refreshing state...-------------------------------------------------------------------- An execution plan has been generated and is shown below. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions: # azurerm_resource_group.aks will be created
+ resource "azurerm_resource_group" "aks" {
+ id = (known after apply)
+ location = "centralus"
+ name = "myakscluster"
}
# module.cluster.azurerm_kubernetes_cluster.cluster will be created
+ resource "azurerm_kubernetes_cluster" "cluster" {
+ dns_prefix = "myakscluster"
+ fqdn = (known after apply)
+ id = (known after apply)
+ kube_admin_config = (known after apply)
+ kube_admin_config_raw = (sensitive value)
+ kube_config = (known after apply)
+ kube_config_raw = (sensitive value)
+ kubernetes_version = "1.16.9"
+ location = "centralus"
+ name = "myakscluster"
+ node_resource_group = (known after apply)
+ private_fqdn = (known after apply)
+ resource_group_name = "myakscluster"
+ tags = {
+ "cluster-name" = "myakscluster"
+ "created-by" = "Terraform"
+ "module-source" = "github.com/FairwindsOps/azure-terraform-modules/aks_cluster"
}
+ addon_profile {
+ aci_connector_linux {
+ enabled = false
}

+ azure_policy {
+ enabled = false
}
+ http_application_routing {
+ enabled = false
+ http_application_routing_zone_name = (known after apply)
}
+ kube_dashboard {
+ enabled = false
}
}
+ default_node_pool {
+ availability_zones = [
+ "1",
+ "2",
+ "3",
]
+ enable_auto_scaling = true
+ enable_node_public_ip = false
+ max_count = 10
+ max_pods = 110
+ min_count = 1
+ name = "default"
+ node_count = 1
+ os_disk_size_gb = 50
+ type = "VirtualMachineScaleSets"
+ vm_size = "Standard_D2_v2"
+ vnet_subnet_id = (known after apply)
}
...Plan: 4 to add, 0 to change, 0 to destroy.

Please note that this snippet has been edited to cut down on the size of this article.

As shown in the example above, Terraform will take action to add our 4 AKS resources. When applied, Terraform will create our network, subnetwork (for pods and services), AKS cluster and node pool.

After the plan is validated, apply the changes by running terraform apply. For one last validation step, Terraform will output the plan again and prompt for confirmation before applying. This step will take around 10 minutes to complete. To interact with your cluster, run this command in your terminal.

az aks get-credentials --resource-group myakscluster --name myakscluster --admin

Then you should be able to kubectl get nodes and you will see two worker nodes from your cluster!

kubectl get nodes

NAME STATUS ROLES AGE VERSION
aks-default-14693408-vmss000000 Ready agent 8m24s v1.16.9
aks-myakspool-14693408-vmss000000 Ready agent 4m31s v1.16.9

Congratulations, you’ve successfully deployed a Kubernetes AKS cluster using Terraform! You can now begin deploying your applications to Kubernetes!

Resources

--

--

Fairwinds

Fairwinds — The Kubernetes Enablement Company | Editor of uptime 99