Quickstart: Configure a Linux virtual machine in Azure using Terraform
Article tested with the following Terraform and Terraform provider versions:
Terraform enables the definition, preview, and deployment of cloud infrastructure. Using Terraform, you create configuration files using HCL syntax. The HCL syntax allows you to specify the cloud provider - such as Azure - and the elements that make up your cloud infrastructure. After you create your configuration files, you create an execution plan that allows you to preview your infrastructure changes before they're deployed. Once you verify the changes, you apply the execution plan to deploy the infrastructure. For more information about using Terraform in Azure, see the Azure Terraform developer center
This article shows you how to create a complete Linux environment and supporting resources with Terraform. Those resources include a virtual network, subnet, public IP address, and more.
In this article, you learn how to:
- Create a virtual network
- Create a subnet
- Create a public IP address
- Create a network security group and SSH inbound rule
- Create a virtual network interface card
- Connect the network security group to the network interface
- Create a storage account for boot diagnostics
- Create SSH key
- Create a virtual machine
- Use SSH to connect to virtual machine
Prerequisites
- Azure subscription: If you don't have an Azure subscription, create a free account before you begin.
Configure Terraform: If you haven't already done so, configure Terraform using one of the following options:
Implement the Terraform code
Create a directory in which to test the sample Terraform code and make it the current directory.
Create a file named
providers.tf
and insert the following code:terraform { required_version = ">=0.12" required_providers { azurerm = { source = "hashicorp/azurerm" version = "~>2.0" } } } provider "azurerm" { features {} }
Create a file named
main.tf
and insert the following code:resource "random_pet" "rg-name" { prefix = var.resource_group_name_prefix } resource "azurerm_resource_group" "rg" { name = random_pet.rg-name.id location = var.resource_group_location } # Create virtual network resource "azurerm_virtual_network" "myterraformnetwork" { name = "myVnet" address_space = ["10.0.0.0/16"] location = azurerm_resource_group.rg.location resource_group_name = azurerm_resource_group.rg.name } # Create subnet resource "azurerm_subnet" "myterraformsubnet" { name = "mySubnet" resource_group_name = azurerm_resource_group.rg.name virtual_network_name = azurerm_virtual_network.myterraformnetwork.name address_prefixes = ["10.0.1.0/24"] } # Create public IPs resource "azurerm_public_ip" "myterraformpublicip" { name = "myPublicIP" location = azurerm_resource_group.rg.location resource_group_name = azurerm_resource_group.rg.name allocation_method = "Dynamic" } # Create Network Security Group and rule resource "azurerm_network_security_group" "myterraformnsg" { name = "myNetworkSecurityGroup" location = azurerm_resource_group.rg.location resource_group_name = azurerm_resource_group.rg.name 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 network interface resource "azurerm_network_interface" "myterraformnic" { name = "myNIC" location = azurerm_resource_group.rg.location resource_group_name = azurerm_resource_group.rg.name ip_configuration { name = "myNicConfiguration" subnet_id = azurerm_subnet.myterraformsubnet.id private_ip_address_allocation = "Dynamic" public_ip_address_id = azurerm_public_ip.myterraformpublicip.id } } # Connect the security group to the network interface resource "azurerm_network_interface_security_group_association" "example" { network_interface_id = azurerm_network_interface.myterraformnic.id network_security_group_id = azurerm_network_security_group.myterraformnsg.id } # Generate random text for a unique storage account name resource "random_id" "randomId" { keepers = { # Generate a new ID only when a new resource group is defined resource_group = azurerm_resource_group.rg.name } byte_length = 8 } # Create storage account for boot diagnostics resource "azurerm_storage_account" "mystorageaccount" { name = "diag${random_id.randomId.hex}" location = azurerm_resource_group.rg.location resource_group_name = azurerm_resource_group.rg.name account_tier = "Standard" account_replication_type = "LRS" } # Create (and display) an SSH key resource "tls_private_key" "example_ssh" { algorithm = "RSA" rsa_bits = 4096 } # Create virtual machine resource "azurerm_linux_virtual_machine" "myterraformvm" { name = "myVM" location = azurerm_resource_group.rg.location resource_group_name = azurerm_resource_group.rg.name network_interface_ids = [azurerm_network_interface.myterraformnic.id] size = "Standard_DS1_v2" os_disk { name = "myOsDisk" caching = "ReadWrite" storage_account_type = "Premium_LRS" } source_image_reference { publisher = "Canonical" offer = "UbuntuServer" sku = "18.04-LTS" version = "latest" } computer_name = "myvm" admin_username = "azureuser" disable_password_authentication = true admin_ssh_key { username = "azureuser" public_key = tls_private_key.example_ssh.public_key_openssh } boot_diagnostics { storage_account_uri = azurerm_storage_account.mystorageaccount.primary_blob_endpoint } }
Create a file named
variables.tf
and insert the following code:variable "resource_group_name_prefix" { default = "rg" description = "Prefix of the resource group name that's combined with a random ID so name is unique in your Azure subscription." } variable "resource_group_location" { default = "eastus" description = "Location of the resource group." }
Create a file named
output.tf
and insert the following code:output "resource_group_name" { value = azurerm_resource_group.rg.name } output "public_ip_address" { value = azurerm_linux_virtual_machine.myterraformvm.public_ip_address } output "tls_private_key" { value = tls_private_key.example_ssh.private_key_pem sensitive = true }
Initialize Terraform
Run terraform init to initialize the Terraform deployment. This command downloads the Azure modules required to manage your Azure resources.
terraform init
Create a Terraform execution plan
Run terraform plan to create an execution plan.
terraform plan -out main.tfplan
Key points:
- The
terraform plan
command creates an execution plan, but doesn't execute it. Instead, it determines what actions are necessary to create the configuration specified in your configuration files. This pattern allows you to verify whether the execution plan matches your expectations before making any changes to actual resources. - The optional
-out
parameter allows you to specify an output file for the plan. Using the-out
parameter ensures that the plan you reviewed is exactly what is applied. - To read more about persisting execution plans and security, see the security warning section.
Apply a Terraform execution plan
Run terraform apply to apply the execution plan to your cloud infrastructure.
terraform apply main.tfplan
Key points:
- The
terraform apply
command above assumes you previously ranterraform plan -out main.tfplan
. - If you specified a different filename for the
-out
parameter, use that same filename in the call toterraform apply
. - If you didn't use the
-out
parameter, simply callterraform apply
without any parameters.
Verify the results
To use SSH to connect to the virtual machine, do the following steps:
Run terraform output to get the SSH private key and save it to a file.
terraform output -raw tls_private_key > id_rsa
Run terraform output to get the virtual machine public IP address.
terraform output public_ip_address
Use SSH to connect to the virtual machine.
ssh -i id_rsa azureuser@<public_ip_address>
Troubleshoot Terraform on Azure
Troubleshoot common problems when using Terraform on Azure
Next steps
Feedback
Submit and view feedback for