From 00b8e702b4b911eb760198d3e6587c44d5924d57 Mon Sep 17 00:00:00 2001 From: Thomas Kurth Date: Wed, 9 Jan 2019 16:36:37 +0100 Subject: [PATCH] Release 0.1.10 -- Added Azure Connectivity Tester --- .../Data/AzureEndpointCache.json | Bin 0 -> 152474 bytes .../Data/AzureEndpointExpectedResults.json | Bin 0 -> 221544 bytes .../Data/UrlWildcardLookup.json | Bin 0 -> 17976 bytes .../Invoke-AnalyzeAzureConnectivity.ps1 | 125 ++++++++ .../Get-AzureEndpointExpectedResult.ps1 | 45 +++ .../Internal/Get-AzureO365UrlEndpoint.ps1 | 39 +++ .../Internal/Get-UrlWildCardLookup.ps1 | 52 ++++ .../ModernWorkplaceClientCenter.psd1 | 20 +- .../ModernWorkplaceClientCenter.psm1 | 8 + .../HttpConnectivityTester.psm1 | 1 - .../TcpConnectivityTester.psd1 | Bin 0 -> 9174 bytes .../TcpConnectivityTester.psm1 | 285 ++++++++++++++++++ PSModule/build.ps1 | 9 +- PSModule/cacheHttpResults.ps1 | 96 ++++++ README.md | 6 +- ReleaseNotes.md | 2 + 16 files changed, 672 insertions(+), 16 deletions(-) create mode 100644 PSModule/ModernWorkplaceClientCenter/Data/AzureEndpointCache.json create mode 100644 PSModule/ModernWorkplaceClientCenter/Data/AzureEndpointExpectedResults.json create mode 100644 PSModule/ModernWorkplaceClientCenter/Data/UrlWildcardLookup.json create mode 100644 PSModule/ModernWorkplaceClientCenter/Functions/Invoke-AnalyzeAzureConnectivity.ps1 create mode 100644 PSModule/ModernWorkplaceClientCenter/Internal/Get-AzureEndpointExpectedResult.ps1 create mode 100644 PSModule/ModernWorkplaceClientCenter/Internal/Get-AzureO365UrlEndpoint.ps1 create mode 100644 PSModule/ModernWorkplaceClientCenter/Internal/Get-UrlWildCardLookup.ps1 create mode 100644 PSModule/ModernWorkplaceClientCenter/NestedModules/TcpConnectivityTester/TcpConnectivityTester.psd1 create mode 100644 PSModule/ModernWorkplaceClientCenter/NestedModules/TcpConnectivityTester/TcpConnectivityTester.psm1 create mode 100644 PSModule/cacheHttpResults.ps1 diff --git a/PSModule/ModernWorkplaceClientCenter/Data/AzureEndpointCache.json b/PSModule/ModernWorkplaceClientCenter/Data/AzureEndpointCache.json new file mode 100644 index 0000000000000000000000000000000000000000..0b42b4d8310aa39a97ef6c99313182d2f67ac365 GIT binary patch literal 152474 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 V{VruE@24%bhV~L~`Hw~)`Cl+D92Wop literal 0 HcmV?d00001 diff --git a/PSModule/ModernWorkplaceClientCenter/Data/AzureEndpointExpectedResults.json b/PSModule/ModernWorkplaceClientCenter/Data/AzureEndpointExpectedResults.json new file mode 100644 index 0000000000000000000000000000000000000000..8eaa91ae1834aa84414d5c70f26ff967a4d452c3 GIT binary patch literal 221544 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 literal 0 HcmV?d00001 diff --git a/PSModule/ModernWorkplaceClientCenter/Data/UrlWildcardLookup.json b/PSModule/ModernWorkplaceClientCenter/Data/UrlWildcardLookup.json new file mode 100644 index 0000000000000000000000000000000000000000..0ced8a5b18392b279223d73b2561fe8f7c405eab GIT binary patch literal 17976 zcmds;-Eta75QXPDRe6ULm#OTQ*G)(=p*3Vx0C4ZxrzE1;P zS?ap|b(6NK*5|QaQ~m#u{<6|*qpLGrdC-+w*OzIoyZrFN^SDc2|F}bW)*{{O4zs;) z{CRK(kNl(bDa$##Fu9j$Azdz{$!*r@CFy)6sUIcvTAs0#l@}N1H4l<{q&39&dQbPh zmseJL!ZJr(r8sCuY8Rf*8aDd33|~d}vNw7e33;Yr{?k|@at*KLP2H~nEWc6w?#Dr$ zv1xlG!uNEqEBX8~=aNdlUk7+SlI|m?CmZz?nY_B!Xrs@4Dfx7~Mu~6oLj-=*%Hf-* z{JzR8;8WVk6MT~1$S#li?o2;n6WwdJ&iB2QjBu8zo^dP9^!3E8e&es7g}Q59^D&g3 zv~d+8_aJL!@FbT>_^1^uv<9>WyYgurrbUM7r-{~$HRsC7v;2F61?G}^n0;lV=Z^E! zu{mq;>m8q&}PW~gV~xV@iimQouyV0gYZ=9vwW%^P1?>c7{e~qq}W2c|1d~rOcUFkBZz52z|w%OyB=`KXC#_87b39pP) zHw^o*%UYa@$e0F6TXsN5OwRO08yF38$LQ2}<~nC}s?9#m8nu6~bM^q68wq_{jFXV+ z`$9fM4nb0{o#P~2$-3|#khD5$U2c?>=904P&X4VD^t8ISA< z^>I=4(ypb=!n|I~-VLT4!H&zlU*30hvx38YL-j9icisD=8^06s4&B&0Odo|j5!z-~ zOC1{i+1*}hqq)_Khlw18IqR z3;ibl9sn7+12Ru^p9pEmQ!{CmT17W<$6@oGw6S~2)iwvS)@MB2I@svEUA644Vrj9o zUv-V?Q;{>N7V|{2`|GVQzIR0x84F>V!VXHU@U+<`I^XmotK5ZnpWf>^`17^i-|6~w z`dRPPIwGQot`oJl!-}nkJ;cRNgP>y};ab|k9@aMmt-QFplDATSgx_OZYRhDEtEtA| z7P*b6w>qVC+>8YASL(v>^Ts#2$Xu+sOdnP` z7qX}Co`Ip+MfPQ{@5V9pjrx_u#Yow;nb7kfLhp%BjB-ED=FMXI+cpKju5y**mmKt2 zD}d>GHGC!fy%ru5XK(d_H#2MSwWoCNEqFW@8Rl6P+ezIBsoMP?D#PUYTI;6*Tc&W^ zeXX_r>Oq`HWdHig3g`X*t;~AUmu0W#*g$vf)G3}e?nYHaA98e@Q!c9%rPkpyUinrDxucKH8UR40vMM$9T0*6_xDH9BTx7IP8@BP@SCzop;`&x{7E4 zK5ZvLj9J;dLaYV)vH+PS$^y;2=;>UZsY~kkbo?W{rKvLP&$)V-09}3%l<8ScEC zj*JsRb&eb2=OMEtrquXEmWx@}Va?PVuW?*@zbE6fBN=#aoGc;L9ceo#3xCh z-f6yTy+HD9nfWwpy!I+AlOI%4Ozro~*Zu*cF~S}I literal 0 HcmV?d00001 diff --git a/PSModule/ModernWorkplaceClientCenter/Functions/Invoke-AnalyzeAzureConnectivity.ps1 b/PSModule/ModernWorkplaceClientCenter/Functions/Invoke-AnalyzeAzureConnectivity.ps1 new file mode 100644 index 0000000..debb751 --- /dev/null +++ b/PSModule/ModernWorkplaceClientCenter/Functions/Invoke-AnalyzeAzureConnectivity.ps1 @@ -0,0 +1,125 @@ +function Invoke-AnalyzeAzureConnectivity { + <# + .Synopsis + Analyzes the connectifity to O365 and Azure Endpoints. + + .Description + Analyzes the connectifity to O365 and Azure Endpoints according to https://docs.microsoft.com/en-us/office365/enterprise/urls-and-ip-address-ranges. + + Returns array of Messages with four properties: + + - Testname: Name of the Tets + - Type: Information, Warning or Error + - Issue: Description of the issue + - Possible Cause: Tips on how to solve the issue. + + .Example + # Displays a deep analyisis of the currently found issues in the system. + Invoke-AnalyzeAzureConnectivity + + #> + [alias("Invoke-AnalyzeO365Connectivity")] + [CmdletBinding()] + param( + [ValidateSet("Common","Exchange","Skype","SharePoint","All")] + [String] + $UrlSet = "Common", + [Switch] + $OnlyRequired + ) + + Write-Verbose "Conenctivity Tests to Azure Endpoints in $UrlSet category, which are Required=$OnlyRequired." + $data = New-Object System.Collections.Generic.List[PSCustomObject] + $possibleErrors = @() + $results = New-Object System.Collections.Generic.List[pscustomobject] + Write-Progress -Activity "Connectivity Tests" -status "Load TestUrls" -percentComplete 0 + + $EndpointsObjs = Get-AzureO365UrlEndpoint -Path ((Get-Item $PSScriptRoot).Parent.FullName) + $EndpointsObjs = $EndpointsObjs | Where-Object { ($_.serviceArea -eq $UrlSet -or $UrlSet -eq "All") -and ($OnlyRequired -eq $false -or $_.required -eq $true)} + Write-Progress -Activity "Connectivity Tests" -status "Load TestUrls finisehed" -percentComplete 100 + Write-Verbose "Found $($EndpointsObjs.length) endpoints to check" + $j = 0 + foreach($EndpointsObj in $EndpointsObjs){ + Write-Progress -Activity "Connectivity Tests" -status "Building urls for $($EndpointsObj.serviceArea) with id $($EndpointsObj.id)" -percentComplete ($j / $EndpointsObjs.length*100) + if($null -ne $EndpointsObj.tcpPorts){ + Add-Member -InputObject $EndpointsObj -MemberType NoteProperty -Name tcpPorts -Value "443" + } + foreach($Port in $EndpointsObj.tcpPorts.Split(',')){ + switch ($Port) { + 80 {$Protocol = "http://"; $UsePort = "";$TestType="HTTP"; break} + 443 {$Protocol = "https://"; $UsePort = "";$TestType="HTTP"; break} + default {$Protocol = ""; $UsePort = $Port;$TestType="TCP"; break} + } + if($EndpointsObj.PSObject.Properties.Name -match "notes"){ + $Notes = " - " + $EndpointsObj.notes + } else { + $Notes = "" + } + foreach($url in $EndpointsObj.urls){ + if($TestType -eq "HTTP"){ + $ExpectedResult = Get-AzureEndpointExpectedResult -TestType $TestType -Url ($Protocol + $url) -Path ((Get-Item $PSScriptRoot).Parent.FullName) + } else { + $ExpectedResult = Get-AzureEndpointExpectedResult -TestType $TestType -Url ($url + ":" + $UsePort) -Path ((Get-Item $PSScriptRoot).Parent.FullName) + } + if($url -notmatch "\*"){ + $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 }) + } else { + $staticUrls = Get-UrlWildCardLookup -Url $url -Path ((Get-Item $PSScriptRoot).Parent.FullName) + if($staticUrls){ + foreach($staticUrl in $staticUrls){ + $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 }) + } + } else { + + $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 + } + } + } + <#if($EndpointsObj.PSObject.Properties.Name -match "ips"){ + foreach($ip in $EndpointsObj.ips){ + $firstip = $ip.Split("/")[0] + $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 }) + } + }#> + } + } + + $possibleErrors = $possibleErrors | Group-Object -Property @("Type", "Issue") | ForEach-Object{ $_.Group | Select-Object * -First 1} + $i = 1 + $dataObjs = $data | Group-Object -Property @("TestUrl","TestType","UsePort") | ForEach-Object{ $_.Group | Select-Object * -First 1} + ForEach($dataObj in $dataObjs) { + Write-Progress -Activity "Connectivity Tests" -status "Processing $($d.TestUrl)" -percentComplete ($i / $dataObjs.count*100) + if($dataObj.TestType -eq "HTTP"){ + $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 + } else { + $connectivity = Get-TcpConnectivity -TestHostname $dataObj.TestUrl -TestPort $dataObj.UsePort -HostnamePattern ($dataObj.UrlPattern + ":" + $dataObj.UsePort) -ExpectedStatusCode $dataObj.ExpectedStatusCode -Description $dataObj.Description + } + $results.Add($connectivity) + if ($connectivity.Blocked -eq $true -and $dataObj.Blocked -eq $false) { + $possibleErrors += New-AnalyzeResult -TestName "Connectivity" -Type "Error" -Issue "Connection blocked `n $($connectivity)" -PossibleCause "Firewall is blocking connection to '$($connectivity.UnblockUrl)'." + } + if ($connectivity.Resolved -eq $false) { + $possibleErrors += New-AnalyzeResult -TestName "Connectivity" -Type "Error" -Issue "DNS name not resolved `n $($connectivity)" -PossibleCause "DNS server not correctly configured." + } + if ($connectivity.ActualStatusCode -ne $connectivity.ExpectedStatusCode) { + if($connectivity.ActualStatusCode -eq 407){ + $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/ " + } else { + $Cause = "Interfering Proxy server can change HTTP status codes." + } + $possibleErrors += New-AnalyzeResult -TestName "Connectivity" -Type "Error" -Issue "Returned Status code '$($connectivity.ActualStatusCode)' is not expected '$($connectivity.ExpectedStatusCode)'`n $($connectivity)" -PossibleCause $Cause + } + if ($null -ne $connectivity.ServerCertificate -and $connectivity.ServerCertificate.HasError -and -not $dataObj.IgnoreCertificateValidationErrors) { + $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." + } + $i += 1 + } + Write-Progress -Completed -Activity "Connectivity Tests" + + # No errors detected, return success message + if ($possibleErrors.Count -eq 0) { + $possibleErrors += New-AnalyzeResult -TestName "All" -Type Information -Issue "All tests went through successfully." -PossibleCause "" + } + + return $possibleErrors +} \ No newline at end of file diff --git a/PSModule/ModernWorkplaceClientCenter/Internal/Get-AzureEndpointExpectedResult.ps1 b/PSModule/ModernWorkplaceClientCenter/Internal/Get-AzureEndpointExpectedResult.ps1 new file mode 100644 index 0000000..aa02fad --- /dev/null +++ b/PSModule/ModernWorkplaceClientCenter/Internal/Get-AzureEndpointExpectedResult.ps1 @@ -0,0 +1,45 @@ +function Get-AzureEndpointExpectedResult{ + <# + .Synopsis + Returns the expected result and SSL error for a specific endpoint. + + .Description + Returns the expected result and SSL error for a specific endpoint. + + .Example + Get-AzureEndpointExpectedResult -Url "http://*.contoso.com" -Path "PathToModule" + + #> + [OutputType([PSCustomObject])] + [CmdletBinding()] + param( + [String]$Url, + [String]$Path, + [String]$TestType + ) + $returnValue = $null + Write-Verbose "Try to get expected connectivity result for '$Url' from file '$Path\Data\AzureEndpointExpectedResults.json'." + try{ + $ExpectedResult = Get-Content -Path "$Path\Data\AzureEndpointExpectedResults.json" -ErrorAction Stop + $ExpectedResultObjs = $ExpectedResult | ConvertFrom-Json + foreach($ExpectedResultObj in $ExpectedResultObjs){ + if($ExpectedResultObj.UnblockUrl -eq $Url){ + $returnValue = $ExpectedResultObj + break + } + } + } catch { + Write-Warning "Could not find '$Path\Data\AzureEndpointExpectedResults.json', failed to get expected connectifity results." + } + + if($null -eq $returnValue){ + if($TestType -eq "HTTP"){ + Write-Warning "Using default Expected Result Http Status 200 without SSL validation for url $($url)." + $returnValue = [PSCustomObject]@{ UnblockUrl = $Url;ActualStatusCode = 200; HasError = $true } + } else { + Write-Warning "Using default Expected Result Tcp Status 1 $($url)." + $returnValue = [PSCustomObject]@{ UnblockUrl = $Url;ActualStatusCode = 1; HasError = $true } + } + } + return $returnValue +} \ No newline at end of file diff --git a/PSModule/ModernWorkplaceClientCenter/Internal/Get-AzureO365UrlEndpoint.ps1 b/PSModule/ModernWorkplaceClientCenter/Internal/Get-AzureO365UrlEndpoint.ps1 new file mode 100644 index 0000000..80c6193 --- /dev/null +++ b/PSModule/ModernWorkplaceClientCenter/Internal/Get-AzureO365UrlEndpoint.ps1 @@ -0,0 +1,39 @@ +function Get-AzureO365UrlEndpoint{ + <# + .Synopsis + Returns list of Azure/O365 endpoints from the official Microsoft webservice. + + .Description + 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. + + .Example + Get-AzureO365UrlEndpoint + + #> + [OutputType([PSCustomObject[]])] + [CmdletBinding()] + param( + [String] + $Path + ) + $Endpoints = Invoke-WebRequest -Uri "https://endpoints.office.com/endpoints/worldwide?clientrequestid=$(New-Guid)" + if($Endpoints.StatusCode -ne 200){ + Write-Error "Error downloading the actual endpoint list ($($Endpoints.StatusDescription) - $($Endpoints.StatusCode)) `n https://endpoints.office.com" -ErrorAction Continue + Write-Warning "Try using cached endpoint list" + + try{ + $AzureEndpointCache = Get-Content -Path "$Path\Data\AzureEndpointCache.json" -ErrorAction Stop + $EndpointsObjs = $AzureEndpointCache | ConvertFrom-Json + } catch { + throw "Could not find '$Path\Data\AzureEndpointCache.json, failed to load azure endpoints for connectivity tests." + } + } else { + $EndpointsObjs = $Endpoints.Content | ConvertFrom-Json + Write-Verbose "Successfully retrieved $($EndpointsObjs.Length) Endpoints from online source." + if(Get-IsAdmin){ + Write-Verbose "Function is executed as Administrator, therefore trying to update local cache file." + Out-File -FilePath "$Path\Data\AzureEndpointCache.json" -InputObject $Endpoints.content -Force + } + } + return $EndpointsObjs +} \ No newline at end of file diff --git a/PSModule/ModernWorkplaceClientCenter/Internal/Get-UrlWildCardLookup.ps1 b/PSModule/ModernWorkplaceClientCenter/Internal/Get-UrlWildCardLookup.ps1 new file mode 100644 index 0000000..9033968 --- /dev/null +++ b/PSModule/ModernWorkplaceClientCenter/Internal/Get-UrlWildCardLookup.ps1 @@ -0,0 +1,52 @@ +function Get-UrlWildCardLookup{ + <# + .Synopsis + tryes to find a static URL for a Wildcard URL from the . + + .Description + Returns $true if the script is executed with administrator priviledge, false if not. + + .Example + Get-UrlWildCardLookup -Url "*.contoso.com" + + #> + [OutputType([String[]])] + [CmdletBinding()] + param( + [String]$Url, + [String]$Path + ) + + + [String[]]$StaticUrls = @() + Write-Verbose "Try to resolve '$Url' Wildcard Url to an static url from file '$Path\Data\UrlWildcardLookup.json'." + try{ + $AddToCache = $true + $WildCardJSON = Get-Content -Path "$Path\Data\UrlWildcardLookup.json" -ErrorAction Stop + $WildCardJSONObjs = $WildCardJSON | ConvertFrom-Json + foreach($WildCardJSONObj in $WildCardJSONObjs){ + if($WildCardJSONObj.Wildcard -eq $Url){ + if($null -ne $WildCardJSONObj.static){ + foreach($UrlPart in $WildCardJSONObj.static.Split(",")){ + if(-not [String]::IsNullOrWhiteSpace($UrlPart)){ + $StaticUrls += $Url -replace "\*",$UrlPart + Write-Verbose "Resolved URL $($Url -replace "\*",$UrlPart)" + } + } + } else { + $AddToCache = $false + Write-Verbose "Found a matching URL, but there are no static entries for '$Url' Url. Please add them in the '$Path\Data\UrlWildcardLookup.json'." + } + } + } + if($StaticUrls.Length -eq 0 -and $AddToCache){ + Write-Warning "Could not find a matching static URL for the suplied wildcard '$Url' Url." + $WildCardJSONObjs += [PSCustomObject]@{ Wildcard = $Url; static = $null } + Out-File -FilePath "$Path\Data\UrlWildcardLookup.json" -InputObject ($WildCardJSONObjs | ConvertTo-Json) -Force + } + } catch { + Write-Warning "Could not find '$Path\Data\UrlWildcardLookup.json', failed to convert wildcard into static url. $($_.Exception.Message)" + + } + return $StaticUrls +} \ No newline at end of file diff --git a/PSModule/ModernWorkplaceClientCenter/ModernWorkplaceClientCenter.psd1 b/PSModule/ModernWorkplaceClientCenter/ModernWorkplaceClientCenter.psd1 index 0af71a6..5b206b6 100644 --- a/PSModule/ModernWorkplaceClientCenter/ModernWorkplaceClientCenter.psd1 +++ b/PSModule/ModernWorkplaceClientCenter/ModernWorkplaceClientCenter.psd1 @@ -3,7 +3,7 @@ # # Generated by: Thomas Kurth # -# Generated on: 02.12.2018 +# Generated on: 09.01.2019 # @{ @@ -12,7 +12,7 @@ RootModule = 'ModernWorkplaceClientCenter.psm1' # Version number of this module. -ModuleVersion = '0.1.9' +ModuleVersion = '0.1.10' # Supported PSEditions # CompatiblePSEditions = @() @@ -27,7 +27,7 @@ Author = 'Thomas Kurth' CompanyName = 'Thomas Kurth' # Copyright statement for this module -Copyright = '(c) 2018 Thomas Kurth. All rights reserved.' +Copyright = '(c) 2019 Thomas Kurth. All rights reserved.' # Description of the functionality provided by this module 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' # FormatsToProcess = @() # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess -NestedModules = @('NestedModules/HttpConnectivityTester/HttpConnectivityTester.psm1') +NestedModules = @('NestedModules/HttpConnectivityTester/HttpConnectivityTester.psm1', + 'NestedModules/TcpConnectivityTester/TcpConnectivityTester.psm1') # 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. FunctionsToExport = 'Get-BCStatusDetailed', 'Get-DsRegStatus', 'Get-MDMDeviceOwnership', 'Get-MDMEnrollmentStatus', 'Get-MDMMsiApp', 'Get-MDMPSScriptStatus', - 'Get-SiteToZoneAssignment', 'Invoke-AnalyzeDeliveryOptimization', + 'Get-SiteToZoneAssignment', 'Invoke-AnalyzeAzureConnectivity', + 'Invoke-AnalyzeDeliveryOptimization', 'Invoke-AnalyzeHybridJoinStatus', 'Invoke-AnalyzeMDMEnrollmentStatus', 'Reset-MDMEnrollmentStatus' @@ -111,10 +113,12 @@ PrivateData = @{ IconUri = 'https://raw.githubusercontent.com/ThomasKur/ModernWorkplaceClientCenter/master/Logo/MWCC-Logo-512.png' # ReleaseNotes of this module - ReleaseNotes = ' 0.1.9 - Delivery Optimization + ReleaseNotes = ' 0.1.10 - Extended Azure AD Hybrid Join checks -* Improved loading of HttpConnectivtyTester Module -* Added new function top analyze Delivery Optimization Configuration and connectifity on a device Invoke-AnalyzeDeliveryOptimization +* Extended Azure AD Hybrid Join checks to include User Device Registration Event Log Invoke-AnalyzeHybridJoinStatus +* Check manually defined IE Intranet Sites Invoke-AnalyzeHybridJoinStatus +* Added TcpConnectivityTester Module to check Non HTTP Connections +* Added Invoke-AnalyzeAzureConnectivity to check for connectivity issues to O365 and Azure based on the actual published list of Microsoft. diff --git a/PSModule/ModernWorkplaceClientCenter/ModernWorkplaceClientCenter.psm1 b/PSModule/ModernWorkplaceClientCenter/ModernWorkplaceClientCenter.psm1 index 0cf9ce8..b3f35dd 100644 --- a/PSModule/ModernWorkplaceClientCenter/ModernWorkplaceClientCenter.psm1 +++ b/PSModule/ModernWorkplaceClientCenter/ModernWorkplaceClientCenter.psm1 @@ -23,4 +23,12 @@ if($HttpConnectivitytester){ } else { Write-Warning -Message "HttpConnectivityTester module is not loaded, trying to import it." Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath "NestedModules\HttpConnectivityTester\HttpConnectivityTester.psd1") +} + +$TcpConnectivitytester = Get-Module -Name TcpConnectivityTester +if($TcpConnectivitytester){ + Write-Verbose -Message "TcpConnectivityTester module is loaded." +} else { + Write-Warning -Message "TcpConnectivityTester module is not loaded, trying to import it." + Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath "NestedModules\TcpConnectivityTester\TcpConnectivityTester.psd1") } \ No newline at end of file diff --git a/PSModule/ModernWorkplaceClientCenter/NestedModules/HttpConnectivityTester/HttpConnectivityTester.psm1 b/PSModule/ModernWorkplaceClientCenter/NestedModules/HttpConnectivityTester/HttpConnectivityTester.psm1 index b096ebc..cd89c33 100644 --- a/PSModule/ModernWorkplaceClientCenter/NestedModules/HttpConnectivityTester/HttpConnectivityTester.psm1 +++ b/PSModule/ModernWorkplaceClientCenter/NestedModules/HttpConnectivityTester/HttpConnectivityTester.psm1 @@ -388,7 +388,6 @@ Function Get-HttpConnectivity() { [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.")] [switch]$IgnoreCertificateValidationErrors, - [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.')] [switch]$PerformBluecoatLookup ) diff --git a/PSModule/ModernWorkplaceClientCenter/NestedModules/TcpConnectivityTester/TcpConnectivityTester.psd1 b/PSModule/ModernWorkplaceClientCenter/NestedModules/TcpConnectivityTester/TcpConnectivityTester.psd1 new file mode 100644 index 0000000000000000000000000000000000000000..963e9560396ceabc2c9d8ac2fa7624e4b5eb8e90 GIT binary patch literal 9174 zcmeI1ZBH9V5Xbj*rG5uXynsjrLYg#9rAo1QXi!NS5t8&R;D@*YLv0fpRr%^||Gyni zZ}+^mu@joAsw}v>z1`V){Lk#%pMR``FT)pMB{ag*a1mPJDx8J3p3cK@=!ahD=xG>6 z8tLf$AihcGt%jp;8ZN`O-uCrt>uIEQ@ANkc@AcN!x3zY4yqE9zgWj=XpuI-AwnY6z zYrfO3Wc&#TSX~h4%C|_OM8<8*P=}^|{a7TJ||pf}@gnCSdy9gW?M``-)SXhm6*O>qlP=CQNvm-3jeavv+l zVtL=n68)$b+y7nER%=0Sa$3(M{kiCv4e|X&`MMNGM0QsziEWV0A``k;v%Hv^@5Ff(VtM>l7Dr>9=);E1 z#P_jwG=y$QhJhpjvGHj~;T)gJ9zAVu(?T;+L>ADpAXvENgiQzGFrs;-JMQKoz`s-bE3%VGRMvFapP$QbsAI7=zREd( z>i0By7hXhWp+?7B)?{7u+YVpp>F4mRsub>;(tAty_m|LS>Oqs-G^g@nNaXyKrAsST zvH@OCRX}A=CCD9hFp)xyiTqxhlEbV<^jPW2=Pxv$XtEqaW<`H}t@bK|Xe2kGNn4-N z_RI*)*)=8hJQUS5Gu7om2UNVP`jxBRX0&Tt{82ggL~phpknmWxv?#buS<0NbL}h96 z^P8W?kZ4Uf!YODgdSGQ|d>Q*%^n1PH7NzQ@A^<&Cqh_$;r?zU2lGrtsV<;rQ5Duqt zIPWZTB<#J6a+P)Po9uly)&!><^YPra*D(W7y)Eyb&c`w`?8O-2d`uL6PB~Ierc1K; ziF_-V9d3$ESSqyzNQ`fTw_ugUBpJ@TIlRql#`GT$vs}t;Nynq$UGR2TmOap z#$3(?=o87k7tql8fWA&kdIBw4qJrl8x~s1M7eNcFt{g=S`RN$3hjkc?0`ibo=$Bbu zrSh{bgLhr5a5q*wj`T6fg zx1GmkX(#uOT$_toVn@}+wk$#&M1@Z+Z}q;{r;A~3-O@XvdKxR$oO+qd>HXOQ`HSJV zWi+eI@*X^%L?`%EXHz@Zfzjo@dzB^Y9H+Ue+SQS<9AnkIoV&Dc`FPIuVkT-^a>)@@b^`RSPaYq{@Ce303!7YLVDiNG!W{2h%L z@0`(ws!n+_L`!YV=_Kt&;3v74@I-E7rqitY{(Ofj_dW8M%XM3EXBL_KzU1F@a#`lh z_Ejt5=}6pmqqUCZ<(Cmbt!6W8mTMr_H_=V~U|mkSHdS2Y_O5qptsdbdV0|6z0;R6_ z@zar=9CHmh?|!ZLcduQQ75Ij@s>+CatI+Z%I}zt&b{=C571xO{t2?GqJzM|Q$6pG8 z4#so1-`D+QBs-^mMe9xd+3I?foXJB$%KlihUTIFVLy9xLgI|g0iq^{*xBj&QPajE3 zLw()c)DOwn<4Tfm#CSQ^=#fVJyQzIO^?Ds`^}}nurI(1`fRJsWJ`z}Ux;?%!cQZy6 zos3OI3bBS|7(bBSwpB4cQ#|fUgGe+>s@eL3w#7Id_!I5peaT5p(rLH|zi1vc?`%J_ zv$D_qyt*_Uxs?~!73U02eTIsbrO|~&v-KZT;5emo~^qt}C0ZswtM(KxkPy+y~+kSIMn&+f&! zef>I}BFI;&iGAeHRYN+ey2a~R*Q6nD1)rS%9LXl7-IfRN)xxAoS5E5e GL--4p`?TW# literal 0 HcmV?d00001 diff --git a/PSModule/ModernWorkplaceClientCenter/NestedModules/TcpConnectivityTester/TcpConnectivityTester.psm1 b/PSModule/ModernWorkplaceClientCenter/NestedModules/TcpConnectivityTester/TcpConnectivityTester.psm1 new file mode 100644 index 0000000..dadaf91 --- /dev/null +++ b/PSModule/ModernWorkplaceClientCenter/NestedModules/TcpConnectivityTester/TcpConnectivityTester.psm1 @@ -0,0 +1,285 @@ +Set-StrictMode -Version 4 + +Function Get-ErrorMessage() { +<# + .SYNOPSIS + Gets a formatted error message from an error record. + + .DESCRIPTION + Gets a formatted error message from an error record. + + .EXAMPLE + Get-ErrorMessage -ErrorRecords $_ + #> + [CmdletBinding()] + [OutputType([string])] + Param( + [Parameter(Mandatory=$true, HelpMessage='The PowerShell error record object to get information from')] + [ValidateNotNullOrEmpty()] + [System.Management.Automation.ErrorRecord]$ErrorRecord + ) + Process { + $msg = [System.Environment]::NewLine,'Exception Message: ',$ErrorRecord.Exception.Message -join '' + + if($null -ne $ErrorRecord.Exception.HResult) { + $msg = $msg,[System.Environment]::NewLine,'Exception HRESULT: ',('{0:X}' -f $ErrorRecord.Exception.HResult),$ErrorRecord.Exception.HResult -join '' + } + + if($null -ne $ErrorRecord.Exception.StackTrace) { + $msg = $msg,[System.Environment]::NewLine,'Exception Stacktrace: ',$ErrorRecord.Exception.StackTrace -join '' + } + + if ($null -ne ($ErrorRecord.Exception | Get-Member | Where-Object { $_.Name -eq 'WasThrownFromThrowStatement'})) { + $msg = $msg,[System.Environment]::NewLine,'Explicitly Thrown: ',$ErrorRecord.Exception.WasThrownFromThrowStatement -join '' + } + + if ($null -ne $ErrorRecord.Exception.InnerException) { + if ($ErrorRecord.Exception.InnerException.Message -ne $ErrorRecord.Exception.Message) { + $msg = $msg,[System.Environment]::NewLine,'Inner Exception: ',$ErrorRecord.Exception.InnerException.Message -join '' + } + + if($null -ne $ErrorRecord.Exception.InnerException.HResult) { + $msg = $msg,[System.Environment]::NewLine,'Inner Exception HRESULT: ',('{0:X}' -f $ErrorRecord.Exception.InnerException.HResult),$ErrorRecord.Exception.InnerException.HResult -join '' + } + } + + $msg = $msg,[System.Environment]::NewLine,'Call Site: ',$ErrorRecord.InvocationInfo.PositionMessage -join '' + + if ($null -ne ($ErrorRecord | Get-Member | Where-Object { $_.Name -eq 'ScriptStackTrace'})) { + $msg = $msg,[System.Environment]::NewLine,"Script Stacktrace: ",$ErrorRecord.ScriptStackTrace -join '' + } + + return $msg + } +} + + + +Function Get-IPAddress() { + <# + .SYNOPSIS + Gets the IP address(es) for a hostname. + + .DESCRIPTION + Gets the IP address(es) for a hostname. + + .EXAMPLE + Get-IPAddress -Hostname www.site.com + #> + [CmdletBinding()] + [OutputType([string[]])] + Param ( + [Parameter(Mandatory=$true, HelpMessage='The Hostname to get the IP address for.')] + [ValidateNotNullOrEmpty()] + [Alias("Url")] + [String]$Hostname + ) + + $addresses = [string[]]@() + + $dnsResults = $null + + $dnsResults = @(Resolve-DnsName -Name $Hostname -NoHostsFile -Type A_AAAA -QuickTimeout -ErrorAction SilentlyContinue | Where-Object {$_.Type -eq 'A'}) + + $addresses = [string[]]@($dnsResults | ForEach-Object { try { $_.IpAddress } catch [System.Management.Automation.PropertyNotFoundException] {Write-Verbose "No IP in Object."} }) # IpAddress results in a PropertyNotFoundException when a URL is blocked upstream + + return [string[]](,$addresses) +} + +Function Get-IPAlias() { + <# + .SYNOPSIS + Gets DNS alias for a Hostname. + + .DESCRIPTION + Gets DNS alias for a Hostname. + + .EXAMPLE + Get-IPAlias -Hostname http://www.site.com + #> + [CmdletBinding()] + [OutputType([string[]])] + Param ( + [Parameter(Mandatory=$true, HelpMessage='The Hostname to get the alias address for.')] + [ValidateNotNullOrEmpty()] + [Alias("Url")] + [String]$Hostname + ) + + $aliases = [string[]]@() + + $dnsResults = $null + + $dnsResults = @(Resolve-DnsName -Name $Hostname -NoHostsFile -QuickTimeout -ErrorAction SilentlyContinue | Where-Object { $_.Type -eq 'CNAME' }) + + $aliases = [string[]]@($dnsResults | ForEach-Object { $_.NameHost }) + + return [string[]](,$aliases) +} + + + +Function Get-TcpConnectivity() { + <# + .SYNOPSIS + Gets TCP connectivity information for a hostname and port. + + .DESCRIPTION + Gets TCP connectivity information for a hostname and port. + + .EXAMPLE + Get-TcpConnectivity -TestHostname "www.site.com" -TestPort 111 + + .EXAMPLE + Get-TcpConnectivity -TestHostname "www.site.com" -TestPort 111 -HostnamePattern "*.site.com" -Description 'A site that does something' + + #> + [CmdletBinding()] + [OutputType([void])] + Param( + [Parameter(Mandatory=$true, HelpMessage='The hostname to test.')] + [ValidateNotNullOrEmpty()] + [String]$TestHostname, + + [Parameter(Mandatory=$true, HelpMessage='The TCP port to test.')] + [ValidateNotNullOrEmpty()] + [Int32]$TestPort, + + [Parameter(Mandatory=$true, HelpMessage='The Expected status code.')] + [Int32]$ExpectedStatusCode, + + [Parameter(Mandatory=$false, HelpMessage='The hostname pattern to unblock when the hostname to unblock is not a literal hostname.')] + [ValidateNotNullOrEmpty()] + [string]$HostnamePattern, + + [Parameter(Mandatory=$false, HelpMessage='A description of the connectivity test or purpose of the hostname.')] + [ValidateNotNullOrEmpty()] + [string]$Description + + ) + + $parameters = $PSBoundParameters + + $isVerbose = $verbosePreference -eq 'Continue' + + $TestHostname = $TestHostname.ToLower() + + + if($parameters.ContainsKey('HostnamePattern')) { + $UnblockHostname = $HostnamePattern + } else { + $UnblockHostname = $TestHostname + } + + $newLine = [System.Environment]::NewLine + + Write-Verbose -Message ('{0}*************************************************{1}Testing {2}{3}*************************************************{4}' -f $newLine,$newLine,$TestHostname,$newLine,$newLine) + + + $statusCode = 0 + $statusMessage = '' + $response = $null + + try { + $response = Test-NetConnection -ComputerName $TestHostname -Port $TestPort -Verbose:$isVerbose + if($response.TcpTestSucceeded){ + $statusCode = 1 + $statusMessage = "Tcp test succeeded" + } elseif($response.PingSucceeded){ + $statusCode = 2 + $statusMessage = "Ping test succeeded" + } elseif($response.NameResolutionSucceeded){ + $statusCode = 3 + $statusMessage = "Name resolution succeeded" + }else { + $statusCode = 5 + $statusMessage = "Unknown error" + } + + + } catch { + $statusMessage = Get-ErrorMessage -ErrorRecord $_ + } + + $address = Get-IPAddress -Hostname $TestHostname -Verbose:$false + $alias = Get-IPAlias -Hostname $TestHostname -Verbose:$false + $resolved = (@($address)).Length -ge 1 -or (@($alias)).Length -ge 1 + $actualStatusCode = [int]$statusCode + $isBlocked = $statusCode -eq 1 -and $resolved + $urlType = if ($HostnamePattern.Contains('*')) { 'Pattern' } else { 'Literal' } + + $isUnexpectedStatus = $statusCode -ne 1 + $simpleStatusMessage = if ($isUnexpectedStatus) { $statusMessage } else { '' } + + $connectivitySummary = ('{0}Test Hostname: {1}{2}Hostname to Unblock: {3}{4}Hostname Type: {5}{6}Description: {7}{8}Resolved: {9}{10}IP Addresses: {11}{12}DNS Aliases: {13}{14}Actual Status Code: {15}{16}Expected Status Code: {17}{18}Is Unexpected Status Code: {19}{20}Status Message: {21}{22}Blocked: {23}{24}{25}' -f $newLine,$TestHostname,$newLine,$HostnamePattern,$newLine,$urlType,$newLine,$Description,$newLine,$resolved,$newLine,($address -join ', '),$newLine,($alias -join ', '),$newLine,$actualStatusCode,$newLine,$ExpectedStatusCode,$newLine,$isUnexpectedStatus,$newLine,$simpleStatusMessage,$newLine,$isBlocked,$newLine,$newLine) + Write-Verbose -Message $connectivitySummary + + $connectivity = [pscustomobject]@{ + TestUrl = $TestHostname; + UnblockUrl = $UnblockHostname; + UrlType = $urlType; + Resolved = $resolved; + IpAddresses = [string[]]$address; + DnsAliases = [string[]]$alias; + Description = $Description; + ActualStatusCode = [int]$actualStatusCode; + ExpectedStatusCode = $ExpectedStatusCode; + UnexpectedStatus = $isUnexpectedStatus; + StatusMessage = $simpleStatusMessage; + DetailedStatusMessage = $statusMessage; + Blocked = $isBlocked; + ServerCertificate = $null; + } + + return $connectivity +} + +Function Save-TcpConnectivity() { + <# + .SYNOPSIS + Saves TCP connectivity objects to a JSON file. + + .DESCRIPTION + Saves TCP connectivity objects to a JSON file. + + .EXAMPLE + Save-TcpConnectivity -FileName 'Connectivity' -Objects $connectivity + + .EXAMPLE + Save-TcpConnectivity -FileName 'Connectivity' -Objects $connectivity -OutputPath "$env:userprofile\Documents\ConnectivityTestResults" + + .EXAMPLE + Save-TcpConnectivity -FileName 'Connectivity' -Objects $connectivity -Compress + #> + [CmdletBinding()] + [OutputType([void])] + Param( + [Parameter(Mandatory=$true, HelpMessage='The filename without the extension.')] + [ValidateNotNullOrEmpty()] + [string]$FileName, + + [Parameter(Mandatory=$true, HelpMessage='The connectivity object(s) to save.')] + [System.Collections.Generic.List[pscustomobject]]$Objects, + + [Parameter(Mandatory=$false, HelpMessage="The path to save the file to. Defaults to the user's Desktop folder.")] + [string]$OutputPath, + + [Parameter(Mandatory=$false, HelpMessage='Compress the JSON text output.')] + [switch]$Compress + ) + + $parameters = $PSBoundParameters + + if (-not($parameters.ContainsKey('OutputPath'))) { + $OutputPath = $env:USERPROFILE,'Desktop' -join [System.IO.Path]::DirectorySeparatorChar + } + + $OutputPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($OutputPath) + + if (-not(Test-Path -Path $OutputPath)) { + New-Item -Path $OutputPath -ItemType Directory + } + + $json = $Objects | ConvertTo-Json -Depth 3 -Compress:$Compress + $json | Out-File -FilePath "$OutputPath\$FileName.json" -NoNewline -Force +} diff --git a/PSModule/build.ps1 b/PSModule/build.ps1 index d84e609..7e80c75 100644 --- a/PSModule/build.ps1 +++ b/PSModule/build.ps1 @@ -1,6 +1,6 @@ $ModulePath = ".\PSModule\ModernWorkplaceClientCenter" -## The following four lines only need to be declared once in your script. +#region UI $yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes","Description." $no = New-Object System.Management.Automation.Host.ChoiceDescription "&No","Description." $cancel = New-Object System.Management.Automation.Host.ChoiceDescription "&Cancel","Description." @@ -20,9 +20,10 @@ switch ($result) { Write-Error "Canceled Publishing Process" -ErrorAction Stop } } +#endregion #region Code Analyzer -Import-Module -Name PSScriptAnalyzer +Import-Module -Name PSScriptAnalyzer -Force $ScriptAnalyzerResult = Invoke-ScriptAnalyzer -Path $ModulePath -Recurse -ErrorAction Stop -ExcludeRule @("PSAvoidTrailingWhitespace") if($ScriptAnalyzerResult){ @@ -49,7 +50,7 @@ switch ($result) { Update-ModuleManifest -Path "$ModulePath\ModernWorkplaceClientCenter.psd1" ` -FunctionsToExport $ExportableFunctions ` -ReleaseNotes $ReleaseNote ` - -NestedModules @("NestedModules/HttpConnectivityTester/HttpConnectivityTester.psm1") ` + -NestedModules @("NestedModules/HttpConnectivityTester/HttpConnectivityTester.psm1","NestedModules/TcpConnectivityTester/TcpConnectivityTester.psm1") ` -IconUri "https://raw.githubusercontent.com/ThomasKur/ModernWorkplaceClientCenter/master/Logo/MWCC-Logo-512.png" ` -ModuleVersion $SuggestedNewVersion } @@ -58,7 +59,7 @@ switch ($result) { Update-ModuleManifest -Path "$ModulePath\ModernWorkplaceClientCenter.psd1" ` -FunctionsToExport $ExportableFunctions ` -ReleaseNotes $ReleaseNote ` - -NestedModules @("NestedModules/HttpConnectivityTester/HttpConnectivityTester.psm1") ` + -NestedModules @("NestedModules/HttpConnectivityTester/HttpConnectivityTester.psm1","NestedModules/TcpConnectivityTester/TcpConnectivityTester.psm1") ` -IconUri "https://raw.githubusercontent.com/ThomasKur/ModernWorkplaceClientCenter/master/Logo/MWCC-Logo-512.png" ` -ModuleVersion $CurrentVersion } diff --git a/PSModule/cacheHttpResults.ps1 b/PSModule/cacheHttpResults.ps1 new file mode 100644 index 0000000..a56495d --- /dev/null +++ b/PSModule/cacheHttpResults.ps1 @@ -0,0 +1,96 @@ +$ModulePath = "$PSScriptRoot\ModernWorkplaceClientCenter" +. "$PSScriptRoot\ModernWorkplaceClientCenter\Internal\Get-UrlWildCardLookup.ps1" +$HttpConModulePath = "$PSScriptRoot\ModernWorkplaceClientCenter\NestedModules\HttpConnectivityTester\HttpConnectivityTester.psd1" +Import-Module $HttpConModulePath +$TcpConModulePath = "$PSScriptRoot\ModernWorkplaceClientCenter\NestedModules\TcpConnectivityTester\TcpConnectivityTester.psd1" +Import-Module $TcpConModulePath +#region Update Azure Endpoints +$Endpoints = Invoke-WebRequest -Uri "https://endpoints.office.com/endpoints/worldwide?clientrequestid=$(New-Guid)" +Out-File -FilePath "$ModulePath\Data\AzureEndpointCache.json" -InputObject $Endpoints.content -Force +#endregion Update Azure Endpoints + +#region Expected Results +$data = New-Object System.Collections.Generic.List[PSCustomObject] +$EndpointsObjs = $Endpoints.content | ConvertFrom-Json +Write-Verbose "Found $($EndpointsObjs.length) endpoints to check" +foreach($EndpointsObj in $EndpointsObjs){ + if($EndpointsObj.tcpPorts -eq $null){ + Add-Member -InputObject $EndpointsObj -MemberType NoteProperty -Name tcpPorts -Value "443" + } + foreach($Port in $EndpointsObj.tcpPorts.Split(',')){ + switch ($Port) { + 80 {$Protocol = "http://"; $UsePort = "";$TestType="HTTP"; break} + 443 {$Protocol = "https://"; $UsePort = "";$TestType="HTTP"; break} + default {$Protocol = ""; $UsePort = $Port;$TestType="TCP"; break} + } + if($EndpointsObj.PSObject.Properties.Name -match "notes"){ + $Notes = " - " + $EndpointsObj.notes + } else { + $Notes = "" + } + foreach($url in $EndpointsObj.urls){ + if($url -notmatch "\*"){ + $data.Add([PSCustomObject]@{ TestType = $TestType; TestUrl = $url; UsePort = $UsePort; Protocol = $Protocol; UrlPattern = $url; Description = "$($EndpointsObj.serviceAreaDisplayName)$Notes"; PerformBluecoatLookup=$false; Verbose=$false }) + } else { + $staticUrls = Get-UrlWildCardLookup -Url $url -Path $ModulePath + foreach($staticUrl in $staticUrls){ + $data.Add([PSCustomObject]@{ TestType = $TestType; TestUrl = $staticUrl; UsePort = $UsePort; Protocol = $Protocol; UrlPattern = $url; Description = "$($EndpointsObj.serviceAreaDisplayName)$Notes"; PerformBluecoatLookup=$false; Verbose=$false }) + } + } + } + } +} + +$results = New-Object System.Collections.Generic.List[pscustomobject] + +$i = 0 +<# +ForEach($d in $data) { + Write-Progress -Activity "Connectivity Tests" -status "Processing $($d.TestUrl)" -percentComplete ($i / $data.count*100) + $connectivity = Get-HttpConnectivity -TestUrl $d.TestUrl -Method "GET" -UrlPattern $d.UrlPattern -ExpectedStatusCode $d.ExpectedStatusCode -Description $d.Description -PerformBluecoatLookup $d.PerformBluecoatLookup -IgnoreCertificateValidationErrors + $results.Add($connectivity) + $i += 1 +}#> + +foreach ($d in $data) { + $running = @(Get-Job | Where-Object { $_.State -eq 'Running' }) + if ($running.Count -ge 10) { + $running | Wait-Job -Any | Out-Null + } + + Write-Progress -Activity "Connectivity Tests" -status "Processing $($d.TestUrl)" -percentComplete ($i / $data.count*100) + + $i += 1 + Start-Job -ArgumentList @($d,$HttpConModulePath,$TcpConModulePath) -ScriptBlock { + param($d,$HttpConModulePath,$TcpConModulePath) + + + if($d.TestType -eq "HTTP"){ + Import-Module $HttpConModulePath + $connectivity = Get-HttpConnectivity -TestUrl ($d.Protocol + $d.TestUrl) -Method "GET" -UrlPattern ($d.Protocol + $d.UrlPattern) -ExpectedStatusCode 200 -Description $d.Description -IgnoreCertificateValidationErrors + } else { + Import-Module $TcpConModulePath + $connectivity = Get-TcpConnectivity -TestHostname $d.TestUrl -TestPort $d.UsePort -HostnamePattern ($d.UrlPattern + ":" + $d.UsePort) -ExpectedStatusCode 1 -Description $d.Description + } + $connectivity + } | Out-Null +} + +# Wait for all jobs to complete and results ready to be received +Wait-Job * | Out-Null + +# Process the results +foreach($job in Get-Job) +{ + $result = Receive-Job $job -AutoRemoveJob -Wait + $results.Add($result) +} + +$CachedResults = $results | Foreach-Object { [pscustomobject]@{ + UnblockUrl = $_.UnblockUrl; + ActualStatusCode = $_.ActualStatusCode; + Blocked = $_.Blocked; + HasError = $_.ServerCertificate.HasError; + }} | Select-Object UnblockUrl,ActualStatusCode,HasError,Blocked +Out-File -FilePath "$ModulePath\Data\AzureEndpointExpectedResults.json" -InputObject ($CachedResults | ConvertTo-Json) -Force +#endregion Expected Results \ No newline at end of file diff --git a/README.md b/README.md index 46449e7..2d77a7e 100644 --- a/README.md +++ b/README.md @@ -19,14 +19,14 @@ The following functions are available now: * Get-MDMPSScriptStatus --> Returns information about the execution of PowerShell Scripts deployed with Intune. * Get-BCStatusDetailed --> Returns Branch Cache usage statistsics of the last downloads per source host including peer usage statistics. * Invoke-AnalyzeDeliveryOptimization --> Analyze Delivery Optimization Configuration and connectifity on a device. +* Invoke-AnalyzeAzureConnectivity --> Check for connectivity issues to O365 and Azure based on the actual published list of Microsoft(https://docs.microsoft.com/en-us/office365/enterprise/urls-and-ip-address-ranges). The following functions will be available in the near future: * Autopilot Troubleshooting * Improvement Intune Enrollment Troubleshooting -* Intune MSI App Installation Troubleshooting -* Intune PowerShell Script Installation Troubleshooting -* BranchCache and Delivery Optimization Troubleshooting +* Improvement Intune MSI App Installation Troubleshooting +* Improvement Intune PowerShell Script Installation Troubleshooting * Pester Tests ### Usage diff --git a/ReleaseNotes.md b/ReleaseNotes.md index bdf25ec..dcb98ed 100644 --- a/ReleaseNotes.md +++ b/ReleaseNotes.md @@ -4,6 +4,8 @@ * Extended Azure AD Hybrid Join checks to include User Device Registration Event Log Invoke-AnalyzeHybridJoinStatus * Check manually defined IE Intranet Sites Invoke-AnalyzeHybridJoinStatus +* Added TcpConnectivityTester Module to check Non HTTP Connections +* Added Invoke-AnalyzeAzureConnectivity to check for connectivity issues to O365 and Azure based on the actual published list of Microsoft. ## 0.1.9 - Delivery Optimization