Public/Functions/Support/VoiceConfig/Get-TeamsPhoneNumber.ps1
# Module: TeamsFunctions # Function: Teams User Voice Configuration # Author: David Eberhardt # Updated: 02-JUN-2022 # Status: Live #CHECK: How to use -ExpandLocation? - Currently broken? function Get-TeamsPhoneNumber { <# .SYNOPSIS Queries Phone Numbers in Teams and returns a concise result .DESCRIPTION Wrapper for Get-CsPhoneNumberAssignment to ascertain the NumberType or filter by it. 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-CsPhoneNumberAssignment 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-CsPhoneNumberAssignment. .PARAMETER NumberType 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-CsPhoneNumberAssignment of the type CallingPlan or OperatorConnect. Requires the LocationId or Location Name (DisplayName). .PARAMETER City Returns an Object abbreviated from Get-CsPhoneNumberAssignment of the type CallingPlan or OperatorConnect. Requires the City as displayed in Get-CsPhoneNumberAssignment. For DirectRouting this value will always be "All Locations" and cannot be queried by using this switch. .PARAMETER CapabilitiesContain Returns an Object abbreviated from Get-CsPhoneNumberAssignment. Available options are: ConferenceAssignment, VoiceApplicationAssignment, UserAssignment .PARAMETER IsNotAssigned Only returns free numbers .EXAMPLE Get-TeamsPhoneNumber -PhoneNumber +15551234567 Returns one Phone Number Object for this string The Object returned is an abbreviation and extension of the Output displayed with Get-CsPhoneNumberAssignment. .EXAMPLE Get-TeamsPhoneNumber -PhoneNumber +1555* Returns all Phone Number Objects found starting with this string .EXAMPLE Get-TeamsPhoneNumber -NumberType OperatorConnect Returns all Phone Numbers added to the tenant via OperatorConnect. Also available: CallingPlan .EXAMPLE Get-TeamsPhoneNumber -Location 00000000-0000-0000-0000-000000000000 Returns all Phone Numbers present in the tenant that matches this LocationID (not the CivicAddressID) Available only for Numbers of PhoneNumberType CallingPlan .EXAMPLE Get-TeamsPhoneNumber -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-TeamsPhoneNumber -City Auckland Returns all Phone Numbers present in the tenant that matches the City provided The City is required to be provided as displayed with Get-CsPhoneNumberAssignment .EXAMPLE Get-TeamsPhoneNumber -CapabilitiesContain UserAssignment Returns all Phone Numbers present in the tenant that can be assigned to a user .EXAMPLE Get-TeamsPhoneNumber -CapabilitiesContain UserAssignment -PhoneNumber +1555* -Location "R&D Building" Returns all Phone Numbers present in the tenant that match all criteria, i.E. with the Capability to assign to User, 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 NumberType. Other functionality is purely wrapped around Get-CsPhoneNumberAssignment to provide a rounded picture For full information, please use Get-CsPhoneNumberAssignment The corresponding Set-Cmdlet Set-TeamsPhoneNumber also supports the NumberType '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/master/docs/Get-TeamsPhoneNumber.md .LINK https://github.com/DEberhardt/TeamsFunctions/tree/master/docs/about_VoiceConfiguration.md .LINK https://github.com/DEberhardt/TeamsFunctions/tree/master/docs/about_UserManagement.md .LINK https://github.com/DEberhardt/TeamsFunctions/tree/master/docs/about_Supporting_Functions.md .LINK https://github.com/DEberhardt/TeamsFunctions/tree/master/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('PhoneNumberType', 'Type')] [string]$NumberType, [Parameter(ValueFromPipelineByPropertyName)] [Alias('LocationId', 'LocationName')] [string]$Location, [Parameter(ValueFromPipelineByPropertyName)] [string]$City, [Parameter(ValueFromPipelineByPropertyName)] [ValidateSet('UserAssignment', 'VoiceApplicationAssignment', 'ConferenceAssignment')] [string]$CapabilitiesContain, [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) { #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')) { Write-Verbose -Message "Location '$Location' queried against Get-CsOnlineLisCivicAddress" try { $CsOnlineLisLocation = if ( $Address -match '^[0-9a-f]{8}-([0-9a-f]{4}\-){3}[0-9a-f]{12}$' ) { Get-CsOnlineLisLocation -LocationId $Address -ErrorAction Stop } else { Get-CsOnlineLisLocation -Location "$Address" -ErrorAction Stop } $Parameters.Location = $CsOnlineLisLocation.LocationId } catch { throw "Location '$Location' not found (CsOnlineLisLocation or CsOnlineLisCivicAddress)! Please provide CsOnlineLisLocation,CsOnlineLisCivicAddress or Description" } } if ($PSBoundParameters.ContainsKey('City')) { $Parameters.City = $City } if ($PSBoundParameters.ContainsKey('CapabilitiesContain')) { $Parameters.CapabilitiesContain = $CapabilitiesContain } #IsNotAssigned is not processed here, but after the query # defining Output Object class Class PhoneNumberObject { [string]$PhoneNumber [ValidateSet('CallingPlan', 'OperatorConnect', 'DirectRouting')] [string]$NumberType [AllowNull()] [string]$LocationId [AllowNull()] [string]$LocationName [AllowNull()] [string]$City [AllowNull()] [string[]]$Capability [nullable[bool]]$Assigned [AllowNull()] [string]$AssignedTo [AllowNull()] [string]$AssignedToSIP [AllowNull()] [string]$AssignedToObjectType [AllowNull()] [string]$AssignedPstnTargetId } } #begin process { Write-Verbose -Message "[PROCESS] $($MyInvocation.MyCommand)" try { if ($PSBoundParameters.ContainsKey('Debug') -or $DebugPreference -eq 'Continue') { "Function: $($MyInvocation.MyCommand.Name) - Parameters", ($Parameters | Format-Table -AutoSize | Out-String).Trim() | Write-Debug } $Numbers = Get-CsPhoneNumberAssignment @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-CsPhoneNumberAssignment: $($_.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 NumberType if ( $PSBoundParameters.ContainsKey('NumberType') ) { #TEST This may be able to be rebound to NumberType if it can be ascertained that it _does_ show OperatorConnect and not CallingPlans # $Numbers = $Numbers | Where-Object NumberType -eq $NumberType } switch ($NumberType) { #'OperatorConnect' { $Numbers = $Numbers | Where-Object NumberType -eq $NumberType } #'CallingPlan' { $Numbers = $Numbers | Where-Object NumberType -eq $NumberType } #'DirectRouting' { $Numbers = $Numbers | Where-Object NumberType -eq $NumberType } 'OperatorConnect' { $Numbers = $Numbers | Where-Object PstnPartnerName -NE 'Microsoft' } 'CallingPlan' { $Numbers = $Numbers | Where-Object PstnPartnerName -EQ 'Microsoft' } 'DirectRouting' { $Numbers = $Numbers | Where-Object PstnPartnerName -EQ '' } } } # Filtering by assignment if ($PSBoundParameters.ContainsKey('IsNotAssigned')) { $Numbers = $Numbers | Where-Object { $_.PstnAssignmentStatus -EQ 'Unassigned' } } foreach ($Number in $Numbers) { # Translating parameters $NumberLocationName = $null $NumberLocationName = if ( $Number.LocationId -NE '00000000-0000-0000-0000-000000000000' ) { (Get-CsOnlineLisLocation -LocationId $Number.LocationId).Description } else { $null } #TEST This may be able to be rebound to NumberType if it can be ascertained that it _does_ show OperatorConnect and not CallingPlans #$NumberNumberType = $Number.NumberType $NumberNumberType = switch ($Number.PstnPartnerName) { 'Microsoft' { 'CallingPlan' } '' { 'DirectRouting' } Default { 'OperatorConnect' } } # Finding assignments $NumberAssignment = $null #VALIDATE Design with nested Objects may be detrimental for export - flattening to UPN, custom name switch ($Number.PstnAssignmentStatus) { 'UserAssigned' { $NumberAssignment = Get-CsOnlineUser $Number.AssignedPstnTargetId -WarningAction SilentlyContinue #$NumberAssignedTo = $NumberAssignment | Select-Object UserPrincipalName, SipAddress, IsSipEnabled $NumberAssignedTo = $NumberAssignment.UserPrincipalName $NumberAssignedToSIP = $NumberAssignment.SipAddress $NumberAssignedToObjectType = 'User' Write-Verbose -Message 'AssignedTo is populated from the CsOnlineUser Object, displaying the UserPrincipalName' } 'VoiceApplicationAssigned' { $NumberAssignment = Get-CsOnlineUser $Number.AssignedPstnTargetId -WarningAction SilentlyContinue #$NumberAssignedTo = $NumberAssignment | Select-Object UserPrincipalName, SipAddress, IsSipEnabled $NumberAssignedTo = $NumberAssignment.UserPrincipalName $NumberAssignedToSIP = $NumberAssignment.SipAddress $NumberAssignedToObjectType = 'ApplicationEndpoint' Write-Verbose -Message 'AssignedTo is populated from the CsOnlineUser Object, displaying the UserPrincipalName' } 'ConferenceAssigned' { $NumberAssignment = Get-CsOnlineDialInConferencingBridge $Number.AssignedPstnTargetId #$NumberAssignedTo = $NumberAssignment | Select-Object Region,Name,DefaultServiceNumber $NumberAssignedTo = "$($NumberAssignment.Region) $($NumberAssignment.Name) $($NumberAssignment.DefaultServiceNumber)" $NumberAssignedToSIP = $null $NumberAssignedToObjectType = 'ConferenceBridge' Write-Verbose -Message 'AssignedTo populated from the CsOnlineDialInConferencingBridge Object, displaying Region, Name & DefaultNumber (string)' } 'Unassigned' { $NumberAssignment = $NumberAssignedToObjectType = $null } 'Default' { $NumberAssignment = $NumberAssignedToObjectType = $null } } # Creating Output Object $PhoneNumberObject = $null $PhoneNumberObject = [PhoneNumberObject]@{ PhoneNumber = $Number.TelephoneNumber NumberType = $NumberNumberType LocationId = $Number.LocationId LocationName = $NumberLocationName City = $Number.City Capability = $Number.Capability Assigned = [bool]$($NumberAssignment) AssignedTo = $NumberAssignedTo AssignedToSIP = $NumberAssignedToSIP AssignedToObjectType = $NumberAssignedToObjectType AssignedPstnTargetId = $Number.AssignedPstnTargetId } Write-Output $PhoneNumberObject } } else { # Assuming Number is not in the Tenant and therefore a DirectRouting Number $NumberAssignment = $null Write-Information 'INFO: Number not found in the tenant, this is expected for Direct Routing numbers.' # Creating Output Object $PhoneNumberObject = $null $PhoneNumberObject = [PhoneNumberObject]@{ PhoneNumber = $PhoneNumber NumberType = 'DirectRouting' LocationId = $null LocationName = $null City = $null Capability = @('ConferenceAssignment', 'VoiceApplicationAssignment', 'UserAssignment' ) Assigned = $false AssignedTo = $null AssignedToSIP = $null AssignedToObjectType = $null AssignedPstnTargetId = $null } Write-Output $PhoneNumberObject } } #process end { Write-Verbose -Message "[END ] $($MyInvocation.MyCommand)" } #end } #Get-TeamsPhoneNumber |