Skip to content

Commit 5002922

Browse files
committed
add proxmox template
1 parent cbde8e8 commit 5002922

File tree

2 files changed

+342
-0
lines changed

2 files changed

+342
-0
lines changed
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
---
2+
name: Develop in a Proxmox VM
3+
description: Get started with a development environment on Proxmox.
4+
tags: [self-hosted, proxmox, vm, code-server]
5+
---
6+
7+
# proxmox-vm
8+
9+
## Getting started
10+
11+
Pick this template in `coder templates init` and follow instructions.
12+
13+
## Authentication
14+
15+
Coder will authenticate with the [PM_API_ environment variables](https://registry.terraform.io/providers/Telmate/proxmox/latest/docs#creating-the-connection-via-username-and-api-token). Ensure you have these set before you run `coder server` or in
16+
`/etc/coder.d/coder.env` if you are running Coder as a system service.
17+
18+
## How it works
19+
20+
This example assumes you have a `ubuntu-2004-cloudinit-template` VM template in your Proxmox cluster. You can create the exact template
21+
by following [this guide](https://austinsnerdythings.com/2021/08/30/how-to-create-a-proxmox-ubuntu-cloud-init-image/)
22+
23+
You can also edit the template to specify any cloud-init compatible VM on your cluster, such as one built with Packer and additional
24+
dependencies (Java, IntelliJ IDEA, VNC, noVNC).
25+
26+
## Add additional web app
27+
28+
If you have additional web applications installed on your VM image (e.g noVNC, JupyterHub), you can add a `coder_app` template
29+
to the template.
30+
31+
```hcl
32+
resource "coder_app" "novnc" {
33+
agent_id = coder_agent.dev.id
34+
name = "VNC Desktop"
35+
icon = "https://novnc.com/noVNC/app/images/icons/novnc-32x32.png"
36+
url = "http://localhost:13338"
37+
relative_path = true
38+
}
39+
```
40+
41+
If applications need to be started, they can be added to the `startup_script` of the coder agent:
42+
43+
```diff
44+
resource "coder_agent" "dev" {
45+
arch = "amd64"
46+
auth = "token"
47+
dir = "/home/${lower(data.coder_workspace.me.owner)}"
48+
os = "linux"
49+
startup_script = <<EOT
50+
#!/bin/sh
51+
# install and start code-server
52+
curl -fsSL https://code-server.dev/install.sh | sh
53+
code-server --auth none --port 13337 &
54+
EOT
55+
56+
+# install and start novnc
57+
+ sudo snap install novnc
58+
+ novnc --listen 13338
59+
}
60+
```
61+
62+
You can, of course, install/start all your applications with a system manager or even build
63+
into the VM template with Packer.
64+
65+
> You'll also need a VNC server running on your workspace/image for this example to work.
66+
> I couldn't find a great one-line command to demonstrate this, but any VNC server will do.

examples/templates/proxmox-vm/main.tf

Lines changed: 276 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,276 @@
1+
terraform {
2+
required_providers {
3+
coder = {
4+
source = "coder/coder"
5+
version = "0.4.2"
6+
}
7+
proxmox = {
8+
source = "telmate/proxmox"
9+
version = "2.9.10"
10+
}
11+
}
12+
}
13+
14+
# code-server
15+
resource "coder_app" "code-server" {
16+
agent_id = coder_agent.dev.id
17+
name = "code-server"
18+
icon = "https://upload.wikimedia.org/wikipedia/commons/thumb/9/9a/Visual_Studio_Code_1.35_icon.svg/2560px-Visual_Studio_Code_1.35_icon.svg.png"
19+
url = "http://localhost:13337"
20+
relative_path = true
21+
}
22+
23+
data "coder_workspace" "me" {
24+
}
25+
26+
resource "coder_agent" "dev" {
27+
arch = "amd64"
28+
auth = "token"
29+
dir = "/home/${lower(data.coder_workspace.me.owner)}"
30+
os = "linux"
31+
startup_script = <<EOT
32+
#!/bin/sh
33+
curl -fsSL https://code-server.dev/install.sh | sh
34+
code-server --auth none --port 13337 &
35+
EOT
36+
}
37+
38+
variable "proxmox_api_auth_info" {
39+
description = <<EOF
40+
Coder will try to authenticate to Proxmox with PM_API_
41+
environment variables on the Coder server. See:
42+
https://registry.terraform.io/providers/Telmate/proxmox/latest/docs
43+
44+
To authenticate with another method, edit this template (`main.tf`)
45+
46+
Press ENTER to continue
47+
EOF
48+
49+
sensitive = true
50+
}
51+
52+
variable "proxmox_api_url" {
53+
description = <<EOF
54+
Proxmox API URL. Often ends in /api2/json
55+
56+
If you specified PM_API_URL, press ENTER to skip
57+
EOF
58+
sensitive = true
59+
}
60+
61+
variable "proxmox_api_insecure" {
62+
description = <<EOF
63+
Select "true" if you have an invalid TLS certificate
64+
65+
Disable TLS verification?
66+
EOF
67+
68+
validation {
69+
condition = contains([
70+
"true",
71+
"false"
72+
], var.proxmox_api_insecure)
73+
error_message = "Specify true or false."
74+
}
75+
sensitive = true
76+
}
77+
78+
variable "vm_target_node" {
79+
description = "VM target node (often \"proxmox\")"
80+
sensitive = true
81+
}
82+
83+
variable "vm_cloudinit_ipconfig0" {
84+
description = <<EOF
85+
ipconfig0 for VM
86+
87+
e.g `ip=dhcp` or `ip=10.0.2.99/16,gw=10.0.2.2`
88+
see: https://registry.terraform.io/providers/Telmate/proxmox/latest/docs/resources/vm_qemu#top-level-block
89+
EOF
90+
91+
sensitive = true
92+
}
93+
94+
provider "proxmox" {
95+
pm_api_url = var.proxmox_api_url != "" ? var.proxmox_api_url : null
96+
pm_tls_insecure = tobool(var.proxmox_api_insecure)
97+
98+
# We recommend authenticating via
99+
# environment variables, but you can
100+
# also specify variables here :)
101+
102+
# For debugging Terraform provider errors:
103+
# pm_log_enable = true
104+
# pm_log_file = "/var/log/terraform-plugin-proxmox.log"
105+
# pm_debug = true
106+
# pm_log_levels = {
107+
# _default = "debug"
108+
# _capturelog = ""
109+
# }
110+
}
111+
112+
# Cloud-init data for VM to auto-start Coder
113+
locals {
114+
user_data = <<EOT
115+
Content-Type: multipart/mixed; boundary="//"
116+
MIME-Version: 1.0
117+
118+
--//
119+
Content-Type: text/cloud-config; charset="us-ascii"
120+
MIME-Version: 1.0
121+
Content-Transfer-Encoding: 7bit
122+
Content-Disposition: attachment; filename="cloud-config.txt"
123+
124+
#cloud-config
125+
hostname: ${lower(data.coder_workspace.me.name)}
126+
users:
127+
- name: ${lower(data.coder_workspace.me.owner)}
128+
sudo: ALL=(ALL) NOPASSWD:ALL
129+
shell: /bin/bash
130+
cloud_final_modules:
131+
- [scripts-user, always]
132+
133+
--//
134+
Content-Type: text/x-shellscript; charset="us-ascii"
135+
MIME-Version: 1.0
136+
Content-Transfer-Encoding: 7bit
137+
Content-Disposition: attachment; filename="userdata.txt"
138+
139+
#!/bin/bash
140+
export CODER_AGENT_TOKEN=${coder_agent.dev.token}
141+
sudo --preserve-env=CODER_AGENT_TOKEN -u ${lower(data.coder_workspace.me.owner)} /bin/bash -c '${coder_agent.dev.init_script}'
142+
--//--
143+
EOT
144+
}
145+
146+
# Copy the generated cloud_init config to the Proxmox node
147+
resource "null_resource" "cloud_init_config_files" {
148+
count = 1
149+
connection {
150+
type = "ssh"
151+
user = "root"
152+
host = "proxmox"
153+
private_key = file("/root/.ssh/id_rsa")
154+
}
155+
156+
provisioner "remote-exec" {
157+
inline = [<<EOT
158+
cat << 'EOF' > "/var/lib/vz/snippets/user_data_vm-${data.coder_workspace.me.owner}-${data.coder_workspace.me.name}.yml"
159+
${local.user_data}
160+
EOF
161+
EOT
162+
]
163+
164+
}
165+
}
166+
167+
variable "clone_template" {
168+
default = "ubuntu-2004-cloudinit-template"
169+
description = <<EOF
170+
Coder requires a cloud-init compatible Proxmox template.
171+
172+
(You can edit this template to specify more images and remove this message)
173+
174+
See the docs:
175+
https://github.com/coder/coder/tree/main/examples/templates/proxmox-vm
176+
177+
Follow this guide to add an Ubuntu 20.04 cloud-init template:
178+
https://austinsnerdythings.com/2021/08/30/how-to-create-a-proxmox-ubuntu-cloud-init-image/
179+
180+
Specify the name of the VM template to clone:
181+
EOF
182+
183+
validation {
184+
condition = contains(["ubuntu-2004-cloudinit-template"], var.clone_template)
185+
error_message = "Invalid clone_template."
186+
}
187+
}
188+
189+
variable "disk_size" {
190+
default = 10
191+
validation {
192+
condition = (
193+
var.disk_size >= 5 &&
194+
var.disk_size <= 100
195+
)
196+
error_message = "Disk size must be integer between 5 and 100 (GB)."
197+
}
198+
}
199+
200+
variable "cpu_cores" {
201+
default = 1
202+
validation {
203+
condition = (
204+
var.cpu_cores >= 1 &&
205+
var.cpu_cores <= 8
206+
)
207+
error_message = "CPU cores must be integer between 1 and 8."
208+
}
209+
}
210+
211+
variable "sockets" {
212+
default = 1
213+
validation {
214+
condition = (
215+
var.sockets >= 1 &&
216+
var.sockets <= 2
217+
)
218+
error_message = "Sockets must be integer between 1 and 2."
219+
}
220+
}
221+
222+
variable "memory" {
223+
default = "2048"
224+
validation {
225+
condition = contains([
226+
"1024",
227+
"2048",
228+
"3072",
229+
"4098",
230+
"6144"
231+
], var.memory)
232+
error_message = "Invalid memory value."
233+
}
234+
}
235+
236+
237+
# Provision the proxmox VM
238+
resource "proxmox_vm_qemu" "cloudinit-test" {
239+
count = 1
240+
depends_on = [
241+
null_resource.cloud_init_config_files,
242+
]
243+
244+
name = replace("${data.coder_workspace.me.owner}-${data.coder_workspace.me.name}", " ", "_")
245+
target_node = var.vm_target_node
246+
247+
# Image to clone
248+
clone = var.clone_template
249+
250+
cores = parseint(var.cpu_cores, 10)
251+
sockets = parseint(var.sockets, 10)
252+
memory = parseint(var.memory, 10)
253+
cpu = "kvm64"
254+
disk {
255+
slot = 0
256+
size = "${parseint(var.disk_size, 10)}G"
257+
type = "scsi"
258+
storage = "local-lvm"
259+
}
260+
nic = "virtio"
261+
bootdisk = "scsi0"
262+
bridge = "vmbr0"
263+
agent = 1
264+
265+
os_type = "cloud-init"
266+
ipconfig0 = var.vm_cloudinit_ipconfig0
267+
268+
269+
# Mount the custom cloud init config we copied to the Proxmox node
270+
cicustom = "user=local:snippets/user_data_vm-${data.coder_workspace.me.owner}-${data.coder_workspace.me.name}.yml"
271+
cloudinit_cdrom_storage = "local-lvm"
272+
273+
# Use these for debugging workspaces you cannot SSH into
274+
# ssshkeys = [""]
275+
276+
}

0 commit comments

Comments
 (0)