Skip to content

Provision local Windows-Users on a Terminal Server #13853

Closed
@DavidBal

Description

@DavidBal

Hi,

my target is to create a coder Template which creates a local Users on a Windows Terminal Server and then run the coder stuff per user.

The user creation works fine, after the creation the workspace look's like this:

image

My terraform file looks like this:

terraform {
  required_providers {
    coder = {
      source = "coder/coder"
    }
  }
}

data "coder_provisioner" "me" {}

data "coder_workspace" "me" {}

data "coder_workspace_owner" "me" {}

locals{
  admin_user = "c***\\coder"
  admin_pw = "coder****"
  machine = "Coder01"

  user = "${substr(data.coder_workspace_owner.me.name,0, 11)}_${substr(data.coder_workspace.me.id, 0, 4)}${substr(data.coder_workspace.me.id, -4, -1)}"
  password = data.coder_workspace.me.id
  buildScript = "C:/coder/${data.coder_workspace_owner.me.name}_${data.coder_workspace.me.id}_coder_build.ps1"
  removeScript = "C:/coder/${data.coder_workspace_owner.me.name}_${data.coder_workspace.me.id}_coder_remove.ps1"
  coderScript = "C:/coder/${data.coder_workspace_owner.me.name}_${data.coder_workspace.me.id}_coder_init.ps1"
}

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!"
}

# Adds code-server
# See all available modules at https://registry.coder.com
module "code-server" {
  source   = "registry.coder.com/modules/code-server/coder"
  version  = "1.0.2"
  agent_id = coder_agent.main.id
}

# Runs a script at workspace start/stop or on a cron schedule
# details: https://registry.terraform.io/providers/coder/coder/latest/docs/resources/script
# resource "coder_script" "startup_script" {
#   agent_id           = coder_agent.main.id
#   display_name       = "Startup Script"
#   run_on_start       = true
#   start_blocks_login = true
# }


resource "terraform_data" "build" {
  input = [
     local.removeScript,
     local.admin_user,
     local.admin_pw,
     local.machine
  ]

  connection {
    type = "ssh"
    user = self.input[1]
    password = self.input[2]
    host = self.input[3]
    target_platform = "windows"
  }

  provisioner "file" {
    destination = local.buildScript
    content = <<-EOT
      $password = ConvertTo-SecureString "${local.password}" -AsPlainText -Force
      New-LocalUser -AccountNeverExpires -Name "${local.user}" -Password $password
      Add-LocalGroupMember -Group "Remotedesktopbenutzer" -Member "${local.user}"

      # init local user
      $Cred = [System.Management.Automation.PSCredential]::new("${local.user}", (ConvertTo-SecureString "${local.password}" -AsPlainText -Force))
      Start-Process "cmd.exe" -Credential $Cred -ArgumentList "/C"
    EOT
  }

  provisioner "file" {
    destination = local.coderScript
    content = <<-EOT
      # Sleep for a while in case the underlying provider deletes the resource on error.
      trap {
        Write-Error "=== Agent script exited with non-zero code. Sleeping 24h to preserve logs..."
        Start-Sleep -Seconds 86400
      }
      
      $coder_exe = "C:\coder\coder-windows-amd64.exe"
      if ((Test-Path $coder_exe))
      {
          Write-Output "$(Get-Date) Kopiere coder-windows-amd64.exe"
          Copy-Item $coder_exe -Destination $env:TEMP\sshd.exe
      }
      else
      {
          # Attempt to download the coder agent.
          # This could fail for a number of reasons, many of which are likely transient.
          # So just keep trying!
          while ($true) {
            try {
              $ProgressPreference = "SilentlyContinue"
              # On Windows, VS Code Remote requires a parent process of the
              # executing shell to be named "sshd", otherwise it fails. See:
              # https://github.com/microsoft/vscode-remote-release/issues/5699
              $BINARY_URL="${data.coder_workspace.me.access_url}/bin/coder-windows-amd64.exe"
              Write-Output "$(Get-Date) Fetching coder agent from $${BINARY_URL}"
              Invoke-WebRequest -Uri "$${BINARY_URL}" -OutFile $env:TEMP\sshd.exe
              break
            } catch {
              Write-Output "$(Get-Date) error: unhandled exception fetching coder agent:"
              Write-Output $_
              Write-Output "$(Get-Date) trying again in 30 seconds..."
              Start-Sleep -Seconds 30
            }
          }
      }
      # Check if running in a Windows container
      if (-not (Get-Command 'Set-MpPreference' -ErrorAction SilentlyContinue)) {
          Write-Output "$(Get-Date) Set-MpPreference not available, skipping..."
      } else {
          Write-Output "$(Get-Date) Set-MpPreference skipping..."
          #Set-MpPreference -DisableRealtimeMonitoring $true -ExclusionPath $env:TEMP\sshd.exe
      }

      $env:CODER_AGENT_AUTH = "token"
      $env:CODER_AGENT_URL = "${data.coder_workspace.me.access_url}"
      $env:CODER_AGENT_TOKEN = "${coder_agent.main.token}"

      # Check if we're running inside a Windows container!
      $inContainer = $false
      if ((Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control' -Name 'ContainerType' -ErrorAction SilentlyContinue) -ne $null) {
          $inContainer = $true
      }
      if ($inContainer) {
          # If we're in a container, run in a the foreground!
          Start-Process -FilePath $env:TEMP\sshd.exe -ArgumentList "agent" -Wait -NoNewWindow
      } else {
          Start-Process -FilePath $env:TEMP\sshd.exe -ArgumentList "agent" -PassThru
      }
    EOT
  }

  provisioner "file" {
    destination = local.removeScript
    content = <<-EOT
      Remove-LocalUser -Name "${local.user}"
      Remove-Item "C:\Users\${local.user}" -Recurse -Force
      Remove-Item "${local.removeScript}" -Force
      Remove-Item "${local.coderScript}" -Force
    EOT
  }

  provisioner "remote-exec"{
    
    inline = [
      "powershell.exe \"${local.buildScript}\""
    ]
  }

  provisioner "remote-exec"{
    when = destroy
    inline = [
      "powershell.exe \"${self.input[0]}\"",
      #"del /f \"${self.input[0]}\"",
    ]
  }
}

resource "terraform_data" "startup" {
  connection {
    type = "ssh"
    target_platform = "windows"
    host = local.machine
    user = local.user
    password = local.password
  }

  provisioner "remote-exec"{
    inline = [
      "powershell.exe \"${local.coderScript}\"",
    ]
  }
}

resource "coder_app" "rdp" {
  agent_id     = coder_agent.main.id
  display_name = "RDP Desktop"
  slug         = "rdp"
  icon         = "https://raw.githubusercontent.com/matifali/logos/main/windows.svg" 
  url          = "http://localhost" #replace
  subdomain    = true
  share        = "owner"
  healthcheck {
    url       = "http://localhost" #replace
    interval  = 3
    threshold = 120
  }
}

I think there is a problem with starting up the "coder-windows-amd64.exe".

Any idea why that would fail?
Is my scenario supported by coder?

Thanks for your help in advance.

Best regards,
David

Metadata

Metadata

Assignees

No one assigned

    Labels

    use-caseA novel and interesting way to use Coder

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions