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
12# See https://registry.coder.com/modules/azure-region
13module "azure_region" {
14  source = "registry.coder.com/modules/azure-region/coder"
15
16  # This ensures that the latest version of the module gets downloaded, you can also pin the module version to prevent breaking changes in production.
17  version = ">= 1.0.0"
18
19  default = "eastus"
20}
21
22data "coder_parameter" "instance_type" {
23  name         = "instance_type"
24  display_name = "Instance type"
25  description  = "What instance type should your workspace use?"
26  default      = "Standard_B4ms"
27  icon         = "/icon/azure.png"
28  mutable      = false
29  option {
30    name  = "Standard_B1ms (1 vCPU, 2 GiB RAM)"
31    value = "Standard_B1ms"
32  }
33  option {
34    name  = "Standard_B2ms (2 vCPU, 8 GiB RAM)"
35    value = "Standard_B2ms"
36  }
37  option {
38    name  = "Standard_B4ms (4 vCPU, 16 GiB RAM)"
39    value = "Standard_B4ms"
40  }
41  option {
42    name  = "Standard_B8ms (8 vCPU, 32 GiB RAM)"
43    value = "Standard_B8ms"
44  }
45  option {
46    name  = "Standard_B12ms (12 vCPU, 48 GiB RAM)"
47    value = "Standard_B12ms"
48  }
49  option {
50    name  = "Standard_B16ms (16 vCPU, 64 GiB RAM)"
51    value = "Standard_B16ms"
52  }
53  option {
54    name  = "Standard_D2as_v5 (2 vCPU, 8 GiB RAM)"
55    value = "Standard_D2as_v5"
56  }
57  option {
58    name  = "Standard_D4as_v5 (4 vCPU, 16 GiB RAM)"
59    value = "Standard_D4as_v5"
60  }
61  option {
62    name  = "Standard_D8as_v5 (8 vCPU, 32 GiB RAM)"
63    value = "Standard_D8as_v5"
64  }
65  option {
66    name  = "Standard_D16as_v5 (16 vCPU, 64 GiB RAM)"
67    value = "Standard_D16as_v5"
68  }
69  option {
70    name  = "Standard_D32as_v5 (32 vCPU, 128 GiB RAM)"
71    value = "Standard_D32as_v5"
72  }
73}
74
75data "coder_parameter" "home_size" {
76  name         = "home_size"
77  display_name = "Home volume size"
78  description  = "How large would you like your home volume to be (in GB)?"
79  default      = 20
80  type         = "number"
81  icon         = "/icon/azure.png"
82  mutable      = false
83  validation {
84    min = 1
85    max = 1024
86  }
87}
88
89provider "azurerm" {
90  features {}
91}
92
93data "coder_workspace" "me" {}
94data "coder_workspace_owner" "me" {}
95
96resource "coder_agent" "main" {
97  arch = "amd64"
98  os   = "linux"
99  auth = "azure-instance-identity"
100
101  metadata {
102    key          = "cpu"
103    display_name = "CPU Usage"
104    interval     = 5
105    timeout      = 5
106    script       = <<-EOT
107      #!/bin/bash
108      set -e
109      top -bn1 | grep "Cpu(s)" | awk '{print $2 + $4 "%"}'
110    EOT
111  }
112  metadata {
113    key          = "memory"
114    display_name = "Memory Usage"
115    interval     = 5
116    timeout      = 5
117    script       = <<-EOT
118      #!/bin/bash
119      set -e
120      free -m | awk 'NR==2{printf "%.2f%%\t", $3*100/$2 }'
121    EOT
122  }
123  metadata {
124    key          = "disk"
125    display_name = "Disk Usage"
126    interval     = 600 # every 10 minutes
127    timeout      = 30  # df can take a while on large filesystems
128    script       = <<-EOT
129      #!/bin/bash
130      set -e
131      df /home/coder | awk '$NF=="/"{printf "%s", $5}'
132    EOT
133  }
134}
135
136# See https://registry.coder.com/modules/code-server
137module "code-server" {
138  count  = data.coder_workspace.me.start_count
139  source = "registry.coder.com/modules/code-server/coder"
140
141  # This ensures that the latest version of the module gets downloaded, you can also pin the module version to prevent breaking changes in production.
142  version = ">= 1.0.0"
143
144  agent_id = coder_agent.main.id
145  order    = 1
146}
147
148# See https://registry.coder.com/modules/jetbrains-gateway
149module "jetbrains_gateway" {
150  count  = data.coder_workspace.me.start_count
151  source = "registry.coder.com/modules/jetbrains-gateway/coder"
152
153  # JetBrains IDEs to make available for the user to select
154  jetbrains_ides = ["IU", "PY", "WS", "PS", "RD", "CL", "GO", "RM"]
155  default        = "IU"
156
157  # Default folder to open when starting a JetBrains IDE
158  folder = "/home/coder"
159
160  # This ensures that the latest version of the module gets downloaded, you can also pin the module version to prevent breaking changes in production.
161  version = ">= 1.0.0"
162
163  agent_id   = coder_agent.main.id
164  agent_name = "main"
165  order      = 2
166}
167
168locals {
169  prefix = "coder-${data.coder_workspace_owner.me.name}-${data.coder_workspace.me.name}"
170
171  userdata = templatefile("cloud-config.yaml.tftpl", {
172    username    = "coder" # Ensure this user/group does not exist in your VM image
173    init_script = base64encode(coder_agent.main.init_script)
174    hostname    = lower(data.coder_workspace.me.name)
175  })
176}
177
178resource "azurerm_resource_group" "main" {
179  name     = "${local.prefix}-resources"
180  location = module.azure_region.value
181
182  tags = {
183    Coder_Provisioned = "true"
184  }
185}
186
187// Uncomment here and in the azurerm_network_interface resource to obtain a public IP
188#resource "azurerm_public_ip" "main" {
189#  name                = "publicip"
190#  resource_group_name = azurerm_resource_group.main.name
191#  location            = azurerm_resource_group.main.location
192#  allocation_method   = "Static"
193#
194#  tags = {
195#    Coder_Provisioned = "true"
196#  }
197#}
198
199resource "azurerm_virtual_network" "main" {
200  name                = "network"
201  address_space       = ["10.0.0.0/24"]
202  location            = azurerm_resource_group.main.location
203  resource_group_name = azurerm_resource_group.main.name
204
205  tags = {
206    Coder_Provisioned = "true"
207  }
208}
209
210resource "azurerm_subnet" "internal" {
211  name                 = "internal"
212  resource_group_name  = azurerm_resource_group.main.name
213  virtual_network_name = azurerm_virtual_network.main.name
214  address_prefixes     = ["10.0.0.0/29"]
215}
216
217resource "azurerm_network_interface" "main" {
218  name                = "nic"
219  resource_group_name = azurerm_resource_group.main.name
220  location            = azurerm_resource_group.main.location
221
222  ip_configuration {
223    name                          = "internal"
224    subnet_id                     = azurerm_subnet.internal.id
225    private_ip_address_allocation = "Dynamic"
226    // Uncomment for public IP address as well as azurerm_public_ip resource above
227    //public_ip_address_id = azurerm_public_ip.main.id
228  }
229
230  tags = {
231    Coder_Provisioned = "true"
232  }
233}
234
235resource "azurerm_managed_disk" "home" {
236  create_option        = "Empty"
237  location             = azurerm_resource_group.main.location
238  name                 = "home"
239  resource_group_name  = azurerm_resource_group.main.name
240  storage_account_type = "StandardSSD_LRS"
241  disk_size_gb         = data.coder_parameter.home_size.value
242}
243
244// azurerm requires an SSH key (or password) for an admin user or it won't start a VM.  However,
245// cloud-init overwrites this anyway, so we'll just use a dummy SSH key.
246resource "tls_private_key" "dummy" {
247  algorithm = "RSA"
248  rsa_bits  = 4096
249}
250
251resource "azurerm_linux_virtual_machine" "main" {
252  count               = data.coder_workspace.me.transition == "start" ? 1 : 0
253  name                = "vm"
254  resource_group_name = azurerm_resource_group.main.name
255  location            = azurerm_resource_group.main.location
256  size                = data.coder_parameter.instance_type.value
257  // cloud-init overwrites this, so the value here doesn't matter
258  admin_username = "adminuser"
259  admin_ssh_key {
260    public_key = tls_private_key.dummy.public_key_openssh
261    username   = "adminuser"
262  }
263
264  network_interface_ids = [
265    azurerm_network_interface.main.id,
266  ]
267  computer_name = lower(data.coder_workspace.me.name)
268  os_disk {
269    caching              = "ReadWrite"
270    storage_account_type = "Standard_LRS"
271  }
272  source_image_reference {
273    publisher = "Canonical"
274    offer     = "0001-com-ubuntu-server-focal"
275    sku       = "20_04-lts-gen2"
276    version   = "latest"
277  }
278  user_data = base64encode(local.userdata)
279
280  tags = {
281    Coder_Provisioned = "true"
282  }
283}
284
285resource "azurerm_virtual_machine_data_disk_attachment" "home" {
286  count              = data.coder_workspace.me.transition == "start" ? 1 : 0
287  managed_disk_id    = azurerm_managed_disk.home.id
288  virtual_machine_id = azurerm_linux_virtual_machine.main[0].id
289  lun                = "10"
290  caching            = "ReadWrite"
291}
292
293resource "coder_metadata" "workspace_info" {
294  count       = data.coder_workspace.me.start_count
295  resource_id = azurerm_linux_virtual_machine.main[0].id
296
297  item {
298    key   = "type"
299    value = azurerm_linux_virtual_machine.main[0].size
300  }
301}
302
303resource "coder_metadata" "home_info" {
304  resource_id = azurerm_managed_disk.home.id
305
306  item {
307    key   = "size"
308    value = "${data.coder_parameter.home_size.value} GiB"
309  }
310}
311