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

      pFad - Phonifier reborn

      Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

      Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


      Alternative Proxies:

      Alternative Proxy

      pFad Proxy

      pFad v3 Proxy

      pFad v4 Proxy