Skip to main content
Jorge Bernhardt Jorge Bernhardt
  1. Posts/

Terraform - Deploying Azure Hub-Spoke Networking

·1516 words·8 mins· 100 views · 5 likes ·
Microsoft Azure Networking Terraform Virtual network

In this blog post, I want to show you how to implement a hub-spoke network architecture on Azure using Terraform, one of my favorite infrastructure as code (IaC) tools. The hub-spoke model is a widely adopted networking strategy that allows you to simplify management, increase scalability, and improve the security of your Azure resources.

The “hub” in this scenario is a virtual network that acts as a central point of connectivity to your network, while the “spokes” are virtual networks that interact with the hub, isolating workloads and allowing them to communicate with resources in the hub.

Prerequisites>

Prerequisites #

  • You need Terraform CLI on your local machine, if you’re new to using Terraform to deploy Microsoft Azure resources, then I recommend you check out this link.
  • A text editor or IDE of your choice (Visual Studio Code with Terraform extension is my recommendation)
Declare Azure Provider in Terraform>

Declare Azure Provider in Terraform #

The provider.tf file in Terraform is used to specify and configure the providers used in your Terraform configuration. A provider is a service or platform where the resources will be managed. This could be a cloud provider like Microsoft Azure, AWS, Google Cloud, etc.

This file is important because it tells Terraform which provider’s API to use when creating, updating, and deleting resources. Without it, Terraform wouldn’t know where to manage your resources.

provider "azurerm" {
  features {}
}
Create an Azure Network Hub-Spoke using Terraform>

Create an Azure Network Hub-Spoke using Terraform #

In the case of Azure hub-spoke network deployment, the main.tf file contains the following key components:

  • Resource Group: This container holds related resources for an Azure solution. The resource group includes those resources that you want to manage as a group.

  • Virtual Networks: Two types of virtual networks are declared in the main.tf, the hub, and the spoke. The hub is typically used to connect to different spokes. The spokes are connected to the hub and can communicate with each other via the hub.

  • Subnets: These are subnetworks within the virtual network. In the main.tf, subnets are declared within both the hub and spoke networks. They provide segmentation within the virtual network.

  • Virtual Network Peering: This allows for connectivity between virtual networks. In the main.tf, peering is established from the hub to each spoke and vice versa. This enables the spokes to communicate with each other via the hub.

// Create a resource group
resource "azurerm_resource_group" "rg" {
  name     = var.hub_network.resourceGroupName
  location = var.hub_network.location
  tags     = var.hub_network.tags
}

// Create a hub virtual network
resource "azurerm_virtual_network" "hub_vnet" {
  name                = var.hub_network.vnetName
  resource_group_name = azurerm_resource_group.rg.name
  location            = var.hub_network.location
  address_space       = [var.hub_network.addressSpace]
  dns_servers         = var.hub_network.dnsServers
  tags                = var.hub_network.tags
}

// Create subnets within the hub virtual network
resource "azurerm_subnet" "hub_subnets" {
  count                = length(var.hub_network.subnetNames)
  name                 = var.hub_network.subnetNames[count.index]
  resource_group_name  = azurerm_resource_group.rg.name
  virtual_network_name = azurerm_virtual_network.hub_vnet.name
  address_prefixes     = [var.hub_network.subnetPrefixes[count.index]]
}

// Create the spokes virtual networks 
resource "azurerm_virtual_network" "spoke_vnets" {
  count               = length(var.spoke_networks)
  name                = var.spoke_networks[count.index].vnetName
  resource_group_name = azurerm_resource_group.rg.name
  location            = var.spoke_networks[count.index].location
  address_space       = [var.spoke_networks[count.index].addressSpace]
  dns_servers         = var.hub_network.dnsServers
  tags                = var.hub_network.tags
}

// Create a default subnet within each spoke virtual network
resource "azurerm_subnet" "spoke_default_subnets" {
  count                = length(var.spoke_networks)
  name                 = var.spoke_networks[count.index].defaultSubnetName
  resource_group_name  = azurerm_resource_group.rg.name
  virtual_network_name = azurerm_virtual_network.spoke_vnets[count.index].name
  address_prefixes     = [var.spoke_networks[count.index].defaultSubnetPrefix]
}

// Create a peering connection from the hub to each spoke
resource "azurerm_virtual_network_peering" "hub_to_spoke" {
  count                     = length(var.spoke_networks)
  name                      = "${var.hub_network.vnetName}-to-${azurerm_virtual_network.spoke_vnets[count.index].name}"
  resource_group_name       = azurerm_resource_group.rg.name
  virtual_network_name      = azurerm_virtual_network.hub_vnet.name
  remote_virtual_network_id = azurerm_virtual_network.spoke_vnets[count.index].id
}

// Create a peering connection from each spoke to the hub
resource "azurerm_virtual_network_peering" "spoke_to_hub" {
  count                     = length(var.spoke_networks)
  name                      = "${azurerm_virtual_network.spoke_vnets[count.index].name}-to-${var.hub_network.vnetName}"
  resource_group_name       = azurerm_resource_group.rg.name
  virtual_network_name      = azurerm_virtual_network.spoke_vnets[count.index].name
  remote_virtual_network_id = azurerm_virtual_network.hub_vnet.id
}
Declaration of input variables>

Declaration of input variables #

The variables.tf file in Terraform defines the variables I will use in the main.tf file. These variables allow for more flexibility and reusability in the code. In this example, the variables are defined in the variables.tf include:

  • hub_network: This variable is an object that contains properties for configuring the hub network. These properties include the virtual network’s name, the resource group’s name, location, address space, DNS servers, tags, subnet names, and subnet prefixes.

  • spoke_networks: This variable is a list of objects, with each object representing a spoke network. The properties of each spoke network include the name of the virtual network, the address space, the default subnet name, the default subnet prefix, and the location.

// Define the variable for the hub network. This is an object with several properties.
variable "hub_network" {
  type = object({
    vnetName          = string // The name of the virtual network for the hub
    resourceGroupName = string // The name of the resource group where the hub network will be created
    location          = string // The Azure region where the hub network will be located
    addressSpace      = string // The IP address space for the hub network
    dnsServers        = list(string) // The list of DNS servers for the hub network
    tags              = map(string) // The tags to be applied to the hub network resources
    subnetNames       = list(string) // The list of subnet names within the hub network
    subnetPrefixes    = list(string) // The list of subnet prefixes corresponding to the subnet names in the hub network
  })
}

// Define the variable for the spoke networks. This is a list of objects, each with several properties.
variable "spoke_networks" {
  type = list(object({
    vnetName            = string // The name of the virtual network for each spoke
    addressSpace        = string // The IP address space for each spoke network
    defaultSubnetName   = string // The name of the default subnet within each spoke network
    defaultSubnetPrefix = string // The subnet prefix for the default subnet within each spoke network
    location            = string // The Azure region where each spoke network will be located
  }))
}
Declaration of output values>

Declaration of output values #

The output.tf file in Terraform extracts and displays information about the resources created or managed by your Terraform configuration. These outputs are defined using the output keyword and can be used to return information that can be useful for the user, for other Terraform configurations, or for programmatically using the information in scripts or other tools.

In this example, the output.tf file returns information about the IDs of the resource group, the hub and spoke virtual networks, the subnets, and the peering connections. Once Terraform has finished applying your configuration, it will display the defined outputs.

// Output for the resource group ID
output "resource_group_id" {
  description = "The ID of the created resource group."
  value       = azurerm_resource_group.rg.id
}

// Output for the hub virtual network ID
output "hub_vnet_id" {
  description = "The ID of the created hub virtual network."
  value       = azurerm_virtual_network.hub_vnet.id
}

// Output for the hub subnets IDs
output "hub_subnets_ids" {
  description = "The IDs of the created subnets in the hub virtual network."
  value       = [for s in azurerm_subnet.hub_subnets : s.id]
}

// Output for the spoke virtual networks IDs
output "spoke_vnets_ids" {
  description = "The IDs of the created spoke virtual networks."
  value       = [for v in azurerm_virtual_network.spoke_vnets : v.id]
}

// Output for the default subnets in the spoke virtual networks IDs
output "spoke_default_subnets_ids" {
  description = "The IDs of the created default subnets in the spoke virtual networks."
  value       = [for s in azurerm_subnet.spoke_default_subnets : s.id]
}

// Output for the peering connections from the hub to the spokes IDs
output "hub_to_spoke_peering_ids" {
  description = "The IDs of the created peering connections from the hub to the spokes."
  value       = [for p in azurerm_virtual_network_peering.hub_to_spoke : p.id]
}

// Output for the peering connections from the spokes to the hub IDs
output "spoke_to_hub_peering_ids" {
  description = "The IDs of the created peering connections from the spokes to the hub."
  value       = [for p in azurerm_virtual_network_peering.spoke_to_hub : p.id]
}
Executing the Terraform Deployment>

Executing the Terraform Deployment #

Now that you’ve declared the resources correctly, it’s time to take the following steps to deploy them in your Azure environment.

  • Initialization: To begin, execute the terraform init command. This will initialize your working directory that holds the .tf files and download the provider specified in the provider.tf file, and configure the Terraform backend. If you want to know how, check this link.

  • Planning: Next, execute the terraform plan. This command creates an execution plan and shows Terraform’s actions to achieve the desired state defined in your .tf files. This gives you a chance to review the changes before applying them.

  • Apply: When you’re satisfied with the plan, execute the terraform apply command. This will implement the required modifications to attain the intended infrastructure state. Before making any changes, you will be asked to confirm your decision.

  • Inspection: After applying the changes, you can use terraform show command to see the current state of your infrastructure.

  • Destroy (optional): when a project is no longer needed or when resources have become outdated. You can use the terraform destroy command. This will remove all the resources that Terraform has created.

References and useful links #

Thank you for taking the time to read my post. I sincerely hope that you find it helpful.