# Create an Azure Linux Virtual Machine with Terraform

## Introduction

In this blog, I'll tell you how to create an Azure Linux Virtual Machine using Terraform.

## **Prerequisite**

* Azure subscription
    
* Terraform installed
    
* Azure CLI installed
    

## Azure Subscription?

An Azure subscription is mandatory when you are using Azure resources like a database, virtual network, storage account or virtual machine.

## Terraform

> Terraform is ***an open-source infrastructure as code software tool that enables you to safely and predictably create, change, and improve infrastructure*.**

We will use this tool to create a simple Linux virtual machine to help you get started on your terraform learning journey.

## Create Terraform Configuration Files

1. Create a separate directory for terraform files
    

```bash
cd ~

mkdir terraform-example

cd terraform-example
```

1. Let’s create our terraform file and name it [main.tf](http://main.tf)
    

```bash
touch main.tf
```

1. Create the AzureRM Provider in Terraform
    

Open up [main.tf](http://main.tf) in your editor of choice and add the Azure provider to the top of the file.

```json
provider "azurerm" {
  features {}
}
```

1. Define the Azure Resource Group
    

Now let’s create our new resource group that everything will live inside. Select a region of your choice

```json
resource "azurerm_resource_group" "main" {
  name     = "tfvmexample-rg"
  location = "Central India"
}
```

1. Define a Virtual Network
    

Virtual Network enables Virtual Machine to securely communicate with the internet and on-premises networks.

```json
# Create virtual network
resource "azurerm_virtual_network" "main" {
  name                = "tfvmexample-vnet"
  address_space       = ["10.0.0.0/16"]
  location            = azurerm_resource_group.main.location
  resource_group_name = azurerm_resource_group.main.name
}
```

1. Define Subnet
    

Subnets enable you to segment the Virtual Network into one or more sub-networks and allocate a portion of the virtual network's address space to each subnet. You can then deploy Azure Virtual Machine in a specific subnet.

```json
# Create subnet
resource "azurerm_subnet" "internal" {
  name                 = "tfvmexample-subnet"
  resource_group_name  = azurerm_resource_group.main.name
  virtual_network_name = azurerm_virtual_network.main.name
  address_prefixes     = ["10.0.1.0/24"]
}
```

1. Define Public IP Address
    

Public IP addresses enables Virtual Machine to communicate to Internet

```json
# Create public IP
resource "azurerm_public_ip" "main" {
  name                = "tfvmexample-public-ip"
  location            = azurerm_resource_group.main.location
  resource_group_name = azurerm_resource_group.main.name
  allocation_method   = "Static"
}
```

1. Define 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 Virtual Machine. Here 3 inbound rules have been defined for SSH, HTTP and TCP connections to be allowed with Virtual Machine.

```json
# Create Network Security Group and rule
resource "azurerm_network_security_group" "main" {
  name                = "tfvmexample-network-security-group"
  location            = azurerm_resource_group.main.location
  resource_group_name = azurerm_resource_group.main.name

  security_rule {
    name                       = "SSH"
    priority                   = 300
    direction                  = "Inbound"
    access                     = "Allow"
    protocol                   = "Tcp"
    source_port_range          = "*"
    destination_port_range     = "22"
    source_address_prefix      = "*"
    destination_address_prefix = "*"
  }

  security_rule {
    name                       = "HTTP"
    priority                   = 310
    direction                  = "Inbound"
    access                     = "Allow"
    protocol                   = "Tcp"
    source_port_range          = "*"
    destination_port_range     = "80"
    source_address_prefix      = "*"
    destination_address_prefix = "*"
  }

  security_rule {
    name                       = "CutomPort9000"
    priority                   = 320
    direction                  = "Inbound"
    access                     = "Allow"
    protocol                   = "Tcp"
    source_port_range          = "*"
    destination_port_range     = "9000"
    source_address_prefix      = "*"
    destination_address_prefix = "*"
  }
}
```

1. Define Network Interface for our VM
    

A virtual machine created with the Azure portal, has one network interface with default settings. You may change default settings also. Here Public address defined earlier is attached to Network Interface

```json
# Create network interface
resource "azurerm_network_interface" "main" {
  name                = "tfvmexample-nic"
  resource_group_name = azurerm_resource_group.main.name
  location            = azurerm_resource_group.main.location

  ip_configuration {
    name                          = "tfvmexample-nic-config"
    subnet_id                     = azurerm_subnet.internal.id
    private_ip_address_allocation = "Dynamic"
    public_ip_address_id          = azurerm_public_ip.main.id
  }
}
```

1. Connect the Security Group to the Network Interface
    

Associate Security Group defined earlier to Network Interface

```json
# Connect the security group to the network interface
resource "azurerm_network_interface_security_group_association" "main" {
  network_interface_id      = azurerm_network_interface.main.id
  network_security_group_id = azurerm_network_security_group.main.id
}
```

1. Define the Virtual Machine
    

Define Virtual Machine with parameters like name, admin username and password, size to handle the workload, OS image reference and disk.

Attach Network Interface Id defined earlier to Virtual Machine

```json

# Create virtual machine
resource "azurerm_linux_virtual_machine" "main" {
  name                            = "tfvmexample-vm"
  resource_group_name             = azurerm_resource_group.main.name
  location                        = azurerm_resource_group.main.location
  size                            = "Standard_D8s_v3"
  admin_username                  = "adminuser"
  admin_password                  = "Terraform@123"
  disable_password_authentication = false
  network_interface_ids = [
    azurerm_network_interface.main.id,
  ]

  source_image_reference {
    publisher = "Canonical"
    offer     = "UbuntuServer"
    sku       = "18_04-lts-gen2"
    version   = "latest"
  }

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

}
```

The final main.tf file is as below

```bash
provider "azurerm" {
  features {}
}

resource "azurerm_resource_group" "main" {
  name     = "tfvmexample-rg"
  location = "Central India"
}

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

# Create subnet
resource "azurerm_subnet" "internal" {
  name                 = "tfvmexample-subnet"
  resource_group_name  = azurerm_resource_group.main.name
  virtual_network_name = azurerm_virtual_network.main.name
  address_prefixes     = ["10.0.1.0/24"]
}

# Create public IP
resource "azurerm_public_ip" "main" {
  name                = "tfvmexample-public-ip"
  location            = azurerm_resource_group.main.location
  resource_group_name = azurerm_resource_group.main.name
  allocation_method   = "Static"
}

# Create Network Security Group and rule
resource "azurerm_network_security_group" "main" {
  name                = "tfvmexample-network-security-group"
  location            = azurerm_resource_group.main.location
  resource_group_name = azurerm_resource_group.main.name

  security_rule {
    name                       = "SSH"
    priority                   = 300
    direction                  = "Inbound"
    access                     = "Allow"
    protocol                   = "Tcp"
    source_port_range          = "*"
    destination_port_range     = "22"
    source_address_prefix      = "*"
    destination_address_prefix = "*"
  }

  security_rule {
    name                       = "HTTP"
    priority                   = 310
    direction                  = "Inbound"
    access                     = "Allow"
    protocol                   = "Tcp"
    source_port_range          = "*"
    destination_port_range     = "80"
    source_address_prefix      = "*"
    destination_address_prefix = "*"
  }

  security_rule {
    name                       = "CutomPort9000"
    priority                   = 320
    direction                  = "Inbound"
    access                     = "Allow"
    protocol                   = "Tcp"
    source_port_range          = "*"
    destination_port_range     = "9000"
    source_address_prefix      = "*"
    destination_address_prefix = "*"
  }
}

# Create network interface
resource "azurerm_network_interface" "main" {
  name                = "tfvmexample-nic"
  resource_group_name = azurerm_resource_group.main.name
  location            = azurerm_resource_group.main.location

  ip_configuration {
    name                          = "tfvmexample-nic-config"
    subnet_id                     = azurerm_subnet.internal.id
    private_ip_address_allocation = "Dynamic"
    public_ip_address_id          = azurerm_public_ip.main.id
  }
}

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

# Create virtual machine
resource "azurerm_linux_virtual_machine" "main" {
  name                            = "tfvmexample-vm"
  resource_group_name             = azurerm_resource_group.main.name
  location                        = azurerm_resource_group.main.location
  size                            = "Standard_D8s_v3"
  admin_username                  = "adminuser"
  admin_password                  = "Terraform@123"
  disable_password_authentication = false
  network_interface_ids = [
    azurerm_network_interface.main.id,
  ]

  source_image_reference {
    publisher = "Canonical"
    offer     = "UbuntuServer"
    sku       = "18_04-lts-gen2"
    version   = "latest"
  }

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

}
```

## Login to Azure with CLI

Make sure you have the Azure CLI installed, then run:

```bash
az login
```

Which should bring up a browser window for you to log in to your Azure subscription.

## Apply Terraform Configuration

Now that we have written our config file, let’s run these 👇🏼 commands to let Terraform do its job.

```bash
terraform init
```

This will download and install the AZURE plugins for the providers we used. It initializes the backend as well.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1675590200961/827a734f-18c2-435b-8594-ed4de2585f4e.png align="center")

Run the below command to see the changes that will take place on the infrastructure.

```bash
terraform plan
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1675590236069/e69834f9-712f-4073-8327-eb205ad78f15.png align="center")

As we can see above plan suggests 8 resources to be added to the infrastructure.

If everything looks alright, let’s apply the changes by running 👇🏼

```bash
terraform apply
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1675590912625/11a2a8f2-03f3-4331-8523-59352feadfbb.png align="center")

As we can see above 1 Virtual Machine addition is completed.

Finally, you should be able to see the Azure Virtual Machine up and running in Azure Portal.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1675590936247/3173b192-c2f2-4a2b-94e2-3faed996e2bf.png align="center")

Destroy the virtual machine because it will cost you a lot if we keep it running for a longer time.

```bash
terraform destroy
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1675591156993/8fb22acd-e7fc-47bb-b869-7666b8bb8ec2.png align="center")

As we can see above 8 resources were destroyed which were created along with Virtual Machine.

# **Conclusion**

Terraform makes it easy for administrators to provision cloud resources in Azure. Using Terraform’s command files, you can automate provisioning to reduce the overhead of manually creating resources in the Azure portal.
