Using Terraform with Azure

This post is a sticky note about how to configure your workstation in order to use Terraform with Microsoft Azure.

Configure Azure CLI on your station

First of all, you need to install this client to be able to connect to your Azure subscription. To do so, download the software from the Microsoft website. For Windows users, it’s a simple MSI package to install, then run a PowerShell console to run the login command as follow:

az login

This command will open your browser, and ask you to log in to your Azure account.

Once’s validated, you will be able to see your subscriptions within the PowerShell console. Take note of the tenant ID and the ID of the subscription you want to use further if you have many subscriptions as below:

az account list                                   [
  {
    "cloudName": "AzureCloud",
    "homeTenantId": "xxx-xxx-xxx-xxx-xxx",
    "id": "xxx-xxx-xxx-xxx-xxx",
    "isDefault": true,
    "managedByTenants": [],
    "name": "Pay-As-You-Go",
    "state": "Enabled",
    "tenantId": "xxx-xxx-xxx-xxx-xxx",
    "user": {
      "name": "xxx@xxx.xxx",
      "type": "user"
    }
  },
  {
    "cloudName": "AzureCloud",
    "homeTenantId": "xxx-xxx-xxx-xxx-xxx",
    "id": "xxx-xxx-xxx-xxx-xxx",
    "isDefault": false,
    "managedByTenants": [],
    "name": "Pay-As-You-Go",
    "state": "Enabled",
    "tenantId": "xxx-xxx-xxx-xxx-xxx",
    "user": {
      "name": "xxx@xxx.xxx",
      "type": "user"
    }
  }
]

For information, you can use the command below to see your subscriptions:

az account list

That’s all for the Azure client, pretty simple, isn’t it?

Configuring Terraform on your station

This step is easy too, really, just browse Terraform web page to get the version that suits you. Keep in mind that, depending on the cloud provider (Azure in that very case), a version can or cannot be compatible with the provider. At this time, I’m using Terraform v0.12.21

OK, so unpack the archive to the folder of your choice. From here you can use the previous PowerShell console and browse to this folder. This is optional, but if you want to be able to run terraform from everywhere, you will need to deal with your environment variables. I won’t describe the process in this post though.

Open the Terraform folder with your file browser, then create inside a file called main.tf This file will contain the very basic information in order to connect to Azure:

provider "azurerm" {
  version = "=1.44.0"

  subscription_id = "xxx-xxx-xxx-xxx-xxx"
  tenant_id       = "xxx-xxx-xxx-xxx-xxx"
  skip_provider_registration = true
}

resource "azurerm_resource_group" "my-ressource-group" {
  name     = "my-ressource-group"
  location = "US West"
}

The first declaration is about the provider required to connect and manage Azure cloud services. Terraform strongly recommend using it to pin the version of the Provider being used. So this value will change in the future. Then set the subscription_id and tenant_id variables with the information gathered earlier. The skip_provider_registration is set to true here due to some restricted permissions I have, that might not be the case for you though.

If you have already created a resource group, you can also add the following declaration to your file:

resource "azurerm_resource_group" "my-resource-group" {
  name     = "my-resource-group"
  location = "US West"
}

Just keep in mind that Terraform, sadly, won’t import the resources you defined automatically. You will have to do that manually.

Now, run the following command to initialize Terraform, making it downloading the provider defined and creating the first local state file.

terraform.exe init

terraform.exe init

Initializing the backend...

Successfully configured the backend "azurerm"! Terraform will automatically
use this backend unless the backend configuration changes.

Initializing provider plugins...
- Checking for available provider plugins...
- Downloading plugin for provider "azurerm" (hashicorp/azurerm) 1.44.0...

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.

Storing Terraform State in an Azure storage

You should see in the Terraform folder a file called terraform.tfstate which contains a representation of what you have in Azure.

If you are planning to work with a team, you should use a common file shared across your people. For that, we are going to create a storage account and a container to put this terraform.tfstate file in it. Note that Azure is handling will manage the lock on that file automatically, avoiding multiple access at the same time from your coworkers. To do so, create a storage-accounts.tf file (choose the name that suits you the best):

resource "azurerm_storage_account" "my-storage" {
  name                     = "my-storage"
  resource_group_name      = azurerm_resource_group.my-resource-group.name
  location                 = azurerm_resource_group.my-resource-group.location
  account_tier             = "Standard"
  account_replication_type = "LRS"
}

resource "azurerm_storage_container" "my-storage" {
  name                  = "tfstate"
  storage_account_name  = azurerm_storage_account.my-storage.name
  container_access_type = "private"
}

Here, we have defined a storage account and a container within, I let you check the documentation about the replication type.

Now, let’s create those resources with the simple command below:

terraform.exe apply

This command will check on Azure to see which change (update, creation or deletion) it has to do. In the end, it will show you the plan, asking you for a yes in order to apply. Type yes and see the magic happens.

Now, you have a container to put your tf file in, however, we need to update our main.tf file in order to use it. Edit the file by adding the following content after the provider definition:

terraform {
  backend "azurerm" {
    resource_group_name   = "my-resource-group"
    storage_account_name  = "my-storage"
    container_name        = "tfstate"
    key                   = "terraform.tfstate"
  }
}

The key parameter represents the file in your local Terraform folder. Run a new init this time, to apply those change:

terraform.exe init

Now you’re being serious, you can work from different places on the same Azure subscription, that’s a good start.

From there, you can go on the Terraform’s documentation page and see what the Azure provider can do for you.

The following script are just reminders for me, you might not be interrested.

Virtual network, route table, and subnet

resource "azurerm_virtual_network" "my-network" {
  name                = "my-network"
  address_space       = ["10.62.34.128/25"]
  location            = azurerm_resource_group.my-resource-group.location
  resource_group_name = azurerm_resource_group.my-resource-group.name
}

resource "azurerm_route_table" "my-route-table" {
  name                          = "my-route-table"
  location                      = azurerm_resource_group.my-resource-group.location
  resource_group_name           = azurerm_resource_group.my-resource-group.name
}

resource "azurerm_subnet" "my-subnet" {
  name                 = "my-subnet"
  resource_group_name  = azurerm_resource_group.my-resource-group.name
  virtual_network_name = azurerm_virtual_network.my-network.name
  address_prefix       = "10.62.34.128/27"
  route_table_id       = azurerm_route_table.my-route-table.id
} 

A basic Linux machine

resource "azurerm_network_interface" "my-interface" {
  name                = "my-interface"
  location            = azurerm_resource_group.my-resource-group.location
  resource_group_name = azurerm_resource_group.my-resource-group.name

  ip_configuration {
    name                          = "ipconfig1"
    subnet_id                     = azurerm_subnet.my-subnet.id
    private_ip_address_allocation = "Static"
	private_ip_address 			  = "10.62.34.136"
  }
}

resource "azurerm_virtual_machine" "my-linux-vm" {
  name                  = "my-linux-vm"
  location              = azurerm_resource_group.my-resource-group.location
  resource_group_name   = azurerm_resource_group.my-resource-group.name
  network_interface_ids = [azurerm_network_interface.my-interface.id]
  vm_size               = "Standard_DS1_v2"

  # Uncomment this line to delete the OS disk automatically when deleting the VM
  delete_os_disk_on_termination = true

  # Uncomment this line to delete the data disks automatically when deleting the VM
  delete_data_disks_on_termination = true

  storage_image_reference {
    publisher = "Canonical"
    offer     = "UbuntuServer"
    sku       = "18.04-LTS"
    version   = "latest"
  }
  storage_os_disk {
    name              = "my-linux-vm-dsk01"
    caching           = "ReadWrite"
    create_option     = "FromImage"
    managed_disk_type = "Standard_LRS"
	disk_size_gb	  = 50
  }
  os_profile {
    computer_name  = "my-linux-vm"
    admin_username = "myUser"
    admin_password = "ASup3r53cr3t!!@"
  }
  os_profile_linux_config {
    disable_password_authentication = false
  }
  tags = {
    type = "A tag I want"
  }
}

Leave a Reply