Functions/Public/Numbers.ps1

# Number, number location, and number range functions

Function Get-NectarNumberLocation {
    <#
        .SYNOPSIS
        Returns a list of Nectar DXP service locations used in the DID Management tool.
         
        .DESCRIPTION
        Returns a list of Nectar DXP service locations used in the DID Management tool.
 
        .PARAMETER LocationName
        The name of the service location to get information on. Can be a partial match. To return an exact match and to avoid ambiguity, enclose service location name with ^ at the beginning and $ at the end.
 
        .PARAMETER TenantName
        The name of the Nectar DXP tenant. Used in multi-tenant configurations.
         
        .PARAMETER ResultSize
        The number of results to return. Defaults to 1000.
         
        .EXAMPLE
        Get-NectarNumberLocation
        Returns the first 10 service locations
         
        .EXAMPLE
        Get-NectarNumberLocation -ResultSize 100
        Returns the first 100 service locations
 
        .EXAMPLE
        Get-NectarNumberLocation -LocationName Location2
        Returns up to 10 service locations that contains "location2" anywhere in the name. The search is not case-sensitive. This example would return Location2, Location20, Location214, MyLocation299 etc
 
        .EXAMPLE
        Get-NectarNumberLocation -LocationName ^Location2
        Returns up to 10 service locations that starts with "location2" in the name. The search is not case-sensitive. This example would return Location2, Location20, Location214 etc, but NOT MyLocation299
 
        .EXAMPLE
        Get-NectarNumberLocation -LocationName ^Location2$
        Returns a service location explicitly named "Location2". The search is not case-sensitive.
 
        .NOTES
        Version 1.1
    #>

    
    [Alias("gnnl")]
    Param (
        [Parameter(Mandatory=$False)]
        [string]$LocationName,
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        [string]$TenantName,
        [Parameter(Mandatory=$False)]
        [ValidateRange(1,100000)]
        [int]$ResultSize = 1000
    )
    
    Begin {
        Connect-NectarCloud
    }        
    Process {
        Try {
            # Use globally set tenant name, if one was set and not explicitly included in the command
            If ($Global:NectarTenantName -And !$PSBoundParameters.ContainsKey('TenantName')) { 
                $TenantName = $Global:NectarTenantName 
            } ElseIf ($TenantName) {
                If ($TenantName -NotIn $Global:NectarTenantList) {
                    $TList = $Global:NectarTenantList -join ', '
                    Throw "Could not find a tenant with the name $TenantName on https://$Global:NectarCloud. Select one of $TList. $($_.Exception.Message)"
                }
            }

            $URI = "https://$Global:NectarCloud/dapi/numbers/locations?pageNumber=1&tenant=$TenantName&pageSize=$ResultSize&q=$LocationName"
            Write-Verbose $URI

            $JSON = Invoke-RestMethod -Method GET -URI $URI -Headers $Global:NectarAuthHeader

            If ($TenantName) {$JSON.elements | Add-Member -Name 'TenantName' -Value $TenantName -MemberType NoteProperty} # Add the tenant name to the output which helps pipelining
            $JSON.elements | Add-Member -TypeName 'Nectar.Number.LocationList'
            Return $JSON.elements
        }
        Catch {
            Write-Error "Service location not found. Ensure you typed the name of the service location correctly. $($_.Exception.Message)"
            If ($PSCmdlet.MyInvocation.BoundParameters["ErrorAction"] -ne "SilentlyContinue") { Get-JSONErrorStream -JSONResponse $_ }
        }
    }
}


Function Set-NectarNumberLocation {
    <#
        .SYNOPSIS
        Update a Nectar DXP service location used in the DID Management tool.
         
        .DESCRIPTION
        Update a Nectar DXP service location used in the DID Management tool.
 
        .PARAMETER LocationName
        The name of the service location to get information on. Can be a partial match. To return an exact match and to avoid ambiguity, enclose location name with ^ at the beginning and $ at the end.
         
        .PARAMETER NewLocationName
        Replace the existing service location name with this one.
         
        .PARAMETER ServiceID
        The service ID associated with the telephony provider for this service location
 
        .PARAMETER ServiceProvider
        The name of the service provider that provides telephony service to this service location
 
        .PARAMETER NetworkLocation
        The phyiscal location for this service location
 
        .PARAMETER Notes
        Can be used for any additional information
 
        .PARAMETER TenantName
        The name of the Nectar DXP tenant. Used in multi-tenant configurations.
         
        .EXAMPLE
        Set-NectarNumberLocation -LocationName Dallas -ServiceID 44FE98 -ServiceProvider Verizon -Notes "Head office"
        Returns the first 10 locations
         
        .NOTES
        Version 1.1
    #>

    
    [Alias("snnl")]
    Param (
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)]
        [Alias("name")]
        [string]$LocationName,
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        [string]$NewLocationName,
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        $ServiceID,
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        [Alias("provider")]
        $ServiceProvider,
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        [Alias("location")]
        $NetworkLocation,
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        $Notes,
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        [string]$TenantName,
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        [Alias("id")]
        [String]$Identity
    )
    
    Begin {
        Connect-NectarCloud
    }        
    Process {
        # Use globally set tenant name, if one was set and not explicitly included in the command
        If ($Global:NectarTenantName -And !$PSBoundParameters.ContainsKey('TenantName')) { 
                $TenantName = $Global:NectarTenantName 
            } ElseIf ($TenantName) {
                If ($TenantName -NotIn $Global:NectarTenantList) {
                    $TList = $Global:NectarTenantList -join ', '
                    Throw "Could not find a tenant with the name $TenantName on https://$Global:NectarCloud. Select one of $TList. $($_.Exception.Message)"
                }
            }
        
        If ($LocationName -And !$Identity) {
            $LocationInfo = Get-NectarNumberLocation -LocationName $LocationName -Tenant $TenantName -ResultSize 1
            $Identity = $LocationInfo.id
        }    
        
        If ($LocationInfo.Count -gt 1) {
            Write-Error "Multiple number locations found that match $LocationName. Please refine your location name search query. $($_.Exception.Message)"
            Break
        }

        If ($NewLocationName) {$LocationName = $NewLocationName}
        If (-not $ServiceID) {$ServiceID = $LocationInfo.ServiceId}
        If (-not $ServiceProvider) {$ServiceProvider = $LocationInfo.provider}
        If (-not $NetworkLocation) {$NetworkLocation = $LocationInfo.location}
        If (-not $Notes) {$Notes = $LocationInfo.notes}

        $URI = "https://$Global:NectarCloud/dapi/numbers/location/$Identity/?tenant=$TenantName"
        Write-Verbose $URI

        $Body = @{
            name = $LocationName
            serviceId = $ServiceID
            provider = $ServiceProvider
            location = $NetworkLocation
            notes = $Notes
        }
        
        $JSONBody = $Body | ConvertTo-Json

        Try {
            $NULL = Invoke-RestMethod -Method PUT -URI $URI -Headers $Global:NectarAuthHeader -Body $JSONBody -ContentType 'application/json; charset=utf-8'
            Write-Verbose $JSONBody
        }
        Catch {
            Write-Error "Unable to apply changes for location $LocationName. $($_.Exception.Message)"
            If ($PSCmdlet.MyInvocation.BoundParameters["ErrorAction"] -ne "SilentlyContinue") { Get-JSONErrorStream -JSONResponse $_ }
        }
    }
}


Function New-NectarNumberLocation {
    <#
        .SYNOPSIS
        Create a new Nectar Service Location for DID Management used in the DID Management tool.
         
        .DESCRIPTION
        Create a new Nectar Service Location for DID Management used in the DID Management tool.
 
        .PARAMETER LocationName
        The name of the new service location. Must be unique.
         
        .PARAMETER ServiceID
        The service ID for telephony services at the newservice location. Can be used as desired. Not required.
         
        .PARAMETER ServiceProvider
        The service provider for telephony services at the newservice location. Can be used as desired. Not required.
         
        .PARAMETER ServiceProvider
        The network location to associate with the newservice location. Can be used as desired. Not required.
 
        .PARAMETER Notes
        Any relevent notes about the service location. Can be used as desired. Not required.
 
        .PARAMETER TenantName
        The name of the Nectar DXP tenant. Used in multi-tenant configurations.
         
        .EXAMPLE
        New-NectarNumberLocation -LocationName Dallas -ServiceID 348FE22 -ServiceProvider Verizon -NetworkLocation Dallas -Notes "This is headquarters"
 
        .NOTES
        Version 1.1
    #>

    
    [Alias("nnnl")]
    Param (
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        [Alias("name")]
        [string]$LocationName, 
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        [string]$ServiceID,
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        [Alias("provider")]
        [string]$ServiceProvider,
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        [Alias("location")]
        [string]$NetworkLocation,
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        [string]$Notes,    
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        [string]$TenantName
    )
    
    Begin {
        Connect-NectarCloud
    }        
    Process {
        # Use globally set tenant name, if one was set and not explicitly included in the command
        If ($Global:NectarTenantName -And !$PSBoundParameters.ContainsKey('TenantName')) { 
                $TenantName = $Global:NectarTenantName 
            } ElseIf ($TenantName) {
                If ($TenantName -NotIn $Global:NectarTenantList) {
                    $TList = $Global:NectarTenantList -join ', '
                    Throw "Could not find a tenant with the name $TenantName on https://$Global:NectarCloud. Select one of $TList. $($_.Exception.Message)"
                }
            }
        
        $URI = "https://$Global:NectarCloud/dapi/numbers/location/?tenant=$TenantName"
        Write-Verbose $URI

        $Body = @{
            name = $LocationName
            serviceId = $ServiceID
            provider = $ServiceProvider
            location = $NetworkLocation
            notes = $Notes
        }
        
        $JSONBody = $Body | ConvertTo-Json

        Try {
            Invoke-RestMethod -Method POST -URI $URI -Headers $Global:NectarAuthHeader -Body $JSONBody -ContentType 'application/json; charset=utf-8'
            Write-Verbose $JSONBody
        }
        Catch {
            Write-Error "Unable to create service location $LocationName. The service location may already exist. $($_.Exception.Message)"
            If ($PSCmdlet.MyInvocation.BoundParameters["ErrorAction"] -ne "SilentlyContinue") { Get-JSONErrorStream -JSONResponse $_ }
        }
    }
}


Function Remove-NectarNumberLocation {
    <#
        .SYNOPSIS
        Removes one or more service locations in the DID Management tool.
 
        .DESCRIPTION
        Removes one or more service locations in the DID Management tool.
         
        .PARAMETER LocationName
        The name of the number service location to remove.
     
        .PARAMETER Identity
        The numerical ID of the number service location. Can be obtained via Get-NectarNumberLocation and pipelined to Remove-NectarNumberLocation
 
        .PARAMETER TenantName
        The name of the Nectar DXP tenant. Used in multi-tenant configurations.
         
        .EXAMPLE
        Remove-NectarNumberLocation Tokyo
        Removes the Toyota location. The command will fail if the location has number ranges assigned.
         
        .NOTES
        Version 1.1
    #>

    
    [Alias("rnnl")]
    Param (
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        [string]$LocationName,
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        [string]$TenantName,        
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        [Alias("id")]
        [string]$Identity
    )
    
    Begin {
        Connect-NectarCloud
    }        
    Process {
        # Use globally set tenant name, if one was set and not explicitly included in the command
        If ($Global:NectarTenantName -And !$PSBoundParameters.ContainsKey('TenantName')) { 
                $TenantName = $Global:NectarTenantName 
            } ElseIf ($TenantName) {
                If ($TenantName -NotIn $Global:NectarTenantList) {
                    $TList = $Global:NectarTenantList -join ', '
                    Throw "Could not find a tenant with the name $TenantName on https://$Global:NectarCloud. Select one of $TList. $($_.Exception.Message)"
                }
            }
        
        If ($LocationName -And !$Identity) {
            $Identity = (Get-NectarNumberLocation -LocationName $LocationName -Tenant $TenantName -ResultSize 1 -ErrorVariable GetLocationError).ID
        }
        
        If ($Identity.Count -gt 1) {
            Write-Error "Multiple number locations found that match $LocationName. Please refine your location name search query."
            Break
        }
            
        If (!$GetLocationError) {
            $URI = "https://$Global:NectarCloud/dapi/numbers/location/$Identity/?tenant=$TenantName"
            Write-Verbose $URI
            
            Try {
                $NULL = Invoke-RestMethod -Method DELETE -URI $URI -Headers $Global:NectarAuthHeader
                Write-Verbose "Successfully deleted $LocationName."
            }
            Catch {
                Write-Error "Unable to delete service location $LocationName. Ensure you typed the name of the service location correctly and that the service location has no assigned ranges. $($_.Exception.Message)"
                If ($PSCmdlet.MyInvocation.BoundParameters["ErrorAction"] -ne "SilentlyContinue") { Get-JSONErrorStream -JSONResponse $_ }
            }
        }
    }
}




#################################################################################################################################################
# #
# DID Number Range Management Functions #
# #


Function Get-NectarNumberRange {
    <#
        .SYNOPSIS
        Returns a list of Nectar DXP number ranges in the DID Management tool
 
        .DESCRIPTION
        Returns a list of Nectar DXP ranges in the DID Management tool
         
        .PARAMETER RangeName
        The name of the number range to get information on. Can be a partial match. To return an exact match and to avoid ambiguity, enclose range name with ^ at the beginning and $ at the end.
     
        .PARAMETER LocationName
        The name of the location to get information on. Will be an exact match.
 
        .PARAMETER TenantName
        The name of the Nectar DXP tenant. Used in multi-tenant configurations.
         
        .PARAMETER ResultSize
        The number of results to return. Defaults to 1000.
         
        .EXAMPLE
        Get-NectarNumberRange
        Returns the first 10 number ranges
         
        .EXAMPLE
        Get-NectarNumberRange -ResultSize 100
        Returns the first 100 number ranges
 
        .EXAMPLE
        Get-NectarNumberRange -LocationName Tokyo
        Returns the first 10 number ranges at the Tokyo location
 
        .EXAMPLE
        Get-NectarNumberRange -RangeName Range2
        Returns up to 10 ranges that contains "range2" anywhere in the name. The search is not case-sensitive. This example would return Range2, Range20, Range214, MyRange299 etc
 
        .EXAMPLE
        Get-NectarNumberRange -RangeName ^Range2
        Returns up to 10 ranges that starts with "range2" in the name. The search is not case-sensitive. This example would return Range2, Range20, Range214 etc, but NOT MyRange299.
 
        .EXAMPLE
        Get-NectarNumberRange -RangeName ^Range2$
        Returns any range explicitly named "Range2". The search is not case-sensitive. This example would return Range2 only. If there are multiple ranges with the name Range2, all will be returned.
 
        .EXAMPLE
        Get-NectarNumberRange -RangeName ^Range2$ -LocationName Tokyo
        Returns a range explicitly named "Range2" in the Tokyo location. The search is not case-sensitive.
 
        .NOTES
        Version 1.1
    #>

    
    [Alias("gnnr")]
    Param (
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        [string]$RangeName,
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        [string]$LocationName,
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        [string]$TenantName,
        [Parameter(Mandatory=$False)]
        [ValidateRange(1,100000)]
        [int]$ResultSize = 1000
    )
    
    Begin {
        Connect-NectarCloud
    }        
    Process {
        # Use globally set tenant name, if one was set and not explicitly included in the command
        If ($Global:NectarTenantName -And !$PSBoundParameters.ContainsKey('TenantName')) { 
                $TenantName = $Global:NectarTenantName 
            } ElseIf ($TenantName) {
                If ($TenantName -NotIn $Global:NectarTenantList) {
                    $TList = $Global:NectarTenantList -join ', '
                    Throw "Could not find a tenant with the name $TenantName on https://$Global:NectarCloud. Select one of $TList. $($_.Exception.Message)"
                }
            }
        
        If ($LocationName) {    
            $LocationID = (Get-NectarNumberLocation -LocationName "$LocationName" -Tenant $TenantName -ErrorVariable LocError).ID 
        }
        
        If ($LocError) { Break}

        $URI = "https://$Global:NectarCloud/dapi/numbers/ranges?pageNumber=1&tenant=$TenantName&pageSize=$ResultSize&serviceLocationId=$LocationID&q=$RangeName"
        Write-Verbose $URI
        
        Try {
            $JSON = Invoke-RestMethod -Method GET -URI $URI -Headers $Global:NectarAuthHeader
            If ($TenantName) {$JSON.elements | Add-Member -Name 'TenantName' -Value $TenantName -MemberType NoteProperty} # Add the tenant name to the output which helps pipelining
            $JSON.elements | Add-Member -TypeName 'Nectar.Number.RangeList'
            Return $JSON.elements
        }
        Catch {
            Write-Error "An error occurred. $($_.Exception.Message)"
            If ($PSCmdlet.MyInvocation.BoundParameters["ErrorAction"] -ne "SilentlyContinue") { Get-JSONErrorStream -JSONResponse $_ }
        }
    }
}


Function Set-NectarNumberRange {
    <#
        .SYNOPSIS
        Make changes to a Nectar range for DID Management
         
        .DESCRIPTION
        Make changes to a Nectar range for DID Management
 
        .PARAMETER RangeName
        The name of the range. Must be unique.
         
        .PARAMETER RangeType
        The type of range. Can be either STANDARD (for DID ranges) or EXTENSION (for extension-based ranges).
         
        .PARAMETER FirstNumber
        The first number in a STANDARD range. Must be numeric, but can start with +.
         
        .PARAMETER LastNumber
        The last number in a STANDARD range. Must be numeric, but can start with +. Must be larger than FirstNumber, and must have the same number of digits.
         
        .PARAMETER BaseNumber
        The base DID for an EXTENSION range. Must be numeric, but can start with +.
         
        .PARAMETER ExtStart
        The first extension number in an EXTENSION range. Must be numeric.
         
        .PARAMETER ExtEnd
        The last extension number in an EXTENSION range. Must be numeric. Must be larger than ExtStart, and must have the same number of digits.
 
        .PARAMETER RangeSize
        The number of phone numbers/extensions in a range. Can be used instead of LastNumber/ExtEnd.
         
        .PARAMETER HoldDays
        The number of days to hold a newly-freed number before returning it to the pool of available numbers.
         
        .PARAMETER LocationName
        The service location to assign the range to.
 
        .PARAMETER TenantName
        The name of the Nectar DXP tenant. Used in multi-tenant configurations.
         
        .EXAMPLE
        Set-NectarNumberRange -RangeName DIDRange1 -RangeType STANDARD -FirstNumber +15552223333 -LastNumber +15552224444 -LocationName Dallas
        Edits a DID range for numbers that fall in the range of +15552223333 to +15552224444
         
        .NOTES
        Version 1.1
    #>

    
    [Alias("snnr")]
    Param (
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        [Alias("name")]
        [string]$RangeName, 
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        [ValidateSet("STANDARD","EXTENSION", IgnoreCase=$True)]
        [Alias("type")]
        [string]$RangeType,
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
# [ValidatePattern("^(\+|%2B)?\d+$")]
        [string]$FirstNumber,
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
# [ValidatePattern("^(\+|%2B)?\d+$")]
        [string]$LastNumber,
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
# [ValidatePattern("^(\+|%2B)?\d+$")]
        [string]$BaseNumber,        
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
# [ValidatePattern("^\d+$")]
        [string]$ExtStart,
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
# [ValidatePattern("^\d+$")]
        [string]$ExtEnd,            
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        [int]$RangeSize,
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        [int]$HoldDays = 0,
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)]
        [Alias("serviceLocationId")]
        [string]$LocationName,
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        [string]$TenantName,
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        [Alias("id")]
        [int]$Identity
    )
    
    Begin {
        Connect-NectarCloud
    }        
    Process {
        # Use globally set tenant name, if one was set and not explicitly included in the command
        If ($Global:NectarTenantName -And !$PSBoundParameters.ContainsKey('TenantName')) { 
                $TenantName = $Global:NectarTenantName 
            } ElseIf ($TenantName) {
                If ($TenantName -NotIn $Global:NectarTenantList) {
                    $TList = $Global:NectarTenantList -join ', '
                    Throw "Could not find a tenant with the name $TenantName on https://$Global:NectarCloud. Select one of $TList. $($_.Exception.Message)"
                }
            }
        
        If ($RangeName -And !$Identity) {
            $RangeInfo = Get-NectarNumberRange -RangeName $RangeName -Tenant $TenantName -ResultSize 1
            $Identity = $RangeInfo.id
            
            If ($NewRangeName) {$RangeName = $NewRangeName}
            If (-not $FirstNumber) {$FirstNumber = $RangeInfo.firstNumber}
            If (-not $LastNumber) {$LastNumber = $RangeInfo.lastNumber}
            If (-not $RangeType) {$RangeType = $RangeInfo.type}
            If (-not $HoldDays) {$HoldDays = $RangeInfo.holdDays}
            If (-not $BaseNumber) {$BaseNumber = $RangeInfo.baseNumber}
            If (-not $ExtStart) {$ExtStart = $RangeInfo.extStart}
            If (-not $ExtEnd) {$ExtEnd = $RangeInfo.extEnd}
            # If (-not $LocationName) {$LocationID = $RangeInfo.serviceLocationId}
        }
            
        $LocationID = (Get-NectarNumberLocation -LocationName "$LocationName" -Tenant $TenantName -ErrorVariable LocError).ID
        
        $URI = "https://$Global:NectarCloud/dapi/numbers/range/$Identity/?tenant=$TenantName"
        Write-Verbose $URI

        $Body = @{
            name = $RangeName
            type = $RangeType
            holdDays = $HoldDays
            serviceLocationId = $LocationID
        }

        If ($FirstNumber) { $Body.Add('firstNumber', $FirstNumber) }
        If ($LastNumber) { $Body.Add('lastNumber', $LastNumber) }
        If ($BaseNumber) { $Body.Add('baseNumber', $BaseNumber) }
        If ($ExtStart) { $Body.Add('extStart', $ExtStart) }
        If ($ExtEnd) { $Body.Add('extEnd', $ExtEnd) }
        If ($RangeSize) { $Body.Add('rangeSize', $RangeSize) }
        
        $JSONBody = $Body | ConvertTo-Json

        Try {
            $NULL = Invoke-RestMethod -Method PUT -URI $URI -Headers $Global:NectarAuthHeader -Body $JSONBody -ContentType 'application/json; charset=utf-8'
            Write-Verbose $JSONBody
        }
        Catch {
            Write-Error "Unable to apply changes for range $RangeName. $($_.Exception.Message)"
            If ($PSCmdlet.MyInvocation.BoundParameters["ErrorAction"] -ne "SilentlyContinue") { Get-JSONErrorStream -JSONResponse $_ }
        }
    }
}


Function New-NectarNumberRange {
    <#
        .SYNOPSIS
        Create a new Nectar range for DID Management
         
        .DESCRIPTION
        Create a new Nectar range for DID Management
 
        .PARAMETER RangeName
        The name of the new range. Must be unique.
         
        .PARAMETER RangeType
        The type of range. Can be either STANDARD (for DID ranges) or EXTENSION (for extension-based ranges).
         
        .PARAMETER FirstNumber
        The first number in a STANDARD range. Must be numeric, but can start with +.
         
        .PARAMETER LastNumber
        The last number in a STANDARD range. Must be numeric, but can start with +. Must be larger than FirstNumber, and must have the same number of digits.
         
        .PARAMETER BaseNumber
        The base DID for an EXTENSION range. Must be numeric, but can start with +.
         
        .PARAMETER ExtStart
        The first extension number in an EXTENSION range. Must be numeric.
         
        .PARAMETER ExtEnd
        The last extension number in an EXTENSION range. Must be numeric. Must be larger than ExtStart, and must have the same number of digits.
 
        .PARAMETER RangeSize
        The number of phone numbers/extensions in a range. Can be used instead of LastNumber/ExtEnd.
         
        .PARAMETER HoldDays
        The number of days to hold a newly-freed number before returning it to the pool of available numbers.
         
        .PARAMETER LocationName
        The location to assign the range to.
 
        .PARAMETER TenantName
        The name of the Nectar DXP tenant. Used in multi-tenant configurations.
                 
        .EXAMPLE
        New-NectarNumberRange -RangeName DIDRange1 -RangeType STANDARD -FirstNumber +15552223333 -LastNumber +15552224444 -LocationName Dallas
        Creates a DID range for numbers that fall in the range of +15552223333 to +15552224444
 
        .EXAMPLE
        New-NectarNumberRange -RangeName DIDRange1 -RangeType STANDARD -FirstNumber +15552223000 -RangeSize 1000 -LocationName Dallas
        Creates a DID range for numbers that fall in the range of +15552223000 to +15552223999
         
        .EXAMPLE
        New-NectarNumberRange -RangeName ExtRange1 -RangeType EXTENSION -BaseNumber +15552223000 -ExtStart 2000 -ExtEnd 2999 -LocationName Dallas
        Creates an extension range for numbers that fall in the range of +15552223000 x2000 to x2999
         
        .NOTES
        Version 1.2
    #>

    
    [Alias("nnnr")]
    Param (
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)]
        [Alias("name")]
        [string]$RangeName, 
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)]
        [ValidateSet("STANDARD","EXTENSION", IgnoreCase=$True)]
        [string]$RangeType,
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        [ValidatePattern("^(\+|%2B)?\d+$")]
        [string]$FirstNumber,
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        [ValidatePattern("^(\+|%2B)?\d+$")]
        [string]$LastNumber,
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        [ValidatePattern("^(\+|%2B)?\d+$")]
        [string]$BaseNumber,        
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        [ValidatePattern("^\d+$")]
        [string]$ExtStart,
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        [ValidatePattern("^\d+$")]
        [string]$ExtEnd,            
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        [int]$RangeSize,
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        [int]$HoldDays,
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)]
        [string]$LocationName,
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        [string]$TenantName,
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        [Alias("id")]
        [int]$Identity
    )
    
    Begin {
        Connect-NectarCloud
    }        
    Process {
        # Use globally set tenant name, if one was set and not explicitly included in the command
        If ($Global:NectarTenantName -And !$PSBoundParameters.ContainsKey('TenantName')) { 
                $TenantName = $Global:NectarTenantName 
            } ElseIf ($TenantName) {
                If ($TenantName -NotIn $Global:NectarTenantList) {
                    $TList = $Global:NectarTenantList -join ', '
                    Throw "Could not find a tenant with the name $TenantName on https://$Global:NectarCloud. Select one of $TList. $($_.Exception.Message)"
                }
            }
        
        $LocationID = (Get-NectarNumberLocation -LocationName $LocationName -Tenant $TenantName -ErrorVariable NumLocError).ID
        
        If ($LocationID.Count -gt 1) {
            Write-Error "Multiple locations found that match $LocationName. Please refine your location name search query"
            Break
        }
    
        $URI = "https://$Global:NectarCloud/dapi/numbers/range?tenant=$TenantName"
        Write-Verbose $URI

        $Body = @{
            name = $RangeName
            type = $RangeType
            holdDays = $HoldDays
            serviceLocationId = $LocationID
        }

        If ($FirstNumber) { $Body.Add('firstNumber', $FirstNumber) }
        If ($LastNumber) { $Body.Add('lastNumber', $LastNumber) }
        If ($BaseNumber) { $Body.Add('baseNumber', $BaseNumber) }
        If ($ExtStart) { $Body.Add('extStart', $ExtStart) }
        If ($ExtEnd) { $Body.Add('extEnd', $ExtEnd) }
        If ($RangeSize) { $Body.Add('rangeSize', $RangeSize) }
        
        $JSONBody = $Body | ConvertTo-Json

        Try {
            Invoke-RestMethod -Method POST -URI $URI -Headers $Global:NectarAuthHeader -Body $JSONBody -ContentType 'application/json; charset=utf-8'
            Write-Verbose $JSONBody
        }
        Catch {
            Write-Error "Unable to create range $RangeName. $($_.Exception.Message)"
            If ($PSCmdlet.MyInvocation.BoundParameters["ErrorAction"] -ne "SilentlyContinue") { Get-JSONErrorStream -JSONResponse $_ }
        }
    }
}


Function Remove-NectarNumberRange {
    <#
        .SYNOPSIS
        Removes one or more ranges from a service location in the DID Management tool.
 
        .DESCRIPTION
        Removes one or more ranges from a service location in the DID Management tool.
         
        .PARAMETER RangeName
        The name of the number range to remove.
 
        .PARAMETER TenantName
        The name of the Nectar DXP tenant. Used in multi-tenant configurations.
                 
        .PARAMETER Identity
        The numerical ID of the number range. Can be obtained via Get-NectarNumberRange and pipelined to Remove-NectarNumberRange
         
        .EXAMPLE
        Remove-NectarNumberRange Range1
        Removes the range Range1
         
        .NOTES
        Version 1.1
    #>

    
    [Alias("rnnr")]
    Param (
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        [string]$RangeName,
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        [string]$TenantName,
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        [Alias("id")]
        [String]$Identity
    )

    Begin {
        Connect-NectarCloud
    }    
    Process {
        # Use globally set tenant name, if one was set and not explicitly included in the command
        If ($Global:NectarTenantName -And !$PSBoundParameters.ContainsKey('TenantName')) { 
                $TenantName = $Global:NectarTenantName 
            } ElseIf ($TenantName) {
                If ($TenantName -NotIn $Global:NectarTenantList) {
                    $TList = $Global:NectarTenantList -join ', '
                    Throw "Could not find a tenant with the name $TenantName on https://$Global:NectarCloud. Select one of $TList. $($_.Exception.Message)"
                }
            }
        
        If ($RangeName -And !$Identity) {
            $Identity = (Get-NectarNumberRange -RangeName $RangeName -Tenant $TenantName -ErrorVariable GetRangeError).ID
        }
        
        If ($Identity.Count -gt 1) {
            Write-Error "Multiple ranges found that match $RangeName. Please refine your range name search query"
            Break
        }
            
        If (!$GetRangeError) {
            $URI = "https://$Global:NectarCloud/dapi/numbers/range/$Identity/?tenant=$TenantName"
            Write-Verbose $URI
            
            Try {
                $NULL = Invoke-RestMethod -Method DELETE -URI $URI -Headers $Global:NectarAuthHeader
                Write-Verbose "Successfully deleted $RangeName number range."
            }
            Catch {
                Write-Error "Unable to delete $RangeName number range. Ensure you typed the name of the range correctly. $($_.Exception.Message)"
                If ($PSCmdlet.MyInvocation.BoundParameters["ErrorAction"] -ne "SilentlyContinue") { Get-JSONErrorStream -JSONResponse $_ }
            }
        }
    }
}




#################################################################################################################################################
# #
# DID Number Management Functions #
# #


Function Get-NectarNumber {
    <#
        .SYNOPSIS
        Returns a list of Nectar DXP numbers from the DID Management tool
 
        .DESCRIPTION
        Returns a list of Nectar DXP numbers from the DID Management tool
         
        .PARAMETER PhoneNumber
        The phone number to return information about. Can be a partial match. To return an exact match and to avoid ambiguity, enclose number with ^ at the beginning and $ at the end.
     
        .PARAMETER LocationName
        The name of the location to get number information about. Will be an exact match.
 
        .PARAMETER RangeName
        The name of the range to get number information about. Will be an exact match.
 
        .PARAMETER NumberState
        Returns information about numbers that are either USED, UNUSED or RESERVED
 
        .PARAMETER TenantName
        The name of the Nectar DXP tenant. Used in multi-tenant configurations.
         
        .PARAMETER ResultSize
        The number of results to return. Defaults to 1000.
         
        .EXAMPLE
        Get-NectarNumber
        Returns the first 10 numbers
         
        .EXAMPLE
        Get-NectarNumber -ResultSize 100
        Returns the first 100 numbers
 
        .EXAMPLE
        Get-NectarNumber -LocationName Tokyo
        Returns the first 10 numbers at the Tokyo location
 
        .EXAMPLE
        Get-NectarNumber -RangeName Range2
        Returns up to 10 numbers from a number range called Range2.
 
        .EXAMPLE
        Get-NectarNumber -RangeName Range2 -NumberState UNUSED -ResultSize 100
        Returns up to 100 unused numbers in the Range2 range.
 
        .NOTES
        Version 1.1
    #>

    
    [Alias("gnn")]    
    Param (
        [Parameter(Mandatory=$False)]
        [ValidatePattern("^(\+|%2B)?\d+$")]
        [string]$PhoneNumber,
        [Parameter(Mandatory=$False)]
        [string]$LocationName, 
        [Parameter(Mandatory=$False)]
        [string]$RangeName,
        [Parameter(Mandatory=$False)]
        [ValidateSet("USED","UNUSED","RESERVED", IgnoreCase=$True)]
        [string]$NumberState,
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        [string]$TenantName,
        [Parameter(Mandatory=$False)]
        [ValidateRange(1,100000)]
        [int]$PageSize = 10000,
        [Parameter(Mandatory=$False)]
        [ValidateRange(1,100000)]
        [int]$ResultSize
    )
    
    Begin {
        Connect-NectarCloud
    }        
    Process {
        Try {
            # Use globally set tenant name, if one was set and not explicitly included in the command
            If ($Global:NectarTenantName -And !$PSBoundParameters.ContainsKey('TenantName')) { 
                $TenantName = $Global:NectarTenantName 
            } ElseIf ($TenantName) {
                If ($TenantName -NotIn $Global:NectarTenantList) {
                    $TList = $Global:NectarTenantList -join ', '
                    Throw "Could not find a tenant with the name $TenantName on https://$Global:NectarCloud. Select one of $TList. $($_.Exception.Message)"
                }
            }
            
            If ($LocationName) {
                $LocationID = (Get-NectarNumberLocation -LocationName ^$LocationName$ -Tenant $TenantName -ResultSize 1 -ErrorVariable NectarError).ID 
            }
            
            If ($RangeName) {
                $RangeID = (Get-NectarNumberRange -RangeName $RangeName -LocationName $LocationName -Tenant $TenantName -ResultSize 1 -ErrorVariable +NectarError).ID
            }
            
            If ($PhoneNumber) {
                # Replace + with %2B if present
                $PhoneNumber = $PhoneNumber.Replace("+", "%2B")
            }
            
            $URI = "https://$Global:NectarCloud/dapi/numbers/"
            Write-Verbose $URI
            
            $Params = @{
                'orderByField' = 'number'
                'orderDirection' = 'asc'
            }
            
            If ($ResultSize) { 
                $Params.Add('pageSize', $ResultSize) 
            }
            Else { 
                $Params.Add('pageSize', $PageSize)
            }

            If ($LocationID) { $Params.Add('serviceLocationId', $LocationID) }
            If ($RangeID) { $Params.Add('numbersRangeId', $RangeID) }
            If ($NumberState) { $Params.Add('states', $NumberState) }
            If ($PhoneNumber) { $Params.Add('q', $PhoneNumber) }
            If ($TenantName) { $Params.Add('Tenant', $TenantName) }            
            
            If (!$NectarError) {
                $JSON = Invoke-RestMethod -Method GET -URI $URI -Headers $Global:NectarAuthHeader -Body $Params
                If ($TenantName) { $JSON.elements | Add-Member -Name 'TenantName' -Value $TenantName -MemberType NoteProperty } # Add the tenant name to the output which helps pipelining
                $JSON.elements | Add-Member -TypeName 'Nectar.Number.List'
                $JSON.elements
                
                $TotalPages = $JSON.totalPages
            
                If ($TotalPages -gt 1 -and !($ResultSize)) {
                    $PageNum = 2
                    Write-Verbose "Page size: $PageSize"
                    While ($PageNum -le $TotalPages) {
                        Write-Verbose "Working on page $PageNum of $TotalPages"
                        $PagedURI = $URI + "?pageNumber=$PageNum"
                        $JSON = Invoke-RestMethod -Method GET -URI $PagedURI -Headers $Global:NectarAuthHeader -Body $Params
                        If ($TenantName) { $JSON.elements | Add-Member -Name 'TenantName' -Value $TenantName -MemberType NoteProperty }
                        $JSON.elements | Add-Member -TypeName 'Nectar.Number.List'
                        $JSON.elements
                        $PageNum++
                    }
                }
            }
        }
        Catch {
            Write-Error "Unable to retrieve number information. $($_.Exception.Message)"
            If ($PSCmdlet.MyInvocation.BoundParameters["ErrorAction"] -ne "SilentlyContinue") { Get-JSONErrorStream -JSONResponse $_ }        
        }
    }
}


Function Set-NectarNumber {
    <#
        .SYNOPSIS
        Makes changes to one or more phone numbers.
         
        .DESCRIPTION
        Makes changes to one or more phone numbers.
 
        .PARAMETER PhoneNumber
        A phone number to make changes to. Must be an exact match.
         
        .PARAMETER NumberState
        Change the state of a phone number to either UNUSED or RESERVED. A number marked USED cannot be modified.
         
        .PARAMETER Comment
        A comment to add to a reserved phone number.
 
        .PARAMETER TenantName
        The name of the Nectar DXP tenant. Used in multi-tenant configurations.
         
        .EXAMPLE
        Set-NectarNumber +12223334444 -NumberState RESERVED
        Reserves the number +12223334444
         
        .NOTES
        Version 1.1
    #>


    [Alias("snn")]
    Param (
        [Parameter(ValueFromPipelineByPropertyName,Mandatory=$False)]
        [ValidatePattern("^(\+|%2B)?\d+$")]
        [string]$PhoneNumber,
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        [ValidateSet("UNUSED","RESERVED", IgnoreCase=$True)]
        [string]$NumberState,
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        [ValidateLength(0,254)]
        [string]$Comment,
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        [string]$TenantName,
        [Parameter(ValueFromPipelineByPropertyName)]
        [Alias("id")]
        [String]$Identity
    )
    
    Process {
        # Use globally set tenant name, if one was set and not explicitly included in the command
        If ($Global:NectarTenantName -And !$PSBoundParameters.ContainsKey('TenantName')) { 
                $TenantName = $Global:NectarTenantName 
            } ElseIf ($TenantName) {
                If ($TenantName -NotIn $Global:NectarTenantList) {
                    $TList = $Global:NectarTenantList -join ', '
                    Throw "Could not find a tenant with the name $TenantName on https://$Global:NectarCloud. Select one of $TList. $($_.Exception.Message)"
                }
            }
        
        If ($PhoneNumber -And !$Identity) {
            $Identity = (Get-NectarNumber -PhoneNumber $PhoneNumber -Tenant $TenantName -ResultSize 1 -ErrorVariable PhoneNumError).ID
        }
        
        If (!$PhoneNumError) {
            $URI = "https://$Global:NectarCloud/dapi/numbers/$Identity/state?state=$NumberState&tenant=$TenantName"
            Write-Verbose $URI
        
            If (($Comment) -And ($NumberState -eq "RESERVED")) {
                # Convert special characters to URI-compatible versions
                #$Comment = [uri]::EscapeDataString($Comment)
                $URI += "&comment=$Comment"
            }
            
            Try {
                $NULL = Invoke-RestMethod -Method PUT -URI $URI -Headers $Global:NectarAuthHeader
                Write-Verbose "Successfully applied changes to $PhoneNumber."
            }
            Catch {
                Write-Error "Unable to apply changes for phone number $PhoneNumber. The number may already be in the desired state. $($_.Exception.Message)"
                If ($PSCmdlet.MyInvocation.BoundParameters["ErrorAction"] -ne "SilentlyContinue") { Get-JSONErrorStream -JSONResponse $_ }
            }
        }
    }
}


Function Get-NectarUnallocatedNumber {
    <#
        .SYNOPSIS
        Returns the next available number in a given location/range.
 
        .DESCRIPTION
        Returns the next available number in a given location/range.
         
        .PARAMETER LocationName
        The service location to return a number for
         
        .PARAMETER RangeName
        The range to return a number for
 
        .PARAMETER TenantName
        The name of the Nectar DXP tenant. Used in multi-tenant configurations.
         
        .EXAMPLE
        Get-NectarUnallocatedNumber -RangeName Jericho
        Returns the next available number in the Jericho range.
             
        .NOTES
        Version 1.1
    #>

    
    [Alias("gnun")]
    Param (
        [Parameter(Mandatory=$False)]
        [string]$LocationName, 
        [Parameter(Mandatory=$False)]
        [string]$RangeName,
        [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)]
        [string]$TenantName
    )
    
    # Use globally set tenant name, if one was set and not explicitly included in the command
    If ($Global:NectarTenantName -And !$PSBoundParameters.ContainsKey('TenantName')) { 
                $TenantName = $Global:NectarTenantName 
            } ElseIf ($TenantName) {
                If ($TenantName -NotIn $Global:NectarTenantList) {
                    $TList = $Global:NectarTenantList -join ', '
                    Throw "Could not find a tenant with the name $TenantName on https://$Global:NectarCloud. Select one of $TList. $($_.Exception.Message)"
                }
            }
    
    $NextFreeNum = Get-NectarNumber -LocationName $LocationName -RangeName $RangeName -NumberState UNUSED -Tenant $TenantName -ResultSize 1 

    If ($NextFreeNum) {
        Return $NextFreeNum
    }
    Else {
        Write-Error "No available phone number found."
    }
}