From 2c696b97be8caef288bfd78fca041b3d2c8eb7f8 Mon Sep 17 00:00:00 2001 From: Toastyice Date: Mon, 10 Jul 2023 21:00:32 +0000 Subject: [PATCH 1/4] Update to coder_paramter, Add ARM64 instances --- README.md | 3 + cloud-config.yaml.tftpl | 43 +++++---- main.tf | 190 +++++++++++++++++++++++++++++++--------- 3 files changed, 175 insertions(+), 61 deletions(-) diff --git a/README.md b/README.md index 065b225..d05b819 100644 --- a/README.md +++ b/README.md @@ -8,3 +8,6 @@ This template will do the following: - Add volumes and firewall policy to the instance - Ask the user if code-server should be installed and if so installs it +# apply +coder template init +coder template push --variable "hcloud_token=token" diff --git a/cloud-config.yaml.tftpl b/cloud-config.yaml.tftpl index eb5e6dd..02b2116 100644 --- a/cloud-config.yaml.tftpl +++ b/cloud-config.yaml.tftpl @@ -3,7 +3,7 @@ users: - name: ${username} sudo: ["ALL=(ALL) NOPASSWD:ALL"] groups: sudo - shell: /bin/bash + shell: /usr/bin/bash packages: - git - curl @@ -43,27 +43,34 @@ write_files: [Install] WantedBy=multi-user.target %{ if code_server_setup ~} - - path: /tmp/install_code_server.sh - permissions: "0777" - content: | - #!/bin/bash - CODE_SERVER_DOWNLOAD_URL=$(curl -sL https://api.github.com/repos/coder/code-server/releases/latest | jq -r '.assets[].browser_download_url' | grep "amd64.deb") - curl -fL $CODE_SERVER_DOWNLOAD_URL -o /tmp/code_server.deb - dpkg -i /tmp/code_server.deb - systemctl enable --now code-server@${username} - rm /tmp/code_server.deb - - path: /home/${username}/.config/code-server/config.yaml + - path: /etc/systemd/system/code-server.service permissions: "0644" content: | - bind-addr: 127.0.0.1:8080 - auth: none - cert: false + [Unit] + Description=code server + After=network-online.target + Wants=network-online.target + + [Service] + User=${username} + ExecStart=/opt/coder/install_code_server + Restart=always + RestartSec=10 + TimeoutStopSec=90 + KillMode=process + + [Install] + WantedBy=multi-user.target + - path: /opt/coder/install_code_server + permissions: "0755" + content: | + #!/usr/bin/env sh + curl -fsSL https://code-server.dev/install.sh | sh + /usr/bin/code-server --bind-addr 127.0.0.1:13337 --user-data-dir /home/${username}/.vscode --auth none %{ endif ~} runcmd: - chown ${username}:${username} /home/${username} - - systemctl enable coder-agent - - systemctl start coder-agent + - systemctl enable --now coder-agent %{ if code_server_setup ~} - - /tmp/install_code_server.sh - - rm /tmp/install_code_server.sh + - systemctl enable --now code-server %{ endif } diff --git a/main.tf b/main.tf index e1c2257..85c486d 100644 --- a/main.tf +++ b/main.tf @@ -2,11 +2,11 @@ terraform { required_providers { coder = { source = "coder/coder" - version = "0.5.0" + version = "0.11.0" } hcloud = { source = "hetznercloud/hcloud" - version = "1.33.2" + version = "1.41.0" } } } @@ -29,66 +29,170 @@ EOF } } -variable "instance_location" { - description = "What region should your workspace live in?" - default = "nbg1" - validation { - condition = contains(["nbg1", "fsn1", "hel1"], var.instance_location) - error_message = "Invalid zone!" +data "coder_parameter" "instance_location" { + name = "Location" + default = "fsn1" + type = "string" + mutable = false + + option { + name = "Nuremberg" + value = "nbg1" + icon = "/emojis/1f1e9-1f1ea.png" } -} -variable "instance_type" { - description = "What instance type should your workspace use?" - default = "cx11" - validation { - condition = contains(["cx11", "cx21", "cx31", "cx41", "cx51"], var.instance_type) - error_message = "Invalid instance type!" + option { + name = "Falkenstein" + value = "fsn1" + icon = "/emojis/1f1e9-1f1ea.png" + } + + option { + name = "Helsinki" + value = "hel1" + icon = "/emojis/1f1eb-1f1ee.png" + } + + option { + name = "Hillsboro" + value = "hil" + icon = "/emojis/1f1fa-1f1f8.png" + } + + option { + name = "Ashburn" + value = "ash" + icon = "/emojis/1f1fa-1f1f8.png" } } -variable "instance_os" { - description = "Which operating system should your workspace use?" - default = "ubuntu-20.04" - validation { - condition = contains(["ubuntu-22.04","ubuntu-20.04", "ubuntu-18.04", "debian-11", "debian-10"], var.instance_os) - error_message = "Invalid OS!" +data "coder_parameter" "instance_type" { + name = "Instance Type" + default = "cpx11" + type = "string" + mutable = true + description = "ARM64 is only available in Falkenstein (fsn1) region." + + option { + name = "cpx11 AMD ADM64 VCPUS 2, RAM 2GB" + value = "cpx11" + } + + option { + name = "cpx21 AMD ADM64 VCPUS 3, RAM 4GB" + value = "cpx21" + } + + option { + name = "cpx31 AMD ADM64 VCPUS 4, RAM 8GB" + value = "cpx31" + } + + option { + name = "cpx41 AMD ADM64 VCPUS 8, RAM 16GB" + value = "cpx41" + } + + option { + name = "cpx51 AMD ADM64 VCPUS 16, RAM 32GB" + value = "cpx51" + } + + option { + name = "cax11 Ampere ARM64 VCPUS 2, RAM 4GB" + value = "cax11" + } + + option { + name = "cax21 Ampere ARM64 VCPUS 4, RAM 8GB" + value = "cax21" + } + + option { + name = "cax31 Ampere ARM64 VCPUS 8, RAM 16GB" + value = "cax31" + } + + option { + name = "cax41 Ampere ARM64 VCPUS 16, RAM 32GB" + value = "cax41" } } -variable "volume_size" { - description = "How much storage space do you need in GB (can't be less then 10)?" - default = "10" - validation { - condition = var.volume_size >= 10 - error_message = "Invalid volume size!" +data "coder_parameter" "instance_os" { + name = "OS" + default = "fedora-38" + type = "string" + mutable = true + + option { + name = "Fedora 38" + value = "fedora-38" + icon = "/icon/fedora.svg" + } + + option { + name = "AlmaLinux 9" + value = "alma-9" + icon = "https://upload.wikimedia.org/wikipedia/commons/1/13/AlmaLinux_Icon_Logo.svg" + } + + option { + name = "Ubuntu 22.04" + value = "ubuntu-22.04" + icon = "/icon/ubuntu.svg" + } + + option { + name = "Debian 12" + value = "debian-12" + icon = "/icon/debian.svg" } } -variable "code_server" { - description = "Should Code Server be installed?" - default = "true" +data "coder_parameter" "volume_size" { + name = "volume_size" + description = "Disk Size in GB" + default = 10 + type = "number" + mutable = true validation { - condition = contains(["true","false"], var.code_server) - error_message = "Your answer can only be yes or no!" + min = 10 + max = 250 + monotonic = "increasing" } } +data "coder_parameter" "code_server" { + name = "code_server" + description = "Should Code-server be installed?" + type = "bool" + default = true + mutable = true +} + data "coder_workspace" "me" { } resource "coder_agent" "dev" { - arch = "amd64" + arch = strcontains(data.coder_parameter.instance_type.value, "cax") ? "arm64" : "amd64" os = "linux" } resource "coder_app" "code-server" { - count = var.code_server ? 1 : 0 + count = data.coder_parameter.code_server.value ? 1 : 0 agent_id = coder_agent.dev.id - name = "code-server" + display_name = "VS Code" + slug = "code-server" icon = "/icon/code.svg" - url = "http://localhost:8080" - relative_path = true + url = "http://127.0.0.1:13337" + subdomain = false + share = "owner" + healthcheck { + url = "http://localhost:13337/healthz" + interval = 5 + threshold = 6 + } } # Generate a dummy ssh key that is not accessible so Hetzner cloud does not spam the admin with emails. @@ -105,24 +209,24 @@ resource "hcloud_ssh_key" "root" { resource "hcloud_server" "root" { count = data.coder_workspace.me.start_count name = "coder-${data.coder_workspace.me.owner}-${data.coder_workspace.me.name}-root" - server_type = var.instance_type - location = var.instance_location - image = var.instance_os + server_type = data.coder_parameter.instance_type.value + location = data.coder_parameter.instance_location.value + image = data.coder_parameter.instance_os.value ssh_keys = [hcloud_ssh_key.root.id] user_data = templatefile("cloud-config.yaml.tftpl", { username = data.coder_workspace.me.owner volume_path = "/dev/disk/by-id/scsi-0HC_Volume_${hcloud_volume.root.id}" init_script = base64encode(coder_agent.dev.init_script) coder_agent_token = coder_agent.dev.token - code_server_setup = var.code_server + code_server_setup = data.coder_parameter.code_server.value }) } resource "hcloud_volume" "root" { name = "coder-${data.coder_workspace.me.owner}-${data.coder_workspace.me.name}-root" - size = var.volume_size format = "ext4" - location = var.instance_location + size = data.coder_parameter.volume_size.value + location = data.coder_parameter.instance_location.value } resource "hcloud_volume_attachment" "root" { From 6c05dae81ce63fa0250bc6a969303733313d17cf Mon Sep 17 00:00:00 2001 From: Toastyice Date: Wed, 12 Jul 2023 04:40:55 +0000 Subject: [PATCH 2/4] Add dotfile support --- main.tf | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/main.tf b/main.tf index 85c486d..aa24668 100644 --- a/main.tf +++ b/main.tf @@ -163,6 +163,14 @@ data "coder_parameter" "volume_size" { } } +data "coder_parameter" "dotfiles_uri" { + name = "Dotfiles URL" + description = "Optional" + default = "" + type = "string" + mutable = true +} + data "coder_parameter" "code_server" { name = "code_server" description = "Should Code-server be installed?" @@ -177,6 +185,7 @@ data "coder_workspace" "me" { resource "coder_agent" "dev" { arch = strcontains(data.coder_parameter.instance_type.value, "cax") ? "arm64" : "amd64" os = "linux" + startup_script = data.coder_parameter.dotfiles_uri.value != "" ? "/tmp/coder*/coder dotfiles -y ${data.coder_parameter.dotfiles_uri.value}" : null } resource "coder_app" "code-server" { From d7bf11b5f93a01b3276e46e241819582e04a1c71 Mon Sep 17 00:00:00 2001 From: Paul Schella Date: Sat, 27 Apr 2024 08:42:16 +0000 Subject: [PATCH 3/4] Add Modules for region, os-type & instance-type, split into multiple files --- README copy.md | 12 ++ cloud-config.yaml.tftpl | 29 --- coder.tf | 113 ++++++++++ hcloud-instance-type/coder_parameter.tf | 29 +++ hcloud-instance-type/data.tf | 2 + hcloud-instance-type/output.tf | 7 + hcloud-instance-type/provider.tf | 17 ++ hcloud-instance-type/variables.tf | 76 +++++++ hcloud-os-type/coder_parameter.tf | 21 ++ hcloud-os-type/data.tf | 11 + hcloud-os-type/output.tf | 11 + hcloud-os-type/provider.tf | 17 ++ hcloud-os-type/variables.tf | 66 ++++++ hcloud-region/coder_parameter.tf | 21 ++ hcloud-region/output.tf | 3 + hcloud-region/provider.tf | 9 + hcloud-region/variables.tf | 47 +++++ hcloud-region/zones.tf | 27 +++ hetzner.tf | 57 +++++ main.tf | 264 ------------------------ modules.tf | 34 +++ provider.tf | 19 ++ variables.tf | 10 + 23 files changed, 609 insertions(+), 293 deletions(-) create mode 100644 README copy.md create mode 100644 coder.tf create mode 100644 hcloud-instance-type/coder_parameter.tf create mode 100644 hcloud-instance-type/data.tf create mode 100644 hcloud-instance-type/output.tf create mode 100644 hcloud-instance-type/provider.tf create mode 100644 hcloud-instance-type/variables.tf create mode 100644 hcloud-os-type/coder_parameter.tf create mode 100644 hcloud-os-type/data.tf create mode 100644 hcloud-os-type/output.tf create mode 100644 hcloud-os-type/provider.tf create mode 100644 hcloud-os-type/variables.tf create mode 100644 hcloud-region/coder_parameter.tf create mode 100644 hcloud-region/output.tf create mode 100644 hcloud-region/provider.tf create mode 100644 hcloud-region/variables.tf create mode 100644 hcloud-region/zones.tf create mode 100644 hetzner.tf delete mode 100644 main.tf create mode 100644 modules.tf create mode 100644 provider.tf create mode 100644 variables.tf diff --git a/README copy.md b/README copy.md new file mode 100644 index 0000000..069a778 --- /dev/null +++ b/README copy.md @@ -0,0 +1,12 @@ +# coder-hetzner-cloud-template +This repo contains a Terraform template for Coder https://github.com/coder/coder to setup a cloud instance as dev environment with or without vscode + +This template will do the following: +- Creates a Hetzner Cloud instance +- Create a Hetzner Cloud volume +- Create a default block inbound firewall policy +- Add volumes and firewall policy to the instance +- Ask the user if code-server should be installed and if so installs it + +# Push +coder template push --variable "hcloud_token=token" diff --git a/cloud-config.yaml.tftpl b/cloud-config.yaml.tftpl index 02b2116..5770dc5 100644 --- a/cloud-config.yaml.tftpl +++ b/cloud-config.yaml.tftpl @@ -42,35 +42,6 @@ write_files: [Install] WantedBy=multi-user.target -%{ if code_server_setup ~} - - path: /etc/systemd/system/code-server.service - permissions: "0644" - content: | - [Unit] - Description=code server - After=network-online.target - Wants=network-online.target - - [Service] - User=${username} - ExecStart=/opt/coder/install_code_server - Restart=always - RestartSec=10 - TimeoutStopSec=90 - KillMode=process - - [Install] - WantedBy=multi-user.target - - path: /opt/coder/install_code_server - permissions: "0755" - content: | - #!/usr/bin/env sh - curl -fsSL https://code-server.dev/install.sh | sh - /usr/bin/code-server --bind-addr 127.0.0.1:13337 --user-data-dir /home/${username}/.vscode --auth none -%{ endif ~} runcmd: - chown ${username}:${username} /home/${username} - systemctl enable --now coder-agent -%{ if code_server_setup ~} - - systemctl enable --now code-server -%{ endif } diff --git a/coder.tf b/coder.tf new file mode 100644 index 0000000..6fe4d5c --- /dev/null +++ b/coder.tf @@ -0,0 +1,113 @@ +data "coder_parameter" "volume_size" { + name = "volume_size" + description = "Disk Size in GB" + default = 10 + type = "number" + mutable = true + validation { + min = 10 + max = 250 + monotonic = "increasing" + } +} + +data "coder_parameter" "code_server_extentions" { + name = "code-server extentions" + type = "list(string)" + description = "List of code-server extentions" + mutable = true + default = jsonencode([ + "dracula-theme.theme-dracula", + "ms-toolsai.jupyter", + "redhat.vscode-yaml", + "redhat.vscode-xml", + "redhat.ansible", + "samuelcolvin.jinjahtml", + "PKief.material-icon-theme", + "hashicorp.terraform", + "hashicorp.hcl", + "GitLab.gitlab-workflow", + "scala-lang.scala", + "scalameta.metals" + ]) +} + +data "coder_workspace" "me" { +} + +resource "coder_agent" "dev" { + arch = strcontains(module.hcloud_instance_type.value, "cax") ? "arm64" : "amd64" #change this! + os = "linux" + + metadata { + display_name = "CPU Usage" + key = "cpu" + script = < v + if contains(var.architecture, v.architecture) && + contains(var.cpu_type, v.cpu_type)&& + var.include_deprecated ? true : v.is_deprecated == false #needs to be tested! + } + content { + name = option.value.description + description = <<-EOF + arch ${option.value.architecture} + cpu_type ${option.value.cpu_type} + cores ${option.value.cores} + ram ${option.value.memory}GB + disk ${option.value.disk}GB + traffic ${option.value.included_traffic / pow(1024, 4)}TB + EOF + value = option.value.name + } + } +} diff --git a/hcloud-instance-type/data.tf b/hcloud-instance-type/data.tf new file mode 100644 index 0000000..ce9cb92 --- /dev/null +++ b/hcloud-instance-type/data.tf @@ -0,0 +1,2 @@ +data "hcloud_server_types" "ds" { +} diff --git a/hcloud-instance-type/output.tf b/hcloud-instance-type/output.tf new file mode 100644 index 0000000..28e7266 --- /dev/null +++ b/hcloud-instance-type/output.tf @@ -0,0 +1,7 @@ +output "value" { + value = data.coder_parameter.instance_type.value +} + +output "server_types" { + value = data.hcloud_server_types.ds +} diff --git a/hcloud-instance-type/provider.tf b/hcloud-instance-type/provider.tf new file mode 100644 index 0000000..ec91a21 --- /dev/null +++ b/hcloud-instance-type/provider.tf @@ -0,0 +1,17 @@ +terraform { + required_version = ">= 1.0" + required_providers { + coder = { + source = "coder/coder" + version = ">= 0.11" + } + hcloud = { + source = "hetznercloud/hcloud" + version = ">= 1.46" + } + } +} + +provider "hcloud" { + token = var.token +} diff --git a/hcloud-instance-type/variables.tf b/hcloud-instance-type/variables.tf new file mode 100644 index 0000000..35aef04 --- /dev/null +++ b/hcloud-instance-type/variables.tf @@ -0,0 +1,76 @@ +variable "token" { + description = <<-EOF + Coder requires a Hetzner Cloud token to provision workspaces. + EOF + sensitive = true + validation { + condition = length(var.token) == 64 + error_message = "Please provide a valid Hetzner Cloud API token." + } +} + +variable "display_name" { + default = "hcloud Instance type" + description = "The display name of the parameter." + type = string + validation { + condition = length(var.display_name) > 0 + error_message = "Display must not be empty" + } +} + +variable "description" { + default = "The region to deploy workspace infrastructure." + description = "The description of the parameter." + type = string +} + +variable "default" { + default = null + description = "Default instance type" + type = string +} + +variable "architecture" { + description = "List of instance architectures to include." + type = set(string) + default = [ + "arm", + "x86" + ] + validation { + condition = length(var.architecture) > 0 + error_message = "At least one architecture must be specified!" + } +} + +variable "cpu_type" { + description = "List of cpu types to include." + type = set(string) + default = [ + "shared", + "dedicated" + ] + validation { + condition = length(var.cpu_type) > 0 + error_message = "At least one cpu type must be specified!" + } +} + +variable "include_deprecated" { + description = "Should deprecated instances be included?" + type = bool + default = false +} + +variable "mutable" { + default = true + description = "Whether the parameter can be changed after creation." + type = bool +} + +variable "coder_parameter_order" { + type = number + description = "The order determines the position of a template parameter in the UI/CLI presentation. The lowest order is shown first and parameters with equal order are sorted by name (ascending order)." + default = null +} diff --git a/hcloud-os-type/coder_parameter.tf b/hcloud-os-type/coder_parameter.tf new file mode 100644 index 0000000..eff7082 --- /dev/null +++ b/hcloud-os-type/coder_parameter.tf @@ -0,0 +1,21 @@ +data "coder_parameter" "os" { + name = "hcloud_os" + display_name = var.display_name + description = var.description + icon = "https://cdn.hetzner.com/assets/Uploads/icon-circle-cloud.svg" + mutable = var.mutable + default = var.default != null && var.default != "" ? var.default : null + order = var.coder_parameter_order + dynamic "option" { + for_each = local.os_list + content { + name = replace(option.value, "-", " ") + value = option.value + # the icon for rocky and alma needs to have a linux appended + icon = ( + "/icon/${split("-", option.value)[0] == "alma" || split("-", option.value)[0] == "rocky" ? + "${split("-", option.value)[0]}linux" : split("-", option.value)[0]}.svg" + ) + } + } +} diff --git a/hcloud-os-type/data.tf b/hcloud-os-type/data.tf new file mode 100644 index 0000000..8ef10bd --- /dev/null +++ b/hcloud-os-type/data.tf @@ -0,0 +1,11 @@ +data "hcloud_images" "images" { + include_deprecated = var.include_deprecated +} + +locals { + os_list = sort(distinct([ + for item in data.hcloud_images.images.images : item.name if + contains(var.os_flavor, item.os_flavor) && + item.type == "system" #make configurable + ])) +} diff --git a/hcloud-os-type/output.tf b/hcloud-os-type/output.tf new file mode 100644 index 0000000..8f00948 --- /dev/null +++ b/hcloud-os-type/output.tf @@ -0,0 +1,11 @@ +output "value" { + value = data.coder_parameter.os.value +} + +output "hcloud_images_all" { + value = data.hcloud_images.images +} + +output "hcloud_images_filtered" { + value = local.os_list +} diff --git a/hcloud-os-type/provider.tf b/hcloud-os-type/provider.tf new file mode 100644 index 0000000..ec91a21 --- /dev/null +++ b/hcloud-os-type/provider.tf @@ -0,0 +1,17 @@ +terraform { + required_version = ">= 1.0" + required_providers { + coder = { + source = "coder/coder" + version = ">= 0.11" + } + hcloud = { + source = "hetznercloud/hcloud" + version = ">= 1.46" + } + } +} + +provider "hcloud" { + token = var.token +} diff --git a/hcloud-os-type/variables.tf b/hcloud-os-type/variables.tf new file mode 100644 index 0000000..2d19afe --- /dev/null +++ b/hcloud-os-type/variables.tf @@ -0,0 +1,66 @@ +variable "token" { + description = <<-EOF + Coder requires a Hetzner Cloud token to provision workspaces. + EOF + sensitive = true + validation { + condition = length(var.token) == 64 + error_message = "Please provide a valid Hetzner Cloud API token." + } +} + +variable "display_name" { + default = "hcloud Instance type" + description = "The display name of the parameter." + type = string + validation { + condition = length(var.display_name) > 0 + error_message = "Display must not be empty" + } +} + +variable "description" { + default = "The region to deploy workspace infrastructure." + description = "The description of the parameter." + type = string +} + +variable "default" { + default = "ubuntu-24.04" + description = "Default instance type" + type = string +} + +variable "os_flavor" { + description = "List of instance os flavors to include." + type = set(string) + default = [ + "ubuntu", #manatory or change default + "debian", + "fedora", + "alma", + "rocky" + ] + validation { + condition = length(var.os_flavor) > 0 + error_message = "At least one flavor must be specified!" + } +} + +variable "include_deprecated" { + description = "Should deprecated instances be included?" + type = bool + default = false +} + +variable "mutable" { + default = true + description = "Whether the parameter can be changed after creation." + type = bool +} + +variable "coder_parameter_order" { + type = number + description = "The order determines the position of a template parameter in the UI/CLI presentation. The lowest order is shown first and parameters with equal order are sorted by name (ascending order)." + default = null +} diff --git a/hcloud-region/coder_parameter.tf b/hcloud-region/coder_parameter.tf new file mode 100644 index 0000000..130b060 --- /dev/null +++ b/hcloud-region/coder_parameter.tf @@ -0,0 +1,21 @@ +data "coder_parameter" "region" { + name = "hcloud_region" + display_name = var.display_name + description = var.description + icon = "https://cdn.hetzner.com/assets/Uploads/icon-circle-cloud.svg" + mutable = var.mutable + default = var.default != null && var.default != "" ? var.default : null + order = var.coder_parameter_order + dynamic "option" { + for_each = { + for k, v in local.zones : k => v + if anytrue([for d in var.regions : startswith(k, d)]) + } + content { + icon = try(var.custom_icons[option.key], option.value.icon) + name = option.value.name + description = option.key + value = option.key + } + } +} diff --git a/hcloud-region/output.tf b/hcloud-region/output.tf new file mode 100644 index 0000000..3fefde5 --- /dev/null +++ b/hcloud-region/output.tf @@ -0,0 +1,3 @@ +output "value" { + value = data.coder_parameter.region.value +} diff --git a/hcloud-region/provider.tf b/hcloud-region/provider.tf new file mode 100644 index 0000000..2d42a52 --- /dev/null +++ b/hcloud-region/provider.tf @@ -0,0 +1,9 @@ +terraform { + required_version = ">= 1.0" + required_providers { + coder = { + source = "coder/coder" + version = ">= 0.11" + } + } +} diff --git a/hcloud-region/variables.tf b/hcloud-region/variables.tf new file mode 100644 index 0000000..484e314 --- /dev/null +++ b/hcloud-region/variables.tf @@ -0,0 +1,47 @@ +variable "display_name" { + default = "hcloud Region" + description = "The display name of the parameter." + type = string +} + +variable "description" { + default = "The region to deploy workspace infrastructure." + description = "The description of the parameter." + type = string +} + +variable "default" { + default = "fsn1" + description = "Default zone" + type = string +} + +variable "regions" { + description = "List of hcloud regions to include." + type = list(string) + default = [ + "fsn1", + "nbg1", + "hel1", + "ash", + "hil" + ] +} + +variable "mutable" { + default = false + description = "Whether the parameter can be changed after creation." + type = bool +} + +variable "custom_icons" { + default = {} + description = "A map of custom icons for region IDs." + type = map(string) +} + +variable "coder_parameter_order" { + type = number + description = "The order determines the position of a template parameter in the UI/CLI presentation. The lowest order is shown first and parameters with equal order are sorted by name (ascending order)." + default = null +} diff --git a/hcloud-region/zones.tf b/hcloud-region/zones.tf new file mode 100644 index 0000000..cf1337a --- /dev/null +++ b/hcloud-region/zones.tf @@ -0,0 +1,27 @@ +locals { + zones = { + # Germany + fsn1 = { + name = "Falkenstein" + icon = "/emojis/1f1e9-1f1ea.png" + } + nbg1 = { + name = "Nuremberg" + icon = "/emojis/1f1e9-1f1ea.png" + } + # Finland + hel1 = { + name = "Helsinki" + icon = "/emojis/1f1eb-1f1ee.png" + } + # United States of America + ash = { + name = "Ashburn" + icon = "/emojis/1f1fa-1f1f8.png" + } + hil = { + name = "Hillsboro" + icon = "/emojis/1f1fa-1f1f8.png" + } + } +} diff --git a/hetzner.tf b/hetzner.tf new file mode 100644 index 0000000..981fd0e --- /dev/null +++ b/hetzner.tf @@ -0,0 +1,57 @@ +# Generate a dummy ssh key that is not accessible so Hetzner cloud does not spam the admin with emails. +resource "tls_private_key" "rsa_4096" { + algorithm = "RSA" + rsa_bits = 4096 +} + +resource "hcloud_ssh_key" "root" { + name = "coder-${data.coder_workspace.me.owner}-${data.coder_workspace.me.name}-root" + public_key = tls_private_key.rsa_4096.public_key_openssh +} + +resource "hcloud_server" "root" { + count = data.coder_workspace.me.start_count + name = "coder-${data.coder_workspace.me.owner}-${data.coder_workspace.me.name}-root" + server_type = module.hcloud_instance_type.value + location = module.hcloud_region.value + image = module.hcloud_os_type.value + ssh_keys = [hcloud_ssh_key.root.id] + user_data = templatefile("cloud-config.yaml.tftpl", { + username = data.coder_workspace.me.owner + volume_path = "/dev/disk/by-id/scsi-0HC_Volume_${hcloud_volume.root.id}" + init_script = base64encode(coder_agent.dev.init_script) + coder_agent_token = coder_agent.dev.token + }) +} + +resource "hcloud_volume" "root" { + name = "coder-${data.coder_workspace.me.owner}-${data.coder_workspace.me.name}-root" + format = "ext4" + size = data.coder_parameter.volume_size.value + location = module.hcloud_region.value +} + +resource "hcloud_volume_attachment" "root" { + count = data.coder_workspace.me.start_count + volume_id = hcloud_volume.root.id + server_id = hcloud_server.root[count.index].id + automount = false +} + +resource "hcloud_firewall" "root" { + name = "coder-${data.coder_workspace.me.owner}-${data.coder_workspace.me.name}-root" + rule { + direction = "in" + protocol = "icmp" + source_ips = [ + "0.0.0.0/0", + "::/0" + ] + } +} + +resource "hcloud_firewall_attachment" "root_fw_attach" { + count = data.coder_workspace.me.start_count + firewall_id = hcloud_firewall.root.id + server_ids = [hcloud_server.root[count.index].id] +} diff --git a/main.tf b/main.tf deleted file mode 100644 index aa24668..0000000 --- a/main.tf +++ /dev/null @@ -1,264 +0,0 @@ -terraform { - required_providers { - coder = { - source = "coder/coder" - version = "0.11.0" - } - hcloud = { - source = "hetznercloud/hcloud" - version = "1.41.0" - } - } -} - -provider "hcloud" { - token = var.hcloud_token -} - -provider "coder" { -} - -variable "hcloud_token" { - description = < Date: Mon, 9 Jun 2025 09:39:13 +0200 Subject: [PATCH 4/4] Update from private fork --- README copy.md | 12 ---- README.md | 5 +- cloud-config.yaml.tftpl | 1 + coder.tf | 61 ++++++++++--------- hcloud-os-type/data.tf | 11 ---- hetzner.tf | 14 ++--- modules.tf | 24 +++++--- modules/docker/main.tf | 24 ++++++++ modules/docker/run.sh | 26 ++++++++ .../hcloud-instance-type}/coder_parameter.tf | 7 +-- .../hcloud-instance-type}/data.tf | 0 .../hcloud-instance-type}/output.tf | 0 .../hcloud-instance-type}/provider.tf | 0 .../hcloud-instance-type}/variables.tf | 4 +- .../hcloud-os-type}/coder_parameter.tf | 4 +- modules/hcloud-os-type/data.tf | 11 ++++ .../hcloud-os-type}/output.tf | 0 .../hcloud-os-type}/provider.tf | 0 .../hcloud-os-type}/variables.tf | 7 +-- .../hcloud-region}/coder_parameter.tf | 2 +- .../hcloud-region}/output.tf | 0 .../hcloud-region}/provider.tf | 0 .../hcloud-region}/variables.tf | 5 +- .../hcloud-region}/zones.tf | 5 ++ provider.tf | 4 +- 25 files changed, 138 insertions(+), 89 deletions(-) delete mode 100644 README copy.md delete mode 100644 hcloud-os-type/data.tf create mode 100644 modules/docker/main.tf create mode 100644 modules/docker/run.sh rename {hcloud-instance-type => modules/hcloud-instance-type}/coder_parameter.tf (76%) rename {hcloud-instance-type => modules/hcloud-instance-type}/data.tf (100%) rename {hcloud-instance-type => modules/hcloud-instance-type}/output.tf (100%) rename {hcloud-instance-type => modules/hcloud-instance-type}/provider.tf (100%) rename {hcloud-instance-type => modules/hcloud-instance-type}/variables.tf (98%) rename {hcloud-os-type => modules/hcloud-os-type}/coder_parameter.tf (93%) create mode 100644 modules/hcloud-os-type/data.tf rename {hcloud-os-type => modules/hcloud-os-type}/output.tf (100%) rename {hcloud-os-type => modules/hcloud-os-type}/provider.tf (100%) rename {hcloud-os-type => modules/hcloud-os-type}/variables.tf (95%) rename {hcloud-region => modules/hcloud-region}/coder_parameter.tf (89%) rename {hcloud-region => modules/hcloud-region}/output.tf (100%) rename {hcloud-region => modules/hcloud-region}/provider.tf (100%) rename {hcloud-region => modules/hcloud-region}/variables.tf (96%) rename {hcloud-region => modules/hcloud-region}/zones.tf (83%) diff --git a/README copy.md b/README copy.md deleted file mode 100644 index 069a778..0000000 --- a/README copy.md +++ /dev/null @@ -1,12 +0,0 @@ -# coder-hetzner-cloud-template -This repo contains a Terraform template for Coder https://github.com/coder/coder to setup a cloud instance as dev environment with or without vscode - -This template will do the following: -- Creates a Hetzner Cloud instance -- Create a Hetzner Cloud volume -- Create a default block inbound firewall policy -- Add volumes and firewall policy to the instance -- Ask the user if code-server should be installed and if so installs it - -# Push -coder template push --variable "hcloud_token=token" diff --git a/README.md b/README.md index d05b819..069a778 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,5 @@ This template will do the following: - Add volumes and firewall policy to the instance - Ask the user if code-server should be installed and if so installs it -# apply -coder template init -coder template push --variable "hcloud_token=token" +# Push +coder template push --variable "hcloud_token=token" diff --git a/cloud-config.yaml.tftpl b/cloud-config.yaml.tftpl index 5770dc5..b493590 100644 --- a/cloud-config.yaml.tftpl +++ b/cloud-config.yaml.tftpl @@ -45,3 +45,4 @@ write_files: runcmd: - chown ${username}:${username} /home/${username} - systemctl enable --now coder-agent + - resize2fs /dev/sdb diff --git a/coder.tf b/coder.tf index 6fe4d5c..e1e9fc5 100644 --- a/coder.tf +++ b/coder.tf @@ -16,7 +16,7 @@ data "coder_parameter" "code_server_extentions" { type = "list(string)" description = "List of code-server extentions" mutable = true - default = jsonencode([ + default = jsonencode([ "dracula-theme.theme-dracula", "ms-toolsai.jupyter", "redhat.vscode-yaml", @@ -35,79 +35,82 @@ data "coder_parameter" "code_server_extentions" { data "coder_workspace" "me" { } +data "coder_workspace_owner" "me" { +} + resource "coder_agent" "dev" { arch = strcontains(module.hcloud_instance_type.value, "cax") ? "arm64" : "amd64" #change this! os = "linux" metadata { display_name = "CPU Usage" - key = "cpu" - script = <&1)" +if [ $? -ne 0 ]; then + echo "Failed to download docker installation script: $script" + exit 1 +fi + +output="$(bash <<< "$script" 2>&1)" +if [ $? -ne 0 ]; then + echo "Failed to install docker: $output" + exit 1 +fi + +# Get the OS name +os_name=$(cat /etc/*-release | grep '^ID=' | cut -d'=' -f2) + +# Check if the OS is Fedora, AlmaLinux, or Rocky Linux +if [[ "$os_name" == "fedora" ]] || [[ "$os_name" == "almalinux" ]] || [[ "$os_name" == "rocky" ]]; then + sudo systemctl enable --now docker 2>&1 +fi + +printf "🥳 docker has been installed\n\n" diff --git a/hcloud-instance-type/coder_parameter.tf b/modules/hcloud-instance-type/coder_parameter.tf similarity index 76% rename from hcloud-instance-type/coder_parameter.tf rename to modules/hcloud-instance-type/coder_parameter.tf index 83c08e3..15d44c5 100644 --- a/hcloud-instance-type/coder_parameter.tf +++ b/modules/hcloud-instance-type/coder_parameter.tf @@ -9,9 +9,9 @@ data "coder_parameter" "instance_type" { dynamic "option" { for_each = { for k, v in data.hcloud_server_types.ds.server_types : k => v - if contains(var.architecture, v.architecture) && - contains(var.cpu_type, v.cpu_type)&& - var.include_deprecated ? true : v.is_deprecated == false #needs to be tested! + if contains(var.architecture, v.architecture) && + contains(var.cpu_type, v.cpu_type) && + var.include_deprecated ? true : !v.is_deprecated } content { name = option.value.description @@ -21,7 +21,6 @@ data "coder_parameter" "instance_type" { cores ${option.value.cores} ram ${option.value.memory}GB disk ${option.value.disk}GB - traffic ${option.value.included_traffic / pow(1024, 4)}TB EOF value = option.value.name } diff --git a/hcloud-instance-type/data.tf b/modules/hcloud-instance-type/data.tf similarity index 100% rename from hcloud-instance-type/data.tf rename to modules/hcloud-instance-type/data.tf diff --git a/hcloud-instance-type/output.tf b/modules/hcloud-instance-type/output.tf similarity index 100% rename from hcloud-instance-type/output.tf rename to modules/hcloud-instance-type/output.tf diff --git a/hcloud-instance-type/provider.tf b/modules/hcloud-instance-type/provider.tf similarity index 100% rename from hcloud-instance-type/provider.tf rename to modules/hcloud-instance-type/provider.tf diff --git a/hcloud-instance-type/variables.tf b/modules/hcloud-instance-type/variables.tf similarity index 98% rename from hcloud-instance-type/variables.tf rename to modules/hcloud-instance-type/variables.tf index 35aef04..4684cb7 100644 --- a/hcloud-instance-type/variables.tf +++ b/modules/hcloud-instance-type/variables.tf @@ -34,7 +34,7 @@ variable "default" { variable "architecture" { description = "List of instance architectures to include." type = set(string) - default = [ + default = [ "arm", "x86" ] @@ -47,7 +47,7 @@ variable "architecture" { variable "cpu_type" { description = "List of cpu types to include." type = set(string) - default = [ + default = [ "shared", "dedicated" ] diff --git a/hcloud-os-type/coder_parameter.tf b/modules/hcloud-os-type/coder_parameter.tf similarity index 93% rename from hcloud-os-type/coder_parameter.tf rename to modules/hcloud-os-type/coder_parameter.tf index eff7082..af001af 100644 --- a/hcloud-os-type/coder_parameter.tf +++ b/modules/hcloud-os-type/coder_parameter.tf @@ -12,8 +12,8 @@ data "coder_parameter" "os" { name = replace(option.value, "-", " ") value = option.value # the icon for rocky and alma needs to have a linux appended - icon = ( - "/icon/${split("-", option.value)[0] == "alma" || split("-", option.value)[0] == "rocky" ? + icon = ( + "/icon/${split("-", option.value)[0] == "alma" || split("-", option.value)[0] == "rocky" ? "${split("-", option.value)[0]}linux" : split("-", option.value)[0]}.svg" ) } diff --git a/modules/hcloud-os-type/data.tf b/modules/hcloud-os-type/data.tf new file mode 100644 index 0000000..70cef38 --- /dev/null +++ b/modules/hcloud-os-type/data.tf @@ -0,0 +1,11 @@ +data "hcloud_images" "images" { + include_deprecated = var.include_deprecated +} + +locals { + os_list = sort(distinct([ + for item in data.hcloud_images.images.images : item.name if + contains(var.os_flavor, item.os_flavor) && + item.type == "system" #make configurable + ])) +} diff --git a/hcloud-os-type/output.tf b/modules/hcloud-os-type/output.tf similarity index 100% rename from hcloud-os-type/output.tf rename to modules/hcloud-os-type/output.tf diff --git a/hcloud-os-type/provider.tf b/modules/hcloud-os-type/provider.tf similarity index 100% rename from hcloud-os-type/provider.tf rename to modules/hcloud-os-type/provider.tf diff --git a/hcloud-os-type/variables.tf b/modules/hcloud-os-type/variables.tf similarity index 95% rename from hcloud-os-type/variables.tf rename to modules/hcloud-os-type/variables.tf index 2d19afe..43e240c 100644 --- a/hcloud-os-type/variables.tf +++ b/modules/hcloud-os-type/variables.tf @@ -34,12 +34,11 @@ variable "default" { variable "os_flavor" { description = "List of instance os flavors to include." type = set(string) - default = [ - "ubuntu", #manatory or change default + default = [ + "ubuntu", #mandatory or change default "debian", "fedora", - "alma", - "rocky" + "alma" ] validation { condition = length(var.os_flavor) > 0 diff --git a/hcloud-region/coder_parameter.tf b/modules/hcloud-region/coder_parameter.tf similarity index 89% rename from hcloud-region/coder_parameter.tf rename to modules/hcloud-region/coder_parameter.tf index 130b060..539aeec 100644 --- a/hcloud-region/coder_parameter.tf +++ b/modules/hcloud-region/coder_parameter.tf @@ -12,7 +12,7 @@ data "coder_parameter" "region" { if anytrue([for d in var.regions : startswith(k, d)]) } content { - icon = try(var.custom_icons[option.key], option.value.icon) + icon = try(var.custom_icons[option.key], option.value.icon) name = option.value.name description = option.key value = option.key diff --git a/hcloud-region/output.tf b/modules/hcloud-region/output.tf similarity index 100% rename from hcloud-region/output.tf rename to modules/hcloud-region/output.tf diff --git a/hcloud-region/provider.tf b/modules/hcloud-region/provider.tf similarity index 100% rename from hcloud-region/provider.tf rename to modules/hcloud-region/provider.tf diff --git a/hcloud-region/variables.tf b/modules/hcloud-region/variables.tf similarity index 96% rename from hcloud-region/variables.tf rename to modules/hcloud-region/variables.tf index 484e314..ed71e76 100644 --- a/hcloud-region/variables.tf +++ b/modules/hcloud-region/variables.tf @@ -19,12 +19,13 @@ variable "default" { variable "regions" { description = "List of hcloud regions to include." type = list(string) - default = [ + default = [ "fsn1", "nbg1", "hel1", "ash", - "hil" + "hil", + "sin" ] } diff --git a/hcloud-region/zones.tf b/modules/hcloud-region/zones.tf similarity index 83% rename from hcloud-region/zones.tf rename to modules/hcloud-region/zones.tf index cf1337a..402ab79 100644 --- a/hcloud-region/zones.tf +++ b/modules/hcloud-region/zones.tf @@ -23,5 +23,10 @@ locals { name = "Hillsboro" icon = "/emojis/1f1fa-1f1f8.png" } + # Singapore + sin = { + name = "Singapore" + icon = "/emojis/1f1f8-1f1ec.png" + } } } diff --git a/provider.tf b/provider.tf index bb5301c..504eeaa 100644 --- a/provider.tf +++ b/provider.tf @@ -2,11 +2,11 @@ terraform { required_providers { coder = { source = "coder/coder" - version = "0.21.0" + version = "2.5.3" } hcloud = { source = "hetznercloud/hcloud" - version = "1.46.1" + version = "1.51.0" } } }