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