Misc/xExchangeCommon.psm1
#Establishes a Exchange remote powershell session to the local server. Reuses the session if it already exists. function GetRemoteExchangeSession { [CmdletBinding()] param([PSCredential]$Credential, [string[]]$CommandsToLoad, $VerbosePreference) #First make sure we are on a valid server version VerifyServerVersion #See if the session already exists $Session = Get-PSSession -Name "DSCExchangeSession" -ErrorAction SilentlyContinue #Attempt to reuse the session if we found one if ($Session -ne $null) { if ($Session.State -eq "Opened") { Write-Verbose "Reusing existing Remote Powershell Session to Exchange" } else #Session is in an unexpected state. Remove it so we can rebuild it { RemoveExistingRemoteSession $Session = $null } } #Either the session didn't exist, or it was broken and we nulled it out. Create a new one if ($Session -eq $null) { Write-Verbose "Creating new Remote Powershell session to Exchange" #Get local server FQDN $machineDomain = (Get-WmiObject -Class Win32_ComputerSystem).Domain.ToLower() $serverName = $env:computername.ToLower() $serverFQDN = $serverName + "." + $machineDomain #Override chatty banner, because chatty New-Alias Get-ExBanner Out-Null New-Alias Get-Tip Out-Null #Load built in Exchange functions, and create session $exbin = Join-Path -Path ((Get-ItemProperty HKLM:\SOFTWARE\Microsoft\ExchangeServer\v15\Setup).MsiInstallPath) -ChildPath "bin" $remoteExchange = Join-Path -Path "$($exbin)" -ChildPath "RemoteExchange.ps1" . $remoteExchange $Session = _NewExchangeRunspace -fqdn $serverFQDN -credential $Credential -UseWIA $false -AllowRedirection $false #Remove the aliases we created earlier Remove-Item Alias:Get-ExBanner Remove-Item Alias:Get-Tip if ($Session -ne $null) { $Session.Name = "DSCExchangeSession" } } #If the session is still null here, things went wrong. Throw exception if ($Session -eq $null) { throw "Failed to establish remote Powershell session to FQDN: $($serverFQDN)" } else #Import the session globally { #Temporarily set Verbose to SilentlyContinue so the Session and Module import isn't noisy $oldVerbose = $VerbosePreference $VerbosePreference = "SilentlyContinue" if ($CommandsToLoad -ne $null -and $CommandsToLoad.Count -gt 0) { $moduleInfo = Import-PSSession $Session -WarningAction SilentlyContinue -DisableNameChecking -AllowClobber -CommandName $CommandsToLoad -Verbose:0 } else { $moduleInfo = Import-PSSession $Session -WarningAction SilentlyContinue -DisableNameChecking -AllowClobber -Verbose:0 } Import-Module $moduleInfo -Global #Set Verbose back $VerbosePreference = $oldVerbose } } #Removes any Remote Sessions that have been setup by us function RemoveExistingRemoteSession { [CmdletBinding()] param($VerbosePreference) $sessions = Get-PSSession -Name "DSCExchangeSession" -ErrorAction SilentlyContinue if ($sessions -ne $null) { Write-Verbose "Removing existing remote Powershell sessions" Get-PSSession -Name "DSCExchangeSession" -ErrorAction SilentlyContinue | Remove-PSSession } } #Ensures that Exchange is installed, and that it is the correct version (2013) function VerifyServerVersion { $key = Get-ItemProperty HKLM:\SOFTWARE\Microsoft\ExchangeServer\v15\Setup -ErrorAction SilentlyContinue if ($key -eq $null) { throw "Exchange is not installed on this machine" } else { $version = $key.MsiProductMajor if ($version -ne 15) { throw "Server running an unsupported version of Exchange. Must be Exchange 2013" } } } #Checks if two strings are equal, or are both either null or empty function CompareStrings { param([string]$String1, [string]$String2, [switch]$IgnoreCase) if (([string]::IsNullOrEmpty($String1) -and [string]::IsNullOrEmpty($String2))) { return $true } else { if ($IgnoreCase -eq $true) { return ($String1 -like $String2) } else { return ($String1 -clike $String2) } } } #Checks if two bools are equal, or are both either null or false function CompareBools($Bool1, $Bool2) { if($Bool1 -ne $Bool2) { if (!(($Bool1 -eq $null -and $Bool2 -eq $false) -or ($Bool2 -eq $null -and $Bool1 -eq $false))) { return $false } } return $true } #Takes a string which should be in timespan format, and compares it to an actual EnhancedTimeSpan object. Returns true if they are equal function CompareTimespanWithString { param([Microsoft.Exchange.Data.EnhancedTimeSpan]$TimeSpan, [string]$String) try { $converted = [Microsoft.Exchange.Data.EnhancedTimeSpan]::Parse($String) return ($TimeSpan.Equals($converted)) } catch { throw "String '$($String)' is not in a valid format for an EnhancedTimeSpan" } return $false } #Takes a string which should be in ByteQuantifiedSize format, and compares it to an actual ByteQuantifiedSize object. Returns true if they are equal function CompareByteQuantifiedSizeWithString { param([Microsoft.Exchange.Data.ByteQuantifiedSize]$ByteQuantifiedSize, [string]$String) try { $converted = [Microsoft.Exchange.Data.ByteQuantifiedSize]::Parse($String) return ($ByteQuantifiedSize.Equals($converted)) } catch { throw "String '$($String)' is not in a valid format for a ByteQuantifiedSize" } } #Takes a string which should be in Microsoft.Exchange.Data.Unlimited format, and compares with an actual Unlimited object. Returns true if they are equal. function CompareUnlimitedWithString { param($Unlimited, [string]$String) if ($Unlimited.IsUnlimited) { return (CompareStrings -String1 "Unlimited" -String2 $String -IgnoreCase) } else { return (CompareByteQuantifiedSizeWithString -ByteQuantifiedSize $Unlimited -String $String) } } #Takes an ADObjectId, gets a mailbox from it, and checks if it's EmailAddresses property contains the given string. #The Get-Mailbox cmdlet must be loaded for this function to succeed. function CompareADObjectIdWithEmailAddressString { param([Microsoft.Exchange.Data.Directory.ADObjectId]$ADObjectId, [string]$String) if ((Get-Command Get-Mailbox -ErrorAction SilentlyContinue) -ne $null) { $mailbox = $ADObjectId | Get-Mailbox -ErrorAction SilentlyContinue return ($mailbox.EmailAddresses.Contains($String)) } else { Write-Error "CompareADObjectIdWithEmailAddressString requires the Get-Mailbox cmdlert" return $false } } #Takes a string containing a given separator, and breaks it into a string array function StringToArray { param([string]$StringIn, [char]$Separator) [string[]]$array = $StringIn.Split($Separator) for ($i = 0; $i -lt $array.Length; $i++) { $array[$i] = $array[$i].Trim() } return $array } #Takes an array of strings and converts all elements to lowercase function StringArrayToLower { param([string[]]$Array) if ($Array -ne $null) { for ($i = 0; $i -lt $Array.Count; $i++) { if (!([string]::IsNullOrEmpty($Array[$i]))) { $Array[$i] = $Array[$i].ToLower() } } } return $Array } #Checks whether two arrays have the same contents, where element order doesn't matter function CompareArrayContents { param([string[]]$Array1, [string[]]$Array2, [switch]$IgnoreCase) $hasSameContents = $true if (($Array1 -eq $null -and $Array2 -ne $null) -or ($Array1 -ne $null -and $Array2 -eq $null) -or ($Array1.Length -ne $Array2.Length)) { $hasSameContents = $false } elseif ($Array1 -ne $null -and $Array2 -ne $null) { if ($IgnoreCase -eq $true) { $Array1 = StringArrayToLower -Array $Array1 $Array2 = StringArrayToLower -Array $Array2 } foreach ($str in $Array1) { if (!($Array2.Contains($str))) { $hasSameContents = $false break } } } return $hasSameContents } #Checks whether Array2 contains all elements of Array1 (Array2 may be larger than Array1) function Array2ContainsArray1Contents { param([string[]]$Array1, [string[]]$Array2, [switch]$IgnoreCase) $hasContents = $true if ($Array1 -eq $null -or $Array1.Length -eq 0) #Do nothing, as Array2 at a minimum contains nothing {} elseif ($Array2 -eq $null -or $Array2.Length -eq 0) #Array2 is empty and Array1 is not. Return false { $hasContents = $false } else { if ($IgnoreCase -eq $true) { $Array1 = StringArrayToLower -Array $Array1 $Array2 = StringArrayToLower -Array $Array2 } foreach ($str in $Array1) { if (!($Array2.Contains($str))) { $hasContents = $false break } } } return $hasContents } #Takes $PSBoundParameters from another function and adds in the keys and values from the given Hashtable function AddParameters { param($PSBoundParametersIn, [Hashtable]$ParamsToAdd) foreach ($key in $ParamsToAdd.Keys) { if (!($PSBoundParametersIn.ContainsKey($key))) #Key doesn't exist, so add it with value { $PSBoundParametersIn.Add($key, $ParamsToAdd[$key]) | Out-Null } else #Key already exists, so just replace the value { $PSBoundParametersIn[$key] = $ParamsToAdd[$key] } } } #Takes $PSBoundParameters from another function. If ParamsToRemove is specified, it will remove each param. #If ParamsToKeep is specified, everything but those params will be removed. If both ParamsToRemove and ParamsToKeep #are specified, only ParamsToKeep will be used. function RemoveParameters { param($PSBoundParametersIn, [string[]]$ParamsToKeep, [string[]]$ParamsToRemove) if ($ParamsToKeep -ne $null -and $ParamsToKeep.Count -gt 0) { [string[]]$ParamsToRemove = @() $lowerParamsToKeep = StringArrayToLower -Array $ParamsToKeep foreach ($key in $PSBoundParametersIn.Keys) { if (!($lowerParamsToKeep.Contains($key.ToLower()))) { $ParamsToRemove += $key } } } if ($ParamsToRemove -ne $null -and $ParamsToRemove.Count -gt 0) { foreach ($param in $ParamsToRemove) { $PSBoundParametersIn.Remove($param) | Out-Null } } } function SetEmptyStringParamsToNull { param($PSBoundParametersIn) [string[]] $emptyStringKeys = @() #First find all parameters that are a string, and are an empty string ("") foreach ($key in $PSBoundParametersIn.Keys) { if ($PSBoundParametersIn[$key] -ne $null -and $PSBoundParametersIn[$key].GetType().Name -eq "String" -and $PSBoundParametersIn[$key] -eq "") { $emptyStringKeys += $key } } #Now that we have the keys, set their values to null foreach ($key in $emptyStringKeys) { $PSBoundParametersIn[$key] = $null } } function VerifySetting { [CmdletBinding()] [OutputType([System.Boolean])] param([string]$Name, [string]$Type, $ExpectedValue, $ActualValue, $PSBoundParametersIn, $VerbosePreference) $returnValue = $true if ($PSBoundParametersIn.ContainsKey($Name)) { if ($Type -like "String") { if ((CompareStrings -String1 $ExpectedValue -String2 $ActualValue -IgnoreCase) -eq $false) { $returnValue = $false } } elseif ($Type -like "Boolean") { if ((CompareBools -Bool1 $ExpectedValue -Bool2 $ActualValue) -eq $false) { $returnValue = $false } } elseif ($Type -like "Array") { if ((CompareArrayContents -Array1 $ExpectedValue -Array2 $ActualValue -IgnoreCase) -eq $false) { $returnValue = $false } } elseif ($Type -like "Int") { if ($ExpectedValue -ne $ActualValue) { $returnValue = $false } } elseif ($Type -like "Unlimited") { if ((CompareUnlimitedWithString -Unlimited $ActualValue -String $ExpectedValue) -eq $false) { $returnValue = $false } } elseif ($Type -like "Timespan") { if ((CompareTimespanWithString -TimeSpan $ActualValue -String $ExpectedValue) -eq $false) { $returnValue = $false } } elseif ($Type -like "ADObjectID") { if ((CompareADObjectIdWithEmailAddressString -ADObjectId $ActualValue -String $ExpectedValue) -eq $false) { $returnValue = $false } } elseif ($Type -like "ByteQuantifiedSize") { if ((CompareByteQuantifiedSizeWithString -ByteQuantifiedSize $ActualValue -String $ExpectedValue) -eq $false) { $returnValue = $false } } else { throw "Type not found: $($Type)" } } if ($returnValue -eq $false) { ReportBadSetting -SettingName $Name -ExpectedValue $ExpectedValue -ActualValue $ActualValue -VerbosePreference $VerbosePreference } return $returnValue } function ReportBadSetting { param($SettingName, $ExpectedValue, $ActualValue, $VerbosePreference) Write-Verbose "Invalid setting '$($SettingName)'. Expected value: '$($ExpectedValue)'. Actual value: '$($ActualValue)'" } function LogFunctionEntry { param([Hashtable]$Parameters, $VerbosePreference) $callingFunction = (Get-PSCallStack)[1].FunctionName if ($Parameters -ne $null -and $Parameters.Count -gt 0) { $parametersString = "" foreach ($key in $Parameters.Keys) { $value = $Parameters[$key] if ($parametersString -ne "") { $parametersString += ", " } $parametersString += "$($key) = '$($value)'" } Write-Verbose "Entering function '$($callingFunction)'. Notable parameters: $($parametersString)" } else { Write-Verbose "Entering function '$($callingFunction)'." } } Export-ModuleMember -Function * |