Office365IPAddress.ps1


<#PSScriptInfo

.VERSION 1.1.6

.GUID e5d18bf9-f775-4a7a-adff-f3da4de7f72f

.AUTHOR timmcmic

.COMPANYNAME Microsoft

.COPYRIGHT

.TAGS

.LICENSEURI

.PROJECTURI

.ICONURI

.EXTERNALMODULEDEPENDENCIES

.REQUIREDSCRIPTS

.EXTERNALSCRIPTDEPENDENCIES

.RELEASENOTES


.PRIVATEDATA

#>


<#

.DESCRIPTION
 This script tests to see if an IP address is contained within the Office 365 URL and IP address ranges.

#>
 
Param(
    [Parameter(Mandatory = $false)]
    [string]$IPAddressToTest="0.0.0.0",
    [Parameter(Mandatory = $false)]
    [string]$URLToTest="nodomain.local",
    [Parameter(Mandatory = $false)]
    [string]$portToTest="0",
    [Parameter(Mandatory = $true)]
    [string]$logFolderPath=$NULL,
    [Parameter(Mandatory = $false)]
    [boolean]$allowQueryIPLocationInformationFromThirdParty=$TRUE
)

$ErrorActionPreference = 'Stop'

#Following function credit to author -> https://github.com/fleschutz/PowerShell/blob/main/docs/check-ipv4-address.md
function IsIPv4AddressValid { param([string]$IP)
    $RegEx = "^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"
    if ($IP -match $RegEx) {
        return $true
    } else {
        return $false
    }
}

#Follwoing function credit to author -> https://github.com/fleschutz/PowerShell/blob/main/docs/check-ipv6-address.md

function IsIPv6AddressValid { param([string]$IP)
    $IPv4Regex = '(((25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})\.){3}(25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2}))'
    $G = '[a-f\d]{1,4}'
    $Tail = @(":",
    "(:($G)?|$IPv4Regex)",
    ":($IPv4Regex|$G(:$G)?|)",
    "(:$IPv4Regex|:$G(:$IPv4Regex|(:$G){0,2})|:)",
    "((:$G){0,2}(:$IPv4Regex|(:$G){1,2})|:)",
    "((:$G){0,3}(:$IPv4Regex|(:$G){1,2})|:)",
    "((:$G){0,4}(:$IPv4Regex|(:$G){1,2})|:)")
    [string] $IPv6RegexString = $G
    $Tail | foreach { $IPv6RegexString = "${G}:($IPv6RegexString|$_)" }
    $IPv6RegexString = ":(:$G){0,5}((:$G){1,2}|:$IPv4Regex)|$IPv6RegexString"
    $IPv6RegexString = $IPv6RegexString -replace '\(' , '(?:' # make all groups non-capturing
    [regex] $IPv6Regex = $IPv6RegexString
    if ($IP -imatch "^$IPv6Regex$") {
        return $true
    } else {
        return $false
    }
}

function create-OutputObject
{
    Param
    (
        [Parameter(Mandatory = $true)]
        $M365Instance,
        [Parameter(Mandatory = $true)]
        $id,
        [Parameter(Mandatory = $true)]
        $ServiceArea,
        [Parameter(Mandatory = $true)]
        $ServiceAreaDisplayName,
        [Parameter(Mandatory = $true)]
        [AllowEmptyString()]
        [AllowNull()]
        $URLs,
        [Parameter(Mandatory = $true)]
        [AllowEmptyString()]
        [AllowNull()]
        $IPs,
        [Parameter(Mandatory = $true)]
        $IPInSubnetorURL,
        [Parameter(Mandatory = $true)]
        [AllowEmptyString()]
        [AllowNull()]
        $TCPPorts,
        [Parameter(Mandatory = $true)]
        [AllowEmptyString()]
        [AllowNull()]
        $UDPPorts,
        [Parameter(Mandatory = $true)]
        $ExpressRoute,
        [Parameter(Mandatory = $true)]
        $Required,
        [Parameter(Mandatory = $true)]
        [AllowEmptyString()]
        [AllowNull()]
        $Notes,
        [Parameter(Mandatory = $true)]
        [AllowEmptyString()]
        [AllowNull()]
        $Category
    )
    
    $outputObject = new-Object psObject -property $([ordered]@{
        M365Instance = $M365Instance
        ID = $ID
        ServiceArea = $ServiceArea
        ServiceAreaDisplayName = $ServiceAreaDisplayName
        URLs = $URLs
        IPs = $ips
        IPInSubnetorURL = $IPInSubnetorURL
        TCPPorts = $tcpports
        UDPPorts = $udpPorts
        ExpressRoute = $expressRoute
        Required = $required
        Notes = $notes
        Category = $Category
    })

    return $outputObject
}

function create-OutputChangebject
{
    Param
    (
        [Parameter(Mandatory = $true)]
        [AllowEmptyString()]
        [AllowNull()]
        $M365Instance,
        [Parameter(Mandatory = $true)]
        [AllowEmptyString()]
        [AllowNull()]
        $ChangeID,
        [Parameter(Mandatory = $true)]
        [AllowEmptyString()]
        [AllowNull()]
        $Disposition,
        [Parameter(Mandatory = $true)]
        [AllowEmptyString()]
        [AllowNull()]
        $EndpointSetID,
        [Parameter(Mandatory = $true)]
        [AllowEmptyString()]
        [AllowNull()]
        $Version,
        [Parameter(Mandatory = $true)]
        [AllowEmptyString()]
        [AllowNull()]
        $ServiceAreaDisplayName,
        [Parameter(Mandatory = $true)]
        [AllowEmptyString()]
        [AllowNull()]
        $IPsAddredorRemoved,
        [Parameter(Mandatory = $true)]
        [AllowEmptyString()]
        [AllowNull()]
        $URLsAddedOrRemoved,
        [Parameter(Mandatory = $true)]
        [AllowEmptyString()]
        [AllowNull()]
        $IPInSubnetOrURL,
        [Parameter(Mandatory = $true)]
        [AllowEmptyString()]
        [AllowNull()]
        $PreviousCategory,
        [Parameter(Mandatory = $true)]
        [AllowEmptyString()]
        [AllowNull()]
        $PreviousExpressRoute,
        [Parameter(Mandatory = $true)]
        [AllowEmptyString()]
        [AllowNull()]
        $PreviousServiceArea,
        [Parameter(Mandatory = $true)]
        [AllowEmptyString()]
        [AllowNull()]
        $PreviousRequire,
        [Parameter(Mandatory = $true)]
        [AllowEmptyString()]
        [AllowNull()]
        $PreviousTCPPort
    )
    
    $outputObject = new-Object psObject -property $([ordered]@{
        M365Instance = $M365Instance
        ChangeID = $ChangeID
        Disposition = $Disposition
        EndpointSetID = $EndpointSetID
        Version = $Version
        ServiceAreaDisplayName = $ServiceAreaDisplayName
        IPsAddedorRemoved = $IPsAddredorRemoved
        URLsAddedOrRemoved = $URLsAddedOrRemoved
        IPInSubnetorURL = $IPInSubnetOrURL
        PreviousCategory = $PreviousCategory
        PreviousExpressRoute = $PreviousExpressRoute
        PreviousServiceArea = $PreviousServiceArea
        PreviousRequire = $PreviousRequire
        PreviousTCPPorts = $PreviousTCPPort
    })

    return $outputObject
}


Function new-LogFile
{
    [cmdletbinding()]

    Param
    (
        [Parameter(Mandatory = $true)]
        [string]$logFileName,
        [Parameter(Mandatory = $true)]
        [string]$logFolderPath
    )

    [string]$logFileSuffix=".log"
    [string]$fileName=$logFileName+$logFileSuffix

    # Get our log file path

    $logFolderPath = $logFolderPath+"\"+$logFileName+"\"
    
    #Since $logFile is defined in the calling function - this sets the log file name for the entire script
    
    $global:LogFile = Join-path $logFolderPath $fileName

    #Test the path to see if this exists if not create.

    [boolean]$pathExists = Test-Path -Path $logFolderPath

    if ($pathExists -eq $false)
    {
        try 
        {
            #Path did not exist - Creating

            New-Item -Path $logFolderPath -Type Directory
        }
        catch 
        {
            throw $_
        } 
    }
}
Function Out-LogFile
{
    [cmdletbinding()]

    Param
    (
        [Parameter(Mandatory = $true)]
        $String,
        [Parameter(Mandatory = $false)]
        [boolean]$isError=$FALSE
    )

    # Get the current date

    [string]$date = Get-Date -Format G

    # Build output string
    #In this case since I abuse the function to write data to screen and record it in log file
    #If the input is not a string type do not time it just throw it to the log.

    if ($string.gettype().name -eq "String")
    {
        [string]$logstring = ( "[" + $date + "] - " + $string)
    }
    else 
    {
        $logString = $String
    }

    # Write everything to our log file and the screen

    $logstring | Out-File -FilePath $global:LogFile -Append

    #Write to the screen the information passed to the log.

    if ($string.gettype().name -eq "String")
    {
        Write-Host $logString
    }
    else 
    {
        write-host $logString | select-object -expandProperty *
    }

    #If the output to the log is terminating exception - throw the same string.

    if ($isError -eq $TRUE)
    {
        #Ok - so here's the deal.
        #By default error action is continue. IN all my function calls I use STOP for the most part.
        #In this case if we hit this error code - one of two things happen.
        #If the call is from another function that is not in a do while - the error is logged and we continue with exiting.
        #If the call is from a function in a do while - write-error rethrows the exception. The exception is caught by the caller where a retry occurs.
        #This is how we end up logging an error then looping back around.

        write-error $logString

        #Now if we're not in a do while we end up here -> go ahead and create the status file this was not a retryable operation and is a hard failure.

        exit
    }
}

function get-ClientGuid
{
    $functionClientGuid = $NULL

    out-logfile -string "Entering new-ClientGuid"

    try
    {   
        out-logfile -string "Obtain client GUID."
        $functionClientGuid = new-GUID -errorAction STOP
        out-logfile -string "Client GUID obtained successfully."
    }
    catch {
        out-logfile -string $_
        out-logfile -string "Unable to obtain client GUID." -isError:$true
    }

    out-logfile -string "Exiting new-ClientGuid"

    return $functionClientGuid
}

function get-Office365IPInformation
{
    Param
    (
        [Parameter(Mandatory = $true)]
        $baseURL
    )

    $functionVersionInfo = $NULL

    out-logfile -string "Entering get-Office365IPInformation"

    try
    {   
        out-logfile -string 'Invoking web request for information...'
        $functionVersionInfo = Invoke-WebRequest -Uri $baseURL -errorAction:STOP
        out-logfile -string 'Invoking web request complete...'
    }
    catch {
        out-logfile -string $_
        out-logfile -string "Unable to invoke web request for Office 365 URL and IP information." -isError:$TRUE
    }

    out-logfile -string "Exiting get-Office365IPInformation"

    return $functionVersionInfo
}

function get-webURL
{
    Param
    (
        [Parameter(Mandatory = $true)]
        [string]$baseURL,
        [Parameter(Mandatory = $true)]
        [string]$clientGuid
    )

    $functionURL = $NULL

    out-logfile -string "Entering get-webURL"

    $functionURL = $baseURL+$clientGuid

    out-logfile -string "Exiting get-webURL"

    return $functionURL
}

function get-jsonData
{
    Param
    (
        [Parameter(Mandatory = $true)]
        $data
    )

    $functionData = $NULL

    out-logfile -string "Entering get-jsonData"

    try {
        $functionData = convertFrom-Json $data -errorAction Stop
    }
    catch {
        out-logfile -string $_
        out-logfile -string "Unable to convert json data." -isError:$true
    }

    out-logfile -string "Exiting get-jsonData"

    return $functionData
}

function test-IPSpace
{
    Param
    (
        [Parameter(Mandatory = $true)]
        $dataToTest,
        [Parameter(Mandatory = $true)]
        $IPAddress,
        [Parameter(Mandatory = $true)]
        $portToTest,
        [Parameter(Mandatory = $true)]
        $RegionString
    )

    $functionNetwork = $NULL
    $functionComma = ","

    out-logfile -string "Entering test-IPSpace"

    foreach ($entry in $dataToTest)
    {
        Out-logfile -string ("Testing entry id: "+$entry.id)

        if ($entry.ips.count -gt 0)
        {
            out-logfile -string "IP count > 0"

            foreach ($ipEntry in $entry.ips)
            {
                $functionPortArray = @()

                out-logfile -string ("Testing entry IP: "+$ipEntry)

                 $functionNetwork = get-IPEntry -ipEntry $ipEntry

                 out-logfile -string ("BaseAddress: "+$functionNetwork.baseAddress+ " PrefixLength: "+$functionNetwork.PrefixLength)

                 if ($functionNetwork.Contains($IPAddress))
                 {
                    if ($portToTest -eq "0")
                    {
                        out-logfile -string "The IP to test is contained within the entry. Log the service."

                        $outputObject = create-outputObject -m365Instance $regionString -id $entry.id -serviceArea $entry.serviceArea -serviceAreaDisplayName $entry.serviceareadisplayname -urls $entry.urls -ips $entry.ips -ipInSubnetorURL $ipEntry -tcpPorts $entry.tcpPorts -udpPorts $entry.udpPorts -expressRoute $entry.expressRoute -required $entry.required -notes $entry.notes -category $entry.category
                            
                        out-logfile -string $outputObject

                        $global:outputArray += $outputObject
                    }
                    else
                    {
                        out-logfile -string "Extracting TCP Ports from entry..."

                        if ($entry.tcpPorts -ne $NULL)
                        {
                            $functionPortArray += $entry.tcpPorts.split($functionComma)
                        }

                        out-logfile -string "Extracting UDP Ports from entry..."
                        
                        if ($entry.udpPorts -ne $NULL)
                        {
                            $functionPortArray += $entry.udpPorts.split($functionComma)
                        }

                        out-logfile -string "Selecting unique ports..."

                        $functionPortArray = $functionPortArray | select-object -Unique

                        out-logfile -string "Removing trailing or leading spaces from any port..."

                        for ($i = 0 ; $i -lt $functionPortArray.count ; $i++)
                        {
                            $functionPortArray[$i] = $functionPortArray[$i].replace(" ","")
                            out-logfile -string $functionPortArray[$i]
                        }

                        if ($functionPortArray.contains($portToTest))
                        {
                            out-logfile -string "The IP to test is contained within the entry. Log the service."

                            $outputObject = create-outputObject -m365Instance $regionString -id $entry.id -serviceArea $entry.serviceArea -serviceAreaDisplayName $entry.serviceareadisplayname -urls $entry.urls -ips $entry.ips -ipInSubnetorURL $ipEntry -tcpPorts $entry.tcpPorts -udpPorts $entry.udpPorts -expressRoute $entry.expressRoute -required $entry.required -notes $entry.notes -category $entry.category
                                
                            out-logfile -string $outputObject

                            $global:outputArray += $outputObject
                        }
                    }
                 }
                 else
                 {
                    out-logfile -string "The IP to test is not contained within the entry - move on."
                 }
            }
        }
        else 
        {
            out-logfile -string "IP count = 0 -> skipping"
        }
    }

    out-logfile -string "Exiting test-IPSpace"
}

function get-IPEntry
{
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory = $true)]
        $ipEntry
    )

    $functionIPEntry = [System.Net.IpNetwork]::Parse($ipEntry)
    
    return $functionIPEntry
}

function calculate-WildCardURL
{
    Param
    (
        [Parameter(Mandatory = $true)]
        $urlEntry,
        [Parameter(Mandatory = $true)]
        $urlToTest
    )

    $functionPeriod = "."
    $functionWildCard = "*"
    $functionSplitURLToTest = @()
    $functionSplitURLEntry =@()
    $functionSplit = @()
    $functionSplitURLEntryCount = 0
    $functionSplitURLToTestCount = 0
    
    out-logfile -string "The URL entry contains a wild card - rebuild the URL to test."

    out-logfile -string "URL to test split by period..."

    $functionSplitURLToTest = $urlToTest.split($functionPeriod)

    foreach ($member in $functionSplitURLToTest)
    {
        out-logfile -string $member
    }

    out-logfile -string "URL Entry split by period..."

    $functionSplitURLEntry = $urlEntry.split($functionPeriod)

    foreach ($member in $functionSplitURLEntry)
    {
        out-logfile -string $member
    }

    out-logfile -string "URL to test reverse split by period..."

    $functionSplitURLToTestReverse = $functionSplitURLToTest[-1..-($functionSplitURLToTest.Length)]
    
    foreach ($member in $functionSplitURLToTestReverse)
    {
        out-logfile -string $member
    }

    out-logfile -string "URL Entry reverse split by period..."

    $functionSplitURLEntryReverse = $functionSplitURLEntry[-1..-($functionSplitURLEntry.Length)]

    foreach ($member in $functionSplitURLEntryReverse)
    {
        out-logfile -string $member
    }

    $functionSplitURLEntryCount = $functionSplitURLEntryReverse.count
    out-logfile -string $functionSplitURLEntryCount.tostring()
    $functionSplitURLToTestCount = $functionSplitURLToTestReverse.count
    out-logfile -string $functionSplitURLToTestCount.tostring()

    if ($functionSplitURLToTestCount -gt $functionSplitURLEntryCount)
    {
        out-logfile -string "The URL to test is > the URL entry."

        for ($i = 0 ; $i -lt $functionSplitURLToTestReverse.count ; $i++)
        {
            if ($functionSplitURLEntryReverse[$i] -eq $functionWildCard)
            {
                out-logfile -string "Wild card character found - injecting and existing loop."
                $functionSplit += $functionWildCard
                $i = $functionSplitURLToTestReverse.count+1
            }
            else 
            {
                $functionSplit += $functionSplitURLToTestReverse[$i]
            }
        }
    }
    elseif ($functionSplitURLToTestCount -le $functionSplitURLEntryCount)
    {
        out-logfile -string "The URL to test is > the URL entry."

        for ($i = 0 ; $i -lt $functionSplitURLToTestReverse.count ; $i++)
        {
            if ($functionSplitURLEntryReverse[$i] -eq $functionWildCard)
            {
                out-logfile -string "Wild card character found - injecting and existing loop."
                $functionSplit += $functionWildCard
            }
            else 
            {
                $functionSplit += $functionSplitURLToTestReverse[$i]
            }
        }
    }

    foreach ($member in $functionSplit)
    {
        out-logfile -string $member
    }

    $functionSplitReverse = $functionSplit[-1..-($functionSplit.Length)]

    foreach ($member in $functionSplitReverse)
    {
        out-logfile -string $member
    }

    $functionTestURL=$functionSplitReverse[0]

    for ($i=1;$i -lt $functionSplitReverse.count ; $i++)
    {
        $functionTestURL = $functionTestURL + $functionPeriod
        $functionTestURL = $functionTestURL + $functionSplitReverse[$i]
    }

    out-logfile -string $functionTestURL

    return $functionTestURL
}

function test-URLSpace
{
    Param
    (
        [Parameter(Mandatory = $true)]
        $dataToTest,
        [Parameter(Mandatory = $true)]
        $URLToTest,
        [Parameter(Mandatory = $true)]
        $RegionString
    )

    $functionWildCard = "*"
    $functionPeriod = "."
    $functionComma = ","
    $functionSplitURLToTest = @()

    out-logfile -string "Entering test-URLSpace"

    foreach ($entry in $dataToTest)
    {
        Out-logfile -string ("Testing entry id: "+$entry.id)

        if ($entry.urls.count -gt 0)
        {
            out-logfile -string "URL count > 0"

            foreach ($urlEntry in $entry.URLs)
            {
                $functionPortArray = @()
                #If the URL entry is a wild card - rebuid the URL to test to contain the wild card.

                out-logfile -string "Determine if the url entry is a wild card URL."

                if ($urlEntry.contains($functionWildCard))
                {
                    $functionTestURL = calculate-WildCardURL -urlEntry $urlEntry -urlToTest $URLToTest
                    out-logfile -string $functionTestURL
                }
                else
                {
                    $functionTestURL = $URLToTest
                    out-logfile -string "The URL entry does not contain a wild card - use the URLToTest value."
                }

                if ($urlEntry -eq $functionTestURL)
                {
                    if ($portToTest -eq "0")
                    {
                        out-logfile -string "The IP to test is contained within the entry. Log the service."

                        $outputObject = create-outputObject -m365Instance $regionString -id $entry.id -serviceArea $entry.serviceArea -serviceAreaDisplayName $entry.serviceareadisplayname -urls $entry.urls -ips $entry.ips -ipInSubnetorURL $urlEntry -tcpPorts $entry.tcpPorts -udpPorts $entry.udpPorts -expressRoute $entry.expressRoute -required $entry.required -notes $entry.notes -category $entry.category
                            
                        out-logfile -string $outputObject

                        $global:outputArray += $outputObject
                    }
                    else
                    {
                        out-logfile -string "Extracting TCP Ports from entry..."

                        if ($entry.tcpPorts -ne $NULL)
                        {
                            $functionPortArray += $entry.tcpPorts.split($functionComma)
                        }

                        out-logfile -string "Extracting UDP Ports from entry..."
                        
                        if ($entry.udpPorts -ne $NULL)
                        {
                            $functionPortArray += $entry.udpPorts.split($functionComma)
                        }

                        foreach ($member in $functionPortArray)
                        {
                            out-logfile -string $member
                        }

                        out-logfile -string "Selecting unique ports..."

                        $functionPortArray = $functionPortArray | select-object -Unique

                        for ($i = 0 ; $i -lt $functionPortArray.count ; $i++)
                        {
                            $functionPortArray[$i] = $functionPortArray[$i].replace(" ","")
                            out-logfile -string $functionPortArray[$i]
                        }

                        if ($functionPortArray.contains($portToTest))
                        {
                            out-logfile -string "The IP to test is contained within the entry. Log the service."

                            $outputObject = create-outputObject -m365Instance $regionString -id $entry.id -serviceArea $entry.serviceArea -serviceAreaDisplayName $entry.serviceareadisplayname -urls $entry.urls -ips $entry.ips -ipInSubnetorURL $urlEntry -tcpPorts $entry.tcpPorts -udpPorts $entry.udpPorts -expressRoute $entry.expressRoute -required $entry.required -notes $entry.notes -category $entry.category
                                
                            out-logfile -string $outputObject

                            $global:outputArray += $outputObject
                        }
                    }
                }
                else
                {
                out-logfile -string "The url to test is not contained within the entry - move on."
                }
            }
        }
        else 
        {
            out-logfile -string "url count = 0 -> skipping"
        }
    }

    out-logfile -string "Exiting test-urlSpace"
}

function test-IPChangeSpace
{
    Param
    (
        [Parameter(Mandatory = $true)]
        $dataToTest,
        [Parameter(Mandatory = $true)]
        $changeDataToTest,
        [Parameter(Mandatory = $true)]
        $IPAddress,
        [Parameter(Mandatory = $true)]
        $RegionString
    )

    $functionNetwork = $NULL
    $functionOriginalID = $null
    $functionServiceAreaDisplayName = $NULL

    out-logfile -string "Entering test-IPSpace"

    foreach ($entry in $changeDataToTest)
    {
        Out-logfile -string ("Testing change entry id: "+$entry.id)

        if ($entry.add.ips.count -gt 0)
        {
            out-logfile -string "IP count > 0"

            foreach ($ipEntry in $entry.add.ips)
            {
                out-logfile -string ("Testing entry IP: "+$ipEntry)

                 $functionNetwork = get-IPEntry -ipEntry $ipEntry

                 out-logfile -string ("BaseAddress: "+$functionNetwork.baseAddress+ " PrefixLength: "+$functionNetwork.PrefixLength)

                 if ($functionNetwork.Contains($IPAddress))
                 {
                    out-logfile -string "The IP to test is contained within the entry. Log the service."

                    $functionOriginalID = $datatoTest | where {$_.id -eq $entry.EndpointSetID}

                    if ($functionOriginalID.ServiceAreaDisplayName -eq $NULL)
                    {
                        $functionServiceAreaDisplayName = "Endpoint Set ID No Longer Active"
                    }
                    else
                    {
                        $functionServiceAreaDisplayName = $functionOriginalID.ServiceAreaDisplayName
                    }

                    $outputObject = create-OutputChangebject -M365Instance $regionString -ChangeID $entry.ID -Disposition ($entry.Disposition+"-Add") -EndpointSetID $entry.endpointSetId -Version $entry.Version -ServiceAreaDisplayName $functionServiceAreaDisplayName -IPsAddredorRemoved $entry.add.ips -URLsAddedOrRemoved $entry.add.urls -IPInSubnetOrURL $ipEntry -PreviousCategory $entry.previous.Category -PreviousExpressRoute $entry.previous.expressRoute -PreviousServiceArea $entry.previous.serviceArea -PreviousRequire $entry.previous.required -PreviousTCPPort $entry.previous.tcpPorts

                    out-logfile -string $outputObject

                    $global:outputChangeArray += $outputObject
                 }
                 else
                 {
                    out-logfile -string "The IP to test is not contained within the entry - move on."
                 }
            }
        }
        else 
        {
            out-logfile -string "IP count = 0 -> skipping"
        }
    }

    out-logfile -string "Exiting test-IPSpace"
}

function test-URLChangeSpace
{
    Param
    (
        [Parameter(Mandatory = $true)]
        $dataToTest,
        [Parameter(Mandatory = $true)]
        $changeDataToTest,
        [Parameter(Mandatory = $true)]
        $urlToTest,
        [Parameter(Mandatory = $true)]
        $RegionString
    )

    $functionOriginalID = $null
    $functionServiceAreaDisplayName = $NULL
    $functionWildCard = "*"
    $functionPeriod = "."
    $functionSplitURLToTest = @()

    out-logfile -string "Entering test-URLChangeSpace"

    foreach ($entry in $changeDataToTest)
    {
        Out-logfile -string ("Testing change entry id: "+$entry.id)

        if ($entry.add.urls.count -gt 0)
        {
            out-logfile -string "URL count > 0"

            foreach ($urlEntry in $entry.add.urls)
            {
                out-logfile -string ("Testing entry url: "+$urlEntry)

                out-logfile -string "Determine if the url entry is a wild card URL."

                if ($urlEntry.contains($functionWildCard))
                {
                    $functionTestURL = calculate-WildCardURL -urlEntry $urlEntry -urlToTest $URLToTest
                    out-logfile -string $functionTestURL
                }
                else
                {
                    $functionTestURL = $URLToTest
                    out-logfile -string "The URL entry does not contain a wild card - use the URLToTest value."
                }

                if ($functionTestURL -eq $urlEntry)
                {
                    out-logfile -string "The IP to test is contained within the entry. Log the service."

                    $functionOriginalID = $datatoTest | where {$_.id -eq $entry.EndpointSetID}

                    if ($functionOriginalID.ServiceAreaDisplayName -eq $NULL)
                    {
                        $functionServiceAreaDisplayName = "Endpoint Set ID No Longer Active"
                    }
                    else
                    {

                        $functionServiceAreaDisplayName = $functionOriginalID.ServiceAreaDisplayName
                    }

                    $outputObject = create-OutputChangebject -M365Instance $regionString -ChangeID $entry.ID -Disposition ($entry.Disposition+"-Add") -EndpointSetID $entry.endpointSetId -Version $entry.Version -ServiceAreaDisplayName $functionServiceAreaDisplayName -IPsAddredorRemoved $entry.add.ips -URLsAddedOrRemoved $entry.add.urls -IPInSubnetOrURL $ipEntry -PreviousCategory $entry.previous.Category -PreviousExpressRoute $entry.previous.expressRoute -PreviousServiceArea $entry.previous.serviceArea -PreviousRequire $entry.previous.required -PreviousTCPPort $entry.previous.tcpPorts

                    out-logfile -string $outputObject

                    $global:outputChangeArray += $outputObject
                 }
                 else
                 {
                    out-logfile -string "The IP to test is not contained within the entry - move on."
                 }
            }
        }
        else 
        {
            out-logfile -string "IP count = 0 -> skipping"
        }
    }

    out-logfile -string "Exiting test-IPSpace"
}

function test-IPRemoveSpace
{
    Param
    (
        [Parameter(Mandatory = $true)]
        $dataToTest,
        [Parameter(Mandatory = $true)]
        $changeDataToTest,
        [Parameter(Mandatory = $true)]
        $IPAddress,
        [Parameter(Mandatory = $true)]
        $RegionString
    )

    $functionNetwork = $NULL
    $functionOriginalID = $null
    $functionServiceAreaDisplayName = $NULL

    out-logfile -string "Entering test-IPSpace"

    foreach ($entry in $changeDataToTest)
    {
        Out-logfile -string ("Testing change entry id: "+$entry.id)

        if ($entry.remove.ips.count -gt 0)
        {
            out-logfile -string "IP count > 0"

            foreach ($ipEntry in $entry.remove.ips)
            {
                out-logfile -string ("Testing entry IP: "+$ipEntry)

                 $functionNetwork = get-IPEntry -ipEntry $ipEntry
                 out-logfile -string ("BaseAddress: "+$functionNetwork.baseAddress+ " PrefixLength: "+$functionNetwork.PrefixLength)

                 if ($functionNetwork.Contains($IPAddress))
                 {
                    out-logfile -string "The IP to test is contained within the entry. Log the service."

                    $functionOriginalID = $datatoTest | where {$_.id -eq $entry.EndpointSetID}

                    if ($functionOriginalID.ServiceAreaDisplayName -eq $null)
                    {
                        $functionServiceAreaDisplayName = "Endpoint Set ID No Longer Active"
                    }
                    else
                    {
                        $functionServiceAreaDisplayName = $functionOriginalID.ServiceAreaDisplayName
                    }

                    $outputObject = create-OutputChangebject -M365Instance $regionString -ChangeID $entry.ID -Disposition ($entry.Disposition+"-Remove") -EndpointSetID $entry.endpointSetId -Version $entry.Version -ServiceAreaDisplayName $functionServiceAreaDisplayName -IPsAddredorRemoved $entry.remove.ips -URLsAddedOrRemoved $entry.remove.urls -IPInSubnetOrURL $ipEntry -PreviousCategory $entry.previous.Category -PreviousExpressRoute $entry.previous.expressRoute -PreviousServiceArea $entry.previous.serviceArea -PreviousRequire $entry.previous.required -PreviousTCPPort $entry.previous.tcpPorts

                    out-logfile -string $outputObject

                    $global:outputRemoveArray += $outputObject
                 }
                 else
                 {
                    out-logfile -string "The IP to test is not contained within the entry - move on."
                 }
            }
        }
        else 
        {
            out-logfile -string "IP count = 0 -> skipping"
        }
    }

    out-logfile -string "Exiting test-IPSpace"
}

function test-URLRemoveSpace
{
    Param
    (
        [Parameter(Mandatory = $true)]
        $dataToTest,
        [Parameter(Mandatory = $true)]
        $changeDataToTest,
        [Parameter(Mandatory = $true)]
        $urlToTest,
        [Parameter(Mandatory = $true)]
        $RegionString
    )

    $functionOriginalID = $null
    $functionServiceAreaDisplayName = $NULL
    $functionWildCard = "*"
    $functionPeriod = "."
    $functionSplitURLToTest = @()

    out-logfile -string "Entering test-URLChangeSpace"

    foreach ($entry in $changeDataToTest)
    {
        Out-logfile -string ("Testing change entry id: "+$entry.id)

        if ($entry.remove.urls.count -gt 0)
        {
            out-logfile -string "URL count > 0"

            foreach ($urlEntry in $entry.remove.urls)
            {
                out-logfile -string ("Testing entry url: "+$urlEntry)

                out-logfile -string "Determine if the url entry is a wild card URL."

                if ($urlEntry.contains($functionWildCard))
                {
                    $functionTestURL = calculate-WildCardURL -urlEntry $urlEntry -urlToTest $URLToTest
                    out-logfile -string $functionTestURL
                }
                else
                {
                    $functionTestURL = $URLToTest
                    out-logfile -string "The URL entry does not contain a wild card - use the URLToTest value."
                }

                if ($functionTestURL -eq $urlEntry)
                {
                    out-logfile -string "The IP to test is contained within the entry. Log the service."

                    $functionOriginalID = $datatoTest | where {$_.id -eq $entry.EndpointSetID}

                    if ($functionOriginalID.ServiceAreaDisplayName -eq $NULL)
                    {
                        $functionServiceAreaDisplayName = "Endpoint Set ID No Longer Active"
                    }
                    else
                    {

                        $functionServiceAreaDisplayName = $functionOriginalID.ServiceAreaDisplayName
                    }

                    $outputObject = create-OutputChangebject -M365Instance $regionString -ChangeID $entry.ID -Disposition ($entry.Disposition+"-Remove") -EndpointSetID $entry.endpointSetId -Version $entry.Version -ServiceAreaDisplayName $functionServiceAreaDisplayName -IPsAddredorRemoved $entry.remove.ips -URLsAddedOrRemoved $entry.remove.urls -IPInSubnetOrURL $urlEntry -PreviousCategory $entry.previous.Category -PreviousExpressRoute $entry.previous.expressRoute -PreviousServiceArea $entry.previous.serviceArea -PreviousRequire $entry.previous.required -PreviousTCPPort $entry.previous.tcpPorts

                    out-logfile -string $outputObject

                    $global:outputRemoveArray += $outputObject
                 }
                 else
                 {
                    out-logfile -string "The IP to test is not contained within the entry - move on."
                 }
            }
        }
        else 
        {
            out-logfile -string "IP count = 0 -> skipping"
        }
    }

    out-logfile -string "Exiting test-IPSpace"
}

function get-IPLocationInformation
{
    Param
    (
        [Parameter(Mandatory = $true)]
        $ipAddress
    )

    $functionData = ""
    $ipLocationProvider = "https://api.country.is/"
    $ipLocationQuery = $ipLocationProvider+$ipAddress

    out-logfile -string "Entering get-IPLocationInformation"

    try {
        $functionData = invoke-WebRequest $ipLocationQuery
    }
    catch {
        out-logfile -string $_
        out-logfile -string "Unable to invoke web request for geolocation lookup."
        $functionData = "Failed"
    }

    out-logfile -string "Exiting get-IPLocationInformation"

    return $functionData
}

Function Test-PowershellVersion
    {
    [cmdletbinding()]

    $functionPowerShellVersion = $NULL

    out-logfile -string "Entering Test-PowerShellVersion"

    #Write function parameter information and variables to a log file.

    $functionPowerShellVersion = $PSVersionTable.PSVersion

    out-logfile -string "Determining powershell version."
    out-logfile -string ("Major: "+$functionPowerShellVersion.major)
    out-logfile -string ("Minor: "+$functionPowerShellVersion.minor)
    out-logfile -string ("Patch: "+$functionPowerShellVersion.patch)
    out-logfile -string $functionPowerShellVersion

    if ($functionPowerShellVersion.Major -lt 7)
    {
        out-logfile -string "Powershell 7 and higher is required to run this script."
        out-logfile -string "Please run module from Powershell 7.x"
        out-logfile -string "" -isError:$true
    }
    else
    {
        out-logfile -string "Powershell version is not powershell 5.X proceed."
    }

    out-logfile -string "Exiting Test-PowerShellVersion"

}

#=====================================================================================
#Begin main function body.
#=====================================================================================

#Define function variables.

$noIPSpecified = "0.0.0.0"
$noURLSpecified = "nodomain.local"
$urlSlashes = "//"
$urlSlash = "/"
$functionURL
$functionDomainName

if (($IPAddressToTest -ne $noIPSpecified) -and ($URLToTest -ne $noURLSpecified))
{
    write-error "Specify either a URL or IP to test - do not specify both in the same command."
}

if ($IPAddressToTest -ne $noIPSpecified)
{
    write-host "IPAddress to test is not a null value - proceed with evaluation"

    if ($IPAddressToTest.contains("."))
    {
        $logFileName = $IPAddressToTest.replace(".","-")

        if (IsIPv4AddressValid -ip $ipAddressToTest)
        {
            write-host "IPv4 Address is in a valid format."
        }
        else 
        {
            write-error "IPv4 address specified and is not in a valid format."
        } 
    }
    else 
    {
        $logFileName = $IPAddressToTest.replace(":","-")

        if (IsIPv6AddressValid -ip $ipAddressToTest)
        {
            write-host "IPv6 address specified and is in proper format."
        }
        else 
        {
             write-error "IPv6 address specified and is not in a valid format."
        } 
    }
}
elseif ($URLToTest -ne $noURLSpecified)
{
    write-host "URL to test is not a null value - proceed with evaluation."

    write-host "Determine if the URL is specified as a domain name <or> web address."

    if ($urlToTest.contains($urlSlashes))
    {
        write-host "URL specified - break URL"

        $functionURL = $urlToTest.split($urlSlashes)

        foreach ($member in $functionURL)
        {
            write-host $member
        }

        write-host "Determine if more of the URL is specified."

        if ($functionURL[1].contains($urlSlash))
        {
            write-host "URL contains more information - break URL."

            $functionURL = $functionURL.split($urlSlash)

            foreach ($member in $functionURL)
            {
                write-host $member
            }

            write-host "Domain name represented in array location 1."

            write-host "Create the log directory based off domain name."
        }
        else 
        {
            write-host "No more of a URL is specified - domain in array position 1."
        }

        $functionDomainName = $functionURL[1]
        write-host $functionDomainName
    }
    else 
    {
        write-host "URL appears to only contain a domain name."

        $functionDomainName = $URLToTest
        $functionDomainName.replace(".","-")
    }

    $logFileName = $functionDomainName.replace(".","-")
}
else 
{
   write-host "Using a generic log file name."

   $logFileName = "Office365IPandURLTest"
}


$clientGuid = $NULL
$allVersionInfoBaseURL = "https://endpoints.office.com/version?ClientRequestId="
$allVersionInfoURL = $NULL
$allVersionInfo = $NULL

$allIPInformationWorldWideBaseURL = "https://endpoints.office.com/endpoints/Worldwide?ClientRequestId="
$allIPInformationChinaBaseURL = "https://endpoints.office.com/endpoints/China?ClientRequestId="
$allIPInformationUSGovGCCHighBaseURL = "https://endpoints.office.com/endpoints/USGovGCCHigh?ClientRequestId="
$allIPInformationUSGovDODBaseURL = "https://endpoints.office.com/endpoints/USGovDOD?ClientRequestId="

$allIPChangeInformationWorldWideBaseURL = "https://endpoints.office.com/changes/worldwide/0000000000?clientrequestid="
$allIPChangeInformationChinaBaseURL = "https://endpoints.office.com/changes/china/0000000000?clientrequestid="
$allIPChangeInformationUSGovGCCHighBaseURL = "https://endpoints.office.com/changes/usgovgcchigh/0000000000?clientrequestid="
$allIPChangeInformationUSGovDODBaseURL = "https://endpoints.office.com/changes/usgovdod/0000000000?clientrequestid="


$allIPInformationWorldWideURL = $NULL
$allIPInformationChinaURL = $NULL
$allIPInformationUSGovGCCHighURL = $NULL
$allIPInformationUSGovDODURL = $NULL

$allIPChangeInformationWorldWideURL = $NULL
$allIPChangeInformationChinaURL = $NULL
$allIPChangeInformationUSGovGCCHighURL = $NULL
$allIPChangeInformationUSGovDODURL = $NULL

$allIPInformationWorldWide = $NULL
$allIPInformationChina = $NULL
$allIPInfomrationUSGovGCCHigh = $NULL
$allIPInformationUSGovDOD = $NULL

$allIPChangeInformationWorldWide = $NULL
$allIPChangeInformationChina = $NULL
$allIPChangeInfomrationUSGovGCCHigh = $NULL
$allIPChangeInformationUSGovDOD = $NULL

$worldWideRegionString = "Microsoft 365 Worldwide (+GCC)"
$chinaRegionString = "Microsoft 365 operated by 21 Vianet"
$gccHighRegionString = "Microsoft 365 U.S. Government GCC High"
$dodRegionString = "Microsoft 365 U.S. Government DoD"

$ipLocation = ""

$global:outputArray = @()
$global:outputChangeArray=@()
$global:outputRemoveArray=@()

#Create the log file.

new-logfile -logFileName $logFileName -logFolderPath $logFolderPath

out-logfile -string $IPAddressToTest
out-logfile -string $URLToTest
out-logfile -string $portToTest

$outputXMLFile = $global:LogFile.replace(".log",".xml")
$outputChangeXMLFile = $global:LogFile.replace(".log","Adds.xml")
$outputRemoveXMLFile = $global:LogFile.replace(".log","Removes.xml")

out-logfile -string $global:LogFile
out-logfile -string $outputXMLFile
out-logfile -string $outputChangeXMLFile

#Start logging

out-logfile -string "*********************************************************************************"
out-logfile -string "Start Office365IPAddress"
out-logfile -string "*********************************************************************************"

Test-PowerShellVersion

out-logfile -string "Obtaining client guid for web requests."

$clientGuid = get-ClientGuid
$clientGUid = $clientGuid.tostring()    

out-logfile -string $clientGuid

out-logfile -string "Obtain and log version information for the query."

$allVersionInfoURL = get-webURL -baseURL $allVersionInfoBaseURL -clientGuid $clientGuid

out-logfile -string $allVersionInfoURL

$allVersionInfo = get-Office365IPInformation -baseURL $allVersionInfoURL

$allVersionInfo = get-jsonData -data $allVersionInfo

foreach ($version in $allVersionInfo)
{
    out-logfile -string ("Instance: "+$version.instance+" VersionInfo: "+$version.latest)
}

out-logfile -string "Calculate URLS for Office 365 IP Addresses"

$allIPInformationWorldWideURL = get-webURL -baseURL $allIPInformationWorldWideBaseURL -clientGuid $clientGuid
out-logfile -string $allIPInformationWorldWideURL
$allIPInformationChinaURL = get-webURL -baseURL $allIPInformationChinaBaseURL -clientGuid $clientGuid
out-logfile -string $allIPInformationChinaURL
$allIPInformationUSGovGCCHighURL = get-webURL -baseURL $allIPInformationUSGovGCCHighBaseURL -clientGuid $clientGuid
out-logfile -string $allIPInformationUSGovGCCHighURL
$allIPInformationUSGovDODURL = get-webURL -baseURL $allIPInformationUSGovDODBaseURL -clientGuid $clientGuid
out-logfile -string $allIPInformationUSGovDODURL

out-logfile -string "Calculate URLS for Office 365 IP Addresses Change"

$allIPChangeInformationWorldWideURL = get-webURL -baseURL $allIPChangeInformationWorldWideBaseURL -clientGuid $clientGuid
out-logfile -string $allIPChangeInformationWorldWideURL
$allIPChangeInformationChinaURL = get-webURL -baseURL $allIPChangeInformationChinaBaseURL -clientGuid $clientGuid
out-logfile -string $allIPChangeInformationChinaURL
$allIPChangeInformationUSGovGCCHighURL = get-webURL -baseURL $allIPChangeInformationUSGovGCCHighBaseURL -clientGuid $clientGuid
out-logfile -string $allIPChangeInformationUSGovGCCHighURL
$allIPChangeInformationUSGovDODURL = get-webURL -baseURL $allIPChangeInformationUSGovDODBaseURL -clientGuid $clientGuid
out-logfile -string $allIPChangeInformationUSGovDODURL

out-logfile -string "Obtain IP information for all available Office 365 instances."

$allIPInformationWorldWide = get-Office365IPInformation -baseURL $allIPInformationWorldWideURL
$allIPInformationChina = get-Office365IPInformation -baseURL $allIPInformationChinaURL
$allIPInfomrationUSGovGCCHigh = get-Office365IPInformation -baseURL $allIPInformationUSGovGCCHighURL
$allIPInformationUSGovDOD = get-Office365IPInformation -baseURL $allIPInformationUSGovDODURL

out-logfile -string "Obtain IP information for all available Office 365 instances changes."

$allIPChangeInformationWorldWide = get-Office365IPInformation -baseURL $allIPChangeInformationWorldWideURL
$allIPChangeInformationChina = get-Office365IPInformation -baseURL $allIPChangeInformationChinaURL
$allIPChangeInfomrationUSGovGCCHigh = get-Office365IPInformation -baseURL $allIPChangeInformationUSGovGCCHighURL
$allIPChangeInformationUSGovDOD = get-Office365IPInformation -baseURL $allIPChangeInformationUSGovDODURL

out-logfile -string "Convert IP information from JSON."

$allIPInformationWorldWide = get-jsonData -data $allIPInformationWorldWide
$allIPInformationChina = get-jsonData -data $allIPInformationChina
$allIPInfomrationUSGovGCCHigh = get-jsonData -data $allIPInfomrationUSGovGCCHigh
$allIPInformationUSGovDOD = get-jsonData -data $allIPInformationUSGovDOD

out-logfile -string "Convert IP information from JSON changes."

$allIPChangeInformationWorldWide = get-jsonData -data $allIPChangeInformationWorldWide
$allIPChangeInformationChina = get-jsonData -data $allIPChangeInformationChina
$allIPChangeInfomrationUSGovGCCHigh = get-jsonData -data $allIPChangeInfomrationUSGovGCCHigh
$allIPChangeInformationUSGovDOD = get-jsonData -data $allIPChangeInformationUSGovDOD

if ($IPAddressToTest -ne $noIPSpecified)
{
    out-logfile -string "Begin testing IP spaces for presence of the specified IP address."

    test-IPSpace -dataToTest $allIPInformationWorldWide -IPAddress $IPAddressToTest -regionString $worldWideRegionString -portToTest $portToTest
    test-IPSpace -dataToTest $allIPInformationChina -IPAddress $IPAddressToTest -regionString $chinaRegionString -portToTest $portToTest
    test-IPSpace -dataToTest $allIPInfomrationUSGovGCCHigh -IPAddress $IPAddressToTest -regionString $gccHighRegionString -portToTest $portToTest
    test-IPSpace -dataToTest $allIPInformationUSGovDOD -IPAddress $IPAddressToTest -regionString $dodRegionString -portToTest $portToTest
    
    if ($global:outputArray.count -gt 0)
    {
        out-logfile -string "IPs found in Active Service - test for changes."
    
        test-IPChangeSpace -dataToTest $allIPInformationWorldWide -IPAddress $IPAddressToTest -regionString $worldWideRegionString -changeDataToTest $allIPChangeInformationWorldWide
        test-IPChangeSpace -dataToTest $allIPInformationChina -IPAddress $IPAddressToTest -regionString $chinaRegionString -changeDataToTest $allIPChangeInformationChina
        test-IPChangeSpace -dataToTest $allIPInfomrationUSGovGCCHigh -IPAddress $IPAddressToTest -regionString $gccHighRegionString -changeDataToTest $allIPChangeInfomrationUSGovGCCHigh
        test-IPChangeSpace -dataToTest $allIPInformationUSGovDOD -IPAddress $IPAddressToTest -regionString $dodRegionString -changeDataToTest $allIPChangeInformationUSGovDOD
    }
    else 
    {
        out-logfile -string "No IP addresses found -> no need to test for additions."
    }
    
    if ($global:outputArray.count -eq 0)
    {
        out-logfile -string "Since the global output count is equal to 0 -> testing to see if the IP address was removed."
    
        test-IPRemoveSpace -dataToTest $allIPInformationWorldWide -IPAddress $IPAddressToTest -regionString $worldWideRegionString -changeDataToTest $allIPChangeInformationWorldWide
        test-IPRemoveSpace -dataToTest $allIPInformationChina -IPAddress $IPAddressToTest -regionString $chinaRegionString -changeDataToTest $allIPChangeInformationChina
        test-IPRemoveSpace -dataToTest $allIPInfomrationUSGovGCCHigh -IPAddress $IPAddressToTest -regionString $gccHighRegionString -changeDataToTest $allIPChangeInfomrationUSGovGCCHigh
        test-IPRemoveSpace -dataToTest $allIPInformationUSGovDOD -IPAddress $IPAddressToTest -regionString $dodRegionString -changeDataToTest $allIPChangeInformationUSGovDOD
    }
    else 
    {
        out-logfile -string "The IP address was found - no need to test for removals."
    }
    
    if ($global:outputArray.count -gt 0)
    {
        if ($allowQueryIPLocationInformationFromThirdParty -eq $TRUE)
        {
            $ipLocation = get-IPLocationInformation -ipAddress $ipAddressToTest
    
            out-logfile -string $ipLocation
    
            if ($ipLocation -ne "Failed")
            {
                out-logfile -string "Converting IP location JSON."
                $ipLocation = get-jsonData -data $ipLocation
            }
        }
    
        out-logfile -string "*"
        out-logfile -string "**"
        out-logfile -string "******************************************************"
        out-logfile -string ("The IP Address: "+$IPAddressToTest+ " was located in any Office 365 Services.")
    
        if ($ipLocation -ne "Failed")
        {   
            out-logfile -string ("The IP Address geo-location is: "+$ipLocation.country)
        }
        else 
        {
            out-logfile -string "Failed to determine the IP address location."
        }
    
        write-host "IP entries present in the following Office 365 Services:"
    
        foreach ($entry in $global:outputArray)
        {
            $entry
        }
    
        if ($global:outputChangeArray)
        {
            write-host "IP entries present in the changes file:"
    
            foreach ($entry in $global:outputChangeArray)
            {
                $entry
            }
        }
    
        out-logfile -string "A XML file containing the above entries is available in the log directory."
        out-logfile -string "******************************************************"
        out-logfile -string "**"
        out-logfile -string "*"
    
        if ($global:outputArray.count -gt 0)
        {
            out-logfile -string ""
            out-logfile -string "IPs was located in the following service description:"
            out-logfile -string ""
    
            foreach ($entry in $global:outputArray)
            {
                out-logfile -string $entry
            }
        
            $global:outputArray | Export-Clixml -Path $outputXMLFile
        }
    
        if ($global:outputChangeArray.count -gt 0)
        {
            out-logfile -string ""
            out-logfile -string "IP was located in the following version additions since 2018:"
            out-logfile -string ""
    
            foreach ($entry in $global:outputChangeArray)
            {
                out-logfile -string $entry
            }
    
            $global:outputChangeArray | Export-Clixml -Path $outputChangeXMLFile
        }  
    }
    else 
    {
        out-logfile -string "******************************************************"
        out-logfile -string ("The IP Address: "+$IPAddressToTest+ " was NOT located in the following Office 365 Services.")
        out-logfile -string "******************************************************"
    
        if ($global:outputRemoveArray.count -gt 0)
        {
            write-host "The following changes removed the IP address:"
    
            foreach ($entry in $global:outputRemoveArray)
            {
                $entry
            }
    
            foreach ($entry in $global:outputRemoveArray)
            {
                out-logfile -string $entry
            }
    
            $global:outputRemoveArray | Export-Clixml -Path $outputRemoveXMLFile
        }
    }
}
elseif ($urlToTest -ne $noURLSpecified)
{
    out-logfile -string "Begin testing URL spaces for presence of the specified IP address."

    test-URLSpace -dataToTest $allIPInformationWorldWide -urlToTest $functionDomainName -regionString $worldWideRegionString
    test-URLSpace -dataToTest $allIPInformationChina -urlToTest $functionDomainName -regionString $chinaRegionString
    test-URLSpace -dataToTest $allIPInfomrationUSGovGCCHigh -urlToTest $functionDomainName -regionString $gccHighRegionString
    test-URLSpace -dataToTest $allIPInformationUSGovDOD -urlToTest $functionDomainName -regionString $dodRegionString

    if ($global:outputArray.count -gt 0)
    {
        out-logfile -string "IPs found in Active Service - test for changes."
    
        test-URLChangeSpace -dataToTest $allIPInformationWorldWide -urlToTest $urlToTest -regionString $worldWideRegionString -changeDataToTest $allIPChangeInformationWorldWide
        test-URLChangeSpace -dataToTest $allIPInformationChina -urlToTest $urlToTest -regionString $chinaRegionString -changeDataToTest $allIPChangeInformationChina
        test-URLChangeSpace -dataToTest $allIPInfomrationUSGovGCCHigh -urlToTest $urlToTest -regionString $gccHighRegionString -changeDataToTest $allIPChangeInfomrationUSGovGCCHigh
        test-URLChangeSpace -dataToTest $allIPInformationUSGovDOD -urlToTest $urlToTest -regionString $dodRegionString -changeDataToTest $allIPChangeInformationUSGovDOD
    }
    else 
    {
        out-logfile -string "No IP addresses found -> no need to test for additions."
    }

    if ($global:outputArray.count -eq 0)
    {
        out-logfile -string "Since the global output count is equal to 0 -> testing to see if the IP address was removed."
    
        test-URLRemoveSpace -dataToTest $allIPInformationWorldWide -urlToTest $URLToTest -regionString $worldWideRegionString -changeDataToTest $allIPChangeInformationWorldWide
        test-URLRemoveSpace -dataToTest $allIPInformationChina -urlToTest $URLToTest -regionString $chinaRegionString -changeDataToTest $allIPChangeInformationChina
        test-URLRemoveSpace -dataToTest $allIPInfomrationUSGovGCCHigh -urlToTest $URLToTest -regionString $gccHighRegionString -changeDataToTest $allIPChangeInfomrationUSGovGCCHigh
        test-URLRemoveSpace -dataToTest $allIPInformationUSGovDOD -urlToTest $URLToTest -regionString $dodRegionString -changeDataToTest $allIPChangeInformationUSGovDOD
    }
    else 
    {
        out-logfile -string "The IP address was found - no need to test for removals."
    }

    if ($global:outputArray.count -gt 0)
    {
        out-logfile -string "*"
        out-logfile -string "**"
        out-logfile -string "******************************************************"
            
        write-host "URL entries present in the following Office 365 Services:"
    
        foreach ($entry in $global:outputArray)
        {
            $entry
        }
    
        if ($global:outputChangeArray)
        {
            write-host "IP entries present in the changes file:"
    
            foreach ($entry in $global:outputChangeArray)
            {
                $entry
            }
        }
    
        out-logfile -string "A XML file containing the above entries is available in the log directory."
        out-logfile -string "******************************************************"
        out-logfile -string "**"
        out-logfile -string "*"
    
        if ($global:outputArray.count -gt 0)
        {
            out-logfile -string ""
            out-logfile -string "URLs was located in the following service description:"
            out-logfile -string ""
    
            foreach ($entry in $global:outputArray)
            {
                out-logfile -string $entry
            }
        
            $global:outputArray | Export-Clixml -Path $outputXMLFile
        }
    
        if ($global:outputChangeArray.count -gt 0)
        {
            out-logfile -string ""
            out-logfile -string "URL was located in the following version additions since 2018:"
            out-logfile -string ""
    
            foreach ($entry in $global:outputChangeArray)
            {
                out-logfile -string $entry
            }
    
            $global:outputChangeArray | Export-Clixml -Path $outputChangeXMLFile
        }
    }
    else 
    {
        out-logfile -string "******************************************************"
        out-logfile -string ("The URL: "+$URLToTest+ " was NOT located in the following Office 365 Services.")
        out-logfile -string "******************************************************"

        out-logfile -string $global:outputRemoveArray.count.tostring()
    
        if ($global:outputRemoveArray.count -gt 0)
        {
            write-host "The following changes removed the URL:"
    
            foreach ($entry in $global:outputRemoveArray)
            {
                $entry
            }
    
            foreach ($entry in $global:outputRemoveArray)
            {
                out-logfile -string $entry
            }
    
            $global:outputRemoveArray | Export-Clixml -Path $outputRemoveXMLFile
        }
    }
}