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}
224
225resource "coder_agent" "main" {
226  arch = "amd64"
227  os   = "linux"
228  auth = "azure-instance-identity"
229
230  metadata {
231    key          = "cpu"
232    display_name = "CPU Usage"
233    interval     = 5
234    timeout      = 5
235    script       = <<-EOT
236      #!/bin/bash
237      set -e
238      top -bn1 | grep "Cpu(s)" | awk '{print $2 + $4 "%"}'
239    EOT
240  }
241  metadata {
242    key          = "memory"
243    display_name = "Memory Usage"
244    interval     = 5
245    timeout      = 5
246    script       = <<-EOT
247      #!/bin/bash
248      set -e
249      free -m | awk 'NR==2{printf "%.2f%%\t", $3*100/$2 }'
250    EOT
251  }
252  metadata {
253    key          = "disk"
254    display_name = "Disk Usage"
255    interval     = 600 # every 10 minutes
256    timeout      = 30  # df can take a while on large filesystems
257    script       = <<-EOT
258      #!/bin/bash
259      set -e
260      df /home/coder | awk '$NF=="/"{printf "%s", $5}'
261    EOT
262  }
263}
264
265locals {
266  prefix = "coder-${data.coder_workspace.me.owner}-${data.coder_workspace.me.name}"
267
268  userdata = templatefile("cloud-config.yaml.tftpl", {
269    username    = "coder" # Ensure this user/group does not exist in your VM image
270    init_script = base64encode(coder_agent.main.init_script)
271    hostname    = lower(data.coder_workspace.me.name)
272  })
273}
274
275resource "azurerm_resource_group" "main" {
276  name     = "${local.prefix}-resources"
277  location = data.coder_parameter.location.value
278
279  tags = {
280    Coder_Provisioned = "true"
281  }
282}
283
284// Uncomment here and in the azurerm_network_interface resource to obtain a public IP
285#resource "azurerm_public_ip" "main" {
286#  name                = "publicip"
287#  resource_group_name = azurerm_resource_group.main.name
288#  location            = azurerm_resource_group.main.location
289#  allocation_method   = "Static"
290#
291#  tags = {
292#    Coder_Provisioned = "true"
293#  }
294#}
295
296resource "azurerm_virtual_network" "main" {
297  name                = "network"
298  address_space       = ["10.0.0.0/24"]
299  location            = azurerm_resource_group.main.location
300  resource_group_name = azurerm_resource_group.main.name
301
302  tags = {
303    Coder_Provisioned = "true"
304  }
305}
306
307resource "azurerm_subnet" "internal" {
308  name                 = "internal"
309  resource_group_name  = azurerm_resource_group.main.name
310  virtual_network_name = azurerm_virtual_network.main.name
311  address_prefixes     = ["10.0.0.0/29"]
312}
313
314resource "azurerm_network_interface" "main" {
315  name                = "nic"
316  resource_group_name = azurerm_resource_group.main.name
317  location            = azurerm_resource_group.main.location
318
319  ip_configuration {
320    name                          = "internal"
321    subnet_id                     = azurerm_subnet.internal.id
322    private_ip_address_allocation = "Dynamic"
323    // Uncomment for public IP address as well as azurerm_public_ip resource above
324    //public_ip_address_id = azurerm_public_ip.main.id
325  }
326
327  tags = {
328    Coder_Provisioned = "true"
329  }
330}
331
332resource "azurerm_managed_disk" "home" {
333  create_option        = "Empty"
334  location             = azurerm_resource_group.main.location
335  name                 = "home"
336  resource_group_name  = azurerm_resource_group.main.name
337  storage_account_type = "StandardSSD_LRS"
338  disk_size_gb         = data.coder_parameter.home_size.value
339}
340
341// azurerm requires an SSH key (or password) for an admin user or it won't start a VM.  However,
342// cloud-init overwrites this anyway, so we'll just use a dummy SSH key.
343resource "tls_private_key" "dummy" {
344  algorithm = "RSA"
345  rsa_bits  = 4096
346}
347
348resource "azurerm_linux_virtual_machine" "main" {
349  count               = data.coder_workspace.me.transition == "start" ? 1 : 0
350  name                = "vm"
351  resource_group_name = azurerm_resource_group.main.name
352  location            = azurerm_resource_group.main.location
353  size                = data.coder_parameter.instance_type.value
354  // cloud-init overwrites this, so the value here doesn't matter
355  admin_username = "adminuser"
356  admin_ssh_key {
357    public_key = tls_private_key.dummy.public_key_openssh
358    username   = "adminuser"
359  }
360
361  network_interface_ids = [
362    azurerm_network_interface.main.id,
363  ]
364  computer_name = lower(data.coder_workspace.me.name)
365  os_disk {
366    caching              = "ReadWrite"
367    storage_account_type = "Standard_LRS"
368  }
369  source_image_reference {
370    publisher = "Canonical"
371    offer     = "0001-com-ubuntu-server-focal"
372    sku       = "20_04-lts-gen2"
373    version   = "latest"
374  }
375  user_data = base64encode(local.userdata)
376
377  tags = {
378    Coder_Provisioned = "true"
379  }
380}
381
382resource "azurerm_virtual_machine_data_disk_attachment" "home" {
383  count              = data.coder_workspace.me.transition == "start" ? 1 : 0
384  managed_disk_id    = azurerm_managed_disk.home.id
385  virtual_machine_id = azurerm_linux_virtual_machine.main[0].id
386  lun                = "10"
387  caching            = "ReadWrite"
388}
389
390resource "coder_metadata" "workspace_info" {
391  count       = data.coder_workspace.me.start_count
392  resource_id = azurerm_linux_virtual_machine.main[0].id
393
394  item {
395    key   = "type"
396    value = azurerm_linux_virtual_machine.main[0].size
397  }
398}
399
400resource "coder_metadata" "home_info" {
401  resource_id = azurerm_managed_disk.home.id
402
403  item {
404    key   = "size"
405    value = "${data.coder_parameter.home_size.value} GiB"
406  }
407}
408