Public/Support/VoiceConfig/Get-TeamsTelephoneNumber.ps1
# Module: TeamsFunctions # Function: Teams User Voice Configuration # Author: David Eberhardt # Updated: 12-FEB-2022 # Status: Live #CHECK: How to use -ExpandLocation? - Currently broken? #FIXME: Function not used any longer as Get-CsOnlineTelephoneNumber is deprecated. Scrap? function Get-TeamsTelephoneNumber { <# .SYNOPSIS Queries Phone Numbers in Teams and returns a concise result .DESCRIPTION Wrapper for Get-CsOnlineTelephoneNumber to ascertain the PhoneNumberType or filter by it. Adds value to output with PhoneNumberType interpreted from parameters returned by Get-CsOnlineTelephoneNumber. If a number is not found in the tenant it is assumed to be of the Type DirectRouting and an object is returned. For exact matches, the potential assignment is queried in addition to the boolean assignment status. .PARAMETER PhoneNumber Returns an Object abbreviated from Get-CsOnlineTelephoneNumber of the type CallingPlan or OperatorConnect. Returns a mocked Object for DirectRouting Numbers as these numbers are not present in the tenant. Expected format is E.164 or LineUri. Requires full number. For more granular search use Get-CsOnlineTelephoneNumber. .PARAMETER PhoneNumberType Valid values are CallingPlan, OperatorConnect or DirectRouting Using DirectRouting will return an empty object as these numbers are not present in the tenant. .PARAMETER Location Returns an Object abbreviated from Get-CsOnlineTelephoneNumber of the type CallingPlan or OperatorConnect. Requires the LocationId or Location Name (DisplayName). .PARAMETER CityCode Returns an Object abbreviated from Get-CsOnlineTelephoneNumber of the type CallingPlan or OperatorConnect. Requires the CityCode as displayed in Get-CsOnlineTelephoneNumber. .PARAMETER InventoryType Returns an Object abbreviated from Get-CsOnlineTelephoneNumber of the type CallingPlan or OperatorConnect. Available options are: Subscriber (User), Service (Conferencing), TollFree .PARAMETER IsNotAssigned Only returns free numbers .EXAMPLE Get-TeamsTelephoneNumber -PhoneNumber +15551234567 Returns all Phone Numbers starting with this string (this can be an exact result or an array of numbers) The Object returned is an abbreviation and extension of the Output displayed with Get-CsOnlineTelephoneNumber. .EXAMPLE Get-TeamsTelephoneNumber -PhoneNumber +1555* Returns all Phone Number Objects found starting with this string .EXAMPLE Get-TeamsTelephoneNumber -PhoneNumberType OperatorConnect Returns all Phone Numbers added to the tenant via OperatorConnect. Also available: CallingPlan .EXAMPLE Get-TeamsTelephoneNumber -Location 00000000-0000-0000-0000-000000000000 Returns all Phone Numbers present in the tenant that matches this location ID Available only for Numbers of PhoneNumberType CallingPlan .EXAMPLE Get-TeamsTelephoneNumber -Location "R&D Building" Returns all Phone Numbers present in the tenant that matches the location with the Description "R&D Building" Search by location is only possible for Numbers of PhoneNumberType CallingPlan. For OperatorConnect the Location is set by the Operator and cannot be changed .EXAMPLE Get-TeamsTelephoneNumber -CityCode APAC-NZ-ALL-AUK_AU Returns all Phone Numbers present in the tenant that matches the City Code provided The CityCode is required to be provided as displayed with Get-CsOnlineTelephoneNumber .EXAMPLE Get-TeamsTelephoneNumber -InventoryType Subscriber Returns all Phone Numbers present in the tenant with the InventoryType Subscriber Available options are: Subscriber (User), Service (Conferencing), TollFree .EXAMPLE Get-TeamsTelephoneNumber -InventoryType Subscriber -PhoneNumber +1555* -Location "R&D Building" Returns all Phone Numbers present in the tenant that match all criteria, i.E. with the InventoryType Subscriber, the phone number starting with 1555 (* performs search), and the location translated from the description "R&D Building" .INPUTS System.String .OUTPUTS System.Object .NOTES Simple helper function to query a Phone Number and receive its PhoneNumberType. Sister Function to Get-TeamsPhoneNumber which queries Get-CsPhoneNumberAssignment. Other functionality is purely wrapped around Get-CsOnlineTelephoneNumber to provide a rounded picture For full information, please use Get-CsOnlineTelephoneNumber The corresponding Set-Cmdlet Set-TeamsPhoneNumber also supports the PhoneNumbertype 'DirectRouting'. For searches by PhoneNumber, the CmdLet will interpret any number not found in the tenant to be of the type DirectRouting and return it as such. .COMPONENT VoiceConfiguration .FUNCTIONALITY Queries information about Phone Numbers in the Teams Tenant to inform Voice Configuration .LINK https://github.com/DEberhardt/TeamsFunctions/tree/main/docs/Get-TeamsTelephoneNumber.md .LINK https://github.com/DEberhardt/TeamsFunctions/tree/main/docs/about_VoiceConfiguration.md .LINK https://github.com/DEberhardt/TeamsFunctions/tree/main/docs/about_UserManagement.md .LINK https://github.com/DEberhardt/TeamsFunctions/tree/main/docs/about_Supporting_Functions.md .LINK https://github.com/DEberhardt/TeamsFunctions/tree/main/docs/ #> [CmdletBinding(ConfirmImpact = 'Low')] [OutputType([System.Object])] param( [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)] [Alias('Number', 'TelephoneNumber')] [AllowNull()] [AllowEmptyString()] [string]$PhoneNumber, [Parameter(ValueFromPipelineByPropertyName)] [ValidateSet('CallingPlan', 'OperatorConnect', 'DirectRouting')] [Alias('Type')] [string]$PhoneNumberType, [Parameter(ValueFromPipelineByPropertyName)] [Alias('LocationId', 'LocationName')] [string]$Location, [Parameter(ValueFromPipelineByPropertyName)] [Alias('City')] [string]$CityCode, [Parameter(ValueFromPipelineByPropertyName)] [ValidateSet('Subscriber', 'Service', 'TollFree')] [string]$InventoryType, [Parameter(ValueFromPipelineByPropertyName)] [switch]$IsNotAssigned ) #param begin { Show-FunctionStatus -Level Live Write-Verbose -Message "[BEGIN ] $($MyInvocation.MyCommand)" # Asserting MicrosoftTeams Connection if ( -not (Assert-MicrosoftTeamsConnection) ) { break } # Setting Preference Variables according to Upstream settings if (-not $PSBoundParameters.ContainsKey('Verbose')) { $VerbosePreference = $PSCmdlet.SessionState.PSVariable.GetValue('VerbosePreference') } if (-not $PSBoundParameters.ContainsKey('Confirm')) { $ConfirmPreference = $PSCmdlet.SessionState.PSVariable.GetValue('ConfirmPreference') } if (-not $PSBoundParameters.ContainsKey('WhatIf')) { $WhatIfPreference = $PSCmdlet.SessionState.PSVariable.GetValue('WhatIfPreference') } if (-not $PSBoundParameters.ContainsKey('Debug')) { $DebugPreference = $PSCmdlet.SessionState.PSVariable.GetValue('DebugPreference') } else { $DebugPreference = 'Continue' } if ( $PSBoundParameters.ContainsKey('InformationAction')) { $InformationPreference = $PSCmdlet.SessionState.PSVariable.GetValue('InformationAction') } else { $InformationPreference = 'Continue' } $Stack = Get-PSCallStack $Called = ($stack.length -ge 3) if ( [String]::IsNullOrEmpty($PhoneNumber) ) { $PhoneNumber = $null } # Preparing Splatting Object $parameters = $null $Parameters = @{ 'WarningAction' = 'SilentlyContinue' 'ErrorAction' = 'Stop' } if ($PhoneNumber) { #TEST Rethinking call to Get-CsOnlineTelephoneNumber (must be in number format, not in TEL URI!) #Normalising Phonenumber before call $PhoneNumberSearchMode = if ( $PhoneNumber -match '\*$' ) { $true } else { $false } $PhoneNumber = $PhoneNumber | Format-StringForUse -As Number if ( $PhoneNumberSearchMode ) { Write-Verbose -Message "PhoneNumber interpreted as part of the string (searching all Phone Numbers starting with '$($PhoneNumber.Replace('*',''))'" $Parameters.TelephoneNumberStartsWith = $PhoneNumber } else { Write-Verbose -Message "PhoneNumber interpreted as full number (searching for exact match of '$($PhoneNumber.Replace('*',''))'" $Parameters.TelephoneNumber = $PhoneNumber } } if ($PSBoundParameters.ContainsKey('Location')) { #TEST This is currently untested as no examples were available (only valid for CallingPlans as well!) Write-Verbose -Message "Location '$Location' queried against Get-CsOnlineLisLocation" try { $CsOnlineLisLocation = if ( $Address -match $script:TFMatchGuid ) { Get-CsOnlineLisLocation -LocationId $Address -ErrorAction Stop } else { Get-CsOnlineLisLocation -Location "$Address" -ErrorAction Stop } $Parameters.Location = $CsOnlineLisLocation.LocationId } catch { throw "Location '$Location' not found (CsOnlineLisLocation)! Please provide LocationId or Address Description" } } if ($PSBoundParameters.ContainsKey('CityCode')) { $Parameters.CityCode = $CityCode } if ($PSBoundParameters.ContainsKey('InventoryType')) { $Parameters.InventoryType = $InventoryType } #IsNotAssigned is not processed here, but after the query # defining Output Object class Class PhoneNumberObject { [string]$PhoneNumber [ValidateSet('CallingPlan', 'OperatorConnect', 'DirectRouting')] [string]$PhoneNumberType [AllowNull()] [string]$Location [AllowNull()] [string]$LocationName [AllowNull()] [string]$CityCode [AllowNull()] [string]$InventoryType [nullable[bool]]$Assigned } # Loading Microsoft Numbers (disabled as not needed) #if (-not $global:TeamsFunctionsCsOnlineTelephoneNumber) { $global:TeamsFunctionsCsOnlineTelephoneNumber = Get-CsOnlineTelephoneNumber -ResultSize 10000 } } #begin process { Write-Verbose -Message "[PROCESS] $($MyInvocation.MyCommand)" try { if ($PSBoundParameters.ContainsKey('Debug') -or $DebugPreference -eq 'Continue') { "Function: $($MyInvocation.MyCommand.Name) - Parameters (Get-CsOnlineTelephoneNumber)", ($Parameters | Format-Table -AutoSize | Out-String).Trim() | Write-Debug } $Numbers = Get-CsOnlineTelephoneNumber @Parameters if ($PSBoundParameters.ContainsKey('Debug') -or $DebugPreference -eq 'Continue') { "Function: $($MyInvocation.MyCommand.Name) - Numbers", ($Numbers | Format-Table -AutoSize | Out-String).Trim() | Write-Debug } } catch { Write-Error -Message "Error querying Get-CsOnlineTelephoneNumber: $($_.Exception.Message)" } # Exit strategy if ( -not $Numbers ) { if ( -not $PhoneNumber ) { Write-Information "INFO: No numbers found with these Parameters: $($PSBoundParameters.keys)" continue } } # Output if ( $Numbers ) { # OperatorConnect or CallingPlans Number detected # Filter by PhoneNumberType if ( $PSBoundParameters.ContainsKey('PhoneNumberType') ) { switch ($PhoneNumberType) { 'OperatorConnect' { $Numbers = $Numbers | Where-Object IsManagedByServiceDesk } 'CallingPlan' { $Numbers = $Numbers | Where-Object { -not $_.IsManagedByServiceDesk } } 'DirectRouting' { continue # no action as no numbers will be found anyway. } } } # Filtering by assignment if ($PSBoundParameters.ContainsKey('IsNotAssigned')) { $Numbers = $Numbers | Where-Object { $null -eq $_.TargetType } } foreach ($Number in $Numbers) { # Translating parameters $NumberLocationName = $null $NumberLocationName = if ( $Number.Location ) { (Get-CsOnlineLisLocation -LocationId $Number.Location).Description } else { $null } $NumberPhoneNumberType = if ( $Number.IsManagedByServiceDesk ) { 'OperatorConnect' } else { 'CallingPlan' } #<# Finding assignments - This was disabled due to runtime concerns! $NumberAssignment = $null #$Filter = 'LineURI -like "*{0}*"' -f $Number.Id $Filter = 'LineURI -like "{0}*"' -f $Number.Id $NumberAssignment = if ( $Number.TargetType ) { (Get-CsOnlineUser -Filter $Filter) } else { $null } #> # Creating Output Object $PhoneNumberObject = $null $PhoneNumberObject = [PhoneNumberObject]@{ PhoneNumber = $Number.Id PhoneNumberType = $NumberPhoneNumberType Location = $Number.Location LocationName = $NumberLocationName CityCode = $Number.CityCode InventoryType = $Number.InventoryType Assigned = [bool]$($Number.TargetType) } if ( $NumberAssignment ) { $PhoneNumberObject | Add-Member -MemberType NoteProperty -Name AssignedTo -Value $NumberAssignment.UserPrincipalName } $PhoneNumberObject.Assigned = if ( $NumberAssignment ) { $true } else { $false } Write-Output $PhoneNumberObject } } else { # Assuming Number is not in the Tenant and therefore a DirectRouting Number $NumberAssignment = $null if ( $PhoneNumber ) { # Creating Output Object $PhoneNumberObject = $null $PhoneNumberObject = [PhoneNumberObject]@{ PhoneNumber = $PhoneNumber PhoneNumberType = 'DirectRouting' Location = $null LocationName = $null CityCode = $null InventoryType = 'Any' Assigned = $NumberAssignment } # Finding assignments # Assignment is ONLY run here if a singular result is expected. if ( $PhoneNumber -match '\*$' ) { Write-Verbose "PhoneNumber contains '*' (search mode). This cannot be used to validate Assignments" -Verbose } else { try { $PhoneNumber = $PhoneNumber | Format-StringForUse -as Number #$Filter = 'LineURI -like "*{0}*"' -f $PhoneNumber $Filter = 'LineURI -like "{0}*"' -f $PhoneNumber $NumberAssignment = (Get-CsOnlineUser -Filter $Filter -ErrorAction Stop) } catch { $NumberAssignment = $null } if ( $NumberAssignment ) { $PhoneNumberObject | Add-Member -MemberType NoteProperty -Name AssignedTo -Value $NumberAssignment.UserPrincipalName } $PhoneNumberObject.Assigned = if ( $NumberAssignment ) { $true } else { $false } } Write-Information 'INFO: Number not found in the tenant, this is expected for Direct Routing numbers.' Write-Output $PhoneNumberObject } } } #process end { Write-Verbose -Message "[END ] $($MyInvocation.MyCommand)" } #end } #Get-TeamsPhoneNumber |