AdhocAves.CommonFunctions.psm1

##Module variables
$GitRepoFolder = "C:\GitRepos"
$PowershellModuleFolder = "C:\Users\abbystrixa\Documents\WindowsPowerShell\Modules"
$SaveVaraiables = @("GitRepoFolder","PowershellModuleFolder","BasePath")
$ExcludedModules = @("Posh-SSH")
$BasePath = "$ENV:USERPROFILE\AppData\Local\AdhocAves\"

#$ModuleFolder = (Get-Module CommonFunctions -ListAvailable).path -replace "CommonFunctions\.psm1"

Function Get-GitRepoFolder
{
    $GitRepoFolder
}

Function Get-PowershellModuleFolder
{
    $PowershellModuleFolder
}

Function Set-GitRepoFolder
{
    param
    (
        [Parameter(Mandatory=$true)]
        [string]
        $NewGitRepoFolder
    )

    if (test-path $NewGitRepoFolder)
    {
        set-variable -scope 1 -name GitRepoFolder -Value $NewGitRepoFolder
    }
    else
    {
        throw "Folder does not exist"
    }
}

Function Set-BasePath
{
    param
    (
        [Parameter(Mandatory=$true)]
        [string]
        $NewBasePath
    )

    if (test-path $NewBasePath)
    {
        set-variable -scope 1 -name GitRepoFolder -Value $NewBasePath
    }
    else
    {
        throw "Folder does not exist"
    }
}

Function Set-PowershellModuleFolder
{
    param
    (
        [Parameter(Mandatory=$true)]
        [string]
        $NewPowershellModuleFolder
    )

    if (test-path $NewPowershellModuleFolder)
    {
        set-variable -scope 1 -name PowershellModuleFolder -Value $NewPowershellModuleFolder
    }
    else
    {
        throw "Folder does not exist"
    }
}

Function Invoke-CommonFunctionsVariableLoad
{

    Write-Verbose "Importing variables from Common-$($ENV:Username)-Variables.json"

    $Variables = Invoke-VariableJSONLoad "Common-$($ENV:Username)-Variables.json"
    foreach ($Variable in $Variables)
    {
        Write-Debug "Importing $($Variable.value.GetType()) variable $($Variable.name)"
        set-variable -name $Variable.name -Value:$Variable.Value -scope 1
    }
}

Function Invoke-CommonFunctionsVariableSave
{
    $AllVariables = Get-Variable -scope 1 | Where-Object {$_.name -in $SaveVaraiables}
    $SaveFile = "Common-$($ENV:Username)-Variables.json"

    Write-Debug "Starting save job to $SaveFile"

    Invoke-VariableJSONSave -ModuleName "PowerStats" -SaveFile $SaveFile -Variables $AllVariables -verbosepreference:$VerbosePreference
}

Function Write-Log
{
    param
    (
        [Parameter(Mandatory=$true)]
        [string]
        $LogMessage,
        [Parameter(Mandatory=$False)]
        [switch]
        $LogToFile,
        [Parameter(Mandatory=$False)]
        [string]
        $LogPath,
        [Parameter(Mandatory=$False)]
        [int]
        $LogLevel = 1
    )

    if ($LogToFile)
    {
        Add-Content -Path $LogPath -Value "$((Get-Date).ToString("yyyy/MM/dd-hh:mm:ss")) - $LogMessage"
    }

    switch ($LogLevel)
    {
        0 {
            Write-Warning "$((Get-Date).ToString("yyyy/MM/dd-hh:mm:ss")) - $LogMessage"            
        }
        1 {
            Write-Information "$((Get-Date).ToString("yyyy/MM/dd-hh:mm:ss")) - $LogMessage"
        }
        2 {
            Write-Verbose "$((Get-Date).ToString("yyyy/MM/dd-hh:mm:ss")) - $LogMessage"
        }
        3 {
            Write-Debug "$((Get-Date).ToString("yyyy/MM/dd-hh:mm:ss")) - $LogMessage"
        }
    }
}

Function Invoke-ConcurrentSessionCheck
{
    while ((get-process powershell).count -gt $MaxConcurrentSessions)
    {
        if ($Waiting % 10 -eq 0)
        {
            write-verbose "Waiting for other processes to complete"
        }
        $Waiting++
        start-sleep 1
    }
}

Function ConvertFrom-FixedWidthTable
{
    param
    (
        [Parameter(Mandatory=$true)]
        [string[]]
        $Table
    )

    $pTable = @()
    $Headers = @()

    #Find the first line that looks like the table headers
    #We'll see how it goes
    $HeaderLine = 0
    foreach ($Line in $Table)
    {
        if ($Line -match "\w{1,}\s{2,}\w{1,}")
        {
            $Headers = $Line -split "\s{2,}"
            break
        }
        $HeaderLine++
    }

    $Columns = @{}

    foreach ($Header in $Headers)
    {
        $Columns += @{$Header = $Table[$HeaderLine].indexof($Header)}
    }

    $tofs = $ofs
    $ofs = ""

    foreach ($Entry in $Table[2..($Table.count - 1)])
    {
        $Row = new-object PSCustomObject

        for ($x = 0; $x -lt $Headers.count; $x++)
        {
            $Header = $Headers[$x]

            if ($x -eq ($Headers.count - 1))
            {
                $Next = $Entry.length - 1
            }
            else {
                $Next = ([int]$Columns[$Headers[$x + 1]] - 1)
            }

            if ($Entry.length -gt $Columns[$Header])
            {
                #$Columns[$Header]
                $Value = "$($Entry[$Columns[$Header]..$Next])"
            }
            else
            {
                $Value = $null
            }

            $Row | add-member -type NoteProperty -name $Header -value $Value -force
        }

        $pTable += $Row
    }

    $ofs = $tofs

    return $pTable
}

Function Confirm-ModuleDependency
{
    param
    (
        [Parameter(Mandatory=$true)]
        [string[]]
        $Modules
    )

    $ModulesExist = $True

    foreach ($Module in $Modules)
    {
        $ModulesExist = ($null -ne (Get-Module $Module)) -and $ModulesExist
    }

    return $ModulesExist
}

## Everything below this line is going to be replaced with a significantly better C# module eventually

Function Get-NetworkRange
{
    param
    (
        [Parameter(Mandatory=$true)]
        [string]
        $Network,
        [Parameter(Mandatory=$true)]
        [string]
        $Mask
    )
    $tofs = $ofs
    $ofs = ""

    $Ranges = @{}

    for ($Octet = 0; $Octet -le 3; $Octet++)
    {
        $OctStart = $Octet * 8
        $OctEnd   = (($Octet + 1) * 8) - 1

        $StartBin = "$($Network[$OctStart..$OctEnd])"
        $EndBin   = "$(for ($x = $OctStart; $x -le $OctEnd; $x++)
 
        {
            if ($Mask[$x] -eq "0")
            {
                "1"
            }
            else {
                $Network[$x]
            }
        })"

        Write-Debug "StartBin - $StartBin"
        Write-Debug "EndBin - $EndBin"
        $Ranges += @{$Octet = @([convert]::toInt32($StartBin,2), [convert]::toInt32($EndBin,2))}
    }
    $ofs = $tofs
    return $Ranges
}


function Get-DNSHostname
{
    param
    (
        [Parameter(Mandatory=$true)]
        [string]$ComputerName
    )
    return (nslookup $ComputerName | Where-Object {$_ -match "name:\s*\S*"} | For-EachObject {$_ -replace "name:\s*(\S*)",'$1'})
}

function Test-HostStatus
{
    param
    (
        [Parameter(Mandatory=$true)]
        [string]$ComputerName,
        [Parameter(Mandatory=$false)]
        [string]$Timeout = "100",
        [Parameter(Mandatory=$false)]
        [string]$PingCount = "2"
    )

    $ping = invoke-command -scriptblock {ping -w $args[0] -n $args[1] $args[2]} -ArgumentList $Timeout,$PingCount,$ComputerName

    return ($ping -match "bytes=")
}

function New-HostDetailsObject
{
    $HostDetails = [PSCustomObject]@{
    "Name" = $null
    "DNSHostname" = $null
    "Active" = $null
    "Services" = $null}
    return $HostDetails
}

function Get-HostDetails
{
    param
    (
        [Parameter(Mandatory=$true)]
        [string]$ComputerName,
        [switch]$BasicServices
    )

    $HostDetails = New-HostDetailsObject
    $HostDetails.Name = $ComputerName
    $HostDetails.DNSHostname = Get-DNSHostname $ComputerName
    $HostDetails.Active = Test-HostStatus $ComputerName

    if ($HostDetails.Active)
    {
        if ($BasicServices)
        {
            $Services = @()
            foreach ($Service in $BasicServicePorts)
            {
                $ServiceStatus = new-object PSCustomObject
                $ServiceStatus | add-member -type NoteProperty -name Port -value $Service
                $ServiceStatus | add-member -type NoteProperty -name Status -value (Test-NetConnection -ComputerName $ComputerName -Port $Service -Verbose:$False)
                $Services += $ServiceStatus
            }
        }

        $HostDetails.Services = $Services
    }
    else {

    }
    return $HostDetails
}


Function Search-Network
{
    param
    (
        [Parameter(Mandatory=$true)]
        [string]
        $Network,
        [switch]
        $StandardGateways
    )

    if ($Network -match "\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/\d{1,2}")
    {
        $tofs = $ofs
        $ofs = ""
        write-verbose "scanning $Network"
        $ScanInstance = [guid]::NewGuid()

        $NetworkSplit = ($Network -split "/")

        $NetworkBase = $NetworkSplit[0] -split "\."
        $NetworkMask = $NetworkSplit[1]

        $BinaryBase = "$($NetworkBase | ForEach-Object {("{0,8}" -f [convert]::ToString([int32]$_,2)) -replace " ","0"})"
        $BinaryMask = "$("1"*$NetworkMask)$("0"*(32-$NetworkMask))"
        Write-Debug "Binary Base - $BinaryBase"
        Write-Debug "Binary Mask - $BinaryMask"
        for ($x = 0; $x -lt 32; $x++)
        {
            if (($BinaryMask[$x] -eq "0") -and ($BinaryBase[$x] -eq "1"))
            {
                throw "invalid network provided"
            }
        }

        $hosts = @()

        $ScanRange = Get-NetworkRange $BinaryBase $BinaryMask
        $Octets = @($ScanRange[0][0],$ScanRange[1][0],$ScanRange[2][0],$ScanRange[3][0])
        Write-Debug "ScanRange Start- $($ScanRange[0][0]).$($ScanRange[1][0]).$($ScanRange[2][0]).$($ScanRange[3][0])"
        Write-Debug "ScanRange Start- $($ScanRange[0][1]).$($ScanRange[1][1]).$($ScanRange[2][1]).$($ScanRange[3][1])"
        $ScanJobs = @()
        for ($Octets[0] = $ScanRange[0][0]; $Octets[0] -le $ScanRange[0][1]; $Octets[0]++)
        {

            for ($Octets[1] = $ScanRange[1][0]; $Octets[1] -le $ScanRange[1][1]; $Octets[1]++)
            {
                for ($Octets[2] = $ScanRange[2][0]; $Octets[2] -le $ScanRange[2][1]; $Octets[2]++)
                {
                    $StandardGateway = "$($Octets[0]).$($Octets[1]).$($Octets[2]).254"
                    if (($StandardGateways -and (Test-HostStatus $StandardGateway)) -or (-not $StandardGateways))
                    {
                        for ($Octets[3] = $ScanRange[3][0]; $Octets[3] -le $ScanRange[3][1]; $Octets[3]++)
                        {
                            #Check-ConcurrentSessions
                            $ComputerName = "$($Octets[0]).$($Octets[1]).$($Octets[2]).$($Octets[3])"
                            $OutputFile = "C:\Temp\NetworkTools\$($ScanInstance)_ping_$ComputerName.txt"
                            Write-Debug "$ComputerName"
                            $ScanJobs += $OutputFile
                            start-process "C:\windows\system32\cmd.exe" -argumentlist "/C ping $ComputerName" -RedirectStandardOutput $OutputFile -NoNewWindow
                            #start-job -name "$ScanInstance_ping_$JobInstance" -scriptblock {Test-HostStatus $args[0]} -ArgumentList $ComputerName
                        }
                    }
                    else {
                        Write-Verbose "$StandardGateway does not appear to be active, skipping network"
                    }
                }
            }
        }

        $ScannedHosts = 0

        while ((Get-ChildItem C:\Temp\NetworkTools | Where-Object {{$_.FullName -match "$($ScanInstance)_ping"} -and $_.length -eq 0}).count -lt $ScanJobs.count)
        {
            if ($Waiting % 10 -eq 0)
            {
                write-verbose "Waiting for the ping jobs to complete"
            }
            $Waiting++
            start-sleep 1
        }

        foreach ($ScanJob in $ScanJobs)
        {
            Write-Debug "JobFile - $ScanJob"
            $JobDeets = get-content $ScanJob
            Write-Debug "JobResults - $JobDeets"
            $ignore = $JobDeets | For-EachObject {$_ -match "\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}"}
            Write-Debug "Matches - $ignore"
            Write-Debug "IP Addresses - $($Matches[0])"
            $ComputerName = $Matches[0]
            Write-Debug "Bytes Fields = $($JobDeets | For-EachObject {$_ -match "bytes="})"
            if ($JobDeets  | For-EachObject {$_ -match "bytes="})
            {
                Write-Verbose "$ComputerName is online"
                Write-Verbose "Starting job to scan $ComputerName"
                $JobInstance = [guid]::NewGuid()
                $ignore = start-job -name "$($ScanInstance)_check_$JobInstance" -scriptblock {Get-HostDetails $args[0] -BasicServices} -ArgumentList $ComputerName
            }
        }

        $ofs = $tofs
        $Waiting = 0
        while ((Get-Job) | Where-Object {$_.state -eq "Running" -and $_.name -match $ScanInstance})
        {
            if ($Waiting % 10 -eq 0)
            {
                write-verbose "Waiting for the scan jobs to complete"
            }
            $Waiting++
            start-sleep 1
        }

        $hosts += (Get-Job) | Where-Object {$_.name -match "$($ScanInstance)_check" } | receive-job

    }
    else
    {
        Write-Error "Invalid Error provided"
    }

    return $hosts
}

Function Invoke-VariableJSONSave
{
    Param
    (
        $ModuleName,
        $SaveFile,
        $Variables
    )

    if (-not (test-path $BasePath))
    {
        new-item -type Directory -path $BasePath
    }

    Write-Verbose "Encrypting all senstitive information"

    $SaveFile = "$BasePath\$SaveFile"

    Write-Information "Saving all variables for $ModuleName to $SaveFile"
    
    if (-not (test-path $BasePath))
    {
        new-item -type Directory -path $SaveFile
    }

    $OutVars = @()
    foreach ($Variable in $Variables)
    {
        Write-Debug $Variable.name
        if ($Variable.value)
        {
            Write-Debug "$($Variable.value.GetType().tostring())"
            if ($Variable.value.GetType().ToString() -eq "System.Management.Automation.PSCredential")
            {
                Write-Debug "Credential - $($Variable.name) - found, encrypting"
                $NewCredObject = [PSCustomObject]@{
                    "Name" = $Variable.name
                    "Description" = $Variable.Description
                    "Value" = [PSCustomObject]@{
                        "username" = $Variable.value.username
                        "SecurePass" = ($Variable.value.password | ConvertFrom-SecureString)
                    }
                    "Visibility" = $Variable.Visibility
                    "Module" = $Variable.Module
                    "ModuleName" = $Variable.ModuleName
                    "Options" = $Variable.Options
                    "Attributes" = $Variable.Attributes
                }
                $OutVars += $NewCredObject
                Write-Debug $OutVars.count
            }
            elseif ($Variable.value.GetType().ToString() -eq "System.Object[]")
            {
                if ($Variable.value[0].GetType().ToString()-eq "System.Management.Automation.PSCredential")
                {
                    $CredArray = @()
                    foreach ($CredObject in $Variable.value)
                    {
                        $NewCredObject = [PSCustomObject]@{
                            "username" = $CredObject.username
                            "SecurePass" = ($CredObject.password | ConvertFrom-SecureString)
                        }
                        $CredArray += $NewCredObject
                    }

                    $NewCredArrayObject = [PSCustomObject]@{
                        "Name" = $Variable.name
                        "Description" = $Variable.Description
                        "Value" = $CredArray
                        "Visibility" = $Variable.Visibility
                        "Module" = $Variable.Module
                        "ModuleName" = $Variable.ModuleName
                        "Options" = $Variable.Options
                        "Attributes" = $Variable.Attributes
                    }

                    $OutVars += $NewCredArrayObject
                    Write-Debug $OutVars.count
                }
                else
                {
                    $OutVars += $Variable
                    Write-Debug $OutVars.count
                }
            }
            elseif ($Variable.name -match "Token")
            {
                if ($Variable.value -ne "")
                {
                    Write-Debug "API Token - $($Variable.name) - found, encrypting"
                    $NewTokenObject = [PSCustomObject]@{
                        "Name" = "SecureToken-$($_.name)"
                        "Description" = $Variable.Description
                        "Value" = (ConvertTo-SecureString -AsPlainText $Variable.Value -Force | ConvertFrom-SecureString)
                        "Visibility" = $Variable.Visibility
                        "Module" = $Variable.Module
                        "ModuleName" = $Variable.ModuleName
                        "Options" = $Variable.Options
                        "Attributes" = $Variable.Attributes
                    }
                    $OutVars += $NewTokenObject
                    Write-Debug $OutVars.count
                }
            }
            elseif ($Variable.name -match "APIKey")
            {
                if ($Variable.value)
                {
                    Write-Debug "API Token - $($Variable.name) - found, encrypting"
                    $NewTokenObject = [PSCustomObject]@{
                        "Name" = $Variable.name
                        "Description" = $Variable.Description
                        "Value" = [PSCustomObject]@{
                            "ID" = $Variable.value.id
                            "SecureKey" = (ConvertTo-SecureString -AsPlainText $Variable.Value.key -Force | ConvertFrom-SecureString)
                        }
                        "Visibility" = $Variable.Visibility
                        "Module" = $Variable.Module
                        "ModuleName" = $Variable.ModuleName
                        "Options" = $Variable.Options
                        "Attributes" = $Variable.Attributes
                    }
                    $OutVars += @($NewTokenObject)
                    Write-Debug $OutVars.count
                }
            }
            else {
                $OutVars += @($Variable)

                Write-Debug $OutVars.count
            }

        }
    }

    Write-Verbose "Saving variables to $SaveFile"

    $OutVars | ConvertTo-Json -depth 10 | Set-Content $SaveFile
}

Function Invoke-VariableJSONLoad
{
    Param
    (
        $LoadFile
    )

    if (-not (test-path $BasePath))
    {
        new-item -type Directory -path $BasePath
    }

    $LoadPath = "$BasePath\$LoadFile"

    if (-not (test-path $LoadPath))
    {
        new-item -type File -path $LoadPath
    }   

    Write-Verbose "Loading all variables from $LoadFile"
    $JsonContent = get-content $LoadPath
    if ($JsonContent -ne "")
    {
        #$JsonContent | Write-Debug
        $Variables = $JsonContent | convertfrom-json
    }

    foreach ($Variable in $Variables)
    {
        Write-Debug "Found variable $($Variable.name)"
        if ($Variable.Name -match "SecureToken")
        {
            Write-Debug "Secure token found, decrypting"
            $TokenSecure = (ConvertTo-SecureString $Variable.value)

            $Variable.value = (New-Object System.Management.Automation.PsCredential("SecureToken", $TokenSecure)).GetNetworkCredential().Password

            $Variable = $Variable | Select-Object @{Name="Name";Expression={$_.name -replace "SecureToken-",""}},Value
        }
        elseif (($Variable.value | Get-Member).name -contains "SecurePass")
        {
            Write-Debug "SecurePass found, decrypting"
            Write-Debug "$($Variable.value.GetType())"
            if ($Variable.value.GetType().ToString() -eq "System.Object[]")
            {
                Write-Debug "Credential array has been found, iterating values"

                $CredArray = @()

                foreach ($CredObject in $Variable.value)
                {
                    Write-Debug "$($CredObject.UserName) object is being decrypted"
                    $CredArray += @(New-Object System.Management.Automation.PsCredential($CredObject.username, (ConvertTo-SecureString ($CredObject.SecurePass))))
                }

                $CredArray | Write-Debug

                $Variable.value = $CredArray
            }
            else
            {
                Write-Debug "Single Credential has been found, decrypting"
                $Variable.value = (New-Object System.Management.Automation.PsCredential($Variable.value.username, (ConvertTo-SecureString ($Variable.value.SecurePass))))
            }
        }
        elseif ($Variable.name -match "APIToken")
        {
            $SecureKey = ConvertTo-SecureString $Variable.Value.SecureKey

            Write-Debug $SecureKey

            $APIKey = @{
                "ID" = $Variable.value.id
                "Key" = (New-Object System.Management.Automation.PsCredential("SecureToken",$SecureKey)).GetNetworkCredential().Password
            }

            $Variable.value = $APIKey
        }
    }

    return $Variables
}

Function Push-GitModulesToPowershell
{
    param
    (
        [switch]
        $OutputCommands
    )

    $Modules = (Get-ChildItem $GitRepoFolder)

    $ModulesLoaded = $false

    if ($OutputCommands)
    {
        Write-Information "The following modules need to be removed first"
        foreach ($Module in ($Modules | Where-Object {$_.name -notmatch "CommonFunctions"}))
        {
            if (Get-Module $Module.Name)
            {
                $ModulesLoaded = $True
                Write-Information "remove-module $($Module.Name) -force"
            }
            else
            {
                Write-Verbose "Module $($Module.name) is not loaded"
            }
        }
    }




    if ($ModulesLoaded)
    {
        Throw "Please unload the above modules first before continuing"
    }

    foreach ($Module in $Modules)
    {

        if ($Module.name -ne "CommonFunctions")
        {
            Write-Verbose "Module $($Module.name) is being moved"

            $CompiledModulePath = "$($Module.FullName)\$($Module.name)"
            $TargetModulePath = "$PowershellModuleFolder"
            if (test-path ($CompiledModulePath))
            {
                try {
                    Copy-Item $CompiledModulePath $TargetModulePath -recurse -force -erroraction stop
                }
                catch
                {
                    if ($Error[0].Exception.Message -match "used by another process")
                    {
                        Write-Warning "$($Module.name) is currently locked by another process, please unload it from powershell and try again"
                    }
                    else
                    {
                        Write-Warning "Unknown exception occured while copying $($Error[0].Exception.Message)"
                    }
                }
            }
        }

    }

    if ($OutputCommands)
    {
        Write-Information "The following commands can now be run"
        foreach ($Module in $Modules)
        {
            Write-Information "import-module $($Module.name) -force"
        }
    }
}

Function Measure-LinesOfCode
{
    $Modules = Get-ChildItem $GitRepoFolder
    $AllModules = @()
    foreach ($Module in $Modules)
    {
        $ModuleCount = 0
        if ($Module.name -notin $ExcludedModules)
        {
            $ModuleFiles = Get-ChildItem $Module.fullname -recurse | Where-Object {$_.name -match "(psm1|ps1|sql|cs|py)"} | Where-Object {$_.mode -notmatch "d"}
            $AllFileStats = @()
            foreach ($File in $ModuleFiles)
            {
                $FileStats = Get-Content $File.fullname | Measure-Object -Line -Character -Word
                $FileStats | add-member -type NoteProperty -value ($File.FullName -replace [regex]::Escape($Module.fullname)) -Name Path
                $AllFileStats += $FileStats
                $ModuleCount += $FileStats.Lines
            }

            $ModuleInfo = [PSCustomObject]@{
                ModuleName = $Module.name
                TotalLines = $ModuleCount
                FileDetails = $AllFileStats
            }
            $AllModules += $ModuleInfo
        }
    }

    return $AllModules
}

Function Get-JSONArray
{
    param
    (
        [Parameter(Mandatory=$true)]
        [object[]]
        $InputArray
    )

    $JSONArray = "["

    foreach ($Input in $InputArray)
    {
        $JSONArray += """$Input"", "
    }

    $JSONArray = $JSONArray.Substring(0,$JSONArray.Length - 2)

    $JSONArray += "]"

    return $JSONArray
}

Function Get-PageinatedList
{
    param
    (
        [Parameter(Mandatory=$true)]
        [String]
        $ListURI,
        [Parameter(Mandatory=$false)]
        [ValidateRange(10,1000)]
        [int]
        $PageSize = 100,
        [Parameter(Mandatory=$false)]
        [ValidateRange(1,100)]
        [int]
        $StartPage = 1,
        $WebSession
    )


    $Results = @()
    $Page = $StartPage

    $Operator = "?"

    if ($ListURI -match "\?")
    {
        $Operator = "&"
    }

    $NextPage = "$($ListURI)$($Operator)limit=$PageSize&page=$Page"

    $Ignore = $ListURI -match "http(s|)://[^/]*/"

    $BaseURI = $Matches[0] -replace "/$"

    Do
    {
        Write-Verbose "Getting page with uri - $NextPage"
        $WebRequest = Invoke-WebRequest -uri $NextPage -WebSession $WebSession

        $CurrentResult = ConvertFrom-Json $WebRequest.content
        
        $Results += $CurrentResult

        $Page++

        if ($CurrentResult.limit -ne $null)
        {
            if ($CurrentResult.next -ne $null)
            {
                if ($CurrentResult.next -match "http(s|)://")
                {
                    $NextPage = $CurrentResult.Next
                }  
                else
                {
                    $NextPage = "$BaseURI$($CurrentResult.Next)"
                }
            }

            $PageTotal = $CurrentResult.Total
            $PageLimit = $CurrentResult.Limit
            $PagePage = $Page - 1
        }
        else
        {

            $NextPage = "$($ListURI)$($Operator)limit=$PageSize&page=$Page"
            try
            {
                $TErrorAction = $ErrorAction
                $ErrorAction = "SilentlyContinue"
                $PageLimit = [int]$WebRequest.Headers.'X-Page-Limit'[0]
                $PagePage = [int]$WebRequest.Headers.'X-Page-Page'[0]
                $PageTotal = [int]$WebRequest.Headers.'X-Page-Total'[0]
                $TErrorAction = $TErrorAction
            }
            catch
            {
                try
                {
                    $PageLimit = [int]$WebRequest.Headers.'X-Page-Limit'
                    $PagePage = [int]$WebRequest.Headers.'X-Page-Page'
                    $PageTotal = [int]$WebRequest.Headers.'X-Page-Total'
                }
                catch
                {
                    Write-Debug "No x-page headers found"
                }
            }            
        }
        Write-Verbose "Trying to count pages"



        if ($PageLimit -gt 0)
        {
            $GettingPages = ($Page -le [math]::Ceiling($PageTotal/$PageLimit))
            Write-Log "Retrieved $PagePage of $([math]::Ceiling($PageTotal/$PageLimit)), there is $PageTotal entries, the pages are $PageLimit Items Long"
        }
        else
        {
            $GettingPages = $false
            Write-Log "Only a single page exists"
        }
    }
    while ($GettingPages)

    return $Results
}

Function Get-SeperatedMAC
{
    param
    (
        [Parameter(Mandatory=$true,ValueFromPipeline)]
        [String[]]
        $MACStrings,
        $Seperator = "-"
    )
    $ReturnMacs = @()
    foreach ($MACString in $MACStrings)
    {
        #Write-Information $mac
        $MACString = $MACString -replace "[:-.]"

        for ($x = 10; $x -gt 0; $x = $x - 2)
        {
            $MACString = $MACString.Insert($x,$Seperator)
        }

        $ReturnMacs += $MACString
    }
    return $ReturnMacs
}

Invoke-CommonFunctionsVariableLoad