#$Global:ErrorActionPreference="SilentlyContinue"

[CmdletBinding()]

param (
	[array]$DCs, 
	[switch]$DebugLogging,
	[switch]$Dns,
	[string]$ForestFQDN,
	[switch]$Network,
	[switch]$OnlineEndPoints,
	[ValidateSet("Commercial","DOD","GCC","GCCHigh")]
	[string]$OnlineEndPointTarget = "Commercial",
	[switch]$OptionalADPortTest,
	[string] $TenantId
	)

$OutputEncoding = [console]::InputEncoding = [console]::OutputEncoding = New-Object System.Text.UTF8Encoding
$PSDefaultParameterValues = @{'*:Encoding' = 'utf8'}

# Use the OptionalADPortTest switch to add the following ports: 88, 636, 3269
# In order to use ports 636 and 3269, domain controllers must be configured with a
# valid server certificate. See https://social.technet.microsoft.com/wiki/contents/articles/18254.ldaps-636-and-msft-gc-ssl-3269-service.aspx
# and https://social.technet.microsoft.com/wiki/contents/articles/2980.ldap-over-ssl-ldaps-certificate.aspx.
If ($OptionalADPortTest) { $OptionalADPorts += @('88', '636','3269') }

if($TenantId) {$TenantId = "$($TenantId).registration.msappproxy.net"}

$Ports = @('53', '135', '389', '445', '3268')
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
function Write-Log([string[]]$Message, [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")
		Write-Host "[$TimeStamp] [$LogLevel] :: $Message" -ForegroundColor $Color
	}
}

function OnlineEndPoints
{
	switch -regex ($OnlineEndPointTarget)
	{
		'commercial|gcc'
		{
			Write-Log -LogLevel INFO -Message "Starting Online Endpoints tests (Commercial/GCC)."
			Write-Log -LogLevel INFO -Message "See https://support.office.com/en-us/article/office-365-urls-and-ip-address-ranges-8548a211-3fe7-47cb-abb1-355ea5aa88a2"
			Write-Log -LogLevel INFO -Message "for more details on Commercial/GCC endpoints."
			$CRL = @(
				"http://crl3.digicert.com",
				"http://crl4.digicert.com",
				"http://crl.microsoft.com/pki/crl/products/microsoftrootcert.crl",
				"http://mscrl.microsoft.com/pki/mscorp/crl/msitwww2.crl",
				"http://ocsp.entrust.net",
				"http://ocsp.msocsp.com",
				"http://ocsp.verisign.com",
				"http://ocsp.digicert.com",
				"http://oneocsp.microsoft.com",
				"http://root-c3-ca2-2009.ocsp.d-trust.net"
			)
			$RequiredResources = @(
				"aadcdn.msauth.net",
				"aadcdn.msauthimages.net",
				"aadcdn.msftauth.net",
				"adminwebservice.microsoftonline.com",
				#"adminwebservice-s1-co2.microsoftonline.com",
				"api.passwordreset.microsoftonline.com",
				#"bba800-anchor.microsoftonline.com",
				"becws.microsoftonline.com",
				"ccscdn.msauth.net",
				"ccscdn.msftauth.net",
				"graph.windows.net",
				"login.microsoftonline.com",
				"login.windows.net",
				"management.core.windows.net",
				"passwordreset.microsoftonline.com",
				"provisioningapi.microsoftonline.com",
				"secure.aadcdn.microsoftonline-p.com",
				"sts.windows.net"
			)
			$RequiredResourcesEndpoints = @(
				"https://adminwebservice.microsoftonline.com/provisioningservice.svc",
				# "https://adminwebservice-s1-co2.microsoftonline.com/provisioningservice.svc", # Removed 2020-01-28
				"https://login.microsoftonline.com",
				"https://login.windows.net",
				"https://provisioningapi.microsoftonline.com/provisioningwebservice.svc",
				"https://secure.aadcdn.microsoftonline-p.com/ests/2.1.5975.9/content/cdnbundles/jquery.1.11.min.js"
			)
			$OptionalResources = @(
				"account.activedirectory.windowsazure.com",
				"adds.aadconnecthealth.azure.com",
				"adhsprodwusehsyncia.servicebus.windows.net",
				"adhsprodwusaadsynciadata.blob.core.windows.net",
				"autoupdate.msappproxy.net",
				"clientconfig.microsoftonline-p.net",
				"enterpriseregistration.windows.net",
				"management.azure.com",
				"management.core.windows.net",
				"policykeyservice.dc.ad.msft.net",
				"pfd.phonefactor.net",
				"prdf.aadg.msidentity.com",
				"s1.adhybridhealth.azure.com",
				"ssprdedicatedsbprodscu.servicebus.windows.net",
				"www.tm.f.prd.aadg.trafficmanager.net"
			)
			$OptionalResourcesEndpoints = @(
				"https://device.login.microsoftonline.com",
				"https://ssprdedicatedsbprodscu.servicebus.windows.net",
				"https://policykeyservice.dc.ad.msft.net/clientregistrationmanager.svc"
			)
			$SeamlessSSOEndpoints = @(
				#"autologon.microsoftazuread-sso.com",
				#"aadg.windows.net.nsatc.net",
				#"0.register.msappproxy.net",
				#"0.registration.msappproxy.net",
				#"proxy.cloudwebappproxy.net"
			)
			# Tenant Registration Endpoint
			If ($TenantId) { $SeamlessSSOEndpoints += $Tenant } # Added 2020-01-28
			# Use the AdditionalResources array to specify items that need a port test on a port other
			# than 80 or 443.
			$AdditionalResources = @(
				# "watchdog.servicebus.windows.net:5671" # Removed 2020-01-28
			)
		}

		'dod|gcchigh'
		{
			Write-Log -LogLevel INFO -Message "Starting Online Endpoints tests (DOD)."
			Write-Log -LogLevel INFO -Message "See https://support.office.com/en-us/article/office-365-u-s-government-dod-endpoints-5d7dce60-4892-4b58-b45e-ee42fe8a907f"
			Write-Log -LogLevel INFO -Message "for more details on DOD/GCCHigh endpoints."
			$CRL = @(
				"http://ocsp.msocsp.com",
				"https://mscrl.microsoft.com/pki/mscorp/crl/msitwww2.crl",
				"http://crl.microsoft.com/pki/crl/products/microsoftrootcert.crl",
				"http://ocsp.verisign.com",
				"http://ocsp.entrust.net"
			)
			$RequiredResources = @(
				"aadcdn.msauth.net",
				"aadcdn.msauthimages.net",
				"aadcdn.msftauth.net",
				"aadcdn.msftauthimages.us",
				"adminwebservice.gov.us.microsoftonline.com",
				"adminwebservice-s1-bn1a.microsoftonline.com",
				"becws.gov.us.microsoftonline.com,"
				"ccscdn.msauth.net",
				"ccscdn.msftauth.net",
				"clientconfig.microsoftonline-p.net",
				"dod-graph.microsoft.us",
				"adminwebservice-s1-dm2a.microsoftonline.com",
				"graph.microsoftazure.us",
				"graph.windows.net",
				"login.microsoftonline.us",
				"login.microsoftonline.com",
				"login.microsoftonline-p.com",
				"loginex.microsoftonline.com",
				"login-us.microsoftonline.com",
				"login.windows.net",
				"mscrl.microsoft.com",
				"nexus.microsoftonline-p.com",
				"passwordreset.activedirectory.windowsazure.us",
				#"passwordreset.microsoftonline.com",
				"provisioningapi.gov.us.microsoftonline.com",
				"provisioningapi-s1-dm2a.microsoftonline.com",
				"provisioningapi-s1-dm2r.microsoftonline.com",
				"secure.aadcdn.microsoftonline-p.com",
				"ssprdedicatedsbprodscu.servicebus.windows.net"
			)
			$RequiredResourcesEndpoints = @(
				"https://adminwebservice.gov.us.microsoftonline.com/provisioningservice.svc",
				"https://adminwebservice-s1-bn1a.microsoftonline.com/provisioningservice.svc",
				"https://adminwebservice-s1-dm2a.microsoftonline.com/provisioningservice.svc",
				"https://login.microsoftonline.us"
				"https://login.microsoftonline.com",
				"https://loginex.microsoftonline.com",
				"https://login-us.microsoftonline.com",
				"https://login.windows.net",
				"https://provisioningapi.gov.us.microsoftonline.com/provisioningwebservice.svc",
				"https://provisioningapi-s1-dm2a.microsoftonline.com/provisioningwebservice.svc",
				"https://provisioningapi-s1-dm2r.microsoftonline.com/provisioningwebservice.svc"
				"https://secure.aadcdn.microsoftonline-p.com/ests/2.1.5975.9/content/cdnbundles/jquery.1.11.min.js")
			# These optional endpoints are newly listed for DOD/GCCHigh
			$OptionalResources = @(
				"management.azure.com", 
				"policykeyservice.aadcdi.azure.us"                                                 # Azure AD Connect Health
				# ,"enterpriseregistration.windows.net" # Not currently listed for DOD/GCCH
			)

			$OptionalResourcesEndpoints = @(
				"https://policykeyservice.aadcdi.azure.us/clientregistrationmanager.svc" # Azure AD Connect Health
				# ,"https://enterpriseregistration.windows.net" # Not currently listed for DOD/GCCH
			)
			# Use the AdditionalResources array to specify items that need a port test on a port other
			# than 80 or 443.
			$AdditionalResources = @(
				# "watchdog.servicebus.windows.net:5671" # ServiceBus endpoints no longer needed
			)
		#>
		}
	}

	# CRL Endpoint tests
	Write-Log -LogLevel INFO -Message "Testing CRL endpoint tests (Invoke-WebRequest)."
	foreach ($url in $CRL)
	{
		try
		{
			$Result = Invoke-WebRequest -Uri $url -usebasicparsing -ea stop -wa silentlycontinue
			Switch ($Result.StatusCode)
			{
				200 { Write-Log -LogLevel SUCCESS -Message "Successfully obtained CRL from $($url)."  }
				400 { Write-Log -LogLevel ERROR -Message "Failed to obtain CRL from $($url): Bad request."   }
				401 { Write-Log -LogLevel ERROR -Message "Failed to obtain CRL from $($url): Unauthorized."  }
				403 { Write-Log -LogLevel ERROR -Message "Failed to obtain CRL from $($url): Forbidden." }
				404 { Write-Log -LogLevel ERROR -Message "Failed to obtain CRL from $($url): Not found."  }
				407 { Write-Log -LogLevel ERROR -Message "Failed to obtain CRL from $($url): Proxy authentication required." }
				502 { Write-Log -LogLevel ERROR -Message "Failed to obtain CRL from $($url): Bad gateway (likely proxy)." }
				503 { Write-Log -LogLevel ERROR -Message "Failed to obtain CRL from $($url): Service unavailable (transient, try again)." }
				504 { Write-Log -LogLevel ERROR -Message "Failed to obtain CRL from $($url): Gateway timeout (likely proxy)."}
				default
				{
					Write-Log -LogLevel ERROR -Message "Unable to obtain CRL from $($url)" 
					Write-Log -LogLevel ERROR -Message "$($Result)"            
				}
			}
		}
		catch
		{
			Write-Log  -LogLevel ERROR -Message "Exception: Unable to obtain CRL from $($url)" 
			Write-Log  -LogLevel ERROR -Message "$($_.Exception.Message)"
		}
		finally
		{
			If ($DebugLogging)
			{
				Write-Log  -LogLevel DEBUG -Message "Debug log entry for $($url)."
				Write-Log  -LogLevel DEBUG -Message $Result.StatusCode
				Write-Log -LogLevel DEBUG -Message $Result.StatusDescription
				If ($Result.RawContent.Length -lt 400)
				{
					$DebugContent = $Result.RawContent -join ";"
					Write-Log  -LogLevel DEBUG -Message $DebugContent
				}
				Else
				{
					$DebugContent = $Result.RawContent.Substring(0, 400) -join ";"
					Write-Log -LogLevel DEBUG -Message $DebugContent
				}
			}
		}
	} # End Foreach CRL

	# Required Resource tests
	Write-Log  -LogLevel INFO -Message "Testing Required Resources (TCP:443)." 
	foreach ($url in $RequiredResources)
	{
		try { [array]$ResourceAddresses = (Resolve-DnsName $url -ErrorAction stop).IP4Address }
		catch { $ErrorMessage = $_;  Write-Log  -LogLevel ERROR -Message "Unable to resolve host $($url).";  Write-Log -LogLevel ERROR -Message $($ErrorMessage); Continue }
		foreach ($ip4 in $ResourceAddresses)
		{
			try
			{
				$Result = Test-NetConnection $ip4 -Port 443 -ea stop -wa silentlycontinue
				switch ($Result.TcpTestSucceeded)
				{
					true { Write-Log  -LogLevel SUCCESS -Message "TCP connection to $($url) [$($ip4)]:443 successful."  }
					false { Write-Log  -LogLevel ERROR -Message "TCP connection to $($url) [$($ip4)]:443 failed."   }
				}
			}
			catch
			{
				Write-Log  -LogLevel ERROR -Message "Error resolving or connecting to $($url) [$($ip4)]:443" 
				Write-Log  -LogLevel ERROR -Message "$($_.Exception.Message)"
			}
			finally
			{
				If ($DebugLogging)
				{
					Write-Log  -LogLevel DEBUG -Message "Debug log entry for $($url) [$($Result.RemoteAddress)]:443."
					Write-Log  -LogLevel DEBUG -Message "Remote endpoint: $($url)"
					Write-Log  -LogLevel DEBUG -Message "Remote port: $($Result.RemotePort)"
					Write-Log  -LogLevel DEBUG -Message "Interface Alias: $($Result.InterfaceAlias)"
					Write-Log  -LogLevel DEBUG -Message "Source Interface Address: $($Result.SourceAddress.IPAddress)"
					Write-Log  -LogLevel DEBUG -Message "Ping Succeeded: $($Result.PingSucceeded)"
					Write-Log  -LogLevel DEBUG -Message "Ping Reply Time (RTT) Status: $($Result.PingReplyDetails.Status)"
					Write-Log  -LogLevel DEBUG -Message "Ping Reply Time (RTT) RoundTripTime: $($Result.PingReplyDetails.RoundtripTime)"
					Write-Log  -LogLevel DEBUG -Message "TCPTestSucceeded: $($Result.TcpTestSucceeded)"
				}
			}
		} 
	} # End Foreach Resources

	# Option Resources tests
	Write-Log  -LogLevel INFO -Message "Testing Optional Resources (TCP:443)." 
	foreach ($url in $OptionalResources)
	{
		try { [array]$ResourceAddresses = (Resolve-DnsName $url -ErrorAction stop).IP4Address }
		catch { $ErrorMessage = $_; Write-Log  -LogLevel ERROR -Message "Unable to resolve host $($url)." ; Write-Log  -LogLevel ERROR -Message $($ErrorMessage); Continue }

		foreach ($ip4 in $ResourceAddresses)
		{
			try
			{
				$Result = Test-NetConnection $ip4 -Port 443 -ea stop -wa silentlycontinue
				switch ($Result.TcpTestSucceeded)
				{
					true { Write-Log  -LogLevel SUCCESS -Message "TCP connection to $($url) [$($ip4)]:443 successful."  }
					false {
						Write-Log  -LogLevel WARN -Message "TCP connection to $($url) [$($ip4)]:443 failed." ; 
						If ($DebugLogging) { Write-Log  -LogLevel DEBUG -Message $($Result) }
					}
				}
			}
			catch
			{
				Write-Log  -LogLevel WARN -Message "Error resolving or connecting to $($url) [$($ip4)]:443" 
				Write-Log  -LogLevel WARN -Message "$($_.Exception.Message)"
			}
			finally
			{
				If ($DebugLogging)
				{
					Write-Log  -LogLevel DEBUG -Message "Debug log entry for $($url) [$($Result.RemoteAddress)]:443."
					Write-Log  -LogLevel DEBUG -Message "Remote endpoint: $($url)"
					Write-Log  -LogLevel DEBUG -Message "Remote port: $($Result.RemotePort)"
					Write-Log  -LogLevel DEBUG -Message "Interface Alias: $($Result.InterfaceAlias)"
					Write-Log  -LogLevel DEBUG -Message "Source Interface Address: $($Result.SourceAddress.IPAddress)"
					Write-Log  -LogLevel DEBUG -Message "Ping Succeeded: $($Result.PingSucceeded)"
					Write-Log  -LogLevel DEBUG -Message "Ping Reply Time (RTT) Status: $($Result.PingReplyDetails.Status)"
					Write-Log  -LogLevel DEBUG -Message "Ping Reply Time (RTT) RoundTripTime: $($Result.PingReplyDetails.RoundtripTime)"
					Write-Log  -LogLevel DEBUG -Message "TCPTestSucceeded: $($Result.TcpTestSucceeded)"
				}
			}
		}
	} # End Foreach OptionalResources

	# Required Resources Endpoints tests
	Write-Log  -LogLevel INFO -Message "Testing Required Resources Endpoints (Invoke-Webrequest)." 
	foreach ($url in $RequiredResourcesEndpoints)
	{
		try
		{
			$Result = Invoke-WebRequest -Uri $url -usebasicparsing -ea stop -wa silentlycontinue
			Switch ($Result.StatusCode)
			{
				200 { Write-Log  -LogLevel SUCCESS -Message "Successfully connected to $($url)."  }
				400 { Write-Log  -LogLevel ERROR -Message "Failed to contact $($url): Bad request." ;  }
				401 { Write-Log  -LogLevel ERROR -Message "Failed to contact $($url): Unauthorized." ;  }
				403 { Write-Log  -LogLevel ERROR -Message "Failed to contact $($url): Forbidden." ;  }
				404 { Write-Log  -LogLevel ERROR -Message "Failed to contact $($url): Not found." ;  }
				407 { Write-Log  -LogLevel ERROR -Message "Failed to contact $($url): Proxy authentication required." ;  }
				502 { Write-Log  -LogLevel ERROR -Message "Failed to contact $($url): Bad gateway (likely proxy)." ;  }
				503 { Write-Log  -LogLevel ERROR -Message "Failed to contact $($url): Service unavailable (transient, try again)." ;  }
				504 { Write-Log  -LogLevel ERROR -Message "Failed to contact $($url): Gateway timeout (likely proxy)." ;  }
				default
				{
					Write-Log  -LogLevel ERROR -Message "OTHER: Failed to contact $($url)" 
					Write-Log  -LogLevel ERROR -Message "$($Result)"             
				}
			}
		}
		catch
		{
			Write-Log  -LogLevel ERROR -Message "Exception: Unable to contact $($url)" 
			Write-Log  -LogLevel ERROR -Message "$($_.Exception.Message)"
		}
		finally
		{
			If ($DebugLogging)
			{
				Write-Log  -LogLevel DEBUG -Message "Debug log entry for $($url)."
				Write-Log  -LogLevel DEBUG -Message $Result.StatusCode
				Write-Log  -LogLevel DEBUG -Message $Result.StatusDescription
				If ($Result.RawContent.Length -lt 400)
				{
					$DebugContent = $Result.RawContent -join ";"
					Write-Log  -LogLevel DEBUG -Message $DebugContent
				}
				Else
				{
					$DebugContent = $Result.RawContent -join ";"
					Write-Log  -LogLevel DEBUG -Message $DebugContent.Substring(0, 400)
				}
			}
		}
	} # End Foreach RequiredResourcesEndpoints

	# Optional Resources Endpoints tests
	Write-Log  -LogLevel INFO -Message "Testing Optional Resources Endpoints (Invoke-Webrequest)." 
	foreach ($url in $OptionalResourcesEndpoints)
	{
		try
		{
			$Result = Invoke-WebRequest -Uri $url -usebasicparsing -ea stop -wa silentlycontinue
			Switch ($Result.StatusCode)
			{
				200 { Write-Log  -LogLevel SUCCESS -Message "Successfully connected to $($url)."  }
				400 { Write-Log  -LogLevel ERROR -Message "Failed to contact $($url): Bad request." ;  }
				401 { Write-Log  -LogLevel ERROR -Message "Failed to contact $($url): Unauthorized." ;  }
				403 { Write-Log  -LogLevel ERROR -Message "Failed to contact $($url): Forbidden." ;  }
				404 { Write-Log  -LogLevel ERROR -Message "Failed to contact $($url): Not found." ;  }
				407 { Write-Log  -LogLevel ERROR -Message "Failed to contact $($url): Proxy authentication required." ;  }
				502 { Write-Log  -LogLevel ERROR -Message "Failed to contact $($url): Bad gateway (likely proxy)." ;  }
				503 { Write-Log  -LogLevel ERROR -Message "Failed to contact $($url): Service unavailable (transient, try again)." ;  }
				504 { Write-Log  -LogLevel ERROR -Message "Failed to contact $($url): Gateway timeout (likely proxy)." ;  }
				default
				{
					Write-Log  -LogLevel ERROR -Message "OTHER: Failed to contact $($url)" 
					Write-Log  -LogLevel ERROR -Message "$($Result)" 
				}
			}
		}
		catch
		{
			Write-Log  -LogLevel ERROR -Message "Exception: Unable to contact $($url)" 
			Write-Log  -LogLevel ERROR -Message "$($_.Exception.Message)"
		}
		finally
		{
			If ($DebugLogging)
			{
				Write-Log  -LogLevel DEBUG -Message "Debug log entry for $($url)."
				Write-Log  -LogLevel DEBUG -Message $Result.StatusCode
				Write-Log  -LogLevel DEBUG -Message $Result.StatusDescription
				If ($Result.RawContent.Length -lt 400)
				{
					$DebugContent = $Result.RawContent -join ";"
					Write-Log  -LogLevel DEBUG -Message $DebugContent
				}
				Else
				{
					$DebugContent = $Result.RawContent -join ";"
					Write-Log  -LogLevel DEBUG -Message $DebugContent.Substring(0, 400)
				}
			}
		}
	} # End Foreach RequiredResourcesEndpoints

	# Seamless SSO Endpoints
	Write-Log  -LogLevel INFO -Message "Testing Seamless SSO Endpoints (TCP:443)." 
	foreach ($url in $SeamlessSSOEndpoints)
	{
		try
		{
			[array]$ResourceAddresses = (Resolve-DnsName $url -ErrorAction stop).IP4Address
		}
		catch
		{
			Write-Log  -LogLevel ERROR -Message "Unable to resolve host $($url)." 
			Write-Log  -LogLevel ERROR -Message "$($_.Exception.Message)"
			Continue
		}

		foreach ($ip4 in $ResourceAddresses)
		{
			try
			{
				$Result = Test-NetConnection $ip4 -Port 443 -ea stop -wa silentlycontinue
				switch ($Result.TcpTestSucceeded)
				{
					true { Write-Log  -LogLevel SUCCESS -Message "TCP connection to $($url) [$($ip4)]:443 successful."  }
					false { Write-Log  -LogLevel ERROR -Message "TCP connection to $($url) [$($ip4)]:443 failed." ;  }
				}
			}
			catch
			{
				Write-Log  -LogLevel ERROR -Message "Error resolving or connecting to $($url) [$($ip4)]:443" 
				Write-Log  -LogLevel ERROR -Message "$($_.Exception.Message)"
			}
			finally
			{
				If ($DebugLogging)
				{
					Write-Log  -LogLevel DEBUG -Message "Debug log entry for $($url) [$($Result.RemoteAddress)]:443."
					Write-Log  -LogLevel DEBUG -Message "Remote endpoint: $($url)"
					Write-Log  -LogLevel DEBUG -Message "Remote port: $($Result.RemotePort)"
					Write-Log  -LogLevel DEBUG -Message "Interface Alias: $($Result.InterfaceAlias)"
					Write-Log  -LogLevel DEBUG -Message "Source Interface Address: $($Result.SourceAddress.IPAddress)"
					Write-Log  -LogLevel DEBUG -Message "Ping Succeeded: $($Result.PingSucceeded)"
					Write-Log  -LogLevel DEBUG -Message "Ping Reply Time (RTT) Status: $($Result.PingReplyDetails.Status)"
					Write-Log  -LogLevel DEBUG -Message "Ping Reply Time (RTT) RoundTripTime: $($Result.PingReplyDetails.RoundtripTime)"
					Write-Log  -LogLevel DEBUG -Message "TCPTestSucceeded: $($Result.TcpTestSucceeded)"
				}
			}
		}
	} # End Foreach Resources

	# Additional Resources tests
	If ($AdditionalResources)
	{
		Write-Log  -LogLevel INFO -Message "Testing Additional Resources Endpoints (Invoke-Webrequest)." 
		foreach ($url in $AdditionalResources)
		{
			if ($url -match "\:")
			{
				$Name = $url.Split(":")[0]
				try { [array]$Resources = (Resolve-DnsName $Name -ErrorAction stop).IP4Address }
				catch
				{
					Write-Log  -LogLevel ERROR -Message "Unable to resolve host $($Name)." 
					Write-Log  -LogLevel ERROR -Message "$($_.Exception.Message)"
					Continue
				}

				#[array]$Resources = (Resolve-DnsName $Name).Ip4Address
				$ResourcesPort = $url.Split(":")[1]
			}
			Else
			{
				$Name = $url
				try
				{
					[array]$Resources = (Resolve-DnsName $Name -ErrorAction stop).IP4Address
				}
				catch
				{
					Write-Log  -LogLevel ERROR -Message "Unable to resolve host $($url)." 
					Write-Log  -LogLevel ERROR -Message "$($_.Exception.Message)"
					Continue
				}

				#[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  -LogLevel SUCCESS -Message "TCP connection to $($Name) [$($ip4)]:$($ResourcesPort) successful."  }
						false
						{
							Write-Log  -LogLevel WARN -Message "TCP connection to $($Name) [$($ip4)]:$($ResourcesPort) failed." 
							If ($DebugLogging) { Write-Log  -LogLevel DEBUG -Message $($Result) }
						}
					}
				}
				catch
				{
					Write-Log  -LogLevel WARN -Message "Error resolving or connecting to $($Name) [$($ip4)]:$($ResourcesPort)" 
					Write-Log  -LogLevel WARN -Message "$($_.Exception.Message)"
				}
				finally
				{
					If ($DebugLogging)
					{
						Write-Log  -LogLevel DEBUG -Message "Debug log entry for $($Name) [$($Result.RemoteAddress)]:443."
						Write-Log  -LogLevel DEBUG -Message "Remote endpoint: $($Name)"
						Write-Log  -LogLevel DEBUG -Message "Remote port: $($Result.RemotePort)"
						Write-Log  -LogLevel DEBUG -Message "Interface Alias: $($Result.InterfaceAlias)"
						Write-Log  -LogLevel DEBUG -Message "Source Interface Address: $($Result.SourceAddress.IPAddress)"
						Write-Log  -LogLevel DEBUG -Message "Ping Succeeded: $($Result.PingSucceeded)"
						Write-Log  -LogLevel DEBUG -Message "Ping Reply Time (RTT) Status: $($Result.PingReplyDetails.Status)"
						Write-Log  -LogLevel DEBUG -Message "Ping Reply Time (RTT) RoundTripTime: $($Result.PingReplyDetails.RoundtripTime)"
						Write-Log  -LogLevel DEBUG -Message "TCPTestSucceeded: $($Result.TcpTestSucceeded)"
					}
				}
			} # End ForEach ip4
		} # End ForEach AdditionalResources
	} # End IF AdditionalResources

	Write-Log  -LogLevel INFO -Message "Finished Online Endpoints tests."
} # End Function OnlineEndPoints

function Network
{
	Write-Log  -LogLevel INFO -Message "Starting local network port tests." 
	Foreach ($Destination in $DCs)
	{
		foreach ($Port in $Ports)
		{
			Try
			{
				$Result = (Test-NetConnection -ComputerName $Destination -Port $Port -ea Stop -wa SilentlyContinue)
				Switch ($Result.TcpTestSucceeded)
				{
					True
					{
						Write-Log  -LogLevel SUCCESS -Message "TCP connection to $($Destination):$($Port) succeeded." 
					}
					False
					{
						Write-Log  -LogLevel ERROR -Message "TCP connection to $($Destination):$($Port) failed." 
						Write-Log  -LogLevel ERROR -Message "$Result"
					}
				} # End Switch
			}
			Catch
			{
				Write-Log  -LogLevel ERROR -Message "Exception: Error attempting TCP connection to $($Destination):$($Port)." 
				Write-Log  -LogLevel ERROR -Message "$($_.Exception.Message)"
			}
			Finally
			{
				If ($DebugLogging)
				{
					Write-Log  -LogLevel DEBUG -Message "Debug log entry for $($Destination) [$($Result.RemoteAddress)]:$($Port)."
					Write-Log  -LogLevel DEBUG -Message "Remote endpoint: $($Destination)"
					Write-Log  -LogLevel DEBUG -Message "Remote port: $($Result.RemotePort)"
					Write-Log  -LogLevel DEBUG -Message "Interface Alias: $($Result.InterfaceAlias)"
					Write-Log  -LogLevel DEBUG -Message "Source Interface Address: $($Result.SourceAddress.IPAddress)"
					Write-Log  -LogLevel DEBUG -Message "Ping Succeeded: $($Result.PingSucceeded)"
					Write-Log  -LogLevel DEBUG -Message "Ping Reply Time (RTT) Status: $($Result.PingReplyDetails.Status)"
					Write-Log  -LogLevel DEBUG -Message "Ping Reply Time (RTT) RoundTripTime: $($Result.PingReplyDetails.RoundtripTime)"
					Write-Log  -LogLevel DEBUG -Message "TCPTestSucceeded: $($Result.TcpTestSucceeded)"
				}
			}
		} # End Foreach Port in Ports
		foreach ($Port in $OptionalADPorts)
		{
			Try
			{
				$Result = (Test-NetConnection -ComputerName $Destination -Port $Port -ea Stop -wa SilentlyContinue)
				Switch ($Result.TcpTestSucceeded)
				{
					True
					{
						Write-Log  -LogLevel SUCCESS -Message "TCP connection to $($Destination):$($Port) succeeded." 
					}
					False
					{
						Write-Log  -LogLevel WARN -Message "TCP connection to $($Destination):$($Port) failed." 
						Write-Log  -LogLevel WARN -Message "$Result"
					}
				} # End Switch
			}
			Catch
			{
				Write-Log  -LogLevel ERROR -Message "Error attempting TCP connection to $($Destination):$($Port)." 
				Write-Log  -LogLevel ERROR -Message "$($_.Exception.Message)"    
			}
			Finally
			{
				If ($DebugLogging)
				{
					Write-Log  -LogLevel DEBUG -Message "Debug log entry for $($Destination) [$($Result.RemoteAddress)]:$($Port)."
					Write-Log  -LogLevel DEBUG -Message "Remote endpoint: $($Destination)"
					Write-Log  -LogLevel DEBUG -Message "Remote port: $($Result.RemotePort)"
					Write-Log  -LogLevel DEBUG -Message "Interface Alias: $($Result.InterfaceAlias)"
					Write-Log  -LogLevel DEBUG -Message "Source Interface Address: $($Result.SourceAddress.IPAddress)"
					Write-Log  -LogLevel DEBUG -Message "Ping Succeeded: $($Result.PingSucceeded)"
					Write-Log  -LogLevel DEBUG -Message "Ping Reply Time (RTT) Status: $($Result.PingReplyDetails.Status)"
					Write-Log  -LogLevel DEBUG -Message "Ping Reply Time (RTT) RoundTripTime: $($Result.PingReplyDetails.RoundtripTime)"
					Write-Log  -LogLevel DEBUG -Message "TCPTestSucceeded: $($Result.TcpTestSucceeded)"
				}
			}
		} # End Foreach Port in OptionalADPorts
	} # End Foreach Destination
	Write-Log  -LogLevel INFO -Message "Finished local network port tests."
} # End Function Network

# Test local DNS resolution for domain controllers
function Dns
{
	Write-Log  -LogLevel INFO -Message "Starting local DNS resolution tests."
	# Attempt DNS Resolution
	$DnsTargets = @("_ldap._tcp.$ForestFQDN") + $DCs
	Foreach ($HostName in $DnsTargets)
	{
		Try
		{
		$DnsResult = (Resolve-DnsName -Type ANY $HostName -ea Stop -wa SilentlyContinue)
		If ($DnsResult.Name)
		{
			Write-Log  -LogLevel SUCCESS -Message "Successfully resolved $($HostName)." 
		}
		Else
		{
			Write-Log  -LogLevel ERROR -Message "Error attempting DNS resolution for $($HostName)." 
			Write-Log  -LogLevel ERROR -Message $DnsResult
		}
		}
		Catch
		{
			Write-Log  -LogLevel ERROR -Message "Exception: Error attempting DNS resolution for $($HostName)." 
			Write-Log  -LogLevel ERROR -Message "$($_.Exception.Message)"
		}
		Finally
		{
			If ($DebugLogging)
			{
				Write-Log  -LogLevel DEBUG -Message "Debug log entry for $($HostName)."
				Write-Log  -LogLevel DEBUG -Message $DnsResult
			}
		}
	}
	Write-Log  -LogLevel INFO -Message "Finished local DNS resolution tests."
} # End function Dns

If (!$DCs)
{
	Write-Log  -LogLevel ERROR -Message "If testing on-premises networking, you must specify at least one on-premises domain controller." 
	break
}

If (!$ForestFQDN)
{
	Write-Log  -LogLevel ERROR -Message "Local Dns resolution, you must specify for Active Directory Forest FQDN." 
	break
}

Dns
Network
OnlineEndPoints

