How to create a Virtual Machine in Azure using Terraform

Jagan mohan Gade
6 min readJul 3, 2024

--

Let’s create a virtual machine in Azure

If you know: Basics of Terraform, Virtual Machine creation using Azure portal, Great Let’s Go!!!

Image credits: https://t.co/tM0W0QBnAv

Install Terraform and connect Azure cloud:

Refer the below link to Install terraform: https://medium.com/@gadejaganmohan/setting-up-ansible-and-terraform-in-2024-d179ac4e706d

  • Install Azure Cli and connect to Azure Cloud (using “az login”)

To create a Virtual Machine in Azure we need the following Terraform resources.

  • Login to the Virtual Machine you created with Terraform installed (ssh username@public-ip)
  • Create a directory “Azure-VM” and a main.tf in the directory you just created .
I used my Local machine since i have terraform already installed

Add the resources to main.tf file:

Azure Provider Configuration:

This configuration block in Terraform specifies which provider (in this case, Azure) to use and any required settings. It initializes the Azure Resource Manager (ARM) provider to interact with Azure services.

provider "azurerm" {
features {}
}

Resource Group:

A resource group is a container that holds related resources for an Azure solution. It helps manage and organize Azure resources.

# Create a resource group
resource "azurerm_resource_group" "rg" {
name = "terra-rg"
location = "East US"
}

Virtual Network:

A virtual network (VNet) is the fundamental building block for your private network in Azure. It enables many types of Azure resources to communicate securely with each other, the internet, and on-premises networks.

# Create a virtual network
resource "azurerm_virtual_network" "vnet" {
name = "terra-vnet"
address_space = ["10.0.0.0/16"]
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
}

Subnet:

A subnet is a range of IP addresses in the VNet. You can divide a VNet into multiple subnets for organization and security.

# Create a subnet
resource "azurerm_subnet" "subnet" {
name = "terra-subnet"
resource_group_name = azurerm_resource_group.rg.name
virtual_network_name = azurerm_virtual_network.vnet.name
address_prefixes = ["10.0.1.0/24"]
}

Public IP:

A public IP address is used to communicate with the virtual machine from the internet. It is assigned to the network interface attached to the VM.

# Create a public IP
resource "azurerm_public_ip" "ip" {
name = "terra-pip"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
allocation_method = "Dynamic"
}

Network Security Group and rules:

A network security group contains security rules that allow or deny inbound network traffic to, or outbound network traffic from, several types of Azure resources. For each rule, you can specify source and destination, port, and protocol.

# Create a network security group and rule
resource "azurerm_network_security_group" "nsg" {
name = "terra-nsg"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
# allow traffic to port 22 for ssh connection
security_rule {
name = "SSH"
priority = 1001
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "22"
source_address_prefix = "*"
destination_address_prefix = "*"
}
}

Network Interface:

A network interface (NIC) connects the virtual machine to the network. It can have one or more IP configurations.

# Create a network interface
resource "azurerm_network_interface" "nic" {
name = "terra-nic"
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.ip.id
}
}

NSG and NIC association:

In terraform we use the “azurerm_network_interface_security_group_association” resource to connect Network Interface Card to Network Security Group.

# Connect the security group to the network interface
resource "azurerm_network_interface_security_group_association" "nic-nsg" {
network_interface_id = azurerm_network_interface.nic.id
network_security_group_id = azurerm_network_security_group.nsg.id
}

Virtual Machine:

A virtual machine (VM) is an Azure compute resource that can be used to deploy and manage a variety of applications and services.

# Create a virtual machine
resource "azurerm_linux_virtual_machine" "vm" {
name = "terra-vm"
resource_group_name = azurerm_resource_group.rg.name
location = azurerm_resource_group.rg.location
size = "Standard_DS1_v2"
admin_username = "adminuser"
admin_password = "Password@123"
disable_password_authentication = "false"
network_interface_ids = [
azurerm_network_interface.nic.id,
]

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

source_image_reference {
publisher = "Canonical"
offer = "0001-com-ubuntu-server-jammy"
sku = "22_04-lts-gen2"
version = "latest"
}

computer_name = "terra-vm"

}

Output Block:

The Output block in Terraform is used to display values after the execution of your Terraform configuration. In this particular case, it is used to output the public IP address of an Azure Linux virtual machine.

output "public_ip_address" {
value = azurerm_linux_virtual_machine.vm.public_ip_address
}

The complete main.tf file:

provider "azurerm" {
features {}
}

# Create a resource group
resource "azurerm_resource_group" "rg" {
name = "terra-rg"
location = "East US"
}

# Create a virtual network
resource "azurerm_virtual_network" "vnet" {
name = "terra-vnet"
address_space = ["10.0.0.0/16"]
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
}

# Create a subnet
resource "azurerm_subnet" "subnet" {
name = "terra-subnet"
resource_group_name = azurerm_resource_group.rg.name
virtual_network_name = azurerm_virtual_network.vnet.name
address_prefixes = ["10.0.1.0/24"]
}

# Create a public IP
resource "azurerm_public_ip" "ip" {
name = "terra-pip"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
allocation_method = "Dynamic"
}

# Create a network security group and rule
resource "azurerm_network_security_group" "nsg" {
name = "terra-nsg"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
# allow traffic to port 22 for ssh connection
security_rule {
name = "SSH"
priority = 1001
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "22"
source_address_prefix = "*"
destination_address_prefix = "*"
}
}


# Create a network interface
resource "azurerm_network_interface" "nic" {
name = "terra-nic"
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.ip.id
}
}

# Connect the security group to the network interface
resource "azurerm_network_interface_security_group_association" "nic-nsg" {
network_interface_id = azurerm_network_interface.nic.id
network_security_group_id = azurerm_network_security_group.nsg.id
}


# Create a virtual machine
resource "azurerm_linux_virtual_machine" "vm" {
name = "terra-vm"
resource_group_name = azurerm_resource_group.rg.name
location = azurerm_resource_group.rg.location
size = "Standard_DS1_v2"
admin_username = "adminuser"
admin_password = "Password@123"
disable_password_authentication = "false"
network_interface_ids = [
azurerm_network_interface.nic.id,
]


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

source_image_reference {
publisher = "Canonical"
offer = "0001-com-ubuntu-server-jammy"
sku = "22_04-lts-gen2"
version = "latest"
}

computer_name = "terra-vm"

}
output "public_ip_address" {
value = azurerm_linux_virtual_machine.vm.public_ip_address
}

Initialize Terraform: “terraform init”

  • This command is used to initialize a Terraform working directory. This step sets up necessary files and downloads any required provider plugins (such as the Azure provider in this case).

Validate Terraform: “terraform validate”

  • This command checks the Terraform configuration files for syntax errors and internal consistency. It ensures that the configuration is syntactically valid and logically sound.

Plan Terraform: “terraform plan”

  • This command creates an execution plan. It shows what actions Terraform will take to achieve the desired state defined in your configuration files. It does not make any changes but provides a preview of the changes that will be applied.

Apply Terraform: “terraform apply”

  • This command applies the changes required to reach the desired state of the configuration. It creates, updates, or deletes resources as necessary to match the configuration files.

Connect to the VM created using SSH:

Enter the password: Password@123
Finally we connected to our VM

Destroy Terraform: “terraform destroy”

*** Be careful while using***

  • This command is used to destroy the Terraform-managed infrastructure. It removes all the resources defined in your configuration files.

--

--