Public/Functions/UserManagement/TeamsCallableEntity/Find-TeamsCallableEntity.ps1

# Module: TeamsFunctions
# Function: CallQueue
# Author: David Eberhardt
# Updated: 01-DEC-2020
# Status: Live





function Find-TeamsCallableEntity {
  <#
  .SYNOPSIS
    Finds all Call Queues where a specific User is an Agent
  .DESCRIPTION
    Finding all Call Queues where a User is linked as an Agent, as an OverflowActionTarget or as a TimeoutActionTarget
  .PARAMETER Identity
    Required. Callable Entity Object to be found (Tel URI, User, Group, Resource Account)
  .PARAMETER Scope
    Optional. Limits searches to Call Queues, Auto Attendants or both (All) - Currently Hardcoded to CallQueue until development finishes
  .EXAMPLE
    Find-TeamsCallableEntity "John@domain.com" [-Scope All]
    Finds all Call Queues or Auto Attendants in which John is an Agent, OverflowTarget or TimeoutTarget, Menu Option, Operator, etc.
  .EXAMPLE
    Find-TeamsCallableEntity "MyGroup@domain.com" -Scope CallQueue
    Finds all Call Queues in which My Group is linked as an Agent Group, OverflowTarget or TimeoutTarget
  .EXAMPLE
    Find-TeamsCallableEntity "tel:+15551234567" -Scope AutoAttendant
    Finds all Auto Attendants in which the Tel URI is linked as an Operator, Menu Option, etc.
  .INPUTS
    System.String
  .OUTPUTS
    System.Object
  .NOTES
    Finding linked agents is useful if the Call Queues are in an unusable state.
    This happens if a User is unlicensed, disabled for Enterprise Voice or disabled completely
    while still being targeted as an Agent or for Overflow or Timeout.
  .COMPONENT
    UserManagement
    TeamsAutoAttendant
    TeamsCallQueue
  .FUNCTIONALITY
    Finding Call Queues and/or Auto Attendants where specific Callable Entity is attached
  .LINK
    https://github.com/DEberhardt/TeamsFunctions/tree/master/docs/Find-TeamsCallableEntity.md
  .LINK
    https://github.com/DEberhardt/TeamsFunctions/tree/master/docs/about_TeamsAutoAttendant.md
  .LINK
    https://github.com/DEberhardt/TeamsFunctions/tree/master/docs/about_TeamsCallQueue.md
  .LINK
    https://github.com/DEberhardt/TeamsFunctions/tree/master/docs/about_UserManagement.md
  .LINK
    https://github.com/DEberhardt/TeamsFunctions/tree/master/docs/
  #>


  [CmdletBinding()]
  [OutputType([System.Object[]])]
  param(
    [Parameter(Mandatory, Position = 0, ValueFromPipeline, ValueFromPipelineByPropertyName, HelpMessage = 'User Identifier')]
    [Alias('ObjectId', 'UserPrincipalName')]
    [string[]]$Identity,

    [Parameter(HelpMessage = 'Scope')]
    [ValidateSet('All', 'CallQueue', 'AutoAttendant')]
    [string]$Scope = 'All'

  ) #param

  begin {
    Show-FunctionStatus -Level Live
    Write-Verbose -Message "[BEGIN ] $($MyInvocation.MyCommand)"
    Write-Verbose -Message "Need help? Online: $global:TeamsFunctionsHelpURLBase$($MyInvocation.MyCommand)`.md"

    # Asserting AzureAD Connection
    if (-not (Assert-AzureADConnection)) { break }

    # 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' }

    #Scope Hardcoded (will become a parameter later)
    #$Scope = "CallQueue"

    # Initialising counters for Progress bars
    [int]$step0 = 0
    [int]$sMax0 = 3

    #region Information Gathering 1
    $Status = 'Information Gathering'
    # Call Queues and Auto Attendants
    if ( $Scope -in ('All', 'CallQueue') ) {
      # Query Queues
      $Operation = 'Querying Call Queues'
      Write-Progress -Id 0 -Status $Status -CurrentOperation $Operation -Activity $MyInvocation.MyCommand -PercentComplete ($step0 / $sMax0 * 100)
      $CQs = Get-CsCallQueue -WarningAction SilentlyContinue -ErrorAction SilentlyContinue
      Write-Verbose -Message "$Status - $Operation -Objects found: $($CQs.Count)"
      $step0++
    }

    if ( $Scope -in ('All', 'AutoAttendant') ) {
      # Query Queues
      $Operation = 'Querying Auto Attendants'
      Write-Progress -Id 0 -Status $Status -CurrentOperation $Operation -Activity $MyInvocation.MyCommand -PercentComplete ($step0 / $sMax0 * 100)
      $AAs = Get-CsAutoAttendant -WarningAction SilentlyContinue -ErrorAction SilentlyContinue
      Write-Verbose -Message "$Status - $Operation - Objects found: $($AAs.Count)"
      $step0++
    }
    #endregion

  } #begin

  process {
    Write-Verbose -Message "[PROCESS] $($MyInvocation.MyCommand)"

    $IdCounter = 0
    foreach ( $Id in $Identity) {
      $CallTarget = $null
      [System.Collections.ArrayList]$Output = @()

      $Operation = "Processing '$Id'"
      Write-Progress -Id 0 -Status $Status -CurrentOperation $Operation -Activity $MyInvocation.MyCommand -PercentComplete ($IdCounter / $($Identity.Count) * 100)
      Write-Verbose -Message "$Status - $Operation"
      $IdCounter++

      # Initialising counters for Progress bars
      [int]$step = 0
      [int]$sMax = 1
      switch ($Scope) {
        'CallQueue' { $sMax = $sMax + 4 }
        'AutoAttendant' { $sMax = $sMax + 3 }
        'All' { $sMax = $sMax + 7 }
      }

      # Object
      $Operation = 'Teams Callable Entity'
      Write-Progress -Id 1 -ParentId 0 -Status $Status -CurrentOperation $Operation -Activity $MyInvocation.MyCommand -PercentComplete ($step / $sMax * 100)
      Write-Verbose -Message "$Status - $Operation"
      try {
        $CallTarget = Get-TeamsCallableEntity -Identity "$Id"
        if ( -not $CallTarget ) {
          Write-Error -Message "Callable Entity '$Id' not found - Please validate input"
          continue
        }
        else {
          Write-Debug -Message "Callable Entity '$Id' found:"
          Write-Debug "$CallTarget"
        }
      }
      catch {
        Write-Error -Message "Callable Entity '$Id' found, but no unique result determined. Cannot continue."
        continue
      }

      #region Search Results
      $Status = "$($CallTarget.Type) '$($CallTarget.Entity)'"
      #region Call Queues
      if ( $Scope -in ('All', 'CallQueue') ) {
        # 1 Searching for Agent or User
        $Operation = 'Call Queues: Agent or User'
        $step++
        Write-Progress -Id 1 -ParentId 0 -Status $Status -CurrentOperation $Operation -Activity $MyInvocation.MyCommand -PercentComplete ($step / $sMax * 100)
        Write-Verbose -Message "$Status - $Operation"

        foreach ($CQ in $CQs) {
          if ( $CallTarget.Identity -in $CQ.Agents.ObjectId ) {
            if ( $CallTarget.Identity -in $CQ.Users ) {
              [void]$Output.Add([TFCallableEntityConnection]::new( "$($CallTarget.Entity)", 'User', 'CallQueue', "$($CQ.Name)", "$($CQ.Identity)"))
            }
            else {
              [void]$Output.Add([TFCallableEntityConnection]::new( "$($CallTarget.Entity)", 'Agent', 'CallQueue', "$($CQ.Name)", "$($CQ.Identity)"))
            }
          }
        }

        # 2 Searching for Group
        $Operation = 'Call Queues: Group'
        $step++
        Write-Progress -Id 1 -ParentId 0 -Status $Status -CurrentOperation $Operation -Activity $MyInvocation.MyCommand -PercentComplete ($step / $sMax * 100)
        Write-Verbose -Message "$Status - $Operation"

        foreach ($CQ in $CQs) {
          if ( $CallTarget.Identity -in $CQ.DistributionLists ) {
            [void]$Output.Add([TFCallableEntityConnection]::new( "$($CallTarget.Entity)", 'Group', 'CallQueue', "$($CQ.Name)", "$($CQ.Identity)"))
          }
        }

        # 3 Searching for Overflow Target
        $Operation = 'Call Queues: Overflow Action Target'
        $step++
        Write-Progress -Id 1 -ParentId 0 -Status $Status -CurrentOperation $Operation -Activity $MyInvocation.MyCommand -PercentComplete ($step / $sMax * 100)
        Write-Verbose -Message "$Status - $Operation"

        foreach ($CQ in $CQs) {
          if ( $CallTarget.Identity -in $CQ.OverflowActionTarget ) {
            [void]$Output.Add([TFCallableEntityConnection]::new( "$($CallTarget.Entity)", 'OverflowActionTarget', 'CallQueue', "$($CQ.Name)", "$($CQ.Identity)"))
          }
        }

        # 4 Searching for Timeout Target
        $Operation = 'Call Queues: Timout Action Target'
        $step++
        Write-Progress -Id 1 -ParentId 0 -Status $Status -CurrentOperation $Operation -Activity $MyInvocation.MyCommand -PercentComplete ($step / $sMax * 100)
        Write-Verbose -Message "$Status - $Operation"

        foreach ($CQ in $CQs) {
          if ( $CallTarget.Identity -in $CQ.TimeoutActionTarget ) {
            [void]$Output.Add([TFCallableEntityConnection]::new( "$($CallTarget.Entity)", 'TimeoutActionTarget', 'CallQueue', "$($CQ.Name)", "$($CQ.Identity)"))
          }
        }

      }
      #endregion

      #region Auto Attendants
      if ( $Scope -in ('All', 'AutoAttendant') ) {
        # 1 Searching for Operator
        $Operation = 'Auto Attendants: Operator'
        $step++
        Write-Progress -Id 1 -ParentId 0 -Status $Status -CurrentOperation $Operation -Activity $MyInvocation.MyCommand -PercentComplete ($step / $sMax * 100)
        Write-Verbose -Message "$Status - $Operation"

        foreach ($AA in $AAs) {
          if ( $CallTarget.Identity -in $AA.Operator.Id ) {
            [void]$Output.Add([TFCallableEntityConnection]::new( "$($CallTarget.Entity)", 'Operator', 'AutoAttendant', "$($AA.Name)", "$($AA.Identity)"))
          }
        }

        # 2 Searching for Routing Target
        $Operation = 'Auto Attendants: MenuOption - Default Call Flow'
        $step++
        Write-Progress -Id 1 -ParentId 0 -Status $Status -CurrentOperation $Operation -Activity $MyInvocation.MyCommand -PercentComplete ($step / $sMax * 100)
        Write-Verbose -Message "$Status - $Operation"

        foreach ($AA in $AAs) {
          foreach ($Target in $AA.DefaultCallFlow.Menu.MenuOptions.CallTarget) {
            if ( $CallTarget.Identity -in $Target.Id ) {
              [void]$Output.Add([TFCallableEntityConnection]::new( "$($CallTarget.Entity)", 'DefaultCallFlow', 'AutoAttendant', "$($AA.Name)", "$($AA.Identity)"))
            }
          }
        }

        # 3 Searching for Routing Target
        $Operation = 'Auto Attendants: MenuOption - Call Flows'
        $step++
        Write-Progress -Id 1 -ParentId 0 -Status $Status -CurrentOperation $Operation -Activity $MyInvocation.MyCommand -PercentComplete ($step / $sMax * 100)
        Write-Verbose -Message "$Status - $Operation"

        foreach ($AA in $AAs) {
          foreach ($Target in $AA.CallFlows.Menu.MenuOptions.CallTarget) {
            if ( $CallTarget.Identity -in $Target.Id ) {
              [void]$Output.Add([TFCallableEntityConnection]::new( "$($CallTarget.Entity)", 'CallFlows', 'AutoAttendant', "$($AA.Name)", "$($AA.Identity)"))
            }
          }
        }

      }
      #endregion
      Write-Progress -Id 1 -Completed -Activity $MyInvocation.MyCommand
      #endregion

      # Output
      Write-Progress -Id 0 -Completed -Activity $MyInvocation.MyCommand
      if ( $Output ) {
        Write-Output $Output #| Select-Object LinkedAs, ObjectType, ObjectName
      }
      else {
        Write-Verbose -Message "No Call Queues or Auto Attendants found where Identity '$Id' is linked" -Verbose
      }
    }

  } #process

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

  } #end
} #Find-TeamsCallableEntity