Public/Functions/VoiceConfig/Get-TeamsVoiceRoutingChain.ps1

# Module: TeamsFunctions
# Function: Tenant Voice Configuration
# Author: David Eberhardt
# Updated: 19-FEB-2022
# Status: RC




function Get-TeamsVoiceRoutingChain {
  <#
  .SYNOPSIS
    Queries an Online Voice Routing Policy and all associated Objects dependent on configuration
  .DESCRIPTION
    Returns a custom object detailing voice routing chain
  .PARAMETER Object
    CsOnlineVoiceRoutingPolicy Object. Identifying the Online Voice Routing Policy
    Required to identify the correct OVP and start the determination chain
  .PARAMETER Identity
    String. Name (Identity) of the Online Voice Routing Policy
    Required to identify the correct OVP and start the determination chain
  .EXAMPLE
    Get-TeamsVoiceRoutingChain -Identity "OVP-AMER-GSIP-MX"
    Queries the Online Voice Routing Policy OVP-AMER-GSIP-MX and determines the Chain.
    Determines Pstn Usages nested in this Policy and Voice Routes that reference this Pstn Usage.
  .EXAMPLE
    Get-TeamsVoiceRoutingChain -Identity $CsOnlineVoiceRoutingPolicy
    Queries the Online Voice Routing Policy Identified in the $CsOnlineVoiceRoutingPolicy Object and determines the Chain.
    Determines Pstn Usages nested in this Policy and Voice Routes that reference this Pstn Usage.
  .EXAMPLE
    $CsOnlineVoiceRoutingPolicy | Get-TeamsVoiceRoutingChain
    Queries the Online Voice Routing Policy Identified in the $CsOnlineVoiceRoutingPolicy Object and determines the Chain.
    Determines Pstn Usages nested in this Policy and Voice Routes that reference this Pstn Usage.
  .INPUTS
    System.String
    System.Object
  .OUTPUTS
    System.Object
  .NOTES
    Sister CmdLet to Find-TeamsUserVoiceRoute that focuses on determining a Route for a Call dependent on the dialled number
  .COMPONENT
    Tenant Voice Routing
  .FUNCTIONALITY
    Direct Routing
  .LINK
    https://github.com/DEberhardt/TeamsFunctions/tree/master/docs/Get-TeamsVoiceRoutingChain.md
  .LINK
    https://github.com/DEberhardt/TeamsFunctions/tree/master/docs/Find-TeamsUserVoiceRoute.md
  .LINK
    https://github.com/DEberhardt/TeamsFunctions/tree/master/docs/Find-TeamsEmergencyCallRoute.md
  .LINK
    https://github.com/DEberhardt/TeamsFunctions/tree/master/docs/about_VoiceConfiguration.md
  .LINK
    https://github.com/DEberhardt/TeamsFunctions/tree/master/docs/
  #>


  [CmdletBinding(DefaultParameterSetName = 'Name', ConfirmImpact = 'Low')]
  [Alias('Get-TeamsVRC')]
  [OutputType([PSCustomObject])]
  param(
    [Parameter(Mandatory, Position = 0, ParameterSetName = 'Object', ValueFromPipeline)]
    [Object[]]$Object,

    [Parameter(Mandatory, Position = 0, ParameterSetName = 'Name', ValueFromPipeline, HelpMessage = 'Name of the Online Voice Routing Policy')]
    [string[]]$Identity,

    [switch]$Detailed
  )
  begin {
    Show-FunctionStatus -Level RC
    Write-Verbose -Message "[BEGIN ] $($MyInvocation.MyCommand.Name)"
    Write-Verbose -Message "Need help? Online: $global:TeamsFunctionsHelpURLBase$($MyInvocation.MyCommand.Name)`.md"
    # 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' }

    #region Defining Output Object
    #TODO This may need a redesign - Analog to Get-TeamsAA, merging the Nested Objects into a better view
    class TFVoiceRoutingChain {
      [string]$OnlineVoiceRoutingPolicy
      $OnlinePstnUsageList
      [System.Collections.ArrayList]$OnlineVoiceRouteList
      [System.Collections.ArrayList]$OnlinePstnGatewayList
      [String]$NumberPattern
      hidden [System.Collections.ArrayList]$VoiceRoutingChain

      TFVoiceRoutingChain(
        [string]$OnlineVoiceRoutingPolicy,
        $OnlinePstnUsageList,
        [System.Collections.ArrayList]$OnlineVoiceRouteList,
        [System.Collections.ArrayList]$OnlinePstnGatewayList,
        [System.Collections.ArrayList]$NumberPattern
      ) {
        $this.OnlineVoiceRoutingPolicy = $OnlineVoiceRoutingPolicy
        $this.OnlinePstnUsageList = $OnlinePstnUsageList
        $this.OnlineVoiceRouteList = $OnlineVoiceRouteList
        $this.OnlinePstnGatewayList = $OnlinePstnGatewayList
        $this.NumberPattern = $NumberPattern
      }
    }
    #endregion

    # Preparing Splatting Object
    $Parameters = $null
    $Parameters = @{
      'Detailed' = ($Detailed)
    }

    #Worker functions
    Function GetVoiceRoutingChain {
      [CmdletBinding()]
      param(
        [Parameter(Mandatory)]
        [object]$Object,
        [switch]$Detailed
      )
      # 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' }

      Write-Verbose -Message "[PROCESS] $($MyInvocation.MyCommand)"
      # Voice Routing
      $VoiceRoutingChain = [TFVoiceRoutingChain]::new($Object.Identity, $null, $null, $null, $null)
      if ($PSBoundParameters.ContainsKey('Debug') -or $DebugPreference -eq 'Continue') {
        "Function: $($MyInvocation.MyCommand.Name) - CsOnlineVoiceRoutingPolicy", ( $Object | Format-List | Out-String).Trim() | Write-Debug
      }

      Write-Verbose -Message "Online Voice Routing Policy '$($Object.Identity)'"
      $OPUs = $null
      $OPUs = $Object.OnlinePstnUsages
      if ($PSBoundParameters.ContainsKey('Debug') -or $DebugPreference -eq 'Continue') {
        "Function: $($MyInvocation.MyCommand.Name) - PSTN Usages: $($OPUs -join ', ')" | Write-Debug
      }

      if ($OPUs) {
        Write-Verbose -Message 'Online Pstn Usage'
        [System.Collections.ArrayList]$OPUObjects = @()
        foreach ($OPU in $OPUs) {
          Write-Verbose -Message "Online Pstn Usage '$OPU'"
          $Routes += Get-CsOnlineVoiceRoute | Where-Object { $_.OnlinePstnUsages -contains $OPU } | Select-Object *, @{label = 'PSTNUsage'; Expression = { $OPU } }
          [System.Collections.ArrayList]$VoiceRoutes = @()
          [System.Collections.ArrayList]$OVRObjects = @()
          foreach ($Route in $Routes) {
            Write-Verbose -Message "Online Pstn Usage '$OPU' - Online Voice Route '$($Route.Name)'"
            if ( $VoiceRoutes -notcontains $Route ) {
              [void]$VoiceRoutes.Add($Route)
            }

            if ( $Detailed ) {
              [System.Collections.ArrayList]$MGWObjects = @()
              foreach ($Gateway in $Route.OnlinePstnGatewayList) {
                Write-Verbose -Message "Online Pstn Usage '$OPU' - Online Voice Route '$($Route.Name)' - Gateway '$($Gateway.Identity)'"
                $CsOnlinePSTNGateway = Get-CsOnlinePSTNGateway $Gateway
                $MGWObject = Merge-VoiceRoutingChainArtefact -Type MGW -Object $CsOnlinePSTNGateway
                if ( $MGWObjects.Identity -notcontains $MGWObject.Identity ) {
                  [void]$MGWObjects.Add($MGWObject)
                }
              }
              $OVRObject = Merge-VoiceRoutingChainArtefact -Type OVR -Object $Route -MGWs $MGWObjects
              if ( $OVRObjects.Name -notcontains $OVRObject.Name ) {
                [void]$OVRObjects.Add($OVRObject)
              }
            }
          }
          if ($PSBoundParameters.ContainsKey('Debug') -or $DebugPreference -eq 'Continue') {
            "Function: $($MyInvocation.MyCommand.Name) - VoiceRoutes", ( $VoiceRoutes | Format-List | Out-String).Trim() | Write-Debug
          }

          $VoiceRoutingChain.OnlinePstnUsageList = $Object.OnlinePstnUsages
          if ( $VoiceRoutes ) {
            $VoiceRoutingChain.OnlineVoiceRouteList = $VoiceRoutes.Identity
            $VoiceRoutingChain.OnlinePstnGatewayList = $VoiceRoutes.OnlinePstnGatewayList
            $VoiceRoutingChain.NumberPattern = $VoiceRoutes.NumberPattern
          }
          else {
            Write-Warning -Message "OVP '$($Object.Identity)' - No Online Voice Routes have been found"
          }

          if ( $Detailed ) {
            Write-Verbose -Message 'Creating Voice Routing Chain Object'
            $OPUObject = Merge-VoiceRoutingChainArtefact -Type OPU -Object $OPU -OVRs $OVRObjects
            if ( $OPUObjects -notcontains $OPUObject ) {
              [void]$OPUObjects.Add($OPUObject)
            }
            #Adding Chained Object to Parent
            $VoiceRoutingChain.VoiceRoutingChain = $OPUObjects
          }
        }
      }
      else {
        Write-Warning -Message "OVP '$($Object.Identity)' - No Online PSTN Usages have been found"
      }

      if ( $Detailed ) {
        Write-Output $VoiceRoutingChain | Select-Object OnlineVoiceRoutingPolicy, OnlinePstnUsageList, OnlineVoiceRouteList, OnlinePstnGatewayList, NumberPattern, VoiceRoutingChain
      }
      else {
        Write-Output $VoiceRoutingChain
      }
    }
  }

  Process {
    Write-Verbose -Message "[PROCESS] $($MyInvocation.MyCommand)"
    switch ($PSCmdlet.ParameterSetName) {
      'Name' {
        foreach ($Id in $Identity) {
          Write-Verbose -Message "[PROCESS] Processing '$Id'"
          try {
            if ($Id -match [regex]::Escape('*')) {
              $CsOnlineVoiceRoutingPolicy = Get-CsOnlineVoiceRoutingPolicy -Filter "*$Id*"-WarningAction SilentlyContinue -ErrorAction Stop
            }
            else {
              $CsOnlineVoiceRoutingPolicy = Get-CsOnlineVoiceRoutingPolicy -Identity "$Id"-WarningAction SilentlyContinue -ErrorAction Stop
            }
          }
          catch {
            Write-Error "CsOnlineVoiceRoutingPolicy '$Id' not found" -Category ObjectNotFound
            continue
          }
          foreach ($O in $CsOnlineVoiceRoutingPolicy) {
            Write-Verbose -Message "[PROCESS] Processing Found '$($O.Identity)'"
            GetVoiceRoutingChain -Object $O @Parameters @Args
          }
        }
      }
      'Object' {
        foreach ($O in $Object) {
          Write-Verbose -Message "[PROCESS] Processing provided CsOnlineVoiceRoutingPolicy Object for '$($O.Identity)'"
          GetVoiceRoutingChain -Object $O @Parameters @Args
        }
      }
    }
  }

  end {
    Write-Verbose -Message "[END ] $($MyInvocation.MyCommand)"
  }
} # Get-TeamsVoiceRoutingChain