Lync DID/Phone Number Capacity Planning

*Updated 12/6/2016 to correct issue where any range with 100% available resulted in 0.  Now it correctly shows all available.

Any large Lync environment that deployed Enterprise Voice likely had the topic of how to manage all of those phone numbers surface at some point.  One of the topology’s I work in currently has over 100,000 DID’s ported to Lync in roughly 400 DID blocks (Unassigned Number Ranges) globally.  Using the guts from other scripts I have posted on this blog it wasn’t too difficult to use that in order to grab a count rather than specific LineURI’s.

This script will generate two ending arrays that export to CSVs.  The PerRange-DIDCapactity CSV details the Percent Available, Assigned Count, Available Count and Total Count for every Get-CSUnassignedNumberRange you have within Lync’s configuration.


It’s likely many large environments have more than a single range ported to one site so it’s even nicer to have all those ranges consolidated, because math.  The Grouped-DIDCapacity CSV will simply group all ranges together for those that have matches names and take care of that math stuff for you.  Leaving you with a quick view  at which sites may be running short on available DID’s to use.


To run effectively it’s dependent upon your naming convention of your Unassigned Number Ranges.  Based on that you’ll need to modify how the $LID variable is determined to sort the names.  For example, in this environment all the ranges have a standard format of REGION-CITY-Description.  Due to this, I can assign $LID as below where $RangeListing is one of the unassigned number ranges.  Using split at the dash and taking [1] will give result me with the CITY for the $LID.

$LID = $RangeListing.Identity.Split(‘-‘)[1]

This should hopefully be the only modification you need to do to have this function.  Enough words, below is what you scrolled down to anyways.


#DID Capacity Script - output will lists a DID/LineURI/Phonenumber count from each Unassigned Number Range
#Two output provides, one is every individual unassigned number range, another is the ranges grouped by similiarities in their name.
#Depending on your environment you'll need to determine how to identify/separate the range names when assigning $LID = $RangeListing.Identity.Split('-')[1] In my environment all range names are determined by REGION-SITENAME so I split the identity at the - and set the SITENAME as $LID only.
#Created by Jeff McBride
#I'm not a pro, run this at your own risk
#Last Update: 8/18/2016

$WarningPreference = "SilentlyContinue"
$FileDate = (Get-Date).ToString('MM-dd-yyyy')

$DIDRange = Get-CsUnassignedNumber | Sort Identity
[System.Collections.ArrayList]$ReplaceCSVArray = @() 

Foreach ($RangeListing in $DIDRange) {
 $DIDRangeStart = $RangeListing.NumberRangeStart
 $DIDRangeEnd = $RangeListing.NumberRangeEnd
 $DIDRangeLike = $DIDRangeEnd + "*"

 #### Get actively used DID's from all account types
 $GetUserLineURI = Get-CSUser -Filter {LineURI -ge $DIDRangeStart -and LineURI -le $DIDRangeEnd -or LineURI -like $DIDRangeLike} | select LineURI
 $GetUSERPriv = Get-CSUser -Filter {PrivateLine -ge $DIDRangeStart -and PrivateLine -le $DIDRangeEnd -or PrivateLine -like $DIDRangeLike} | select @{N='LineURI';E={($_.PrivateLine)}}
 $GetCAP = Get-CSCommonAreaPhone -Filter {LineURI -ge $DIDRangeStart -and LineURI -le $DIDRangeEnd -or LineURI -like $DIDRangeLike} | select LineURI
 $GetAnalog = Get-CSAnalogDevice -Filter {LineURI -ge $DIDRangeStart -and LineURI -le $DIDRangeEnd -or LineURI -like $DIDRangeLike} | select LineURI
 $GetWorkFlow = Get-CSRGSWorkflow | Where-Object {$_.LineURI -ge $DIDRangeStart -and $_.LineURI -le $DIDRangeEnd -or $_.LineURI -like $DIDRangeLike} | Select LineURI
 $GetEXUMContact = Get-CSEXUMContact -Filter {LineURI -ge $DIDRangeStart -and LineURI -le $DIDRangeEnd -or LineURI -like $DIDRangeLike} | Select LineURI
 $GetDialInAccess = Get-CsDialInConferencingAccessNumber -Filter {LineURI -ge $DIDRangeStart -and LineURI -le $DIDRangeEnd -or LineURI -like $DIDRangeLike} | Select LineURI
 $GetMeetingRoom = Get-CSMeetingRoom -Filter {LineURI -ge $DIDRangeStart -and LineURI -le $DIDRangeEnd -or LineURI -like $DIDRangeLike} | Select LineURI

 [System.Collections.ArrayList]$CurrentlyAssignedDID = @()
 $CurrentlyAssignedDID.Add($GetUserLineURI) > $null
 $CurrentlyAssignedDID.Add($GetUserPriv) > $null
 $CurrentlyAssignedDID.Add($GetCAP) > $null
 $CurrentlyAssignedDID.Add($GetAnalog) > $null
 $CurrentlyAssignedDID.Add($GetWorkFlow) > $null
 $CurrentlyAssignedDID.Add($GetEXUMContact) > $null
 $CurrentlyAssignedDID.Add($GetDialInAccess) > $null
 $CurrentlyAssignedDID.Add($GetMeetingRoom) > $null

 If ([string]$CurrentlyAssignedDID.length -eq "0 0 0 0 0 0 0 0") {
 $AssignedDIDArray = $null
 Write-Host "No DID's in use within range"
 Else {
 $AssignedDIDlower = $CurrentlyAssignedDID.LineURI.ToLower()
 $AssignedDIDtemp = $AssignedDIDlower.TrimStart("tel:+")
 #This is the final array of all actively assigned DIDs as 11 digits only (US)
 $AssignedDIDArray = $AssignedDIDtemp -replace '(.+?);.+','$1'

 ###### Setup array of all DIDs within a range regardless of in use or not
 $DIDRangeStart = $RangeListing.NumberRangeStart.TrimStart("tel:+")
 $DIDRangeEnd = $RangeListing.NumberRangeEnd.TrimStart("tel:+")
 $DIDRangeStart = [long]$DIDRangeStart
 $DIDRangeEnd = [long]$DIDRangeEnd
 $Start = $DIDRangeStart
 $End = $DIDRangeEnd

 $DIDRangeArray = @()
 While ($Start -le $End) {$DIDRangeArray += $Start ; $Start = $Start +1}
 ##### Determine what count of DIDs are available compared to those used

 If ($AssignedDIDArray -eq $null) {
$AssignedDIDArrayUnique = @("00000000000")
 $AvailableDIDArray = Compare-Object $AssignedDIDArrayUnique $DIDRangeArray
 $AvailableDIDArray = $AvailableDIDArray | ? {$_.SideIndicator -eq "=>"}
 Else {
 $AvailableDIDArray = Compare-Object $AssignedDIDArray $DIDRangeArray

 ##### Provide count of each in range
 $AssignedCount = $DIDRangeArray.count - $AvailableDIDArray.count
 Write-Host $AssignedCount "Assigned DID Array" $RangeListing.Identity
 Write-Host $AvailableDIDArray.count "available DID numbers within" $RangeListing.Identity
 Write-Host $DIDRangeArray.count "Total DID Array" $RangeListing.Identity
 Write-Host ""

 $AvailCountTemp = $AvailableDIDArray.count
 $TotalCountTemp = $DIDRangeArray.count
 $PerAvailConvert = $AvailCountTemp / $TotalCountTemp
 $PercentAvailable = "{0:P0}" -f $PerAvailConvert

 $LID = $RangeListing.Identity.Split('-')[1]

 $CountingArray = New-Object PSObject
 $CountingArray | Add-Member -NotePropertyName PercentAvailable -NotePropertyValue $PercentAvailable
 $CountingArray | Add-Member -NotePropertyName AssignedDIDs -NotePropertyValue $AssignedCount
 $CountingArray | Add-Member -NotePropertyName AvailableDIDs -NotePropertyValue $AvailableDIDArray.count
 $CountingArray | Add-Member -NotePropertyName TotalDIDs -NotePropertyValue $DIDRangeArray.count
 $CountingArray | Add-Member -NotePropertyName RangeName -NotePropertyValue $RangeListing.Identity
 $CountingArray | Add-Member -NotePropertyName LID -NotePropertyValue $LID
 $ReplaceCSVArray.Add($CountingArray) > $null

[System.Collections.ArrayList]$GroupedSites = @()
$DIDGroup = $ReplaceCSVArray | Group LID

ForEach ($Name in $DIDGroup) {
 $GroupAssignedSum = $Name.Group.AssignedDIDs | Measure -Sum | Select Sum
 $GroupAvailableSum = $Name.Group.AvailableDIDs | Measure -Sum | Select Sum
 $GroupTotalSum = $Name.Group.TotalDIDs | Measure -Sum | Select Sum

 $GroupPerAvailConvert = $GroupAvailableSum.Sum / $GroupTotalSum.Sum
 $GroupPercentAvailable = "{0:P0}" -f $GroupPerAvailConvert
 $DIDGroupArray = New-Object PSObject
 $DIDGroupArray | Add-Member -NotePropertyName LID -NotePropertyValue $Name.Name
 $DIDGroupArray | Add-Member -NotePropertyName PercentAvailable -NotePropertyValue $GroupPercentAvailable
 $DIDGroupArray | Add-Member -NotePropertyName AssignedDIDs -NotePropertyValue $GroupAssignedSum.Sum
 $DIDGroupArray | Add-Member -NotePropertyName AvailableDIDs -NotePropertyValue $GroupAvailableSum.Sum
 $DIDGroupArray | Add-Member -NotePropertyName TotalDIDs -NotePropertyValue $GroupTotalSum.Sum
 $GroupedSites.Add($DIDGroupArray) > $null

$ReplaceCSVArray | Export-CSV -append -NoTypeInformation (".\PerRange-LyncLead-DIDCapacity_" + $FileDate + ".csv")
$GroupedSites | Export-CSV -append -NoTypeInformation (".\Grouped-LyncLead-DIDCapacity_" + $FileDate + ".csv")

Leave a Reply