Azure Virtual Network explained with Terraform

azure cloud terraform infrastructureascode virtualnetwork subnet virtualmachine securitygroup hashicorp microsoft devops

This post is about explaining an Azure Virtual Network using the corresponding resource blocks of a Terraform configuration

1. Introduction

“VNet”, “subnet”, “network security group”, “network interface”, and “public IP address”,…if you are planning to set up a private network in Azure, then you’ve to deal with the different resources related to an Azure virtual network (VNet). This post is intended for all, who would like to ramp up their basic knowledge of the different resources. For that, I’m going to explain what you need to establish an Azure virtual network, in which a virtual machine is deployed, by using a Terraform configuration.

2. Overview of the Azure Resources

The picture below should illustrate the architecture of the whole Terraform configuration, which is about to define an Azure virtual network, in which an Azure resource, like a virtual machine, is deployed within a dedicated subnet. Each Azure resource will be described in the following section including their corresponding Terraform resource block.

architecture

3. Explaining the Resources of an Azure Virtual Network with Terraform

Each Azure resource has its corresponding Terraform resource block. Those resource blocks are part of a Terraform configuration, which will provide the Azure resources after applying it using the dedicated Terraform commands. I’ll go through each resource block and explain it in detail.

3.1 The Azure Virtual Network - VNet

This Terraform resource block below covers a virtual network (VNet) in Azure: that’s essential if you would like to create a private network. An Azure resource, like a virtual machine, which is deployed in this VNet in my example, can communicate with other resources within this VNet securely.

I’ve named this VNet instance “vNet”, further attributes refer to a location of your choice and to a resource group. The “address_space” is defined with 10.0.0.0/16. This means there are 65.536 possible IP addresses, which can be used in the whole VNet.

The amount of IP addresses can be calculated with: 2^(32-16) = 65.536

resource "azurerm_virtual_network" "vnet" {
  name                = "vNet"
  address_space       = ["10.0.0.0/16"]
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
}

Reference

https://learn.microsoft.com/en-us/azure/virtual-network/virtual-networks-overview https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/virtual_network

3.2 The Subnet

The next important resource block is the subnet. Subnets are used to divide the network into several, smaller parts. For instance, the VNet could contain for example a subnet A and a subnet B. I’ll create just one subnet, which is named “internal”. The value of the “address_prefixes” attribute is “10.0.2.0/24”. According to the calculation, there are 256 possible IP addresses for this subnet.

The calculation for that would be: 2^(32-24) = 256

Of course, the range of a subnet has to be smaller in contrast to the range of the virtual network, as the subnet is part of it. By assigning the name of the VNet (virtual_network_name = azurerm_virtual_network.vnet.name), you attach the subnet to the virtual network. A possible IP address would be 10.0.2.4, which can be assigned to an Azure resource, like the already mentioned virtual machine.

resource "azurerm_subnet" "subnet" {
  name                 = "internal"
  resource_group_name  = azurerm_resource_group.rg.name
  virtual_network_name = azurerm_virtual_network.vnet.name
  address_prefixes     = ["10.0.2.0/24"]
}

Reference

https://www.techtarget.com/searchnetworking/definition/subnet https://learn.microsoft.com/en-us/azure/virtual-network/virtual-network-manage-subnet https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/subnet

3.3 The Virtual Machine

The virtual machine, named “windows10-20h1”, will be deployed in the virtual network, respectively in the subnet of the VNet. Consider the value of the property “Virtual network/subnet”, which is “vNet/internal” (see picture below) - it refers to the provided name of the “azurerm_virtual_network” instance and to the name of the “azurerm_subnet instance”. As already mentioned, a possible IP address of the subnet could be e.g.: 10.0.2.4. In my example, that IP address will be assigned to the virtual machine - consider the property “Private IP address” in the picture below:

vm_subnet_privateip

The Terraform resource block for the virtual machine can be seen below and consists of a lot of attributes, e.g.: for defining the name (“windows10-20h1”), defining an admin_username (“adminuser”), a password, etc. An additional important aspect is the assignment of “azurerm_network_interface.mynetworkinterface.id” to the “network_interface_ids”: this associates the virtual machine with the network interface.

resource "azurerm_windows_virtual_machine" "myvirtualmachine" {
  name                = "windows10-20h1"
  resource_group_name = azurerm_resource_group.rg.name
  location            = azurerm_resource_group.rg.location
  size                = var.my_virtual_machine_size
  admin_username      = "adminuser"
  admin_password      = var.my_virtual_machine_password
  availability_set_id = azurerm_availability_set.myavailabilityset.id
  network_interface_ids = [
    azurerm_network_interface.mynetworkinterface.id,
  ]

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

  source_image_reference {
    publisher = "MicrosoftWindowsDesktop"
    offer     = "windows-10"
    sku       = "20h1-pro"
    version   = "latest"
  }
}

Reference

https://learn.microsoft.com/en-us/azure/virtual-machines/overview https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/virtual_machine

3.4 The Network Interface

As already mentioned, a network interface can be associated with a virtual machine - it allows the virtual machine to access (among others) the internet. Besides the attributes referring to the name (“my-network-interface”), the location, and the resource group, the network interface also contains a specific block with regard to the IP configuration. By assigning the id of the subnet to the attribute “subnet_id”, you define this subnet as the location for the network interface. If you don’t want to take care about providing a specific IP address, then prefer “Dynamic” as a proper value instead of “Static” for the attribute “private_ip_address_allocation”. In addition, the IP configuration block also contains a public IP address reference.

resource "azurerm_network_interface" "mynetworkinterface" {
  name                = "my-network-interface"
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name

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

    public_ip_address_id = azurerm_public_ip.my-public-ip.id 
  }
}

Reference

https://learn.microsoft.com/en-us/azure/virtual-network/virtual-network-network-interface?tabs=network-interface-portal https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/network_interface

3.5 The Public IP

The public IP address allows you to connect to the Azure resources from the internet. E.g.: establish a remote desktop connection, without being connected to the virtual network. Without an assigned public IP address to your Azure resource (which is the virtual machine in that example), you’d need to connect to the virtual network by e.g.: using a VPN client.

resource "azurerm_subnet" "subnet" {
  name                = "my-public-ip"
  resource_group_name = azurerm_resource_group.rg.name
  location            = azurerm_resource_group.rg.location
  allocation_method   = "Dynamic"

  tags = {
    environment = "Testing"
  }
}

Reference

https://learn.microsoft.com/en-us/azure/virtual-network/ip-services/public-ip-addresses https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/public_ip

3.6 The Network Security Group

Use a network security group to define (among others) which ports should be exposed. This network security group named “allowrdpconnection” contains a security rule “rdpport”, which defines an inbound rule for allowing access to port 3389 using the TCP protocol. That’s mandatory for making it possible to establish an RDP connection to the virtual machine after provisioning it. The priority has a value of 100 - the number has to be unique for each rule.

Possible values are >= 100 and <= 4096. A rule with a priority of 200 would be more important than a rule with a priority of 300.

resource "azurerm_network_security_group" "sg-rdp-connection" {
  name                = "allowrdpconnection"
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name

  security_rule {
    name                       = "rdpport"
    priority                   = 100
    direction                  = "Inbound"
    access                     = "Allow"
    protocol                   = "Tcp"
    source_port_range          = "*"
    destination_port_range     = "3389"
    source_address_prefix      = "*"
    destination_address_prefix = "*"
  }

  tags = {
    environment = "Testing"
  }
}

The network security group has to be associated with the network interface - this is done with following block:

resource "azurerm_network_interface_security_group_association" "example" {
  network_interface_id      = azurerm_network_interface.mynetworkinterface.id
  network_security_group_id = azurerm_network_security_group.sg-rdp-connection.id
}

Reference

https://learn.microsoft.com/en-us/azure/virtual-network/network-security-groups-overview https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/network_security_group

4 The complete Terraform Configuration

The complete Terraform configuration, which contains the resource blocks explained in the previous section, can be seen below. The full example is also available at following link of my GitHub repository:

https://github.com/patkoch/iac_terraform_azure/tree/main/vm/win10-sg

main.tf

resource "azurerm_resource_group" "rg" {
  name     = "iac-azure-terraform"
  location = "westeurope"
}

resource "azurerm_availability_set" "myavailabilityset" {
  name                = "example-aset"
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
}

resource "azurerm_virtual_network" "vnet" {
  name                = "vNet"
  address_space       = ["10.0.0.0/16"]
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
}

resource "azurerm_subnet" "subnet" {
  name                 = "internal"
  resource_group_name  = azurerm_resource_group.rg.name
  virtual_network_name = azurerm_virtual_network.vnet.name
  address_prefixes     = ["10.0.2.0/24"]
}

resource "azurerm_public_ip" "my-public-ip" {
  name                = "my-public-ip"
  resource_group_name = azurerm_resource_group.rg.name
  location            = azurerm_resource_group.rg.location
  allocation_method   = "Dynamic"

  tags = {
    environment = "Testing"
  }
}

resource "azurerm_network_interface" "mynetworkinterface" {
  name                = "my-network-interface"
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name

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

    public_ip_address_id = azurerm_public_ip.my-public-ip.id 
  }
}

# Windows 10 Virtual Machine
resource "azurerm_windows_virtual_machine" "myvirtualmachine" {
  name                = "windows10-20h1"
  resource_group_name = azurerm_resource_group.rg.name
  location            = azurerm_resource_group.rg.location
  size                = var.my_virtual_machine_size
  admin_username      = "adminuser"
  admin_password      = var.my_virtual_machine_password
  availability_set_id = azurerm_availability_set.myavailabilityset.id
  network_interface_ids = [
    azurerm_network_interface.mynetworkinterface.id,
  ]

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

  source_image_reference {
    publisher = "MicrosoftWindowsDesktop"
    offer     = "windows-10"
    sku       = "20h1-pro"
    version   = "latest"
  }
}

# Security Group for allowing RDP Connection
resource "azurerm_network_security_group" "sg-rdp-connection" {
  name                = "allowrdpconnection"
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name

  security_rule {
    name                       = "rdpport"
    priority                   = 100
    direction                  = "Inbound"
    access                     = "Allow"
    protocol                   = "Tcp"
    source_port_range          = "*"
    destination_port_range     = "3389"
    source_address_prefix      = "*"
    destination_address_prefix = "*"
  }

  tags = {
    environment = "Testing"
  }
}

# Associate security group with network interface
resource "azurerm_network_interface_security_group_association" "example" {
  network_interface_id      = azurerm_network_interface.mynetworkinterface.id
  network_security_group_id = azurerm_network_security_group.sg-rdp-connection.id
}

variables.tf

variable "my_virtual_machine_password" {
  default     = "P@$$w0rd1234!"
  description = "Password of the Virtual Machine"
}

variable "my_virtual_machine_size" {
  default     = "Standard_D2_v4"
  description = "Size of the Virtual Machine"
}

Conclusion

There are several components, which need to mesh with one another like gear wheels if you are going to set up a virtual network in which you deploy resources like virtual machines. For that, it’s important to get a basic understanding of the different resources and their related dependency on other resources in Azure. I hope to go through each resource, which is covered by the corresponding Terraform resource block, helps you to improve your knowledge regarding the network concepts in Azure.