Skip to content

Commit 00b8e70

Browse files
committed
Release 0.1.10 -- Added Azure Connectivity Tester
1 parent 7d6b15c commit 00b8e70

16 files changed

+672
-16
lines changed
Binary file not shown.
Binary file not shown.
17.6 KB
Binary file not shown.
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
function Invoke-AnalyzeAzureConnectivity {
2+
<#
3+
.Synopsis
4+
Analyzes the connectifity to O365 and Azure Endpoints.
5+
6+
.Description
7+
Analyzes the connectifity to O365 and Azure Endpoints according to https://docs.microsoft.com/en-us/office365/enterprise/urls-and-ip-address-ranges.
8+
9+
Returns array of Messages with four properties:
10+
11+
- Testname: Name of the Tets
12+
- Type: Information, Warning or Error
13+
- Issue: Description of the issue
14+
- Possible Cause: Tips on how to solve the issue.
15+
16+
.Example
17+
# Displays a deep analyisis of the currently found issues in the system.
18+
Invoke-AnalyzeAzureConnectivity
19+
20+
#>
21+
[alias("Invoke-AnalyzeO365Connectivity")]
22+
[CmdletBinding()]
23+
param(
24+
[ValidateSet("Common","Exchange","Skype","SharePoint","All")]
25+
[String]
26+
$UrlSet = "Common",
27+
[Switch]
28+
$OnlyRequired
29+
)
30+
31+
Write-Verbose "Conenctivity Tests to Azure Endpoints in $UrlSet category, which are Required=$OnlyRequired."
32+
$data = New-Object System.Collections.Generic.List[PSCustomObject]
33+
$possibleErrors = @()
34+
$results = New-Object System.Collections.Generic.List[pscustomobject]
35+
Write-Progress -Activity "Connectivity Tests" -status "Load TestUrls" -percentComplete 0
36+
37+
$EndpointsObjs = Get-AzureO365UrlEndpoint -Path ((Get-Item $PSScriptRoot).Parent.FullName)
38+
$EndpointsObjs = $EndpointsObjs | Where-Object { ($_.serviceArea -eq $UrlSet -or $UrlSet -eq "All") -and ($OnlyRequired -eq $false -or $_.required -eq $true)}
39+
Write-Progress -Activity "Connectivity Tests" -status "Load TestUrls finisehed" -percentComplete 100
40+
Write-Verbose "Found $($EndpointsObjs.length) endpoints to check"
41+
$j = 0
42+
foreach($EndpointsObj in $EndpointsObjs){
43+
Write-Progress -Activity "Connectivity Tests" -status "Building urls for $($EndpointsObj.serviceArea) with id $($EndpointsObj.id)" -percentComplete ($j / $EndpointsObjs.length*100)
44+
if($null -ne $EndpointsObj.tcpPorts){
45+
Add-Member -InputObject $EndpointsObj -MemberType NoteProperty -Name tcpPorts -Value "443"
46+
}
47+
foreach($Port in $EndpointsObj.tcpPorts.Split(',')){
48+
switch ($Port) {
49+
80 {$Protocol = "http://"; $UsePort = "";$TestType="HTTP"; break}
50+
443 {$Protocol = "https://"; $UsePort = "";$TestType="HTTP"; break}
51+
default {$Protocol = ""; $UsePort = $Port;$TestType="TCP"; break}
52+
}
53+
if($EndpointsObj.PSObject.Properties.Name -match "notes"){
54+
$Notes = " - " + $EndpointsObj.notes
55+
} else {
56+
$Notes = ""
57+
}
58+
foreach($url in $EndpointsObj.urls){
59+
if($TestType -eq "HTTP"){
60+
$ExpectedResult = Get-AzureEndpointExpectedResult -TestType $TestType -Url ($Protocol + $url) -Path ((Get-Item $PSScriptRoot).Parent.FullName)
61+
} else {
62+
$ExpectedResult = Get-AzureEndpointExpectedResult -TestType $TestType -Url ($url + ":" + $UsePort) -Path ((Get-Item $PSScriptRoot).Parent.FullName)
63+
}
64+
if($url -notmatch "\*"){
65+
$data.Add([PSCustomObject]@{ TestType = $TestType; TestUrl = $url; UsePort = $UsePort; Protocol = $Protocol; UrlPattern = $url; ExpectedStatusCode = $ExpectedResult.ActualStatusCode; Description = "$($EndpointsObj.serviceAreaDisplayName)$Notes"; PerformBluecoatLookup=$false; IgnoreCertificateValidationErrors=$ExpectedResult.HasError; Blocked=$ExpectedResult.Blocked; Verbose=$false })
66+
} else {
67+
$staticUrls = Get-UrlWildCardLookup -Url $url -Path ((Get-Item $PSScriptRoot).Parent.FullName)
68+
if($staticUrls){
69+
foreach($staticUrl in $staticUrls){
70+
$data.Add([PSCustomObject]@{ TestType = $TestType; TestUrl = $staticUrl; UsePort = $UsePort; Protocol = $Protocol; UrlPattern = $url; ExpectedStatusCode = $ExpectedResult.ActualStatusCode; Description = "$($EndpointsObj.serviceAreaDisplayName)$Notes"; PerformBluecoatLookup=$false; IgnoreCertificateValidationErrors=$ExpectedResult.HasError; Blocked=$ExpectedResult.Blocked; Verbose=$false })
71+
}
72+
} else {
73+
74+
$possibleErrors += New-AnalyzeResult -TestName "Connectivity" -Type "Warning" -Issue "Could not check connectivity to $url and Port $Port because no static url for this wildcard url was found." -PossibleCause $Cause
75+
}
76+
}
77+
}
78+
<#if($EndpointsObj.PSObject.Properties.Name -match "ips"){
79+
foreach($ip in $EndpointsObj.ips){
80+
$firstip = $ip.Split("/")[0]
81+
$data.Add(@{ TestUrl = ($Protocol + $firstip + $UsePort); UrlPattern = ($Protocol + $firstip + $UsePort); ExpectedStatusCode = 403; Description = "$($EndpointsObj.serviceAreaDisplayName) - $Notes - Need communication $Protocol to $ip"; PerformBluecoatLookup=$false; Verbose=$false })
82+
}
83+
}#>
84+
}
85+
}
86+
87+
$possibleErrors = $possibleErrors | Group-Object -Property @("Type", "Issue") | ForEach-Object{ $_.Group | Select-Object * -First 1}
88+
$i = 1
89+
$dataObjs = $data | Group-Object -Property @("TestUrl","TestType","UsePort") | ForEach-Object{ $_.Group | Select-Object * -First 1}
90+
ForEach($dataObj in $dataObjs) {
91+
Write-Progress -Activity "Connectivity Tests" -status "Processing $($d.TestUrl)" -percentComplete ($i / $dataObjs.count*100)
92+
if($dataObj.TestType -eq "HTTP"){
93+
$connectivity = Get-HttpConnectivity -TestUrl ($dataObj.Protocol + $dataObj.TestUrl) -Method "GET" -UrlPattern ($dataObj.Protocol + $dataObj.UrlPattern) -ExpectedStatusCode $dataObj.ExpectedStatusCode -Description $dataObj.Description -PerformBluecoatLookup $dataObj.PerformBluecoatLookup -IgnoreCertificateValidationErrors:$dataObj.IgnoreCertificateValidationErrors
94+
} else {
95+
$connectivity = Get-TcpConnectivity -TestHostname $dataObj.TestUrl -TestPort $dataObj.UsePort -HostnamePattern ($dataObj.UrlPattern + ":" + $dataObj.UsePort) -ExpectedStatusCode $dataObj.ExpectedStatusCode -Description $dataObj.Description
96+
}
97+
$results.Add($connectivity)
98+
if ($connectivity.Blocked -eq $true -and $dataObj.Blocked -eq $false) {
99+
$possibleErrors += New-AnalyzeResult -TestName "Connectivity" -Type "Error" -Issue "Connection blocked `n $($connectivity)" -PossibleCause "Firewall is blocking connection to '$($connectivity.UnblockUrl)'."
100+
}
101+
if ($connectivity.Resolved -eq $false) {
102+
$possibleErrors += New-AnalyzeResult -TestName "Connectivity" -Type "Error" -Issue "DNS name not resolved `n $($connectivity)" -PossibleCause "DNS server not correctly configured."
103+
}
104+
if ($connectivity.ActualStatusCode -ne $connectivity.ExpectedStatusCode) {
105+
if($connectivity.ActualStatusCode -eq 407){
106+
$Cause = "Keep in mind that the proxy has to be set in WinHTTP.`nWindows 1709 and newer: Set the proxy by using netsh or WPAD. --> https://docs.microsoft.com/en-us/windows/desktop/WinHttp/winhttp-autoproxy-support `nWindows 1709 and older: Set the proxy by using 'netsh winhttp set proxy ?' --> https://blogs.technet.microsoft.com/netgeeks/2018/06/19/winhttp-proxy-settings-deployed-by-gpo/ "
107+
} else {
108+
$Cause = "Interfering Proxy server can change HTTP status codes."
109+
}
110+
$possibleErrors += New-AnalyzeResult -TestName "Connectivity" -Type "Error" -Issue "Returned Status code '$($connectivity.ActualStatusCode)' is not expected '$($connectivity.ExpectedStatusCode)'`n $($connectivity)" -PossibleCause $Cause
111+
}
112+
if ($null -ne $connectivity.ServerCertificate -and $connectivity.ServerCertificate.HasError -and -not $dataObj.IgnoreCertificateValidationErrors) {
113+
$possibleErrors += New-AnalyzeResult -TestName "Connectivity" -Type "Error" -Issue "Certificate Error when connecting to $($connectivity.TestUrl)`n $(($connectivity.ServerCertificate))" -PossibleCause "Interfering Proxy server can change Certificate or not the Root Certificate is not trusted."
114+
}
115+
$i += 1
116+
}
117+
Write-Progress -Completed -Activity "Connectivity Tests"
118+
119+
# No errors detected, return success message
120+
if ($possibleErrors.Count -eq 0) {
121+
$possibleErrors += New-AnalyzeResult -TestName "All" -Type Information -Issue "All tests went through successfully." -PossibleCause ""
122+
}
123+
124+
return $possibleErrors
125+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
function Get-AzureEndpointExpectedResult{
2+
<#
3+
.Synopsis
4+
Returns the expected result and SSL error for a specific endpoint.
5+
6+
.Description
7+
Returns the expected result and SSL error for a specific endpoint.
8+
9+
.Example
10+
Get-AzureEndpointExpectedResult -Url "http://*.contoso.com" -Path "PathToModule"
11+
12+
#>
13+
[OutputType([PSCustomObject])]
14+
[CmdletBinding()]
15+
param(
16+
[String]$Url,
17+
[String]$Path,
18+
[String]$TestType
19+
)
20+
$returnValue = $null
21+
Write-Verbose "Try to get expected connectivity result for '$Url' from file '$Path\Data\AzureEndpointExpectedResults.json'."
22+
try{
23+
$ExpectedResult = Get-Content -Path "$Path\Data\AzureEndpointExpectedResults.json" -ErrorAction Stop
24+
$ExpectedResultObjs = $ExpectedResult | ConvertFrom-Json
25+
foreach($ExpectedResultObj in $ExpectedResultObjs){
26+
if($ExpectedResultObj.UnblockUrl -eq $Url){
27+
$returnValue = $ExpectedResultObj
28+
break
29+
}
30+
}
31+
} catch {
32+
Write-Warning "Could not find '$Path\Data\AzureEndpointExpectedResults.json', failed to get expected connectifity results."
33+
}
34+
35+
if($null -eq $returnValue){
36+
if($TestType -eq "HTTP"){
37+
Write-Warning "Using default Expected Result Http Status 200 without SSL validation for url $($url)."
38+
$returnValue = [PSCustomObject]@{ UnblockUrl = $Url;ActualStatusCode = 200; HasError = $true }
39+
} else {
40+
Write-Warning "Using default Expected Result Tcp Status 1 $($url)."
41+
$returnValue = [PSCustomObject]@{ UnblockUrl = $Url;ActualStatusCode = 1; HasError = $true }
42+
}
43+
}
44+
return $returnValue
45+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
function Get-AzureO365UrlEndpoint{
2+
<#
3+
.Synopsis
4+
Returns list of Azure/O365 endpoints from the official Microsoft webservice.
5+
6+
.Description
7+
Try loading the actual list of Azure/O365 endpoints from the official Microsoft webservice. If not possible it will used a cached version. If an online version can be retriefed and the script is executed with administrative permission it also updates the local cache.
8+
9+
.Example
10+
Get-AzureO365UrlEndpoint
11+
12+
#>
13+
[OutputType([PSCustomObject[]])]
14+
[CmdletBinding()]
15+
param(
16+
[String]
17+
$Path
18+
)
19+
$Endpoints = Invoke-WebRequest -Uri "https://endpoints.office.com/endpoints/worldwide?clientrequestid=$(New-Guid)"
20+
if($Endpoints.StatusCode -ne 200){
21+
Write-Error "Error downloading the actual endpoint list ($($Endpoints.StatusDescription) - $($Endpoints.StatusCode)) `n https://endpoints.office.com" -ErrorAction Continue
22+
Write-Warning "Try using cached endpoint list"
23+
24+
try{
25+
$AzureEndpointCache = Get-Content -Path "$Path\Data\AzureEndpointCache.json" -ErrorAction Stop
26+
$EndpointsObjs = $AzureEndpointCache | ConvertFrom-Json
27+
} catch {
28+
throw "Could not find '$Path\Data\AzureEndpointCache.json, failed to load azure endpoints for connectivity tests."
29+
}
30+
} else {
31+
$EndpointsObjs = $Endpoints.Content | ConvertFrom-Json
32+
Write-Verbose "Successfully retrieved $($EndpointsObjs.Length) Endpoints from online source."
33+
if(Get-IsAdmin){
34+
Write-Verbose "Function is executed as Administrator, therefore trying to update local cache file."
35+
Out-File -FilePath "$Path\Data\AzureEndpointCache.json" -InputObject $Endpoints.content -Force
36+
}
37+
}
38+
return $EndpointsObjs
39+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
function Get-UrlWildCardLookup{
2+
<#
3+
.Synopsis
4+
tryes to find a static URL for a Wildcard URL from the .
5+
6+
.Description
7+
Returns $true if the script is executed with administrator priviledge, false if not.
8+
9+
.Example
10+
Get-UrlWildCardLookup -Url "*.contoso.com"
11+
12+
#>
13+
[OutputType([String[]])]
14+
[CmdletBinding()]
15+
param(
16+
[String]$Url,
17+
[String]$Path
18+
)
19+
20+
21+
[String[]]$StaticUrls = @()
22+
Write-Verbose "Try to resolve '$Url' Wildcard Url to an static url from file '$Path\Data\UrlWildcardLookup.json'."
23+
try{
24+
$AddToCache = $true
25+
$WildCardJSON = Get-Content -Path "$Path\Data\UrlWildcardLookup.json" -ErrorAction Stop
26+
$WildCardJSONObjs = $WildCardJSON | ConvertFrom-Json
27+
foreach($WildCardJSONObj in $WildCardJSONObjs){
28+
if($WildCardJSONObj.Wildcard -eq $Url){
29+
if($null -ne $WildCardJSONObj.static){
30+
foreach($UrlPart in $WildCardJSONObj.static.Split(",")){
31+
if(-not [String]::IsNullOrWhiteSpace($UrlPart)){
32+
$StaticUrls += $Url -replace "\*",$UrlPart
33+
Write-Verbose "Resolved URL $($Url -replace "\*",$UrlPart)"
34+
}
35+
}
36+
} else {
37+
$AddToCache = $false
38+
Write-Verbose "Found a matching URL, but there are no static entries for '$Url' Url. Please add them in the '$Path\Data\UrlWildcardLookup.json'."
39+
}
40+
}
41+
}
42+
if($StaticUrls.Length -eq 0 -and $AddToCache){
43+
Write-Warning "Could not find a matching static URL for the suplied wildcard '$Url' Url."
44+
$WildCardJSONObjs += [PSCustomObject]@{ Wildcard = $Url; static = $null }
45+
Out-File -FilePath "$Path\Data\UrlWildcardLookup.json" -InputObject ($WildCardJSONObjs | ConvertTo-Json) -Force
46+
}
47+
} catch {
48+
Write-Warning "Could not find '$Path\Data\UrlWildcardLookup.json', failed to convert wildcard into static url. $($_.Exception.Message)"
49+
50+
}
51+
return $StaticUrls
52+
}

PSModule/ModernWorkplaceClientCenter/ModernWorkplaceClientCenter.psd1

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#
44
# Generated by: Thomas Kurth
55
#
6-
# Generated on: 02.12.2018
6+
# Generated on: 09.01.2019
77
#
88

99
@{
@@ -12,7 +12,7 @@
1212
RootModule = 'ModernWorkplaceClientCenter.psm1'
1313

1414
# Version number of this module.
15-
ModuleVersion = '0.1.9'
15+
ModuleVersion = '0.1.10'
1616

1717
# Supported PSEditions
1818
# CompatiblePSEditions = @()
@@ -27,7 +27,7 @@ Author = 'Thomas Kurth'
2727
CompanyName = 'Thomas Kurth'
2828

2929
# Copyright statement for this module
30-
Copyright = '(c) 2018 Thomas Kurth. All rights reserved.'
30+
Copyright = '(c) 2019 Thomas Kurth. All rights reserved.'
3131

3232
# Description of the functionality provided by this module
3333
Description = 'The Modern Workplace Client Center Module provides functions to troubleshoot Microsoft Intune on a Windows 10 client in a modern managed environment. Th initial version mainly allows troubleshooting Azure AD Hybrid Join.'
@@ -66,12 +66,14 @@ PowerShellVersion = '5.0'
6666
# FormatsToProcess = @()
6767

6868
# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess
69-
NestedModules = @('NestedModules/HttpConnectivityTester/HttpConnectivityTester.psm1')
69+
NestedModules = @('NestedModules/HttpConnectivityTester/HttpConnectivityTester.psm1',
70+
'NestedModules/TcpConnectivityTester/TcpConnectivityTester.psm1')
7071

7172
# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
7273
FunctionsToExport = 'Get-BCStatusDetailed', 'Get-DsRegStatus', 'Get-MDMDeviceOwnership',
7374
'Get-MDMEnrollmentStatus', 'Get-MDMMsiApp', 'Get-MDMPSScriptStatus',
74-
'Get-SiteToZoneAssignment', 'Invoke-AnalyzeDeliveryOptimization',
75+
'Get-SiteToZoneAssignment', 'Invoke-AnalyzeAzureConnectivity',
76+
'Invoke-AnalyzeDeliveryOptimization',
7577
'Invoke-AnalyzeHybridJoinStatus',
7678
'Invoke-AnalyzeMDMEnrollmentStatus', 'Reset-MDMEnrollmentStatus'
7779

@@ -111,10 +113,12 @@ PrivateData = @{
111113
IconUri = 'https://raw.githubusercontent.com/ThomasKur/ModernWorkplaceClientCenter/master/Logo/MWCC-Logo-512.png'
112114

113115
# ReleaseNotes of this module
114-
ReleaseNotes = ' 0.1.9 - Delivery Optimization
116+
ReleaseNotes = ' 0.1.10 - Extended Azure AD Hybrid Join checks
115117
116-
* Improved loading of HttpConnectivtyTester Module
117-
* Added new function top analyze Delivery Optimization Configuration and connectifity on a device Invoke-AnalyzeDeliveryOptimization
118+
* Extended Azure AD Hybrid Join checks to include User Device Registration Event Log Invoke-AnalyzeHybridJoinStatus
119+
* Check manually defined IE Intranet Sites Invoke-AnalyzeHybridJoinStatus
120+
* Added TcpConnectivityTester Module to check Non HTTP Connections
121+
* Added Invoke-AnalyzeAzureConnectivity to check for connectivity issues to O365 and Azure based on the actual published list of Microsoft.
118122
119123
120124

PSModule/ModernWorkplaceClientCenter/ModernWorkplaceClientCenter.psm1

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,12 @@ if($HttpConnectivitytester){
2323
} else {
2424
Write-Warning -Message "HttpConnectivityTester module is not loaded, trying to import it."
2525
Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath "NestedModules\HttpConnectivityTester\HttpConnectivityTester.psd1")
26+
}
27+
28+
$TcpConnectivitytester = Get-Module -Name TcpConnectivityTester
29+
if($TcpConnectivitytester){
30+
Write-Verbose -Message "TcpConnectivityTester module is loaded."
31+
} else {
32+
Write-Warning -Message "TcpConnectivityTester module is not loaded, trying to import it."
33+
Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath "NestedModules\TcpConnectivityTester\TcpConnectivityTester.psd1")
2634
}

PSModule/ModernWorkplaceClientCenter/NestedModules/HttpConnectivityTester/HttpConnectivityTester.psm1

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,6 @@ Function Get-HttpConnectivity() {
388388

389389
[Parameter(Mandatory=$false, HelpMessage="Whether to ignore certificate validation errors so they don't affect the connectivity test. Some HTTPS endpoints are not meant to be accessed by a browser so the endpoint will not validate against browser security requirements.")]
390390
[switch]$IgnoreCertificateValidationErrors,
391-
392391
[Parameter(Mandatory=$false, HelpMessage='Whether to perform a Symantec BlueCoat Site Review lookup on the URL. Warning: The BlueCoat Site Review REST API is rate limited. Automatic throttling is performed when this parameter is used.')]
393392
[switch]$PerformBluecoatLookup
394393
)

0 commit comments

Comments
 (0)