functions/Get-D365LBDDependencyHealth.ps1

function Get-D365LBDDependencyHealth {
    <#
    .SYNOPSIS
   Checks and validates the dependencies configured in the AdditionalEnvironmentDetails.xml
   .DESCRIPTION
    Checks and validates the dependencies configured in the AdditionalEnvironmentDetails.xml This can check web addresses, services running, processes open, and databases being accessible.
    This is recommended to be ran from the environment itself as some dependencies are better ran from the required environment.
   .EXAMPLE
    Get-D365LBDDependencyHealth
   Checks and validates the dependencies configured in the AdditionalEnvironmentDetails.xml on the local server's environment
   .EXAMPLE
   Get-D365LBDDependencyHealth -config $Config
   Checks and validates the dependencies configured in the AdditionalEnvironmentDetails.xml on the defined configuration's environment
   .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
   .PARAMETER CustomModuleName
   optional string
   The name of the custom module you will be using to capture the version number
   .PARAMETER WebsiteChecksOnly
   switch
   If you want to only check the website dependencies
   .PARAMETER SendAlertIfIssue
    switch
   If you want to only to send alerts when there is a found issue with a dependency
   .PARAMETER SMTPServer
   string
   Email smtp server to email the alerts if issue (that switch must be on as well)
   .PARAMETER MSTeamsURI
   string
   MSTeams URI smtp server to email the alerts if issue (that switch must be on as well)
   #>

    [alias("Get-D365DependencyHealth")]
    [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,
        [string]$CustomModuleName,
        [switch]$WebsiteChecksOnly,
        [switch]$SendAlertIfIssue,
        [string]$SMTPServer,
        [string]$MSTeamsURI
    )
    ##Gather Information from the Dynamics 365 Orchestrator Server Config
    BEGIN {
    } 
    PROCESS {
        if (!$Config -or $Config.OrchestratorServerNames.Count -eq 0) {
            if ($CustomModuleName) {
                $Config = Get-D365LBDConfig -ComputerName $ComputerName -CustomModuleName $CustomModuleName -highlevelonly
            }
            else {
                $Config = Get-D365LBDConfig -ComputerName $ComputerName -highlevelonly
            }
        }
        $AgentShareLocation = $config.AgentShareLocation 
        $OutputList = @()
        $EnvironmentAdditionalConfig = get-childitem  "$AgentShareLocation\scripts\D365FOLBDAdmin\AdditionalEnvironmentDetails.xml"
        if (!$EnvironmentAdditionalConfig) {
            Stop-PSFFunction -Message "Error: AdditionalEnvironmentDetails.xml not configured at $AgentShareLocation\scripts\D365FOLBDAdmin" -EnableException $true -FunctionName $_
        }
        [xml]$EnvironmentAdditionalConfigXML = get-content $EnvironmentAdditionalConfig.FullName

        ##checking WebURLS
        $EnvironmentAdditionalConfigXML.D365LBDEnvironment.Dependencies.CustomWebURLDependencies.CustomWebURL | ForEach-Object -Process { 
            $Note = $_.Note
            if ($_.Type.'#text'.Trim() -eq 'Basic') {
                ##Basic WebURL Start
                $results = Invoke-WebRequest -Uri $_.uri -UseBasicParsing
                if ($results.statusCode -eq 200 -or $results.statusCode -eq 203 -or $results.statusCode -eq 204 ) {
                    $Output = New-Object -TypeName PSObject -Property `
                    @{'Source'      = $env:COMPUTERNAME ;
                        'Name'      = $_.uri ;
                        'State'     = "Operational";
                        'ExtraInfo' = $results.Statuscode
                        'Group'     = 'Web Service/Page Web Basic'
                    }
                    $OutputList += $Output
                }
                else {
                    $Output += New-Object -TypeName PSObject -Property `
                    @{'Source'      = $env:COMPUTERNAME ;
                        'Name'      = $_.uri ;
                        'State'     = "Down";
                        'ExtraInfo' = $results.Statuscode
                        'Group'     = 'Web Service/Page Web Basic'
                    }
                }
            }
            else {
                ##Advanced Weburl start
                $childnodes = $($_.AdvancedCustomSuccessResponse | Select-Object childnodes).childnodes
                $properties = $childnodes | Get-Member -MemberType Property
                $propertiestocheck = $properties.Name
                [int]$countofproperties = $propertiestocheck.count
                $results = Invoke-RestMethod -Uri $_.uri -UseBasicParsing
                if ($countofproperties -eq 0 -or $countofproperties -eq 1 ) {
                    ##only one or 0 child items start
                    $diff = Compare-Object $results -DifferenceObject $($childnodes.'#text'.Trim())
                    if ($diff) {
                        Write-PSFMessage -message "Found differences $diff" -Level VeryVerbose
                        $Output = New-Object -TypeName PSObject -Property `
                        @{'Source'      = $env:COMPUTERNAME ;
                            'Name'      = $_.uri ;
                            'State'     = "Down";
                            'ExtraInfo' = $Note
                            'Group'     = 'Web Service/Page Web Advanced'
                        }
                        $OutputList += $Output
                    }
                    else {
                        ##no differences found so success
                        $Output = New-Object -TypeName PSObject -Property `
                        @{'Source'      = $env:COMPUTERNAME ;
                            'Name'      = $_.uri ;
                            'State'     = "Operational";
                            'ExtraInfo' = $Note
                            'Group'     = 'Web Service/Page Web Advanced'
                        }
                        $OutputList += $Output
                    }  ##only one or 0 child items end
                }##multiple items to check start
                else {
                    foreach ($property in $propertiestocheck) {
                        $diff = compare-object $results.data.$property -DifferenceObject $childnodes.$property.trim()
                        if ($diff) {
                            Write-PSFMessage -message "Found differences $diff" -Level VeryVerbose
                            $Output = New-Object -TypeName PSObject -Property `
                            @{'Source'      = $env:COMPUTERNAME ;
                                'Name'      = $_.uri ;
                                'State'     = "Down";
                                'ExtraInfo' = $results.Statuscode
                                'Group'     = 'Web Service/Page Web Advanced'
                            }
                            $OutputList += $Output
                        }
                        else {
                            $Output = New-Object -TypeName PSObject -Property `
                            @{'Source'      = $env:COMPUTERNAME ;
                                'Name'      = $_.uri ;
                                'State'     = "Operational";
                                'ExtraInfo' = $results.Statuscode;
                                'Group'     = 'Web Service/Page Web Advanced'
                            }
                            $OutputList += $Output
                        }
                    }
                } 
            } ## Advanced Weburl End
        }##End of All Custom WebURL
        if ($WebsiteChecksOnly) {
            Write-PSFMessage -Level VeryVerbose -Message "Only Checking Websites"
        }
        else {
            Write-PSFMessage -Level VeryVerbose -Message "Checking for server dependencies"
            $servicestovalidate = $EnvironmentAdditionalConfigXML.D365LBDEnvironment.Dependencies.ServerDependencies.Dependency | Where-Object { $_.Type.'#text'.Trim() -eq "service" }
            foreach ($servicetovalidate in $servicestovalidate) {
                ##Services Start
                if ($servicestovalidate.locationType.'#text'.Trim() -eq 'AXSF') {
                    foreach ($AXSfServerName in $Config.AXSFServerNames) {
                        $servicetovalidateName = $servicetovalidate.Name
                        $results = Invoke-Command -ComputerName $AXSfServerName -ScriptBlock { Get-service $Using:servicetovalidateName } 
                        if ($results.Status -eq "Running") {
                            $results | ForEach-Object -Process { `
                                    $Output = New-Object -TypeName PSObject -Property `
                                @{'Source'      = $AXSfServerName ;
                                    'Name'      = "$servicetovalidateName"; 
                                    'State'     = "Operational";
                                    'ExtraInfo' = $_.StartType;
                                    'Group'     = 'AXSFService'
                                }
                                $OutputList += $Output
                            }
                        } ##Operational start
                        else {
                            $results | ForEach-Object -Process { `
                                    $Output = New-Object -TypeName PSObject -Property `
                                @{'Source'      = $AXSfServerName ;
                                    'Name'      = "$servicetovalidateName"; 
                                    'State'     = "Down";
                                    'ExtraInfo' = $_.StartType;
                                    'Group'     = 'AXSFService'
                                }
                                $OutputList += $Output
                            }
                        }##Failure end
                    }
                }
                if ($servicestovalidate.locationType.'#text'.Trim() -eq 'SSRS') {
                    foreach ($SSRSClusterServerName in $Config.SSRSClusterServerNames) {
                        $results = Invoke-Command -ComputerName $SSRSClusterServerName -ScriptBlock { Get-service $Using:servicetovalidateName } 
                        if ($results.Status -eq "Running") {
                            $results | ForEach-Object -Process { `
                                    $Output = New-Object -TypeName PSObject -Property `
                                @{'Source'      = $SSRSClusterServerName ;
                                    'Name'      = "$servicetovalidateName"; 
                                    'State'     = "Operational";
                                    'ExtraInfo' = $_.StartType;
                                    'Group'     = 'SSRSService'
                                }
                                $OutputList += $Output
                            }
                        } ##Operational start
                        else {
                            $results | ForEach-Object -Process { `
                                    $Output = New-Object -TypeName PSObject -Property `
                                @{'Source'      = $SSRSClusterServerName ;
                                    'Name'      = "$servicetovalidateName"; 
                                    'State'     = "Down";
                                    'ExtraInfo' = $_.StartType;
                                    'Group'     = 'SSRSService'
                                }
                                $OutputList += $Output
                            }
                        }##Failure end
                    }
                }
                if ($servicestovalidate.locationType.'#text'.Trim() -eq 'SQLDB') {
                    foreach ($DatabaseClusterServerName in $config.DatabaseClusterServerNames) {
                        $results = Invoke-Command -ComputerName $DatabaseClusterServerName -ScriptBlock { Get-service $Using:servicetovalidateName } 
                        if ($results.Status -eq "Running") {
                            $results | ForEach-Object -Process { `
                                    $Output = New-Object -TypeName PSObject -Property `
                                @{'Source'      = $DatabaseClusterServerName ;
                                    'Name'      = "$servicetovalidateName"; 
                                    'State'     = "Operational";
                                    'ExtraInfo' = $_.StartType;
                                    'Group'     = 'SQLDBService'
                                }
                                $OutputList += $Output
                            }
                        } ##Operational start
                        else {
                            $results | ForEach-Object -Process { `
                                    $Output = New-Object -TypeName PSObject -Property `
                                @{'Source'      = $DatabaseClusterServerName ;
                                    'Name'      = "$servicetovalidateName"; 
                                    'State'     = "Down";
                                    'ExtraInfo' = $_.StartType;
                                    'Group'     = 'SQLDBService'
                                }
                                $OutputList += $Output
                            }
                        }##Failure end
                    }
                }
                if ($servicestovalidate.locationType.'#text'.Trim() -eq 'ManagementReporter') {
                    foreach ($ManagementReporterServer in $ManagementReporterServers) {
                        $results = Invoke-Command -ComputerName $ManagementReporterServer -ScriptBlock { Get-service $Using:servicetovalidateName } 
                        if ($results.Status -eq "Running") {
                            $results | ForEach-Object -Process { `
                                    $Output = New-Object -TypeName PSObject -Property `
                                @{'Source'      = $ManagementReporterServer ;
                                    'Name'      = "$servicetovalidateName"; 
                                    'State'     = "Operational";
                                    'ExtraInfo' = $_.StartType;
                                    'Group'     = 'ManagementReporterService'
                                }
                                $OutputList += $Output
                            }
                        } ##Operational start
                        else {
                            $results | ForEach-Object -Process { `
                                    $Output = New-Object -TypeName PSObject -Property `
                                @{'Source'      = $ManagementReporterServer ;
                                    'Name'      = "$servicetovalidateName"; 
                                    'State'     = "Down";
                                    'ExtraInfo' = $_.StartType;
                                    'Group'     = 'ManagementReporterService'
                                }
                                $OutputList += $Output
                            }
                        }##Failure end
                    }
                }
                if ($servicestovalidate.locationType.'#text'.Trim() -eq 'All') {
                    foreach ($AppServer in $Config.AllAppServerList) {
                        $results = Invoke-Command -ComputerName $AppServer -ScriptBlock { Get-service $Using:servicetovalidateName } 
                        if ($results.Status -eq "Running") {
                            $results | ForEach-Object -Process { `
                                    $Output = New-Object -TypeName PSObject -Property `
                                @{'Source'      = $AppServer ;
                                    'Name'      = "$servicetovalidateName"; 
                                    'State'     = "Operational";
                                    'ExtraInfo' = $_.StartType;
                                    'Group'     = 'AllServersService'
                                }
                                $OutputList += $Output
                            }
                        } ##Operational start
                        else {
                            $results | ForEach-Object -Process { `
                                    $Output = New-Object -TypeName PSObject -Property `
                                @{'Source'      = $AppServer ;
                                    'Name'      = "$servicetovalidateName"; 
                                    'State'     = "Down";
                                    'ExtraInfo' = $_.StartType;
                                    'Group'     = 'AllServersService'
                                }
                                $OutputList += $Output
                            }
                        }##Failure end
                    }
                }
            }##Services End

            $processestovalidate = $EnvironmentAdditionalConfigXML.D365LBDEnvironment.Dependencies.ServerDependencies.Dependency | Where-Object { $_.Type.'#text'.Trim() -eq "process" }
            ##Process
            foreach ($processtovalidate in $processestovalidate) {
                $ProcessName = $processtovalidate.name
                if ($processtovalidate.locationType.'#text'.Trim() -eq 'AXSF') {
                    foreach ($AXSfServerName in $Config.AXSFServerNames) {
                        $results = Invoke-Command -ComputerName $AXSfServerName -ScriptBlock { Get-process | where-object { $_.Name -eq $Using:ProcessName } | Select-Object -First 1 } 
                        if ($results) {
                            $Output = New-Object -TypeName PSObject -Property `
                            @{'Source'      = $AXSfServerName ;
                                'Name'      = "$ProcessName"; 
                                'State'     = "Operational";
                                'ExtraInfo' = $_.StartType;
                                'Group'     = 'AXServerProcess'
                            }
                        }
                        else {
                            $Output = New-Object -TypeName PSObject -Property `
                            @{'Source'      = $AXSfServerName ;
                                'Name'      = "$ProcessName"; 
                                'State'     = "Down";
                                'ExtraInfo' = $_.StartType;
                                'Group'     = 'AXServerProcess'
                            }
                        }
                        $OutputList += $Output
                    }
                }
            
                if ($processtovalidate.locationType.'#text'.Trim() -eq 'SSRS') {
                    foreach ($SSRSClusterServerName in $Config.SSRSClusterServerNames) {
                        $results = Invoke-Command -ComputerName $SSRSClusterServerName -ScriptBlock { Get-process | where-object { $_.Name -eq $Using:ProcessName }  | Select-Object -First 1 } 
                        if ($results) {
                            $Output = New-Object -TypeName PSObject -Property `
                            @{'Source'      = $SSRSClusterServerName ;
                                'Name'      = "$ProcessName"; 
                                'State'     = "Operational";
                                'ExtraInfo' = $_.StartType;
                                'Group'     = 'SSRSServerProcess'
                            }
                        }
                        else {
                            $Output = New-Object -TypeName PSObject -Property `
                            @{'Source'      = $SSRSClusterServerName ;
                                'Name'      = "$ProcessName"; 
                                'State'     = "Down";
                                'ExtraInfo' = $_.StartType;
                                'Group'     = '$SSRSServerProcess'
                            }
                        }
                        $OutputList += $Output
                    }
                }
                if ($processtovalidate.locationType.'#text'.Trim() -eq 'SQLDB') {
                    foreach ($DatabaseClusterServerName in $config.DatabaseClusterServerNames) {
                        $results = Invoke-Command -ComputerName $DatabaseClusterServerName -ScriptBlock { Get-process | where-object { $_.Name -eq $Using:ProcessName } | Select-Object -First 1 } 
                        if ($results) {
                            $Output = New-Object -TypeName PSObject -Property `
                            @{'Source'      = $DatabaseClusterServerName ;
                                'Name'      = "$ProcessName"; 
                                'State'     = "Operational";
                                'ExtraInfo' = $_.StartType;
                                'Group'     = 'DatabaseClusterServerProcess'
                            }
                        }
                        else {
                            $Output = New-Object -TypeName PSObject -Property `
                            @{'Source'      = $DatabaseClusterServerName ;
                                'Name'      = "$ProcessName"; 
                                'State'     = "Down";
                                'ExtraInfo' = $_.StartType;
                                'Group'     = 'DatabaseClusterServerProcess'
                            }
                        }
                        $OutputList += $Output
                    }
                }
            
                if ($processtovalidate.locationType.'#text'.Trim() -eq 'ManagementReporter') {
                    foreach ($ManagementReporterServer in $ManagementReporterServers) {
                        $results = Invoke-Command -ComputerName $ManagementReporterServer -ScriptBlock { Get-process | where-object { $_.Name -eq $Using:ProcessName } | Select-Object -First 1 } 
                        if ($results) {
                            $Output = New-Object -TypeName PSObject -Property `
                            @{'Source'      = $ManagementReporterServer ;
                                'Name'      = "$ProcessName"; 
                                'State'     = "Operational";
                                'ExtraInfo' = $_.StartType;
                                'Group'     = 'ManagementReporterProcess'
                            }
                        }
                        else {
                            $Output = New-Object -TypeName PSObject -Property `
                            @{'Source'      = $ManagementReporterServer ;
                                'Name'      = "$ProcessName"; 
                                'State'     = "Down";
                                'ExtraInfo' = $_.StartType;
                                'Group'     = 'ManagementReporterProcess'
                            }
                        }
                        $OutputList += $Output
                    } 
                }
                if ($processtovalidate.locationType.'#text'.Trim() -eq 'All') {
                    foreach ($AppServer in $Config.AllAppServerList) {
                        $results = Invoke-Command -ComputerName $AppServer -ScriptBlock { Get-process | where-object { $_.Name -eq $Using:ProcessName }  | Select-Object -First 1 } 
                        if ($results) {
                            $Output = New-Object -TypeName PSObject -Property `
                            @{'Source'      = $AppServer ;
                                'Name'      = "$ProcessName"; 
                                'State'     = "Operational";
                                'ExtraInfo' = $_.StartType;
                                'Group'     = 'AllProcess'
                            }
                        }
                        else {
                            $Output = New-Object -TypeName PSObject -Property `
                            @{'Source'      = $AppServer ;
                                'Name'      = "$ProcessName"; 
                                'State'     = "Down";
                                'ExtraInfo' = $_.StartType;
                                'Group'     = 'AllProcess'
                            }
                        }
                        $OutputList += $Output
                    }
                }
            }
            ##Database
        }
        $FoundIssue = $false
        foreach ($scanneditem in $Output) {
            if ($scanneditem.State -eq "Down") {
                Write-PSFMessage -Message "Found an item down: $Scanneditem" -Level VeryVerbose
                $FoundIssue = $True
                if ($SendAlertIfIssue) {
                    $EnvironmentOwnerEmail = $EnvironmentAdditionalConfigXML.D365LBDEnvironment.EnvironmentAdditionalConfig.EnvironmentOwnerEmail
                    if ($SMTPServer) {
                        Write-PSFMessage -Level VeryVerbose -Message "Sending email to $EnvironmentOwnerEmail using $SMTPServer"
                        Send-MailMessage -to "$EnvironmentOwnerEmail" -Body "$scanneditem is down for $($Config.LCSEnvironmentName) " -Verbose -SmtpServer "$SMTPServer" -Subject "D365 Found issue with $($Config.LCSEnvironmentName)"
                    }
                    else {
                        Write-PSFMessage -Level VeryVerbose -Message "WARNING: Configure SMTP Server to send emails"

                    }
                    if ($MSTeamsURI) {
                        Send-D365LBDUpdateMSTeams -messageType "StatusReport" -MSTeamsURI "$MSTeamsURI"
                    }
                    else {
                        $MSTEAMSURLS = $EnvironmentAdditionalConfigXML.D365LBDEnvironment.Communication.Webhooks.Webhook | Where-Object { $_.type.'#text'.trim() -eq "MSTEAMS" } | select ChannelWebHookURL
                        foreach ($MSTEAMSURL in $MSTEAMSURLS) {
                            Send-D365LBDUpdateMSTeams -messageType "StatusReport" -MSTeamsURI "htts://fakemicrosoft.office.com/webhookb2/98984684987156465-4654/incominginwebhook/ea5s6d4sa6" -config $Config
                        }
                    }
                }
            }
        }
        [PSCustomObject] $OutputList
    }
    END {
        if ($SFModuleSession) {
            Remove-PSSession -Session $SFModuleSession  
        }
    }
}