Deploy your Network and VPN Gateway via Terraform to Microsoft Azure

As in previous post (Terraform with Visual Studio Code for Azure) , I have explained why we can use Terraform to setup our Azure environment and how-to configure Visual Studio Code to obtain this goal.

In this post, I will explain how we can build a configuration files in Terraform and push the design to Azure and apply the configuration. In this test we will deploy some network components in Azure. Following has been included:

  • vNet 
  • Subnet(s)
  • VPN gateway. Because the on-premises test firewall is only supporting Internet Key Exchange version 1. I need to include a route table.
  • Route Table
  • Public IP – address
  • Network Security Group

Infrastructure as a code with Terraform, a declarative approach.

To make it more clear I have decided to pull apart the Terraform main configuration file, into smaller configuration files based on the type of the infrastructure, such as network, storage and compute. Below design of the infrastructure that will be deployed in Azure.

Design

`High level design

Before building the configuration files, you need to know following:

  • The order of variables, resources, etc. defined within the configuration doesn’t matter. Terraform configurations are declarative, so references to other resources and variables do not depend on the order they’re defined.
  • Files need to have the extension .tf or .tf.json
  • Mix Terraform files with Terraform syntax, with JSON is possible.
  • Use modules where possible the will lower the amount of code.

When I continue with last point, Modules. You can find these at the following link registry.terraform.io . Modules will reduce the amount of code in your configuration file and can be re-used. For example, Module Network, will create virtual networks and subnets. Below you will find the differences between using and not using the module network.

Using Module

The module network, using version 2.0.0 The module will create a virtual network and 3 subnets. All values are defined variables, in the «variables.tf» – file. 

module "network" {    
source="Azure/network/azurerm"
version="2.0.0"
vnet_name= "${var.ARM_AZ_VNET_NME}"
resource_group_name="${azurerm_resource_group.RGName.name}"
location="${azurerm_resource_group.RGName.location}"
address_space="${var.ARM_AZ_VNET_ADDR}" subnet_prefixes="${var.ARM_AZ_SBN_PROD_FE_ADDR}","${var.ARM_AZ_SBN_PROD_BE_ADDR}"] subnet_names="${var.ARM_AZ_SBN_PROD_FE_NME}","${var.ARM_AZ_SBN_PROD_BE_NME}"]
tags= {
Environment = "${var.TAGS_ENVPRD}"
Departement = "${var.TAGS_DEPIT}"
}
}

Standard Terraform Syntax


#Create Virtual Network for the Azure tenant
resource "azurerm_virtual_network" "vNetName" {
name = "${var.ARM_AZ_VNETNAME}"
location = "${azurerm_resource_group.RGName.location}"
resource_group_name = "${azurerm_resource_group.RGName.name}"
address_space = "${var.ARM_AZ_VNET_ADDR}"
tags {
Environment = "${var.TAGS_EnvPrd}"
Departement = "${var.TAGS_DepIT}"
}
}
#Define subnets
resource "azurerm_subnet" "SBN_PROD_FE" {
name = "${var.ARM_AZ_SBN_PROD_FE}"
resource_group_name = "${azurerm_resource_group.RGName.name}"
virtual_network_name = "${azurerm_virtual_network.vNetName.name}"
address_prefix = "${var.ARM_AZ_SBN_PROD_FE_ADDR}"
}
resource "azurerm_subnet" "SBN_PROD_BE" {
name = "${var.ARM_AZ_SBN_PROD_BE}"
resource_group_name = "${azurerm_resource_group.RGName.name}"
virtual_network_name = "${azurerm_virtual_network.vNetName.name}"
address_prefix = "${var.ARM_AZ_SBN_PROD_BE_ADDR}"
}

What do you think? I prefer to use modules where it’s possible or create your own modules. Blog will follow on how-to create a Terraform Module.

Folder structure

The structure of the folder will be as follow:


  • modules: will contain module (even nested modules is possible)
  • compute.tf: currently empty, but later will contain the virtual machine configuration
  • main.tf: Main configuration will contain general resource of the Azure environment and authentication parameters
  • network.tf: Will contain the most configuration items and contains network related resources
  • outputs.tf: currently empty
  • storage.tf: currently empty, contains configuration information about Azure Storage
  • variables.tf: containce variables that can be called in the main and other files of the configuration files. Always starting with “var. “

In the sections below I will explain the configuration files.

VARIABLES.TF

Input variables serve as parameters for a Terraform module. When used in the root module of a configuration, variables can be set from CLI arguments and environment variables. For child moduless, they allow values to pass from parent to child.


############################
# Authentication variables #
############################
# Add you service endpoint access strings
variable "ARM_CLIENT_ID" {
default=""
description="The Client ID of the Service Principal."
}
variable "ARM_SUBSCRIPTION_ID" {
default=""
description="The ID of the Azure Subscription in which to run the Acceptance Tests."
}
variable "ARM_CLIENT_SECRET" {
default=""
description="The Client Secret associated with the Service Principal."
}

variable "ARM_TENANT_ID" {
default=""
description="The Tenant ID to use."
}
############################
# GENERAL TENANT VARIABLES #
############################
variable "ARM_AZ_LOCATION" {
default="WestEurope"
description="Azure DataCenter Location."
}

variable "ARM_AZ_RGNAME" {
default="RG-..."
description="Resource Group"
}
####################
#NETWORK VARIABLES #
####################
#Virtual Network Name
variable "ARM_AZ_VNET_NME" {
default="vNet-..."
description="Production Virtual Network."
}
#Virtual Network Address Space
variable "ARM_AZ_VNET_ADDR" {
default="xxx.xxx.xxx.xxx/xx"
description="Virtual Network address space."
}
#VPN Gateway Subnet
variable "ARM_AZ_VPNGW_SBN_NME" {
default="GateWaySubnet"
description="GateWaySubnet is the standard default name for a VPN Gateway."
}
variable "ARM_AZ_VPNGW_SBN_ADDR" {
default="xxx.xxx.xxx.xxx/28"
}
#Virtual Network Subnet 01 - Front End
variable "ARM_AZ_SBN_PROD_FE_NME" {
default="SBN-xxx"
}
variable "ARM_AZ_SBN_PROD_FE_ADDR" {
default="xxx.xxx.xxx.xxx/xx"
}
#Virtual Network Subnet 02 - Back End
variable "ARM_AZ_SBN_PROD_BE_NME" {
default="SBN-..."
}
variable "ARM_AZ_SBN_PROD_BE_ADDR" {
default="xxx.xxx.xxx.xxx/xx"
}
#################################
# On-Premises Network variables #
#################################
#Define variable Local Network variables
variable "ONPREM_LOCATION_NAME" {
default="..."
description="On-Premises network name of the site"
}
variable "ONPREM_WANIP" {
default="xxx.xxx.xxx.xxx"
description="WAN IP Address of the On-Premises location"
}
variable "ONPREM_LCLNETW_ADDR" {
default=["xxx.xxx.xxx.xxx/xx"]
description="Local Networks of the On-Premises location"
}
variable "ONPREM_S2S_CONN_NAME" {
default="S2S_..."
description="VPN Gateway for the Site2Site connection between Azure and On-premises location"
}
#Define variable Public IP addresses
variable "PIP_S2S_NME" {
default="PIP-GW-S2S"
}
###################################
# Network variables - Route tables #
####################################
variable "ARM_AZ_ROUTETABLE001_NAME" {
default="ROUTETABLE001"
description="Route table on the vNet. Is needed because of the Policy Based VPN."
}
variable "ARM_AZ_ROUTE001-01_NAME" {
default="ROUTE xxx.xxx.xxx.xxx"
}
variable "ARM_AZ_ROUTE001-01_ADDR" {
default="xxx.xxx.xxx.xxx/16"
}
###########################
# Network variables - NSG #
###########################
variable "ARM_AZ_NSG01" {
default="NSG_FE"
description="Network Security Group connecte to the Front End Subnet"
}
variable "ARM_AZ_NSG02" {
default="NSG_BE"
description="Network Security Group connecte to the Back End Subnet"
}
########
# TAGS #
########
variable "TAGS_ENVPRD" {
default="Production"
}
variable "TAGS_ENVTST" {
default="Test"
}
variable "TAGS_ENVDEV" {
default="Development"
}
variable "TAGS_DEPIT" {
default="IT"
}
variable "TAGS_DEPHR" {
default="Human Resources"
}
##########################

MAIN.TF

In the main file I have define the provider and general Azure components.


#PROVIDER AZURE
provider "azurerm" {
subscription_id = "${var.ARM_SUBSCRIPTION_ID}"
client_id = "${var.ARM_CLIENT_ID}"
client_secret = "${var.ARM_CLIENT_SECRET}"
tenant_id = "${var.ARM_TENANT_ID}"
}
#CREATING RESOURCE GROUP NAME
resource "azurerm_resource_group" "RGName" {
name = "${var.ARM_AZ_RGNAME}"
location = "${var.ARM_AZ_LOCATION}"
tags {
Environment = "${var.TAGS_ENVPRD}"
Departement = "${var.TAGS_DEPIT}"
}
}

NETWORK.TF

The network file contain all the network components. 


/*
###################################
# NETWORK CONFIGURATION TERRAFORM #
###################################
*/
##########################################################################
# CREATING NETWORK INFRASTRUCTURE (Using the Azure Terraform Module, that
# is located on terraform)
##########################################################################
module "network" {
source = "Azure/network/azurerm"
version = "2.0.0"
vnet_name = "${var.ARM_AZ_VNET_NME}"
resource_group_name = "${azurerm_resource_group.RGName.name}"
location = "${azurerm_resource_group.RGName.location}"
address_space = "${var.ARM_AZ_VNET_ADDR}"
subnet_prefixes = ["${var.ARM_AZ_SBN_PROD_FE_ADDR}","${var.ARM_AZ_SBN_PROD_BE_ADDR}"]
subnet_names = ["${var.ARM_AZ_SBN_PROD_FE_NME}", "${var.ARM_AZ_SBN_PROD_BE_NME}"]
tags = {
Environment = "${var.TAGS_ENVPRD}"
Departement = "${var.TAGS_DEPIT}"
}
}

resource "azurerm_subnet" "SBN_VPNGW_S2S" {
name = "${var.ARM_AZ_VPNGW_SBN_NME}"
resource_group_name = "${azurerm_resource_group.RGName.name}"
virtual_network_name = "${module.network.vnet_name}"
address_prefix = "${var.ARM_AZ_VPNGW_SBN_ADDR}"
}

########################
# CREATING VPN GATEWAY #
########################
#Create Local Network Gateway
resource "azurerm_local_network_gateway" "On-Premises" {
name = "${var.ONPREM_LOCATION_NAME}"
resource_group_name = "${azurerm_resource_group.RGName.name}"
location = "${azurerm_resource_group.RGName.location}"
gateway_address = "${var.ONPREM_WANIP}"
address_space = "${var.ONPREM_LCLNETW_ADDR}"
}
#Create a Public IP Address, that will be used by the VPN Gateway.
resource "azurerm_public_ip" "PIP_S2S" {
name = "${var.PIP_S2S_NME}"
location = "${azurerm_resource_group.RGName.location}"
resource_group_name = "${azurerm_resource_group.RGName.name}"
sku = "Basic"
public_ip_address_allocation = "Dynamic"
idle_timeout_in_minutes = 5
tags {
Environment = "${var.TAGS_ENVPRD}"
Departement = "${var.TAGS_DEPIT}"
}
}
resource "azurerm_virtual_network_gateway" "VNETGW_S2S" {
name = "TUN-VNETGW-S2S-BVE"
location = "${azurerm_resource_group.RGName.location}"
resource_group_name = "${azurerm_resource_group.RGName.name}"

type = "VPN"
vpn_type = "PolicyBased"

active_active = false
enable_bgp = false
sku = "Basic"

ip_configuration {
name = "vnetGatewayConfig"
public_ip_address_id = "${azurerm_public_ip.PIP_S2S.id}"
private_ip_address_allocation = "Dynamic"
subnet_id = "${azurerm_subnet.SBN_VPNGW_S2S.id}"
}
}
resource "azurerm_virtual_network_gateway_connection" "VPNGW_S2S_CONN" {
name = "${var.ONPREM_S2S_CONN_NAME}"
location = "${azurerm_resource_group.RGName.location}"
resource_group_name = "${azurerm_resource_group.RGName.name}"
#Site-2-Site: IPSec
#ExpressRoute: ExpressRoute
#VNet-2-VNet: Vnet2Vnet
type = "IPsec"
virtual_network_gateway_id = "${azurerm_virtual_network_gateway.VNETGW_S2S.id}"
local_network_gateway_id = "${azurerm_local_network_gateway.On-Premises.id}"
shared_key = "5-s3gf-24fd34-4j94s-2f5s9g-h2k-bwcjrj-329543"
}

########################
# CREATING ROUTE TABLE #
########################
module "routetable" {
source = "Azure/routetable/azurerm"
version = "1.0.0"
resource_group_name = "${azurerm_resource_group.RGName.name}"
location = "${azurerm_resource_group.RGName.location}"
route_table_name = "${var.ARM_AZ_ROUTETABLE001_NAME}"
route_prefixes = ["${var.ARM_AZ_ROUTE001-01_ADDR}"]
route_nexthop_types = ["VirtualNetworkGateway"]
route_names = ["${var.ARM_AZ_ROUTE001-01_NAME}"]

tags {
Environment = "${var.TAGS_ENVPRD}"
Departement = "${var.TAGS_DEPIT}"
}
}

###################################
# CREATING NETWORK SECURITY GROUP #
###################################
module "network-security-group" {
source = "Azure/network-security-group/azurerm"
version = "1.1.1"
resource_group_name = "${azurerm_resource_group.RGName.name}"
location = "${azurerm_resource_group.RGName.location}"
security_group_name = "${var.ARM_AZ_NSG01}"
custom_rules = [
{
name = "DNS-TCP"
priority = "100"
direction = "Inbound"
access = "Allow"
protocol = "tcp"
destination_port_range = "53"
source_address_prefix = "*"
destination_address_prefix = "*"
description = "DNS-TCP"
},
{
name = "DNS-UDP"
priority = "110"
direction = "Inbound"
access = "Allow"
protocol = "udp"
destination_port_range = "53"
source_address_prefix = "*"
destination_address_prefix = "*"
description = "DNS-UDP"
},
{
name = "RDP"
priority = "120"
direction = "Inbound"
access = "Allow"
protocol = "tcp"
destination_port_range = "3389"
source_address_prefix = "*"
destination_address_prefix = "*"
description = "RDP"
}
]
#"DNS-TCP": [ "Inbound", "Allow", "TCP", "*", "53", "DNS-TCP" ], "DNS-UDP": [ "Inbound", "Allow", "UDP", "*", "53", "DNS-UDP" ]
tags {
Environment = "${var.TAGS_ENVPRD}"
Departement = "${var.TAGS_DEPIT}"
}
}

resource "azurerm_subnet_network_security_group_association" "AssignNSGFE" {
#Assign the vNet Subnet Front End subnet_names =
# ["${var.ARM_AZ_SBN_PROD_FE_NME}"(0), "${var.ARM_AZ_SBN_PROD_BE_NME}"(1)]
subnet_id = "${module.network.vnet_subnets[0]}"
network_security_group_id = "${module.network-security-group.network_security_group_id}"
}

When we push the configuration files, plan and apply the network infrastructure, the resource group will look like this based on the variable that have been defined in the variables.tf file.

Azure Resource Group 

Following step will create storage account and deploy compute power in Azure.


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

This site uses Akismet to reduce spam. Learn how your comment data is processed.