Sanity-Checking RDG Files

Remote Desktop Connection Manager is a must-have tool. However, it too easily falls out of date.  Depending on your environment, you might be spinning up new machines regularly, especially if you’re in a virtualized environment.  Here’s a quick way to see if your .RDG file and your AD server agree.

At the core, it’s pretty simple:

  • RDG files are XML
  • The XPath to a server’s name is “//server/name”, then take the InnerText.
  • The <displayname /> element, while nice to look at, means very little.

===

 function Test-RDGFile
{
    #region header

    <#
    .synopsis
    Validates an RDCManager config file (.RDG)

    .Description
    With change comes opportunity.  RDG files are constantly being rendered obsolete.  Here's a way to see what's missing, and what's outdated.  Requires domain trust with AD server in target domains.

    .parameter Path
    Path to RDG file.  Required.

    .Parameter ADDomain
    List of Active Directory domains to check.  If not set, will pull from all AD domains in the RDG file.

    .parameter OutputPath
    Path to *folder* to store output files.  Defaults to "$home\$basename (datestamp)".

    .parameter Filter
    Value to Get-ADComputer-Filter –Filter parameter.  Defaults to '*'.

    Output files consiste of three types:

    suspect.txt - server is in RDG, but unable to check, usually because of failed domain trust.

    missing.txt - server is not in RDG, but is in Get-ADComputer -server 

    outdated.txt - server is in RDG, but not in Get-ADComputer -server 

    #>

    param (
        [parameter(Mandatory=$true)][string]$Path = $null,
        [string[]]$ADDomain = @(),
        [string]$OutputPath = $null,
        [string]$Filter = '*'
    );

    #endregion
    #region setup

    # used for messaging
    $scriptName = (&{$MyInvocation}).ScriptName;
    $baseName = Split-Path -Path $scriptName -Leaf

    ########################################
    
    # import the ActiveDirectory module if needed
    if (!(Get-Command -Name Get-ADComputer -ErrorAction SilentlyContinue))
    {
        $local:oldProgressPreference = $ProgressPreference;
        $ProgressPreference = 'SilentlyContinue';
        Import-Module ServerManager;
        Add-WindowsFeature RSAT-AD-Powershell;
        $ProgressPreference = $local:oldProgressPreference;
    } # if (!(Get-Command -Name Get-ADComputer -ErrorAction SilentlyContinue))

    ########################################

    # input validation
    if (!(Test-Path -Path $Path))
    {
        Write-Warning "$basename -Path '$Path' not found.  Exiting.";
        return;
    } # if (!(Test-Path -Path $Path))

    # parse input file phase 1: as XML
    Write-progress $basename "Parsing $Path";
    if (!($xml = (Get-Content -Path $Path) -as [xml]))
    {
        Write-Warning "$basename -Path '$Path' cannot be parsed as XML.  Exiting.";
        return;
    } # if (!($xml = (Get-Content -Path $Path) -as [xml]))

    # parse input file phase 2: extract server names
    $rdgHosts = $xml.selectNodes("//server/name") | 
    % { 
        $_.InnerText; 
    } |
    Group-Object |
    % { # needed in case we have multiple entries of the same hostname in the file
        $_.Name;
    } | 
    Sort-Object # $xml.selectNodes("//server/name") |

    # check all ADDomains if none specified.
    if (!$ADDomain)
    {
        $ADDomain = $rdgHosts -replace '^[^\.]*\.' | 
        Group-Object | 
        % {
            $_.Name;
        } |
        Sort-Object # $rdgDomains = $rdgHosts -replace '^[^\.]*\.' | 
    } # if (!$ADDomain)

    ########################################

    # store output files here
    if (!$OutputPath)
    {
        $OutputPath = "$home\$($baseName -replace '\.[^\.]+$') ($(Get-Date -Format 'yyyy-MM-dd HHmmss'))";
    } # if (!$OutputPath)

    if (!(Test-Path -Path $OutputPath))
    {
        mkdir $OutputPath | Out-Null;
    } # if (!(Test-Path -Path $OutputPath))

    #endregion
    #region do the real work

    foreach ($_adDomain in $ADDomain)
    {
        Write-Progress $baseName "Checking -ADDomain $_adDomain";

        if ($rdgDomainHosts = $rdgHosts | ? { $_ -match "$_adDomain$" })
        {
            try
            {
                if (!($adDomainHosts = Get-ADComputer -Filter $Filter -Server $_adDomain -ErrorAction SilentlyContinue | 
                % { $_.DistinguishedName -replace ',DC=', '.' -replace ',[^\.]*\.', '.' -replace '.*='; } |
                Sort-Object ))
                {
                    Write-Warning "$basename -ADDomain '$_adDomain' Unable to get server list.  Skipping.";
                    $rdgDomainHosts | Out-File -Append -FilePath "$OutputPath\suspect.txt"
                    continue;
                } # if (!($adComputer = Get-ADComputer -Filter $Filter -Server $_adDomain ...
            } # try
            catch
            {
                Write-Warning "Get-ADComputer -Filter $Filter -server $_addomain failed with '$($_.exception.message)'";
            } # catch

            if ($compareResults = Compare-Object -ReferenceObject $rdgDomainHosts -DifferenceObject $adDomainHosts)
            {
                $compareResults | ? { $_.SideIndicator -eq '=>' } | % { $_.InputObject } | Out-File -Append -FilePath "$OutputPath\missing.txt";
                $compareResults | ? { $_.SideIndicator -eq '<=' } | % { $_.InputObject } | Out-File -Append -FilePath "$OutputPath\outdated.txt";
            } # if ($compareResults = Compare-Object -ReferenceObject $rdgDomainHosts ...
        } # if ($rdgDomainHosts = $rdgHosts | ? { $_ -match "$_adDomain$" })
        else 
        {
            try
            {
                 # have to build up the hostnames because DNSHostnames are often infra.lync.com...
                if ($adDomainHosts = Get-ADComputer -Filter $Filter -Server $_adDomain -ErrorAction SilentlyContinue | 
                % { $_.DistinguishedName -replace ',DC=', '.' -replace ',[^\.]*\.', '.' -replace '.*='; } |
                Sort-Object )
                {
                    $adDomainHosts | Out-File -Append -FilePath "$OutputPath\missing.txt"
                    continue;
                } # if ($adComputer = Get-ADComputer -Filter $Filter -Server $_adDomain ...
                else
                {
                    Write-Warning "$basename -ADDomain '$_adDomain' Unable to get server list.  Skipping.";
                    continue;
                } # if ($adComputer = Get-ADComputer -Filter $Filter -Server $_adDomain ...
            } # try
            catch
            {
                Write-Warning "Get-ADComputer -Filter * -server $_addomain failed with '$($_.exception.message)'";
            } # catch
        } # if ($rdgDomainHosts = $rdgHosts | ? { $_ -match "$_adDomain$" })

    } # foreach ($_adDomain in $ADDomain)

    # output results
    Get-ChildItem $OutputPath | 
    %{
        $_.Fullname;
    } # Get-ChildItem $OutputPath | 

    #endregion
}