Writing a Terraform script to deploy Azure infrastructure
To illustrate the use of Terraform to deploy resources in Azure, we will provision a simple Azure architecture with Terraform that is composed of the following:
- There's a group resource.
- There's also a network part composed of a virtual network and a subnet.
- In this subnet, we will create a virtual machine that has a public IP address in order to be publicly available.
For this, in the same directory where we previously created the provider.tf file, we will create a main.tf file with the following code:
- Let's start with the code that provides the resource group:
resource "azurerm_resource_group" "rg" {
name = "bookRg"
location = "West Europe"
tags {
environment = "Terraform Azure"
}
}
Any Terraform code is composed of the same syntax model, and the syntax of a Terraform object consists of four parts:
-
- A type of resource or data block
- A name of the resource to be managed (here, it's azurerm_resource_group)
- An internal Terraform ID (here, it's rg)
- A list of properties that correspond to the real properties of the resource (that is, name and location)
This code uses the azurerm_resource_group Terraform resource and will provision a resource group named bookRg that will be stored in the West Europe location.
- Then, we will write the code for the network part:
resource "azurerm_virtual_network" "vnet" {
name = "book-vnet"
location = "West Europe"
address_space = ["10.0.0.0/16"]
resource_group_name = azurerm_resource_group.rg.name
}
resource "azurerm_subnet" "subnet" {
name = "book-subnet"
virtual_network_name = azurerm_virtual_network.vnet.name
resource_group_name = azurerm_resource_group.rg.name
address_prefix = "10.0.10.0/24"
}
In this Terraform code for the network part, we create the code for a VNet, book-vnet, and in it we create a subnet, book-subnet.
If we look at this code carefully, we can see that, for dependencies between resources, we do not put in clear IDs, but we use pointers on Terraform resources.
The VNet and subnet are the property of the resource group with ${azurerm_resource_group.rg.name}, which tells Terraform that the VNet and subnet will be created just after the resource group. As for the subnet, it is dependent on its VNet with the use of the ${azurerm_virtual_network.vnet.name} value; it's the explicit dependence concept.
Let's now write the Terraform provisioning code of the virtual machine, which is composed of the following:
- A network interface
- A public IP address
- A storage for the diagnostic boot (boot information logs)
- A virtual machine
The sample code for the network interface with the IP configuration is as follows:
resource "azurerm_network_interface" "nic" {
name = "book-nic"
location = "West Europe"
resource_group_name = azurerm_resource_group.rg.name
ip_configuration {
name = "bookipconfig"
subnet_id = azurerm_subnet.subnet.id
private_ip_address_allocation = "Dynamic"
public_ip_address_id = azurerm_public_ip.pip.id
}
}
In this Terraform code, we use an azurerm_network_interface block (https://www.terraform.io/docs/providers/azurerm/r/network_interface.html), in which we configure the name, region, resource group, and IP configuration with the dynamic IP address of the network interface.
The code for public ip address, which has an IP address in the subnet we just created, is as follows:
resource "azurerm_public_ip" "pip" {
name = "book-ip"
location = "West Europe"
resource_group_name = "${azurerm_resource_group.rg.name}"
public_ip_address_allocation = "Dynamic"
domain_name_label = "bookdevops"
}
In this Terraform code, we use an azurerm_public_ip block at https://www.terraform.io/docs/providers/azurerm/r/public_ip.html, in which we configure the dynamic allocation of the IP address and the DNS label.
The code for storage account, which we use for the boot diagnostic logs, is as follows:
resource "azurerm_storage_account" "stor" {
name = "bookstor"
location = "West Europe"
resource_group_name = azurerm_resource_group.rg.name
account_tier = "Standard"
account_replication_type = "LRS"
}
In this Terraform code, we use an azurerm_storage_account block at https://www.terraform.io/docs/providers/azurerm/r/storage_account.html, in which we configure the name, region, resource group, and type of storage, which, in our case, is Standard LRS.
And the code for the Ubuntu virtual machine which contains the ID of the network interface created previously, is as follows:
resource "azurerm_virtual_machine" "vm" {
name = "bookvm"
location = "West Europe"
resource_group_name = azurerm_resource_group.rg.name
vm_size = "Standard_DS1_v2"
network_interface_ids = ["${azurerm_network_interface.nic.id}"]
storage_image_reference {
publisher = "Canonical"
offer = "UbuntuServer"
sku = "16.04-LTS"
version = "latest"
}
....
}
In this Terraform code, we use an azurerm_virtual_machine block at https://www.terraform.io/docs/providers/azurerm/r/virtual_machine.html, in which we configure the name, size (Standard_DS1_V2), reference to the network_interface Terraform object, and type of virtual machine OS system (Ubuntu).
All of these code sections are exactly like the previous ones with the use of explicit dependency to specify the relationships between resources.
We have just created a complete Terraform script that allows us to provision a small Azure infrastructure, but as in any language, there are good practices regarding file separation, applying a clear and readable code, and, finally, the use of built-in functions.