Hashicorp’s Terraform AzureRM Provider v2.0 – Updates and Features

There are some great feature additions to the Terraform AzureRM 2.0 provider.  The provider has grown significantly in the past 5 years. There are some major improvements that we discuss in the video, Hashi has really looked into the future and are making sure this provider can grow with its usage for years to come.  While the overall experience of Terraform has certainly improved in this update, it does mean that it’s time to update your code.

The great news is that they have just released the new Visual Studio Code Extension for version 2.0.0.  This will greatly enhance your ability write your code with less errors AND it supports Terraform 0.12!  But wait, there’s more! Hashicorp have a dedicated team focused on supporting extensions and language server. Expect even better development and productivity from them in their roadmap.  You can read more about it from their announcement here.

Upgrading the Provider

Declaring the version of the Provider that you are using in Terraform is best practice. Previously you would call the AzureRM attribute in the provider block, with either a specific version or to any 1.x release.  Now you can upgrade the AzureRM Provider in the specified block:

provider "azurerm" {
  version = "=2.0.0"
  features {}
}

Custom Timeouts for Resources

Previously, if you deployed a resource you could not set a custom timeout for resource creation or deletion. The default value was one hour, which could not be changed. This is great for when you’re deploying resources that have dependencies.  For example, if you’re deploying a virtual machine but need to create NSGs/ASGs (Network Security Groups/Application Security Groups).  Azure has built in requirements for in what order the resources can then be deleted. I personally encountered this multiple times.  Read about my experience and work around here.  That issue can be easily resolved now with custom timeouts!

resource "azurerm_resource_group" "rg" {
  name     = "rg-Demo-Existing"
  location = "West Europe"
  timeouts {
    create = "10m"
    delete = "30m"
  }

New Resources for Virtual Machine and VM Scale Set

Virtual Machine and Virtual Machine Scale Sets (VMSS) have been available to deploy using Terraform for over 4 years now.  In that time, the features available to VMs has massively grown, such as Managed Disks, etc. Also as Azure has grown, Microsoft has changed some of the behaviors of the VM/VMSS configuration fields.  While that has opened up value for the end user, it has become a bit messy as the technology has grown.

You had one block for VMs (same for VMSS):

azurerm_virtual_machine  or azurerm_virtual_machine_scale_set

From there you would declare all configuration details of the VM, what OS image to use, the name of the VM etc.  But…Windows and Linux VMs have different capabilities, even the length of the ‘name’ varies.  Linux supports 63 characters, but Windows only supports 15 characters. It was difficult to validate your build.

To make things easier in the long run and to make configuration/validation a much better experience, they have split out the resources by operating system for both VMs and VMSS:

  • a Linux Virtual Machine Resource (working name: azurerm_linux_virtual_machine)
  • a Windows Virtual Machine Resource (working name: azurerm_windows_virtual_machine)
  • updating the Data Disk Attachment Resource to support Unmanaged Disks
  • a Linux Virtual Machine Scale Set Resource (working name: azurerm_linux_virtual_machine_scale_set)
  • a Windows Virtual Machine Scale Set Resource (working name: azurerm_windows_virtual_machine_scale_set)
  • a separate resource for Virtual Machine Scale Set Extensions (working name azurerm_virtual_machine_scale_set_extension)

I’ve provided a bit of sample code for creating a new Windows VM:

provider "azurerm" {
  features {}
}

resource "azurerm_resource_group" "example" {
  name     = "example-resources"
  location = "West Europe"
}

resource "azurerm_virtual_network" "example" {
  name                = "example-network"
  address_space       = ["10.0.0.0/16"]
  location            = azurerm_resource_group.example.location
  resource_group_name = azurerm_resource_group.example.name
}

resource "azurerm_subnet" "example" {
  name                 = "internal"
  resource_group_name  = azurerm_resource_group.example.name
  virtual_network_name = azurerm_virtual_network.example.name
  address_prefix       = "10.0.2.0/24"
}

resource "azurerm_network_interface" "example" {
  name                = "example-nic"
  location            = azurerm_resource_group.example.location
  resource_group_name = azurerm_resource_group.example.name

  ip_configuration {
    name                          = "internal"
    subnet_id                     = azurerm_subnet.example.id
    private_ip_address_allocation = "Dynamic"
  }
}

resource "azurerm_windows_virtual_machine" "example" {
  name                = "example-machine"
  resource_group_name = azurerm_resource_group.example.name
  location            = azurerm_resource_group.example.location
  size                = "Standard_F2"
  admin_username      = "adminuser"
  admin_password      = "Password1234!"
  network_interface_ids = [
    azurerm_network_interface.example.id,
}

  os_disk {
    caching              = "ReadWrite"
    storage_account_type = "Standard_LRS"
  }

  source_image_reference {
    publisher = "MicrosoftWindowsServer"
    offer     = "WindowsServer"
    sku       = "2016-Datacenter"
    version   = "latest"
  }
}

Now what if you want to run v2.0.0 AzureRM Provider and you’re using the existing ‘azurerm_virtual_machine’ and ‘azurerm_virtual_machine_scale_set’?  You still can, for now.  Hashicorp has decided to support them for now, but in future versions there will NOT be any support.  While there is not a direct and clear cut upgrade path, users can update their code or import an existing resource.  Hashicorp really wanted to improve the schema design and be able to support the new use-cases and fix the existing bugs.

Importing Resources

While there is not a direct upgrade path to the AzureRM Provider v2.0.0, the import tool may become your next best friend.  This is supported for BOTH AzureRM v1.x and AzureRM v2.x.  It’s not only going to be helpful for upgrading, but what do you do about resources that are deployed into Azure, but were not built, nor are they managed by Terraform?

I am going to walk you through importing your resources that were created by some other means and bring it under Terraform management in the State File.  First and foremost, understanding how the State File works and references your resources is important, have a review here.

The one downside to Terraform Import is that it does not generate a configuration, but word on the street is that is coming!  Import will only import your resources into your state file.

In my example I deployed a resource group from the Azure Portal manually.  I need to import that resource group, then start deploying resources to it. Let’s walk through what I did:

My existing resource group is: ‘rg-Demo-Existing’

  1. In order to import it, I need the resource ID.  There are 2 ways in which to obtain this:

a) Browse to the properties of the resource in the Azure Portal and look up ‘Resource ID’. Copy the Resource ID

b) Or from CloudShell, run the following command:

az group show --name rg-Demo-Existing --query id --output tsv

              Which will output the ID of the subscription:

az group show --name rg-Demo-Existing --query id --output tsv
/subscriptions/xxxxxxxxxxxxxx/resourceGroups/rg-Demo-Existing

2. Create an ‘import.tf’ file at the same level as your ‘main.tf’ file, this will be used to manage imported variables.  We will add the resource block for our existing resource group:

3. Once you have created your ‘import.tf’ file you can initialize Terraform from the Azure CLI or Azure Cloud Shell

Terraform init

4. After Terraform has initialized, you will need to run ‘terraform import’ followed by resource and resource ID

 Terraform import azurerm_resource_group.rg-Demo-Existing /subscriptions/a6baafb4-bad4-4c83-a5f3-63c0e63f5d32/resourceGroups/rg-Demo-Existing

 You should see the import complete successfully

5. Once the import is successful, you will see the state file has been updated with the imported resources. We can browse the state file and read the JSON and verify that they are being managed by the state file.

6. Now that my resource group is being managed by my state file, I can add the name and location of the existing resource to the resource group.

7. From here, you can begin adding in additional resources and reference the imported resource.  In this demo I want to deploy a vNET to my resource group. I input the resource configuration for my vNET and can start using variables from my import file to keep my naming conventions and coding simplified (and repeatable). I write all my changes to my ‘main.tf’ file, then save (preferably to your favorite form of source control).

8. Deploy your code with terraform apply

9. Voila! I have deployed a new vNet within my resource group. I can now add other resources as I need to (VMs, WebApps, etc).

I hope you enjoy the updates and features to AzureRM v2.0.0 as much as I do!

For more learnings on Terraform have a look at the links below:

Terraform State File – Managing remote state for securing and scaling your deployments

Terraform Modules – Deploying re-usable code

Terraform and Azure DevOps – Delivering CI/CD deployments Link Coming Soon! In the meantime you can watch the video on Channel9

Terraform and Github Actions – Delivering code from your repoLink Coming Soon! In the meantime, you can watch the video on Channel9

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s