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
}
Comments
- Anonymous
May 18, 2014
Thanks.. exactly what I needed