DynamicsNetworkTest.ps1

<#PSScriptInfo
 
.VERSION 1.1
 
.GUID 3835ff6c-24eb-49b8-8a11-8b8350402fb5
 
.DESCRIPTION Use this script to test basic network connectivity to Dynamics 365 online endpoints.
 
.AUTHOR Aaron Guilmette
 
.COMPANYNAME Microsoft
 
.COPYRIGHT 2021
 
.TAGS User hold policies
 
.LICENSEURI
 
.PROJECTURI https://www.undocumented-features.com/2018/04/20/dynamics-365-network-test-tool/
 
.ICONURI
 
.EXTERNALMODULEDEPENDENCIES
 
.REQUIREDSCRIPTS
 
.EXTERNALSCRIPTDEPENDENCIES
 
.RELEASENOTES
#>


<#
.SYNOPSIS
Test basic connectivity and name resolution for Dynamics 365.
 
.PARAMETER DebugLogging
Enable debug error logging to log file.
 
.PARAMETER Logfile
Self-explanatory.
 
.PARAMETER OnlineEndPoints
Use this parameter to conduct communication tests against online endpoints.
 
.PARAMETER OnlineEndPointTarget
Use this optional parameter to select region.
Valid values:
- NA (North America)
- SA (South America)
- EMEA (Europe, Middle East, Africa)
- APAC (Asia-Pacific)
- JP (Japan)
- IN (India)
- CA (Canada)
- USGOV (United States Government)
- UK (United Kingdom)
- Oceania (Oceania-area)
 
.PARAMETER SystemConfiguration
Report on system configuration items, including TLS registry entries and proxy
configurations.
 
.EXAMPLE
.\DynamicsNetworkTest.ps1
Runs all tests and writes to default log file location (YYYY-MM-DD_DynamicsConnectivity.txt)
 
.EXAMPLE
.\DynamicsNetworkTest.ps1 -SystemConfiguration
Runs System Configuration test and writes to default log file location (YYYY-MM-DD_DynamicsConnectivity.txt).
 
.EXAMPLE
.\DynamicsNetworkTest.ps1 -OnlineEndPointTarget USGOV -DebugLogging
Runs OnlineEndPoints test using the U.S. Government online endpoints list
and writes to default log file location (YYYY-MM-DD_DynamicsConnectivity.txt)
with debug logging enabled.
 
.LINK
https://www.undocumented-features.com/2018/04/20/dynamics-365-network-test-tool/
 
.NOTES
- 2021-04-07 Updated endpoints with latest from https://support.microsoft.com/en-us/help/2655102/internet-accessible-urls-required-for-connectivity-to-microsoft-dynami
                Updated for PowerShell Gallery.
- 2018-06-27 Updated endpoints for crmdynint.com and passport.net.
                Updated system configuration check.
                Added browser detection.
- 2018-04-20 Updated system configuration check.
- 2018-04-20 Updated online endpoint targets for additional regions.
- 2018-04-20 Initial release.
#>


param (
    [switch]$DebugLogging,
    [string]$Logfile = (Get-Date -Format yyyy-MM-dd) + "_DynamicsConnectivity.txt",
    [switch]$OnlineEndPoints,
    [ValidateSet("NA", "USGOV","SA","EMEA","APAC","JP","IN","CA","Oceania","UK")]
    [string]$OnlineEndPointTarget = "NA",
    [switch]$SystemConfiguration
)

## Functions
# Logging function
function Write-Log([string[]]$Message, [string]$LogFile = $Script:LogFile, [switch]$ConsoleOutput, [ValidateSet("SUCCESS", "INFO", "WARN", "ERROR", "DEBUG")][string]$LogLevel)
{
    $Message = $Message + $Input
    If (!$LogLevel) { $LogLevel = "INFO" }
    switch ($LogLevel)
    {
        SUCCESS { $Color = "Green" }
        INFO { $Color = "White" }
        WARN { $Color = "Yellow" }
        ERROR { $Color = "Red" }
        DEBUG { $Color = "Gray" }
    }
    if ($Message -ne $null -and $Message.Length -gt 0)
    {
        $TimeStamp = [System.DateTime]::Now.ToString("yyyy-MM-dd HH:mm:ss")
        if ($LogFile -ne $null -and $LogFile -ne [System.String]::Empty)
        {
            Out-File -Append -FilePath $LogFile -InputObject "[$TimeStamp] [$LogLevel] $Message"
        }
        if ($ConsoleOutput -eq $true)
        {
            Write-Host "[$TimeStamp] [$LogLevel] :: $Message" -ForegroundColor $Color
        }
    }
}

# Test Online Networking Only
function OnlineEndPoints
{
    switch -regex ($OnlineEndPointTarget)
    {
        'NA'
        {
            Write-Log -LogFile $Logfile -LogLevel INFO -Message "Starting Online Endpoints tests (North America)."
            Write-Log -LogFile $Logfile -LogLevel INFO -Message "See https://support.microsoft.com/en-us/help/2655102/internet-accessible-urls-required-for-connectivity-to-microsoft-dynami"
            Write-Log -LogFile $Logfile -LogLevel INFO -Message "for more details on endpoints."
            # Test on port 80 to retrieve CRL
            $CRL = @(
                "http://crl.microsoft.com/pki/crl/products/microsoftrootcert.crl",
                "http://mscrl.microsoft.com/pki/mscorp/crl/msitwww2.crl",
                "http://ocsp.verisign.com",
                "http://ocsp.entrust.net")
            # Resolve DNS name to IP and attempt TCP connection on 443
            $RequiredResources = @(
                "login.microsoftonline-p.com",
                "login.live.com",
                "online.dynamics.com",
                "login.windows.net",
                "secure.aadcdn.microsoftonline-p.com",
                "mbs.microsoft.com",
                "go.microsoft.com",
                "urs.microsoft.com",
                "auth.gfx.ms",
                # "sc.imp.live.com", # URL does not have valid DNS
                "dynamicscrmna.accesscontrol.windows.net",
                "nexus.passport.com", # in place of passport.net, since can't find a valid endpoint
                "clientconfig.passport.net",
                "login.microsoftonline.com",
                "home.dynamics.com",
                "crmcdn.azureedge.net", # in place of *.azureedge.net
                "www.crmdynint.com",
                # "cloudredirectornamsec.cloudapp.net", # URL does not have valid DNS
                # "cloudredirectornam.cloudapp.net", # URL does not have valid DNS
                "mem.gfx.ms",
                "test.crm.dynamics.com" # used in place of *.crm2.dynamics.com
                "www.d365ccafpi.com"
                )
            # Resolve DNS and attempt to retrieve data from endpoint
            $RequiredResourcesEndpoints = @(
                "http://clientconfig.passport.net/ppcrlconfig.bin",
                "https://disco.crm.dynamics.com/XRMServices/2011/Discovery.svc",
                "http://disco.crm.dynamics.com/XRMServices/2011/Discovery.svc",
                "https://disco.crm9.dynamics.com/XRMServices/2011/Discovery.svc"
                "https://login.windows.net",
                "https://secure.aadcdn.microsoftonline-p.com/ests/2.1.5975.9/content/cdnbundles/jquery.1.11.min.js")
            # Use Optional resources for addtional services.
            $OptionalResources = @()
            
            # Optional resources endpoints. Specify as exact URI to download/test, such as
            # https://endpoint.domain.com/uri.svc
            $OptionalResourcesEndpoints = @()
            
            # Use the AdditionalResources array to specify items that need a port test on a port other
            # than 80 or 443. Specify as endpoint:port
            $AdditionalResources = @()
        }
        
        'USGOV'
        {
            Write-Log -LogFile $Logfile -LogLevel INFO -Message "Starting Online Endpoints tests (US Government)."
            Write-Log -LogFile $Logfile -LogLevel INFO -Message "See https://support.microsoft.com/en-us/help/2655102/internet-accessible-urls-required-for-connectivity-to-microsoft-dynami"
            Write-Log -LogFile $Logfile -LogLevel INFO -Message "for more details on endpoints."
            # Test on port 80 to retrieve CRL
            $CRL = @(
                "http://crl.microsoft.com/pki/crl/products/microsoftrootcert.crl",
                "http://mscrl.microsoft.com/pki/mscorp/crl/msitwww2.crl",
                "http://ocsp.verisign.com",
                "http://ocsp.entrust.net")
            # Resolve DNS name to IP and attempt TCP connection on 443
            $RequiredResources = @(
                "login.microsoftonline-p.com",
                "login.live.com",
                "online.dynamics.com",
                "login.windows.net",
                "secure.aadcdn.microsoftonline-p.com",
                "mbs.microsoft.com",
                "go.microsoft.com",
                "urs.microsoft.com",
                "auth.gfx.ms",
                #"sc.imp.live.com", # url does not have valid DNS
                #"dynamicscrmgcc.accesscontrol.usgovcloudapi.net", # no valid DNS
                "nexus.passport.com", # in place of passport.net, since can't find a valid endpoint
                "clientconfig.passport.net",
                "login.microsoftonline.com",
                "home.dynamics.com",
                "crmcdn.azureedge.net", # in place of *.azureedge.net
                # "cloudredirectornamsec.cloudapp.net", # URL does not have valid DNS
                # "cloudredirectornam.cloudapp.net", # URL does not have valid DNS
                "mem.gfx.ms",
                "test.crm9.dynamics.com" # used in place of *.crm2.dynamics.com
                "www.d365ccafpi.com",
                "www.crmdynint-gcc.com") # Support site incorrectly lists this as www.www.crmdynint-gcc.com
            # Resolve DNS and attempt to retrieve data from endpoint
            $RequiredResourcesEndpoints = @(
                "http://clientconfig.passport.net/ppcrlconfig.bin",
                "https://disco.crm9.dynamics.com/XRMServices/2011/Discovery.svc"
                "https://login.windows.net",
                "https://secure.aadcdn.microsoftonline-p.com/ests/2.1.5975.9/content/cdnbundles/jquery.1.11.min.js")
            # Use Optional resources for addtional services.
            $OptionalResources = @()

            # Optional resources endpoints. Specify as exact URI to download/test, such as
            # https://endpoint.domain.com/uri.svc
            $OptionalResourcesEndpoints = @()
            
            # Use the AdditionalResources array to specify items that need a port test on a port other
            # than 80 or 443. Specify as endpoint:port
            $AdditionalResources = @()
        }
        
        'SA'
        {
            Write-Log -LogFile $Logfile -LogLevel INFO -Message "Starting Online Endpoints tests (South America)."
            Write-Log -LogFile $Logfile -LogLevel INFO -Message "See https://support.microsoft.com/en-us/help/2655102/internet-accessible-urls-required-for-connectivity-to-microsoft-dynami"
            Write-Log -LogFile $Logfile -LogLevel INFO -Message "for more details on endpoints."
            # Test on port 80 to retrieve CRL
            $CRL = @(
                "http://crl.microsoft.com/pki/crl/products/microsoftrootcert.crl",
                "http://mscrl.microsoft.com/pki/mscorp/crl/msitwww2.crl",
                "http://ocsp.verisign.com",
                "http://ocsp.entrust.net")
            # Resolve DNS name to IP and attempt TCP connection on 443
            $RequiredResources = @(
                "login.microsoftonline-p.com",
                "login.live.com",
                "online.dynamics.com",
                "login.windows.net",
                "secure.aadcdn.microsoftonline-p.com",
                "mbs.microsoft.com",
                "go.microsoft.com",
                "urs.microsoft.com",
                "auth.gfx.ms",
                # "sc.imp.live.com", # URL does not have valid DNS
                "dynamicscrmsam.accesscontrol.windows.net",
                "nexus.passport.com", # in place of passport.net, since can't find a valid endpoint
                "clientconfig.passport.net",
                "login.microsoftonline.com",
                "home.dynamics.com",
                "crmcdn.azureedge.net", # in place of *.azureedge.net
                "www.crmdynint.com", # parked domain
                # "cloudredirectorsamsec.cloudapp.net", # URL does not have valid DNS
                # "cloudredirectorsam.cloudapp.net", # URL does not have valid DNS
                "mem.gfx.ms",
                "test.crm2.dynamics.com" # used in place of *.crm2.dynamics.com
                "www.d365ccafpi.com"
            )
            # Resolve DNS and attempt to retrieve data from endpoint
            $RequiredResourcesEndpoints = @(
                "http://clientconfig.passport.net/ppcrlconfig.bin",
                "http://disco.crm2.dynamics.com/XRMServices/2011/Discovery.svc",
                "https://disco.crm2.dynamics.com/XRMServices/2011/Discovery.svc"
                "https://login.windows.net",
                "https://secure.aadcdn.microsoftonline-p.com/ests/2.1.5975.9/content/cdnbundles/jquery.1.11.min.js")
            # Use Optional resources for addtional services.
            $OptionalResources = @()
            
            # Optional resources endpoints. Specify as exact URI to download/test, such as
            # https://endpoint.domain.com/uri.svc
            $OptionalResourcesEndpoints = @()
            
            # Use the AdditionalResources array to specify items that need a port test on a port other
            # than 80 or 443. Specify as endpoint:port
            $AdditionalResources = @()
        }
        
        'EMEA'
        {
            Write-Log -LogFile $Logfile -LogLevel INFO -Message "Starting Online Endpoints tests (Europe, Middle East, and Africa)."
            Write-Log -LogFile $Logfile -LogLevel INFO -Message "See https://support.microsoft.com/en-us/help/2655102/internet-accessible-urls-required-for-connectivity-to-microsoft-dynami"
            Write-Log -LogFile $Logfile -LogLevel INFO -Message "for more details on endpoints."
            # Test on port 80 to retrieve CRL
            $CRL = @(
                "http://crl.microsoft.com/pki/crl/products/microsoftrootcert.crl",
                "http://mscrl.microsoft.com/pki/mscorp/crl/msitwww2.crl",
                "http://ocsp.verisign.com",
                "http://ocsp.entrust.net")
            # Resolve DNS name to IP and attempt TCP connection on 443
            $RequiredResources = @(
                "login.microsoftonline-p.com",
                "login.live.com",
                "online.dynamics.com",
                "login.windows.net",
                "secure.aadcdn.microsoftonline-p.com",
                "mbs.microsoft.com",
                "go.microsoft.com",
                "urs.microsoft.com",
                "auth.gfx.ms",
                # "sc.imp.live.com", # URL does not have valid DNS
                "dynamicscrmemea.accesscontrol.windows.net",
                "nexus.passport.com", # in place of passport.net, since can't find a valid endpoint
                "clientconfig.passport.net",
                "login.microsoftonline.com",
                "home.dynamics.com",
                "crmcdn.azureedge.net", # in place of *.azureedge.net
                "www.crmdynint.com",
                # "cloudredirectoreursec.cloudapp.net", # URL does not have valid DNS
                # "cloudredirectoreur.cloudapp.net", # URL does not have valid DNS
                "mem.gfx.ms",
                "test.crm4.dynamics.com" # used in place of *.crm4.dynamics.com
                "www.d365ccafpi.com")
            # Resolve DNS and attempt to retrieve data from endpoint
            $RequiredResourcesEndpoints = @(
                "http://clientconfig.passport.net/ppcrlconfig.bin",
                "http://disco.crm4.dynamics.com/XRMServices/2011/Discovery.svc",
                "https://disco.crm4.dynamics.com/XRMServices/2011/Discovery.svc"
                "https://login.windows.net",
                "https://secure.aadcdn.microsoftonline-p.com/ests/2.1.5975.9/content/cdnbundles/jquery.1.11.min.js")
            # Use Optional resources for addtional services.
            $OptionalResources = @()
            
            # Optional resources endpoints. Specify as exact URI to download/test, such as
            # https://endpoint.domain.com/uri.svc
            $OptionalResourcesEndpoints = @()
            
            # Use the AdditionalResources array to specify items that need a port test on a port other
            # than 80 or 443. Specify as endpoint:port
            $AdditionalResources = @()
        }
        
        'APAC'
        {
            Write-Log -LogFile $Logfile -LogLevel INFO -Message "Starting Online Endpoints tests (Europe, Middle East, and Africa)."
            Write-Log -LogFile $Logfile -LogLevel INFO -Message "See https://support.microsoft.com/en-us/help/2655102/internet-accessible-urls-required-for-connectivity-to-microsoft-dynami"
            Write-Log -LogFile $Logfile -LogLevel INFO -Message "for more details on endpoints."
            # Test on port 80 to retrieve CRL
            $CRL = @(
                "http://crl.microsoft.com/pki/crl/products/microsoftrootcert.crl",
                "http://mscrl.microsoft.com/pki/mscorp/crl/msitwww2.crl",
                "http://ocsp.verisign.com",
                "http://ocsp.entrust.net")
            # Resolve DNS name to IP and attempt TCP connection on 443
            $RequiredResources = @(
                "login.microsoftonline-p.com",
                "login.live.com",
                "online.dynamics.com",
                "login.windows.net",
                "secure.aadcdn.microsoftonline-p.com",
                "mbs.microsoft.com",
                "go.microsoft.com",
                "urs.microsoft.com",
                "auth.gfx.ms",
                # "sc.imp.live.com", # URL does not have valid DNS
                "dynamicscrmapac.accesscontrol.windows.net",
                "nexus.passport.com", # in place of passport.net, since can't find a valid endpoint
                "clientconfig.passport.net",
                "login.microsoftonline.com",
                "home.dynamics.com",
                "crmcdn.azureedge.net", # in place of *.azureedge.net
                "www.crmdynint.com",
                # "cloudredirectorapjsec.cloudapp.net", # URL does not have valid DNS
                # "cloudredirectorapj.cloudapp.net", # URL does not have valid DNS
                "mem.gfx.ms",
                "test.crm5.dynamics.com" # used in place of *.crm2.dynamics.com
                "www.d365ccafpi.com")
            # Resolve DNS and attempt to retrieve data from endpoint
            $RequiredResourcesEndpoints = @(
                "http://clientconfig.passport.net/ppcrlconfig.bin",
                "http://disco.crm5.dynamics.com/XRMServices/2011/Discovery.svc",
                "https://disco.crm5.dynamics.com/XRMServices/2011/Discovery.svc"
                "https://login.windows.net",
                "https://secure.aadcdn.microsoftonline-p.com/ests/2.1.5975.9/content/cdnbundles/jquery.1.11.min.js")
            # Use Optional resources for addtional services.
            $OptionalResources = @()
            
            # Optional resources endpoints. Specify as exact URI to download/test, such as
            # https://endpoint.domain.com/uri.svc
            $OptionalResourcesEndpoints = @()
            
            # Use the AdditionalResources array to specify items that need a port test on a port other
            # than 80 or 443. Specify as endpoint:port
            $AdditionalResources = @()
        }
        
        'JP'
        {
            Write-Log -LogFile $Logfile -LogLevel INFO -Message "Starting Online Endpoints tests (Europe, Middle East, and Africa)."
            Write-Log -LogFile $Logfile -LogLevel INFO -Message "See https://support.microsoft.com/en-us/help/2655102/internet-accessible-urls-required-for-connectivity-to-microsoft-dynami"
            Write-Log -LogFile $Logfile -LogLevel INFO -Message "for more details on endpoints."
            # Test on port 80 to retrieve CRL
            $CRL = @(
                "http://crl.microsoft.com/pki/crl/products/microsoftrootcert.crl",
                "http://mscrl.microsoft.com/pki/mscorp/crl/msitwww2.crl",
                "http://ocsp.verisign.com",
                "http://ocsp.entrust.net")
            # Resolve DNS name to IP and attempt TCP connection on 443
            $RequiredResources = @(
                "login.microsoftonline-p.com",
                "login.live.com",
                "online.dynamics.com",
                "login.windows.net",
                "secure.aadcdn.microsoftonline-p.com",
                "mbs.microsoft.com",
                "go.microsoft.com",
                "urs.microsoft.com",
                "auth.gfx.ms",
                # "sc.imp.live.com", # URL does not have valid DNS
                "dynamicscrmjpn.accesscontrol.windows.net",
                "nexus.passport.com", # in place of passport.net, since can't find a valid endpoint
                "clientconfig.passport.net",
                "login.microsoftonline.com",
                "home.dynamics.com",
                "crmcdn.azureedge.net", # in place of *.azureedge.net
                "www.crmdynint.com",
                # "cloudredirectorjpnsec.cloudapp.net", # URL does not have valid DNS
                # "cloudredirectorjpn.cloudapp.net", # URL does not have valid DNS
                "mem.gfx.ms",
                "test.crm7.dynamics.com" # used in place of *.crm2.dynamics.com
                "www.d365ccafpi.com")
            # Resolve DNS and attempt to retrieve data from endpoint
            $RequiredResourcesEndpoints = @(
                "http://clientconfig.passport.net/ppcrlconfig.bin",
                "http://disco.crm7.dynamics.com/XRMServices/2011/Discovery.svc",
                "https://disco.crm7.dynamics.com/XRMServices/2011/Discovery.svc"
                "https://login.windows.net",
                "https://secure.aadcdn.microsoftonline-p.com/ests/2.1.5975.9/content/cdnbundles/jquery.1.11.min.js")
            # Use Optional resources for addtional services.
            $OptionalResources = @()
            
            # Optional resources endpoints. Specify as exact URI to download/test, such as
            # https://endpoint.domain.com/uri.svc
            $OptionalResourcesEndpoints = @()
            
            # Use the AdditionalResources array to specify items that need a port test on a port other
            # than 80 or 443. Specify as endpoint:port
            $AdditionalResources = @()
        }
        
        'IN'
        {
            Write-Log -LogFile $Logfile -LogLevel INFO -Message "Starting Online Endpoints tests (Europe, Middle East, and Africa)."
            Write-Log -LogFile $Logfile -LogLevel INFO -Message "See https://support.microsoft.com/en-us/help/2655102/internet-accessible-urls-required-for-connectivity-to-microsoft-dynami"
            Write-Log -LogFile $Logfile -LogLevel INFO -Message "for more details on endpoints."
            # Test on port 80 to retrieve CRL
            $CRL = @(
                "http://crl.microsoft.com/pki/crl/products/microsoftrootcert.crl",
                "http://mscrl.microsoft.com/pki/mscorp/crl/msitwww2.crl",
                "http://ocsp.verisign.com",
                "http://ocsp.entrust.net")
            # Resolve DNS name to IP and attempt TCP connection on 443
            $RequiredResources = @(
                "login.microsoftonline-p.com",
                "login.live.com",
                "online.dynamics.com",
                "login.windows.net",
                "secure.aadcdn.microsoftonline-p.com",
                "mbs.microsoft.com",
                "go.microsoft.com",
                "urs.microsoft.com",
                "auth.gfx.ms",
                # "sc.imp.live.com", # URL does not have valid DNS
                "dynamicscrmind.accesscontrol.windows.net",
                "nexus.passport.com", # in place of passport.net, since can't find a valid endpoint
                "clientconfig.passport.net",
                "login.microsoftonline.com",
                "home.dynamics.com",
                "crmcdn.azureedge.net", # in place of *.azureedge.net
                "www.crmdynint.com",
                # "cloudredirectorindsec.cloudapp.net", # URL does not have valid DNS
                # "cloudredirectorind.cloudapp.net", # URL does not have valid DNS
                "mem.gfx.ms",
                "test.crm8.dynamics.com" # used in place of *.crm2.dynamics.com
                "www.d365ccafpi.com")
            # Resolve DNS and attempt to retrieve data from endpoint
            $RequiredResourcesEndpoints = @(
                "http://clientconfig.passport.net/ppcrlconfig.bin",
                "http://disco.crm8.dynamics.com/XRMServices/2011/Discovery.svc",
                "https://disco.crm8.dynamics.com/XRMServices/2011/Discovery.svc"
                "https://login.windows.net",
                "https://secure.aadcdn.microsoftonline-p.com/ests/2.1.5975.9/content/cdnbundles/jquery.1.11.min.js")
            # Use Optional resources for addtional services.
            $OptionalResources = @()
            
            # Optional resources endpoints. Specify as exact URI to download/test, such as
            # https://endpoint.domain.com/uri.svc
            $OptionalResourcesEndpoints = @()
            
            # Use the AdditionalResources array to specify items that need a port test on a port other
            # than 80 or 443. Specify as endpoint:port
            $AdditionalResources = @()
        }
        
        'CA'
        {
            Write-Log -LogFile $Logfile -LogLevel INFO -Message "Starting Online Endpoints tests (Europe, Middle East, and Africa)."
            Write-Log -LogFile $Logfile -LogLevel INFO -Message "See https://support.microsoft.com/en-us/help/2655102/internet-accessible-urls-required-for-connectivity-to-microsoft-dynami"
            Write-Log -LogFile $Logfile -LogLevel INFO -Message "for more details on endpoints."
            # Test on port 80 to retrieve CRL
            $CRL = @(
                "http://crl.microsoft.com/pki/crl/products/microsoftrootcert.crl",
                "http://mscrl.microsoft.com/pki/mscorp/crl/msitwww2.crl",
                "http://ocsp.verisign.com",
                "http://ocsp.entrust.net")
            # Resolve DNS name to IP and attempt TCP connection on 443
            $RequiredResources = @(
                "login.microsoftonline-p.com",
                "login.live.com",
                "online.dynamics.com",
                "login.windows.net",
                "secure.aadcdn.microsoftonline-p.com",
                "mbs.microsoft.com",
                "go.microsoft.com",
                "urs.microsoft.com",
                "auth.gfx.ms",
                # "sc.imp.live.com", # URL does not have valid DNS
                "dynamicscrmcan.accesscontrol.windows.net",
                "nexus.passport.com", # in place of passport.net, since can't find a valid endpoint
                "clientconfig.passport.net",
                "login.microsoftonline.com",
                "home.dynamics.com",
                "crmcdn.azureedge.net", # in place of *.azureedge.net
                "www.crmdynint.com",
                # "cloudredirectorcansec.cloudapp.net", # URL does not have valid DNS
                # "cloudredirectorcan.cloudapp.net", # URL does not have valid DNS
                "mem.gfx.ms",
                "test.crm3.dynamics.com" # used in place of *.crm2.dynamics.com
                "www.d365ccafpi.com")
            # Resolve DNS and attempt to retrieve data from endpoint
            $RequiredResourcesEndpoints = @(
                "http://clientconfig.passport.net/ppcrlconfig.bin",
                "http://disco.crm3.dynamics.com/XRMServices/2011/Discovery.svc",
                "https://disco.crm3.dynamics.com/XRMServices/2011/Discovery.svc"
                "https://login.windows.net",
                "https://secure.aadcdn.microsoftonline-p.com/ests/2.1.5975.9/content/cdnbundles/jquery.1.11.min.js")
            # Use Optional resources for addtional services.
            $OptionalResources = @()
            
            # Optional resources endpoints. Specify as exact URI to download/test, such as
            # https://endpoint.domain.com/uri.svc
            $OptionalResourcesEndpoints = @()
            
            # Use the AdditionalResources array to specify items that need a port test on a port other
            # than 80 or 443. Specify as endpoint:port
            $AdditionalResources = @()
        }
        
        'Oceania'
        {
            Write-Log -LogFile $Logfile -LogLevel INFO -Message "Starting Online Endpoints tests (Europe, Middle East, and Africa)."
            Write-Log -LogFile $Logfile -LogLevel INFO -Message "See https://support.microsoft.com/en-us/help/2655102/internet-accessible-urls-required-for-connectivity-to-microsoft-dynami"
            Write-Log -LogFile $Logfile -LogLevel INFO -Message "for more details on endpoints."
            # Test on port 80 to retrieve CRL
            $CRL = @(
                "http://crl.microsoft.com/pki/crl/products/microsoftrootcert.crl",
                "http://mscrl.microsoft.com/pki/mscorp/crl/msitwww2.crl",
                "http://ocsp.verisign.com",
                "http://ocsp.entrust.net")
            # Resolve DNS name to IP and attempt TCP connection on 443
            $RequiredResources = @(
                "login.microsoftonline-p.com",
                "login.live.com",
                "online.dynamics.com",
                "login.windows.net",
                "secure.aadcdn.microsoftonline-p.com",
                "mbs.microsoft.com",
                "go.microsoft.com",
                "urs.microsoft.com",
                "auth.gfx.ms",
                # "sc.imp.live.com", # URL does not have valid DNS
                "dynamicscrmoce.accesscontrol.windows.net",
                "nexus.passport.com", # in place of passport.net, since can't find a valid endpoint
                "clientconfig.passport.net",
                "login.microsoftonline.com",
                "home.dynamics.com",
                "crmcdn.azureedge.net", # in place of *.azureedge.net
                "www.crmdynint.com",
                # "cloudredirectorocesec.cloudapp.net", # URL does not have valid DNS
                # "cloudredirectoroce.cloudapp.net", # URL does not have valid DNS
                "mem.gfx.ms",
                "test.crm6.dynamics.com" # used in place of *.crm2.dynamics.com
                "www.d365ccafpi.com")
            # Resolve DNS and attempt to retrieve data from endpoint
            $RequiredResourcesEndpoints = @(
                "http://clientconfig.passport.net/ppcrlconfig.bin",
                "http://disco.crm6.dynamics.com/XRMServices/2011/Discovery.svc",
                "https://disco.crm6.dynamics.com/XRMServices/2011/Discovery.svc"
                "https://login.windows.net",
                "https://secure.aadcdn.microsoftonline-p.com/ests/2.1.5975.9/content/cdnbundles/jquery.1.11.min.js")
            # Use Optional resources for addtional services.
            $OptionalResources = @()
            
            # Optional resources endpoints. Specify as exact URI to download/test, such as
            # https://endpoint.domain.com/uri.svc
            $OptionalResourcesEndpoints = @()
            
            # Use the AdditionalResources array to specify items that need a port test on a port other
            # than 80 or 443. Specify as endpoint:port
            $AdditionalResources = @()
        }
        
        'UK'
        {
            Write-Log -LogFile $Logfile -LogLevel INFO -Message "Starting Online Endpoints tests (Europe, Middle East, and Africa)."
            Write-Log -LogFile $Logfile -LogLevel INFO -Message "See https://support.microsoft.com/en-us/help/2655102/internet-accessible-urls-required-for-connectivity-to-microsoft-dynami"
            Write-Log -LogFile $Logfile -LogLevel INFO -Message "for more details on endpoints."
            # Test on port 80 to retrieve CRL
            $CRL = @(
                "http://crl.microsoft.com/pki/crl/products/microsoftrootcert.crl",
                "http://mscrl.microsoft.com/pki/mscorp/crl/msitwww2.crl",
                "http://ocsp.verisign.com",
                "http://ocsp.entrust.net")
            # Resolve DNS name to IP and attempt TCP connection on 443
            $RequiredResources = @(
                "login.microsoftonline-p.com",
                "login.live.com",
                "online.dynamics.com",
                "login.windows.net",
                "secure.aadcdn.microsoftonline-p.com",
                "mbs.microsoft.com",
                "go.microsoft.com",
                "urs.microsoft.com",
                "auth.gfx.ms",
                # "sc.imp.live.com", # URL does not have valid DNS
                "dynamicscrmoce.accesscontrol.windows.net",
                "nexus.passport.com", # in place of passport.net, since can't find a valid endpoint
                "clientconfig.passport.net",
                "login.microsoftonline.com",
                "home.dynamics.com",
                "crmcdn.azureedge.net", # in place of *.azureedge.net
                "www.crmdynint.com",
                # "cloudredirectorocesec.cloudapp.net", # URL does not have valid DNS
                # "cloudredirectoroce.cloudapp.net", # URL does not have valid DNS
                "mem.gfx.ms",
                "test.crm11.dynamics.com" # used in place of *.crm2.dynamics.com
                "www.d365ccafpi.com")
            # Resolve DNS and attempt to retrieve data from endpoint
            $RequiredResourcesEndpoints = @(
                "http://clientconfig.passport.net/ppcrlconfig.bin",
                "http://disco.crm11.dynamics.com/XRMServices/2011/Discovery.svc",
                "https://disco.crm11.dynamics.com/XRMServices/2011/Discovery.svc"
                "https://login.windows.net",
                "https://secure.aadcdn.microsoftonline-p.com/ests/2.1.5975.9/content/cdnbundles/jquery.1.11.min.js")
            # Use Optional resources for addtional services.
            $OptionalResources = @()
            
            # Optional resources endpoints. Specify as exact URI to download/test, such as
            # https://endpoint.domain.com/uri.svc
            $OptionalResourcesEndpoints = @()
            
            # Use the AdditionalResources array to specify items that need a port test on a port other
            # than 80 or 443. Specify as endpoint:port
            $AdditionalResources = @()
        }
    }
    
    # CRL Endpoint tests
    Write-Log -LogFile $Logfile -LogLevel INFO -Message "Testing CRL endpoint tests (Invoke-WebRequest)." -ConsoleOutput
    foreach ($url in $CRL)
    {
        try
        {
            $Result = Invoke-WebRequest -Uri $url -ea stop -wa silentlycontinue
            Switch ($Result.StatusCode)
            {
                200 { Write-Log -LogFile $Logfile -LogLevel SUCCESS -Message "Successfully obtained CRL from $($url)." -ConsoleOutput }
                400 { Write-Log -LogFile $Logfile -LogLevel ERROR -Message "Failed to obtain CRL from $($url): Bad request." -ConsoleOutput }
                401 { Write-Log -LogFile $Logfile -LogLevel ERROR -Message "Failed to obtain CRL from $($url): Unauthorized." -ConsoleOutput }
                403 { Write-Log -LogFile $Logfile -LogLevel ERROR -Message "Failed to obtain CRL from $($url): Forbidden." -ConsoleOutput }
                404 { Write-Log -LogFile $Logfile -LogLevel ERROR -Message "Failed to obtain CRL from $($url): Not found." -ConsoleOutput }
                407 { Write-Log -LogFile $Logfile -LogLevel ERROR -Message "Failed to obtain CRL from $($url): Proxy authentication required." -ConsoleOutput }
                502 { Write-Log -LogFile $Logfile -LogLevel ERROR -Message "Failed to obtain CRL from $($url): Bad gateway (likely proxy)." -ConsoleOutput }
                503 { Write-Log -LogFile $Logfile -LogLevel ERROR -Message "Failed to obtain CRL from $($url): Service unavailable (transient, try again)." -ConsoleOutput }
                504 { Write-Log -LogFile $Logfile -LogLevel ERROR -Message "Failed to obtain CRL from $($url): Gateway timeout (likely proxy)." -ConsoleOutput }
                default
                {
                    Write-Log -LogFile $Logfile -LogLevel ERROR -Message "Unable to obtain CRL from $($url)" -ConsoleOutput
                    Write-Log -LogFile $Logfile -LogLevel ERROR -Message "$($Result)"
                }
            }
        }
        catch
        {
            $ErrorMessage = $_
            Write-Log -LogFile $Logfile -LogLevel ERROR -Message "Exception: Unable to obtain CRL from $($url)" -ConsoleOutput
            Write-Log -LogFile $Logfile -LogLevel ERROR -Message $($ErrorMessage)
        }
        finally
        {
            If ($DebugLogging)
            {
                Write-Log -LogFile $Logfile -LogLevel DEBUG -Message "Debug log entry for $($url)."
                Write-Log -LogFile $Logfile -LogLevel DEBUG -Message $Result.StatusCode
                Write-Log -LogFile $Logfile -LogLevel DEBUG -Message $Result.StatusDescription
                If ($Result.RawContent.Length -lt 400)
                {
                    $DebugContent = $Result.RawContent -join ";"
                    Write-Log -LogFile $Logfile -LogLevel DEBUG -Message $DebugContent
                }
                Else
                {
                    $DebugContent = $Result.RawContent.Substring(0, 400) -join ";"
                    Write-Log -LogFile $Logfile -LogLevel DEBUG -Message $DebugContent
                }
            }
        }
    } # End Foreach CRL
    
    # Required Resource tests
    Write-Log -LogFile $Logfile -LogLevel INFO -Message "Testing Required Resources (TCP:443)." -ConsoleOutput
    foreach ($url in $RequiredResources)
    {
        [array]$ResourceAddresses = (Resolve-DnsName $url).IP4Address
        foreach ($ip4 in $ResourceAddresses)
        {
            try
            {
                $Result = Test-NetConnection $ip4 -Port 443 -ea stop -wa silentlycontinue
                switch ($Result.TcpTestSucceeded)
                {
                    true { Write-Log -LogFile $Logfile -LogLevel SUCCESS -Message "TCP connection to $($url) [$($ip4)]:443 successful." -ConsoleOutput }
                    false { Write-Log -LogFile $Logfile -LogLevel ERROR -Message "TCP connection to $($url) [$($ip4)]:443 failed." -ConsoleOutput }
                }
            }
            catch
            {
                $ErrorMessage = $_
                Write-Log -LogFile $Logfile -LogLevel ERROR -Message "Error resolving or connecting to $($url) [$($ip4)]:443" -ConsoleOutput
                Write-Log -LogFile $Logfile -LogLevel ERROR -Message $($Error)
            }
            finally
            {
                If ($DebugLogging)
                {
                    Write-Log -LogFile $Logfile -LogLevel DEBUG -Message "Debug log entry for $($url) [$($Result.RemoteAddress)]:443."
                    Write-Log -LogFile $Logfile -LogLevel DEBUG -Message "Remote endpoint: $($url)"
                    Write-Log -LogFile $Logfile -LogLevel DEBUG -Message "Remote port: $($Result.RemotePort)"
                    Write-Log -LogFile $Logfile -LogLevel DEBUG -Message "Interface Alias: $($Result.InterfaceAlias)"
                    Write-Log -LogFile $Logfile -LogLevel DEBUG -Message "Source Interface Address: $($Result.SourceAddress.IPAddress)"
                    Write-Log -LogFile $Logfile -LogLevel DEBUG -Message "Ping Succeeded: $($Result.PingSucceeded)"
                    Write-Log -LogFile $Logfile -LogLevel DEBUG -Message "Ping Reply Time (RTT) Status: $($Result.PingReplyDetails.Status)"
                    Write-Log -LogFile $Logfile -LogLevel DEBUG -Message "Ping Reply Time (RTT) RoundTripTime: $($Result.PingReplyDetails.RoundtripTime)"
                    Write-Log -LogFile $Logfile -LogLevel DEBUG -Message "TCPTestSucceeded: $($Result.TcpTestSucceeded)"
                }
            }
        }
    } # End Foreach Resources
    
    # Optional Resources tests
    If ($OptionalResources)
    {
        Write-Log -LogFile $Logfile -LogLevel INFO -Message "Testing Optional Resources (TCP:443)." -ConsoleOutput
        foreach ($url in $OptionalResources)
        {
            [array]$OptionalResourceAddresses = (Resolve-DnsName $url).IP4Address
            foreach ($ip4 in $OptionalResourceAddresses)
            {
                try
                {
                    $Result = Test-NetConnection $ip4 -Port 443 -ea stop -wa silentlycontinue
                    switch ($Result.TcpTestSucceeded)
                    {
                        true { Write-Log -LogFile $Logfile -LogLevel SUCCESS -Message "TCP connection to $($url) [$($ip4)]:443 successful." -ConsoleOutput }
                        false {
                            Write-Log -LogFile $Logfile -LogLevel WARN -Message "TCP connection to $($url) [$($ip4)]:443 failed." -ConsoleOutput
                            If ($DebugLogging) { Write-Log -LogFile $Logfile -LogLevel DEBUG -Message $($Result) }
                        }
                    }
                }
                catch
                {
                    $ErrorMessage = $_
                    Write-Log -LogFile $Logfile -LogLevel WARN -Message "Error resolving or connecting to $($url) [$($ip4)]:443" -ConsoleOutput
                    Write-Log -LogFile $Logfile -LogLevel WARN -Message $($ErrorMessage)
                }
                finally
                {
                    If ($DebugLogging)
                    {
                        Write-Log -LogFile $Logfile -LogLevel DEBUG -Message "Debug log entry for $($url) [$($Result.RemoteAddress)]:443."
                        Write-Log -LogFile $Logfile -LogLevel DEBUG -Message "Remote endpoint: $($url)"
                        Write-Log -LogFile $Logfile -LogLevel DEBUG -Message "Remote port: $($Result.RemotePort)"
                        Write-Log -LogFile $Logfile -LogLevel DEBUG -Message "Interface Alias: $($Result.InterfaceAlias)"
                        Write-Log -LogFile $Logfile -LogLevel DEBUG -Message "Source Interface Address: $($Result.SourceAddress.IPAddress)"
                        Write-Log -LogFile $Logfile -LogLevel DEBUG -Message "Ping Succeeded: $($Result.PingSucceeded)"
                        Write-Log -LogFile $Logfile -LogLevel DEBUG -Message "Ping Reply Time (RTT) Status: $($Result.PingReplyDetails.Status)"
                        Write-Log -LogFile $Logfile -LogLevel DEBUG -Message "Ping Reply Time (RTT) RoundTripTime: $($Result.PingReplyDetails.RoundtripTime)"
                        Write-Log -LogFile $Logfile -LogLevel DEBUG -Message "TCPTestSucceeded: $($Result.TcpTestSucceeded)"
                    }
                }
            }
        } # End Foreach OptionalResources
    }
    
    # Required Resources Endpoints tests
    Write-Log -LogFile $Logfile -LogLevel INFO -Message "Testing Required Resources Endpoints (Invoke-Webrequest)." -ConsoleOutput
    foreach ($url in $RequiredResourcesEndpoints)
    {
        try
        {
            $Result = Invoke-WebRequest -Uri $url -ea stop -wa silentlycontinue
            Switch ($Result.StatusCode)
            {
                200 { Write-Log -LogFile $Logfile -LogLevel SUCCESS -Message "Successfully connected to $($url)." -ConsoleOutput }
                400 { Write-Log -LogFile $Logfile -LogLevel ERROR -Message "Failed to contact $($url): Bad request." -ConsoleOutput }
                401 { Write-Log -LogFile $Logfile -LogLevel ERROR -Message "Failed to contact $($url): Unauthorized." -ConsoleOutput }
                403 { Write-Log -LogFile $Logfile -LogLevel ERROR -Message "Failed to contact $($url): Forbidden." -ConsoleOutput }
                404 { Write-Log -LogFile $Logfile -LogLevel ERROR -Message "Failed to contact $($url): Not found." -ConsoleOutput }
                407 { Write-Log -LogFile $Logfile -LogLevel ERROR -Message "Failed to contact $($url): Proxy authentication required." -ConsoleOutput }
                502 { Write-Log -LogFile $Logfile -LogLevel ERROR -Message "Failed to contact $($url): Bad gateway (likely proxy)." -ConsoleOutput }
                503 { Write-Log -LogFile $Logfile -LogLevel ERROR -Message "Failed to contact $($url): Service unavailable (transient, try again)." -ConsoleOutput }
                504 { Write-Log -LogFile $Logfile -LogLevel ERROR -Message "Failed to contact $($url): Gateway timeout (likely proxy)." -ConsoleOutput }
                default
                {
                    Write-Log -LogFile $Logfile -LogLevel ERROR -Message "OTHER: Failed to contact $($url)" -ConsoleOutput
                    Write-Log -LogFile $Logfile -LogLevel ERROR -Message "$($Result)" -ConsoleOutput
                }
            }
        }
        catch
        {
            $ErrorMessage = $_
            Write-Log -LogFile $Logfile -LogLevel ERROR -Message "Exception: Unable to contact $($url)" -ConsoleOutput
            Write-Log -LogFile $Logfile -LogLevel ERROR -Message $($ErrorMessage)
        }
        finally
        {
            If ($DebugLogging)
            {
                Write-Log -LogFile $Logfile -LogLevel DEBUG -Message "Debug log entry for $($url)."
                Write-Log -LogFile $Logfile -LogLevel DEBUG -Message $Result.StatusCode
                Write-Log -LogFile $Logfile -LogLevel DEBUG -Message $Result.StatusDescription
                If ($Result.RawContent.Length -lt 400)
                {
                    $DebugContent = $Result.RawContent -join ";"
                    Write-Log -LogFile $Logfile -LogLevel DEBUG -Message $DebugContent
                }
                Else
                {
                    $DebugContent = $Result.RawContent -join ";"
                    Write-Log -LogFile $Logfile -LogLevel DEBUG -Message $DebugContent.Substring(0, 400)
                }
            }
        }
    } # End Foreach RequiredResourcesEndpoints
    
    # Optional Resources Endpoints tests
    If ($OptionalResourcesEndpoints)
    {
        Write-Log -LogFile $Logfile -LogLevel INFO -Message "Testing Optional Resources Endpoints (Invoke-Webrequest)." -ConsoleOutput
        foreach ($url in $OptionalResourcesEndpoints)
        {
            try
            {
                $Result = Invoke-WebRequest -Uri $url -ea stop -wa silentlycontinue
                Switch ($Result.StatusCode)
                {
                    200 { Write-Log -LogFile $Logfile -LogLevel SUCCESS -Message "Successfully connected to $($url)." -ConsoleOutput }
                    400 { Write-Log -LogFile $Logfile -LogLevel ERROR -Message "Failed to contact $($url): Bad request." -ConsoleOutput }
                    401 { Write-Log -LogFile $Logfile -LogLevel ERROR -Message "Failed to contact $($url): Unauthorized." -ConsoleOutput }
                    403 { Write-Log -LogFile $Logfile -LogLevel ERROR -Message "Failed to contact $($url): Forbidden." -ConsoleOutput }
                    404 { Write-Log -LogFile $Logfile -LogLevel ERROR -Message "Failed to contact $($url): Not found." -ConsoleOutput }
                    407 { Write-Log -LogFile $Logfile -LogLevel ERROR -Message "Failed to contact $($url): Proxy authentication required." -ConsoleOutput }
                    502 { Write-Log -LogFile $Logfile -LogLevel ERROR -Message "Failed to contact $($url): Bad gateway (likely proxy)." -ConsoleOutput }
                    503 { Write-Log -LogFile $Logfile -LogLevel ERROR -Message "Failed to contact $($url): Service unavailable (transient, try again)." -ConsoleOutput }
                    504 { Write-Log -LogFile $Logfile -LogLevel ERROR -Message "Failed to contact $($url): Gateway timeout (likely proxy)." -ConsoleOutput }
                    default
                    {
                        Write-Log -LogFile $Logfile -LogLevel ERROR -Message "OTHER: Failed to contact $($url)" -ConsoleOutput
                        Write-Log -LogFile $Logfile -LogLevel ERROR -Message "$($Result)" -ConsoleOutput
                    }
                }
            }
            catch
            {
                $ErrorMessage = $_
                Write-Log -LogFile $Logfile -LogLevel ERROR -Message "Exception: Unable to contact $($url)" -ConsoleOutput
                Write-Log -LogFile $Logfile -LogLevel ERROR -Message $($ErrorMessage)
            }
            finally
            {
                If ($DebugLogging)
                {
                    Write-Log -LogFile $Logfile -LogLevel DEBUG -Message "Debug log entry for $($url)."
                    Write-Log -LogFile $Logfile -LogLevel DEBUG -Message $Result.StatusCode
                    Write-Log -LogFile $Logfile -LogLevel DEBUG -Message $Result.StatusDescription
                    If ($Result.RawContent.Length -lt 400)
                    {
                        $DebugContent = $Result.RawContent -join ";"
                        Write-Log -LogFile $Logfile -LogLevel DEBUG -Message $DebugContent
                    }
                    Else
                    {
                        $DebugContent = $Result.RawContent -join ";"
                        Write-Log -LogFile $Logfile -LogLevel DEBUG -Message $DebugContent.Substring(0, 400)
                    }
                }
            }
        } # End Foreach RequiredResourcesEndpoints
    }
    
    # Additional Resources tests
    If ($AdditionalResources)
    {
        Write-Log -LogFile $Logfile -LogLevel INFO -Message "Testing Additional Resources Endpoints (Invoke-Webrequest)." -ConsoleOutput
        foreach ($url in $AdditionalResources)
        {
            if ($url -match "\:")
            {
                $Name = $url.Split(":")[0]
                [array]$Resources = (Resolve-DnsName $Name).Ip4Address
                $ResourcesPort = $url.Split(":")[1]
            }
            Else
            {
                $Name = $url
                [array]$Resources = (Resolve-DnsName $Name).IP4Address
                $ResourcesPort = "443"
            }
            foreach ($ip4 in $Resources)
            {
                try
                {
                    $Result = Test-NetConnection $ip4 -Port $ResourcesPort -ea stop -wa silentlycontinue
                    switch ($Result.TcpTestSucceeded)
                    {
                        true { Write-Log -LogFile $Logfile -LogLevel SUCCESS -Message "TCP connection to $($Name) [$($ip4)]:$($ResourcesPort) successful." -ConsoleOutput }
                        false {
                            Write-Log -LogFile $Logfile -LogLevel WARN -Message "TCP connection to $($Name) [$($ip4)]:$($ResourcesPort) failed." -ConsoleOutput
                            If ($DebugLogging) { Write-Log -LogFile $Logfile -LogLevel DEBUG -Message $($Result) }
                        }
                    }
                }
                catch
                {
                    $ErrorMessage = $_
                    Write-Log -LogFile $Logfile -LogLevel WARN -Message "Error resolving or connecting to $($Name) [$($ip4)]:$($ResourcesPort)" -ConsoleOutput
                    Write-Log -LogFile $Logfile -LogLevel WARN -Message $($ErrorMessage)
                }
                finally
                {
                    If ($DebugLogging)
                    {
                        Write-Log -LogFile $Logfile -LogLevel DEBUG -Message "Debug log entry for $($Name) [$($Result.RemoteAddress)]:443."
                        Write-Log -LogFile $Logfile -LogLevel DEBUG -Message "Remote endpoint: $($Name)"
                        Write-Log -LogFile $Logfile -LogLevel DEBUG -Message "Remote port: $($Result.RemotePort)"
                        Write-Log -LogFile $Logfile -LogLevel DEBUG -Message "Interface Alias: $($Result.InterfaceAlias)"
                        Write-Log -LogFile $Logfile -LogLevel DEBUG -Message "Source Interface Address: $($Result.SourceAddress.IPAddress)"
                        Write-Log -LogFile $Logfile -LogLevel DEBUG -Message "Ping Succeeded: $($Result.PingSucceeded)"
                        Write-Log -LogFile $Logfile -LogLevel DEBUG -Message "Ping Reply Time (RTT) Status: $($Result.PingReplyDetails.Status)"
                        Write-Log -LogFile $Logfile -LogLevel DEBUG -Message "Ping Reply Time (RTT) RoundTripTime: $($Result.PingReplyDetails.RoundtripTime)"
                        Write-Log -LogFile $Logfile -LogLevel DEBUG -Message "TCPTestSucceeded: $($Result.TcpTestSucceeded)"
                    }
                }
            }
        } # End ForEach AdditionalResources
    } # End IF AdditionalResources
    
    Write-Log -LogFile $Logfile -LogLevel INFO -Message "Finished Online Endpoints tests."
} # End Function OnlineEndPoints

function SystemConfiguration
{
    ## Show system configuration
    Write-Log -LogFile $Logfile -LogLevel INFO -Message "Starting system configuration gathering."
    [string]$OSVersion = ([System.Environment]::OSVersion.Version.Major.ToString() + "." + [System.Environment]::OSVersion.Version.Minor.ToString())
    [string]$OperatingSystem = (Get-WmiObject -Class Win32_OperatingSystem -Namespace "root\cimv2").Caption
    [string]$OSBitness = [System.Environment]::Is64BitOperatingSystem
    [string]$OSMachineName = [System.Environment]::MachineName.ToString()
    $bytes = (Get-WmiObject -class "cim_physicalmemory" | Measure-Object -Property Capacity -Sum).Sum
    $gb = $bytes/1073741824
    
    Write-Log -LogFile $Logfile -LogLevel INFO -Message "System name: $($OSMachineName)"
    Write-Log -LogFile $Logfile -LogLevel INFO -Message "64-bit operating system detected: $($OSBitness)"
    Write-Log -LogFile $Logfile -LogLevel INFO -Message "Operating System: $($OperatingSystem) $($OSVersion)"
    Write-Log -LogFile $Logfile -LogLevel INFO -Message "System memory: $($gb) GB"
    
    If ($bytes)
    {
        switch ($gb)
        {
            ({ $_ -lt 2 }) { Write-Log -ConsoleOutput -LogFile $Logfile -LogLevel ERROR -Message "This computer does not meet the minimum memory recommendation of 2GB." }
            ({ $_ -lt 4 }) { Write-Log -LogFile $Logfile -LogLevel INFO -Message "This computer meets the minimum memory recommendation of 2GB." }
            ({ $_ -ge 4 }) { Write-Log -LogFile $Logfile -LogLevel INFO -Message "This computer meets or exceeds recommended memory recommendation of 4GB." }
        }
    }
    
    ## Browser versions
    # Internet Explorer
    Write-Log -LogFile $Logfile -LogLevel INFO -Message "Checking browser versions." -ConsoleOutput
    If ((Get-ItemProperty -ea silentlycontinue 'HKLM:\SOFTWARE\Microsoft\Internet Explorer').svcVersion)
    {
        $IEVersion = (Get-ItemProperty -ea silentlycontinue 'HKLM:\SOFTWARE\Microsoft\Internet Explorer').svcVersion
    }
    Else
    {
        $IEVersion = (Get-ItemProperty -ea silentlycontinue 'HKLM:\SOFTWARE\Microsoft\Internet Explorer').Version
    }
    
    # Firefox
    If ((Get-ItemProperty -ea silentlycontinue 'HKLM:\SOFTWARE\Mozilla\Mozilla Firefox').'(Default)')
    {
        $FirefoxVersion = (Get-ItemProperty -ea silentlycontinue 'HKLM:\SOFTWARE\Mozilla\Mozilla Firefox').'(Default)'
    }
    
    # Chrome
    Try
    {
        If ((Get-Item -ea silentlycontinue (Get-ItemProperty -ea silentlycontinue 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\chrome.exe').'(Default)').VersionInfo.ProductVersion)
        {
            $ChromeVersion = (Get-Item -ea silentlycontinue (Get-ItemProperty -ea silentlycontinue 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\chrome.exe').'(Default)').VersionInfo.ProductVersion
            $ChromeVersion = $ChromeVersion.Split(".")[0]
        }
    }
    catch
    {
        # Chrome is not installed
    }
    
    # Edge
    If ($OSVersion -eq "10.0")
    {
        $EdgePath = (Get-AppxPackage Microsoft.MicrosoftEdge -ea silentlycontinue).InstallLocation + "\MicrosoftEdge.exe"
        $EdgeVersion = (Get-ItemProperty $EdgePath -ea silentlycontinue).VersionInfo.ProductVersion
    }
    
    If ($IEVersion)
    {
        Write-Log -LogFile $Logfile -LogLevel INFO -Message "Internet Explorer Version $($IEVersion) detected."
        If (($IEVersion.Split(".")[0]) -ge 10)
        {
            Write-Log -LogFile $Logfile -LogLevel SUCCESS -Message "This computer has a supported version of Internet Explorer."
        }
        Else
        {
        Write-Log -LogFile $Logfile -LogLevel ERROR -Message "This computer does not have a supported version of Internet Explorer."    
        }
    }
    
    If ($FirefoxVersion)
    {
        Write-Log -LogFile $Logfile -LogLevel INFO -Message "Mozilla Firefox Version $($FirefoxVersion) detected."
        If (($FirefoxVersion.Split(".")[0]) -ge 60)
        {
            Write-Log -LogFile $Logfile -LogLevel SUCCESS -Message "This computer has a supported version of Mozilla Firefox."
        }
        Else
        {
            Write-Log -LogFile $Logfile -LogLevel ERROR -Message "This computer does not have a supported version of Mozilla Firefox."
        }
    }
    
    If ($ChromeVersion)
    {
        Write-Log -LogFile $Logfile -LogLevel INFO -Message "Google Chrome Version $($ChromeVersion) detected."
        If (($ChromeVersion.Split(".")[0]) -ge 67)
        {
            Write-Log -LogFile $Logfile -LogLevel SUCCESS -Message "This computer has a supported version of Google Chrome."
        }
        Else
        {
            Write-Log -LogFile $Logfile -LogLevel ERROR -Message "This computer does not have a supported version of Google Chrome."
        }
    }
    
    If ($EdgeVersion)
    {
        Write-Log -LogFile $Logfile -LogLevel INFO -Message "Microsoft Edge $($EdgeVersion) detected."
        If (($EdgeVersion.Split(".")[0]) -ge 11)
        {
            Write-Log -LogFile $Logfile -LogLevel SUCCESS -Message "This computer has a supported version of Microsoft Edge."
        }
        Else
        {
            Write-Log -LogFile $Logfile -LogLevel ERROR -Message "This computer does not have a supported version of Microsoft Edge."    
        }
    }
    
    # Netsh WinHTTP proxy
    Write-Log -LogFile $Logfile -LogLevel INFO -Message "WinHTTP proxy settings (netsh winhttp show proxy):"
    $WinHTTPProxy = (netsh winhttp show proxy)
    $WinHTTPProxy = ($WinHTTPProxy -join " ").Trim()
    Write-Log -LogFile $Logfile -LogLevel INFO -Message $WinHTTPProxy
    
    # .NET Proxy
    Write-Log -LogFile $Logfile -LogLevel INFO -Message ".NET proxy settings (machine.config/configuration/system.net/defaultproxy):"
    [xml]$machineconfig = gc $env:windir\Microsoft.NET\Framework64\v4.0.30319\Config\machine.config
    if (!$machineconfig.configuration.'system.net'.defaultproxy)
    {
        Write-Log -LogFile $Logfile -LogLevel INFO -Message "No proxy configuration exists in $env:windir\Microsoft.NET\Framework64\v4.0.30319\Config\machine.config."
    }
    else
    {
        Write-Log -LogFile $Logfile -LogLevel INFO -Message "The following proxy configuration exists in $env:windir\Microsoft.NET\Framework64\v4.0.30319\Config\machine.config."
        $nodes = $machineconfig.ChildNodes.SelectNodes("/configuration/system.net/defaultproxy/proxy") | Sort -Unique
        Write-Log -Logfile $Logfile -LogLevel INFO -Message "UseSystemDefault: $($nodes.usesystemdefault)"
        Write-Log -LogFile $Logfile -LogLevel INFO -Message "ProxyAddress: $($nodes.proxyaddress)"
        Write-Log -LogFile $Logfile -LogLevel INFO -Message "BypassOnLocal $($nodes.bypassonlocal)"
    }
    Write-Log -LogFile $Logfile -LogLevel INFO -Message "For more .NET proxy configuration parameters, see https://docs.microsoft.com/en-us/dotnet/framework/configure-apps/file-schema/network/proxy-element-network-settings"
    
    # Check for TLS capabilities
    switch ($OSVersion)
    {
        "10.0"    {
            Write-Log -Logfile $Logfile -ConsoleOutput -LogLevel INFO -Message "Checking TLS settings for Windows 10 or Windows Server 2016."
            $KeysArray = @(
                @{ Path = "HKLM:SOFTWARE\Microsoft\.NETFramework\v4.0.30319"; Item = "SchUseStrongCrypto"; type = "REG_DWORD"; Value = "1" },
                @{ Path = "HKLM:SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v4.0.30319"; Item = "SchUseStrongCrypto"; type = "REG_DWORD"; Value = "1" }
            )
        } # End 10.0 / Windows Server 2016
        
        "6.3"    {
            Write-Log -Logfile $Logfile -ConsoleOutput -LogLevel INFO -Message "Checking TLS settings for Windows 8.1 or Server 2012 R2."
            $KeysArray = @(
                @{ Path = "HKLM:SOFTWARE\Microsoft\.NETFramework\v4.0.30319"; Item = "SchUseStrongCrypto"; type = "REG_DWORD"; Value = "1" },
                @{ Path = "HKLM:SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v4.0.30319"; Item = "SchUseStrongCrypto"; type = "REG_DWORD"; Value = "1" }
            )
        } # End 6.3 / Windows Server 2012 R2
        
        "6.2"    {
            Write-Log -Logfile $Logfile -ConsoleOutput -LogLevel INFO -Message "Checking TLS settings for Windows 8 Server 2012."
            $KeysArray = @(
                @{ Path = "HKLM:SOFTWARE\Microsoft\.NETFramework\v4.0.30319"; Item = "SchUseStrongCrypto"; type = "REG_DWORD"; Value = "1" },
                @{ Path = "HKLM:SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v4.0.30319"; Item = "SchUseStrongCrypto"; type = "REG_DWORD"; Value = "1" }
            )
        } # End 6.2 / Windows Server 2012
        
        "6.1"    {
            Write-Log -Logfile $Logfile -ConsoleOutput -LogLevel INFO -Message "Checking TLS settings for Windows 7 or Windows Server Server 2008 R2."
            $KeysArray = @(
                @{ Path = "HKLM:SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Client"; Item = "DisabledByDefault"; Type = "REG_DWORD"; Value = "0" },
                @{ Path = "HKLM:SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Client"; Item = "Enabled"; type = "REG_DWORD"; Value = "1" },
                @{ Path = "HKLM:SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Server"; Item = "DisabledByDefault"; type = "REG_DWORD"; Value = "0" },
                @{ Path = "HKLM:SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Server"; Item = "Enabled"; type = "REG_DWORD"; Value = "1" },
                @{ Path = "HKLM:SOFTWARE\Microsoft\.NETFramework\v4.0.30319"; Item = "SchUseStrongCrypto"; type = "REG_DWORD"; Value = "1" },
                @{ Path = "HKLM:SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v4.0.30319"; Item = "SchUseStrongCrypto"; type = "REG_DWORD"; Value = "1" }
            )
        } # End 6.1 / Windows Server 2008 R2
        
        "6.0"    {
            Write-Log -Logfile $Logfile -ConsoleOutput -LogLevel INFO -Message "Checking TLS settings for Windows Vista or Windows Server 2008."
            Write-Log -LogFile $Logfile -ConsoleOutput -LogLevel WARN -Message "TLS 1.2 cannot be enabled on Windows Vista or Windows Server 2008."
        } # End 6.0 / Windows Vista or Windows Server 2008
        
        default { Write-Log -LogFile $Logfile -ConsoleOutput -LogLevel WARN -Message "Unable to determine Windows Version. TLS checks will be skipped." }
    }
    
    If ($KeysArray)
    {
        foreach ($Key in $KeysArray)
        {
            try
            {
                $Result = (Get-ItemProperty -erroraction SilentlyContinue $Key.Path).$($Key.Item).ToString()
                If ($Result)
                {
                    If ($Result -match $Key.Value)
                    {
                        Write-Log -LogFile $Logfile -LogLevel INFO -Message "Key $($Key.Path)\$($Key.Item) with a value of $($Key.Value) is set correctly for TLS 1.2 Configuration."
                    }
                    Else
                    {
                        $RegKeyPath = ($Key.Path).Replace(":", "\")
                        Write-Log -LogFile $Logfile -LogLevel INFO -Message "Key $($Key.Path)\$($Key.Item) with a value of $($Key.Value) is not set correctly for TLS 1.2 Configuration."
                        Write-Log -LogFile $LogFile -LogLevel INFO -Message "Key $($Key.Path)\$($Key.Item) must be set to $($Key.Value) for TLS 1.2 support."
                        Write-Log -LogFile $Logfile -LogLevel INFO -Message "To configure, run: REG ADD ""$($RegKeyPath)"" /v $($Key.Item) /d $($Key.Value) /t REG_DWORD /f"
                    }
                }
                Else
                {
                    $RegKeyPath = ($Key.Path).Replace(":", "\")
                    Write-Log -LogFile $Logfile -LogLevel INFO -Message "Key $($Key.Path)\$($Key.Item) not found."
                    Write-Log -LogFile $LogFile -LogLevel INFO -Message "Key $($Key.Path)\$($Key.Item) must be set to $($Key.Value) for TLS 1.2 support."
                    Write-Log -LogFile $Logfile -LogLevel INFO -Message "To configure, run: REG ADD ""$($RegKeyPath)"" /v $($Key.Item) /d $($Key.Value) /t REG_DWORD /f"
                }
            }
            Catch
            {
                $RegKeyPath = ($Key.Path).Replace(":", "\")
                Write-Log -LogFile $Logfile -LogLevel INFO -Message "Exception or $($Key.Path)\$($Key.Item) not found."
                Write-Log -LogFile $LogFile -LogLevel INFO -Message "Key $($Key.Path)\$($Key.Item) must be set to $($Key.Value) for TLS 1.2 support."
                Write-Log -LogFile $Logfile -LogLevel INFO -Message "To configure, run: REG ADD ""$($RegKeyPath)"" /v $($Key.Item) /d $($Key.Value) /t REG_DWORD /f"
            }
        }
        Write-Log -LogFile $Logfile -LogLevel INFO -Message "Finished checking for TLS 1.2 Configuration settings."
    }
        
    Write-Log -LogFile $Logfile -LogLevel INFO -Message "Finished gathering system configuration."
} # End Function System Configuration

## Begin script
Write-Log -LogFile $Logfile -LogLevel INFO -Message "========================================================="
Write-Log -LogFile $Logfile -LogLevel INFO -Message "Starting Dynamics 365 connectivity and resolution testing."

# List modifier parameters to exclude from switch statement. These are the parameters that should not affect which tests are run.
$Excluded = @(
    'debuglogging',
    'logfile',
    'onlineendpointtarget')
[regex]$ParametersToExclude = '(?i)^(\b' + (($Excluded | foreach { [regex]::escape($_) }) â€“join "\b|\b") + '\b)$'
$Params = $PSBoundParameters.Keys | ? { $_ -notmatch $ParametersToExclude }
If ($Params)
{
    switch -regex ($Params)
    {
        '\bonlineendpoints\b' { OnlineEndPoints }
        '\bsystemconfiguration\b' { SystemConfiguration }
    }
}
else
{
    "Running all tests."
    OnlineEndPoints; SystemConfiguration
}
Write-Log -LogFile $Logfile -LogLevel INFO -Message "Done! Logfile is $($Logfile)." -ConsoleOutput
Write-Log -LogFile $Logfile -LogLevel INFO -Message "---------------------------------------------------------"