Test if Microsoft services TCP ports are accessible

In a previous blog post, I showed how to retrieve all the Microsoft Services FQDNs, ports, and IP addresses. It’s nice to know those in secure environments where not everything can go onto the internet, but how can you test if they are accessible? This blog post will show how to test most of these services using PowerShell.

How the script works

It uses the method I used in this blog post to retrieve the most current list of services. It then checks for all the URLs in it and tries to connect to each TCP port. On-screen, while running the script, it will show the progress of the scan and will output it to an Out-GridView screen or a CSV file if specified.

Note: The Microsoft services have a few UDP ports, but this script does not check for them. I will try to update it to include UDP ports, but checking them is more complex than checking TCP ports.

Running the script

The function Test-MicrosoftEndpoints has a few parameters that you can use to search for specific products and services or to specify the location of the CSV output file:

  • -All, scans all the services and the associated TCP ports.
  • -CSVPath, the full path to the CSV file. For example, d:\data\endpointcheck.csv
  • -Note this parameter followed by ‘Exchange,’ for example, will search for all Exchange ports.
  • -ServiceDisplayName, this parameter followed by ‘OneDrive’ will search for all OneDrive ports.
  • -URL, this parameter followed by ‘Outlook,’ for example, will search for all URLs containing Outlook

Note: For a good overview of keywords to search for, create an output file using this blog post or go to this URL.

In the example below, I started the script with the -Note ‘Exchange’ parameter to test all Exchange TCP ports. As you can see, the script can’t check the wildcard (*) addresses and will show a warning for those addresses.

The results in the Out-GridView look like this: In the IPAddressUsed column, you can see which IP address was returned from the DNS query for outlook.office.com at that moment:

Below is another example in which I used the -Note ‘Stream’ and -CSVPath parameters to save the results for Microsoft Stream ports to d:\temp\output.csv.

The CSV file looks like this:

The script

Below is the script. Please save it to c:\data\Test-MicrosoftEndpoints.ps1, for example. You can run it in your current session by running ‘ . c:\data\Test-MicrosoftEndpoints.ps1’ to have the Test-MicrosoftEndpoints function available.

function Test-MicrosoftEndpoints {
    [CmdletBinding(DefaultParameterSetName = 'Default')]
    param (
        [parameter(parameterSetName = "All")][switch]$All,
        [parameter(Mandatory = $false)][string]$CSVPath,
        [parameter(parameterSetName = "Note")][string]$Note,
        [parameter(parameterSetName = "ServiceAreaDisplayName")][string]$ServiceAreaDisplayName,
        [parameter(parameterSetName = "URL")][string]$URL
    )

    #Hide download progress, get current JSON url, retrieve all Endpoints and Convert it from JSON format
    $ProgressPreference = "SilentlyContinue"
    try {
        $site = Invoke-WebRequest -Uri 'https://learn.microsoft.com/en-us/microsoft-365/enterprise/urls-and-ip-address-ranges?view=o365-worldwide' -UseBasicParsing
        $jsonlink = ($site.Links | where-Object OuterHTML -match 'JSON formatted').href
    }
    catch {
        Write-Warning ("Error downloading JSON file, please check if https://learn.microsoft.com/en-us/microsoft-365/enterprise/urls-and-ip-address-ranges?view=o365-worldwide is accessible")
        return 
    }

    try {
        $Endpoints = Invoke-WebRequest -Uri $jsonlink -ErrorAction Stop | ConvertFrom-Json
        Write-Host ("Downloading worldwide Microsoft Endpoints") -ForegroundColor Green
    }
    catch {
        Write-Warning ("Error downloading worldwide Microsoft Endpoints, please check if {0} is accessible" -f $jsonlink)
        return
    }

    #Search for specified parameter value
    if ($All) {
        $TestEndpoints = $Endpoints | Where-Object urls -ne $null | Select-Object urls, tcpports, udpports, ips, notes
    }

    if ($note) {
        $TestEndpoints = $Endpoints | Where-Object Notes -Match $note | Select-Object urls, tcpports, udpports, ips, notes
    }

    if ($ServiceAreaDisplayName) {
        $TestEndpoints = $Endpoints | Where-Object ServiceAreaDisplayName -Match $ServiceAreaDisplayName | Select-Object urls, tcpports, udpports, ips, notes
    }

    if ($URL) {
        $TestEndpoints = $Endpoints | Where-Object urls -Match $URL | Select-Object urls, tcpports, udpports, ips, notes
    }

    if ($null -eq $TestEndpoints) {
        Write-Warning ("No results found...")
        return
    }

    #Test Microsoft Endpoint Adresses and report if failed or succeeded
    $Global:ProgressPreference = 'SilentlyContinue'
    $total = foreach ($TestEndpoint in $TestEndpoints) {
        if ($TestEndpoint.tcpPorts) {
            foreach ($tcpport in $TestEndpoint.tcpPorts.split(',')) {
                foreach ($testurl in $TestEndpoint.urls) {
                    if ($TestEndpoint.notes) {
                        $notes = $TestEndpoint.notes
                    }
                    else {
                        $notes = "No notes available"
                    }
                    #Skip wildcard adresses because these are note resolvable
                    if ($testurl.Contains("*")) {
                        Write-Warning ("Skipping {0} because it's a wildcard address" -f $testurl)
                        $Status = "Failed or couldn't resolve DNS name"
                        $ipaddress = "Not applicable"
                    }
                    else {
                        #Test connection and retrieve all information
                        $test = Test-NetConnection -Port $tcpport -ComputerName $testurl -ErrorAction SilentlyContinue -InformationLevel Detailed 
                        if ($test.TcpTestSucceeded -eq $true) {
                            $Status = 'Succeeded'
                            $ipaddress = $test.RemoteAddress
                            Write-Host ("{0} is reachable on TCP port {1} ({2}) using IP-Address {3}" -f $testurl, $tcpport, $notes, $ipaddress) -ForegroundColor Green
                        
                        }
                        else {
                            $Status = "Failed or couldn't resolve DNS name"
                            $ipaddress = "Not applicable"
                        }
                    }
                    #Set iprange variable if applicable
                    if ($TestEndpoint.ips) {
                        $iprange = $TestEndpoint.ips -join (', ')
                    }
                    else {
                        $iprange = "Not applicable"
                    }
                    [PSCustomObject]@{
                        Status          = $Status
                        URL             = $testurl
                        TCPport         = $tcpport
                        IPAddressUsed   = $ipaddress
                        Notes           = $notes
                        EndpointIPrange = $iprange
                    }
                }
            }
        }
    }

    #Output results to Out-Gridview or CSV
    if (-not $CSVPath) {
        Write-Host ("Output results to Out-GridView `nDone!") -ForegroundColor Green
        $total | Sort-Object Url, TCPport | Out-GridView -Title 'Microsoft Endpoints Test results'
    }
    else {
        try {
            New-Item -Path $CSVPath -ItemType File -Force:$true -ErrorAction Stop | Out-Null
            $Total | Sort-Object Url, TCPport | Export-Csv -Path $CSVPath -Encoding UTF8 -Delimiter ';' -NoTypeInformation
            Write-Host ("Saved results to {0} `nDone!" -f $CSVPath) -ForegroundColor Green
        }
        catch {
            Write-Warning ("Could not save results to {0}" -f $CSVPath)
        }
    }
}

Download the script(s) from GitHub here

6 thoughts on “Test if Microsoft services TCP ports are accessible

    • That’s strange, no errors while testing in PowerShell 7 in VSCode… But I added it now, if this helps for other people… Then it’s good, it still works for me too after adding 🙂

  1. Pingback: Endpoint Manager Newsletter - 30th September 2022 - Andrew Taylor

  2. I guess one comment I’ll add is that name resolution will obviously fail for *.cloud.microsoft and others like these:

    WARNING: Name resolution of *.cloud.microsoft failed
    WARNING: Name resolution of *.static.microsoft failed
    WARNING: Name resolution of *.usercontent.microsoft failed
    WARNING: Name resolution of *.cloud.microsoft failed
    WARNING: Name resolution of *.static.microsoft failed
    WARNING: Name resolution of *.usercontent.microsoft failed

    Is there any way to test them?

    • Yes, I saw that a few weeks ago when I was checking this script again for updates to it. I think I will skip wildcard addresses, because they are not revolving. Or, if that works, try a random name in that just to check if it will give a response.

      I will put this on my to do list 👍

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.