Multi-region Azure VM deployment with Terraform
There are many cases where you would want to deploy a number of VMs to multiple regions, particularly the region pairs like UK South and West, for a resilient application. In this post I’ll show how this can be achieved with some simple Terraform logic.
The clever bit is using the Terraform count
argument with some maths and conditions to create multiple VMs and split them between the two regions. In summary this looks something like this, but read on for the detail.
1module "vm" {
2 count = 8
3 name = "MyVM-${count.index}"
4 location = count.index % 2 == 0 ? "uksouth" : "ukwest"
The Brief
Deploy multiple identical Virtual Machines into an Azure environment, split evenly between two Regions, for example “UK South” and “UK West”.
The Solution
The following Terraform will deploy a number of VMs evenly split between two regions. Here’s the full code, and I’ve broken it down later in this post to show how it works.
1# Define Variables
2locals {
3 tenant_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" #Tenant to Deploy to
4 subscription_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" #Subscription to deploy to
5 rg_name = "rg-VMDemo" #Resource Group to Create and fill with VMs
6 primary_region = "uksouth" #Primary Region
7 secondary_region = "ukwest" #Secondary Region
8 vm_count = 4 #Number of VMs to Deploy
9 vm_name_root = "vm-VMDemo-" #Start of VM Name, will be suffixed with a number
10}
11
12# Setup Terraform
13provider "azurerm" {
14 tenant_id = local.tenant_id
15 subscription_id = local.subscription_id
16 features {}
17}
18
19terraform {
20 required_providers {
21 azurerm = {
22 source = "hashicorp/azurerm"
23 version = "=3.116.0"
24 }
25 }
26}
27
28# Resource group
29resource "azurerm_resource_group" "rg" {
30 name = local.rg_name
31 location = local.primary_region
32}
33
34# Virtual Machines
35module "vm" {
36 count = local.vm_count #Deploy this module this many times
37 source = "Azure/avm-res-compute-virtualmachine/azurerm"
38 version = "0.18.0"
39 name = "${local.vm_name_root}${count.index}" #Use the counter to identify VM
40 resource_group_name = azurerm_resource_group.rg.name
41 #Even Numbered VMs (0,2,....) get Primary Region, odd numbered VMs (1,3,....) get Secondary
42 location = count.index % 2 == 0 ? local.primary_region : local.secondary_region
43 zone = null
44 network_interfaces = {
45 network_interface_1 = {
46 name = "${local.vm_name_root}${count.index}-nic"
47 ip_configurations = {
48 ip_configuration_1 = {
49 name = "ipconfig1"
50 #Use the VNET in the same region -vnet[0] is in primary, [1] in Secondary
51 private_ip_subnet_resource_id = module.vnet[count.index % 2].subnets["subnet1"].resource_id
52 }
53 }
54 }
55 }
56 sku_size = "Standard_B1ls" # Just to keep things small for this demo
57}
58
59# Create virtual networks - One per region
60module "vnet" {
61 count = 2 # One VNET for each Region
62 source = "Azure/avm-res-network-virtualnetwork/azurerm"
63 version = "0.7.1"
64 name = "vnet-${count.index}"
65 resource_group_name = azurerm_resource_group.rg.name
66 #Use the same logic as VMs to determine Location
67 location = count.index % 2 == 0 ? local.primary_region : local.secondary_region
68 address_space = ["10.0.0.0/24"]
69 subnets = {
70 subnet1 = {
71 name = "subnet"
72 address_prefixes = ["10.0.0.0/24"]
73 }
74 }
75}
How it works
First up in the code we have some variables to define where to deploy and what this environment should look like.
Name | Description |
---|---|
tenant_id | The ID of the Tenant to Deploy to |
subscription_id | The ID of the Subscription to deploy to |
rg_name | The name of the Resource Group to Create and fill with VMs |
primary_region | The name of the Primary Azure Region |
secondary_region | The name of the Secondary Azure Region |
vm_count | The number of VMs to Deploy- this can be any positive number. |
vm_name_root | Start of VM Name, will be suffixed with a number to distinguish the VMs |
The next section (provider, terraform, resource group) has all the bits to make Terraform work, and create a resource group to drop our VMs in. This is standard stuff, so I won’t describe it further here.
Now we get to the good bit- deploying the Virtual Machines! I’ve used Azure Verified Modules here for simplicity, but you could use regular AzureRM or AzureAPI calls in the same way.
1module "vm" {
2 count = local.vm_count #Deploy this module this many times
3 source = "Azure/avm-res-compute-virtualmachine/azurerm"
4 name = "${local.vm_name_root}${count.index}" #Use the counter to identify VM
5 location = count.index % 2 == 0 ? local.primary_region : local.secondary_region
The count instruction tells Terraform to deploy that many copies of this module. We use the value of that count to give unique names to the Virtual Machines (VMDemo-0, VMDemo-1 and so on). Then in the location property we decide which region to place that VM in. Even Numbered VMs (VMDemo-0,VMDemo-2 etc) get put in the Primary Region (UK South in this example), but odd numbered VMs (VMDemo-1,VMDemo-3 etc) get placed in the Secondary Region (UK West).
To explain the logic: The value of count.index % 2
-the Modulus of the count value and 2- gives us a 0 for even numbers (divide an even number by 2 and the remainder is zero) and a 1 for odd numbers (divide an odd number by 2 and the remainder is 1). We then compare that value to zero to get a Boolean- True for even numbers, False for odd. That boolean condition then determines we get the Primary Region if True and the Secondary if false.
Note that the count.index
value is zero-indexed, so if you specify the count
as 4 you will get VMDemo-0 to VM-Demo3 created.
Finally, we need to deploy two VNETs- one for each Region - so the VMs have somewhere to put there Network Interfaces. We use the same location logic here as for the Virtual Machines, but only deploying 2 VNETs.
1module "vnet" {
2 count = 2 # One VNET for each Region
3 source = "Azure/avm-res-network-virtualnetwork/azurerm"
4 name = "vnet-${count.index}"
5 location = count.index % 2 == 0 ? local.primary_region : local.secondary_region
Hopefully this is useful to someone, and a useful starting point for a multi-region deployment.