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.
Pingback: PowerShell Script to Distribute Scripting Agent Configuration File to Exchange Servers