I was recently dealing with a scenario where I needed to determine the Exchange install path via PowerShell script so that I could copy a scripting agent config XML file to all servers automatically.

After coming up with a solution I asked my fellow Exchange Server MVPs Steve Goodman and Michael van Horenbeeck if they had any better methods that they use. PowerShell being what it is, and no two scenarios being exactly the same, it turns out we each had different approaches.

Michael points out that a script running locally on an Exchange server can simply reference the environment variable that stores the Exchange install path.

PS C:\> $env:exchangeinstallpath
C:Program FilesMicrosoftExchange ServerV14

Finding the Exchange install path of a remote server requires a different approach. I came up with a whole bunch of code to query registry keys and output a report of the install paths for all servers. The results look like this (that warning is for the Edge Transport server):

PS C:Scripts> .Get-ExchangeInstallPaths.ps1
Checking HO-EX2010-MB1
Checking HO-EX2010-MB2
Checking BR-EX2010-MB
Checking HO-EX2010-PF
Checking HO-EX2010-EDGE
WARNING: Exception calling "OpenRemoteBaseKey" with "2" argument(s): "Attempted to perform an unauthorized operation."

Server Name                                                 Install Path
-----------                                                 ------------
HO-EX2010-MB1                                               C:Program FilesMicrosoftExchange ServerV14
HO-EX2010-MB2                                               C:Program FilesMicrosoftExchange ServerV14
BR-EX2010-MB                                                C:Program FilesMicrosoftExchange ServerV14
HO-EX2010-PF                                                C:Program FilesMicrosoftExchange ServerV14
HO-EX2010-EDGE                                              Unable to connect to registry

The script itself is as follows:

 
#requires -version 2

#...................................
# Initialize
#...................................

#Add Exchange snapin if not already loaded
if (!(Get-PSSnapin | where {$_.Name -eq "Microsoft.Exchange.Management.PowerShell.E2010"}))
{
	Write-Verbose "Loading the Exchange 2010 snapin"
	try
	{
		Add-PSSnapin Microsoft.Exchange.Management.PowerShell.E2010 -ErrorAction STOP
	}
	catch
	{
		#Snapin not loaded
		Write-Warning $_.Exception.Message
		EXIT
	}
	. $env:ExchangeInstallPathbinRemoteExchange.ps1
	Connect-ExchangeServer -auto -AllowClobber
}

$exchangeservers = Get-ExchangeServer

$report = @()

foreach ($srv in $exchangeservers)
{
    $server = $srv.Name
    if ($srv.AdminDisplayVersion -match "Version 14") {$ver = "V14"}
    if ($srv.AdminDisplayVersion -match "Version 15") {$ver = "V15"}

    Write-Host "Checking $server"

    $installpath = $null
    $reg = $null

	try {
        $reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey("LocalMachine",$server)
    }
    catch
    {
        Write-Warning $_.Exception.Message
        $installpath = "Unable to connect to registry"
    }

    if (!($installpath))
    {

	    $installpath = $reg.OpenSubKey("SOFTWAREMicrosoftExchangeServer$verSetup").GetValue("MsiInstallPath")
    }

    $serverObj = New-Object PSObject
	$serverObj | Add-Member NoteProperty -Name "Server Name" -Value $server
	$serverObj | Add-Member NoteProperty -Name "Install Path" -Value $installpath

    $report += $serverObj    
}

$report

As it turns out, Michael has another idea for remote servers, using remoting. Working his tip into the same script looks like this:

#requires -version 2

#...................................
# Initialize
#...................................

#Add Exchange snapin if not already loaded
if (!(Get-PSSnapin | where {$_.Name -eq "Microsoft.Exchange.Management.PowerShell.E2010"}))
{
	Write-Verbose "Loading the Exchange 2010 snapin"
	try
	{
		Add-PSSnapin Microsoft.Exchange.Management.PowerShell.E2010 -ErrorAction STOP
	}
	catch
	{
		#Snapin not loaded
		Write-Warning $_.Exception.Message
		EXIT
	}
	. $env:ExchangeInstallPathbinRemoteExchange.ps1
	Connect-ExchangeServer -auto -AllowClobber
}

$exchangeservers = Get-ExchangeServer

$report = @()

foreach ($srv in $exchangeservers)
{
    $server = $srv.Name

    Write-Host "Checking $server"

    $installpath = $null

	try {
        $installpath = Invoke-Command –Computername $server -ScriptBlock {$env:ExchangeInstallPath} -ErrorAction STOP
    }
    catch
    {
        Write-Warning $_.Exception.Message
        $installpath = "Unable to connect to server"
    }

    $serverObj = New-Object PSObject
	$serverObj | Add-Member NoteProperty -Name "Server Name" -Value $server
	$serverObj | Add-Member NoteProperty -Name "Install Path" -Value $installpath

    $report += $serverObj    
}

$report

The resulting output is the same, though it seemed to run a little slower and also failed on my servers that didn’t have remoting already enabled. That is a quick fix, but not a problem I ran into with the remote registry query method.

So there you have it. Multiple methods for retrieving the Exchange install path in PowerShell scripts. If you’ve got another technique you use feel free to share in the comments below.

About the Author

Paul Cunningham

Paul is a former Microsoft MVP for Office Apps and Services. He works as a consultant, writer, and trainer specializing in Office 365 and Exchange Server. Paul no longer writes for Practical365.com.

Comments

Leave a Reply