#$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


# SIG # Begin signature block
# MIIlTAYJKoZIhvcNAQcCoIIlPTCCJTkCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUJMK3ctQEHoqXocrVln+RjKd4
# hiqggh5yMIIFMDCCBBigAwIBAgIQBAkYG1/Vu2Z1U0O1b5VQCDANBgkqhkiG9w0B
# AQsFADBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYD
# VQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVk
# IElEIFJvb3QgQ0EwHhcNMTMxMDIyMTIwMDAwWhcNMjgxMDIyMTIwMDAwWjByMQsw
# CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu
# ZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFzc3VyZWQgSUQg
# Q29kZSBTaWduaW5nIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
# +NOzHH8OEa9ndwfTCzFJGc/Q+0WZsTrbRPV/5aid2zLXcep2nQUut4/6kkPApfmJ
# 1DcZ17aq8JyGpdglrA55KDp+6dFn08b7KSfH03sjlOSRI5aQd4L5oYQjZhJUM1B0
# sSgmuyRpwsJS8hRniolF1C2ho+mILCCVrhxKhwjfDPXiTWAYvqrEsq5wMWYzcT6s
# cKKrzn/pfMuSoeU7MRzP6vIK5Fe7SrXpdOYr/mzLfnQ5Ng2Q7+S1TqSp6moKq4Tz
# rGdOtcT3jNEgJSPrCGQ+UpbB8g8S9MWOD8Gi6CxR93O8vYWxYoNzQYIH5DiLanMg
# 0A9kczyen6Yzqf0Z3yWT0QIDAQABo4IBzTCCAckwEgYDVR0TAQH/BAgwBgEB/wIB
# ADAOBgNVHQ8BAf8EBAMCAYYwEwYDVR0lBAwwCgYIKwYBBQUHAwMweQYIKwYBBQUH
# AQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYI
# KwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFz
# c3VyZWRJRFJvb3RDQS5jcnQwgYEGA1UdHwR6MHgwOqA4oDaGNGh0dHA6Ly9jcmw0
# LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwOqA4oDaG
# NGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RD
# QS5jcmwwTwYDVR0gBEgwRjA4BgpghkgBhv1sAAIEMCowKAYIKwYBBQUHAgEWHGh0
# dHBzOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwCgYIYIZIAYb9bAMwHQYDVR0OBBYE
# FFrEuXsqCqOl6nEDwGD5LfZldQ5YMB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6en
# IZ3zbcgPMA0GCSqGSIb3DQEBCwUAA4IBAQA+7A1aJLPzItEVyCx8JSl2qB1dHC06
# GsTvMGHXfgtg/cM9D8Svi/3vKt8gVTew4fbRknUPUbRupY5a4l4kgU4QpO4/cY5j
# DhNLrddfRHnzNhQGivecRk5c/5CxGwcOkRX7uq+1UcKNJK4kxscnKqEpKBo6cSgC
# PC6Ro8AlEeKcFEehemhor5unXCBc2XGxDI+7qPjFEmifz0DLQESlE/DmZAwlCEIy
# sjaKJAL+L3J+HNdJRZboWR3p+nRka7LrZkPas7CM1ekN3fYBIM6ZMWM9CBoYs4Gb
# T8aTEAb8B4H6i9r5gkn3Ym6hU/oSlBiFLpKR6mhsRDKyZqHnGKSaZFHvMIIFjTCC
# BHWgAwIBAgIQDpsYjvnQLefv21DiCEAYWjANBgkqhkiG9w0BAQwFADBlMQswCQYD
# VQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGln
# aWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0Ew
# HhcNMjIwODAxMDAwMDAwWhcNMzExMTA5MjM1OTU5WjBiMQswCQYDVQQGEwJVUzEV
# MBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29t
# MSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0GCSqGSIb3
# DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3yithZ
# wuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1Ifxp4V
# pX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDVySAd
# YyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfISKhmV1efVFiODCu3
# T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jHtrHEtWoYOAMQjdjU
# N6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6MUSaM0C/CNda
# SaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCiEhtm
# mnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADMfRyV
# w4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QYuKZ3
# AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXKchYi
# Cd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4KJpn15GkvmB0t9dmp
# sh3lGwIDAQABo4IBOjCCATYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU7Nfj
# gtJxXWRM3y5nP+e6mK4cD08wHwYDVR0jBBgwFoAUReuir/SSy4IxLVGLp6chnfNt
# yA8wDgYDVR0PAQH/BAQDAgGGMHkGCCsGAQUFBwEBBG0wazAkBggrBgEFBQcwAYYY
# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEMGCCsGAQUFBzAChjdodHRwOi8vY2Fj
# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3J0MEUG
# A1UdHwQ+MDwwOqA4oDaGNGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2Vy
# dEFzc3VyZWRJRFJvb3RDQS5jcmwwEQYDVR0gBAowCDAGBgRVHSAAMA0GCSqGSIb3
# DQEBDAUAA4IBAQBwoL9DXFXnOF+go3QbPbYW1/e/Vwe9mqyhhyzshV6pGrsi+Ica
# aVQi7aSId229GhT0E0p6Ly23OO/0/4C5+KH38nLeJLxSA8hO0Cre+i1Wz/n096ww
# epqLsl7Uz9FDRJtDIeuWcqFItJnLnU+nBgMTdydE1Od/6Fmo8L8vC6bp8jQ87PcD
# x4eo0kxAGTVGamlUsLihVo7spNU96LHc/RzY9HdaXFSMb++hUD38dglohJ9vytsg
# jTVgHAIDyyCwrFigDkBjxZgiwbJZ9VVrzyerbHbObyMt9H5xaiNrIv8SuFQtJ37Y
# OtnwtoeW/VvRXKwYw02fc7cBqZ9Xql4o4rmUMIIGMzCCBRugAwIBAgIQCJp0nrgt
# w+wn6mXq2/g1MTANBgkqhkiG9w0BAQsFADByMQswCQYDVQQGEwJVUzEVMBMGA1UE
# ChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMTEwLwYD
# VQQDEyhEaWdpQ2VydCBTSEEyIEFzc3VyZWQgSUQgQ29kZSBTaWduaW5nIENBMB4X
# DTIxMDUyNTAwMDAwMFoXDTI0MDUyOTIzNTk1OVowcTELMAkGA1UEBhMCVVMxEzAR
# BgNVBAgTCk5ldyBKZXJzZXkxDzANBgNVBAcTBklzZWxpbjEdMBsGA1UEChMUZUcg
# SW5ub3ZhdGlvbnMsIEluYy4xHTAbBgNVBAMTFGVHIElubm92YXRpb25zLCBJbmMu
# MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxGUJ0prjbfxcmgZGjd7J
# DxOs7sySHvaQ3wGR2KbD3aZageSE+kG0tKkEsQ4na8bnNViY/zF4Pbo0ZkYtMzJI
# k0AZXDTMvXr/mEvmKxCbiJFTCpKkmZ4sa5BZfO7igIvcNSa0zII2a5jiQJFy85j+
# f9I4EgTo9OcdVeINXhgQ2xZ6TjEK+pbhqmVXvr8DB26JclOaed0L5Vs7+CbGzZuK
# ifgxL2i8d5FDzkhZSZfdCtGtEvE5pLesXSWfpzUddvCNRisIPGB7mg+Rln2XvUZy
# yaRURtlx11pYfJ/KNYAkUCL23rnh38/maxtSvdeioYBj4xLyt7poQBG+b0uzCjgR
# ADe3/k0NtiVwJIo0ZEsUyDANp7JIIjMePzsOMYzfMPyi8clAwfBYc2XhSuUcX3yK
# f4kpRFmClYfK5LstGQRNbONjpsCBTEgZuwEfkdOU0rmaMywZFVLHEpmpYZePz7M3
# VhN+aV56kR/efo8eD81E5VDQqmJiGWKq/s9jhbkYH7g2uhL2MXb/uRshVDSOkFRv
# IID/l+yDJogDBpae3x2ov5YvjY8Zo8RF0tXvyS0rfsDvANZw7mptzHn0Blmw5lug
# 3F2CVJpNbzb1HmAH6k0yFHmWYCoUANbBB0YR/k4JvgY2byttF7cQPJyl7UI9Cu4K
# TAg7ROmGkwdLhmXoPCBco0ECAwEAAaOCAcQwggHAMB8GA1UdIwQYMBaAFFrEuXsq
# CqOl6nEDwGD5LfZldQ5YMB0GA1UdDgQWBBT5yESaz9gTSS8aBNa1r34MMA42SjAO
# BgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwdwYDVR0fBHAwbjA1
# oDOgMYYvaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL3NoYTItYXNzdXJlZC1jcy1n
# MS5jcmwwNaAzoDGGL2h0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9zaGEyLWFzc3Vy
# ZWQtY3MtZzEuY3JsMEsGA1UdIAREMEIwNgYJYIZIAYb9bAMBMCkwJwYIKwYBBQUH
# AgEWG2h0dHA6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAIBgZngQwBBAEwgYQGCCsG
# AQUFBwEBBHgwdjAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29t
# ME4GCCsGAQUFBzAChkJodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNl
# cnRTSEEyQXNzdXJlZElEQ29kZVNpZ25pbmdDQS5jcnQwDAYDVR0TAQH/BAIwADAN
# BgkqhkiG9w0BAQsFAAOCAQEAlAjncFPvLKywT/4DECG7bHbtiVl+uumfRj2YrFuC
# hsgkv1PmQpgfypxC3g+ErV9yRV8+XXSCwOaKJ3v6RLnfphLhEwYc2+0Qs/Nlib5N
# AxDGuIczAIeXOc5kRRpvFsQ2XSNtM7XL1tLDm6p/VG7BoUAyqRXsMPdWbTkN/9nd
# CmGSsqcxjG2ud8O6Vhte9J5LaHBVk3lIZAMtH6ACdo5QTrM49nbIU8QGuRYNXZKR
# LAUu6IgD6WJKMVfZXWlyfD8dZ2r3ej6Q1uAO/Nbtd397T+BVQrDWMOG8+GeRiJwo
# evxbIWh0SenZOUrAq9vTJaSvFMSvctkJm/oxLUcUdEGS3zCCBq4wggSWoAMCAQIC
# EAc2N7ckVHzYR6z9KGYqXlswDQYJKoZIhvcNAQELBQAwYjELMAkGA1UEBhMCVVMx
# FTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNv
# bTEhMB8GA1UEAxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290IEc0MB4XDTIyMDMyMzAw
# MDAwMFoXDTM3MDMyMjIzNTk1OVowYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRp
# Z2lDZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQw
# OTYgU0hBMjU2IFRpbWVTdGFtcGluZyBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIP
# ADCCAgoCggIBAMaGNQZJs8E9cklRVcclA8TykTepl1Gh1tKD0Z5Mom2gsMyD+Vr2
# EaFEFUJfpIjzaPp985yJC3+dH54PMx9QEwsmc5Zt+FeoAn39Q7SE2hHxc7Gz7iuA
# hIoiGN/r2j3EF3+rGSs+QtxnjupRPfDWVtTnKC3r07G1decfBmWNlCnT2exp39mQ
# h0YAe9tEQYncfGpXevA3eZ9drMvohGS0UvJ2R/dhgxndX7RUCyFobjchu0CsX7Le
# Sn3O9TkSZ+8OpWNs5KbFHc02DVzV5huowWR0QKfAcsW6Th+xtVhNef7Xj3OTrCw5
# 4qVI1vCwMROpVymWJy71h6aPTnYVVSZwmCZ/oBpHIEPjQ2OAe3VuJyWQmDo4EbP2
# 9p7mO1vsgd4iFNmCKseSv6De4z6ic/rnH1pslPJSlRErWHRAKKtzQ87fSqEcazjF
# KfPKqpZzQmiftkaznTqj1QPgv/CiPMpC3BhIfxQ0z9JMq++bPf4OuGQq+nUoJEHt
# Qr8FnGZJUlD0UfM2SU2LINIsVzV5K6jzRWC8I41Y99xh3pP+OcD5sjClTNfpmEpY
# PtMDiP6zj9NeS3YSUZPJjAw7W4oiqMEmCPkUEBIDfV8ju2TjY+Cm4T72wnSyPx4J
# duyrXUZ14mCjWAkBKAAOhFTuzuldyF4wEr1GnrXTdrnSDmuZDNIztM2xAgMBAAGj
# ggFdMIIBWTASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBS6FtltTYUvcyl2
# mi91jGogj57IbzAfBgNVHSMEGDAWgBTs1+OC0nFdZEzfLmc/57qYrhwPTzAOBgNV
# HQ8BAf8EBAMCAYYwEwYDVR0lBAwwCgYIKwYBBQUHAwgwdwYIKwYBBQUHAQEEazBp
# MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQQYIKwYBBQUH
# MAKGNWh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRS
# b290RzQuY3J0MEMGA1UdHwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0
# LmNvbS9EaWdpQ2VydFRydXN0ZWRSb290RzQuY3JsMCAGA1UdIAQZMBcwCAYGZ4EM
# AQQCMAsGCWCGSAGG/WwHATANBgkqhkiG9w0BAQsFAAOCAgEAfVmOwJO2b5ipRCIB
# fmbW2CFC4bAYLhBNE88wU86/GPvHUF3iSyn7cIoNqilp/GnBzx0H6T5gyNgL5Vxb
# 122H+oQgJTQxZ822EpZvxFBMYh0MCIKoFr2pVs8Vc40BIiXOlWk/R3f7cnQU1/+r
# T4osequFzUNf7WC2qk+RZp4snuCKrOX9jLxkJodskr2dfNBwCnzvqLx1T7pa96kQ
# sl3p/yhUifDVinF2ZdrM8HKjI/rAJ4JErpknG6skHibBt94q6/aesXmZgaNWhqsK
# RcnfxI2g55j7+6adcq/Ex8HBanHZxhOACcS2n82HhyS7T6NJuXdmkfFynOlLAlKn
# N36TU6w7HQhJD5TNOXrd/yVjmScsPT9rp/Fmw0HNT7ZAmyEhQNC3EyTN3B14OuSe
# reU0cZLXJmvkOHOrpgFPvT87eK1MrfvElXvtCl8zOYdBeHo46Zzh3SP9HSjTx/no
# 8Zhf+yvYfvJGnXUsHicsJttvFXseGYs2uJPU5vIXmVnKcPA3v5gA3yAWTyf7YGcW
# oWa63VXAOimGsJigK+2VQbc61RWYMbRiCQ8KvYHZE/6/pNHzV9m8BPqC3jLfBInw
# AM1dwvnQI38AC+R2AibZ8GV2QqYphwlHK+Z/GqSFD/yYlvZVVCsfgPrA8g4r5db7
# qS9EFUrnEw4d2zc4GqEr9u3WfPwwggbAMIIEqKADAgECAhAMTWlyS5T6PCpKPSkH
# gD1aMA0GCSqGSIb3DQEBCwUAMGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdp
# Q2VydCwgSW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2
# IFNIQTI1NiBUaW1lU3RhbXBpbmcgQ0EwHhcNMjIwOTIxMDAwMDAwWhcNMzMxMTIx
# MjM1OTU5WjBGMQswCQYDVQQGEwJVUzERMA8GA1UEChMIRGlnaUNlcnQxJDAiBgNV
# BAMTG0RpZ2lDZXJ0IFRpbWVzdGFtcCAyMDIyIC0gMjCCAiIwDQYJKoZIhvcNAQEB
# BQADggIPADCCAgoCggIBAM/spSY6xqnya7uNwQ2a26HoFIV0MxomrNAcVR4eNm28
# klUMYfSdCXc9FZYIL2tkpP0GgxbXkZI4HDEClvtysZc6Va8z7GGK6aYo25BjXL2J
# U+A6LYyHQq4mpOS7eHi5ehbhVsbAumRTuyoW51BIu4hpDIjG8b7gL307scpTjUCD
# HufLckkoHkyAHoVW54Xt8mG8qjoHffarbuVm3eJc9S/tjdRNlYRo44DLannR0hCR
# RinrPibytIzNTLlmyLuqUDgN5YyUXRlav/V7QG5vFqianJVHhoV5PgxeZowaCiS+
# nKrSnLb3T254xCg/oxwPUAY3ugjZNaa1Htp4WB056PhMkRCWfk3h3cKtpX74LRsf
# 7CtGGKMZ9jn39cFPcS6JAxGiS7uYv/pP5Hs27wZE5FX/NurlfDHn88JSxOYWe1p+
# pSVz28BqmSEtY+VZ9U0vkB8nt9KrFOU4ZodRCGv7U0M50GT6Vs/g9ArmFG1keLuY
# /ZTDcyHzL8IuINeBrNPxB9ThvdldS24xlCmL5kGkZZTAWOXlLimQprdhZPrZIGwY
# UWC6poEPCSVT8b876asHDmoHOWIZydaFfxPZjXnPYsXs4Xu5zGcTB5rBeO3GiMiw
# bjJ5xwtZg43G7vUsfHuOy2SJ8bHEuOdTXl9V0n0ZKVkDTvpd6kVzHIR+187i1Dp3
# AgMBAAGjggGLMIIBhzAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAWBgNV
# HSUBAf8EDDAKBggrBgEFBQcDCDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgB
# hv1sBwEwHwYDVR0jBBgwFoAUuhbZbU2FL3MpdpovdYxqII+eyG8wHQYDVR0OBBYE
# FGKK3tBh/I8xFO2XC809KpQU31KcMFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9j
# cmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZU
# aW1lU3RhbXBpbmdDQS5jcmwwgZAGCCsGAQUFBwEBBIGDMIGAMCQGCCsGAQUFBzAB
# hhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wWAYIKwYBBQUHMAKGTGh0dHA6Ly9j
# YWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEy
# NTZUaW1lU3RhbXBpbmdDQS5jcnQwDQYJKoZIhvcNAQELBQADggIBAFWqKhrzRvN4
# Vzcw/HXjT9aFI/H8+ZU5myXm93KKmMN31GT8Ffs2wklRLHiIY1UJRjkA/GnUypsp
# +6M/wMkAmxMdsJiJ3HjyzXyFzVOdr2LiYWajFCpFh0qYQitQ/Bu1nggwCfrkLdcJ
# iXn5CeaIzn0buGqim8FTYAnoo7id160fHLjsmEHw9g6A++T/350Qp+sAul9Kjxo6
# UrTqvwlJFTU2WZoPVNKyG39+XgmtdlSKdG3K0gVnK3br/5iyJpU4GYhEFOUKWaJr
# 5yI+RCHSPxzAm+18SLLYkgyRTzxmlK9dAlPrnuKe5NMfhgFknADC6Vp0dQ094XmI
# vxwBl8kZI4DXNlpflhaxYwzGRkA7zl011Fk+Q5oYrsPJy8P7mxNfarXH4PMFw1nf
# J2Ir3kHJU7n/NBBn9iYymHv+XEKUgZSCnawKi8ZLFUrTmJBFYDOA4CPe+AOk9kVH
# 5c64A0JH6EE2cXet/aLol3ROLtoeHYxayB6a1cLwxiKoT5u92ByaUcQvmvZfpyeX
# upYuhVfAYOd4Vn9q78KVmksRAsiCnMkaBXy6cbVOepls9Oie1FqYyJ+/jbsYXEP1
# 0Cro4mLueATbvdH7WwqocH7wl4R44wgDXUcsY6glOJcB0j862uXl9uab3H4szP8X
# TE0AotjWAQ64i+7m4HJViSwnGWH2dwGMMYIGRDCCBkACAQEwgYYwcjELMAkGA1UE
# BhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2lj
# ZXJ0LmNvbTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVkIElEIENvZGUg
# U2lnbmluZyBDQQIQCJp0nrgtw+wn6mXq2/g1MTAJBgUrDgMCGgUAoHAwEAYKKwYB
# BAGCNwIBDDECMAAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGC
# NwIBCzEOMAwGCisGAQQBgjcCARUwIwYJKoZIhvcNAQkEMRYEFFRORqpZ3U4wXrwR
# ss9fee2wRClDMA0GCSqGSIb3DQEBAQUABIICAAZFmcvHVRi6z/NXQ1SjhBIORfx7
# RiumaEbyUHuzXiFdY1KXgPIbITvdrlFdGimpUU+WZmLAbmHxJoUYKktPE6Ksmx92
# mOxRbWm4Ok6qBCutYwGUOc8NE2LCJHzD4WvSsGWMy2SA3J8Tqdhelkv4RFO3k56b
# onFZQQGKMnHM55nqATLpGcgXz4aKak+tngu7QyQuTtlEVsfMlPp66F7aBW11ZNzF
# 4n981MFB8BkO6rPU61Va3RusqWMoIilxsZpQKB/Lfxo2nm+VNx4Qzx0FQ7n1Z8Qv
# vZLbKTEq7hIR2p+fKzkD6HhaRNZ8PdJsU6VHML0iZvALk1e63HAy3AtlsnXXhp+s
# RA2nVA6tsEmk9tq60AgxsokLn3oA68izOSCqDmigc9o6qmtL0+phd3w6XNGUDaPf
# awe+9yAFhAD31ArhsLGDX8nfeg2Mif69lBrlOSfqKtGBhwJ9QnFnasXSHflVnPbd
# PpVAPNkK9Gwm0KwHlKLiTe3TmyF+LLxJh3Gvhh3X5pCBz9vSq43f+npBHk2ResWe
# ucyk++WihaTVHXxo99E2FOkw+BlvWd0zxRgzgQk2ypfei154JmDN7LKdmBi/wjY6
# M0ou0yB4POkNrny4nza7ksnRhQ+0DSRFXo3OwahG1OFTxV0ndyVzbQgaiE5uBUU7
# dNCVfm+hx9pYi293oYIDIDCCAxwGCSqGSIb3DQEJBjGCAw0wggMJAgEBMHcwYzEL
# MAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYDVQQDEzJE
# aWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFtcGluZyBD
# QQIQDE1pckuU+jwqSj0pB4A9WjANBglghkgBZQMEAgEFAKBpMBgGCSqGSIb3DQEJ
# AzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTIzMDMwODA0NTM1N1owLwYJ
# KoZIhvcNAQkEMSIEINpjVkC5CC4IoF/s3CaFoVgI100NNhS63UGTWri37h2XMA0G
# CSqGSIb3DQEBAQUABIICAEuRIQvo3zfCmGpamIq8BFgF8RkQv4wdthyMrWlSFOXF
# dOXObTTfpnwMIySnBQyyWsKQKHZkoRE3DmyWYDaxcegSx9O6tTAActJFyKol2U+i
# amJe2ZsJ4yzD5E6EoJl+7teXSYYCv64b62nehaLggPpkfFxuSS7ZZn+2cNTipjh1
# 9HYrQhzSxhoxv4DTTCwQSqzT1nAhSbhQR1DfOt3SW5Vq5LCB68X4egZ1JtlOS52U
# rPcvEo9FYDHcX5XyJHhFVHMD9PDjOsq/YWM8CoqXBpgNovZoNsTMnG6PJ6fatVl9
# +npzUFLtzbJ+YBuzQu4nYRio3rNLFrX4MDRCwYftvAqKx2OqZ+Ggi3ZcL5SawUOZ
# BWWLKCakqodR/867TXof7MoubMqrHmKhFAgQYFt3zEcxFsZAS06SkBy8/7swW0R3
# q0hssP2UV+IicDYVXRD6tSfPnCJTP7i2V3XyMJ2fnT3hUxKfEvcgJP0FcXRrhzTt
# IDF+BgnV1DvlIPjglkyz+vkUDTvfpkIZ7A8dD9byuUbVLM1OgbXa2DAXCuAazdMw
# jYRfymlvZy3jCOotfbiQqCgdjef9HziK+/TzlJOqiE6eQ1gfF2aMVMgYbgCdPxVl
# Z/sKmSVPf+reNjtg6qVMDn2t+FlSNkC9CdFIwH6VKEH+TFXYrYUCLl+LOjC9k0cM
# SIG # End signature block
