Generally we recommend not to join any Cameyo servers into an Active Directory domain. However, sometimes it is necessary to do so.

This guide explains how to prepare the master server to successfully join elastic servers.


Good to know:

  1. Every server is created from the master and therefore has the same name as the master and also the same SID.

  2. The process needs multiple reboots (and therefore some time)

  3. The process needs a third party product SIDCHG that needs to be purchased (trial available)

  4. The master server MUST NOT be joined to the domain



Steps:

  1. Create a user in your Active Directory that only has rights to join a machine to the domain, nothing else

  2. Get the MAC address of the master server
    PS >Get-NetAdapter

  3. Get the DNS server IP

  4. Install PSTools into C:\Tools\PSTools (C:\Tools\PSTools\PsExec.exe is required)

  5. Install SIDCHG into C:\ProgramData\Cameyo (C:\ProgramData\Cameyo\sidchg64.exe is required)

  6. Purchase a license for SIDCHG or get the trial key for testing

  7. On the master server create a new PowerShell script in C:\ProgramData\Cameyo with the name StartupRegisterJoin.ps1 and add the following content to it:

    $user = "<domain>\<username>"    
    $pass = "<password>" 
    $domain = "<FQDN>"
    $joinOU = "<ADFullOUPath>"
    $masterMAC = "<MacAddressOfMaster"
    $DNSserver = "<DNSServerIPAddress>"
    $SIDCHGLicense = "<SIDCHGLicenseKey>"
    
    $envir = (dir env:) | Out-String
    New-EventLog -LogName "Cameyo" -Source "StartupRegisterJoin" -ErrorAction SilentlyContinue
    
    $cred = New-Object System.Management.Automation.PSCredential -ArgumentList ($user, ($pass | ConvertTo-SecureString -AsPlainText -Force))
    #$suffix = (Get-NetAdapter | Where Status -eq Up)[0].MacAddress.Split("-")[3..6] -join ""
    $hostname = $env:CAMEYO_SERVERNAME
    
    if (-not (gwmi win32_computersystem).PartOfDomain -and (Get-NetAdapter).MacAddress -ne $masterMAC) {
        Write-EventLog -LogName "Cameyo" -Source "StartupRegisterJoin" -Message "Starting RegisterJoin" -EventId 0 -EntryType Information
        Write-EventLog -LogName "Cameyo" -Source "StartupRegisterJoin" -Message ("User: "+($env:USERNAME)+"`n`nEnvironment Variables:"+($envir)) -EventId 1 -EntryType Information    
    
        if ($hostname -ne $env:COMPUTERNAME) {
            # Stage 1
            Write-EventLog -LogName "Cameyo" -Source "StartupRegisterJoin" -Message "Created hostname '$hostname' ($env:CAMEYO_SERVERNAME)" -EventId 2 -EntryType Information
            Write-EventLog -LogName "Cameyo" -Source "StartupRegisterJoin" -Message "Stopping Cameyo service" -EventId 3 -EntryType Information
            Stop-Service RapStartSvc -ErrorAction SilentlyContinue
            Write-EventLog -LogName "Cameyo" -Source "StartupRegisterJoin" -Message "Setting TrustedHosts to '$domain'" -EventId 4 -EntryType Information
            Set-Item WSMan:\localhost\Client\TrustedHosts -Value $domain -Force
            $ifindex = (Get-NetAdapter).ifIndex
            Write-EventLog -LogName "Cameyo" -Source "StartupRegisterJoin" -Message "Setting DNS Server to $DNSserver (IF:$ifindex)" -EventId 5 -EntryType Information
            Set-DnsClientServerAddress -InterfaceIndex $ifindex -ServerAddresses ($DNSserver)
            Write-EventLog -LogName "Cameyo" -Source "StartupRegisterJoin" -Message "Rename Computer to $host" -EventId 6 -EntryType Information
            Rename-Computer $hostname
            Write-EventLog -LogName "Cameyo" -Source "StartupRegisterJoin" -Message "Running SIDCHG64... and reboot" -EventId 7 -EntryType Information
            &"$env:ProgramData\Cameyo\sidchg64.exe" /F /R /KEY=$SIDCHGLicense
            Sleep -Seconds 60
            Restart-Computer
        } else {
            # Stage 2
            Write-EventLog -LogName "Cameyo" -Source "StartupRegisterJoin" -Message "Set Cameyo service to automatic" -EventId 8 -EntryType Information
            Set-Service RapStartSvc -StartupType Automatic
            Write-EventLog -LogName "Cameyo" -Source "StartupRegisterJoin" -Message "Join computer '$hostname' to domain '$domain'" -EventId 9 -EntryType Information
            Add-Computer -DomainName $domain -Restart -Credential $cred -OUPath $JoinOU
        }
    } else {
        if ((gwmi win32_computersystem).PartOfDomain -and $hostname -eq $env:COMPUTERNAME) {
            # Stage 3
            Write-EventLog -LogName "Cameyo" -Source "StartupRegisterJoin" -Message "Add domain users to local Remote Desktop Users group" -EventId 10 -EntryType Information
            &C:\Tools\PSTools\PsExec.exe -accepteula -nobanner -s -d -i -u $user -p $pass -c net.exe localgroup "Remote Desktop Users" "$domain\Domain Users" /add
            Write-EventLog -LogName "Cameyo" -Source "StartupRegisterJoin" -Message "Unregister scheduled task" -EventId 11 -EntryType Information
            Unregister-ScheduledTask -TaskName "Cameyo StartupRegisterJoin" -Confirm:$false
            Write-EventLog -LogName "Cameyo" -Source "StartupRegisterJoin" -Message "Removing script" -EventId 12 -EntryType Information
            Remove-Item -Path "$env:ProgramData\Cameyo\StartupRegisterJoin.ps1"
        } else {
            # Master
            Write-EventLog -LogName "Cameyo" -Source "StartupRegisterJoin" -Message "Running on master therefore not doing anything" -EventId 9999 -EntryType Information
        }
    }


  8. Change the variables at the top of the script accordingly
    Example:

    $user = "ELASTIC\JoinUser"    
    $pass = "ygHuBbYd5CYlSfhRFFmw" 
    $domain = "elastic.local"
    $joinOU = "OU=uswest2,OU=Cameyo servers,DC=elastic,DC=local"
    $masterMAC = "00-0D-2D-24-67-21"
    $DNSserver = "10.0.0.4"
    $SIDCHGLicense = "7qrd3-Kj#Zx-iC5dn-eZ"


  9. Run the following script (with elevated rights) on the master server to create a scheduled task to run the join script above:

    $taskname = "Cameyo StartupRegisterJoin"
    $action = New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument "-File ""$env:ProgramData\Cameyo\StartupRegisterJoin.ps1"""
    $trigger = New-ScheduledTaskTrigger -AtStartup
    $sysuser = "SYSTEM"
    $settings = New-ScheduledTaskSettingsSet
    Register-ScheduledTask -User "SYSTEM" -Action $action -TaskName $taskName -Trigger $trigger -RunLevel Highest


  10. Go to the elastic cluster and configure one idle reserver instance:

    This is needed to prevent users from have failed sessions, because the creation of a new server takes so long that a session would run into a timeout if the server doesn’t already exist

  11. Also on the elastic cluster set the the following PowerTag:
    !SHORTNAMEPATTERN=yourcompanyname-app%randnum%
    Make sure the name doesn’t exceed 15 characters as this is the limit of hostname (by Microsoft)
    The elastic server will get that name as Windows hostname as well as the DNS hostname (.cameyo.net)!


With the above steps your automatically created machines will be joined to the domain, however they won’t be ever deleted from the domain.



Cleanup:

The following script gets the servers from the Cameyo API and deletes every machine that isn’t found in the cluster, therefore make sure you create an OU for each cluster and join the machines into that OU:

$clientSecret = "<CameypAPIClientSecret>"
$companyId = "<CameyoCompanyId>"
$serverGroup = "<ServerGroupId>"
$searchBase = "<ADFullOUPath>"

$computers = Get-ADComputer -Filter * -SearchBase $searchBase

$serverlist = Invoke-RestMethod -Uri ("https://api.cameyo.com/servergroups/$serverGroup/list?clientId=$companyId&clientSecret=$clientSecret") -Method GET -ContentType "application/text; charset=UTF-8"

foreach ($computer in $computers) {
    $id = $serverlist | where name -Match $computer.Name
    if ($id) {
        "Keep $($computer.Name)"
    } else {        
        "Delete $($computer.Name)"
        #Remove-ADComputer $computer.Name
    }
}

For safety reasons the Remove-ADComputer command has been commented out to not accidentally delete machines. It needs to be un-commented.


The password for the join user will be in clear text in the joining script, however, we delete the script after joining, so that it’s not accessible/visible on an elastic server