feat: add base templates for all major cloud providers (#26634)

Add 6 new base templates to the template builder, covering all major
cloud providers and platforms:

- **scratch**: Minimal starter template with only `coder_agent` and
metadata
- **aws-windows**: AWS EC2 Windows instances with PowerShell user_data
- **azure-linux**: Azure VMs with cloud-init and managed disk
persistence
- **gcp-linux**: Google Compute Engine Linux instances with persistent
disk
- **gcp-windows**: Google Compute Engine Windows instances
- **digitalocean-linux**: DigitalOcean Linux droplets with persistent
volumes

Each base includes `base.json`, `main.tf.tmpl`, `README.md` (with
prerequisite markers), and any static files (cloud-init configs). Tests
verify all 9 bases load, render without error, and produce valid
single-agent declarations.

`azure-windows` is deferred; it needs to be registered in the `examples`
package first.

Depends on #26633

> [!NOTE]
> This PR was authored by Coder Agents on behalf of @jeremyruppel.

---

NB: This is very much an agent-generated PR and draws completely from
base templates that exist in `examples/templates/`. The base.json files
are new, so review those, but don't spend any brain tokens on the
correctness of the terraform and supporting files: any issues there are
issues with the upstream example template
This commit is contained in:
Jeremy Ruppel
2026-06-30 18:26:31 -04:00
committed by GitHub
parent 6f0b81cfbc
commit c40b3b9bcb
30 changed files with 2837 additions and 25 deletions
+214
View File
@@ -0,0 +1,214 @@
terraform {
required_providers {
coder = {
source = "coder/coder"
}
aws = {
source = "hashicorp/aws"
}
}
}
# Last updated 2023-03-14
# aws ec2 describe-regions | jq -r '[.Regions[].RegionName] | sort'
data "coder_parameter" "region" {
name = "region"
display_name = "Region"
description = "The region to deploy the workspace in."
default = "us-east-1"
mutable = false
option {
name = "Asia Pacific (Tokyo)"
value = "ap-northeast-1"
icon = "/emojis/1f1ef-1f1f5.png"
}
option {
name = "Asia Pacific (Seoul)"
value = "ap-northeast-2"
icon = "/emojis/1f1f0-1f1f7.png"
}
option {
name = "Asia Pacific (Osaka-Local)"
value = "ap-northeast-3"
icon = "/emojis/1f1f0-1f1f7.png"
}
option {
name = "Asia Pacific (Mumbai)"
value = "ap-south-1"
icon = "/emojis/1f1f0-1f1f7.png"
}
option {
name = "Asia Pacific (Singapore)"
value = "ap-southeast-1"
icon = "/emojis/1f1f0-1f1f7.png"
}
option {
name = "Asia Pacific (Sydney)"
value = "ap-southeast-2"
icon = "/emojis/1f1f0-1f1f7.png"
}
option {
name = "Canada (Central)"
value = "ca-central-1"
icon = "/emojis/1f1e8-1f1e6.png"
}
option {
name = "EU (Frankfurt)"
value = "eu-central-1"
icon = "/emojis/1f1ea-1f1fa.png"
}
option {
name = "EU (Stockholm)"
value = "eu-north-1"
icon = "/emojis/1f1ea-1f1fa.png"
}
option {
name = "EU (Ireland)"
value = "eu-west-1"
icon = "/emojis/1f1ea-1f1fa.png"
}
option {
name = "EU (London)"
value = "eu-west-2"
icon = "/emojis/1f1ea-1f1fa.png"
}
option {
name = "EU (Paris)"
value = "eu-west-3"
icon = "/emojis/1f1ea-1f1fa.png"
}
option {
name = "South America (São Paulo)"
value = "sa-east-1"
icon = "/emojis/1f1e7-1f1f7.png"
}
option {
name = "US East (N. Virginia)"
value = "us-east-1"
icon = "/emojis/1f1fa-1f1f8.png"
}
option {
name = "US East (Ohio)"
value = "us-east-2"
icon = "/emojis/1f1fa-1f1f8.png"
}
option {
name = "US West (N. California)"
value = "us-west-1"
icon = "/emojis/1f1fa-1f1f8.png"
}
option {
name = "US West (Oregon)"
value = "us-west-2"
icon = "/emojis/1f1fa-1f1f8.png"
}
}
data "coder_parameter" "instance_type" {
name = "instance_type"
display_name = "Instance type"
description = "What instance type should your workspace use?"
default = "t3.micro"
mutable = false
option {
name = "2 vCPU, 1 GiB RAM"
value = "t3.micro"
}
option {
name = "2 vCPU, 2 GiB RAM"
value = "t3.small"
}
option {
name = "2 vCPU, 4 GiB RAM"
value = "t3.medium"
}
option {
name = "2 vCPU, 8 GiB RAM"
value = "t3.large"
}
option {
name = "4 vCPU, 16 GiB RAM"
value = "t3.xlarge"
}
option {
name = "8 vCPU, 32 GiB RAM"
value = "t3.2xlarge"
}
}
provider "aws" {
region = data.coder_parameter.region.value
}
data "coder_workspace" "me" {
}
data "coder_workspace_owner" "me" {}
data "aws_ami" "windows" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["Windows_Server-2019-English-Full-Base-*"]
}
}
resource "coder_agent" "main" {
arch = "amd64"
auth = "aws-instance-identity"
os = "windows"
}
locals {
# User data is used to stop/start AWS instances. See:
# https://github.com/hashicorp/terraform-provider-aws/issues/22
user_data_start = <<EOT
<powershell>
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
${coder_agent.main.init_script}
</powershell>
<persist>true</persist>
EOT
user_data_end = <<EOT
<powershell>
shutdown /s
</powershell>
<persist>true</persist>
EOT
}
resource "aws_instance" "dev" {
ami = data.aws_ami.windows.id
availability_zone = "${data.coder_parameter.region.value}a"
instance_type = data.coder_parameter.instance_type.value
user_data = data.coder_workspace.me.transition == "start" ? local.user_data_start : local.user_data_end
tags = {
Name = "coder-${data.coder_workspace_owner.me.name}-${data.coder_workspace.me.name}"
# Required if you are using our example policy, see template README
Coder_Provisioned = "true"
}
lifecycle {
ignore_changes = [ami]
}
}
resource "coder_metadata" "workspace_info" {
resource_id = aws_instance.dev.id
item {
key = "region"
value = data.coder_parameter.region.value
}
item {
key = "instance type"
value = aws_instance.dev.instance_type
}
item {
key = "disk"
value = "${aws_instance.dev.root_block_device[0].volume_size} GiB"
}
}
+293
View File
@@ -0,0 +1,293 @@
terraform {
required_providers {
coder = {
source = "coder/coder"
}
azurerm = {
source = "hashicorp/azurerm"
}
cloudinit = {
source = "hashicorp/cloudinit"
}
}
}
# See https://registry.coder.com/modules/coder/azure-region
module "azure_region" {
source = "registry.coder.com/coder/azure-region/coder"
# This ensures that the latest non-breaking version of the module gets downloaded, you can also pin the module version to prevent breaking changes in production.
version = "~> 1.0"
default = "eastus"
}
data "coder_parameter" "instance_type" {
name = "instance_type"
display_name = "Instance type"
description = "What instance type should your workspace use?"
default = "Standard_B4ms"
icon = "/icon/azure.png"
mutable = false
option {
name = "Standard_B1ms (1 vCPU, 2 GiB RAM)"
value = "Standard_B1ms"
}
option {
name = "Standard_B2ms (2 vCPU, 8 GiB RAM)"
value = "Standard_B2ms"
}
option {
name = "Standard_B4ms (4 vCPU, 16 GiB RAM)"
value = "Standard_B4ms"
}
option {
name = "Standard_B8ms (8 vCPU, 32 GiB RAM)"
value = "Standard_B8ms"
}
option {
name = "Standard_B12ms (12 vCPU, 48 GiB RAM)"
value = "Standard_B12ms"
}
option {
name = "Standard_B16ms (16 vCPU, 64 GiB RAM)"
value = "Standard_B16ms"
}
option {
name = "Standard_D2as_v5 (2 vCPU, 8 GiB RAM)"
value = "Standard_D2as_v5"
}
option {
name = "Standard_D4as_v5 (4 vCPU, 16 GiB RAM)"
value = "Standard_D4as_v5"
}
option {
name = "Standard_D8as_v5 (8 vCPU, 32 GiB RAM)"
value = "Standard_D8as_v5"
}
option {
name = "Standard_D16as_v5 (16 vCPU, 64 GiB RAM)"
value = "Standard_D16as_v5"
}
option {
name = "Standard_D32as_v5 (32 vCPU, 128 GiB RAM)"
value = "Standard_D32as_v5"
}
}
data "coder_parameter" "home_size" {
name = "home_size"
display_name = "Home volume size"
description = "How large would you like your home volume to be (in GB)?"
default = 20
type = "number"
icon = "/icon/azure.png"
mutable = false
validation {
min = 1
max = 1024
}
}
provider "azurerm" {
features {}
}
data "coder_workspace" "me" {}
data "coder_workspace_owner" "me" {}
resource "coder_agent" "main" {
arch = "amd64"
os = "linux"
auth = "azure-instance-identity"
metadata {
key = "cpu"
display_name = "CPU Usage"
interval = 5
timeout = 5
script = <<-EOT
#!/bin/bash
set -e
top -bn1 | grep "Cpu(s)" | awk '{print $2 + $4 "%"}'
EOT
}
metadata {
key = "memory"
display_name = "Memory Usage"
interval = 5
timeout = 5
script = <<-EOT
#!/bin/bash
set -e
free -m | awk 'NR==2{printf "%.2f%%\t", $3*100/$2 }'
EOT
}
metadata {
key = "disk"
display_name = "Disk Usage"
interval = 600 # every 10 minutes
timeout = 30 # df can take a while on large filesystems
script = <<-EOT
#!/bin/bash
set -e
df /home/coder | awk '$NF=="/"{printf "%s", $5}'
EOT
}
}
locals {
prefix = "coder-${data.coder_workspace_owner.me.name}-${data.coder_workspace.me.name}"
}
data "cloudinit_config" "user_data" {
gzip = false
base64_encode = true
boundary = "//"
part {
filename = "cloud-config.yaml"
content_type = "text/cloud-config"
content = templatefile("${path.module}/cloud-init/cloud-config.yaml.tftpl", {
username = "coder" # Ensure this user/group does not exist in your VM image
init_script = base64encode(coder_agent.main.init_script)
hostname = lower(data.coder_workspace.me.name)
})
}
}
resource "azurerm_resource_group" "main" {
name = "${local.prefix}-resources"
location = module.azure_region.value
tags = {
Coder_Provisioned = "true"
}
}
// Uncomment here and in the azurerm_network_interface resource to obtain a public IP
#resource "azurerm_public_ip" "main" {
# name = "publicip"
# resource_group_name = azurerm_resource_group.main.name
# location = azurerm_resource_group.main.location
# allocation_method = "Static"
#
# tags = {
# Coder_Provisioned = "true"
# }
#}
resource "azurerm_virtual_network" "main" {
name = "network"
address_space = ["10.0.0.0/24"]
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
tags = {
Coder_Provisioned = "true"
}
}
resource "azurerm_subnet" "internal" {
name = "internal"
resource_group_name = azurerm_resource_group.main.name
virtual_network_name = azurerm_virtual_network.main.name
address_prefixes = ["10.0.0.0/29"]
}
resource "azurerm_network_interface" "main" {
name = "nic"
resource_group_name = azurerm_resource_group.main.name
location = azurerm_resource_group.main.location
ip_configuration {
name = "internal"
subnet_id = azurerm_subnet.internal.id
private_ip_address_allocation = "Dynamic"
// Uncomment for public IP address as well as azurerm_public_ip resource above
//public_ip_address_id = azurerm_public_ip.main.id
}
tags = {
Coder_Provisioned = "true"
}
}
resource "azurerm_managed_disk" "home" {
create_option = "Empty"
location = azurerm_resource_group.main.location
name = "home"
resource_group_name = azurerm_resource_group.main.name
storage_account_type = "StandardSSD_LRS"
disk_size_gb = data.coder_parameter.home_size.value
}
// azurerm requires an SSH key (or password) for an admin user or it won't start a VM. However,
// cloud-init overwrites this anyway, so we'll just use a dummy SSH key.
resource "tls_private_key" "dummy" {
algorithm = "RSA"
rsa_bits = 4096
}
resource "azurerm_linux_virtual_machine" "main" {
count = data.coder_workspace.me.start_count
name = "vm"
resource_group_name = azurerm_resource_group.main.name
location = azurerm_resource_group.main.location
size = data.coder_parameter.instance_type.value
// cloud-init overwrites this, so the value here doesn't matter
admin_username = "adminuser"
admin_ssh_key {
public_key = tls_private_key.dummy.public_key_openssh
username = "adminuser"
}
network_interface_ids = [
azurerm_network_interface.main.id,
]
computer_name = lower(data.coder_workspace.me.name)
os_disk {
caching = "ReadWrite"
storage_account_type = "Standard_LRS"
}
source_image_reference {
publisher = "Canonical"
offer = "0001-com-ubuntu-server-focal"
sku = "20_04-lts-gen2"
version = "latest"
}
user_data = data.cloudinit_config.user_data.rendered
tags = {
Coder_Provisioned = "true"
}
}
resource "azurerm_virtual_machine_data_disk_attachment" "home" {
count = data.coder_workspace.me.transition == "start" ? 1 : 0
managed_disk_id = azurerm_managed_disk.home.id
virtual_machine_id = azurerm_linux_virtual_machine.main[0].id
lun = "10"
caching = "ReadWrite"
}
resource "coder_metadata" "workspace_info" {
count = data.coder_workspace.me.start_count
resource_id = azurerm_linux_virtual_machine.main[0].id
item {
key = "type"
value = azurerm_linux_virtual_machine.main[0].size
}
}
resource "coder_metadata" "home_info" {
resource_id = azurerm_managed_disk.home.id
item {
key = "size"
value = "${data.coder_parameter.home_size.value} GiB"
}
}
@@ -0,0 +1,329 @@
terraform {
required_providers {
coder = {
source = "coder/coder"
}
digitalocean = {
source = "digitalocean/digitalocean"
}
}
}
provider "coder" {}
variable "project_uuid" {
type = string
description = <<-EOF
DigitalOcean project ID
$ doctl projects list
EOF
sensitive = true
validation {
# make sure length of alphanumeric string is 36 (UUIDv4 size)
condition = length(var.project_uuid) == 36
error_message = "Invalid Digital Ocean Project ID."
}
}
variable "ssh_key_id" {
type = number
description = <<-EOF
DigitalOcean SSH key ID (some Droplet images require an SSH key to be set):
Can be set to "0" for no key.
Note: Setting this to zero will break Fedora images and notify root passwords via email.
$ doctl compute ssh-key list
EOF
sensitive = true
default = 0
validation {
condition = var.ssh_key_id >= 0
error_message = "Invalid Digital Ocean SSH key ID, a number is required."
}
}
data "coder_parameter" "droplet_image" {
name = "droplet_image"
display_name = "Droplet image"
description = "Which Droplet image would you like to use?"
default = "ubuntu-22-04-x64"
type = "string"
mutable = false
option {
name = "AlmaLinux 9"
value = "almalinux-9-x64"
icon = "/icon/almalinux.svg"
}
option {
name = "AlmaLinux 8"
value = "almalinux-8-x64"
icon = "/icon/almalinux.svg"
}
option {
name = "Fedora 39"
value = "fedora-39-x64"
icon = "/icon/fedora.svg"
}
option {
name = "Fedora 38"
value = "fedora-38-x64"
icon = "/icon/fedora.svg"
}
option {
name = "CentOS Stream 9"
value = "centos-stream-9-x64"
icon = "/icon/centos.svg"
}
option {
name = "CentOS Stream 8"
value = "centos-stream-8-x64"
icon = "/icon/centos.svg"
}
option {
name = "Debian 12"
value = "debian-12-x64"
icon = "/icon/debian.svg"
}
option {
name = "Debian 11"
value = "debian-11-x64"
icon = "/icon/debian.svg"
}
option {
name = "Debian 10"
value = "debian-10-x64"
icon = "/icon/debian.svg"
}
option {
name = "Rocky Linux 9"
value = "rockylinux-9-x64"
icon = "/icon/rockylinux.svg"
}
option {
name = "Rocky Linux 8"
value = "rockylinux-8-x64"
icon = "/icon/rockylinux.svg"
}
option {
name = "Ubuntu 22.04 (LTS)"
value = "ubuntu-22-04-x64"
icon = "/icon/ubuntu.svg"
}
option {
name = "Ubuntu 20.04 (LTS)"
value = "ubuntu-20-04-x64"
icon = "/icon/ubuntu.svg"
}
}
data "coder_parameter" "droplet_size" {
name = "droplet_size"
display_name = "Droplet size"
description = "Which Droplet configuration would you like to use?"
default = "s-1vcpu-1gb"
type = "string"
icon = "/icon/memory.svg"
mutable = false
# s-1vcpu-512mb-10gb is unsupported in tor1, blr1, lon1, sfo2, and nyc3 regions
# s-8vcpu-16gb access requires a support ticket with Digital Ocean
option {
name = "1 vCPU, 1 GB RAM"
value = "s-1vcpu-1gb"
}
option {
name = "1 vCPU, 2 GB RAM"
value = "s-1vcpu-2gb"
}
option {
name = "2 vCPU, 2 GB RAM"
value = "s-2vcpu-2gb"
}
option {
name = "2 vCPU, 4 GB RAM"
value = "s-2vcpu-4gb"
}
option {
name = "4 vCPU, 8 GB RAM"
value = "s-4vcpu-8gb"
}
}
data "coder_parameter" "home_volume_size" {
name = "home_volume_size"
display_name = "Home volume size"
description = "How large would you like your home volume to be (in GB)?"
type = "number"
default = "20"
mutable = false
validation {
min = 1
max = 100 # Sizes larger than 100 GB require a support ticket with Digital Ocean
}
}
data "coder_parameter" "region" {
name = "region"
display_name = "Region"
description = "This is the region where your workspace will be created."
icon = "/emojis/1f30e.png"
type = "string"
default = "ams3"
mutable = false
# nyc1, sfo1, and ams2 regions were excluded because they do not support volumes, which are used to persist data while decreasing cost
option {
name = "Canada (Toronto)"
value = "tor1"
icon = "/emojis/1f1e8-1f1e6.png"
}
option {
name = "Germany (Frankfurt)"
value = "fra1"
icon = "/emojis/1f1e9-1f1ea.png"
}
option {
name = "India (Bangalore)"
value = "blr1"
icon = "/emojis/1f1ee-1f1f3.png"
}
option {
name = "Netherlands (Amsterdam)"
value = "ams3"
icon = "/emojis/1f1f3-1f1f1.png"
}
option {
name = "Singapore"
value = "sgp1"
icon = "/emojis/1f1f8-1f1ec.png"
}
option {
name = "United Kingdom (London)"
value = "lon1"
icon = "/emojis/1f1ec-1f1e7.png"
}
option {
name = "United States (California - 2)"
value = "sfo2"
icon = "/emojis/1f1fa-1f1f8.png"
}
option {
name = "United States (California - 3)"
value = "sfo3"
icon = "/emojis/1f1fa-1f1f8.png"
}
option {
name = "United States (New York - 1)"
value = "nyc1"
icon = "/emojis/1f1fa-1f1f8.png"
}
option {
name = "United States (New York - 3)"
value = "nyc3"
icon = "/emojis/1f1fa-1f1f8.png"
}
}
# Configure the DigitalOcean Provider
provider "digitalocean" {
# Recommended: use environment variable DIGITALOCEAN_TOKEN with your personal access token when starting coderd
# alternatively, you can pass the token via a variable.
}
data "coder_workspace" "me" {}
data "coder_workspace_owner" "me" {}
resource "coder_agent" "main" {
os = "linux"
arch = "amd64"
metadata {
key = "cpu"
display_name = "CPU Usage"
interval = 5
timeout = 5
script = "coder stat cpu"
}
metadata {
key = "memory"
display_name = "Memory Usage"
interval = 5
timeout = 5
script = "coder stat mem"
}
metadata {
key = "home"
display_name = "Home Usage"
interval = 600 # every 10 minutes
timeout = 30 # df can take a while on large filesystems
script = "coder stat disk --path /home/${lower(data.coder_workspace_owner.me.name)}"
}
}
resource "digitalocean_volume" "home_volume" {
region = data.coder_parameter.region.value
name = "coder-${data.coder_workspace.me.id}-home"
size = data.coder_parameter.home_volume_size.value
initial_filesystem_type = "ext4"
initial_filesystem_label = "coder-home"
# Protect the volume from being deleted due to changes in attributes.
lifecycle {
ignore_changes = all
}
}
resource "digitalocean_droplet" "workspace" {
region = data.coder_parameter.region.value
count = data.coder_workspace.me.start_count
name = "coder-${lower(data.coder_workspace_owner.me.name)}-${lower(data.coder_workspace.me.name)}"
image = data.coder_parameter.droplet_image.value
size = data.coder_parameter.droplet_size.value
volume_ids = [digitalocean_volume.home_volume.id]
user_data = templatefile("cloud-config.yaml.tftpl", {
username = lower(data.coder_workspace_owner.me.name)
home_volume_label = digitalocean_volume.home_volume.initial_filesystem_label
init_script = base64encode(coder_agent.main.init_script)
coder_agent_token = coder_agent.main.token
})
# Required to provision Fedora.
ssh_keys = var.ssh_key_id > 0 ? [var.ssh_key_id] : []
}
resource "digitalocean_project_resources" "project" {
project = var.project_uuid
# Workaround for terraform plan when using count.
resources = length(digitalocean_droplet.workspace) > 0 ? [
digitalocean_volume.home_volume.urn,
digitalocean_droplet.workspace[0].urn
] : [
digitalocean_volume.home_volume.urn
]
}
resource "coder_metadata" "workspace-info" {
count = data.coder_workspace.me.start_count
resource_id = digitalocean_droplet.workspace[0].id
item {
key = "region"
value = digitalocean_droplet.workspace[0].region
}
item {
key = "image"
value = digitalocean_droplet.workspace[0].image
}
}
resource "coder_metadata" "volume-info" {
resource_id = digitalocean_volume.home_volume.id
item {
key = "size"
value = "${digitalocean_volume.home_volume.size} GiB"
}
}
+152
View File
@@ -0,0 +1,152 @@
terraform {
required_providers {
coder = {
source = "coder/coder"
}
google = {
source = "hashicorp/google"
}
}
}
provider "coder" {}
variable "project_id" {
description = "Which Google Compute Project should your workspace live in?"
}
# See https://registry.coder.com/modules/coder/gcp-region
module "gcp_region" {
source = "registry.coder.com/coder/gcp-region/coder"
# This ensures that the latest non-breaking version of the module gets downloaded, you can also pin the module version to prevent breaking changes in production.
version = "~> 1.0"
regions = ["us", "europe"]
default = "us-central1-a"
}
provider "google" {
zone = module.gcp_region.value
project = var.project_id
}
data "google_compute_default_service_account" "default" {}
data "coder_workspace" "me" {}
data "coder_workspace_owner" "me" {}
resource "google_compute_disk" "root" {
name = "coder-${data.coder_workspace.me.id}-root"
type = "pd-ssd"
zone = module.gcp_region.value
image = "debian-cloud/debian-11"
lifecycle {
ignore_changes = [name, image]
}
}
resource "coder_agent" "main" {
auth = "google-instance-identity"
arch = "amd64"
os = "linux"
startup_script = <<-EOT
set -e
# Add any commands that should be executed at workspace startup (e.g install requirements, start a program, etc) here
EOT
metadata {
key = "cpu"
display_name = "CPU Usage"
interval = 5
timeout = 5
script = <<-EOT
#!/bin/bash
set -e
top -bn1 | grep "Cpu(s)" | awk '{print $2 + $4 "%"}'
EOT
}
metadata {
key = "memory"
display_name = "Memory Usage"
interval = 5
timeout = 5
script = <<-EOT
#!/bin/bash
set -e
free -m | awk 'NR==2{printf "%.2f%%\t", $3*100/$2 }'
EOT
}
metadata {
key = "disk"
display_name = "Disk Usage"
interval = 600 # every 10 minutes
timeout = 30 # df can take a while on large filesystems
script = <<-EOT
#!/bin/bash
set -e
df /home/coder | awk '$NF=="/"{printf "%s", $5}'
EOT
}
}
resource "google_compute_instance" "dev" {
zone = module.gcp_region.value
count = data.coder_workspace.me.start_count
name = "coder-${lower(data.coder_workspace_owner.me.name)}-${lower(data.coder_workspace.me.name)}-root"
machine_type = "e2-medium"
network_interface {
network = "default"
access_config {
// Ephemeral public IP
}
}
boot_disk {
auto_delete = false
source = google_compute_disk.root.name
}
service_account {
email = data.google_compute_default_service_account.default.email
scopes = ["cloud-platform"]
}
# The startup script runs as root with no $HOME environment set up, so instead of directly
# running the agent init script, create a user (with a homedir, default shell and sudo
# permissions) and execute the init script as that user.
metadata_startup_script = <<EOMETA
#!/usr/bin/env sh
set -eux
# If user does not exist, create it and set up passwordless sudo
if ! id -u "${local.linux_user}" >/dev/null 2>&1; then
useradd -m -s /bin/bash "${local.linux_user}"
echo "${local.linux_user} ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/coder-user
fi
exec sudo -u "${local.linux_user}" sh -c '${coder_agent.main.init_script}'
EOMETA
}
locals {
# Ensure Coder username is a valid Linux username
linux_user = lower(substr(data.coder_workspace_owner.me.name, 0, 32))
}
resource "coder_metadata" "workspace_info" {
count = data.coder_workspace.me.start_count
resource_id = google_compute_instance.dev[0].id
item {
key = "type"
value = google_compute_instance.dev[0].machine_type
}
}
resource "coder_metadata" "home_info" {
resource_id = google_compute_disk.root.id
item {
key = "size"
value = "${google_compute_disk.root.size} GiB"
}
}
+96
View File
@@ -0,0 +1,96 @@
terraform {
required_providers {
coder = {
source = "coder/coder"
}
google = {
source = "hashicorp/google"
}
}
}
provider "coder" {}
variable "project_id" {
description = "Which Google Compute Project should your workspace live in?"
}
# See https://registry.coder.com/modules/coder/gcp-region
module "gcp_region" {
source = "registry.coder.com/coder/gcp-region/coder"
# This ensures that the latest non-breaking version of the module gets downloaded, you can also pin the module version to prevent breaking changes in production.
version = "~> 1.0"
regions = ["us", "europe"]
default = "us-central1-a"
}
provider "google" {
zone = module.gcp_region.value
project = var.project_id
}
data "coder_workspace" "me" {}
data "coder_workspace_owner" "me" {}
data "google_compute_default_service_account" "default" {}
resource "google_compute_disk" "root" {
name = "coder-${data.coder_workspace.me.id}-root"
type = "pd-ssd"
zone = module.gcp_region.value
image = "projects/windows-cloud/global/images/windows-server-2022-dc-core-v20220215"
lifecycle {
ignore_changes = [name, image]
}
}
resource "coder_agent" "main" {
auth = "google-instance-identity"
arch = "amd64"
os = "windows"
}
resource "google_compute_instance" "dev" {
zone = module.gcp_region.value
count = data.coder_workspace.me.start_count
name = "coder-${lower(data.coder_workspace_owner.me.name)}-${lower(data.coder_workspace.me.name)}"
machine_type = "e2-medium"
network_interface {
network = "default"
access_config {
// Ephemeral public IP
}
}
boot_disk {
auto_delete = false
source = google_compute_disk.root.name
}
service_account {
email = data.google_compute_default_service_account.default.email
scopes = ["cloud-platform"]
}
metadata = {
windows-startup-script-ps1 = coder_agent.main.init_script
serial-port-enable = "TRUE"
}
}
resource "coder_metadata" "workspace_info" {
count = data.coder_workspace.me.start_count
resource_id = google_compute_instance.dev[0].id
item {
key = "type"
value = google_compute_instance.dev[0].machine_type
}
}
resource "coder_metadata" "home_info" {
resource_id = google_compute_disk.root.id
item {
key = "size"
value = "${google_compute_disk.root.size} GiB"
}
}
+40
View File
@@ -0,0 +1,40 @@
terraform {
required_providers {
coder = {
source = "coder/coder"
}
}
}
data "coder_provisioner" "me" {}
data "coder_workspace" "me" {}
resource "coder_agent" "main" {
arch = data.coder_provisioner.me.arch
os = data.coder_provisioner.me.os
metadata {
display_name = "CPU Usage"
key = "0_cpu_usage"
script = "coder stat cpu"
interval = 10
timeout = 1
}
metadata {
display_name = "RAM Usage"
key = "1_ram_usage"
script = "coder stat mem"
interval = 10
timeout = 1
}
}
# Use this to set environment variables in your workspace
# details: https://registry.terraform.io/providers/coder/coder/latest/docs/resources/env
resource "coder_env" "welcome_message" {
agent_id = coder_agent.main.id
name = "WELCOME_MESSAGE"
value = "Welcome to your Coder workspace!"
}