docs: restructure docs (#14421)

Closes #13434 
Supersedes #14182

---------

Co-authored-by: Ethan <39577870+ethanndickson@users.noreply.github.com>
Co-authored-by: Ethan Dickson <ethan@coder.com>
Co-authored-by: Ben Potter <ben@coder.com>
Co-authored-by: Stephen Kirby <58410745+stirby@users.noreply.github.com>
Co-authored-by: Stephen Kirby <me@skirby.dev>
Co-authored-by: EdwardAngert <17991901+EdwardAngert@users.noreply.github.com>
Co-authored-by: Edward Angert <EdwardAngert@users.noreply.github.com>
This commit is contained in:
Muhammad Atif Ali
2024-10-05 08:52:04 -07:00
committed by GitHub
parent 288df75686
commit 419eba5fb6
298 changed files with 5009 additions and 3889 deletions

View File

@ -0,0 +1,148 @@
# Agent metadata
![agent-metadata](../../../images/admin/templates/agent-metadata-ui.png)
You can show live operational metrics to workspace users with agent metadata. It
is the dynamic complement of [resource metadata](./resource-metadata.md).
You specify agent metadata in the
[`coder_agent`](https://registry.terraform.io/providers/coder/coder/latest/docs/resources/agent).
## Examples
All of these examples use
[heredoc strings](https://developer.hashicorp.com/terraform/language/expressions/strings#heredoc-strings)
for the script declaration. With heredoc strings, you can script without messy
escape codes, just as if you were working in your terminal.
Some of the examples use the [`coder stat`](../../../reference/cli/stat.md)
command. This is useful for determining CPU and memory usage of the VM or
container that the workspace is running in, which is more accurate than resource
usage about the workspace's host.
Here's a standard set of metadata snippets for Linux agents:
```tf
resource "coder_agent" "main" {
os = "linux"
...
metadata {
display_name = "CPU Usage"
key = "cpu"
# Uses the coder stat command to get container CPU usage.
script = "coder stat cpu"
interval = 1
timeout = 1
}
metadata {
display_name = "Memory Usage"
key = "mem"
# Uses the coder stat command to get container memory usage in GiB.
script = "coder stat mem --prefix Gi"
interval = 1
timeout = 1
}
metadata {
display_name = "CPU Usage (Host)"
key = "cpu_host"
# calculates CPU usage by summing the "us", "sy" and "id" columns of
# top.
script = <<EOT
top -bn1 | awk 'FNR==3 {printf "%2.0f%%", $2+$3+$4}'
EOT
interval = 1
timeout = 1
}
metadata {
display_name = "Memory Usage (Host)"
key = "mem_host"
script = <<EOT
free | awk '/^Mem/ { printf("%.0f%%", $4/$2 * 100.0) }'
EOT
interval = 1
timeout = 1
}
metadata {
display_name = "Disk Usage"
key = "disk"
script = "df -h | awk '$6 ~ /^\\/$/ { print $5 }'"
interval = 1
timeout = 1
}
metadata {
display_name = "Load Average"
key = "load"
script = <<EOT
awk '{print $1,$2,$3}' /proc/loadavg
EOT
interval = 1
timeout = 1
}
}
```
## Useful utilities
You can also show agent metadata for information about the workspace's host.
[top](https://manpages.ubuntu.com/manpages/jammy/en/man1/top.1.html) is
available in most Linux distributions and provides virtual memory, CPU and IO
statistics. Running `top` produces output that looks like:
```text
%Cpu(s): 65.8 us, 4.4 sy, 0.0 ni, 29.3 id, 0.3 wa, 0.0 hi, 0.2 si, 0.0 st
MiB Mem : 16009.0 total, 493.7 free, 4624.8 used, 10890.5 buff/cache
MiB Swap: 0.0 total, 0.0 free, 0.0 used. 11021.3 avail Mem
```
[vmstat](https://manpages.ubuntu.com/manpages/jammy/en/man8/vmstat.8.html) is
available in most Linux distributions and provides virtual memory, CPU and IO
statistics. Running `vmstat` produces output that looks like:
```text
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
0 0 19580 4781680 12133692 217646944 0 2 4 32 1 0 1 1 98 0 0
```
[dstat](https://manpages.ubuntu.com/manpages/jammy/man1/dstat.1.html) is
considerably more parseable than `vmstat` but often not included in base images.
It is easily installed by most package managers under the name `dstat`. The
output of running `dstat 1 1` looks like:
```text
--total-cpu-usage-- -dsk/total- -net/total- ---paging-- ---system--
usr sys idl wai stl| read writ| recv send| in out | int csw
1 1 98 0 0|3422k 25M| 0 0 | 153k 904k| 123k 174k
```
## Managing the database load
Agent metadata can generate a significant write load and overwhelm your Coder
database if you're not careful. The approximate writes per second can be
calculated using the formula:
```text
(metadata_count * num_running_agents * 2) / metadata_avg_interval
```
For example, let's say you have
- 10 running agents
- each with 6 metadata snippets
- with an average interval of 4 seconds
You can expect `(10 * 6 * 2) / 4`, or 30 writes per second.
One of the writes is to the `UNLOGGED` `workspace_agent_metadata` table and the
other to the `NOTIFY` query that enables live stats streaming in the UI.
## Next Steps
- [Resource metadata](./resource-metadata.md)
- [Parameters](./parameters.md)

View File

@ -0,0 +1,461 @@
# Docker in Workspaces
There are a few ways to run Docker within container-based Coder workspaces.
| Method | Description | Limitations |
| ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Sysbox container runtime](#sysbox-container-runtime) | Install the Sysbox runtime on your Kubernetes nodes or Docker host(s) for secure docker-in-docker and systemd-in-docker. Works with GKE, EKS, AKS, Docker. | Requires [compatible nodes](https://github.com/nestybox/sysbox#host-requirements). [Limitations](https://github.com/nestybox/sysbox/blob/master/docs/user-guide/limitations.md) |
| [Envbox](#envbox) | A container image with all the packages necessary to run an inner Sysbox container. Removes the need to setup sysbox-runc on your nodes. Works with GKE, EKS, AKS. | Requires running the outer container as privileged (the inner container that acts as the workspace is locked down). Requires compatible [nodes](https://github.com/nestybox/sysbox/blob/master/docs/distro-compat.md#sysbox-distro-compatibility). |
| [Rootless Podman](#rootless-podman) | Run Podman inside Coder workspaces. Does not require a custom runtime or privileged containers. Works with GKE, EKS, AKS, RKE, OpenShift | Requires smarter-device-manager for FUSE mounts. [See all](https://github.com/containers/podman/blob/main/rootless.md#shortcomings-of-rootless-podman) |
| [Privileged docker sidecar](#privileged-sidecar-container) | Run Docker as a privileged sidecar container. | Requires a privileged container. Workspaces can break out to root on the host machine. |
## Sysbox container runtime
The [Sysbox](https://github.com/nestybox/sysbox) container runtime allows
unprivileged users to run system-level applications, such as Docker, securely
from the workspace containers. Sysbox requires a
[compatible Linux distribution](https://github.com/nestybox/sysbox/blob/master/docs/distro-compat.md)
to implement these security features. Sysbox can also be used to run systemd
inside Coder workspaces. See [Systemd in Docker](#systemd-in-docker).
### Use Sysbox in Docker-based templates
After [installing Sysbox](https://github.com/nestybox/sysbox#installation) on
the Coder host, modify your template to use the sysbox-runc runtime:
```tf
resource "docker_container" "workspace" {
# ...
name = "coder-${data.coder_workspace.me.owner}-${lower(data.coder_workspace.me.name)}"
image = "codercom/enterprise-base:ubuntu"
env = ["CODER_AGENT_TOKEN=${coder_agent.main.token}"]
command = ["sh", "-c", coder_agent.main.init_script]
# Use the Sysbox container runtime (required)
runtime = "sysbox-runc"
}
resource "coder_agent" "main" {
arch = data.coder_provisioner.me.arch
os = "linux"
startup_script = <<EOF
#!/bin/sh
# Start Docker
sudo dockerd &
# ...
EOF
}
```
### Use Sysbox in Kubernetes-based templates
After
[installing Sysbox on Kubernetes](https://github.com/nestybox/sysbox/blob/master/docs/user-guide/install-k8s.md),
modify your template to use the sysbox-runc RuntimeClass. This requires the
Kubernetes Terraform provider version 2.16.0 or greater.
```tf
terraform {
required_providers {
coder = {
source = "coder/coder"
}
kubernetes = {
source = "hashicorp/kubernetes"
version = "2.16.0"
}
}
}
variable "workspaces_namespace" {
default = "coder-namespace"
}
data "coder_workspace" "me" {}
resource "coder_agent" "main" {
os = "linux"
arch = "amd64"
dir = "/home/coder"
startup_script = <<EOF
#!/bin/sh
# Start Docker
sudo dockerd &
# ...
EOF
}
resource "kubernetes_pod" "dev" {
count = data.coder_workspace.me.start_count
metadata {
name = "coder-${data.coder_workspace.me.owner}-${data.coder_workspace.me.name}"
namespace = var.workspaces_namespace
annotations = {
"io.kubernetes.cri-o.userns-mode" = "auto:size=65536"
}
}
spec {
runtime_class_name = "sysbox-runc"
# Use the Sysbox container runtime (required)
security_context {
run_as_user = 1000
fs_group = 1000
}
container {
name = "dev"
env {
name = "CODER_AGENT_TOKEN"
value = coder_agent.main.token
}
image = "codercom/enterprise-base:ubuntu"
command = ["sh", "-c", coder_agent.main.init_script]
}
}
}
```
## Envbox
[Envbox](https://github.com/coder/envbox) is an image developed and maintained
by Coder that bundles the sysbox runtime. It works by starting an outer
container that manages the various sysbox daemons and spawns an unprivileged
inner container that acts as the user's workspace. The inner container is able
to run system-level software similar to a regular virtual machine (e.g.
`systemd`, `dockerd`, etc). Envbox offers the following benefits over running
sysbox directly on the nodes:
- No custom runtime installation or management on your Kubernetes nodes.
- No limit to the number of pods that run envbox.
Some drawbacks include:
- The outer container must be run as privileged
- Note: the inner container is _not_ privileged. For more information on the
security of sysbox containers see sysbox's
[official documentation](https://github.com/nestybox/sysbox/blob/master/docs/user-guide/security.md).
- Initial workspace startup is slower than running `sysbox-runc` directly on the
nodes. This is due to `envbox` having to pull the image to its own Docker
cache on its initial startup. Once the image is cached in `envbox`, startup
performance is similar.
Envbox requires the same kernel requirements as running sysbox directly on the
nodes. Refer to sysbox's
[compatibility matrix](https://github.com/nestybox/sysbox/blob/master/docs/distro-compat.md#sysbox-distro-compatibility)
to ensure your nodes are compliant.
To get started with `envbox` check out the
[starter template](https://github.com/coder/coder/tree/main/examples/templates/envbox)
or visit the [repo](https://github.com/coder/envbox).
### Authenticating with a Private Registry
Authenticating with a private container registry can be done by referencing the
credentials via the `CODER_IMAGE_PULL_SECRET` environment variable. It is
encouraged to populate this
[environment variable](https://kubernetes.io/docs/tasks/inject-data-application/distribute-credentials-secure/#define-container-environment-variables-using-secret-data)
by using a Kubernetes
[secret](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/#registry-secret-existing-credentials).
Refer to your container registry documentation to understand how to best create
this secret.
The following shows a minimal example using a the JSON API key from a GCP
service account to pull a private image:
```bash
# Create the secret
$ kubectl create secret docker-registry <name> \
--docker-server=us.gcr.io \
--docker-username=_json_key \
--docker-password="$(cat ./json-key-file.yaml)" \
--docker-email=<service-account-email>
```
```tf
env {
name = "CODER_IMAGE_PULL_SECRET"
value_from {
secret_key_ref {
name = "<name>"
key = ".dockerconfigjson"
}
}
}
```
## Rootless podman
[Podman](https://docs.podman.io/en/latest/) is Docker alternative that is
compatible with OCI containers specification. which can run rootless inside
Kubernetes pods. No custom RuntimeClass is required.
Before using Podman, please review the following documentation:
- [Basic setup and use of Podman in a rootless environment](https://github.com/containers/podman/blob/main/docs/tutorials/rootless_tutorial.md)
- [Shortcomings of Rootless Podman](https://github.com/containers/podman/blob/main/rootless.md#shortcomings-of-rootless-podman)
1. Enable
[smart-device-manager](https://gitlab.com/arm-research/smarter/smarter-device-manager#enabling-access)
to securely expose a FUSE devices to pods.
```shell
cat <<EOF | kubectl create -f -
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fuse-device-plugin-daemonset
namespace: kube-system
spec:
selector:
matchLabels:
name: fuse-device-plugin-ds
template:
metadata:
labels:
name: fuse-device-plugin-ds
spec:
hostNetwork: true
containers:
- name: fuse-device-plugin-ctr
image: soolaugust/fuse-device-plugin:v1.0
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
volumeMounts:
- name: device-plugin
mountPath: /var/lib/kubelet/device-plugins
volumes:
- name: device-plugin
hostPath:
path: /var/lib/kubelet/device-plugins
imagePullSecrets:
- name: registry-secret
EOF
```
2. Be sure to label your nodes to enable smarter-device-manager:
```shell
kubectl get nodes
kubectl label nodes --all smarter-device-manager=enabled
```
> ⚠️ **Warning**: If you are using a managed Kubernetes distribution (e.g.
> AKS, EKS, GKE), be sure to set node labels via your cloud provider.
> Otherwise, your nodes may drop the labels and break podman functionality.
3. For systems running SELinux (typically Fedora-, CentOS-, and Red Hat-based
systems), you might need to disable SELinux or set it to permissive mode.
4. Use this
[kubernetes-with-podman](https://github.com/coder/community-templates/tree/main/kubernetes-podman)
example template, or make your own.
```shell
echo "kubernetes-with-podman" | coder templates init
cd ./kubernetes-with-podman
coder templates create
```
> For more information around the requirements of rootless podman pods, see:
> [How to run Podman inside of Kubernetes](https://www.redhat.com/sysadmin/podman-inside-kubernetes)
## Privileged sidecar container
A
[privileged container](https://docs.docker.com/engine/containers/run/#runtime-privilege-and-linux-capabilities)
can be added to your templates to add docker support. This may come in handy if
your nodes cannot run Sysbox.
> ⚠️ **Warning**: This is insecure. Workspaces will be able to gain root access
> to the host machine.
### Use a privileged sidecar container in Docker-based templates
```tf
resource "coder_agent" "main" {
os = "linux"
arch = "amd64"
}
resource "docker_network" "private_network" {
name = "network-${data.coder_workspace.me.id}"
}
resource "docker_container" "dind" {
image = "docker:dind"
privileged = true
name = "dind-${data.coder_workspace.me.id}"
entrypoint = ["dockerd", "-H", "tcp://0.0.0.0:2375"]
networks_advanced {
name = docker_network.private_network.name
}
}
resource "docker_container" "workspace" {
count = data.coder_workspace.me.start_count
image = "codercom/enterprise-base:ubuntu"
name = "dev-${data.coder_workspace.me.id}"
command = ["sh", "-c", coder_agent.main.init_script]
env = [
"CODER_AGENT_TOKEN=${coder_agent.main.token}",
"DOCKER_HOST=${docker_container.dind.name}:2375"
]
networks_advanced {
name = docker_network.private_network.name
}
}
```
### Use a privileged sidecar container in Kubernetes-based templates
```tf
terraform {
required_providers {
coder = {
source = "coder/coder"
}
kubernetes = {
source = "hashicorp/kubernetes"
version = "2.16.0"
}
}
}
variable "workspaces_namespace" {
default = "coder-namespace"
}
data "coder_workspace" "me" {}
resource "coder_agent" "main" {
os = "linux"
arch = "amd64"
}
resource "kubernetes_pod" "main" {
count = data.coder_workspace.me.start_count
metadata {
name = "coder-${data.coder_workspace.me.owner}-${data.coder_workspace.me.name}"
namespace = var.namespace
}
spec {
# Run a privileged dind (Docker in Docker) container
container {
name = "docker-sidecar"
image = "docker:dind"
security_context {
privileged = true
run_as_user = 0
}
command = ["dockerd", "-H", "tcp://127.0.0.1:2375"]
}
container {
name = "dev"
image = "codercom/enterprise-base:ubuntu"
command = ["sh", "-c", coder_agent.main.init_script]
security_context {
run_as_user = "1000"
}
env {
name = "CODER_AGENT_TOKEN"
value = coder_agent.main.token
}
# Use the Docker daemon in the "docker-sidecar" container
env {
name = "DOCKER_HOST"
value = "localhost:2375"
}
}
}
}
```
## Systemd in Docker
Additionally, [Sysbox](https://github.com/nestybox/sysbox) can be used to give
workspaces full `systemd` capabilities.
After
[installing Sysbox on Kubernetes](https://github.com/nestybox/sysbox/blob/master/docs/user-guide/install-k8s.md),
modify your template to use the sysbox-runc RuntimeClass. This requires the
Kubernetes Terraform provider version 2.16.0 or greater.
```tf
terraform {
required_providers {
coder = {
source = "coder/coder"
}
kubernetes = {
source = "hashicorp/kubernetes"
version = "2.16.0"
}
}
}
variable "workspaces_namespace" {
default = "coder-namespace"
}
data "coder_workspace" "me" {}
resource "coder_agent" "main" {
os = "linux"
arch = "amd64"
dir = "/home/coder"
}
resource "kubernetes_pod" "dev" {
count = data.coder_workspace.me.start_count
metadata {
name = "coder-${data.coder_workspace.me.owner}-${data.coder_workspace.me.name}"
namespace = var.workspaces_namespace
annotations = {
"io.kubernetes.cri-o.userns-mode" = "auto:size=65536"
}
}
spec {
# Use Sysbox container runtime (required)
runtime_class_name = "sysbox-runc"
# Run as root in order to start systemd (required)
security_context {
run_as_user = 0
fs_group = 0
}
container {
name = "dev"
env {
name = "CODER_AGENT_TOKEN"
value = coder_agent.main.token
}
image = "codercom/enterprise-base:ubuntu"
command = ["sh", "-c", <<EOF
# Start the Coder agent as the "coder" user
# once systemd has started up
sudo -u coder --preserve-env=CODER_AGENT_TOKEN /bin/bash -- <<-' EOT' &
while [[ ! $(systemctl is-system-running) =~ ^(running|degraded) ]]
do
echo "Waiting for system to start... $(systemctl is-system-running)"
sleep 2
done
${coder_agent.main.init_script}
EOT
exec /sbin/init
EOF
]
}
}
}
```

View File

@ -0,0 +1,96 @@
# External Authentication
Coder integrates with any OpenID Connect provider to automate away the need for
developers to authenticate with external services within their workspace. This
can be used to authenticate with git providers, private registries, or any other
service that requires authentication.
## External Auth Providers
External auth providers are configured using environment variables in the Coder
Control Plane. See
## Git Providers
When developers use `git` inside their workspace, they are prompted to
authenticate. After that, Coder will store and refresh tokens for future
operations.
<video autoplay playsinline loop>
<source src="https://github.com/coder/coder/blob/main/site/static/external-auth.mp4?raw=true" type="video/mp4">
Your browser does not support the video tag.
</video>
### Require git authentication in templates
If your template requires git authentication (e.g. running `git clone` in the
[startup_script](https://registry.terraform.io/providers/coder/coder/latest/docs/resources/agent#startup_script)),
you can require users authenticate via git prior to creating a workspace:
![Git authentication in template](../../../images/admin/git-auth-template.png)
### Native git authentication will auto-refresh tokens
<blockquote class="info">
<p>
This is the preferred authentication method.
</p>
</blockquote>
By default, the coder agent will configure native `git` authentication via the
`GIT_ASKPASS` environment variable. Meaning, with no additional configuration,
external authentication will work with native `git` commands.
To check the auth token being used **from inside a running workspace**, run:
```shell
# If the exit code is non-zero, then the user is not authenticated with the
# external provider.
coder external-auth access-token <external-auth-id>
```
Note: Some IDE's override the `GIT_ASKPASS` environment variable and need to be
configured.
**VSCode**
Use the
[Coder](https://marketplace.visualstudio.com/items?itemName=coder.coder-remote)
extension to automatically configure these settings for you!
Otherwise, you can manually configure the following settings:
- Set `git.terminalAuthentication` to `false`
- Set `git.useIntegratedAskPass` to `false`
### Hard coded tokens do not auto-refresh
If the token is required to be inserted into the workspace, for example
[GitHub cli](https://cli.github.com/), the auth token can be inserted from the
template. This token will not auto-refresh. The following example will
authenticate via GitHub and auto-clone a repo into the `~/coder` directory.
```tf
data "coder_external_auth" "github" {
# Matches the ID of the external auth provider in Coder.
id = "github"
}
resource "coder_agent" "dev" {
os = "linux"
arch = "amd64"
dir = "~/coder"
env = {
GITHUB_TOKEN : data.coder_external_auth.github.access_token
}
startup_script = <<EOF
if [ ! -d ~/coder ]; then
git clone https://github.com/coder/coder
fi
EOF
}
```
See the
[Terraform provider documentation](https://registry.terraform.io/providers/coder/coder/latest/docs/data-sources/external_auth)
for all available options.

View File

@ -0,0 +1,80 @@
# Icons
Coder uses icons in several places, including ones that can be configured
throughout the app, or specified in your Terraform. They're specified by a URL,
which can be to an image hosted on a CDN of your own, or one of the icons that
come bundled with your Coder deployment.
- **Template Icons**:
- Make templates and workspaces visually recognizable with a relevant or
memorable icon
- [**Terraform**](https://registry.terraform.io/providers/coder/coder/latest/docs):
- [`coder_app`](https://registry.terraform.io/providers/coder/coder/latest/docs/resources/app#icon)
- [`coder_parameter`](https://registry.terraform.io/providers/coder/coder/latest/docs/data-sources/parameter#icon)
and
[`option`](https://registry.terraform.io/providers/coder/coder/latest/docs/data-sources/parameter#nested-schema-for-option)
blocks
- [`coder_script`](https://registry.terraform.io/providers/coder/coder/latest/docs/resources/script#icon)
- [`coder_metadata`](https://registry.terraform.io/providers/coder/coder/latest/docs/resources/metadata#icon)
These can all be configured to use an icon by setting the `icon` field.
```tf
data "coder_parameter" "my_parameter" {
icon = "/icon/coder.svg"
option {
icon = "/emojis/1f3f3-fe0f-200d-26a7-fe0f.png"
}
}
```
- [**Authentication Providers**](https://coder.com/docs/admin/external-auth):
- Use icons for external authentication providers to make them recognizable.
You can set an icon for each provider by setting the
`CODER_EXTERNAL_AUTH_X_ICON` environment variable, where `X` is the number
of the provider.
```env
CODER_EXTERNAL_AUTH_0_ICON=/icon/github.svg
CODER_EXTERNAL_AUTH_1_ICON=/icon/google.svg
```
- [**Support Links**](../../setup/appearance.md#support-links):
- Use icons for support links to make them recognizable. You can set the
`icon` field for each link in `CODER_SUPPORT_LINKS` array.
## Bundled icons
Coder is distributed with a bundle of icons for popular cloud providers and
programming languages. You can see all of the icons (or suggest new ones) in our
repository on
[GitHub](https://github.com/coder/coder/tree/main/site/static/icon).
You can also view the entire list, with search and previews, by navigating to
/icons on your Coder deployment. E.g. [https://coder.example.com/icons](#). This
can be particularly useful in airgapped deployments.
![The icon gallery](../../../images/icons-gallery.png)
## External icons
You can use any image served over HTTPS as an icon, by specifying the full URL
of the image. We recommend that you use a CDN that you control, but it can be
served from any source that you trust.
You can also embed an image by using data: URLs.
- Only the https: and data: protocols are supported in icon URLs (not http:)
- Be careful when using images hosted by someone else; they might disappear or
change!
- Be careful when using data: URLs. They can get rather large, and can
negatively impact loading times for pages and queries they appear in. Only use
them for very small icons that compress well.

View File

@ -0,0 +1,93 @@
# Extending templates
There are a variety of Coder-native features to extend the configuration of your
development environments. Many of the following features are defined in your
templates using the
[Coder Terraform provider](https://registry.terraform.io/providers/coder/coder/latest/docs).
The provider docs will provide code examples for usage; alternatively, you can
view our
[example templates](https://github.com/coder/coder/tree/main/examples/templates)
to get started.
## Workspace agents
For users to connect to a workspace, the template must include a
[`coder_agent`](https://registry.terraform.io/providers/coder/coder/latest/docs/resources/agent).
The associated agent will facilitate
[workspace connections](../../../user-guides/workspace-access/index.md) via SSH,
port forwarding, and IDEs. The agent may also display real-time
[workspace metadata](./agent-metadata.md) like resource usage.
```tf
resource "coder_agent" "dev" {
os = "linux"
arch = "amd64"
dir = "/workspace"
display_apps {
vscode = true
}
}
```
You can also leverage [resource metadata](./resource-metadata.md) to display
static resource information from your template.
Templates must include some computational resource to start the agent. All
processes on the workspace are then spawned from the agent. It also provides all
information displayed in the dashboard's workspace view.
![A healthy workspace agent](../../../images/templates/healthy-workspace-agent.png)
Multiple agents may be used in a single template or even a single resource. Each
agent may have it's own apps, startup script, and metadata. This can be used to
associate multiple containers or VMs with a workspace.
## Resource persistence
The resources you define in a template may be _ephemeral_ or _persistent_.
Persistent resources stay provisioned when workspaces are stopped, where as
ephemeral resources are destroyed and recreated on restart. All resources are
destroyed when a workspace is deleted.
> You can read more about how resource behavior and workspace state in the
> [workspace lifecycle documentation](../../../user-guides/workspace-lifecycle.md).
Template resources follow the
[behavior of Terraform resources](https://developer.hashicorp.com/terraform/language/resources/behavior#how-terraform-applies-a-configuration)
and can be further configured  using the
[lifecycle argument](https://developer.hashicorp.com/terraform/language/meta-arguments/lifecycle).
A common configuration is a template whose only persistent resource is the home
directory. This allows the developer to retain their work while ensuring the
rest of their environment is consistently up-to-date on each workspace restart.
When a workspace is deleted, the Coder server essentially runs a
[terraform destroy](https://www.terraform.io/cli/commands/destroy) to remove all
resources associated with the workspace.
> Terraform's
> [prevent-destroy](https://www.terraform.io/language/meta-arguments/lifecycle#prevent_destroy)
> and
> [ignore-changes](https://www.terraform.io/language/meta-arguments/lifecycle#ignore_changes)
> meta-arguments can be used to prevent accidental data loss.
## Coder apps
Additional IDEs, documentation, or services can be associated to your workspace
using the
[`coder_app`](https://registry.terraform.io/providers/coder/coder/latest/docs/resources/app)
resource.
![Coder Apps in the dashboard](../../../images/admin/templates/coder-apps-ui.png)
Note that some apps are associated to the agent by default as
[`display_apps`](https://registry.terraform.io/providers/coder/coder/latest/docs/resources/agent#nested-schema-for-display_apps)
and can be hidden directly in the
[`coder_agent`](https://registry.terraform.io/providers/coder/coder/latest/docs/resources/agent)
resource. You can arrange the display orientation of Coder apps in your template
using [resource ordering](./resource-ordering.md).
Check out our [module registry](https://registry.coder.com/modules) for
additional Coder apps from the team and our OSS community.
<children></children>

View File

@ -0,0 +1,198 @@
# Reusing template code
To reuse code across different Coder templates, such as common scripts or
resource definitions, we suggest using
[Terraform Modules](https://developer.hashicorp.com/terraform/language/modules).
You can store these modules externally from your Coder deployment, like in a git
repository or a Terraform registry. This example shows how to reference a module
from your template:
```tf
data "coder_workspace" "me" {}
module "coder-base" {
source = "github.com/my-organization/coder-base"
# Modules take in variables and can provision infrastructure
vpc_name = "devex-3"
subnet_tags = { "name": data.coder_workspace.me.name }
code_server_version = 4.14.1
}
resource "coder_agent" "dev" {
# Modules can provide outputs, such as helper scripts
startup_script=<<EOF
#!/bin/sh
${module.coder-base.code_server_install_command}
EOF
}
```
Learn more about
[creating modules](https://developer.hashicorp.com/terraform/language/modules)
and
[module sources](https://developer.hashicorp.com/terraform/language/modules/sources)
in the Terraform documentation.
## Coder modules
Coder publishes plenty of modules that can be used to simplify some common tasks
across templates. Some of the modules we publish are,
1. [`code-server`](https://registry.coder.com/modules/code-server) and
[`vscode-web`](https://registry.coder.com/modules/vscode-web)
2. [`git-clone`](https://registry.coder.com/modules/git-clone)
3. [`dotfiles`](https://registry.coder.com/modules/dotfiles)
4. [`jetbrains-gateway`](https://registry.coder.com/modules/jetbrains-gateway)
5. [`jfrog-oauth`](https://registry.coder.com/modules/jfrog-oauth) and
[`jfrog-token`](https://registry.coder.com/modules/jfrog-token)
6. [`vault-github`](https://registry.coder.com/modules/vault-github)
For a full list of available modules please check
[Coder module registry](https://registry.coder.com/modules).
## Offline installations
In offline and restricted deploymnets, there are 2 ways to fetch modules.
1. Artifactory
2. Private git repository
### Artifactory
Air gapped users can clone the [coder/modules](htpps://github.com/coder/modules)
repo and publish a
[local terraform module repository](https://jfrog.com/help/r/jfrog-artifactory-documentation/set-up-a-terraform-module/provider-registry)
to resolve modules via [Artifactory](https://jfrog.com/artifactory/).
1. Create a local-terraform-repository with name `coder-modules-local`
2. Create a virtual repository with name `tf`
3. Follow the below instructions to publish coder modules to Artifactory
```shell
git clone https://github.com/coder/modules
cd modules
jf tfc
jf tf p --namespace="coder" --provider="coder" --tag="1.0.0"
```
4. Generate a token with access to the `tf` repo and set an `ENV` variable
`TF_TOKEN_example.jfrog.io="XXXXXXXXXXXXXXX"` on the Coder provisioner.
5. Create a file `.terraformrc` with following content and mount at
`/home/coder/.terraformrc` within the Coder provisioner.
```tf
provider_installation {
direct {
exclude = ["registry.terraform.io/*/*"]
}
network_mirror {
url = "https://example.jfrog.io/artifactory/api/terraform/tf/providers/"
}
}
```
6. Update module source as,
```tf
module "module-name" {
source = "https://example.jfrog.io/tf__coder/module-name/coder"
version = "1.0.0"
agent_id = coder_agent.example.id
...
}
```
> Do not forget to replace example.jfrog.io with your Artifactory URL
Based on the instructions
[here](https://jfrog.com/blog/tour-terraform-registries-in-artifactory/).
#### Example template
We have an example template
[here](https://github.com/coder/coder/blob/main/examples/jfrog/remote/main.tf)
that uses our
[JFrog Docker](https://github.com/coder/coder/blob/main/examples/jfrog/docker/main.tf)
template as the underlying module.
### Private git repository
If you are importing a module from a private git repository, the Coder server or
[provisioner](../../provisioners.md) needs git credentials. Since this token
will only be used for cloning your repositories with modules, it is best to
create a token with access limited to the repository and no extra permissions.
In GitHub, you can generate a
[fine-grained token](https://docs.github.com/en/rest/overview/permissions-required-for-fine-grained-personal-access-tokens?apiVersion=2022-11-28)
with read only access to the necessary repos.
If you are running Coder on a VM, make sure that you have `git` installed and
the `coder` user has access to the following files:
```shell
# /home/coder/.gitconfig
[credential]
helper = store
```
```shell
# /home/coder/.git-credentials
# GitHub example:
https://your-github-username:your-github-pat@github.com
```
If you are running Coder on Docker or Kubernetes, `git` is pre-installed in the
Coder image. However, you still need to mount credentials. This can be done via
a Docker volume mount or Kubernetes secrets.
#### Passing git credentials in Kubernetes
First, create a `.gitconfig` and `.git-credentials` file on your local machine.
You might want to do this in a temporary directory to avoid conflicting with
your own git credentials.
Next, create the secret in Kubernetes. Be sure to do this in the same namespace
that Coder is installed in.
```shell
export NAMESPACE=coder
kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
name: git-secrets
namespace: $NAMESPACE
type: Opaque
data:
.gitconfig: $(cat .gitconfig | base64 | tr -d '\n')
.git-credentials: $(cat .git-credentials | base64 | tr -d '\n')
EOF
```
Then, modify Coder's Helm values to mount the secret.
```yaml
coder:
volumes:
- name: git-secrets
secret:
secretName: git-secrets
volumeMounts:
- name: git-secrets
mountPath: "/home/coder/.gitconfig"
subPath: .gitconfig
readOnly: true
- name: git-secrets
mountPath: "/home/coder/.git-credentials"
subPath: .git-credentials
readOnly: true
```
### Next steps
- JFrog's
[Terraform Registry support](https://jfrog.com/help/r/jfrog-artifactory-documentation/terraform-registry)
- [Configuring the JFrog toolchain inside a workspace](../../integrations/jfrog-artifactory.md)
- [Coder Module Registry](https://registry.coder.com/modules)

View File

@ -0,0 +1,300 @@
# Parameters
A template can prompt the user for additional information when creating
workspaces with
[_parameters_](https://registry.terraform.io/providers/coder/coder/latest/docs/data-sources/parameter).
![Parameters in Create Workspace screen](../../../images/parameters.png)
The user can set parameters in the dashboard UI and CLI.
You'll likely want to hardcode certain template properties for workspaces, such
as security group. But you can let developers specify other properties with
parameters like instance size, geographical location, repository URL, etc.
This example lets a developer choose a Docker host for the workspace:
```tf
data "coder_parameter" "docker_host" {
name = "Region"
description = "Which region would you like to deploy to?"
icon = "/emojis/1f30f.png"
type = "string"
default = "tcp://100.94.74.63:2375"
option {
name = "Pittsburgh, USA"
value = "tcp://100.94.74.63:2375"
icon = "/emojis/1f1fa-1f1f8.png"
}
option {
name = "Helsinki, Finland"
value = "tcp://100.117.102.81:2375"
icon = "/emojis/1f1eb-1f1ee.png"
}
option {
name = "Sydney, Australia"
value = "tcp://100.127.2.1:2375"
icon = "/emojis/1f1e6-1f1f9.png"
}
}
```
From there, a template can refer to a parameter's value:
```tf
provider "docker" {
host = data.coder_parameter.docker_host.value
}
```
## Types
A Coder parameter can have one of these types:
- `string`
- `bool`
- `number`
- `list(string)`
To specify a default value for a parameter with the `list(string)` type, use a
JSON array and the Terraform
[jsonencode](https://developer.hashicorp.com/terraform/language/functions/jsonencode)
function. For example:
```tf
data "coder_parameter" "security_groups" {
name = "Security groups"
icon = "/icon/aws.png"
type = "list(string)"
description = "Select appropriate security groups."
mutable = true
default = jsonencode([
"Web Server Security Group",
"Database Security Group",
"Backend Security Group"
])
}
```
## Options
A `string` parameter can provide a set of options to limit the user's choices:
```tf
data "coder_parameter" "docker_host" {
name = "Region"
description = "Which region would you like to deploy to?"
type = "string"
default = "tcp://100.94.74.63:2375"
option {
name = "Pittsburgh, USA"
value = "tcp://100.94.74.63:2375"
icon = "/emojis/1f1fa-1f1f8.png"
}
option {
name = "Helsinki, Finland"
value = "tcp://100.117.102.81:2375"
icon = "/emojis/1f1eb-1f1ee.png"
}
option {
name = "Sydney, Australia"
value = "tcp://100.127.2.1:2375"
icon = "/emojis/1f1e6-1f1f9.png"
}
}
```
### Incompatibility in Parameter Options for Workspace Builds
When creating Coder templates, authors have the flexibility to modify parameter
options associated with rich parameters. Such modifications can involve adding,
substituting, or removing a parameter option. It's important to note that making
these changes can lead to discrepancies in parameter values utilized by ongoing
workspace builds.
Consequently, workspace users will be prompted to select the new value from a
pop-up window or by using the command-line interface. While this additional
interactive step might seem like an interruption, it serves a crucial purpose.
It prevents workspace users from becoming trapped with outdated template
versions, ensuring they can smoothly update their workspace without any
hindrances.
Example:
- Bob creates a workspace using the `python-dev` template. This template has a
parameter `image_tag`, and Bob selects `1.12`.
- Later, the template author Alice is notified of a critical vulnerability in a
package installed in the `python-dev` template, which affects the image tag
`1.12`.
- Alice remediates this vulnerability, and pushes an updated template version
that replaces option `1.12` with `1.13` for the `image_tag` parameter. She
then notifies all users of that template to update their workspace
immediately.
- Bob saves their work, and selects the `Update` option in the UI. As their
workspace uses the now-invalid option `1.12`, for the `image_tag` parameter,
they are prompted to select a new value for `image_tag`.
## Required and optional parameters
A parameter is _required_ if it doesn't have the `default` property. The user
**must** provide a value to this parameter before creating a workspace:
```tf
data "coder_parameter" "account_name" {
name = "Account name"
description = "Cloud account name"
mutable = true
}
```
If a parameter contains the `default` property, Coder will use this value if the
user does not specify any:
```tf
data "coder_parameter" "base_image" {
name = "Base image"
description = "Base machine image to download"
default = "ubuntu:latest"
}
```
Admins can also set the `default` property to an empty value so that the
parameter field can remain empty:
```tf
data "coder_parameter" "dotfiles_url" {
name = "dotfiles URL"
description = "Git repository with dotfiles"
mutable = true
default = ""
}
```
## Mutability
Immutable parameters can only be set in these situations:
- Creating a workspace for the first time.
- Updating a workspace to a new template version. This sets the initial value
for required parameters.
The idea is to prevent users from modifying fragile or persistent workspace
resources like volumes, regions, and so on.
Example:
```tf
data "coder_parameter" "region" {
name = "Region"
description = "Region where the workspace is hosted"
mutable = false
default = "us-east-1"
}
```
You can modify a parameter's `mutable` attribute state anytime. In case of
emergency, you can temporarily allow for changing immutable parameters to fix an
operational issue, but it is not advised to overuse this opportunity.
## Ephemeral parameters
Ephemeral parameters are introduced to users in the form of "build options." Use
ephemeral parameters to model specific behaviors in a Coder workspace, such as
reverting to a previous image, restoring from a volume snapshot, or building a
project without using cache.
Since these parameters are ephemeral in nature, subsequent builds proceed in the
standard manner:
```tf
data "coder_parameter" "force_rebuild" {
name = "force_rebuild"
type = "bool"
description = "Rebuild the Docker image rather than use the cached one."
mutable = true
default = false
ephemeral = true
}
```
## Validating parameters
Coder supports rich parameters with multiple validation modes: min, max,
monotonic numbers, and regular expressions.
### Number
You can limit a `number` parameter to `min` and `max` boundaries.
You can also specify its monotonicity as `increasing` or `decreasing` to verify
the current and new values. Use the `monotonic` attribute for resources that
can't be shrunk or grown without implications, like disk volume size.
```tf
data "coder_parameter" "instances" {
name = "Instances"
type = "number"
description = "Number of compute instances"
validation {
min = 1
max = 8
monotonic = "increasing"
}
}
```
It is possible to override the default `error` message for a `number` parameter,
along with its associated `min` and/or `max` properties. The following message
placeholders are available `{min}`, `{max}`, and `{value}`.
```tf
data "coder_parameter" "instances" {
name = "Instances"
type = "number"
description = "Number of compute instances"
validation {
min = 1
max = 4
error = "Sorry, we can't provision too many instances - maximum limit: {max}, wanted: {value}."
}
}
```
**NOTE:** as of
[`terraform-provider-coder` v0.19.0](https://registry.terraform.io/providers/coder/coder/0.19.0/docs),
`options` can be specified in `number` parameters; this also works with
validations such as `monotonic`.
### String
You can validate a `string` parameter to match a regular expression. The `regex`
property requires a corresponding `error` property.
```tf
data "coder_parameter" "project_id" {
name = "Project ID"
description = "Alpha-numeric project ID"
validation {
regex = "^[a-z0-9]+$"
error = "Unfortunately, this isn't a valid project ID"
}
}
```
## Create Autofill
When the template doesn't specify default values, Coder may still autofill
parameters.
1. Coder will look for URL query parameters with form `param.<name>=<value>`.
This feature enables platform teams to create pre-filled template creation
links.
2. Coder will populate recently used parameter key-value pairs for the user.
This feature helps reduce repetition when filling common parameters such as
`dotfiles_url` or `region`.

View File

@ -0,0 +1,315 @@
# Workspace Process Logging
The workspace process logging feature allows you to log all system-level
processes executing in the workspace.
> **Note:** This feature is only available on Linux in Kubernetes. There are
> additional requirements outlined further in this document.
Workspace process logging adds a sidecar container to workspace pods that will
log all processes started in the workspace container (e.g., commands executed in
the terminal or processes created in the background by other processes).
Processes launched inside containers or nested containers within the workspace
are also logged. You can view the output from the sidecar or send it to a
monitoring stack, such as CloudWatch, for further analysis or long-term storage.
Please note that these logs are not recorded or captured by the Coder
organization in any way, shape, or form.
> This is an [Premium or Enterprise](https://coder.com/pricing) feature. To
> learn more about Coder Enterprise, please
> [contact sales](https://coder.com/contact).
## How this works
Coder uses [eBPF](https://ebpf.io/) (which we chose for its minimal performance
impact) to perform in-kernel logging and filtering of all exec system calls
originating from the workspace container.
The core of this feature is also open source and can be found in the
[exectrace](https://github.com/coder/exectrace) GitHub repo. The enterprise
component (in the `enterprise/` directory of the repo) is responsible for
starting the eBPF program with the correct filtering options for the specific
workspace.
## Requirements
The host machine must be running a Linux kernel >= 5.8 with the kernel config
`CONFIG_DEBUG_INFO_BTF=y` enabled.
To check your kernel version, run:
```shell
uname -r
```
To validate the required kernel config is enabled, run either of the following
commands on your nodes directly (_not_ from a workspace terminal):
```shell
cat /proc/config.gz | gunzip | grep CONFIG_DEBUG_INFO_BTF
```
```shell
cat "/boot/config-$(uname -r)" | grep CONFIG_DEBUG_INFO_BTF
```
If these requirements are not met, workspaces will fail to start for security
reasons.
Your template must be a Kubernetes template. Workspace process logging is not
compatible with the `sysbox-runc` runtime due to technical limitations, but it
is compatible with our `envbox` template family.
## Example templates
We provide working example templates for Kubernetes, and Kubernetes with
`envbox` (for [Docker support in workspaces](./docker-in-workspaces.md)). You
can view these templates in the
[exectrace repo](https://github.com/coder/exectrace/tree/main/enterprise/templates).
## Configuring custom templates to use workspace process logging
If you have an existing Kubernetes or Kubernetes with `envbox` template that you
would like to add workspace process logging to, follow these steps:
1. Ensure the image used in your template has `curl` installed.
1. Add the following section to your template's `main.tf` file:
<!--
If you are updating this section, please also update the example templates
in the exectrace repo.
-->
```hcl
locals {
# This is the init script for the main workspace container that runs before the
# agent starts to configure workspace process logging.
exectrace_init_script = <<EOT
set -eu
pidns_inum=$(readlink /proc/self/ns/pid | sed 's/[^0-9]//g')
if [ -z "$pidns_inum" ]; then
echo "Could not determine process ID namespace inum"
exit 1
fi
# Before we start the script, does curl exist?
if ! command -v curl >/dev/null 2>&1; then
echo "curl is required to download the Coder binary"
echo "Please install curl to your image and try again"
# 127 is command not found.
exit 127
fi
echo "Sending process ID namespace inum to exectrace sidecar"
rc=0
max_retry=5
counter=0
until [ $counter -ge $max_retry ]; do
set +e
curl \
--fail \
--silent \
--connect-timeout 5 \
-X POST \
-H "Content-Type: text/plain" \
--data "$pidns_inum" \
http://127.0.0.1:56123
rc=$?
set -e
if [ $rc -eq 0 ]; then
break
fi
counter=$((counter+1))
echo "Curl failed with exit code $${rc}, attempt $${counter}/$${max_retry}; Retrying in 3 seconds..."
sleep 3
done
if [ $rc -ne 0 ]; then
echo "Failed to send process ID namespace inum to exectrace sidecar"
exit $rc
fi
EOT
}
```
1. Update the `command` of your workspace container like the following:
<!--
If you are updating this section, please also update the example templates
in the exectrace repo.
-->
```hcl
resource "kubernetes_pod" "main" {
...
spec {
...
container {
...
// NOTE: this command is changed compared to the upstream kubernetes
// template
command = [
"sh",
"-c",
"${local.exectrace_init_script}\n\n${coder_agent.main.init_script}",
]
...
}
...
}
...
}
```
> **Note:** If you are using the `envbox` template, you will need to update
> the third argument to be
> `"${local.exectrace_init_script}\n\nexec /envbox docker"` instead.
1. Add the following container to your workspace pod spec.
<!--
If you are updating this section, please also update the example templates
in the exectrace repo.
-->
```hcl
resource "kubernetes_pod" "main" {
...
spec {
...
// NOTE: this container is added compared to the upstream kubernetes
// template
container {
name = "exectrace"
image = "ghcr.io/coder/exectrace:latest"
image_pull_policy = "Always"
command = [
"/opt/exectrace",
"--init-address", "127.0.0.1:56123",
"--label", "workspace_id=${data.coder_workspace.me.id}",
"--label", "workspace_name=${data.coder_workspace.me.name}",
"--label", "user_id=${data.coder_workspace_owner.me.id}",
"--label", "username=${data.coder_workspace_owner.me.name}",
"--label", "user_email=${data.coder_workspace_owner.me.email}",
]
security_context {
// exectrace must be started as root so it can attach probes into the
// kernel to record process events with high throughput.
run_as_user = "0"
run_as_group = "0"
// exectrace requires a privileged container so it can control mounts
// and perform privileged syscalls against the host kernel to attach
// probes.
privileged = true
}
}
...
}
...
}
```
> **Note:** `exectrace` requires root privileges and a privileged container
> to attach probes to the kernel. This is a requirement of eBPF.
1. Add the following environment variable to your workspace pod:
<!--
If you are updating this section, please also update the example templates
in the exectrace repo.
-->
```hcl
resource "kubernetes_pod" "main" {
...
spec {
...
env {
name = "CODER_AGENT_SUBSYSTEM"
value = "exectrace"
}
...
}
...
}
```
Once you have made these changes, you can push a new version of your template
and workspace process logging will be enabled for all workspaces once they are
restarted.
## Viewing workspace process logs
To view the process logs for a specific workspace you can use `kubectl` to print
the logs:
```bash
kubectl logs pod-name --container exectrace
```
The raw logs will look something like this:
```json
{
"ts": "2022-02-28T20:29:38.038452202Z",
"level": "INFO",
"msg": "exec",
"fields": {
"labels": {
"user_email": "jessie@coder.com",
"user_id": "5e876e9a-121663f01ebd1522060d5270",
"username": "jessie",
"workspace_id": "621d2e52-a6987ef6c56210058ee2593c",
"workspace_name": "main"
},
"cmdline": "uname -a",
"event": {
"filename": "/usr/bin/uname",
"argv": ["uname", "-a"],
"truncated": false,
"pid": 920684,
"uid": 101000,
"gid": 101000,
"comm": "bash"
}
}
}
```
### View logs in AWS EKS
If you're using AWS' Elastic Kubernetes Service, you can
[configure your cluster](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Container-Insights-EKS-logs.html)
to send logs to CloudWatch. This allows you to view the logs for a specific user
or workspace.
To view your logs, go to the CloudWatch dashboard (which is available on the
**Log Insights** tab) and run a query similar to the following:
```text
fields @timestamp, log_processed.fields.cmdline
| sort @timestamp asc
| filter kubernetes.container_name="exectrace"
| filter log_processed.fields.labels.username="zac"
| filter log_processed.fields.labels.workspace_name="code"
```
## Usage considerations
- The sidecar attached to each workspace is a privileged container, so you may
need to review your organization's security policies before enabling this
feature. Enabling workspace process logging does _not_ grant extra privileges
to the workspace container itself, however.
- `exectrace` will log processes from nested Docker containers (including deeply
nested containers) correctly, but Coder does not distinguish between processes
started in the workspace and processes started in a child container in the
logs.
- With `envbox` workspaces, this feature will detect and log startup processes
begun in the outer container (including container initialization processes).
- Because this feature logs **all** processes in the workspace, high levels of
usage (e.g., during a `make` run) will result in an abundance of output in the
sidecar container. Depending on how your Kubernetes cluster is configured, you
may incur extra charges from your cloud provider to store the additional logs.

View File

@ -0,0 +1,48 @@
# Provider Authentication
<blockquote class="danger">
<p>
Do not store secrets in templates. Assume every user has cleartext access
to every template.
</p>
</blockquote>
The Coder server's
[provisioner](https://registry.terraform.io/providers/coder/coder/latest/docs/data-sources/provisioner)
process needs to authenticate with other provider APIs to provision workspaces.
There are two approaches to do this:
- Pass credentials to the provisioner as parameters.
- Preferred: Execute the Coder server in an environment that is authenticated
with the provider.
We encourage the latter approach where supported:
- Simplifies the template.
- Keeps provider credentials out of Coder's database, making it a less valuable
target for attackers.
- Compatible with agent-based authentication schemes, which handle credential
rotation or ensure the credentials are not written to disk.
Generally, you can set up an environment to provide credentials to Coder in
these ways:
- A well-known location on disk. For example, `~/.aws/credentials` for AWS on
POSIX systems.
- Environment variables.
It is usually sufficient to authenticate using the CLI or SDK for the provider
before running Coder, but check the Terraform provider's documentation for
details.
These platforms have Terraform providers that support authenticated
environments:
- [Google Cloud](https://registry.terraform.io/providers/hashicorp/google/latest/docs)
- [Amazon Web Services](https://registry.terraform.io/providers/hashicorp/aws/latest/docs)
- [Microsoft Azure](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs)
- [Kubernetes](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs)
Other providers might also support authenticated environments. Check the
[documentation of the Terraform provider](https://registry.terraform.io/browse/providers)
for details.

View File

@ -0,0 +1,111 @@
# Resource Metadata
Expose key workspace information to your users with
[`coder_metadata`](https://registry.terraform.io/providers/coder/coder/latest/docs/resources/metadata)
resources in your template code.
You can use `coder_metadata` to show Terraform resource attributes like these:
- Compute resources
- IP addresses
- [Secrets](../../security/secrets.md#displaying-secrets)
- Important file paths
![ui](../../../images/admin/templates/coder-metadata-ui.png)
<blockquote class="info">
Coder automatically generates the <code>type</code> metadata.
</blockquote>
You can also present automatically updating, dynamic values with
[agent metadata](./agent-metadata.md).
## Example
Expose the disk size, deployment name, and persistent directory in a Kubernetes
template with:
```tf
resource "kubernetes_persistent_volume_claim" "root" {
...
}
resource "kubernetes_deployment" "coder" {
# My deployment is ephemeral
count = data.coder_workspace.me.start_count
...
}
resource "coder_metadata" "pvc" {
resource_id = kubernetes_persistent_volume_claim.root.id
item {
key = "size"
value = kubernetes_persistent_volume_claim.root.spec[0].resources[0].requests.storage
}
item {
key = "dir"
value = "/home/coder"
}
}
resource "coder_metadata" "deployment" {
count = data.coder_workspace.me.start_count
resource_id = kubernetes_deployment.coder[0].id
item {
key = "name"
value = kubernetes_deployment.coder[0].metadata[0].name
}
}
```
## Hiding resources in the dashboard
Some resources don't need to be exposed in the dashboard's UI. This helps keep
the workspace view clean for developers. To hide a resource, use the `hide`
attribute:
```tf
resource "coder_metadata" "hide_serviceaccount" {
count = data.coder_workspace.me.start_count
resource_id = kubernetes_service_account.user_data.id
hide = true
item {
key = "name"
value = kubernetes_deployment.coder[0].metadata[0].name
}
}
```
## Using a custom resource icon
To use custom icons for your resource metadata, use the `icon` attribute. It
must be a valid path or URL.
```tf
resource "coder_metadata" "resource_with_icon" {
count = data.coder_workspace.me.start_count
resource_id = kubernetes_service_account.user_data.id
icon = "/icon/database.svg"
item {
key = "name"
value = kubernetes_deployment.coder[0].metadata[0].name
}
}
```
To make it easier for you to customize your resource we added some built-in
icons:
- Folder `/icon/folder.svg`
- Memory `/icon/memory.svg`
- Image `/icon/image.svg`
- Widgets `/icon/widgets.svg`
- Database `/icon/database.svg`
We also have other icons related to the IDEs. You can see more information on
how to use the builtin icons [here](./icons.md).
## Up next
- [Secrets](../../security/secrets.md)
- [Agent metadata](./agent-metadata.md)

View File

@ -0,0 +1,183 @@
# UI Resource Ordering
In Coder templates, managing the order of UI elements is crucial for a seamless
user experience. This page outlines how resources can be aligned using the
`order` Terraform property or inherit the natural order from the file.
The resource with the lower `order` is presented before the one with greater
value. A missing `order` property defaults to 0. If two resources have the same
`order` property, the resources will be ordered by property `name` (or `key`).
## Using "order" property
### Coder parameters
The `order` property of `coder_parameter` resource allows specifying the order
of parameters in UI forms. In the below example, `project_id` will appear
_before_ `account_id`:
```tf
data "coder_parameter" "project_id" {
name = "project_id"
display_name = "Project ID"
description = "Specify cloud provider project ID."
order = 2
}
data "coder_parameter" "account_id" {
name = "account_id"
display_name = "Account ID"
description = "Specify cloud provider account ID."
order = 1
}
```
### Agents
Agent resources within the UI left pane are sorted based on the `order`
property, followed by `name`, ensuring a consistent and intuitive arrangement.
```tf
resource "coder_agent" "primary" {
...
order = 1
}
resource "coder_agent" "secondary" {
...
order = 2
}
```
The agent with the lowest order is presented at the top in the workspace view.
### Agent metadata
The `coder_agent` exposes metadata to present operational metrics in the UI.
Metrics defined with Terraform `metadata` blocks can be ordered using additional
`order` property; otherwise, they are sorted by `key`.
```tf
resource "coder_agent" "main" {
...
metadata {
display_name = "CPU Usage"
key = "cpu_usage"
script = "coder stat cpu"
interval = 10
timeout = 1
order = 1
}
metadata {
display_name = "CPU Usage (Host)"
key = "cpu_usage_host"
script = "coder stat cpu --host"
interval = 10
timeout = 1
order = 2
}
metadata {
display_name = "RAM Usage"
key = "ram_usage"
script = "coder stat mem"
interval = 10
timeout = 1
order = 1
}
metadata {
display_name = "RAM Usage (Host)"
key = "ram_usage_host"
script = "coder stat mem --host"
interval = 10
timeout = 1
order = 2
}
}
```
### Applications
Similarly to Coder agents, `coder_app` resources incorporate the `order`
property to organize button apps in the app bar within a `coder_agent` in the
workspace view.
Only template defined applications can be arranged. _VS Code_ or _Terminal_
buttons are static.
```tf
resource "coder_app" "code-server" {
agent_id = coder_agent.main.id
slug = "code-server"
display_name = "code-server"
...
order = 2
}
resource "coder_app" "filebrowser" {
agent_id = coder_agent.main.id
display_name = "File Browser"
slug = "filebrowser"
...
order = 1
}
```
## Inherit order from file
### Coder parameter options
The options for Coder parameters maintain the same order as in the file
structure. This simplifies management and ensures consistency between
configuration files and UI presentation.
```tf
data "coder_parameter" "database_region" {
name = "database_region"
display_name = "Database Region"
icon = "/icon/database.svg"
description = "These are options."
mutable = true
default = "us-east1-a"
// The order of options is stable and inherited from .tf file.
option {
name = "US Central"
description = "Select for central!"
value = "us-central1-a"
}
option {
name = "US East"
description = "Select for east!"
value = "us-east1-a"
}
...
}
```
### Coder metadata items
In cases where multiple item properties exist, the order is inherited from the
file, facilitating seamless integration between a Coder template and UI
presentation.
```tf
resource "coder_metadata" "attached_volumes" {
resource_id = docker_image.main.id
// Items will be presented in the UI in the following order.
item {
key = "disk-a"
value = "60 GiB"
}
item {
key = "disk-b"
value = "128 GiB"
}
}
```

View File

@ -0,0 +1,93 @@
# Resource persistence
By default, all Coder resources are persistent, but production templates
**must** use the practices laid out in this document to prevent accidental
deletion.
Coder templates have full control over workspace ephemerality. In a completely
ephemeral workspace, there are zero resources in the Off state. In a completely
persistent workspace, there is no difference between the Off and On states.
The needs of most workspaces fall somewhere in the middle, persisting user data
like filesystem volumes, but deleting expensive, reproducible resources such as
compute instances.
## Disabling persistence
The Terraform
[`coder_workspace` data source](https://registry.terraform.io/providers/coder/coder/latest/docs/data-sources/workspace)
exposes the `start_count = [0 | 1]` attribute. To make a resource ephemeral, you
can assign the `start_count` attribute to resource's
[`count`](https://developer.hashicorp.com/terraform/language/meta-arguments/count)
meta-argument.
In this example, Coder will provision or tear down the `docker_container`
resource:
```tf
data "coder_workspace" "me" {
}
resource "docker_container" "workspace" {
# When `start_count` is 0, `count` is 0, so no `docker_container` is created.
count = data.coder_workspace.me.start_count # 0 (stopped), 1 (started)
# ... other config
}
```
## ⚠️ Persistence pitfalls
Take this example resource:
```tf
data "coder_workspace" "me" {
}
resource "docker_volume" "home_volume" {
name = "coder-${data.coder_workspace.me.owner}-home"
}
```
Because we depend on `coder_workspace.me.owner`, if the owner changes their
username, Terraform will recreate the volume (wiping its data!) the next time
that Coder starts the workspace.
To prevent this, use immutable IDs:
- `coder_workspace.me.owner_id`
- `coder_workspace.me.id`
```tf
data "coder_workspace" "me" {
}
resource "docker_volume" "home_volume" {
# This volume will survive until the Workspace is deleted or the template
# admin changes this resource block.
name = "coder-${data.coder_workspace.id}-home"
}
```
## 🛡 Bulletproofing
Even if your persistent resource depends exclusively on immutable IDs, a change
to the `name` format or other attributes would cause Terraform to rebuild the
resource.
You can prevent Terraform from recreating a resource under any circumstance by
setting the
[`ignore_changes = all` directive in the `lifecycle` block](https://developer.hashicorp.com/terraform/language/meta-arguments/lifecycle#ignore_changes).
```tf
data "coder_workspace" "me" {
}
resource "docker_volume" "home_volume" {
# This resource will survive until either the entire block is deleted
# or the workspace is.
name = "coder-${data.coder_workspace.me.id}-home"
lifecycle {
ignore_changes = all
}
}
```

View File

@ -0,0 +1,126 @@
# Terraform template-wide variables
In Coder, Terraform templates offer extensive flexibility through template-wide
variables. These variables, managed by template authors, facilitate the
construction of customizable templates. Unlike parameters, which are primarily
for workspace customization, template variables remain under the control of the
template author, ensuring workspace users cannot modify them.
```tf
variable "CLOUD_API_KEY" {
type = string
description = "API key for the service"
default = "1234567890"
sensitive = true
}
```
Given that variables are a
[fundamental concept in Terraform](https://developer.hashicorp.com/terraform/language/values/variables),
Coder endeavors to fully support them. Native support includes `string`,
`number`, and `bool` formats. However, other types such as `list(string)` or
`map(any)` will default to being treated as strings.
## Default value
Upon adding a template variable, it's mandatory to provide a value during the
first push. At this stage, the template administrator faces two choices:
1. _No `default` property_: opt not to define a default property. Instead,
utilize the `--var name=value` command-line argument during the push to
supply the variable's value.
2. _Define `default` property_: set a default property for the template
variable. If the administrator doesn't input a value via CLI, Coder
automatically uses this default during the push.
After the initial push, variables are stored in the database table, associated
with the specific template version. They can be conveniently managed via
_Template Settings_ without requiring an extra push.
### Resolved values vs. default values
It's crucial to note that Coder templates operate based on resolved values
during a push, rather than default values. This ensures that default values do
not inadvertently override the configured variable settings during the push
process.
This approach caters to users who prefer to avoid accidental overrides of their
variable settings with default values during pushes, thereby enhancing control
and predictability.
If you encounter a situation where you need to override template settings for
variables, you can employ a straightforward solution:
1. Create a `terraform.tfvars` file in in the template directory:
```tf
coder_image = newimage:tag
```
2. Push the new template revision using Coder CLI:
```
coder templates push my-template -y # no need to use --var
```
This file serves as a mechanism to override the template settings for variables.
It can be stored in the repository for easy access and reference. Coder CLI
automatically detects it and loads variable values.
## Input options
When working with Terraform configurations in Coder, you have several options
for providing values to variables using the Coder CLI:
1. _Manual input in CLI_: You can manually input values for Terraform variables
directly in the CLI during the deployment process.
2. _Command-line argument_: Utilize the `--var name=value` command-line argument
to specify variable values inline as key-value pairs.
3. _Variables file selection_: Alternatively, you can use a variables file
selected via the `--variables-file values.yml` command-line argument. This
approach is particularly useful when dealing with multiple variables or to
avoid manual input of numerous values. Variables files can be versioned for
better traceability and management, and it enhances reproducibility.
Here's an example of a YAML-formatted variables file, `values.yml`:
```yaml
region: us-east-1
bucket_name: magic
zone_types: '{"us-east-1":"US East", "eu-west-1": "EU West"}'
cpu: 1
```
In this sample file:
- `region`, `bucket_name`, `zone_types`, and `cpu` are Terraform variable names.
- Corresponding values are provided for each variable.
- The `zone_types` variable demonstrates how to provide a JSON-formatted string
as a value in YAML.
## Terraform .tfvars files
In Terraform, `.tfvars` files provide a convenient means to define variable
values for a project in a reusable manner. These files, ending with either
`.tfvars` or `.tfvars.json`, streamline the process of setting numerous
variables.
By utilizing `.tfvars` files, you can efficiently manage and organize variable
values for your Terraform projects. This approach offers several advantages:
- Clarity and consistency: Centralize variable definitions in dedicated files,
enhancing clarity, instead of input values on template push.
- Ease of maintenance: Modify variable values in a single location under version
control, simplifying maintenance and updates.
Coder automatically loads variable definition files following a specific order,
providing flexibility and control over variable configuration. The loading
sequence is as follows:
1. `terraform.tfvars`: This file contains variable values and is loaded first.
2. `terraform.tfvars.json`: If present, this JSON-formatted file is loaded after
`terraform.tfvars`.
3. `\*.auto.tfvars`: Files matching this pattern are loaded next, ordered
alphabetically.
4. `\*.auto.tfvars.json`: JSON-formatted files matching this pattern are loaded
last.

View File

@ -0,0 +1,376 @@
# Web IDEs
In Coder, web IDEs are defined as
[coder_app](https://registry.terraform.io/providers/coder/coder/latest/docs/resources/app)
resources in the template. With our generic model, any web application can be
used as a Coder application. For example:
```tf
# Add button to open Portainer in the workspace dashboard
# Note: Portainer must be already running in the workspace
resource "coder_app" "portainer" {
agent_id = coder_agent.main.id
slug = "portainer"
display_name = "Portainer"
icon = "https://simpleicons.org/icons/portainer.svg"
url = "https://localhost:9443/api/status"
healthcheck {
url = "https://localhost:9443/api/status"
interval = 6
threshold = 10
}
}
```
## code-server
[code-server](https://github.com/coder/coder) is our supported method of running
VS Code in the web browser. A simple way to install code-server in Linux/macOS
workspaces is via the Coder agent in your template:
```console
# edit your template
cd your-template/
vim main.tf
```
```tf
resource "coder_agent" "main" {
arch = "amd64"
os = "linux"
startup_script = <<EOF
#!/bin/sh
# install code-server
# add '-s -- --version x.x.x' to install a specific code-server version
curl -fsSL https://code-server.dev/install.sh | sh -s -- --method=standalone --prefix=/tmp/code-server
# start code-server on a specific port
# authn is off since the user already authn-ed into the coder deployment
# & is used to run the process in the background
/tmp/code-server/bin/code-server --auth none --port 13337 &
EOF
}
```
For advanced use, we recommend installing code-server in your VM snapshot or
container image. Here's a Dockerfile which leverages some special
[code-server features](https://coder.com/docs/code-server/):
```Dockerfile
FROM codercom/enterprise-base:ubuntu
# install the latest version
USER root
RUN curl -fsSL https://code-server.dev/install.sh | sh
USER coder
# pre-install VS Code extensions
RUN code-server --install-extension eamodio.gitlens
# directly start code-server with the agent's startup_script (see above),
# or use a process manager like supervisord
```
You'll also need to specify a `coder_app` resource related to the agent. This is
how code-server is displayed on the workspace page.
```tf
resource "coder_app" "code-server" {
agent_id = coder_agent.main.id
slug = "code-server"
display_name = "code-server"
url = "http://localhost:13337/?folder=/home/coder"
icon = "/icon/code.svg"
subdomain = false
healthcheck {
url = "http://localhost:13337/healthz"
interval = 2
threshold = 10
}
}
```
![code-server in a workspace](../../../images/code-server-ide.png)
## VS Code Web
VS Code supports launching a local web client using the `code serve-web`
command. To add VS Code web as a web IDE, you have two options.
1. Install using the
[vscode-web module](https://registry.coder.com/modules/vscode-web) from the
coder registry.
```tf
module "vscode-web" {
source = "registry.coder.com/modules/vscode-web/coder"
version = "1.0.14"
agent_id = coder_agent.main.id
accept_license = true
}
```
2. Install and start in your `startup_script` and create a corresponding
`coder_app`
```tf
resource "coder_agent" "main" {
arch = "amd64"
os = "linux"
startup_script = <<EOF
#!/bin/sh
# install VS Code
curl -Lk 'https://code.visualstudio.com/sha/download?build=stable&os=cli-alpine-x64' --output vscode_cli.tar.gz
mkdir -p /tmp/vscode-cli
tar -xf vscode_cli.tar.gz -C /tmp/vscode-cli
rm vscode_cli.tar.gz
# start the web server on a specific port
/tmp/vscode-cli/code serve-web --port 13338 --without-connection-token --accept-server-license-terms >/tmp/vscode-web.log 2>&1 &
EOF
}
```
> `code serve-web` was introduced in version 1.82.0 (August 2023).
You also need to add a `coder_app` resource for this.
```tf
# VS Code Web
resource "coder_app" "vscode-web" {
agent_id = coder_agent.coder.id
slug = "vscode-web"
display_name = "VS Code Web"
icon = "/icon/code.svg"
url = "http://localhost:13338?folder=/home/coder"
subdomain = true # VS Code Web does currently does not work with a subpath https://github.com/microsoft/vscode/issues/192947
share = "owner"
}
```
## Jupyter Notebook
To use Jupyter Notebook in your workspace, you can install it by using the
[Jupyter Notebook module](https://registry.coder.com/modules/jupyter-notebook)
from the Coder registry:
```tf
module "jupyter-notebook" {
source = "registry.coder.com/modules/jupyter-notebook/coder"
version = "1.0.19"
agent_id = coder_agent.example.id
}
```
![Jupyter Notebook in Coder](../../../images/jupyter-notebook.png)
## JupyterLab
Configure your agent and `coder_app` like so to use Jupyter. Notice the
`subdomain=true` configuration:
```tf
data "coder_workspace" "me" {}
resource "coder_agent" "coder" {
os = "linux"
arch = "amd64"
dir = "/home/coder"
startup_script = <<-EOF
pip3 install jupyterlab
$HOME/.local/bin/jupyter lab --ServerApp.token='' --ip='*'
EOF
}
resource "coder_app" "jupyter" {
agent_id = coder_agent.coder.id
slug = "jupyter"
display_name = "JupyterLab"
url = "http://localhost:8888"
icon = "/icon/jupyter.svg"
share = "owner"
subdomain = true
healthcheck {
url = "http://localhost:8888/healthz"
interval = 5
threshold = 10
}
}
```
Or Alternatively, you can use the JupyterLab module from the Coder registry:
```tf
module "jupyter" {
source = "registry.coder.com/modules/jupyter-lab/coder"
version = "1.0.0"
agent_id = coder_agent.main.id
}
```
If you cannot enable a
[wildcard subdomain](../../../admin/setup/index.md#wildcard-access-url), you can
configure the template to run Jupyter on a path. There is however
[security risk](../../../reference/cli/server.md#--dangerous-allow-path-app-sharing)
running an app on a path and the template code is more complicated with coder
value substitution to recreate the path structure.
![JupyterLab in Coder](../../../images/jupyter.png)
## RStudio
Configure your agent and `coder_app` like so to use RStudio. Notice the
`subdomain=true` configuration:
```tf
resource "coder_agent" "coder" {
os = "linux"
arch = "amd64"
dir = "/home/coder"
startup_script = <<EOT
#!/bin/bash
# start rstudio
/usr/lib/rstudio-server/bin/rserver --server-daemonize=1 --auth-none=1 &
EOT
}
resource "coder_app" "rstudio" {
agent_id = coder_agent.coder.id
slug = "rstudio"
display_name = "R Studio"
icon = "https://upload.wikimedia.org/wikipedia/commons/d/d0/RStudio_logo_flat.svg"
url = "http://localhost:8787"
subdomain = true
share = "owner"
healthcheck {
url = "http://localhost:8787/healthz"
interval = 3
threshold = 10
}
}
```
If you cannot enable a
[wildcard subdomain](https://coder.com/docs/admin/configure#wildcard-access-url),
you can configure the template to run RStudio on a path using an NGINX reverse
proxy in the template. There is however
[security risk](https://coder.com/docs/reference/cli/server#--dangerous-allow-path-app-sharing)
running an app on a path and the template code is more complicated with coder
value substitution to recreate the path structure.
[This](https://github.com/sempie/coder-templates/tree/main/rstudio) is a
community template example.
![RStudio in Coder](../../../images/rstudio-port-forward.png)
## Airflow
Configure your agent and `coder_app` like so to use Airflow. Notice the
`subdomain=true` configuration:
```tf
resource "coder_agent" "coder" {
os = "linux"
arch = "amd64"
dir = "/home/coder"
startup_script = <<EOT
#!/bin/bash
# install and start airflow
pip3 install apache-airflow
/home/coder/.local/bin/airflow standalone &
EOT
}
resource "coder_app" "airflow" {
agent_id = coder_agent.coder.id
slug = "airflow"
display_name = "Airflow"
icon = "/icon/airflow.svg"
url = "http://localhost:8080"
subdomain = true
share = "owner"
healthcheck {
url = "http://localhost:8080/healthz"
interval = 10
threshold = 60
}
}
```
or use the [Airflow module](https://registry.coder.com/modules/apache-airflow)
from the Coder registry:
```tf
module "airflow" {
source = "registry.coder.com/modules/airflow/coder"
version = "1.0.13"
agent_id = coder_agent.main.id
}
```
![Airflow in Coder](../../../images/airflow-port-forward.png)
## File Browser
To access the contents of a workspace directory in a browser, you can use File
Browser. File Browser is a lightweight file manager that allows you to view and
manipulate files in a web browser.
Show and manipulate the contents of the `/home/coder` directory in a browser.
```tf
resource "coder_agent" "coder" {
os = "linux"
arch = "amd64"
dir = "/home/coder"
startup_script = <<EOT
#!/bin/bash
curl -fsSL https://raw.githubusercontent.com/filebrowser/get/master/get.sh | bash
filebrowser --noauth --root /home/coder --port 13339 >/tmp/filebrowser.log 2>&1 &
EOT
}
resource "coder_app" "filebrowser" {
agent_id = coder_agent.coder.id
display_name = "file browser"
slug = "filebrowser"
url = "http://localhost:13339"
icon = "https://raw.githubusercontent.com/matifali/logos/main/database.svg"
subdomain = true
share = "owner"
healthcheck {
url = "http://localhost:13339/healthz"
interval = 3
threshold = 10
}
}
```
Or alternatively, you can use the
[`filebrowser`](https://registry.coder.com/modules/filebrowser) module from the
Coder registry:
```tf
module "filebrowser" {
source = "registry.coder.com/modules/filebrowser/coder"
version = "1.0.8"
agent_id = coder_agent.main.id
}
```
![File Browser](../../../images/file-browser.png)
## SSH Fallback
If you prefer to run web IDEs in localhost, you can port forward using
[SSH](../../../user-guides/workspace-access/index.md#ssh) or the Coder CLI
`port-forward` sub-command. Some web IDEs may not support URL base path
adjustment so port forwarding is the only approach.

View File

@ -0,0 +1,87 @@
# Workspace Tags
Template administrators can leverage static template tags to limit workspace
provisioning to designated provisioner groups that have locally deployed
credentials for creating workspace resources. While this method ensures
controlled access, it offers limited flexibility and does not permit users to
select the nodes for their workspace creation.
By using `coder_workspace_tags` and `coder_parameter`s, template administrators
can enable dynamic tag selection and modify static template tags.
## Dynamic tag selection
Here is a sample `coder_workspace_tags` data resource with a few workspace tags
specified:
```tf
data "coder_workspace_tags" "custom_workspace_tags" {
tags = {
"zone" = "developers"
"runtime" = data.coder_parameter.runtime_selector.value
"project_id" = "PROJECT_${data.coder_parameter.project_name.value}"
"cache" = data.coder_parameter.feature_cache_enabled.value == "true" ? "with-cache" : "no-cache"
}
}
```
**Legend**
- `zone` - static tag value set to `developers`
- `runtime` - supported by the string-type `coder_parameter` to select
provisioner runtime, `runtime_selector`
- `project_id` - a formatted string supported by the string-type
`coder_parameter`, `project_name`
- `cache` - an HCL condition involving boolean-type `coder_parameter`,
`feature_cache_enabled`
Review the
[full template example](https://github.com/coder/coder/tree/main/examples/workspace-tags)
using `coder_workspace_tags` and `coder_parameter`s.
## Constraints
### Tagged provisioners
It is possible to choose tag combinations that no provisioner can handle. This
will cause the provisioner job to get stuck in the queue until a provisioner is
added that can handle its combination of tags.
Before releasing the template version with configurable workspace tags, ensure
that every tag set is associated with at least one healthy provisioner.
### Parameters types
Provisioners require job tags to be defined in plain string format. When a
workspace tag refers to a `coder_parameter` without involving the string
formatter, for example,
(`"runtime" = data.coder_parameter.runtime_selector.value`), the Coder
provisioner server can transform only the following parameter types to strings:
_string_, _number_, and _bool_.
### Mutability
A mutable `coder_parameter` can be dangerous for a workspace tag as it allows
the workspace owner to change a provisioner group (due to different tags). In
most cases, `coder_parameter`s backing `coder_workspace_tags` should be marked
as immutable and set only once, during workspace creation.
### HCL syntax
When importing the template version with `coder_workspace_tags`, the Coder
provisioner server extracts raw partial queries for each workspace tag and
stores them in the database. During workspace build time, the Coder server uses
the [Hashicorp HCL library](https://github.com/hashicorp/hcl) to evaluate these
raw queries on-the-fly without processing the entire Terraform template. This
evaluation is simpler but also limited in terms of available functions,
variables, and references to other resources.
**Supported syntax**
- Static string: `foobar_tag = "foobaz"`
- Formatted string: `foobar_tag = "foobaz ${data.coder_parameter.foobaz.value}"`
- Reference to `coder_parameter`:
`foobar_tag = data.coder_parameter.foobar.value`
- Boolean logic: `production_tag = !data.coder_parameter.staging_env.value`
- Condition:
`cache = data.coder_parameter.feature_cache_enabled.value == "true" ? "with-cache" : "no-cache"`