Citrix VDA Registration Check

Check the status and common issues with Citrix VDA registration
Version 1.0.1
Created on 2021-08-19
Modified on 2021-08-19
Created by chris.rogers
Downloads: 259

The Script Copy Script Copied to clipboard
#requires -Version 3.0
<#  
.SYNOPSIS     Check Citrix VDA Registration Status [ALPHA]
.DESCRIPTION  [ALPHA] Citrix VDAs need to register with a DDC.
              Troubleshoot and give information on possible causes of unregistered VDAs.
              

.SOURCES      

.EXAMPLE:     \\util01\share\research\Citrix_VDA_Registration.ps1
.CONTEXT      Machine
.TAGS         $HDX, $Citrix, $VDA
.HISTORY      Chris Rogers     - 2021-04-19 - ALPHA Release 
#>

#Set-StrictMode -Version Latest
[string]$ErrorActionPreference = 'Stop'
# $VerbosePreference = 'Continue'      # Remove the comment in the begining to enable Verbose output
# $DebugPreference = 'Continue'      # Remove the comment in the begining to enable Debug output


###############################################################################################################
# VDA Checks
# expand checks to include both conditions where applicable

# Verify domain membership - $domainTest

    $name=$env:COMPUTERNAME
    $domain=$env:USERDNSDOMAIN

    # PartOfDomain (boolean Property)
    $domainMember=(Get-WmiObject -Class Win32_ComputerSystem).PartOfDomain

    $domainTest = $domainMember

    Write-Debug "Name: $name"
    Write-Debug "Domain: $domain"
    
    if($domainMember){
        Write-Debug "Verified: $domain"
    } 
    else{
        Write-Debug "Domain NOT Verified"
    }
    

# look for the VDA ListOfDDCs in the registry - $ddcTest

    $DDCs=((Get-ItemProperty -Path HKLM:\SOFTWARE\Citrix\VirtualDesktopAgent).ListOfDDCs -split" ")
    $ddc1=$DDCs[0]
    $ddc2=$DDCs[1]

    $ddcTest = $DDCs
            
    if($DDCs){
        Write-Debug "Verified DDC1: $ddc1"
        Write-Debug "Verified DDC2: $ddc2"
    } 
    else{
        Write-Debug "DDCs NOT Verified"
    }

# Resolve DNS for DDCs - $dnsTest
    
    $ddcip1=(Resolve-DnsName -Name $ddc1 -Type A -ErrorAction Ignore | select -exp IPAddress)
    $ddcip2=(Resolve-DnsName -Name $ddc2 -Type A -ErrorAction Ignore | select -exp IPAddress)

    $DDCIPs=@($ddcip1, $ddcip2)

    $dnsTest = $DDCIPs

    if($DDCIPs){
        Write-Debug "Verified DDC1 IP: $ddcip1"
        Write-Debug "Verified DDC2 IP: $ddcip2"
    } 
    else{
        Write-Debug "DDCs DNS NOT Verified"
    }

# Reverse DNS for DDCs - $revDnsTest

    $ddcrev1=(Resolve-DnsName $ddcip1 -Type PTR -ErrorAction Ignore | select -exp NameHost)
    $ddcrev2=(Resolve-DnsName $ddcip2 -Type PTR -ErrorAction Ignore | select -exp NameHost)

    
    $DDCRevIPs=@($ddcrev1, $ddcrev2)

    $revDnsTest = $DDCRevIPs

    if($DDCRevIPs){
        Write-Debug "Verified DDC1 Reverse DNS: $ddcrev1"
        Write-Debug "Verified DDC2 Reverse DNS: $ddcrev2"
    } 
    else{
        Write-Debug "DDCs Reverse DNS NOT Verified"
    }

# verify connectivity to DDCS - $pingTest

    $ddcping1=(test-connection -computername "$ddc1" -quiet -count 1)
    $ddcping2=(test-connection -computername "$ddc2" -quiet -count 1)

    $DDCpings=@($ddcping1, $ddcping2)

    $pingTest = $DDCpings

    if($DDCpings){
        Write-Debug "Verified DDC1 Ping: $ddcping1"
        Write-Debug "Verified DDC2 Ping: $ddcping2"
    } 
    else{
        Write-Debug "DDCs Ping NOT Verified"
    }

    #port 80/443 check?

# verify VDA time sync - $timeTest

if ( $env:logonserver -match "\\\\(?<server>(.+))")
{
    $timeServer = $matches['server']
    Write-Debug "NTP Server: $timeServer"    
}
else
{
    $timeServer = 'pool.ntp.org'
    Write-Debug "NTP Server: $timeServer"    
}


### Credit:  https://chrisjwarwick.wordpress.com/2012/08/26/getting-ntpsntp-network-time-with-powershell/
    # Construct client NTP time packet to send to specified server
    # (Request Header: [00=No Leap Warning; 011=Version 3; 011=Client Mode]; 00011011 = 0x1B)
    
    [Byte[]]$NtpData = ,0 * 48
    $NtpData[0] = 0x1B  
    
    $Socket = New-Object Net.Sockets.Socket([Net.Sockets.AddressFamily]::InterNetwork,
    [Net.Sockets.SocketType]::Dgram,
    [Net.Sockets.ProtocolType]::Udp)
    
    $Socket.Connect($timeServer,123)
    [Void]$Socket.Send($NtpData)
    [Void]$Socket.Receive($NtpData)    # Returns length – should be 48…
    
    $Socket.Close()
    
    # Decode the received NTP time packet
    
    # We now have the 64-bit NTP time in the last 8 bytes of the received data.
    # The NTP time is the number of seconds since 1/1/1900 and is split into an
    # integer part (top 32 bits) and a fractional part, multipled by 2^32, in the
    # bottom 32 bits.
    
    # Convert Integer and Fractional parts of 64-bit NTP time from byte array
    $IntPart=0;  Foreach ($Byte in $NtpData[40..43]) {$IntPart  = $IntPart  * 256 + $Byte}
    $FracPart=0; Foreach ($Byte in $NtpData[44..47]) {$FracPart = $FracPart * 256 + $Byte}
    
    # Convert to Millseconds (convert fractional part by dividing value by 2^32)
    [UInt64]$Milliseconds = $IntPart * 1000 + ($FracPart * 1000 / 0x100000000)
    
    # Create UTC date of 1 Jan 1900, add the NTP offset and convert result to local time
    $ntptime = (New-Object DateTime(1900,1,1,0,0,0,[DateTimeKind]::Utc)).AddMilliseconds($Milliseconds).ToLocalTime()
    $pcTime = Get-date
    
    Write-Debug "     PC Time : $pcTime"
    Write-Debug "    NTP Time : $ntpTime"
    
    $timeDiff = (New-TimeSpan -Start $pcTime -End $ntpTime)
    $minDiff = [math]::abs([math]::Round($timeDiff.TotalMinutes,2))
    
    Write-Debug "  Difference : $minDiff minutes"

if($minDiff -lt 4){
    Write-Debug "Verified VDA time difference: $minDiff minutes"
    $timeTest = 'True'
    } 
else{
    Write-Debug "VDA time out of sync: $minDiff"
    $timeTest = 'False'
}

###############################################################################################################
# Report findings - some tests return $true , other return a value

Write-Output "====================== Citrix VDA Check ======================"
if ($domainTest)      {Write-Output "PASS:       VDA is a member of $domain" }
         else        {Write-Output "`n======!!======!!"
        Write-Output "FAIL:       VDA is not a domain member"
                       Write-Output "      TRY:  Join domain"
                      }
      
if ($ddcTest)    {Write-Output "PASS:       DDCs are set: $ddc1 $ddc2" }
   else        {Write-Output "`n======!!======!!"
        Write-Output "FAIL:       DDCs are not set "
           Write-Output "      TRY:  Manually set DDCs in HKLM:\SOFTWARE\Citrix\VirtualDesktopAgent\ListOfDDCs"
          }

if ($dnsTest)    {Write-Output "PASS:       DDC forward DNS resolves: $ddcip1 $ddcip2" }
   else        {Write-Output "`n======!!======!!"
        Write-Output "FAIL:       DDCs do not have forward DNS "
           Write-Output "      TRY:  Update DNS records"
          }

if ($revDnsTest)    {Write-Output "PASS:       DDC reverse DNS resolves: $ddcrev1 $ddcrev2" }
   else        {Write-Output "`n======!!======!!"
        Write-Output "FAIL:       DDCs do not have reverse DNS "
           Write-Output "      TRY:  Update DNS records"
          }

if ($pingTest)    {Write-Output "PASS:       VDA can ping DDC: $ddcip1 $ddcip2" }
   else        {Write-Output "`n======!!======!!"
        Write-Output "FAIL:       DDCs are not reachable "
           Write-Output "      TRY:  Verify network routes and firewall rules"
          }

if ($timeTest)    {Write-Output "PASS:       VDA time is synced. Deviation: $minDiff Minutes" }
   else        {Write-Output "`n======!!======!!"
        Write-Output "FAIL:       VDA time is NOT synced"
           Write-Output "      TRY:  Sync time with domain controller"
          }


############## REMEDIATIONS ##############

Write-Output "====================== VDA Remediation ======================"
Write-Output "Automatic remediation not enabled."

# Force sync system time - $timeSync

#  sleep -seconds 20

#    w32tm /config /syncfromflags:DOMHIER
#    w32tm /resync /nowait
#    net stop w32time
#    net start w32time

# Force a Group Policy update if things have changed - $gpUpdate

# gpupdate /force

    
#Purge Kerberos - is this needed? - $kerberosPurge

  #  klist purge      
  #  klist purge_bind 
  #  klist -lh 0 -li 0x3e7 purge


# Restart the BrokerAgent service - $brokerRestart

 #   Stop-Service -Name BrokerAgent
 #   sleep 1
 #   Start-Service -Name BrokerAgent

 # which is better?
 #    net stop brokeragent
 #    net start brokeragent