Skip to content

DNXLabs/terraform-azurerm-virtual-machine

terraform-azurerm-virtual-machine

Terraform module for creating and managing Azure Virtual Machines (Linux and Windows) with support for managed disks, public IPs, managed identities, and optional diagnostic settings.

This module supports both Linux and Windows VMs through a unified interface, with SSH key authentication for Linux and password authentication for both platforms.

Features

  • Dual OS Support: Create Linux or Windows VMs from a single module
  • SSH Key Authentication: SSH public key support for Linux VMs
  • Password Authentication: Secure password handling via separate variable
  • Public IP Management: Optional automatic public IP creation
  • Managed Disks: Multiple data disk attachments with configurable storage types
  • Managed Identity: SystemAssigned, UserAssigned, or combined identity support
  • Security Types: Standard, TrustedLaunch, or ConfidentialVM security configurations
  • Custom Images: Marketplace image references with publisher/offer/sku/version
  • OS Disk Configuration: Customizable caching, storage type, and disk size
  • Diagnostic Settings: Optional Azure Monitor integration
  • Resource Group Flexibility: Create new or use existing resource groups
  • Tagging Strategy: Built-in default tagging with custom tag support

Usage

Example 1 — Non-Prod (Linux VM with SSH Key)

A simple Linux VM for development with SSH key authentication.

module "linux_vm" {
  source = "./modules/vm"

  name = "mycompany-dev-aue-app"

  resource_group = {
    create   = false
    name     = "rg-mycompany-dev-aue-app-001"
    location = "australiaeast"
  }

  tags = {
    project     = "my-app"
    environment = "development"
  }

  network = {
    subnet_id        = "/subscriptions/xxxx/resourceGroups/rg-network/providers/Microsoft.Network/virtualNetworks/vnet-dev/subnets/snet-app"
    create_public_ip = false
  }

  vm = {
    os_type        = "linux"
    size           = "Standard_B2s"
    admin_username = "azureadmin"

    disable_password_authentication = true
    ssh_public_key                  = file("~/.ssh/id_rsa.pub")

    image = {
      publisher = "Canonical"
      offer     = "ubuntu-24_04-lts"
      sku       = "server"
      version   = "latest"
    }

    os_disk = {
      storage_account_type = "Standard_LRS"
      disk_size_gb         = 30
    }
  }
}

Example 2 — Production (Windows VM with Data Disks and Identity)

A production Windows VM with managed identity, data disks, and diagnostics.

module "windows_vm" {
  source = "./modules/vm"

  name = "contoso-prod-aue-web"

  resource_group = {
    create   = false
    name     = "rg-contoso-prod-aue-web-001"
    location = "australiaeast"
  }

  tags = {
    project     = "web-platform"
    environment = "production"
    compliance  = "soc2"
  }

  network = {
    subnet_id                     = "/subscriptions/xxxx/resourceGroups/rg-network/providers/Microsoft.Network/virtualNetworks/vnet-prod/subnets/snet-web"
    private_ip_address_allocation = "Static"
    private_ip_address            = "10.0.1.10"
    create_public_ip              = false
  }

  vm = {
    os_type        = "windows"
    size           = "Standard_D4s_v5"
    admin_username = "winadmin"

    image = {
      publisher = "MicrosoftWindowsServer"
      offer     = "WindowsServer"
      sku       = "2022-datacenter-g2"
      version   = "latest"
    }

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

    identity = {
      type = "SystemAssigned"
    }

    data_disks = [
      {
        lun                  = 0
        size_gb              = 256
        storage_account_type = "Premium_LRS"
        caching              = "ReadOnly"
      },
      {
        lun                  = 1
        size_gb              = 512
        storage_account_type = "Premium_LRS"
        caching              = "None"
      }
    ]
  }

  admin_password = module.vm_password.value

  diagnostics = {
    enabled                    = true
    log_analytics_workspace_id = "/subscriptions/xxxx/resourceGroups/rg-monitor/providers/Microsoft.OperationalInsights/workspaces/law-prod"
  }
}

Using YAML Variables

Create a vars/identity.yaml file:

azure:
  subscription_id: "afb35bd4-145f-4a15-889e-5da052d030ce"
  location: australiaeast

network_lookup:
  resource_group_name: "rg-managed-services-lab-aue-stg-001"
  vnet_name: "vnet-managed-services-lab-aue-stg-001"

identity:
  vms:
    app-server:
      naming:
        org: managed-services
        env: lab
        region: aue
        workload: stg

      resource_group:
        create: false
        name: rg-managed-services-lab-aue-stg-001
        location: australiaeast

      subnet_name: snet-stg-app

      vm:
        os_type: linux
        size: Standard_B2s
        admin_username: azureadmin
        disable_password_authentication: false

        image:
          publisher: Canonical
          offer: ubuntu-24_04-lts
          sku: server
          version: latest

        os_disk:
          storage_account_type: Standard_LRS
          disk_size_gb: 30

        identity:
          type: SystemAssigned

      password:
        name: vm-app-server-password
        type: password
        length: 24

Then use in your Terraform:

locals {
  workspace = yamldecode(file("vars/${terraform.workspace}.yaml"))
}

data "azurerm_subnet" "vm" {
  for_each = try(local.workspace.identity.vms, {})

  name                 = each.value.subnet_name
  virtual_network_name = local.workspace.network_lookup.vnet_name
  resource_group_name  = local.workspace.network_lookup.resource_group_name
}

module "vm_passwords" {
  for_each = { for k, v in try(local.workspace.identity.vms, {}) : k => v if try(v.password, null) != null }

  source = "./modules/password"

  name                    = each.value.password.name
  key_vault_name          = module.keyvault["main"].key_vault.name
  key_vault_resource_group = module.keyvault["main"].resource_group_name

  type   = try(each.value.password.type, "password")
  length = try(each.value.password.length, 32)
}

module "vm" {
  for_each = try(local.workspace.identity.vms, {})

  source = "./modules/vm"

  name           = "${each.value.naming.org}-${each.value.naming.env}-${each.value.naming.region}-${each.value.naming.workload}"
  resource_group = each.value.resource_group
  tags           = try(each.value.tags, {})

  network = {
    subnet_id = data.azurerm_subnet.vm[each.key].id
  }

  vm             = each.value.vm
  admin_password = try(module.vm_passwords[each.key].value, null)
  vm_ssh_key     = try(module.vm_ssh_keys[each.key].public_key, null)
  diagnostics    = try(each.value.diagnostics, {})
}

VM Images

Common Linux Images

Distribution Publisher Offer SKU
Ubuntu 24.04 Canonical ubuntu-24_04-lts server
Ubuntu 22.04 Canonical 0001-com-ubuntu-server-jammy 22_04-lts-gen2
RHEL 9 RedHat RHEL 9-lvm-gen2
Debian 12 Debian debian-12 12-gen2

Common Windows Images

Version Publisher Offer SKU
Server 2022 MicrosoftWindowsServer WindowsServer 2022-datacenter-g2
Server 2019 MicrosoftWindowsServer WindowsServer 2019-Datacenter

Naming Convention

Resources are named using the prefix pattern: {name}

Example:

  • Linux VM: vm-{name}-001
  • Windows VM: vm-{name}-001
  • NIC: nic-vm-{name}-001
  • Public IP: pip-vm-{name}-001
  • OS Disk: osdisk-vm-{name}-001
  • Data Disk: disk-vm-{name}-lun{N}-001

Outputs

Name Description
resource_group_name Resource Group where the VM is deployed
vm VM object with id, name, os, private/public IPs, identity, data disks
resource Generic resource output (id, name, type)
nic NIC output with id, name, ip_configuration_name (for LB associations)

Requirements

Name Version
terraform >= 1.6.0
azurerm >= 4.0.0

Providers

Name Version
azurerm >= 4.0.0

Inputs

Name Description Type Required
name Resource name prefix for all resources string yes
resource_group Resource group configuration object yes
network Network settings for the VM NIC object yes
vm VM configuration (OS type, size, image, disks) object yes
tags Extra tags merged with default tags map(string) no
admin_password VM admin password (sensitive) string no
vm_ssh_key SSH public key override string no
diagnostics Azure Monitor diagnostic settings object no

Detailed Input Specifications

vm

object({
  os_type = string  # linux | windows

  name        = optional(string)
  name_suffix = optional(string, "001")

  size           = string   # e.g., Standard_B2s, Standard_D4s_v5
  admin_username = string

  disable_password_authentication = optional(bool, true)  # Linux only
  ssh_public_key                  = optional(string)      # Linux only

  computer_name = optional(string)

  image = object({
    publisher = string
    offer     = string
    sku       = string
    version   = optional(string, "latest")
  })

  os_disk = optional(object({
    name                 = optional(string)
    caching              = optional(string, "ReadWrite")
    storage_account_type = optional(string, "Standard_LRS")
    disk_size_gb         = optional(number)
  }), {})

  identity = optional(object({
    type         = optional(string, "SystemAssigned")
    identity_ids = optional(list(string), [])
  }), {})

  security_type       = optional(string)
  secure_boot_enabled = optional(bool, false)
  vtpm_enabled        = optional(bool, false)

  data_disks = optional(list(object({
    lun                  = number
    size_gb              = number
    storage_account_type = optional(string, "Standard_LRS")
    caching              = optional(string, "ReadOnly")
    create_option        = optional(string, "Empty")
    name                 = optional(string)
  })), [])
})

network

object({
  subnet_id                     = string
  private_ip_address_allocation = optional(string, "Dynamic")
  private_ip_address            = optional(string)
  create_public_ip              = optional(bool, false)
  public_ip_sku                 = optional(string, "Standard")
  public_ip_allocation_method   = optional(string, "Static")
})

License

Apache 2.0 Licensed. See LICENSE for full details.

Authors

Module managed by DNX Solutions.

Contributing

Please read CONTRIBUTING.md for details on our code of conduct and the process for submitting pull requests.

About

Azure Virtual machines

Resources

License

Code of conduct

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages