diff --git a/.icons/linux.svg b/.icons/linux.svg new file mode 100644 index 000000000..6b558e7b7 --- /dev/null +++ b/.icons/linux.svg @@ -0,0 +1,438 @@ + + + Tux + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/registry/IamTaoChen/.images/avatar.png b/registry/IamTaoChen/.images/avatar.png new file mode 100644 index 000000000..f14100c4a Binary files /dev/null and b/registry/IamTaoChen/.images/avatar.png differ diff --git a/registry/IamTaoChen/README.md b/registry/IamTaoChen/README.md new file mode 100644 index 000000000..ed82ca0d9 --- /dev/null +++ b/registry/IamTaoChen/README.md @@ -0,0 +1,18 @@ +--- +display_name: "Tao Chen" +bio: "" +github: "IamTaoChen" +avatar: "./.images/avatar.png" +linkedin: "" +website: "" +support_email: "IamTaoChen@gmail.com" +status: "community" +--- + +# Tao Chen + +## Template + +### ssh-linux + +Provision an existing Linux system as a workspace by deploying the Coder agent via SSH with this example template. diff --git a/registry/IamTaoChen/templates/ssh-linux/README.md b/registry/IamTaoChen/templates/ssh-linux/README.md new file mode 100644 index 000000000..4ec625fbf --- /dev/null +++ b/registry/IamTaoChen/templates/ssh-linux/README.md @@ -0,0 +1,70 @@ +--- +display_name: Deploy Coder on existing Linux System +description: Provision an existing Linux system as a workspace by deploying the Coder agent via SSH with this example template. +icon: "../../../../.icons/linux.svg" +verified: false +tags: ["linux"] +--- + +# Deploy Coder on existing Linux system + +Provision an existing Linux system as a [Coder workspace](https://coder.com/docs/workspaces) by deploying the Coder agent via SSH with this example template. + +## Prerequisites + +### Authentication + +This template assumes you have SSH access to the target Linux system. You can use either password-based authentication or an SSH private key. Ensure the target system allows SSH connections and has basic utilities like `bash` installed. The user account specified must have sufficient permissions to execute scripts and manage processes in their home directory. + +For more details on SSH setup, consult your Linux distribution's documentation or standard SSH guides. + +## Architecture + +This template deploys the following: + +- A Coder agent configured for Linux (amd64 architecture). +- Conditional parameters for SSH authentication (password or key). +- A selection of applications (e.g., VS Code Desktop, VS Code Web, Cursor) that can be enabled via multi-select. +- `null_resource` blocks to handle workspace start/stop: + - On start: Connects via SSH, creates a cache directory, writes and executes the agent's init script in the background, and logs the process ID. + - On stop: Connects via SSH, kills the agent process if running, and removes the cache directory. +- Optional modules for additional apps like `coder-login`, `cursor`, and `vscode-web`, which are provisioned only if selected and when the workspace starts. + +This setup does not provision new infrastructure; it remotely deploys and manages the Coder agent on your existing Linux host. Files and configurations in the user's home directory persist across restarts, but the agent is stopped and cleaned up on workspace stop. + +### Persistent Agent + +The agent is ephemeral by design (started on workspace start, stopped on stop). If you need a persistently running agent, modify the template to remove the stop logic or run the agent manually on the host. + +## Security Considerations + +Warning: This template stores SSH credentials (password or private key) in the Terraform state file and passes them as environment variables during deployment. In production environments, this can introduce security risks, as the state file contains sensitive information in plain text and may be accessible if not properly secured. + +## Parameters + +The template includes the following configurable parameters: + +- **Host**: The remote hostname, IPv4, or IPv6 address of the Linux system (default: `192.168.1.1`). Must match the regex `^[a-zA-Z0-9:.%\\-]+$`. +- **Username**: The SSH username for connecting to the host (default: the workspace owner's name). +- **SSH Auth Type**: The authentication method—either "password" or "SSH Key" (default: "password"). +- **SSH Password**: (Shown only if "password" is selected) The password for SSH login. Input is masked. +- **SSH Private Key**: (Shown only if "SSH Key" is selected) The private key for SSH login, provided as a textarea. Input is masked. +- **Port**: The SSH port on the remote host (default: `22`). Must be between 1 and 65535. +- **Apps**: A multi-select list of applications to include in the workspace (default: `["VS Code Desktop"]`). Options: "VS Code Desktop", "VS Code Web", "Cursor". + +## Usage + +1. Create a new workspace in Coder using this template. +2. Fill in the parameters with your Linux system's details. +3. Start the workspace—Coder will connect via SSH and deploy the agent. +4. Access the workspace through the Coder dashboard. Selected apps (e.g., VS Code) will be available. +5. On stop, the agent process is terminated and cleaned up. + +## Troubleshooting + +- **SSH Connection Issues**: Verify the host, port, username, and credentials. Check firewall rules and SSH server status on the target system. Review the debug log at `~/.coder//debug.log` on the remote host. +- **Agent Not Starting**: Inspect the log file at `~/.coder//coder.log` on the remote host for errors. +- **App Not Appearing**: Ensure the app is selected in parameters and the workspace is restarted if changes are made. +- **Validation Errors**: Parameters like host and port have built-in validations—ensure inputs match the requirements. + +For more advanced customization, refer to the [Coder Terraform provider documentation](https://registry.terraform.io/providers/coder/coder/latest/docs). diff --git a/registry/IamTaoChen/templates/ssh-linux/main.tf b/registry/IamTaoChen/templates/ssh-linux/main.tf new file mode 100644 index 000000000..90b2b485e --- /dev/null +++ b/registry/IamTaoChen/templates/ssh-linux/main.tf @@ -0,0 +1,274 @@ +terraform { + required_providers { + coder = { + source = "coder/coder" + version = ">= 2.4.0" + } + random = { + source = "hashicorp/random" + version = ">= 3.6" + } + } +} + +provider "coder" {} +data "coder_workspace" "me" {} +data "coder_workspace_owner" "me" {} + + +data "coder_parameter" "host" { + description = "Remote Host or IP" + display_name = "Host" + name = "host" + type = "string" + default = "192.168.1.1" + mutable = false + order = 1 + validation { + regex = "^[a-zA-Z0-9:.%\\-]+$" + error = "Please enter a valid hostname, IPv4, or IPv6 address. Examples: example.com, 192.168.1.1, or fe80::1" + } +} + +data "coder_parameter" "username" { + default = data.coder_workspace_owner.me.name + description = "SSH Username" + display_name = "Username" + name = "username" + mutable = false + order = 2 +} + +data "coder_parameter" "auth_type" { + name = "auth_type" + display_name = "SSH Auth Type" + description = "Authentication method for SSH" + type = "string" + + form_type = "dropdown" + default = "password" + mutable = true + order = 3 + option { + name = "password" + value = "password" + } + + option { + name = "SSH Key" + value = "ssh_key" + } +} + +data "coder_parameter" "ssh_password" { + count = data.coder_parameter.auth_type.value == "password" ? 1 : 0 + name = "ssh_password" + display_name = "SSH Password" + description = "Password for SSH login" + type = "string" + mutable = true + styling = jsonencode({ + mask_input = true + }) + order = 4 +} + +data "coder_parameter" "ssh_key" { + count = data.coder_parameter.auth_type.value == "ssh_key" ? 1 : 0 + name = "ssh_key" + display_name = "SSH Private Key" + description = "Paste SSH private key" + type = "string" + mutable = true + form_type = "textarea" + styling = jsonencode({ + mask_input = true + }) + order = 4 +} + + +data "coder_parameter" "port" { + default = 22 + description = "SSH Port" + display_name = "Port" + name = "port" + type = "number" + mutable = true + order = 5 + validation { + min = 1 + max = 65535 + error = "Port must be between 1 and 65535" + } +} + +data "coder_parameter" "apps" { + name = "apps" + display_name = "Choose any APPs for your workspace." + type = "list(string)" + form_type = "multi-select" + mutable = true + default = jsonencode(["VS Code Desktop"]) + dynamic "option" { + for_each = local.apps_candidate + content { + name = option.value + value = option.value + } + } +} + +locals { + username = data.coder_parameter.username.value + home_dir = "/home/${lower(local.username)}" + coder_cache_dir = "${local.home_dir}/.coder/${data.coder_workspace.me.id}" + agent_id_file = "${local.coder_cache_dir}/agent.id" + use_password = data.coder_parameter.auth_type.value == "password" + use_key = data.coder_parameter.auth_type.value == "ssh_key" + ssh_password = local.use_password ? data.coder_parameter.ssh_password[0].value : null + ssh_private_key = local.use_key ? data.coder_parameter.ssh_key[0].value : null + apps_candidate = ["VS Code Desktop", "VS Code Web", "Cursor"] + apps_selected = (can(data.coder_parameter.apps.value) && data.coder_parameter.apps.value != "") ? jsondecode(data.coder_parameter.apps.value) : [] +} + +resource "random_integer" "vs_code_port" { + min = 54000 + max = 55999 +} + +resource "coder_agent" "main" { + os = "linux" + arch = "amd64" + + startup_script = <<-EOT + #!/bin/bash + set -euo pipefail + EOT + + env = { + GIT_AUTHOR_NAME = coalesce(data.coder_workspace_owner.me.full_name, data.coder_workspace_owner.me.name) + GIT_AUTHOR_EMAIL = "${data.coder_workspace_owner.me.email}" + GIT_COMMITTER_NAME = coalesce(data.coder_workspace_owner.me.full_name, data.coder_workspace_owner.me.name) + GIT_COMMITTER_EMAIL = "${data.coder_workspace_owner.me.email}" + } + + display_apps { + port_forwarding_helper = true + vscode = contains(local.apps_selected, "VS Code Desktop") + vscode_insiders = false + web_terminal = true + ssh_helper = true + } + + metadata { + key = "cpu" + display_name = "CPU Usage" + interval = 5 + timeout = 5 + script = "coder stat cpu" + } + metadata { + key = "memory" + display_name = "Memory Usage" + interval = 5 + timeout = 5 + script = "coder stat mem" + } + metadata { + key = "disk" + display_name = "Home Disk Usage" + interval = 600 + timeout = 30 + script = "coder stat disk --path ${lower(local.home_dir)}" + } +} + +resource "null_resource" "deploy_coder_agent" { + count = data.coder_workspace.me.start_count + + triggers = { + init_script = sha256(coder_agent.main.init_script) + token = coder_agent.main.token + } + + connection { + type = "ssh" + host = data.coder_parameter.host.value + user = data.coder_parameter.username.value + port = data.coder_parameter.port.value + password = local.ssh_password + private_key = local.ssh_private_key + timeout = "5m" + } + + provisioner "remote-exec" { + inline = [ + "mkdir -p ${local.coder_cache_dir}", + "coder_sh=${local.coder_cache_dir}/coder.sh", + "log_file=${local.coder_cache_dir}/coder.log", + "cat > $coder_sh << 'EOF'", + "${coder_agent.main.init_script}", + "EOF", + "chmod +x $coder_sh", + "echo \"$(date) : create $coder_sh\" >> ${local.coder_cache_dir}/debug.log", + "nohup env CODER_AGENT_TOKEN='${coder_agent.main.token}' $coder_sh > $log_file 2>&1 &", + "echo $! > ${local.agent_id_file}", + "echo \"$(date) : run $coder_sh and log at $log_file\" >> ${local.coder_cache_dir}/debug.log", + ] + } +} + +resource "null_resource" "coder_stop" { + count = (try(data.coder_workspace.me.start_count, 1) > 0 ? 0 : 1) + + connection { + type = "ssh" + host = data.coder_parameter.host.value + user = data.coder_parameter.username.value + port = data.coder_parameter.port.value + password = local.ssh_password + private_key = local.ssh_private_key + timeout = "5m" + } + + provisioner "remote-exec" { + inline = [ + "PID_FILE=${local.agent_id_file}", + "if [ -f \"$PID_FILE\" ]; then", + " PID=$(cat \"$PID_FILE\")", + " if kill -0 \"$PID\" 2>/dev/null; then", + " kill -TERM \"$PID\" || true", + " sleep 5", + " kill -KILL \"$PID\" || true", + " fi", + " rm -r ${local.coder_cache_dir}", + "fi", + ] + } +} + + +module "coder-login" { + count = data.coder_workspace.me.start_count + source = "registry.coder.com/coder/coder-login/coder" + version = "1.1.1" + agent_id = coder_agent.main.id +} + +module "cursor" { + count = contains(local.apps_selected, "Cursor") ? data.coder_workspace.me.start_count : 0 + source = "registry.coder.com/coder/cursor/coder" + version = "1.4.0" + agent_id = coder_agent.main.id +} + +module "vscode-web" { + count = contains(local.apps_selected, "VS Code Web") ? data.coder_workspace.me.start_count : 0 + source = "registry.coder.com/coder/vscode-web/coder" + version = "1.4.3" + agent_id = coder_agent.main.id + folder = local.home_dir + port = random_integer.vs_code_port.result + accept_license = true +}