EmmExDAGModule.psm1
<#PSScriptInfo
.VERSION 1.2 .AUTHOR Faris Malaeb .PROJECTURI https://www.powershellcenter.com/ .DESCRIPTION This Powershell module will Place your Exchange Server DAG in maintenance Mode Also you can remove Exchange DAG from Maintenance Mode. Available Commands Start-EMMDAGEnabled: Set your Exchange Server to be in Maintenance Mode. Stop-EMMDAGEnabled: Remove Exchange from maintenanace Mode Test-EMMReadiness: Test the environment for readiness to go in maintenance Mode #> Function Check-ScriptReadiness{ param( $ServerName, $AltServer ) if (((Test-NetConnection -Port 80 -ComputerName $PSBoundParameters['ServerName']).TcpTestSucceeded -like $true) -and (Test-NetConnection -Port 80 -ComputerName $PSBoundParameters['AltServer']).TcpTestSucceeded -like $true){ $isadmin=[bool](([System.Security.Principal.WindowsIdentity]::GetCurrent()).groups -match "S-1-5-32-544") switch ($isadmin) { $true {return 1} $false {return 0} } } Else{ write-host "Operation failed, please check if the computer and the Alternative Server are reachable" -ForegroundColor Red Write-host $Error[0] break } } Function Start-EMMDAGEnabled { Param( [parameter(mandatory=$false,ValueFromPipeline=$true,Position=0)]$ServerForMaintenance, [parameter(mandatory=$false)][ValidatePattern("(?=^.{1,254}$)(^(?:(?!\d+\.|-)[a-zA-Z0-9_\-]{1,63}(?<!-)\.?)+(?:[a-zA-Z]{2,})$)")][string]$ReplacementServerFQDN, [parameter(Mandatory=$false)][switch]$IgnoreQueue, [parameter(Mandatory=$false)][switch]$IgnoreCluster, [parameter(Mandatory=$false)][switch]$SkipDatabaseHealthCheck ) Begin{ AddEmptylines -numberoflines 1 -MessageToIncludeAtTheEnd "Checking Readiness... Please wait" -MessageColor Yellow -ProgressState "Starting" -ProgressPercent 3 $ErrorActionPreference="Stop" $ReadyToExecute=Check-ScriptReadiness -ServerName $PSBoundParameters['ServerForMaintenance'] -AltServer $PSBoundParameters['ReplacementServerFQDN'] if ($ReadyToExecute -eq 0){Write-Host "Please Make sure that you execute Powershell as Admin" -ForegroundColor Red return } [hashtable]$ExMainProgress=[ordered]@{} if ($PSBoundParameters.ContainsKey('SkipDatabaseHealthCheck')){ AddEmptylines -numberoflines 1 -MessageToIncludeAtTheEnd "DB Health check will be ignored as the -SkipDatabaseHealthCheck is selected.`nIts a recommended to use this option in production environment." -MessageColor red Write-Host "Please check the online manual and ensure to follow the best practices" } } Process{ AddEmptylines -numberoflines 1 -MessageToIncludeAtTheEnd "Preparing $($PSBoundParameters['ServerForMaintenance']) to be placed in Maintinance Mode" -MessageColor Yellow -ProgressState "Turnning Off HubTransport Activities..." -ProgressPercent 10 $Step1=Set-EMMHubTransportState -Servername $PSBoundParameters['ServerForMaintenance'] -Status Draining switch ($PSBoundParameters.Containskey('IgnoreQueue')){ $true {write-host "Queue Check... Skipped" -ForegroundColor Yellow } $false{ $QState=QueueFailure -ServernameToCheck $PSBoundParameters['ServerForMaintenance'] if ($QState -eq 1){ AddEmptylines -numberoflines 2 -MessageToIncludeAtTheEnd "Message Redirection Process will Start, expected time to finish is 60 seconds." -MessageColor Yellow -ProgressState "Redirecting Messages..." -ProgressPercent 25 Start-EMMRedirectMessage -SourceServer $PSBoundParameters['ServerForMaintenance'] -ToServer $PSBoundParameters['ReplacementServerFQDN'] Start-EMMRedirectMessage -SourceServer $PSBoundParameters['ServerForMaintenance'] -ToServer $PSBoundParameters['ReplacementServerFQDN'] -CheckOnly Start-Sleep -Seconds 2 $Qlength=(Get-Queue -server $PSBoundParameters['ServerForMaintenance'] | Where-Object {($_.DeliveryType -notlike "Shadow*") -and ($_.DeliveryType -notlike "Undefined") }| Select-Object Messagecount | Measure-Object -Sum -Property MessageCount).Sum } } } Switch($PSBoundParameters.Containskey('IgnoreCluster')){ $true { AddEmptylines -numberoflines 2 -MessageToIncludeAtTheEnd "Skipping Cluster MGMT as user requests." -MessageColor Yellow -ProgressState "Skipping Cluster" -ProgressPercent 50 $step3="Skipped"} $false { AddEmptylines -numberoflines 2 -MessageToIncludeAtTheEnd "Starting Cluster MGMT." -MessageColor Yellow -ProgressState "Pausing $($PSBoundParameters['ServerForMaintenance']) " -ProgressPercent 50 $step3=Set-EMMClusterConfig -ClusterNode $PSBoundParameters['ServerForMaintenance'] -PauseOrResume PauseThisNode} } AddEmptylines -numberoflines 2 -MessageToIncludeAtTheEnd "Starting Exchange Database Managment" -MessageColor Yellow -ProgressState "Moving Database to another node" -ProgressPercent 70 switch ($PSBoundParameters.Containskey('SkipDatabaseHealthCheck')){ $true {Set-EMMDBActivationMoveNow -ServerName $PSBoundParameters['ServerForMaintenance'] -ActivationMode BlockMode -TimeoutBeforeManualMove 120 -SkipValidation} $false {Set-EMMDBActivationMoveNow -ServerName $PSBoundParameters['ServerForMaintenance'] -ActivationMode BlockMode -TimeoutBeforeManualMove 120 } } AddEmptylines -numberoflines 2 -MessageToIncludeAtTheEnd "Switching ServerComponentState ServerWideOffline to Off" -MessageColor Yellow -ProgressState "Updating ServerWideOffline" -ProgressPercent 95 Set-ServerComponentState $PSBoundParameters['ServerForMaintenance'] -Component ServerWideOffline -State Inactive -Requester Maintenance -ErrorAction Stop $step5=get-ServerComponentState $PSBoundParameters['ServerForMaintenance'] -Component ServerWideOffline Write-Host "All Commands are completed, and below are the result...`n"-ForegroundColor Yellow $ExMainProgress.Add("HubTransport Draining",$Step1) if (($Qlength -eq 0) -or ($Qlength -like $null)){$ExMainProgress.Add("Queue Length Status","All Transfared.")} Else{$ExMainProgress.Add("Queue Length Status",$Qlength)} $ExMainProgress.Add("Cluster Node",$step3) $ExMainProgress.Add("Activation Policy",(Get-MailboxServer -Identity $PSBoundParameters['ServerForMaintenance']).DatabaseCopyAutoActivationPolicy) $ExMainProgress.Add("ServerWide",$step5.State) } End{ Return $ExMainProgress | Format-Table -AutoSize -Wrap } } Export-ModuleMember Start-EmmDAGEnabled Function AddEmptylines{ param( [parameter(mandatory=$true)]$numberoflines, [parameter(mandatory=$true)]$MessageToIncludeAtTheEnd, [parameter(mandatory=$True)]$MessageColor, [parameter(mandatory=$false)]$ProgressState, [parameter(mandatory=$false)]$ProgressPercent ) $numofline=0 while($numofline -lt $PSBoundParameters['numberoflines']){ Write-Host "" $numofline++ } Write-Host $($PSBoundParameters['MessageToIncludeAtTheEnd']) -ForegroundColor $PSBoundParameters['MessageColor'] if ($PSBoundParameters['ProgressState']){ Write-Progress -Activity $PSBoundParameters['MessageToIncludeAtTheEnd'] -Status $PSBoundParameters['ProgressState'] -PercentComplete $PSBoundParameters['ProgressPercent'] } } Function Stop-EMMDAGEnabled { Param( [parameter(mandatory=$false,ValueFromPipeline=$true,Position=0)]$ServerInMaintenance, [parameter(Mandatory=$false)][switch]$IgnoreCluster, [parameter(mandatory=$false)][validateset("IntrasiteOnly","Unrestricted")]$ServerActivationMode="Unrestricted" ) Begin{ $ErrorActionPreference="Stop" [hashtable]$ExOutMainProgress=[ordered]@{} } Process{ Write-Host "Preparing $($PSBoundParameters['ServerInMaintenance']) for Activation..." -ForegroundColor Yellow AddEmptylines -numberoflines 2 -MessageToIncludeAtTheEnd "Taking the Server Out of Maintenance mode..." -MessageColor Yellow -ProgressState "Enabling ServerWideOffline component" -ProgressPercent 15 Set-ServerComponentState $PSBoundParameters['ServerInMaintenance'] -Component ServerWideOffline -State active -Requester Maintenance AddEmptylines -numberoflines 2 -MessageToIncludeAtTheEnd "Configuring cluster if required..." -MessageColor Yellow -ProgressState "Cluster Configuration" -ProgressPercent 35 switch($PSBoundParameters.Containskey('IgnoreCluster')){ $true {write-host "Cluster Config are Skipped";$outstep1="Skipped"} $false {$outstep1=Set-EMMClusterConfig -ClusterNode $PSBoundParameters['ServerInMaintenance'] -PauseOrResume ResumeThisNode} } $outStep2=Set-EMMDBActivationMoveNow -ServerName $PSBoundParameters['ServerInMaintenance'] -ActivationMode $ServerActivationMode AddEmptylines -numberoflines 2 -MessageToIncludeAtTheEnd "Enabling HubTransport Components..." -MessageColor Yellow -ProgressState "Enabling HubTransport..." -ProgressPercent 60 $outStep3=Set-EMMHubTransportState -Servername $PSBoundParameters['ServerInMaintenance'] -Status Active AddEmptylines -numberoflines 2 -MessageToIncludeAtTheEnd "Enabling Exchange Server Components..." -MessageColor Yellow -ProgressState "All should be done, below are the result, Make sure that there is no failure or other issues" -ProgressPercent 90 Write-Host "-------- Result for Activating Server " -NoNewline ;Write-Host "$($PSBoundParameters['ServerInMaintenance']) " -ForegroundColor Yellow -NoNewline ;Write-Host " -----------" $ExOutMainProgress.Add("ServerWide",(Get-ServerComponentState $PSBoundParameters['ServerInMaintenance'] -Component ServerWideOffline).State) $ExOutMainProgress.Add("ClusterNode",$outstep1) $ExOutMainProgress.Add("DB Server Activation",$outStep2) $ExOutMainProgress.Add("HubTransport",$outStep3) } End{ return $ExOutMainProgress | Format-Table -AutoSize -Wrap } } Export-ModuleMember Stop-EMMDAGEnabled Function Set-EMMHubTransportState { [CmdletBinding()] Param( [parameter(mandatory=$true,ValueFromPipeline=$true,Position=0)]$Servername, [validateset("Draining","Active")]$Status ) Process{ Write-Host "Configuring Hub Transport to be " -NoNewline; Write-Host "$($PSBoundParameters['Status'])" -ForegroundColor Green -NoNewline ; Write-Host " For " -NoNewline; Write-Host "$($PSBoundParameters['Servername'])" -ForegroundColor Green Try { if (@((Get-ExchangeServer | Get-ServerComponentState -Component Hubtransport | Where-Object {($_.State -like "Active") -and ($_.Serverfqdn -notlike "*$Servername*")}).state).Count -eq 0){ Write-warning "Ops, there are no more servers with a HubTransport state set to Active State in the environment, Please make sure to have at least one" break } $TransportState=@{ identity=$PSBoundParameters['servername'] Component='HubTransport' State=$PSBoundParameters['Status'] Requester="Maintenance" } Set-ServerComponentState @TransportState Start-Sleep -Seconds 2 $Srvcomstate=(Get-ServerComponentState $PSBoundParameters['servername'] -Component HubTransport).state return $Srvcomstate } catch { Write-Warning -Message $Error[0] break } } End{ Write-Host "Configs are completed, Now $($PSBoundParameters['servername']) is set to be :" -NoNewline; write-host (Get-ServerComponentState $PSBoundParameters['servername'] -Component HubTransport).state -ForegroundColor Green } } Function QueueFailure{ param( $ServernameToCheck) $timer=0 Write-Host "Restarting HubTransport Server on $($PSBoundParameters['ServernameToCheck'])" sleep 2 Get-Service -ComputerName $PSBoundParameters['ServernameToCheck'] -Name MSExchangeTransport | Restart-Service -Force Get-Service -ComputerName $PSBoundParameters['ServernameToCheck'] -Name MSExchangeFrontEndTransport| Restart-Service -Force while ($timer -ne 120) { Trap { Write-Host "." -NoNewline -ForegroundColor RED continue } Start-Sleep 1 if (Get-Queue -server $PSBoundParameters['ServernameToCheck'] -ErrorAction stop){ Return 1 } $timer++ } Return 2 } Function Start-EMMRedirectMessage{ param( [parameter(mandatory=$True,ValueFromPipeline=$true,Position=0)]$SourceServer, [parameter(mandatory=$True)][ValidatePattern("(?=^.{1,254}$)(^(?:(?!\d+\.|-)[a-zA-Z0-9_\-]{1,63}(?<!-)\.?)+(?:[a-zA-Z]{2,})$)")][string]$ToServer, [parameter(mandatory=$False,ValueFromPipeline=$true,Position=0)][switch]$CheckOnly ) $counter=0 switch ($PSBoundParameters.ContainsKey('CheckOnly')) { $False { Write-Host "Redirecting the Queue..." try{ Redirect-Message -Server $PSBoundParameters['SourceServer'] -Target $PSBoundParameters['ToServer'] -Confirm:$False -ErrorAction Stop AddEmptylines -numberoflines 1 -MessageToIncludeAtTheEnd "Queue Transfar request sent.." -MessageColor Yellow } Catch{ $_.Exception.Message } } $True { Write-Host "Checking Queue Value, and waiting for it to be 0, the timeout for this process is 60 second, Please wait." try{ do { $QL=(Get-Queue -server $PSBoundParameters['SourceServer'] -ErrorAction stop | Where-Object {($_.DeliveryType -notlike "Shadow*") -and ($_.DeliveryType -notlike "Undefined") }| Select-Object Messagecount | Measure-Object -Sum -Property MessageCount).Sum if (($ql -eq 0) -or $($ql -eq $null)){return "Queue Transfer successfully"} Start-Sleep -Seconds 1 $counter++ if ($counter -eq 60){ Write-Host "Queue Transfer was not completed" Write-Host "The Number of remaining Queue is" $($QL) $YesNo=Read-Host "Press Y to continue or any other key to abort the process" if ($YesNo -like "Y"){return "Queue Transfer is not completed, But the user accepted it"} else{ Throw "User Aborted Queue Transfar.." } } } while ($ql -gt 0) } Catch{ $_.Exception.Message } } } } Function Set-EMMClusterConfig { Param( [parameter(mandatory=$true,ValueFromPipeline=$true,Position=0)]$ClusterNode, [parameter(mandatory=$true)][validateset("PauseThisNode","ResumeThisNode")]$PauseOrResume ) Process{ Write-Host "Starting Cluster Management for "-NoNewline ; Write-Host $PSBoundParameters['ClusterNode'] -ForegroundColor Yellow try{ Write-Host "Checking Cluster Readiness and resilience" -ForegroundColor Yellow $Status=Get-ClusterNode -Cluster (Get-DatabaseAvailabilityGroup) -ErrorAction Stop Write-Host "The number of Up Nodes are $(@(($Status | Where-Object {$_.state -like 'up'}).State).count)" -ForegroundColor Yellow if ($PSBoundParameters['PauseOrResume'] -like "PauseThisNode"){ if (@($Status | Where-Object {($_.state -like 'up') -and ($_.name -notlike $PSBoundParameters['ClusterNode'])}).count -eq 0){ Write-Host "WARNING: The number of available clusters is not enough, Please stop and resume one node at least" -ForegroundColor Red $Status | Select-Object Name,State,Cluster break } if (($Status | Where-Object{$_.name -like $PSBoundParameters['ClusterNode']}).State -Like "Paused"){ Write-Host "The node is already disabled...Nothing to do in this step" return "Node is Already Paused" } $clsstate=Suspend-ClusterNode -Name $PSBoundParameters['ClusterNode'] -Cluster (Get-DatabaseAvailabilityGroup) -ErrorAction Stop Start-Sleep -Seconds 2 return $clsstate.State } ## Resume Cluster node if ($PSBoundParameters['PauseOrResume'] -like "ResumeThisNode"){ if (($Status | Where-Object{$_.name -like $PSBoundParameters['ClusterNode']}).State -Like "Up"){ Write-Host "Node already Up...Nothing to do in this step" return "Node is Already Up" } $clsresumestate=Resume-ClusterNode -Name $PSBoundParameters['ClusterNode'] -Cluster (Get-DatabaseAvailabilityGroup) -ErrorAction Stop Start-Sleep -Seconds 2 return $clsresumestate.State } } Catch { Write-host $Error[0].Exception -ForegroundColor Red Write-Host "Failed to prepare the cluster, Please check if the computer name is correct and if the computer still reachable or went offline... Aborting" break } } End{ Write-Host "Cluster Management is completed..." } } Function Set-EMMDBActivationMoveNow{ [cmdletbinding()] Param( [parameter(Mandatory=$true, ValueFromPipeline=$true, Position=0)] $ServerName, [parameter(mandatory=$True)][validateset("IntrasiteOnly","Unrestricted","BlockMode")]$ActivationMode, [parameter(mandatory=$false)]$TimeoutBeforeManualMove=120, [parameter(mandatory=$false)][switch]$SkipValidation ) begin{ $FinalResult="" } Process{ Try{ ##Validation first $DBSetting=Get-MailboxServer if (@($DBSetting | Where-Object {($_.DatabaseCopyAutoActivationPolicy -notlike "Blocked") -and ($_.name -notlike $PSBoundParameters['ServerName'])}).count -eq 0){ Write-Warning "There is no available server with an Activation Policy set to Unrestricted or IntrasiteOnly" Write-Warning "Please ensure that there is at least one server available to handle the load..." Write-Warning "Try to run Stop-EMMDAGEnabled and set the healthy servers as a ServerInMaintenance." Write-Warning "This will ensure that the server is ready to be in service" $DBSetting break } if (($PSBoundParameters['ActivationMode'] -like "BlockMode")){ Set-MailboxServer $PSBoundParameters['ServerName'] -DatabaseCopyActivationDisabledAndMoveNow $true -ErrorAction stop Start-Sleep 1 $DatabaseCopyPolicy=Get-MailboxServer $PSBoundParameters['ServerName'] -ErrorAction Stop Write-Host "Please write down the current Activation policy as it might be needed later" write-host $DatabaseCopyPolicy.DatabaseCopyAutoActivationPolicy -ForegroundColor DarkRed -BackgroundColor Yellow Set-MailboxServer $PSBoundParameters['ServerName'] -DatabaseCopyAutoActivationPolicy Blocked -ErrorAction Stop if (@(Get-MailboxDatabaseCopyStatus -Server $PSBoundParameters['ServerName'] | Where-Object{$_.Status -eq "Mounted"}).count -eq 0){ Write-Host "No Active Database on this server was found... The New DatabaseCopyAutoActivationPolicy is: " -NoNewline Write-Host (Get-MailboxServer $PSBoundParameters['ServerName']).DatabaseCopyAutoActivationPolicy -ForegroundColor Green return "No Active Database, Server is ready" } try{ Write-Host "EMMEXDAGModule v2 note: Database migration will follow database activation preference instead of moving all databases to a single server." -ForegroundColor Yellow Write-host "Manual migration will start and move all DBs from $($PSBoundParameters['ServerName'])" Write-Host "ReplayQueue Length and Copy Queue length should be zero, if not the script will wait untill all transaction are completed." $DBOnServer=Get-MailboxDatabaseCopyStatus -Server $PSBoundParameters['ServerName'] -ErrorAction stop| Where-Object{$_.Status -eq "Mounted"} foreach ($singleDB in $DBOnServer){ # Checking Queue length Write-Host "Processing" $($singleDB).DatabaseName -ForegroundColor Green $DBOnRemoteServerQL=Get-MailboxDatabase $singleDB.DatabaseName | Get-MailboxDatabaseCopyStatus -ErrorAction Stop | Where-Object {($_.databasename -like $singleDB.DatabaseName) -and ($_.MailboxServer -notlike $PSBoundParameters['ServerName'])} if (($DBOnRemoteServerQL.status -contains 'FailedAndSuspended')){ Write-Host "WARNING: The other copy of this database is not health, failover for this database wont work" -ForegroundColor Red -BackgroundColor White Write-Host "The Database wont move. Before shutting down the server, make sure to have this DB Fixed and moved safely" -ForegroundColor Red -BackgroundColor White Continue } $TotalQueueLength =$(($DBOnRemoteServerQL.copyQueuelength | Measure-Object -Sum).Sum) +$(($DBOnRemoteServerQL.ReplayQueueLength | Measure-Object -Sum).Sum) if ($TotalQueueLength -gt 0){ Write-Host "Some pending Logs are waiting for replay, I will wait till the process is finished" do{ Write-Host "." -NoNewline $DBOnRemoteServerQL=Get-MailboxDatabase $singleDB.DatabaseName | Get-MailboxDatabaseCopyStatus -ErrorAction Stop | Where-Object {($_.databasename -like $singleDB.DatabaseName) -and ($_.MailboxServer -notlike $PSBoundParameters['ServerName'])} Start-Sleep 1 } While ( $(($DBOnRemoteServerQL.copyQueuelength | Measure-Object -Sum).Sum) +$(($DBOnRemoteServerQL.ReplayQueueLength | Measure-Object -Sum).Sum) -ne 0 ) } Else{ switch($PSBoundParameters.ContainsKey('SkipValidation')){ $true {Move-ActiveMailboxDatabase -Identity $singleDB.DatabaseName -Confirm:$false -ErrorAction Stop -SkipClientExperienceChecks -SkipCpuChecks -SkipMaximumActiveDatabasesChecks -MoveComment "EMM Module" -SkipMoveSuppressionChecks } $false {Move-ActiveMailboxDatabase -Identity $singleDB.DatabaseName -Confirm:$false -ErrorAction Stop } } Write-Host "Database $($singleDB.DatabaseName) is now hosted on " -NoNewline Write-Host $(Get-MailboxDatabase | Get-MailboxDatabaseCopyStatus | Where-Object {($_.databasename -like $singleDB.DatabaseName) -and ($_.status -like "mounted")}).MailboxServer -ForegroundColor Green Start-Sleep -Seconds 1 } } } Catch [Microsoft.Exchange.Cluster.Replay.AmDbActionWrapperException]{ Write-Host "It seems that there still more logs to be shipped, please check the error below and try to re-run the commands after sometime" -ForegroundColor Yellow Write-Host "Or the database has been already activated on the remote server." Write-Host $_.exception.message return "Require review, Please Run Get-MailboxDatabaseCopyStatus and also run the Test-EMMReadiness cmdlet to confirm the readiness" } catch [Microsoft.Exchange.Cluster.Replay.AmDbMoveMoveSuppressedException]{ Write-Host "`nIt seems that there are multiple move request for this database" -ForegroundColor Red Write-Host $_.exception.message -ForegroundColor Red Write-Host "To ignore the error and move the database, use the following paramter " -NoNewline -ForegroundColor white Write-Host "-SkipValidation" -ForegroundColor Green } catch{ Write-Warning $_.Exception.Message break } } Else{ Write-Host "Leaving Block Mode" try{ Set-MailboxServer $PSBoundParameters['ServerName'] -DatabaseCopyAutoActivationPolicy $PSBoundParameters['ActivationMode'] -ErrorAction Stop Set-MailboxServer $PSBoundParameters['ServerName'] -DatabaseCopyActivationDisabledAndMoveNow $false -ErrorAction Stop Start-Sleep 1 $FinalResult= (Get-MailboxServer $PSBoundParameters['ServerName'] -ErrorAction Stop) return $FinalResult.DatabaseCopyAutoActivationPolicy } catch{ Write-Host $Error[0] break } } } Catch{ Write-Host "Failure in Set-EMMDBActivationMoveNow" Write-Host $Error[0] break } } End{ Write-Host "Activation configuration is completed..." } } Function Test-EMMReadiness{ param( [parameter(Mandatory=$false)][switch]$IgnoreCluster ) Process{ Write-Host "This process will check the server readiness" -ForegroundColor Yellow Write-Host "There will be no move or any change to the environment, just a check" -ForegroundColor Yellow $EXServers=Get-ExchangeServer AddEmptylines -numberoflines 1 -MessageToIncludeAtTheEnd "Testing Exchange Services Ports reachability, Checking Port 80..." -MessageColor Yellow ($EXServers).foreach{$Port80Test=Test-NetConnection -ComputerName $_.name -Port 80 if ($Port80Test.TcpTestSucceeded -like $True){ Write-Host $($_.name) -ForegroundColor Green -NoNewline;Write-Host " is reachable on Port 80" } Else{ Write-Host $($_.name) -ForegroundColor Red -NoNewline;Write-Host " is NOT reachable on Port 80" } } AddEmptylines -numberoflines 1 -MessageToIncludeAtTheEnd "Checking SSL Certificate Validity " -MessageColor Yellow ($EXServers).foreach{ Write-Host "Certificates from $_.name" $SrvCert=Get-ExchangeCertificate -Server $_ foreach ($SingleCert in $SrvCert){ Write-host "Certificate Detail for $($SingleCert.Thumbprint)" -ForegroundColor Yellow $SingleCert | Format-List if ($SingleCert.NotAfter -lt (Get-Date)){Write-Host "$($SingleCert.Thumbprint) has expired" -ForegroundColor Red} Else {Write-host "$($SingleCert.Thumbprint) looks OK`n" -ForegroundColor Green} } } AddEmptylines -numberoflines 1 -MessageToIncludeAtTheEnd "Testing Exchange Ports reachability, Checking Port 443..." -MessageColor Yellow ($EXServers).foreach{$Port443Test=Test-NetConnection -ComputerName $_.name -Port 443 if ($Port443Test.TcpTestSucceeded -like $True){ Write-Host $($_.name) -ForegroundColor Green -NoNewline;Write-Host " is reachable on Port 443" } Else{ Write-Host $($_.name) -ForegroundColor Red -NoNewline;Write-Host " is NOT reachable on Port 443" } } AddEmptylines -numberoflines 1 -MessageToIncludeAtTheEnd "Checking HubTransport Server Component" -MessageColor Yellow $ServerComp=Get-ExchangeServer | Get-ServerComponentState -Component Hubtransport if (!($ServerComp | Where-Object {($_.State -like "Active")})){ Write-host "You Don't have any additional Node with a Hubtransport State set to Active" -ForegroundColor Red $ServerComp } Else{ $ServerComp.foreach{ if ($_.state -like "Active"){Write-Host "The HubTransport State of $($_.ServerFqdn) is: " -NoNewline; Write-Host "Active" -ForegroundColor Green} Else{ Write-Host "WARNING: The HubTransport State of $($_.ServerFqdn) is: " -NoNewline; Write-Host $_.State -ForegroundColor RED} } } AddEmptylines -numberoflines 1 -MessageToIncludeAtTheEnd "Checking ServerWideOffline Server Component" -MessageColor Yellow $ServerCompSWO=Get-ExchangeServer | Get-ServerComponentState -Component ServerWideOffline if (!($ServerCompSWO | Where-Object {($_.State -like "Active")})){ Write-host "You Don't have any additional Node with a ServerWideOffline State set to Active" -ForegroundColor Red } Else{ $ServerCompSWO.foreach{ if ($_.state -like "Active"){Write-Host "The ServerWideOffline State of $($_.ServerFqdn) is: " -NoNewline; Write-Host "Active" -ForegroundColor Green} Else{ Write-Host "WARNING: The ServerWideOffline State of $($_.ServerFqdn) is: " -NoNewline; Write-Host $_.State -ForegroundColor RED} } } AddEmptylines -numberoflines 1 -MessageToIncludeAtTheEnd "Checking HighAvailability Server Component" -MessageColor Yellow $ServerCompHA=Get-ExchangeServer | Get-ServerComponentState -Component HighAvailability if (!($ServerCompHA | Where-Object {($_.State -like "Active")})){ Write-host "You Don't have any additional Node with a HighAvailability State set to Active" -ForegroundColor Red $ServerCompHA } Else{ $ServerCompHA.foreach{ if ($_.state -like "Active"){Write-Host "The HighAvailability State of $($_.ServerFqdn) is: " -NoNewline; Write-Host "Active" -ForegroundColor Green} Else{ Write-Host "WARNING: The HighAvailability State of $($_.ServerFqdn) is: " -NoNewline; Write-Host $_.State -ForegroundColor RED} } } switch ($PSBoundParameters.ContainsKey('IgnoreCluster')){ $true {Write-Host "Skipping Cluster check..." -ForegroundColor Yellow } $false {Write-Host "Starting Cluster Check..." -ForegroundColor Yellow $Status=Get-Cluster (Get-DatabaseAvailabilityGroup)| Get-ClusterNode if (!($Status | Where-Object {($_.state -like 'up')})){ Write-Host "WARNING: The number of available clusters is not enough, Please resume one node at least" -ForegroundColor Red $Status } Else{ Write-Host "Active Cluster Nodes are: " -NoNewline ;Write-Host $($Status | Where-Object {$_.state -like "Up"}).count -ForegroundColor Green Write-Host "Unstable Cluster Nodes are: " -NoNewline $NotUpCluster=@($Status | Where-Object {$_.state -notlike "Up"}).count switch ($NotUpCluster) { '0' {Write-Host "0" -ForegroundColor Green} {$_ -gt 0} {Write-Host $($Status | Where-Object {$_.state -notlike "Up"}).count -ForegroundColor Red} } $Status | Where-Object {$_.state -notlike "Up"} } } } AddEmptylines -numberoflines 2 -MessageToIncludeAtTheEnd "Checking Exchange Servers for Mounting policy" -MessageColor Yellow $DBSetting=Get-MailboxServer if (!($DBSetting | Where-Object {($_.DatabaseCopyAutoActivationPolicy -notlike "Blocked")})){ Write-Warning "There is no available server with an Mounting Policy set to Unrestricted or IntrasiteOnly" Write-Warning "Please ensure that there is at least one server available to handle the load..." $DBSetting | Select-Object name,DatabaseCopyAutoActivationPolicy,DatabaseCopyActivationDisabledAndMoveNow } Else{ $DBSetting.ForEach{ if ($_.DatabaseCopyAutoActivationPolicy -like "Unrestricted"){Write-Host "Mounting Policy for $($_.Name) is: "-NoNewline; Write-Host "Unrestricted" -ForegroundColor Green} if ($_.DatabaseCopyAutoActivationPolicy -Like "IntrasiteOnly"){Write-Host "Mounting Policy for $($_.Name) is: "-NoNewline; Write-Host "IntrasiteOnly" -ForegroundColor Yellow} if ($_.DatabaseCopyAutoActivationPolicy -Like "Blocked"){Write-Host "Mounting Policy for $($_.Name) is: "-NoNewline; Write-Host "Blocked" -ForegroundColor Red} } } AddEmptylines -numberoflines 1 -MessageToIncludeAtTheEnd "Checking Exchange Servers for Activating Policy" -MessageColor White if (@($DBSetting | Where-Object {($_.DatabaseCopyActivationDisabledAndMoveNow -notlike $true)}).count -eq 0){ Write-Warning "There is no available server with an Activation Policy set to Unrestricted or IntrasiteOnly" Write-Warning "Please ensure that there is at least one server available to handle the load..." $DBSetting | Select-Object name,DatabaseCopyAutoActivationPolicy,DatabaseCopyActivationDisabledAndMoveNow } Else{ $DBSetting.ForEach{ if ($_.DatabaseCopyActivationDisabledAndMoveNow -like $False){Write-Host "Activation Policy for $($_.Name) is: "-NoNewline; Write-Host "Can host DB" -ForegroundColor Green} if ($_.DatabaseCopyActivationDisabledAndMoveNow -Like $true){Write-Host "Activation Policy for $($_.Name) is: "-NoNewline; Write-Host "Not Recommended, True for DatabaseCopyActivationDisabledAndMoveNow" -ForegroundColor red} } } Write-Host "Checking Servicelth:`n" foreach($singleExServer in $EXServers){ $ServiceNotRunning=Test-ServiceHealth -Server $singleExServer $ServiceNotRunning.ForEach{ if ($_.ServicesNotRunning.count -gt 0){ write-host $singleExServer "has " -NoNewline write-host $_.ServicesNotRunning.count -NoNewline -ForegroundColor Red Write-Host " of failed Service:" -NoNewline Write-Host $_.ServicesNotRunning -ForegroundColor red } Else{ write-host $singleExServer $_.Role -NoNewline Write-Host " OK" -ForegroundColor Green } } } Write-Host "Checking Log size, make sure that there is no log queue or copy queue" (get-ExchangeServer).foreach{ Get-MailboxDatabaseCopyStatus -Server $_.name | Format-Table Name,Status,ContentIndexState,CopyQueueLength,ReplayQueueLength} Write-Host "Testing Replication Health" get-exchangeserver | Test-ReplicationHealth | Format-Table -AutoSize } End{ Write-Host "Process is completed.." } } Export-ModuleMember Test-EMMReadiness Write-Host "***************************************************************" -ForegroundColor White Write-Host "Welcome to EMM (Exchange Maintenance Module)" -ForegroundColor Green -NoNewline Write-Host " V2.1" -ForegroundColor Yellow Write-Host "***************************************************************" -ForegroundColor White Write-Host "Checking for latest version update details and known issues.. " -ForegroundColor Green try{ [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 $Messageoftheday=(Invoke-WebRequest -Method get -Uri 'https://www.powershellcenter.com/psmessage/emm.txt').content Write-Host $Messageoftheday -ForegroundColor yellow } Catch{ $_.exception.Message Write-Host "Cannot connect to PowerShellcenter.com, its OK.. the script will continue normally :)" -ForegroundColor Green } Write-Host "Please Give me a moment to load Exchange Snapin...." -ForegroundColor Green try{ Import-Module $env:ExchangeInstallPath\bin\RemoteExchange.ps1 -ErrorAction Stop Connect-ExchangeServer -Auto -ClientApplication:ManagementShell } catch{ Write-Warning "Ops, something went wrong, are you sure you have Exchange Management Shell installed ?!`n" Throw $_.exception.message } Write-Host "One more tip: Run this Module using RunAsAdministrator " -ForegroundColor Green Write-Host "If you have any issue or idea request, please feel free and post it as an Issue on my GitHub or keep it a comment on the Module home page" Write-Host "https://github.com/farismalaeb/Powershell/issues" -ForegroundColor Blue -BackgroundColor White |