TemplatesModules
Back to Templates
Azure VM (Linux) Icon

Azure VM (Linux)

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

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
12data "coder_parameter" "location" {
13  name         = "location"
14  display_name = "Location"
15  description  = "What location should your workspace live in?"
16  default      = "eastus"
17  icon         = "/emojis/1f310.png"
18  mutable      = false
19  option {
20    name  = "US (Virginia)"
21    value = "eastus"
22    icon  = "/emojis/1f1fa-1f1f8.png"
23  }
24  option {
25    name  = "US (Virginia) 2"
26    value = "eastus2"
27    icon  = "/emojis/1f1fa-1f1f8.png"
28  }
29  option {
30    name  = "US (Texas)"
31    value = "southcentralus"
32    icon  = "/emojis/1f1fa-1f1f8.png"
33  }
34  option {
35    name  = "US (Washington)"
36    value = "westus2"
37    icon  = "/emojis/1f1fa-1f1f8.png"
38  }
39  option {
40    name  = "US (Arizona)"
41    value = "westus3"
42    icon  = "/emojis/1f1fa-1f1f8.png"
43  }
44  option {
45    name  = "US (Iowa)"
46    value = "centralus"
47    icon  = "/emojis/1f1fa-1f1f8.png"
48  }
49  option {
50    name  = "Canada (Toronto)"
51    value = "canadacentral"
52    icon  = "/emojis/1f1e8-1f1e6.png"
53  }
54  option {
55    name  = "Brazil (Sao Paulo)"
56    value = "brazilsouth"
57    icon  = "/emojis/1f1e7-1f1f7.png"
58  }
59  option {
60    name  = "East Asia (Hong Kong)"
61    value = "eastasia"
62    icon  = "/emojis/1f1f0-1f1f7.png"
63  }
64  option {
65    name  = "Southeast Asia (Singapore)"
66    value = "southeastasia"
67    icon  = "/emojis/1f1f0-1f1f7.png"
68  }
69  option {
70    name  = "Australia (New South Wales)"
71    value = "australiaeast"
72    icon  = "/emojis/1f1e6-1f1fa.png"
73  }
74  option {
75    name  = "China (Hebei)"
76    value = "chinanorth3"
77    icon  = "/emojis/1f1e8-1f1f3.png"
78  }
79  option {
80    name  = "India (Pune)"
81    value = "centralindia"
82    icon  = "/emojis/1f1ee-1f1f3.png"
83  }
84  option {
85    name  = "Japan (Tokyo)"
86    value = "japaneast"
87    icon  = "/emojis/1f1ef-1f1f5.png"
88  }
89  option {
90    name  = "Korea (Seoul)"
91    value = "koreacentral"
92    icon  = "/emojis/1f1f0-1f1f7.png"
93  }
94  option {
95    name  = "Europe (Ireland)"
96    value = "northeurope"
97    icon  = "/emojis/1f1ea-1f1fa.png"
98  }
99  option {
100    name  = "Europe (Netherlands)"
101    value = "westeurope"
102    icon  = "/emojis/1f1ea-1f1fa.png"
103  }
104  option {
105    name  = "France (Paris)"
106    value = "francecentral"
107    icon  = "/emojis/1f1eb-1f1f7.png"
108  }
109  option {
110    name  = "Germany (Frankfurt)"
111    value = "germanywestcentral"
112    icon  = "/emojis/1f1e9-1f1ea.png"
113  }
114  option {
115    name  = "Norway (Oslo)"
116    value = "norwayeast"
117    icon  = "/emojis/1f1f3-1f1f4.png"
118  }
119  option {
120    name  = "Sweden (Gävle)"
121    value = "swedencentral"
122    icon  = "/emojis/1f1f8-1f1ea.png"
123  }
124  option {
125    name  = "Switzerland (Zurich)"
126    value = "switzerlandnorth"
127    icon  = "/emojis/1f1e8-1f1ed.png"
128  }
129  option {
130    name  = "Qatar (Doha)"
131    value = "qatarcentral"
132    icon  = "/emojis/1f1f6-1f1e6.png"
133  }
134  option {
135    name  = "UAE (Dubai)"
136    value = "uaenorth"
137    icon  = "/emojis/1f1e6-1f1ea.png"
138  }
139  option {
140    name  = "South Africa (Johannesburg)"
141    value = "southafricanorth"
142    icon  = "/emojis/1f1ff-1f1e6.png"
143  }
144  option {
145    name  = "UK (London)"
146    value = "uksouth"
147    icon  = "/emojis/1f1ec-1f1e7.png"
148  }
149}
150
151data "coder_parameter" "instance_type" {
152  name         = "instance_type"
153  display_name = "Instance type"
154  description  = "What instance type should your workspace use?"
155  default      = "Standard_B4ms"
156  icon         = "/icon/azure.png"
157  mutable      = false
158  option {
159    name  = "Standard_B1ms (1 vCPU, 2 GiB RAM)"
160    value = "Standard_B1ms"
161  }
162  option {
163    name  = "Standard_B2ms (2 vCPU, 8 GiB RAM)"
164    value = "Standard_B2ms"
165  }
166  option {
167    name  = "Standard_B4ms (4 vCPU, 16 GiB RAM)"
168    value = "Standard_B4ms"
169  }
170  option {
171    name  = "Standard_B8ms (8 vCPU, 32 GiB RAM)"
172    value = "Standard_B8ms"
173  }
174  option {
175    name  = "Standard_B12ms (12 vCPU, 48 GiB RAM)"
176    value = "Standard_B12ms"
177  }
178  option {
179    name  = "Standard_B16ms (16 vCPU, 64 GiB RAM)"
180    value = "Standard_B16ms"
181  }
182  option {
183    name  = "Standard_D2as_v5 (2 vCPU, 8 GiB RAM)"
184    value = "Standard_D2as_v5"
185  }
186  option {
187    name  = "Standard_D4as_v5 (4 vCPU, 16 GiB RAM)"
188    value = "Standard_D4as_v5"
189  }
190  option {
191    name  = "Standard_D8as_v5 (8 vCPU, 32 GiB RAM)"
192    value = "Standard_D8as_v5"
193  }
194  option {
195    name  = "Standard_D16as_v5 (16 vCPU, 64 GiB RAM)"
196    value = "Standard_D16as_v5"
197  }
198  option {
199    name  = "Standard_D32as_v5 (32 vCPU, 128 GiB RAM)"
200    value = "Standard_D32as_v5"
201  }
202}
203
204data "coder_parameter" "home_size" {
205  name         = "home_size"
206  display_name = "Home volume size"
207  description  = "How large would you like your home volume to be (in GB)?"
208  default      = 20
209  type         = "number"
210  icon         = "/icon/azure.png"
211  mutable      = false
212  validation {
213    min = 1
214    max = 1024
215  }
216}
217
218provider "azurerm" {
219  features {}
220}
221
222data "coder_workspace" "me" {
223}
224data "coder_workspace_owner" "me" {}
225
226resource "coder_agent" "main" {
227  arch = "amd64"
228  os   = "linux"
229  auth = "azure-instance-identity"
230
231  metadata {
232    key          = "cpu"
233    display_name = "CPU Usage"
234    interval     = 5
235    timeout      = 5
236    script       = <<-EOT
237      #!/bin/bash
238      set -e
239      top -bn1 | grep "Cpu(s)" | awk '{print $2 + $4 "%"}'
240    EOT
241  }
242  metadata {
243    key          = "memory"
244    display_name = "Memory Usage"
245    interval     = 5
246    timeout      = 5
247    script       = <<-EOT
248      #!/bin/bash
249      set -e
250      free -m | awk 'NR==2{printf "%.2f%%\t", $3*100/$2 }'
251    EOT
252  }
253  metadata {
254    key          = "disk"
255    display_name = "Disk Usage"
256    interval     = 600 # every 10 minutes
257    timeout      = 30  # df can take a while on large filesystems
258    script       = <<-EOT
259      #!/bin/bash
260      set -e
261      df /home/coder | awk '$NF=="/"{printf "%s", $5}'
262    EOT
263  }
264}
265
266locals {
267  prefix = "coder-${data.coder_workspace_owner.me.name}-${data.coder_workspace.me.name}"
268
269  userdata = templatefile("cloud-config.yaml.tftpl", {
270    username    = "coder" # Ensure this user/group does not exist in your VM image
271    init_script = base64encode(coder_agent.main.init_script)
272    hostname    = lower(data.coder_workspace.me.name)
273  })
274}
275
276resource "azurerm_resource_group" "main" {
277  name     = "${local.prefix}-resources"
278  location = data.coder_parameter.location.value
279
280  tags = {
281    Coder_Provisioned = "true"
282  }
283}
284
285// Uncomment here and in the azurerm_network_interface resource to obtain a public IP
286#resource "azurerm_public_ip" "main" {
287#  name                = "publicip"
288#  resource_group_name = azurerm_resource_group.main.name
289#  location            = azurerm_resource_group.main.location
290#  allocation_method   = "Static"
291#
292#  tags = {
293#    Coder_Provisioned = "true"
294#  }
295#}
296
297resource "azurerm_virtual_network" "main" {
298  name                = "network"
299  address_space       = ["10.0.0.0/24"]
300  location            = azurerm_resource_group.main.location
301  resource_group_name = azurerm_resource_group.main.name
302
303  tags = {
304    Coder_Provisioned = "true"
305  }
306}
307
308resource "azurerm_subnet" "internal" {
309  name                 = "internal"
310  resource_group_name  = azurerm_resource_group.main.name
311  virtual_network_name = azurerm_virtual_network.main.name
312  address_prefixes     = ["10.0.0.0/29"]
313}
314
315resource "azurerm_network_interface" "main" {
316  name                = "nic"
317  resource_group_name = azurerm_resource_group.main.name
318  location            = azurerm_resource_group.main.location
319
320  ip_configuration {
321    name                          = "internal"
322    subnet_id                     = azurerm_subnet.internal.id
323    private_ip_address_allocation = "Dynamic"
324    // Uncomment for public IP address as well as azurerm_public_ip resource above
325    //public_ip_address_id = azurerm_public_ip.main.id
326  }
327
328  tags = {
329    Coder_Provisioned = "true"
330  }
331}
332
333resource "azurerm_managed_disk" "home" {
334  create_option        = "Empty"
335  location             = azurerm_resource_group.main.location
336  name                 = "home"
337  resource_group_name  = azurerm_resource_group.main.name
338  storage_account_type = "StandardSSD_LRS"
339  disk_size_gb         = data.coder_parameter.home_size.value
340}
341
342// azurerm requires an SSH key (or password) for an admin user or it won't start a VM.  However,
343// cloud-init overwrites this anyway, so we'll just use a dummy SSH key.
344resource "tls_private_key" "dummy" {
345  algorithm = "RSA"
346  rsa_bits  = 4096
347}
348
349resource "azurerm_linux_virtual_machine" "main" {
350  count               = data.coder_workspace.me.transition == "start" ? 1 : 0
351  name                = "vm"
352  resource_group_name = azurerm_resource_group.main.name
353  location            = azurerm_resource_group.main.location
354  size                = data.coder_parameter.instance_type.value
355  // cloud-init overwrites this, so the value here doesn't matter
356  admin_username = "adminuser"
357  admin_ssh_key {
358    public_key = tls_private_key.dummy.public_key_openssh
359    username   = "adminuser"
360  }
361
362  network_interface_ids = [
363    azurerm_network_interface.main.id,
364  ]
365  computer_name = lower(data.coder_workspace.me.name)
366  os_disk {
367    caching              = "ReadWrite"
368    storage_account_type = "Standard_LRS"
369  }
370  source_image_reference {
371    publisher = "Canonical"
372    offer     = "0001-com-ubuntu-server-focal"
373    sku       = "20_04-lts-gen2"
374    version   = "latest"
375  }
376  user_data = base64encode(local.userdata)
377
378  tags = {
379    Coder_Provisioned = "true"
380  }
381}
382
383resource "azurerm_virtual_machine_data_disk_attachment" "home" {
384  count              = data.coder_workspace.me.transition == "start" ? 1 : 0
385  managed_disk_id    = azurerm_managed_disk.home.id
386  virtual_machine_id = azurerm_linux_virtual_machine.main[0].id
387  lun                = "10"
388  caching            = "ReadWrite"
389}
390
391resource "coder_metadata" "workspace_info" {
392  count       = data.coder_workspace.me.start_count
393  resource_id = azurerm_linux_virtual_machine.main[0].id
394
395  item {
396    key   = "type"
397    value = azurerm_linux_virtual_machine.main[0].size
398  }
399}
400
401resource "coder_metadata" "home_info" {
402  resource_id = azurerm_managed_disk.home.id
403
404  item {
405    key   = "size"
406    value = "${data.coder_parameter.home_size.value} GiB"
407  }
408}
409