Skip to content

Commit e1edc61

Browse files
committed
azure-linux example template
Signed-off-by: Spike Curtis <spike@coder.com>
1 parent 74c8766 commit e1edc61

File tree

3 files changed

+302
-0
lines changed

3 files changed

+302
-0
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
name: Develop in Linux on Azure
3+
description: Get started with Linux development on Microsoft Azure.
4+
tags: [cloud, azure, linux]
5+
---
6+
7+
# azure-linux
8+
9+
To get started, run `coder templates init`. When prompted, select this template.
10+
Follow the on-screen instructions to proceed.
11+
12+
## Authentication
13+
14+
This template assumes that coderd is run in an environment that is authenticated
15+
with Azure. For example, run `az login` then `az account set --subscription=<id>`
16+
to import credentials on the system and user running coderd. For other ways to
17+
authenticate [consult the Terraform docs](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs#authenticating-to-azure).
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#cloud-config
2+
cloud_final_modules:
3+
- [scripts-user, always]
4+
device_aliases:
5+
# this is a little fragile because the data volume isn't always sdc when the workspace is stopped
6+
# and started. But, it appears that it is always sdc when the workspace is *first* created.
7+
# This allows us to format and label the volume, and then we rely on the label to mount on
8+
# subsequent workspace builds.
9+
homedir: /dev/sdc
10+
disk_setup:
11+
homedir:
12+
table_type: gpt
13+
layout: true
14+
fs_setup:
15+
- label: coder_home
16+
filesystem: ext4
17+
device: homedir.1
18+
mounts:
19+
- ["LABEL=coder_home", "/home/${username}"]
20+
hostname: ${hostname}
21+
users:
22+
- name: ${username}
23+
sudo: ["ALL=(ALL) NOPASSWD:ALL"]
24+
groups: sudo
25+
shell: /bin/bash
26+
packages:
27+
- git
28+
write_files:
29+
- path: /opt/coder/init
30+
permissions: "0755"
31+
encoding: b64
32+
content: ${init_script}
33+
- path: /etc/systemd/system/coder-agent.service
34+
permissions: "0644"
35+
content: |
36+
[Unit]
37+
Description=Coder Agent
38+
After=network-online.target
39+
Wants=network-online.target
40+
41+
[Service]
42+
User=${username}
43+
ExecStart=/opt/coder/init
44+
Environment=CODER_AGENT_TOKEN=${coder_agent_token}
45+
Restart=always
46+
RestartSec=10
47+
TimeoutStopSec=90
48+
KillMode=process
49+
50+
OOMScoreAdjust=-900
51+
SyslogIdentifier=coder-agent
52+
53+
[Install]
54+
WantedBy=multi-user.target
55+
runcmd:
56+
- chown ${username}:${username} /home/${username}
57+
- systemctl enable coder-agent
58+
- systemctl start coder-agent
Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
terraform {
2+
required_providers {
3+
coder = {
4+
source = "coder/coder"
5+
version = "0.4.3"
6+
}
7+
azurerm = {
8+
source = "hashicorp/azurerm"
9+
version = "=3.0.0"
10+
}
11+
}
12+
}
13+
14+
variable "location" {
15+
description = "What location should your workspace live in?"
16+
default = "eastus"
17+
validation {
18+
condition = contains([
19+
"eastus",
20+
"southcentralus",
21+
"westus2",
22+
"australiaeast",
23+
"southeastasia",
24+
"northeurope",
25+
"westeurope",
26+
"centralindia",
27+
"eastasia",
28+
"japaneast",
29+
"brazilsouth",
30+
"asia",
31+
"asiapacific",
32+
"australia",
33+
"brazil",
34+
"india",
35+
"japan",
36+
"southafrica",
37+
"switzerland",
38+
"uae",
39+
], var.location)
40+
error_message = "Invalid location!"
41+
}
42+
}
43+
44+
variable "instance_type" {
45+
description = "What instance type should your workspace use?"
46+
default = "Standard_B4ms"
47+
validation {
48+
condition = contains([
49+
"Standard_B1ms",
50+
"Standard_B2ms",
51+
"Standard_B4ms",
52+
"Standard_B8ms",
53+
"Standard_B12ms",
54+
"Standard_B16ms",
55+
"Standard_D2as_v5",
56+
"Standard_D4as_v5",
57+
"Standard_D8as_v5",
58+
"Standard_D16as_v5",
59+
"Standard_D32as_v5",
60+
], var.instance_type)
61+
error_message = "Invalid instance type!"
62+
}
63+
}
64+
65+
variable "home_size" {
66+
type = number
67+
description = "How large would you like your home volume to be (in GB)?"
68+
default = 20
69+
validation {
70+
condition = var.home_size >= 1
71+
error_message = "Value must be greater than or equal to 1."
72+
}
73+
}
74+
75+
provider "azurerm" {
76+
features {}
77+
}
78+
79+
data "coder_workspace" "me" {
80+
}
81+
82+
resource "coder_agent" "main" {
83+
arch = "amd64"
84+
os = "linux"
85+
}
86+
87+
locals {
88+
prefix = "coder-${data.coder_workspace.me.owner}-${data.coder_workspace.me.name}"
89+
90+
userdata = templatefile("cloud-config.yaml.tftpl", {
91+
username = lower(substr(data.coder_workspace.me.owner, 0, 32))
92+
init_script = base64encode(coder_agent.main.init_script)
93+
coder_agent_token = coder_agent.main.token
94+
hostname = lower(data.coder_workspace.me.name)
95+
})
96+
}
97+
98+
resource "azurerm_resource_group" "main" {
99+
name = "${local.prefix}-resources"
100+
location = var.location
101+
102+
tags = {
103+
Name = "coder-${data.coder_workspace.me.owner}-${data.coder_workspace.me.name}"
104+
Coder_Provisioned = "true"
105+
}
106+
}
107+
108+
// Uncomment here and in the azurerm_network_interface resource to obtain a public IP
109+
#resource "azurerm_public_ip" "main" {
110+
# name = "publicip"
111+
# resource_group_name = azurerm_resource_group.main.name
112+
# location = azurerm_resource_group.main.location
113+
# allocation_method = "Static"
114+
#
115+
# tags = {
116+
# Name = "coder-${data.coder_workspace.me.owner}-${data.coder_workspace.me.name}"
117+
# Coder_Provisioned = "true"
118+
# }
119+
#}
120+
121+
resource "azurerm_virtual_network" "main" {
122+
name = "network"
123+
address_space = ["10.0.0.0/24"]
124+
location = azurerm_resource_group.main.location
125+
resource_group_name = azurerm_resource_group.main.name
126+
127+
tags = {
128+
Name = "coder-${data.coder_workspace.me.owner}-${data.coder_workspace.me.name}"
129+
Coder_Provisioned = "true"
130+
}
131+
}
132+
133+
resource "azurerm_subnet" "internal" {
134+
name = "internal"
135+
resource_group_name = azurerm_resource_group.main.name
136+
virtual_network_name = azurerm_virtual_network.main.name
137+
address_prefixes = ["10.0.0.0/29"]
138+
}
139+
140+
resource "azurerm_network_interface" "main" {
141+
name = "nic"
142+
resource_group_name = azurerm_resource_group.main.name
143+
location = azurerm_resource_group.main.location
144+
145+
ip_configuration {
146+
name = "internal"
147+
subnet_id = azurerm_subnet.internal.id
148+
private_ip_address_allocation = "Dynamic"
149+
// Uncomment for public IP address as well as azurerm_public_ip resource above
150+
//public_ip_address_id = azurerm_public_ip.main.id
151+
}
152+
153+
tags = {
154+
Name = "coder-${data.coder_workspace.me.owner}-${data.coder_workspace.me.name}"
155+
Coder_Provisioned = "true"
156+
}
157+
}
158+
159+
resource "azurerm_managed_disk" "home" {
160+
create_option = "Empty"
161+
location = azurerm_resource_group.main.location
162+
name = "home"
163+
resource_group_name = azurerm_resource_group.main.name
164+
storage_account_type = "StandardSSD_LRS"
165+
disk_size_gb = var.home_size
166+
}
167+
168+
// azurerm requires an SSH key (or password) for an admin user or it won't start a VM. However,
169+
// cloud-init overwrites this anyway, so we'll just use a dummy SSH key.
170+
resource "tls_private_key" "dummy" {
171+
algorithm = "RSA"
172+
rsa_bits = 4096
173+
}
174+
175+
resource "azurerm_virtual_machine" "main" {
176+
count = data.coder_workspace.me.transition == "start" ? 1 : 0
177+
name = "vm"
178+
resource_group_name = azurerm_resource_group.main.name
179+
location = azurerm_resource_group.main.location
180+
vm_size = var.instance_type
181+
network_interface_ids = [
182+
azurerm_network_interface.main.id,
183+
]
184+
185+
os_profile {
186+
admin_username = "adminuser"
187+
computer_name = data.coder_workspace.me.name
188+
custom_data = local.userdata
189+
}
190+
191+
os_profile_linux_config {
192+
disable_password_authentication = true
193+
ssh_keys {
194+
key_data = tls_private_key.dummy.public_key_openssh
195+
path = "/home/adminuser/.ssh/authorized_keys"
196+
}
197+
}
198+
199+
storage_image_reference {
200+
publisher = "Canonical"
201+
offer = "0001-com-ubuntu-server-focal"
202+
sku = "20_04-lts-gen2"
203+
version = "latest"
204+
}
205+
206+
storage_os_disk {
207+
managed_disk_type = "StandardSSD_LRS"
208+
caching = "ReadWrite"
209+
create_option = "FromImage"
210+
name = "os"
211+
}
212+
delete_os_disk_on_termination = true
213+
214+
storage_data_disk {
215+
create_option = "Attach"
216+
lun = 10
217+
name = "home"
218+
caching = "ReadWrite"
219+
managed_disk_id = azurerm_managed_disk.home.id
220+
disk_size_gb = var.home_size
221+
}
222+
223+
tags = {
224+
Name = "coder-${data.coder_workspace.me.owner}-${data.coder_workspace.me.name}"
225+
Coder_Provisioned = "true"
226+
}
227+
}

0 commit comments

Comments
 (0)