Invoke-magdevExchangeMW.ps1

<#PSScriptInfo
.DESCRIPTION
    Allows Interactive Patching of Exchange during maintenance windows (WSUS/SCCM)
.VERSION
    1.0
.GUID
    6e52afd9-3a79-48ed-97a5-8c6a0a804eae
.AUTHOR
    Ken Maglio @kenmaglio
.TAGS
    2013
.RELEASENOTES
    Intial Release.
#>



Add-PSSnapin *exchange* -ErrorAction SilentlyContinue

$WhatIf = $false # you can set to $true and this will not take effect


function Get-Answer {

    param(
        $caption = "Proceed?",
        $message = "Please see prior output to decide!",
        $option1 = "Proceed",
        $option2 = "Stop"
    )
    
    $opt1 = new-Object System.Management.Automation.Host.ChoiceDescription "&$option1","$option1";
    $opt2 = new-Object System.Management.Automation.Host.ChoiceDescription "&$option2","$option2";
    $choices = [System.Management.Automation.Host.ChoiceDescription[]]($opt1,$opt2);
    $answer = $host.ui.PromptForChoice($caption,$message,$choices,0)

    switch ($answer){
        0 { return $true  }
        1 { return $false }
        default { return $false }
    }    
}

function Get-MailStoreHealth {
    param (
        $copyQueueLengthMax = 100
    )
    $allStores = Get-MailboxDatabaseCopyStatus *
    $healthy = 0
    $mounted = 0
    $other = 0
    $copyQueueLengthCurrent = 0

    $allStores | ForEach-Object {
        switch ($_.Status) {
            "Mounted" { $mounted++ }
            "Healthy" { $healthy++ }
            default { $other++ }
        }

        if ($_.CopyQueueLength -gt $copyQueueLengthCurrent) {
            $copyQueueLengthCurrent =  $_.CopyQueueLength
        }
    }

    Write-Host "Current Status: " -ForegroundColor White
    Write-Host "Mounted: $mounted" -ForegroundColor Magenta
    Write-Host "Healthy: $healthy" -ForegroundColor Green
    Write-Host "Other: $other" -ForegroundColor Red
    Write-Host ""
    Write-Host "Max Copy Queue Length: $copyQueueLengthCurrent" -ForegroundColor Cyan


    if ($other -gt 0) {
        Write-Host "*********************************************"  -ForegroundColor Red
        Write-Host "*********************************************"  -ForegroundColor Red
        Write-Host "***** MailStores Not Healthy ******"  -ForegroundColor Red
        Write-Host "*********************************************"  -ForegroundColor Red
        Write-Host "*********************************************"  -ForegroundColor Red
        return $false
    } elseif ($copyQueueLengthCurrent -gt $copyQueueLengthMax) {
        Write-Host "*********************************************"  -ForegroundColor Red
        Write-Host "*********************************************"  -ForegroundColor Red
        Write-Host "***** Copy Queue Length > $copyQueueLengthMax ******"  -ForegroundColor Red
        Write-Host "*********************************************"  -ForegroundColor Red
        Write-Host "*********************************************"  -ForegroundColor Red
        return $false
    } else {
        return $true
    }

}

function Restart-ComputerAndWaitForExchange {
    param(
        $server,
        $WhatIf,
        $waitMinutes = 2
    )

    #reboot server
    if(Get-Answer -message "Do you want to reboot $($server)?" -option1 "Yes" -option2 "No") {
        Write-Host "Issuing Reboot to Server and Waiting for the reboot..." -ForegroundColor Yellow
        Restart-Computer $server -Force -Confirm:$false -Wait -WhatIf:$WhatIf 
        #wait for exchange to be healthy
            
    
        Write-Host "Waiting $waitMinutes minutes for exchange health ..."
        if (-not($WhatIf) ) {
            $Time = [System.Diagnostics.Stopwatch]::StartNew()
            while($Time.Elapsed.Minutes -lt 5) {
                Write-Host "Elapsed: $($Time.Elapsed.Minutes)min."
                $success = Get-MailStoreHealth 
                if ($success) { break; }
                Start-Sleep -Seconds 30                
            }
            $Time.Stop()


            if ($Time.Elapsed.Minutes -ge $waitMinutes) {
                Write-Host "Time Elapsed is > $($waitMinutes)min. and we still have bad Exchange Health"

                Write-Host "*********************************************"  -ForegroundColor Red
                Write-Host "*********************************************"  -ForegroundColor Red
                Write-Host "***** BAD EXCHANGE HEALTH ******"  -ForegroundColor Red
                Write-Host "*********************************************"  -ForegroundColor Red
                Write-Host "*********************************************"  -ForegroundColor Red

                return $false
            }

            Write-Host "Server Reboot and Wait complete!" -ForegroundColor Green

            return $true

        } else {
            Write-Host "---WhatIf -- not waiting"
            return $true
        }        
    }
}


#####################################################################################################################
#####################################################################################################################
#####################################################################################################################
#####################################################################################################################

Get-MailStoreHealth | Out-Null

if (Get-Answer -message "Is Exchange Healthy Enough To Proceed?" -option1 "Yes" -option2 "NO! STOP THE PRESSES!") {
    $allExServers = Get-ExchangeServer
    ForEach($server in $allExServers) {
        Write-Host ("Server: $($server.Name)")        

        $mountedStores = Get-MailboxDatabaseCopyStatus -Server $server.id  | Where {$_.Status -eq 'Mounted'}

        $activationsDone = 0
        if ($mountedStores.Count -gt 0) {  
        
            if(Get-Answer -message "There are active copies on $($server.Name)... Proceed?" -option2 "Skip") {
                                                      
                foreach($mountedStore in $mountedStores) {                
                    $mailStoreName = $mountedStore.Name.Split("\")[0]
                    $mountedServer = $mountedStore.Name.Split("\")[1]
                    Write-Host "Mailstore $mailStoreName is on $($server.Name)" -ForegroundColor Green                
                    $allCopies = Get-MailboxDatabaseCopyStatus $mailStoreName | Where {$_.Name -notlike "*$($mountedServer)"}
                    $moveToServer = $allCopies[0].Name.Split("\")[1]
                    Write-Host "Intent is to Activate on $moveToServer" -ForegroundColor Yellow
                
                    if(Get-Answer -message "Activate $mailStoreName on $($moveToServer)?" -option2 "Skip") {
                        Write-Host "Activating Mailstore..."
                        try {
                            Move-ActiveMailboxDatabase $mailStoreName -ActivateOnServer $moveToServer -WhatIf:$WhatIf -Confirm:$false
                            Write-Host "Activating Mailstore Complete!!!" -ForegroundColor Green
                        } catch {
                            Write-Host "ERROR!!!" -ForegroundColor Red
                            break;
                        }

                        $activationsDone++

                    } else {
                        Write-Host " --- Not Moving cause you said not to..."
                    }
                }       
                Write-Host "Mounted Stores: $($mountedStores.Count) -- 'Move' Activations Completed: $activationsDone"

                $rebootAndHealthy = Restart-ComputerAndWaitForExchange -server $server -WhatIf $WhatIf     
                if (-not($rebootAndHealthy)) {
                    Write-Host "Stopping Script"
                    break;
                }       
            } else {
                Write-Host "Skipping $($server.Name)... " -ForegroundColor Yellow
            }
        } else {
            Write-Host "There are no mailstores active on $($server.Name)..." -ForegroundColor Yellow

            Restart-ComputerAndWaitForExchange -server $server -WhatIf $WhatIf            
        }                
    }

    if(Get-Answer -message "Rebalance?" -option1 "Yup! Do it!" -option2 "No - leave it alone") {
        $scriptPath = "C:\Program Files\Microsoft\Exchange Server\V15\Scripts\RedistributeActiveDatabases.ps1"
        
        $dag = Get-DatabaseAvailabilityGroup

        $argumentList = @()
        $argumentList += ("-DagName", "$($dag.Name)")
        $argumentList += ("-BalanceDbsByActivationPreference")        
        $argumentList += ("-Confirm:`$false")
        $argumentList += ("-WhatIf:`$$($WhatIf.ToString().ToLower())")

        Write-Host "Attempting to invoke-expression with: `"$scriptPath`" $argumentList"
        Invoke-Expression "& `"$scriptPath`" $argumentList"

    }
}