-
Notifications
You must be signed in to change notification settings - Fork 1k
Open
Labels
needs-triageIssue that require triageIssue that require triage
Description
Is there an existing issue for this?
- I have searched the existing issues
Current Behavior
Version: Coder v2.25.2 (reproduces on both “beta” and “stable" versions 2.24 and 2.25)
When dynamic parameters are enabled for a template, the UI shows a warning that a local Terraform module cannot be resolved and will be ignored:
Module 'module "foo"' in file "main.tf:177,1-13" cannot be resolved. This module will be ignored.
The exact same template (and module) work as expected in:
- Previous Coder versions, and
- Templates that do not use dynamic parameters.
The warning blocks/invalidates the module reference in the dynamic-parameters form preview, even though the module is available at runtime. This makes templates that rely on local modules appear broken and can confuse end users.

Relevant Log Output
Expected Behavior
- Dynamic parameters preview should not fail module resolution, or
- Coder should run an init/module download step (or otherwise resolve local paths) in whatever environment parses the template for dynamic parameters, or
- If resolution isn’t possible at preview time, the UI should tolerate unresolved modules without discarding them (no “will be ignored”), since they resolve at apply time in the agent.
Banner doesn't show in non-dynamic parameter templates:

Steps to Reproduce
- Use a Terraform module with a local/absolute source path mounted into the workspace (e.g., /opt/terraform-modules). I mounted a volume and referenced it in my template.
resource "null_resource" "dummy_action" {
provisioner "local-exec" {
command = "echo 'hello world'"
}
}
output "message" {
value = "Foo module dummy action completed"
}
- Create a dummy template.
This is the dummy template I used:
# Local values for convenience
locals {
workspace_name = lower("${data.coder_workspace.me.owner}-${data.coder_workspace.me.name}")
# Simulated resource status
fake_resource_status = {
container_id = "dummy-${substr(md5(local.workspace_name), 0, 12)}"
ip_address = "10.0.0.${(data.coder_workspace.me.transition == "start" ? "100" : "0")}"
status = data.coder_workspace.me.transition == "start" ? "running" : "stopped"
}
}
# Dummy resource that represents our "infrastructure"
resource "null_resource" "workspace" {
count = data.coder_workspace.me.start_count
triggers = {
workspace_id = data.coder_workspace.me.id
workspace_name = local.workspace_name
cpu = data.coder_parameter.cpu.value
memory = data.coder_parameter.memory.value
always_run = timestamp()
}
# Simulate provisioning
provisioner "local-exec" {
command = "echo 'Starting dummy workspace ${local.workspace_name} with ${data.coder_parameter.cpu.value} CPUs and ${data.coder_parameter.memory.value}GB RAM'"
}
# Simulate cleanup
provisioner "local-exec" {
when = destroy
command = "echo 'Stopping dummy workspace ${self.triggers.workspace_name}'"
}
}
# Create a dummy state file to simulate persistence
resource "local_file" "workspace_state" {
count = data.coder_workspace.me.start_count
filename = "/tmp/coder-dummy-${local.workspace_name}.state"
content = jsonencode({
workspace_id = data.coder_workspace.me.id
owner = data.coder_workspace.me.owner
name = data.coder_workspace.me.name
created_at = timestamp()
resources = {
cpu = data.coder_parameter.cpu.value
memory = data.coder_parameter.memory.value
disk = data.coder_parameter.disk_size.value
}
dotfiles_repo = data.coder_parameter.dotfiles_repo.value
status = local.fake_resource_status
})
}
# Coder agent - this is required for Coder to manage the workspace
resource "coder_agent" "main" {
arch = "amd64"
os = "linux"
# Startup script that simulates workspace initialization
startup_script = <<-EOT
#!/bin/bash
set -e
echo "Starting dummy workspace..."
echo "Resources allocated:"
echo " - CPU: ${data.coder_parameter.cpu.value} cores"
echo " - Memory: ${data.coder_parameter.memory.value} GB"
echo " - Disk: ${data.coder_parameter.disk_size.value} GB"
# Simulate dotfiles setup
if [ -n "${data.coder_parameter.dotfiles_repo.value}" ]; then
echo "📁 Would clone dotfiles from: ${data.coder_parameter.dotfiles_repo.value}"
echo " (This is a dummy workspace - no actual cloning performed)"
fi
# Create a dummy process to keep the agent alive
echo "Dummy workspace ready!"
echo "This is a simulated environment for testing purposes"
# Keep the agent running
while true; do
sleep 30
echo "Dummy workspace heartbeat at $(date)"
done &
EOT
# Connection options
display_apps {
vscode = true
vscode_insiders = false
web_terminal = true
ssh_helper = false # SSH not available in dummy mode
}
}
# Dummy VS Code app (simulated)
resource "coder_app" "code-server" {
agent_id = coder_agent.main.id
slug = "vscode"
display_name = "VS Code (Dummy)"
url = "http://localhost:13337?folder=/tmp/dummy-workspace"
icon = "/icon/code.svg"
subdomain = false
share = "owner"
healthcheck {
url = "http://localhost:13337/healthz"
interval = 30
threshold = 3
}
}
# Web terminal (simulated)
resource "coder_app" "terminal" {
agent_id = coder_agent.main.id
slug = "terminal"
display_name = "Terminal (Dummy)"
icon = "/icon/terminal.svg"
command = "echo 'This is a dummy terminal - no real shell available' && cat"
}
# Status page showing dummy info
resource "coder_app" "status" {
agent_id = coder_agent.main.id
slug = "status"
display_name = "Workspace Status"
icon = "/icon/info.svg"
url = "http://localhost:8080"
subdomain = false
share = "owner"
}
# Metadata for the Coder UI
resource "coder_metadata" "workspace_info" {
count = data.coder_workspace.me.start_count
resource_id = null_resource.workspace[0].id
item {
key = "Type"
value = "Dummy Workspace"
}
item {
key = "CPU"
value = "${data.coder_parameter.cpu.value} cores (simulated)"
}
item {
key = "Memory"
value = "${data.coder_parameter.memory.value} GB (simulated)"
}
item {
key = "Disk"
value = "${data.coder_parameter.disk_size.value} GB (simulated)"
}
item {
key = "Container ID"
value = local.fake_resource_status.container_id
}
item {
key = "IP Address"
value = local.fake_resource_status.ip_address
}
item {
key = "Status"
value = local.fake_resource_status.status
}
}
# Call the foo module dummy action (THIS WILL CAUSE A WARNING TO OCCUR WHEN DYNAMIC PARAMETERS ARE ENABLED)
module "foo" {
source = "/opt/terraform-modules"
}
# Show some helpful info in the logs
resource "null_resource" "startup_message" {
count = data.coder_workspace.me.start_count
provisioner "local-exec" {
command = <<-EOT
echo "=========================================="
echo "DUMMY WORKSPACE CREATED"
echo "=========================================="
echo "This is a simulated workspace for testing."
echo "No real resources have been provisioned."
echo ""
echo "Workspace Details:"
echo "- Name: ${local.workspace_name}"
echo "- Owner: ${data.coder_workspace.me.owner}"
echo "- ID: ${data.coder_workspace.me.id}"
echo ""
echo "Simulated Resources:"
echo "- CPU: ${data.coder_parameter.cpu.value} cores"
echo "- Memory: ${data.coder_parameter.memory.value} GB"
echo "- Disk: ${data.coder_parameter.disk_size.value} GB"
echo ""
echo "Foo module message: ${module.foo.message}"!
echo "=========================================="
EOT
}
}
- Open “Create workspace” and observe the “Module not loaded…” warning shown under Parameters.
Environment
- Host OS: Debian GNU/Linux 12 (bookworm)
- Coder version: 2.25.2, 2.24.4
Additional Context
I have tested this on the latest version
Metadata
Metadata
Assignees
Labels
needs-triageIssue that require triageIssue that require triage