Terraform create an Azure Linux Virtual Machine with SSH key

Terraform create an Azure Linux Virtual Machine with SSH key

Introduction

Previously I have written one article on how to create an Azure Linux Virtual Machine with Terraform. In that article, I have explained how to create a Virtual Machine with a password using Terraform. Now in this article, I will explain how to create a VM with an SSH Key using Terraform.

Prerequisite

  • Azure subscription

  • Terraform installed

  • Azure CLI installed

Terraform Configuration Files

We will use configuration files from the previous article only and will start making changes to them. I have created git repo of that for your reference.

We will add the following configuration in main.tf configuration file. It's creating SSH private key and saving in the local system.

# Create an SSH key
resource "tls_private_key" "linux_key" {
  algorithm = "RSA"
  rsa_bits  = 4096
}

# Save the private key to our local machine
# Use this key to connect to our Linux VM
resource "local_file" "linuxkey" {
  filename="linuxkey.pem"  
  content=tls_private_key.linux_key.private_key_pem 
}

Now scroll down to the same main.tf configuration file and locate azurerm_linux_virtual_machine section and remove the following lines from it as it suggests we are disabling password authentication.

disable_password_authentication = false
admin_username = "${var.username}"

Add the following configuration in the same section as above. It specifies the username and public key to connect the VM.

admin_ssh_key {
    username   = "${var.username}"
    public_key = tls_private_key.linux_key.public_key_openssh
  }

That's it we have done all changes required. Our section will look like below

resource "azurerm_linux_virtual_machine" "main" {
  name                            = "${var.prefix}-vm"
  computer_name                   = "linuxtestcomputername"
  resource_group_name             = azurerm_resource_group.main.name
  location                        = azurerm_resource_group.main.location
  size                            = "Standard_D2s_v4"
  admin_username                  = "${var.username}"
  network_interface_ids = [
    azurerm_network_interface.main.id,
  ]

  admin_ssh_key {
    username   = "${var.username}"
    public_key = tls_private_key.linux_key.public_key_openssh
  }

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

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

Final main.tf configuration will be as follows

provider "azurerm" {
  features {}
}

# Create an SSH key
resource "tls_private_key" "linux_key" {
  algorithm = "RSA"
  rsa_bits = 4096
}

# Save the private key to our local machine
# Use this key to connect to our Linux VM
resource "local_file" "linuxkey" {
  filename="linuxkey.pem"  
  content=tls_private_key.linux_key.private_key_pem 
}

resource "azurerm_resource_group" "main" {
  name     = "${var.prefix}-rg"
  location = var.location
}

# Create virtual network
resource "azurerm_virtual_network" "main" {
  name                = "${var.prefix}-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                 = "${var.prefix}-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                = "${var.prefix}-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                = "${var.prefix}-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 = "*"
  }

}

# Create network interface
resource "azurerm_network_interface" "main" {
  name                = "${var.prefix}-nic"
  resource_group_name = azurerm_resource_group.main.name
  location            = azurerm_resource_group.main.location

  ip_configuration {
    name                          = "${var.prefix}-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
}


resource "azurerm_linux_virtual_machine" "main" {
  name                            = "${var.prefix}-vm"
  computer_name                   = "linuxtestcomputername"
  resource_group_name             = azurerm_resource_group.main.name
  location                        = azurerm_resource_group.main.location
  size                            = "Standard_D2s_v4"
  admin_username                  = "${var.username}"
  network_interface_ids = [
    azurerm_network_interface.main.id,
  ]

  admin_ssh_key {
    username   = "${var.username}"
    public_key = tls_private_key.linux_key.public_key_openssh
  }

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

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

Apply configuration

As we have learned how to apply configuration in the previous article. We will do it once again with the following commands

terraform init
terraform apply

After the successful implementation of applying the configuration, you will see a message in the console saying that 1 resource added to the infrastructure. You will see one linuxkey.pem file also created in the local directory. You can use this file to connect to VM using the following command for connection. Replace xx.xx.xx.xx with the public IP address of the newly created VM.

ssh -i linuxkey.pem adminlinux@xx.xx.xx.xx

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.