Skip to content

Commit 0367f30

Browse files
chapterjasonclaude
andcommitted
Add Coder Terraform module for web-shell
Wraps the web-shell feature's workspace-app wiring in a reusable terraform/web-shell/ module (agent_id, port, host, auth_token, install_version inputs) so workspace templates can drop it in without duplicating the coder_app / install block. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 6442056 commit 0367f30

2 files changed

Lines changed: 195 additions & 0 deletions

File tree

terraform/web-shell/main.tf

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
terraform {
2+
required_version = ">= 1.0"
3+
4+
required_providers {
5+
coder = {
6+
source = "coder/coder"
7+
version = ">= 2.5"
8+
}
9+
}
10+
}
11+
12+
variable "agent_id" {
13+
type = string
14+
description = "The ID of a Coder agent."
15+
}
16+
17+
variable "port" {
18+
type = number
19+
description = "The port to run web-shell on."
20+
default = 4000
21+
}
22+
23+
variable "host" {
24+
type = string
25+
description = "The bind address web-shell should listen on."
26+
default = "127.0.0.1"
27+
}
28+
29+
variable "auth_token" {
30+
type = string
31+
description = "Optional shared bearer token enforced by web-shell. Leave empty when fronted by Coder."
32+
default = ""
33+
sensitive = true
34+
}
35+
36+
variable "install_version" {
37+
type = string
38+
description = "Release tag of web-shell to install (e.g. 'v0.1.0'). Empty means latest."
39+
default = ""
40+
}
41+
42+
variable "log_path" {
43+
type = string
44+
description = "The path to log web-shell to."
45+
default = "/tmp/web-shell.log"
46+
}
47+
48+
variable "display_name" {
49+
type = string
50+
description = "The display name for the web-shell application."
51+
default = "web-shell"
52+
}
53+
54+
variable "slug" {
55+
type = string
56+
description = "The slug for the web-shell application."
57+
default = "web-shell"
58+
}
59+
60+
variable "subdomain" {
61+
type = bool
62+
description = <<-EOT
63+
Serve web-shell on its own subdomain. Required for the WebSocket /ws endpoint
64+
to work reliably behind path-based proxying.
65+
EOT
66+
default = true
67+
}
68+
69+
variable "share" {
70+
type = string
71+
default = "owner"
72+
validation {
73+
condition = contains(["owner", "authenticated", "public"], var.share)
74+
error_message = "Incorrect value. Please set either 'owner', 'authenticated', or 'public'."
75+
}
76+
}
77+
78+
variable "order" {
79+
type = number
80+
description = "The order determines the position of app in the UI presentation. The lowest order is shown first and apps with equal order are sorted by name (ascending order)."
81+
default = null
82+
}
83+
84+
variable "group" {
85+
type = string
86+
description = "The name of a group that this app belongs to."
87+
default = null
88+
}
89+
90+
variable "open_in" {
91+
type = string
92+
description = <<-EOT
93+
Determines where the app will be opened. Valid values are `"tab"` and `"slim-window" (default)`.
94+
`"tab"` opens in a new tab in the same browser window.
95+
`"slim-window"` opens a new browser window without navigation controls.
96+
EOT
97+
default = "slim-window"
98+
validation {
99+
condition = contains(["tab", "slim-window"], var.open_in)
100+
error_message = "The 'open_in' variable must be one of: 'tab', 'slim-window'."
101+
}
102+
}
103+
104+
resource "coder_script" "web-shell" {
105+
agent_id = var.agent_id
106+
display_name = "web-shell"
107+
icon = "/icon/terminal.svg"
108+
script = templatefile("${path.module}/run.sh", {
109+
VERSION : var.install_version,
110+
HOST : var.host,
111+
PORT : var.port,
112+
AUTH_TOKEN : var.auth_token,
113+
LOG_PATH : var.log_path,
114+
})
115+
run_on_start = true
116+
}
117+
118+
resource "coder_app" "web-shell" {
119+
agent_id = var.agent_id
120+
slug = var.slug
121+
display_name = var.display_name
122+
url = "http://localhost:${var.port}"
123+
icon = "/icon/terminal.svg"
124+
subdomain = var.subdomain
125+
share = var.share
126+
order = var.order
127+
group = var.group
128+
open_in = var.open_in
129+
130+
healthcheck {
131+
url = "http://localhost:${var.port}/api/sessions"
132+
interval = 5
133+
threshold = 6
134+
}
135+
}

terraform/web-shell/run.sh

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#!/usr/bin/env bash
2+
3+
BOLD='\033[0;1m'
4+
RESET='\033[0m'
5+
6+
function run_web_shell() {
7+
echo "👷 Running web-shell in the background..."
8+
echo "Check logs at ${LOG_PATH}!"
9+
HOST="${HOST}" PORT="${PORT}" AUTH_TOKEN="${AUTH_TOKEN}" \
10+
web-shell > "${LOG_PATH}" 2>&1 &
11+
}
12+
13+
# Resolve the version to install. Empty means latest.
14+
WANTED_VERSION="${VERSION}"
15+
if [ -z "$${WANTED_VERSION}" ]; then
16+
WANTED_VERSION=$(curl -fsSL https://api.github.com/repos/SoureCode/web-shell/releases/latest \
17+
| awk -F\" '/"tag_name":/ {print $4; exit}')
18+
fi
19+
WANTED_VERSION="$${WANTED_VERSION#v}"
20+
21+
if [ -z "$${WANTED_VERSION}" ]; then
22+
echo "Failed to resolve web-shell release tag."
23+
exit 1
24+
fi
25+
26+
# Install prereqs: tmux is required at runtime; build-essential + python3 cover
27+
# node-pty's native build step when a prebuilt binding isn't available.
28+
need=()
29+
for pkg in tmux build-essential python3; do
30+
dpkg -s "$pkg" >/dev/null 2>&1 || need+=("$pkg")
31+
done
32+
if [ $${#need[@]} -gt 0 ]; then
33+
printf "$${BOLD}Installing prereqs: $${need[*]}$${RESET}\n"
34+
sudo apt-get update
35+
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends "$${need[@]}"
36+
fi
37+
38+
# Install or upgrade web-shell if the installed version doesn't match.
39+
INSTALLED_VERSION=""
40+
if command -v web-shell >/dev/null 2>&1; then
41+
INSTALLED_VERSION=$(npm ls -g --depth=0 --json web-shell 2>/dev/null \
42+
| awk -F\" '/"version":/ {print $4; exit}')
43+
fi
44+
45+
if [ "$${INSTALLED_VERSION}" != "$${WANTED_VERSION}" ]; then
46+
printf "$${BOLD}Installing web-shell $${WANTED_VERSION}$${RESET}\n"
47+
TARBALL="https://github.com/SoureCode/web-shell/releases/download/v$${WANTED_VERSION}/web-shell-$${WANTED_VERSION}.tgz"
48+
if ! sudo -E env "PATH=$PATH" npm install -g "$${TARBALL}"; then
49+
echo "Failed to install web-shell $${WANTED_VERSION}"
50+
exit 1
51+
fi
52+
printf "🥳 web-shell $${WANTED_VERSION} installed\n\n"
53+
fi
54+
55+
# Make web-shell available via CODER_SCRIPT_BIN_DIR too, like code-server does.
56+
if [ -n "$CODER_SCRIPT_BIN_DIR" ] && [ ! -e "$CODER_SCRIPT_BIN_DIR/web-shell" ]; then
57+
ln -s "$(command -v web-shell)" "$CODER_SCRIPT_BIN_DIR/web-shell"
58+
fi
59+
60+
run_web_shell

0 commit comments

Comments
 (0)