Private/Collect/Get-TcTeamsPhoneCollector.ps1

function Get-TcTeamsPhoneCollector {
    <#
    .SYNOPSIS
    Lists the Teams Phone collectors, in the order they run.
    #>

    [CmdletBinding()]
    [OutputType([hashtable])]
    param()

    @(
        @{
            Id       = 'TEL-01'
            Workload = 'Teams Phone'
            Title    = 'Phone numbers'
            Service  = 'Teams'
            Collect  = {
                $assignees = @{}
                foreach ($u in Get-TcTable 'teamsUsers') { if ((Get-TcProperty $u 'ObjectId')) { $assignees[[string](Get-TcProperty $u 'ObjectId')] = (Get-TcProperty $u 'UPN') } }
                $numbers = [System.Collections.Generic.List[object]]::new(); $skip = 0
                do { $page = @(Get-CsPhoneNumberAssignment -Top 1000 -Skip $skip -ErrorAction Stop); foreach ($x in $page) { $numbers.Add($x) }; $skip += 1000 } while ($page.Count -eq 1000)
                Add-TcTable 'phoneNumbers' @(foreach ($x in $numbers) {
                        $t = [string](Get-TcProperty $x 'AssignedPstnTargetId')
                        [ordered]@{
                            'Number'            = (Get-TcProperty $x 'TelephoneNumber')
                            'Type'              = [string](Get-TcProperty $x 'NumberType')
                            'Activation'        = [string](Get-TcProperty $x 'ActivationState')
                            'Assignment'        = [string](Get-TcProperty $x 'PstnAssignmentStatus')
                            'Assigned to'       = $(if ($assignees[$t]) { $assignees[$t] } else { $t })
                            'Capabilities'      = (Format-TcValue (Get-TcProperty $x 'Capability'))
                            'City'              = (Get-TcProperty $x 'City')
                            'Country'           = (Get-TcProperty $x 'IsoCountryCode')
                            'Partner'           = (Get-TcProperty $x 'PstnPartnerName')
                            'Porting'           = [string](Get-TcProperty $x 'PortInOrderStatus')
                            'Source'            = [string](Get-TcSafeProperty $x 'NumberSource')
                            'Emergency address' = [string](Get-TcSafeProperty $x 'CivicAddressId')
                        }
                    })
            }
        }

        @{
            Id       = 'TEL-02'
            Workload = 'Teams Phone'
            Title    = 'Resource accounts, auto attendants and call queues'
            Service  = 'Teams'
            Collect  = {
                $usage = @{ 'ce933385-9390-45d1-9512-c8d228074e07' = 'Auto attendant'; '11cd3e2e-fccb-42ad-ad00-878b93575e07' = 'Call queue' }
                Add-TcTable 'resourceAccounts' @(foreach ($r in @(Get-CsOnlineApplicationInstance -ErrorAction Stop)) {
                        [ordered]@{
                            'UPN'      = (Get-TcProperty $r 'UserPrincipalName')
                            'Name'     = (Get-TcProperty $r 'DisplayName')
                            'Number'   = (([string](Get-TcProperty $r 'PhoneNumber')) -replace '^tel:', '')
                            'Usage'    = $(if ($usage[[string](Get-TcProperty $r 'ApplicationId')]) { $usage[[string](Get-TcProperty $r 'ApplicationId')] } else { [string](Get-TcProperty $r 'ApplicationId') })
                            'ObjectId' = [string](Get-TcProperty $r 'ObjectId')
                        }
                    })
                $resourceNames = @{}; foreach ($r in Get-TcTable 'resourceAccounts') { $resourceNames[[string](Get-TcProperty $r 'ObjectId')] = (Get-TcProperty $r 'Name') }
                Invoke-TcCollectorPart 'Auto attendants' {
                    $autoAttendants = [System.Collections.Generic.List[object]]::new(); $skip = 0
                    do { $page = @(Get-CsAutoAttendant -First 100 -Skip $skip -ErrorAction Stop); foreach ($x in $page) { $autoAttendants.Add($x) }; $skip += 100 } while ($page.Count -eq 100)
                    Add-TcTable 'autoAttendants' @(foreach ($a in $autoAttendants) {
                            [ordered]@{
                                'Name'              = (Get-TcProperty $a 'Name')
                                'Language'          = (Get-TcProperty $a 'LanguageId')
                                'Time zone'         = (Get-TcProperty $a 'TimeZoneId')
                                'Resource accounts' = ((@((Get-TcProperty $a 'ApplicationInstances')) | ForEach-Object { if ($resourceNames[[string]$_]) { $resourceNames[[string]$_] } else { [string]$_ } }) -join ', ')
                                'Call flows'        = @((Get-TcProperty $a 'CallFlows')).Count
                                'Schedules'         = @((Get-TcProperty $a 'Schedules')).Count
                                'Operator'          = [string](Get-TcSafeProperty (Get-TcProperty $a 'Operator') 'Type')
                                'Directory lookup'  = [string](Get-TcSafeProperty $a 'DirectoryLookupScope')
                                'Identity'          = [string](Get-TcProperty $a 'Identity')
                            }
                        })
                }
                Invoke-TcCollectorPart 'Call queues' {
                    $callQueues = [System.Collections.Generic.List[object]]::new(); $skip = 0
                    do { $page = @(Get-CsCallQueue -First 100 -Skip $skip -ErrorAction Stop -WarningAction SilentlyContinue); foreach ($x in $page) { $callQueues.Add($x) }; $skip += 100 } while ($page.Count -eq 100)
                    Add-TcTable 'callQueues' @(foreach ($queue in $callQueues) {
                            [ordered]@{
                                'Name'              = (Get-TcProperty $queue 'Name')
                                'Routing'           = [string](Get-TcProperty $queue 'RoutingMethod')
                                'Agents'            = @((Get-TcProperty $queue 'Agents')).Count
                                'Lists'             = @((Get-TcProperty $queue 'DistributionLists')).Count
                                'Teams channel'     = [bool](Get-TcSafeProperty $queue 'ChannelId')
                                'Overflow'          = ('{0} after {1}' -f (Get-TcProperty $queue 'OverflowAction'), (Get-TcProperty $queue 'OverflowThreshold'))
                                'Expiration'        = ('{0} after {1} s' -f (Get-TcProperty $queue 'TimeoutAction'), (Get-TcProperty $queue 'TimeoutThreshold'))
                                'Resource accounts' = ((@((Get-TcProperty $queue 'ApplicationInstances')) | ForEach-Object { if ($resourceNames[[string]$_]) { $resourceNames[[string]$_] } else { [string]$_ } }) -join ', ')
                                'Presence'          = [bool](Get-TcProperty $queue 'PresenceBasedRouting')
                            }
                        })
                }
            }
        }

        @{
            Id       = 'TEL-03'
            Workload = 'Teams Phone'
            Title    = 'Direct Routing, voice routes and dial plans'
            Service  = 'Teams'
            Collect  = {
                Add-TcTable 'pstnGateways' @(foreach ($g in @(Get-CsOnlinePSTNGateway -ErrorAction Stop)) {
                        [ordered]@{
                            'FQDN'         = (Get-TcProperty $g 'FQDN')
                            'Port'         = (Get-TcProperty $g 'SipSignalingPort')
                            'Enabled'      = [bool](Get-TcProperty $g 'Enabled')
                            'Max sessions' = (Get-TcProperty $g 'MaxConcurrentSessions')
                        }
                    })
                Invoke-TcCollectorPart 'Voice routes' { Add-TcTable 'voiceRoutes' @(foreach ($r in @(Get-CsOnlineVoiceRoute -ErrorAction Stop)) {
                            [ordered]@{
                                'Name'     = [string](Get-TcProperty $r 'Identity')
                                'Pattern'  = (Get-TcProperty $r 'NumberPattern')
                                'Gateways' = (Format-TcValue (Get-TcProperty $r 'OnlinePstnGatewayList'))
                                'Usages'   = (Format-TcValue (Get-TcProperty $r 'OnlinePstnUsages'))
                                'Priority' = (Get-TcProperty $r 'Priority')
                            }
                        }) }
                Invoke-TcCollectorPart 'Voice routing policies' { Add-TcTable 'voiceRoutingPolicies' @(foreach ($r in @(Get-CsOnlineVoiceRoutingPolicy -ErrorAction Stop)) { [ordered]@{ 'Name' = [string](Get-TcProperty $r 'Identity'); 'PSTN usages' = (Format-TcValue (Get-TcProperty $r 'OnlinePstnUsages')) } }) }
                Invoke-TcCollectorPart 'Dial plans' { Add-TcTable 'dialPlans' @(foreach ($r in @(Get-CsTenantDialPlan -ErrorAction Stop)) {
                            [ordered]@{
                                'Name'        = [string](Get-TcProperty $r 'Identity')
                                'Rules'       = @((Get-TcProperty $r 'NormalizationRules')).Count
                                'Description' = (Get-TcProperty $r 'Description')
                            }
                        }) }
                Invoke-TcCollectorPart 'Translation rules' { if (Get-Command Get-CsTeamsTranslationRule -ErrorAction SilentlyContinue) {
                        Add-TcTable 'translationRules' @(foreach ($r in @(Get-CsTeamsTranslationRule -ErrorAction Stop)) {
                                [ordered]@{
                                    'Name'        = [string](Get-TcProperty $r 'Identity')
                                    'Pattern'     = (Get-TcProperty $r 'Pattern')
                                    'Translation' = (Get-TcProperty $r 'Translation')
                                }
                            })
                    } }
            }
        }

        @{
            Id       = 'TEL-04'
            Workload = 'Teams Phone'
            Title    = 'Emergency addresses and locations, conference bridges'
            Service  = 'Teams'
            Collect  = {
                $locations = @{}
                Invoke-TcCollectorPart 'Emergency locations' { foreach ($l in @(Get-CsOnlineLisLocation -ErrorAction Stop)) { $k = [string](Get-TcProperty $l 'CivicAddressId'); $locations[$k] = 1 + [int]$locations[$k] } }
                Add-TcTable 'emergencyAddresses' @(foreach ($a in @(Get-CsOnlineLisCivicAddress -ErrorAction Stop)) {
                        [ordered]@{
                            'Description'       = (Get-TcProperty $a 'Description')
                            'Company'           = (Get-TcProperty $a 'CompanyName')
                            'Address'           = ('{0} {1} {2}' -f (Get-TcProperty $a 'HouseNumber'), (Get-TcProperty $a 'StreetName'), (Get-TcProperty $a 'StreetSuffix')).Trim()
                            'City'              = (Get-TcProperty $a 'City')
                            'State or province' = (Get-TcProperty $a 'StateOrProvince')
                            'Postal code'       = (Get-TcProperty $a 'PostalCode')
                            'Country'           = (Get-TcProperty $a 'CountryOrRegion')
                            'Validation'        = [string](Get-TcSafeProperty $a 'ValidationStatus')
                            'Locations'         = [int]$locations[[string](Get-TcProperty $a 'CivicAddressId')]
                        }
                    })
                Invoke-TcCollectorPart 'Audio conferencing bridges' {
                    if (Get-Command Get-CsOnlineDialInConferencingBridge -ErrorAction SilentlyContinue) {
                        Add-TcTable 'conferenceBridges' @(foreach ($b in @(Get-CsOnlineDialinConferencingBridge -ErrorAction Stop)) {
                                [ordered]@{
                                    'Name'           = (Get-TcProperty $b 'Name')
                                    'Default number' = [string](Get-TcProperty $b 'DefaultServiceNumber')
                                    'Numbers'        = (Format-TcValue (Get-TcProperty $b 'ServiceNumbers'))
                                }
                            })
                    }
                }
            }
        }

        @{
            Id       = 'TEL-05'
            Workload = 'Teams Phone'
            Title    = 'Calling settings of voice users (forwarding, delegation)'
            Service  = 'Teams'
            Depth    = 'Complete'
            Requires = @('teamsUsers')
            Collect  = {
                $voiceUsers = @(Limit-TcCollectorRow 'voice users' @(Get-TcTable 'teamsUsers' | Where-Object { (Get-TcProperty $_ 'Enterprise Voice') }) ([int](Get-TcCollectionSetting 'maxCallingUsers')))
                if (-not (Get-Command Get-CsUserCallingSettings -ErrorAction SilentlyContinue)) { throw 'Get-CsUserCallingSettings is not recognized as a name of a cmdlet (MicrosoftTeams 4.0 or later is required)' }
                Add-TcTable 'userCallingSettings' @(foreach ($u in $voiceUsers) {
                        try {
                            $s = Get-CsUserCallingSettings -Identity (Get-TcProperty $u 'UPN') -ErrorAction Stop
                            [ordered]@{
                                'UPN'        = (Get-TcProperty $u 'UPN')
                                'Forwarding' = $(if ((Get-TcProperty $s 'IsForwardingEnabled')) { '{0} to {1}' -f (Get-TcProperty $s 'ForwardingType'), (Get-TcProperty $s 'ForwardingTarget') })
                                'Unanswered' = $(if ((Get-TcProperty $s 'IsUnansweredEnabled')) { '{0} after {1}' -f (Get-TcProperty $s 'UnansweredTarget'), (Get-TcProperty $s 'UnansweredDelay') })
                                'Delegates'  = (Format-TcValue (@((Get-TcProperty $s 'Delegates')) | ForEach-Object { (Get-TcProperty $_ 'Id') }))
                                'Call group' = (Format-TcValue (Get-TcProperty $s 'CallGroupTargets'))
                            }
                        }
                        catch { Add-TcCollectorWarning "Calling settings of $(Get-TcProperty $u 'UPN')" $_ }
                    })
            }
        }

        @{
            Id       = 'TEL-06'
            Workload = 'Teams Phone'
            Title    = 'Schedules, holidays and network topology (emergency, routing)'
            Service  = 'Teams'
            Collect  = {
                Invoke-TcCollectorPart 'Schedules and holidays' {
                    Add-TcTable 'phoneSchedules' @(foreach ($s in @(Get-CsOnlineSchedule -ErrorAction Stop)) {
                            [ordered]@{
                                'Name'    = (Get-TcProperty $s 'Name')
                                'Type'    = $(if ((Get-TcProperty $s 'FixedSchedule')) { 'Fixed dates (holidays)' } else { 'Weekly (business hours)' })
                                'Used by' = @((Get-TcProperty $s 'AssociatedConfigurationIds')).Count
                            }
                        })
                }
                Invoke-TcCollectorPart 'Network topology' {
                    $r = @(foreach ($x in @(Get-CsTenantNetworkSite -ErrorAction Stop)) {
                            [ordered]@{
                                'Type'   = 'Network site'
                                'Name'   = [string](Get-TcProperty $x 'NetworkSiteID')
                                'Detail' = ('{0} - emergency {1}' -f (Get-TcProperty $x 'Description'), (Get-TcProperty $x 'EmergencyCallRoutingPolicy')).Trim(' -')
                            }
                        })
                    $r += @(foreach ($x in @(Get-CsTenantNetworkSubnet -ErrorAction Stop)) {
                            [ordered]@{
                                'Type'   = 'Subnet'
                                'Name'   = ('{0}/{1}' -f (Get-TcProperty $x 'SubnetID'), (Get-TcProperty $x 'MaskBits'))
                                'Detail' = [string](Get-TcProperty $x 'NetworkSiteID')
                            }
                        })
                    $r += @(foreach ($x in @(Get-CsTenantTrustedIPAddress -ErrorAction Stop)) {
                            [ordered]@{
                                'Type'   = 'Trusted IP address'
                                'Name'   = ('{0}/{1}' -f (Get-TcProperty $x 'IPAddress'), (Get-TcProperty $x 'MaskBits'))
                                'Detail' = (Get-TcProperty $x 'Description')
                            }
                        })
                    Add-TcTable 'phoneNetworkTopology' $r
                }
            }
        }

    )
}