This is Part 2 of a 3-article series that demonstrates how to use infrastructure-as-code tools – in this case, Terraform – to provision and configure cloud networks. This article highlights the key benefits of IaC. It explores Azure Resource Manager and Terraform, a popular open-source IaC software solution, and demonstrates how to use Terraform to deploy Azure VNets.
Infrastructure as code (IaC) is an approach to managing cloud resources using a declarative model. In IaC, you declare your cloud environment, including VNets, in-text files of a specific format. These files are typically called templates.
The idea is to have reproducible declarations or templates of cloud environments, which you can deploy, update, and delete using a single command. The dedicated tool will take your template files, compare the list of resources to the current deployment state, and update the cloud services and resources accordingly.
In this article, we’ll highlight the key benefits of IaC. Then, we’ll explore Azure Resource Manager and Terraform, a popular open-source IaC software solution, and demonstrate how to use Terraform to deploy Azure VNets.
We will use the Azure Cloud Shell, so you don’t need to install anything locally. The Terraform command-line interface is already there. However, if you want to install Terraform locally, you can follow the installation documentation.
Advantages of IaC
Let’s begin by exploring four key advantages of IaC. First, you can keep your declarations in the source control system. This enables collaborative work on declaration files. It also allows you to track all the changes your team members make to the environment. By doing so, you can accelerate your cloud deployments and adopt similar strategies to resource provisioning as to the application’s source code. For example, the pull request can be employed to validate and consult the infrastructure changes with other team members before deployment.
Second, provisioning and configuring cloud resources and services using web portals, like Azure Portal, is generally easier and more convenient than using command-line tools. At some point, however, you need to perform many repetitive tasks, which are prone to human errors. Often, you need to make ad-hoc changes to the infrastructure, which can easily be forgotten. By keeping the declarations in the source code control system, you keep track of your changes and can quickly revert infrastructure as needed.
Third, having the declaration of your environment, you can quickly deploy its clone or similar version to other regions. Usually, IaC tools enable you to parameterize the deployment. This means you can quickly deploy similar environments.
Fourth, many code deployment and DevOps tools support IaC. You can deploy the environment using the similar CI/CD pipelines as you use for the code deployment. The cloud environment configuration then becomes a part of the code deployment process. As a result, you can adapt the underlying hardware to the changes in your workloads as your application or service grows.
Azure Resource Manager and Terraform
Azure natively provides the Azure Resource Manager (ARM) for IaC. ARM uses template files in JSON format. To generate the ARM templates, you can use the Azure Portal. When provisioning any service, during one of the very last steps of any Azure Portal wizard, you will see the option to “Generate template for automation.” This will generate the necessary files.
Keep in mind that ARM is Azure-specific. Other cloud platforms have their own tools. For example, Google uses Deployment Manager, while Amazon Web Services has CloudFormation. On top of that, third parties provide cross-platform tools, which can be used for IaC against any cloud or on-premises.
One such tool is Terraform from HashiCorp, which has become the standard for IaC automation. In Terraform, you create the template files describing your infrastructure and then deploy it to the cloud using the single command-line tool (terraform apply
).
Terraform Structure and Workflow
Terraform uses providers to enable its use across different clouds and on-premises datacenters. The provider is the bridge, which connects the Terraform CLI with the target API (like ARM), delivered by the cloud provider. Publicly available Terraform providers can be accessed through Terraform registry.
Typically, the Terraform provider is the first thing you specify in the Terraform template file. Then, you declare the resources to be provisioned. In fact, the single template file can use multiple providers to deploy various resources to different clouds at the same time. To install the provider, use the terraform init
CLI command.
Once you have the template file, it’s time to validate it using terraform validate
. Then, you create what is known as the execution plan (terraform apply
). Terraform produces the execution plan to compare the actual infrastructure model (whose resources you already have in the cloud or clouds) to the template file. Upon assessing the differences, Terraform presents the list of changes it will perform after you approve the plan.
Let’s see how to use Terraform to provision VNets in Azure!
Single Virtual Network Pattern
First, we deploy the VNet, which implements the Single Virtual Network pattern. Specifically, we will create the VNet with its two subnets — one for hypothetical back ends and the other for the front ends.
After logging into Azure, open the Cloud Shell and choose Bash interpreter by clicking the first icon in the top-right menu:
After the Cloud Shell is initialized, create the new folder 01, and inside of this folder, create the new file, vnet.tf.
mkdir 01
cd 01
code vnet.tf
Then, modify the file using the following statements:
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 3.0.2"
}
}
required_version = ">= 1.1.0"
}
provider "azurerm" {
features {}
}
resource "azurerm_resource_group" "rg-terraform-vnet" {
name = "rg-terraform-vnet"
location = "eastus"
}
resource "azurerm_virtual_network" "single-vnet" {
name = "single-vnet"
location = azurerm_resource_group.rg-terraform-vnet.location
resource_group_name = azurerm_resource_group. rg-terraform-vnet.name
address_space = ["10.0.0.0/16"]
dns_servers = ["10.0.0.4", "10.0.0.5"]
subnet {
name = "subnet-backends"
address_prefix = "10.0.1.0/24"
}
subnet {
name = "subnet-frontends"
address_prefix = "10.0.2.0/24"
}
tags = {
Pattern = "Single Virtual Network"
}
}
Afterwards, save and close the file using the code editor menu:
Before proceeding further, let’s analyze the structure of the Terraform template file we just created. First, we require Terraform to install the azurerm
provider:
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 3.0.2"
}
}
required_version = ">= 1.1.0"
}
Then, we require this provider:
provider "azurerm" {
features {}
}
The azurerm
provider will convert all the requests from the Terraform to the Azure Resource Manager to provision Azure resources.
The list of resources to be deployed follows the provider declaration. We start with the resource group called rg-terraform-vnet
, which will be provisioned in the East US region (eastus
):
resource "azurerm_resource_group" "rg-terraform-vnet" {
name = "rg-terraform-vnet"
location = "eastus"
}
Then, we have the declaration of our VNet:
resource "azurerm_virtual_network" "single-vnet" {
name = "single-vnet"
location = azurerm_resource_group.rg-terraform-vnet.location
resource_group_name = azurerm_resource_group.rg-terraform-vnet.name
address_space = ["10.0.0.0/16"]
dns_servers = ["10.0.0.4", "10.0.0.5"]
subnet {
name = "subnet-backends"
address_prefix = "10.0.1.0/24"
}
subnet {
name = "subnet-frontends"
address_prefix = "10.0.2.0/24"
}
tags = {
Pattern = "Single Virtual Network"
}
}
This VNet will be deployed to the same region as the resource group (location parameter). It will use the IP address range of 10.0.0.0/16 and contain two DNS servers. All requests will be the same.
Then, we have declarations of two subnets: one called subnet-backends
(address range of 10.0.1.0/24) and one named subnet-frontends
(10.0.2.0/24). Additionally, we created the tag of name Pattern
with the value Single Virtual Network
. Basically, you can configure the VNet in the same way as using Azure Portal.
Once we have this template file, we initialize the Terraform by typing:
terraform init
This will produce an output similar to the one shown in the following screenshot:
Then, we can validate our template:
terraform validate
If the template is valid, the command should output the “Success! The configuration is valid.” string as shown below.
Now, we can create the execution plan:
terraform apply
The execution plan contains the list of resources to be deployed, updated, or deleted:
By scrolling down, you will see the summary:
Now, you need to confirm the execution plan by typing “yes
”. Terraform will start the provisioning process. After it’s done, you will see the summary:
Under the hood, Terraform created a terraform.tfstate file, which is the JSON-formatted text file that Terraform uses for deployment:
Once the resources have been deployed, you can see your new VNet in the Azure portal:
Note that the configuration matches the Terraform template file. Moreover, after going to Settings > Subnets, you will see our back-end and front-end subnets:
As mentioned above, we can remove all the resources with the single Terraform command:
terraform destroy
VNet Peering
Now that you know how to work with Terraform to deploy VNet, let’s create another Terraform template, which will now create two VNets. Additionally, the template will configure the VNet peering between two virtual networks.
In the Cloud Shell, create a new folder, 02. Assuming you’re still in 01, you can do this using the following commands:
cd ..
mkdir 02
Under this folder, create the new vnet_with_peering.tf (code vnet_with_peering.tf)
and populate it using the following statements:
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 3.0.2"
}
}
required_version = ">= 1.1.0"
}
provider "azurerm" {
features {}
}
resource "azurerm_resource_group" "rg-terraform-vnet" {
name = "rg-terraform-vnet"
location = "eastus"
}
resource "azurerm_virtual_network" "vnet-1" {
name = "vnet-1"
resource_group_name = azurerm_resource_group.rg-terraform-vnet.name
address_space = ["10.0.1.0/24"]
location = azurerm_resource_group.rg-terraform-vnet.location
}
resource "azurerm_virtual_network" "vnet-2" {
name = "vnet-2"
resource_group_name = azurerm_resource_group.rg-terraform-vnet.name
address_space = ["10.0.2.0/24"]
location = azurerm_resource_group.rg-terraform-vnet.location
}
resource "azurerm_virtual_network_peering" "peer-vnet1-to-vnet2" {
name = "peer-vnet1-to-vnet2"
resource_group_name = azurerm_resource_group.rg-terraform-vnet.name
virtual_network_name = azurerm_virtual_network.vnet-1.name
remote_virtual_network_id = azurerm_virtual_network.vnet-2.id
}
resource "azurerm_virtual_network_peering" "peer-vnet2-to-vnet1" {
name = "peer-vnet2-to-vnet1"
resource_group_name = azurerm_resource_group.rg-terraform-vnet.name
virtual_network_name = azurerm_virtual_network.vnet-2.name
remote_virtual_network_id = azurerm_virtual_network.vnet-1.id
}
Apart from the provider, the template contains five resources: the resource group, two VNets, and two VNet peering. The VNets declarations follow the same structure as before. The new elements are the azurerm_virtual_network_peering
resources, which will configure the VNet peering. As shown above, those resources use the virtual_network_name
to provide the VNet from which the peering will be directed to, and the remote_virtual_network_id
pointing to the target VNet. The identifiers of the target networks are retrieved from the corresponding azurerm_virtual_network
resources.
We can now initialize Terraform:
terraform init
To validate the template, and provision the resources type (remember to confirm the execution plan by typing yes
):
terraform validate
terraform apply
After the deployment is complete, you will now have two VNets:
Each will contain the peering configuration:
Again, to destroy the resource type:
terraform destroy
Summary
In this tutorial, we learned how to use Terraform to provision virtual networks in Azure using IaC. We also learned how Terraform works and used this knowledge to create a single VNet with two subnets and multiple VNets with peering between them.
Most importantly, we achieved all this without installing anything locally, thanks to Azure Cloud Shell and a built-in code editor. The final article in this series will address securing and routing your cloud networks.
To learn more about how, through IaC, you apply software engineering practices such as testing and versioning to your DevOps practices, check out the resource Infrastructure as code.