TemplatesModules
Back to Templates
Azure VM (Windows) Icon

Azure VM (Windows)

By:
Provision Azure VMs as Coder workspaces
Source
README
Resources (11)

Copy and paste the following into main.tf and run coder template push:

1terraform {
2  required_providers {
3    coder = {
4      source = "coder/coder"
5    }
6    azurerm = {
7      source = "hashicorp/azurerm"
8    }
9  }
10}
11
12provider "azurerm" {
13  features {}
14}
15
16provider "coder" {
17}
18
19data "coder_workspace" "me" {}
20
21data "coder_parameter" "location" {
22  description  = "What location should your workspace live in?"
23  display_name = "Location"
24  name         = "location"
25  default      = "eastus"
26  mutable      = false
27  option {
28    value = "eastus"
29    name  = "East US"
30  }
31  option {
32    value = "centralus"
33    name  = "Central US"
34  }
35  option {
36    value = "southcentralus"
37    name  = "South Central US"
38  }
39  option {
40    value = "westus2"
41    name  = "West US 2"
42  }
43}
44
45data "coder_parameter" "data_disk_size" {
46  description  = "Size of your data (F:) drive in GB"
47  display_name = "Data disk size"
48  name         = "data_disk_size"
49  default      = 20
50  mutable      = "false"
51  type         = "number"
52  validation {
53    min = 5
54    max = 5000
55  }
56}
57
58resource "coder_agent" "main" {
59  arch = "amd64"
60  auth = "azure-instance-identity"
61  os   = "windows"
62}
63
64resource "random_password" "admin_password" {
65  length  = 16
66  special = true
67  # https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/password-must-meet-complexity-requirements#reference
68  # we remove characters that require special handling in XML, as this is how we pass it to the VM
69  # namely: <>&'"
70  override_special = "~!@#$%^*_-+=`|\\(){}[]:;,.?/"
71}
72
73locals {
74  prefix         = "coder-win"
75  admin_username = "coder"
76}
77
78resource "azurerm_resource_group" "main" {
79  name     = "${local.prefix}-${data.coder_workspace.me.id}"
80  location = data.coder_parameter.location.value
81  tags = {
82    Coder_Provisioned = "true"
83  }
84}
85
86// Uncomment here and in the azurerm_network_interface resource to obtain a public IP
87#resource "azurerm_public_ip" "main" {
88#  name                = "publicip"
89#  resource_group_name = azurerm_resource_group.main.name
90#  location            = azurerm_resource_group.main.location
91#  allocation_method   = "Static"
92#  tags = {
93#    Coder_Provisioned = "true"
94#  }
95#}
96resource "azurerm_virtual_network" "main" {
97  name                = "network"
98  address_space       = ["10.0.0.0/24"]
99  location            = azurerm_resource_group.main.location
100  resource_group_name = azurerm_resource_group.main.name
101  tags = {
102    Coder_Provisioned = "true"
103  }
104}
105resource "azurerm_subnet" "internal" {
106  name                 = "internal"
107  resource_group_name  = azurerm_resource_group.main.name
108  virtual_network_name = azurerm_virtual_network.main.name
109  address_prefixes     = ["10.0.0.0/29"]
110}
111resource "azurerm_network_interface" "main" {
112  name                = "nic"
113  resource_group_name = azurerm_resource_group.main.name
114  location            = azurerm_resource_group.main.location
115  ip_configuration {
116    name                          = "internal"
117    subnet_id                     = azurerm_subnet.internal.id
118    private_ip_address_allocation = "Dynamic"
119    // Uncomment for public IP address as well as azurerm_public_ip resource above
120    #    public_ip_address_id = azurerm_public_ip.main.id
121  }
122  tags = {
123    Coder_Provisioned = "true"
124  }
125}
126# Create storage account for boot diagnostics
127resource "azurerm_storage_account" "my_storage_account" {
128  name                     = "diag${random_id.storage_id.hex}"
129  location                 = azurerm_resource_group.main.location
130  resource_group_name      = azurerm_resource_group.main.name
131  account_tier             = "Standard"
132  account_replication_type = "LRS"
133}
134# Generate random text for a unique storage account name
135resource "random_id" "storage_id" {
136  keepers = {
137    # Generate a new ID only when a new resource group is defined
138    resource_group = azurerm_resource_group.main.name
139  }
140  byte_length = 8
141}
142
143resource "azurerm_managed_disk" "data" {
144  name                 = "data_disk"
145  location             = azurerm_resource_group.main.location
146  resource_group_name  = azurerm_resource_group.main.name
147  storage_account_type = "Standard_LRS"
148  create_option        = "Empty"
149  disk_size_gb         = data.coder_parameter.data_disk_size.value
150}
151
152# Create virtual machine
153resource "azurerm_windows_virtual_machine" "main" {
154  name                  = "vm"
155  admin_username        = local.admin_username
156  admin_password        = random_password.admin_password.result
157  location              = azurerm_resource_group.main.location
158  resource_group_name   = azurerm_resource_group.main.name
159  network_interface_ids = [azurerm_network_interface.main.id]
160  size                  = "Standard_DS1_v2"
161  custom_data = base64encode(
162    templatefile("${path.module}/Initialize.ps1.tftpl", { init_script = coder_agent.main.init_script })
163  )
164  os_disk {
165    name                 = "myOsDisk"
166    caching              = "ReadWrite"
167    storage_account_type = "Premium_LRS"
168  }
169  source_image_reference {
170    publisher = "MicrosoftWindowsServer"
171    offer     = "WindowsServer"
172    sku       = "2022-datacenter-azure-edition"
173    version   = "latest"
174  }
175  additional_unattend_content {
176    content = "<AutoLogon><Password><Value>${random_password.admin_password.result}</Value></Password><Enabled>true</Enabled><LogonCount>1</LogonCount><Username>${local.admin_username}</Username></AutoLogon>"
177    setting = "AutoLogon"
178  }
179  additional_unattend_content {
180    content = file("${path.module}/FirstLogonCommands.xml")
181    setting = "FirstLogonCommands"
182  }
183  boot_diagnostics {
184    storage_account_uri = azurerm_storage_account.my_storage_account.primary_blob_endpoint
185  }
186  tags = {
187    Coder_Provisioned = "true"
188  }
189}
190
191resource "coder_metadata" "rdp_login" {
192  resource_id = azurerm_windows_virtual_machine.main.id
193  item {
194    key   = "Username"
195    value = local.admin_username
196  }
197  item {
198    key       = "Password"
199    value     = random_password.admin_password.result
200    sensitive = true
201  }
202}
203
204resource "azurerm_virtual_machine_data_disk_attachment" "main_data" {
205  managed_disk_id    = azurerm_managed_disk.data.id
206  virtual_machine_id = azurerm_windows_virtual_machine.main.id
207  lun                = "10"
208  caching            = "ReadWrite"
209}
210
211# Stop the VM
212resource "null_resource" "stop_vm" {
213  count      = data.coder_workspace.me.transition == "stop" ? 1 : 0
214  depends_on = [azurerm_windows_virtual_machine.main]
215  provisioner "local-exec" {
216    # Use deallocate so the VM is not charged
217    command = "az vm deallocate --ids ${azurerm_windows_virtual_machine.main.id}"
218  }
219}
220
221# Start the VM
222resource "null_resource" "start" {
223  count      = data.coder_workspace.me.transition == "start" ? 1 : 0
224  depends_on = [azurerm_windows_virtual_machine.main]
225  provisioner "local-exec" {
226    command = "az vm start --ids ${azurerm_windows_virtual_machine.main.id}"
227  }
228}
229