Create Azure VM with Terraform
Azure Vm will be created along with Resource Group, Vnet, Security Group, Public IP
Why do I use Terraform to create Azure resources?
Well, I am not interested in repeating the same manual steps in Azure Portal, every-time for multiple environments which needs same configuration.
Terraform gives the flexibility to define a template for an environment & then use it for multiple times.
Now, let's write some hcl code...
terraform {
backend "local" {}
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "3.20.0"
}
}
}
provider "azurerm" {
features {}
}
Configure a remote backend to store state files. Terraform uses persisted state data to keep track of the resources it manages. This lets multiple people access the state data and work together on that collection of infrastructure resources.
Resource Group
resource "azurerm_resource_group" "rg" {
location = "eastus"
name = "tf-anils-demo"
}
All our resources will be created in this resource group only.
Vnet
resource "azurerm_virtual_network" "vnet" {
address_space = ["10.0.0.0/16"]
name = "anils_demo-vnet"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
depends_on = [
azurerm_resource_group.rg,
]
}
Subnet
resource "azurerm_subnet" "subnet" {
address_prefixes = ["10.0.0.0/24"]
name = "default"
resource_group_name = azurerm_resource_group.rg.name
virtual_network_name = azurerm_virtual_network.vnet.name
depends_on = [
azurerm_virtual_network.vnet,
]
}
Public-IP
resource "azurerm_public_ip" "public_ip" {
allocation_method = "Static"
location = azurerm_resource_group.rg.location
name = "anils-demo-1-ip"
resource_group_name = azurerm_resource_group.rg.name
sku = "Standard"
depends_on = [
azurerm_resource_group.rg,
]
}
Public IP will be attached to Network Interface.
Security Group & Rule
resource "azurerm_network_security_group" "sg" {
location = azurerm_resource_group.rg.location
name = "anils-demo-1-nsg"
resource_group_name = azurerm_resource_group.rg.name
depends_on = [
azurerm_resource_group.rg,
]
}
resource "azurerm_network_security_rule" "rule1" {
access = "Allow"
destination_address_prefix = "*"
destination_port_range = "22"
direction = "Inbound"
name = "SSH"
network_security_group_name = azurerm_network_security_group.sg.name
priority = 300
protocol = "Tcp"
resource_group_name = azurerm_resource_group.rg.name
source_address_prefix = "*"
source_port_range = "*"
depends_on = [
azurerm_network_security_group.sg,
]
}
Here in rules allowing only SSH for our login purpose.
Network Interface
resource "azurerm_network_interface" "ni-1" {
location = azurerm_resource_group.rg.location
name = "anils-demo-114"
resource_group_name = azurerm_resource_group.rg.name
ip_configuration {
name = "ipconfig1"
private_ip_address_allocation = "Dynamic"
public_ip_address_id = azurerm_public_ip.public_ip.id
subnet_id = azurerm_subnet.subnet.id
}
depends_on = [
azurerm_public_ip.public_ip,
azurerm_subnet.subnet,
azurerm_network_security_group.sg,
]
}
resource "azurerm_network_interface_security_group_association" "attach-sg-ni" {
network_interface_id = azurerm_network_interface.ni-1.id
network_security_group_id = azurerm_network_security_group.sg.id
}
Don't forget to attach network interface & security group. If not attached, you can't able to access VM.
Linux VM
resource "tls_private_key" "ssh" {
algorithm = "RSA"
rsa_bits = "4096"
}
resource "local_file" "ssh_private_key" {
content = tls_private_key.ssh.private_key_pem
filename = "./tls/private.pem"
file_permission = "0600"
}
resource "azurerm_linux_virtual_machine" "vm-1" {
admin_username = "anil"
disable_password_authentication = true
location = azurerm_resource_group.rg.location
name = "anils-demo-1"
admin_ssh_key {
username = "anil"
# public_key = file("./ssh_keys/id_rsa.pub") ## use this if you existing keys
public_key = tls_private_key.ssh.public_key_openssh
}
network_interface_ids = [
azurerm_network_interface.ni-1.id,
]
resource_group_name = azurerm_resource_group.rg.name
size = "Standard_B1s"
boot_diagnostics {
}
os_disk {
caching = "ReadWrite"
storage_account_type = "Premium_LRS"
}
source_image_reference {
offer = "0001-com-ubuntu-server-jammy"
publisher = "canonical"
sku = "22_04-lts-gen2"
version = "latest"
}
depends_on = [
azurerm_network_interface.ni-1,
]
}
Here, ssh-keys will be created and then public key will be added to VM. Using the private we have to login.
Place all components in single file. main.tf
################################
## Resource Group
################################
resource "azurerm_resource_group" "rg" {
location = "eastus"
name = "tf-anils-demo"
}
################################
## Vnet
################################
resource "azurerm_virtual_network" "vnet" {
address_space = ["10.0.0.0/16"]
name = "anils_demo-vnet"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
depends_on = [
azurerm_resource_group.rg,
]
}
################################
## subnet
################################
resource "azurerm_subnet" "subnet" {
address_prefixes = ["10.0.0.0/24"]
name = "default"
resource_group_name = azurerm_resource_group.rg.name
virtual_network_name = azurerm_virtual_network.vnet.name
depends_on = [
azurerm_virtual_network.vnet,
]
}
################################
## Public IP
################################
resource "azurerm_public_ip" "public_ip" {
allocation_method = "Static"
location = azurerm_resource_group.rg.location
name = "anils-demo-1-ip"
resource_group_name = azurerm_resource_group.rg.name
sku = "Standard"
depends_on = [
azurerm_resource_group.rg,
]
}
################################
## Security Group & Rules
################################
resource "azurerm_network_security_group" "sg" {
location = azurerm_resource_group.rg.location
name = "anils-demo-1-nsg"
resource_group_name = azurerm_resource_group.rg.name
depends_on = [
azurerm_resource_group.rg,
]
}
resource "azurerm_network_security_rule" "rule1" {
access = "Allow"
destination_address_prefix = "*"
destination_port_range = "22"
direction = "Inbound"
name = "SSH"
network_security_group_name = azurerm_network_security_group.sg.name
priority = 300
protocol = "Tcp"
resource_group_name = azurerm_resource_group.rg.name
source_address_prefix = "*"
source_port_range = "*"
depends_on = [
azurerm_network_security_group.sg,
]
}
################################
## Network Interface
################################
resource "azurerm_network_interface" "ni-1" {
location = azurerm_resource_group.rg.location
name = "anils-demo-114"
resource_group_name = azurerm_resource_group.rg.name
ip_configuration {
name = "ipconfig1"
private_ip_address_allocation = "Dynamic"
public_ip_address_id = azurerm_public_ip.public_ip.id
subnet_id = azurerm_subnet.subnet.id
}
depends_on = [
azurerm_public_ip.public_ip,
azurerm_subnet.subnet,
azurerm_network_security_group.sg,
]
}
################################
## Attach Security Group & Network Interface
################################
resource "azurerm_network_interface_security_group_association" "attach-sg-ni" {
network_interface_id = azurerm_network_interface.ni-1.id
network_security_group_id = azurerm_network_security_group.sg.id
}
################################
## Linux VM
################################
resource "tls_private_key" "ssh" {
algorithm = "RSA"
rsa_bits = "4096"
}
resource "local_file" "ssh_private_key" {
content = tls_private_key.ssh.private_key_pem
filename = "./tls/private.pem"
file_permission = "0600"
}
resource "azurerm_linux_virtual_machine" "vm-1" {
admin_username = "anil"
disable_password_authentication = true
location = azurerm_resource_group.rg.location
name = "anils-demo-1"
admin_ssh_key {
username = "anil"
# public_key = file("./ssh_keys/id_rsa.pub") ## use this if you existing keys
public_key = tls_private_key.ssh.public_key_openssh
}
network_interface_ids = [
azurerm_network_interface.ni-1.id,
]
resource_group_name = azurerm_resource_group.rg.name
size = "Standard_B1s"
boot_diagnostics {
}
os_disk {
caching = "ReadWrite"
storage_account_type = "Premium_LRS"
}
source_image_reference {
offer = "0001-com-ubuntu-server-jammy"
publisher = "canonical"
sku = "22_04-lts-gen2"
version = "latest"
}
depends_on = [
azurerm_network_interface.ni-1,
]
}
output "Vm-ip" {
value = azurerm_linux_virtual_machine.vm-1.public_ip_address
}
Run the terraform commands to create resources
$ terraform init
$ terraform plan
-> Plan: 12 to add, 0 to change, 0 to destroy.
$ terraform apply --auto-approve
-> Apply complete! Resources: 12 added, 0 changed, 0 destroyed.
Outputs:
Vm-ip = "20.232.149.161"
Destroy resources once done...
$ terraform destroy --auto-approve
-> Destroy complete! Resources: 12 destroyed.
Code available at Github Thanks for reading :)