functions/Restart-D365LBDSFAppServers.ps1

function Restart-D365LBDSFAppServers {
    <#
    .SYNOPSIS
    Restarts all application nodes in the service fabric layer. Also has the ability to reboot the whole Operating system (OS) and also to restart the orchestrator nodes (full OS restart only)
   .DESCRIPTION
   Restarts all application nodes in the service fabric layer. Also has the ability to reboot the whole Operating system (OS) and also to restart the orchestrator nodes (full OS restart only)
   .EXAMPLE
   Restart-D365LBDSFAppServers
   Based on the local server it will determine all the environment's servers and restart the service fabric nodes in the service fabric layer.
   .EXAMPLE
    Restart-D365LBDSFAppServers -ComputerName "LBDServerName" -verbose
    Based on the defined server it will determine all the environment's servers and restart the service fabric nodes in the service fabric layer.
   .EXAMPLE
    Restart-D365LBDSFAppServers -config $config -RebootWholeOS -verbose
    Based on the defined config it will determine all the environment's AX SF application servers and restart the whole Operating system of each.
    .EXAMPLE
    Restart-D365LBDSFAppServers -config $config -RebootWholeOSIncludingOrch -verbose -waittillhealthy
    Based on the config it will determine all the environment's servers including orchestrator nodes and restart the whole Operating system of each and will wait till the servers are "healthy" (can be accessed for more commands).
   .PARAMETER ComputerName
   String
   The name of the D365 LBD Server to grab the environment details; needed if a config is not specified and will default to local machine.
   .PARAMETER Config
    Custom PSObject
    Config Object created by either the Get-D365LBDConfig or Get-D365TestConfigData function inside this module
   #>

    [alias(" Restart-D365SFAppServers")]
    [CmdletBinding()]
    param([Parameter(ValueFromPipeline = $True,
            ValueFromPipelineByPropertyName = $True,
            Mandatory = $false,
            HelpMessage = 'D365FO Local Business Data Server Name',
            ParameterSetName = 'NoConfig')]
        [PSFComputer]$ComputerName = "$env:COMPUTERNAME",
        [Parameter(ParameterSetName = 'Config',
            ValueFromPipeline = $True)]
        [psobject]$Config,
        [int]$Timeout = 600,
        [switch]$waittillhealthy,
        [switch]$RebootWholeOS,
        [switch]$RebootWholeOSIncludingOrch
    )
    ##Gather Information from the Dynamics 365 Orchestrator Server Config
    BEGIN {
    } 
    PROCESS {
        if (!$Config -or $Config.OrchestratorServerNames.Count -eq 0) {
            Write-PSFMessage -Level VeryVerbose -Message "Config not defined or Config is invalid. Trying to Get new config using $ComputerName"
            $Config = Get-D365LBDConfig -ComputerName $ComputerName
        }[int]$count = 0
        while (!$connection) {
            do {
                $OrchestratorServerName = $Config.OrchestratorServerNames | Select-Object -First 1 -Skip $count
                Write-PSFMessage -Message "Verbose: Reaching out to $OrchestratorServerName to try and connect to the service fabric" -Level Verbose
                $SFModuleSession = New-PSSession -ComputerName $OrchestratorServerName
                if (!$module) {
                    $module = Import-Module -Name ServiceFabric -PSSession $SFModuleSession 
                }
                Write-PSFMessage -Message "-ConnectionEndpoint $($config.SFConnectionEndpoint) -X509Credential -FindType FindByThumbprint -FindValue $($config.SFServerCertificate) -ServerCertThumbprint $($config.SFServerCertificate) -StoreLocation LocalMachine -StoreName My" -Level Verbose
                $connection = Connect-ServiceFabricCluster -ConnectionEndpoint $config.SFConnectionEndpoint -X509Credential -FindType FindByThumbprint -FindValue $config.SFServerCertificate -ServerCertThumbprint $config.SFServerCertificate -StoreLocation LocalMachine -StoreName My
                $count = $count + 1
                if ($connection) {
                    $ConnectedSFConnectionEndpoint = $config.SFConnectionEndpoint
                    $ConnectedOrchestratorServerName = $OrchestratorServerName
                    Write-PSFMessage -Message "Connected using: $ConnectedSFConnectionEndpoint and $ConnectedOrchestratorServerName" -Level VeryVerbose
                }
                if (!$connection) {
                    $trialEndpoint = "https://$OrchestratorServerName" + ":198000"
                    $connection = Connect-ServiceFabricCluster -ConnectionEndpoint $trialEndpoint -X509Credential -FindType FindByThumbprint -FindValue $config.SFServerCertificate -ServerCertThumbprint $config.SFServerCertificate -StoreLocation LocalMachine -StoreName My
                    if ($connection) {
                        $ConnectedSFConnectionEndpoint = $trialEndpoint 
                        $ConnectedOrchestratorServerName = $OrchestratorServerName
                        Write-PSFMessage -Message "Connected using: $ConnectedSFConnectionEndpoint and $ConnectedOrchestratorServerName" -Level VeryVerbose
                    }
                }
                
                if (!$connection) {
                    Write-PSFMessage -Message "Count of servers tried $count" -Level Verbose
                }
            } until ($connection -or ($count -eq $($Config.OrchestratorServerNames).Count) -or ($($Config.OrchestratorServerNames).Count -eq 0))
            if (($count -eq $($Config.OrchestratorServerNames).Count) -and (!$connection)) {
                Stop-PSFFunction -Message "Error: Can't connect to Service Fabric"
            }
        }
        $AppNodes = Get-ServiceFabricNode | Where-Object { ($_.NodeType -eq "AOSNodeType") -or ($_.NodeType -eq "MRType") -or ($_.NodeType -eq "ReportServerType") -or ($_.NodeType -eq "PrimaryNodeType") } 
      
        if ($RebootWholeOS -or $RebootWholeOSIncludingOrch) {
            if ($RebootWholeOSIncludingOrch) {
                if ($waittillhealthy) {
                    Write-PSFMessage -Message "Restarting $($config.AllAppServerList) and Waiting for Powershell to be available" -Level Verbose
                    Restart-computer -ComputerName  $config.AllAppServerList -Force -Wait -for PowerShell -Delay 2 -Verbose
                }
                else {
                    Write-PSFMessage -Message "Restarting $($config.AllAppServerList)" -Level Verbose
                    Restart-computer -ComputerName  $config.AllAppServerList -Force -Wait -for PowerShell -Delay 2 -Verbose
                }   
            }
            else {
                ## Only SF nodes
                if ($waittillhealthy) {
                    Write-PSFMessage -Message "Restarting $($config.AXSFServerNames) and Waiting for Powershell to be available" -Level Verbose
                    Restart-computer -ComputerName  $config.AXSFServerNames -Force -Wait -for PowerShell -Delay 2
                }
                else {
                    Write-PSFMessage -Message "Restarting $($config.AXSFServerNames)" -Level Verbose
                    Restart-computer -ComputerName  $config.AXSFServerNames -Force -Verbose
                }  
            }
        }
        else {
            foreach ($AppNode in $AppNodes) {
                Restart-ServiceFabricNode -NodeName $AppNode.NodeName -CommandCompletionMode Verify -Timeout 200
            }
        }
        [int] $timer = 0
        do {
            $OrchestratorServerName = $ConnectedOrchestratorServerName
            Write-PSFMessage -Message "Verbose: Disconnected Connection Reaching out to $OrchestratorServerName to try and connect to the service fabric." -Level Verbose
                
            $SFModuleSession = New-PSSession -ComputerName $OrchestratorServerName
            if (!$module) {
                $module = Import-Module -Name ServiceFabric -PSSession $SFModuleSession 
            }
            Write-PSFMessage -Message "-ConnectionEndpoint $($ConnectedSFConnectionEndpoint) -X509Credential -FindType FindByThumbprint -FindValue $($config.SFServerCertificate) -ServerCertThumbprint $($config.SFServerCertificate) -StoreLocation LocalMachine -StoreName My" -Level Verbose
            $connectionnew = Connect-ServiceFabricCluster -ConnectionEndpoint $ConnectedSFConnectionEndpoint -X509Credential -FindType FindByThumbprint -FindValue $config.SFServerCertificate -ServerCertThumbprint $config.SFServerCertificate -StoreLocation LocalMachine -StoreName My 
            if (!$connectionnew) {
                Write-PSFMessage -Message "Verbose: Sleeping 10 seconds to try and reconnect to service fabric at $ConnectedSFConnectionEndpoint." -Level Verbose
                Start-Sleep -Seconds 10
                $timer = $timer + 10
            }
                
        } until ($connectionnew -or ($timer -gt $Timeout))
        
      
        Start-Sleep -Seconds 5
        [int]$timeoutondisablecounter = 0
        $nodestatus = Get-ServiceFabricNode | Where-Object { $_.NodeStatus -eq 'Disabled' -and (($_.NodeType -eq "AOSNodeType") -or ($_.NodeType -eq "MRType")) }
        do {
            $nodestatus = Get-ServiceFabricNode | Where-Object { $_.NodeStatus -eq 'Disabled' -and (($_.NodeType -eq "AOSNodeType") -or ($_.NodeType -eq "MRType")) } 
            $timeoutondisablecounter = $timeoutondisablecounter + 5
            Start-Sleep -Seconds 5
        } until (!$nodestatus -or $nodestatus -eq 0 -or ($timeoutondisablecounter -gt $Timeout))
        Write-PSFMessage -Message "All App Nodes Enabled" -Level VeryVerbose

        do {
            try {
                $apps = Get-ServiceFabricApplication -ErrorAction Stop
                Start-Sleep -Seconds 5
            }
            catch {}
        } until ($apps.count -gt 0)
        $counterofhealthyapps = 0
        foreach ($app in $apps) {
            $health = Get-serviceFabricApplicationHealth -ApplicationName $app.ApplicationName

            if ($health.aggregatedhealthstate -eq "Ok") {
                $counterofhealthyapps = $counterofhealthyapps + 1
            }
            else {
                Write-PSFMessage -Level Warning -Message "Warning: $($health.ApplicationName) is Unhealthy"
                if ($waittillhealthy) {
                    $timer = 0
                    Write-PSFMessage -Message "Waiting for $($app.ApplicationName) to be healthy" -Level VeryVerbose`
                    do {
                        $health = Get-serviceFabricApplicationHealth -ApplicationName $app.ApplicationName
                        $timer = $timer + 10
                        Start-Sleep -Seconds 10
                        Write-PSFMessage -Message "Waiting for $($app.ApplicationName) to be healthy" -Level VeryVerbose
                    } until ($health.aggregatedhealthstate -eq "Ok" -or $timer -gt $Timeout)
                    Write-PSFMessage -Message "Health AggregatedHealthState is $($health.aggregatedhealthstate)" -Level VeryVerbose
                }
                if ($timer -gt $Timeout) {
                    Write-PSFMessage -Message "Warning: Timeout occured $Timeout seconds" -Level Warning
                }
                else {
                    if ($waittillhealthy) {
                        $RealHealth = Get-D365LBDEnvironmentHealth -ComputerName $ConnectedOrchestratorServerName | Where-Object { $_.Name -eq "ServiceFabricApplications" }
                        if ($RealHealth.State -eq "Operational") {
                            Write-PSFMessage -Level VeryVerbose -Message "All Service Fabric Apps are up"
                        }
                        else {
                            $timer = 0
                            while ($RealHealth.state -ne "Operational" -and $timer -le $Timeout) {
                                $timer = $timer + 10
                                Write-PSFMessage -Level VeryVerbose -Message "Sleeping before next check of health"
                                Start-Sleep -Seconds 10
                                $RealHealth = Get-D365LBDEnvironmentHealth -ComputerName $ConnectedOrchestratorServerName | Where-Object { $_.Name -eq "ServiceFabricApplications" }
                            }
                        }
                        Write-PSFMessage -Level VeryVerbose -Message "$RealHealth"
                    }
                }
            }

        }
    }
    END {}
}