Public/Functions/CallQueue/Get-TeamsCallQueue.ps1

# Module: TeamsFunctions
# Function: CallQueue
# Author: David Eberhardt
# Updated: 01-OCT-2020
# Status: PreLive

#FIXME TimeoutActionTarget not enumerated (User) - Also check OverflowActionTarget for the same (breakout check?)


function Get-TeamsCallQueue {
  <#
    .SYNOPSIS
        Queries Call Queues and displays friendly Names (UPN or Displayname)
    .DESCRIPTION
        Same functionality as Get-CsCallQueue, but display reveals friendly Names,
        like UserPrincipalName or DisplayName for the following connected Objects
    OverflowActionTarget, TimeoutActionTarget, Agents, DistributionLists and ApplicationInstances (Resource Accounts)
    .PARAMETER Name
        Optional. Searches all Call Queues for this name (multiple results possible).
    If omitted, Get-TeamsCallQueue acts like an Alias to Get-CsCallQueue (no friendly names)
  .PARAMETER ConciseView
    Optional Switch. Displays reduced set of Parameters for better visibility
    Parameters relating to Language & Shared Voicemail are not shown.
    .EXAMPLE
        Get-TeamsCallQueue
        Same result as Get-CsCallQueue
    .EXAMPLE
        Get-TeamsCallQueue -Name "My CallQueue"
        Returns an Object for every Call Queue found with the String "My CallQueue"
        Agents, DistributionLists, Targets and Resource Accounts are displayed with friendly name.
  .INPUTS
    System.String
  .OUTPUTS
    System.Object
  .NOTES
    Main difference to Get-CsCallQueue (apart from the friendly names) is that the
    Output view is by default detailed
    .FUNCTIONALITY
        Get-CsCallQueue with friendly names instead of GUID-strings for connected objects
    .LINK
        New-TeamsCallQueue
        Get-TeamsCallQueue
    Set-TeamsCallQueue
    Remove-TeamsCallQueue
    New-TeamsAutoAttendant
    Get-TeamsAutoAttendant
    Set-TeamsAutoAttendant
    Remove-TeamsAutoAttendant
    Get-TeamsResourceAccountAssociation
    New-TeamsResourceAccountAssociation
        Remove-TeamsResourceAccountAssociation
  #>


  [CmdletBinding()]
  [Alias('Get-TeamsCQ')]
  [OutputType([System.Object[]])]
  param(
    [Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Partial or full Name of the Call Queue to search')]
    [AllowNull()]
    [string]$Name,

    [switch]$ConciseView
  ) #param

  begin {
    Show-FunctionStatus -Level PreLive
    Write-Verbose -Message "[BEGIN ] $($MyInvocation.Mycommand)"

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

    # Asserting SkypeOnline Connection
    if (-not (Assert-SkypeOnlineConnection)) { 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')
    }

  } #begin

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

    # Capturing no input
    try {
      if (-not $PSBoundParameters.ContainsKey('Name')) {
        Write-Verbose -Message "No parameters specified. Acting as an Alias to Get-CsCallQueue" -Verbose
        Write-Verbose -Message "Warnings are suppressed for this operation. Please query with -Name to display them" -Verbose
        Get-CsCallQueue -WarningAction SilentlyContinue -ErrorAction STOP
      }
      else {
        foreach ($DN in $Name) {
          Write-Verbose -Message "[PROCESS] $($MyInvocation.Mycommand) - '$DN'"
          # Finding all Queues with this Name (Should return one Object, but since it IS a filter, handling it as an array)
          #$Queues = Get-CsCallQueue -NameFilter "$DN" -WarningAction SilentlyContinue -ErrorAction STOP
          # NOTE: Like AAs, piping to "FL *" can show more information. Here though, there is no benefit
          $Queues = Get-CsCallQueue -NameFilter "$DN" -WarningAction SilentlyContinue -ErrorAction STOP -WarningVariable $Warnings

          if ($null -ne $Queues) {
            if ($PSBoundParameters.ContainsKey('ConciseView')) {
              Write-Verbose -Message "ConciseView: Parameters relating to Language & Shared Voicemail are not shown." -Verbose
            }
          }

          # Initialising Arrays
          [System.Collections.ArrayList]$UserObjects = @()
          [System.Collections.ArrayList]$DLObjects = @()
          [System.Collections.ArrayList]$AgentObjects = @()
          [System.Collections.ArrayList]$AIObjects = @()

          # Reworking Objects
          Write-Verbose -Message "[PROCESS] Finding parsable Objects for $($Queues.Count) Queues"
          foreach ($Q in $Queues) {
            #region Finding OverflowActionTarget
            Write-Verbose -Message "'$($Q.Name)' - Parsing OverflowActionTarget"
            if ($null -eq $Q.OverflowActionTarget) {
              $OAT = $null
            }
            else {
              switch ($Q.OverflowActionTarget.Type) {
                "ApplicationEndpoint" {
                  try {
                    $OATobject = Get-CsOnlineApplicationInstance -ObjectId "$($Q.OverflowActionTarget.Id)" -WarningAction SilentlyContinue -ErrorAction STOP
                    $OAT = $OATobject.UserPrincipalName
                  }
                  catch {
                    Write-Warning -Message "'$($Q.Name)' OverflowActionTarget: Not enumerated"
                  }
                }
                "Mailbox" {
                  try {
                    $OATobject = Get-AzureADGroup -ObjectId "$($Q.OverflowActionTarget.Id)" -WarningAction SilentlyContinue -ErrorAction STOP
                    $OAT = $OATobject.DisplayName
                  }
                  catch {
                    Write-Warning -Message "'$($Q.Name)' OverflowActionTarget: Not enumerated"
                  }
                }
                "User" {
                  try {
                    $OATobject = Get-AzureADUser -ObjectId "$($Q.OverflowActionTarget.Id)" -WarningAction SilentlyContinue -ErrorAction STOP
                    $OAT = $OATobject.UserPrincipalName
                  }
                  catch {
                    Write-Warning -Message "'$($Q.Name)' OverflowActionTarget: Not enumerated"
                  }
                }
                "Phone" {
                  try {
                    $OATobject = Get-AzureADUser -ObjectId "$($Q.OverflowActionTarget.Id)" -WarningAction SilentlyContinue -ErrorAction STOP
                    $OAT = $OATobject.UserPrincipalName
                  }
                  catch {
                    Write-Warning -Message "'$($Q.Name)' OverflowActionTarget: Not enumerated"
                  }
                }
                default {
                  try {
                    $OATobject = Get-AzureADUser -ObjectId "$($Q.OverflowActionTarget.Id)" -WarningAction SilentlyContinue -ErrorAction STOP
                    $OAT = $OATobject.UserPrincipalName
                    if ($null -eq $OAT) {
                      try {
                        $OATobject = Get-AzureADGroup -ObjectId "$($Q.OverflowActionTarget.Id)" -WarningAction SilentlyContinue -ErrorAction STOP
                        $OAT = $OATobject.DisplayName
                        if ($null -eq $OAT) {
                          throw
                        }
                      }
                      catch {
                        Write-Warning -Message "'$($Q.Name)' OverflowActionTarget: Not enumerated"
                      }
                    }
                  }
                  catch {
                    Write-Warning -Message "'$($Q.Name)' OverflowActionTarget: Not enumerated"
                  }
                }
              }
            }
            # Output: $OAT, $Q.OverflowActionTarget.Type
            #endregion

            #region Finding TimeoutActionTarget
            Write-Verbose -Message "'$($Q.Name)' - Parsing OverflowActionTarget"
            if ($null -eq $Q.TimeoutActionTarget) {
              $TAT = $null
            }
            else {
              switch ($Q.TimeoutActionTarget.Type) {
                "ApplicationEndpoint" {
                  try {
                    $TATobject = Get-CsOnlineApplicationInstance -ObjectId "$($Q.TimeoutActionTarget.Id)" -WarningAction SilentlyContinue -ErrorAction STOP
                    $TAT = $TATObject.UserPrincipalName
                  }
                  catch {
                    Write-Warning -Message "'$($Q.Name)' TimeoutActionTarget: Not enumerated"
                  }
                }
                "Mailbox" {
                  try {
                    $TATobject = Get-AzureADGroup -ObjectId "$($Q.TimeoutActionTarget.Id)" -WarningAction SilentlyContinue -ErrorAction STOP
                    $TAT = $TATObject.DisplayName
                  }
                  catch {
                    Write-Warning -Message "'$($Q.Name)' TimeoutActionTarget: Not enumerated"
                  }
                }
                "User" {
                  try {
                    $TATobject = Get-AzureADUser -ObjectId "$($Q.TimeoutActionTarget.Id)" -WarningAction SilentlyContinue -ErrorAction STOP
                    $TAT = $TATObject.UserPrincipalName
                  }
                  catch {
                    Write-Warning -Message "'$($Q.Name)' TimeoutActionTarget: Not enumerated"
                  }
                }
                default {
                  try {
                    $TATobject = Get-AzureADUser -ObjectId "$($Q.TimeoutActionTarget.Id)" -WarningAction SilentlyContinue -ErrorAction STOP
                    $TAT = $TATObject.UserPrincipalName
                    if ($null -eq $TAT) {
                      try {
                        $TATobject = Get-AzureADGroup -ObjectId "$($Q.TimeoutActionTarget.Id)" -WarningAction SilentlyContinue -ErrorAction STOP
                        $TAT = $TATObject.DisplayName
                        if ($null -eq $TAT) {
                          throw
                        }
                      }
                      catch {
                        Write-Warning -Message "'$($Q.Name)' TimeoutActionTarget: Not enumerated"
                      }
                    }
                  }
                  catch {
                    Write-Warning -Message "'$($Q.Name)' TimeoutActionTarget: Not enumerated"
                  }
                }
              }
            }
            # Output: $TAT, $Q.TimeoutActionTarget.Type
            #endregion

            #region Endpoints - DistributionLists and Agents
            Write-Verbose -Message "'$($Q.Name)' - Parsing DistributionLists"
            foreach ($DL in $Q.DistributionLists) {
              $DLObject = Get-AzureADGroup -ObjectId $DL -WarningAction SilentlyContinue | Select-Object DisplayName, Description, SecurityEnabled, MailEnabled, MailNickName, Mail
              [void]$DLObjects.Add($DLObject)
            }
            # Output: $DLObjects.DisplayName

            Write-Verbose -Message "'$($Q.Name)' - Parsing Users"
            foreach ($User in $Q.Users) {
              $UserObject = Get-AzureADUser -ObjectId "$($User.Guid)" -WarningAction SilentlyContinue | Select-Object UserPrincipalName, DisplayName, JobTitle, CompanyName, Country, UsageLocation, PreferredLanguage
              [void]$UserObjects.Add($UserObject)
            }
            # Output: $UserObjects.UserPrincipalName

            Write-Verbose -Message "'$($Q.Name)' - Parsing Agents"
            foreach ($Agent in $Q.Agents) {
              $AgentObject = Get-AzureADUser -ObjectId "$($Agent.ObjectId)" -WarningAction SilentlyContinue | Select-Object UserPrincipalName, DisplayName, JobTitle, CompanyName, Country, UsageLocation, PreferredLanguage
              [void]$AgentObjects.Add($AgentObject)
            }
            # Output: $AgentObjects.UserPrincipalName
            #endregion

            #region Application Instance UPNs
            Write-Verbose -Message "'$($Q.Name)' - Parsing Resource Accounts"
            foreach ($AI in $Q.ApplicationInstances) {
              $AIObject = $null
              $AIObject = Get-CsOnlineApplicationInstance | Where-Object { $_.ObjectId -eq $AI } | Select-Object UserPrincipalName, DisplayName, PhoneNumber
              if ($null -ne $AIObject) {
                [void]$AIObjects.Add($AIObject)
              }
            }

            # Output: $AIObjects.UserPrincipalName
            #endregion

            #region Creating Output Object
            Write-Verbose -Message "'$($Q.Name)' - Constructing Output Object"
            # Building custom Object with Friendly Names
            if ($PSBoundParameters.ContainsKey('ConciseView')) {
              $QueueObject = [PSCustomObject][ordered]@{
                Identity                  = $Q.Identity
                Name                      = $Q.Name
                UseDefaultMusicOnHold     = $Q.UseDefaultMusicOnHold
                MusicOnHoldAudioFileName  = $Q.MusicOnHoldFileName
                WelcomeMusicAudioFileName = $Q.WelcomeMusicFileName
                RoutingMethod             = $Q.RoutingMethod
                PresenceBasedRouting      = $Q.PresenceBasedRouting
                AgentAlertTime            = $Q.AgentAlertTime
                AllowOptOut               = $Q.AllowOptOut
                ConferenceMode            = $Q.ConferenceMode
                OverflowThreshold         = $Q.OverflowThreshold
                OverflowAction            = $Q.OverflowAction
                OverflowActionTarget      = $OAT
                OverflowActionTargetType  = $Q.OverflowActionTarget.Type

                TimeoutThreshold          = $Q.TimeoutThreshold
                TimeoutAction             = $Q.TimeoutAction
                TimeoutActionTarget       = $TAT
                TimeoutActionTargetType   = $Q.TimeoutActionTarget.Type

                Users                     = $UserObjects.UserPrincipalName
                DistributionLists         = $DLObjects.DisplayName

                Agents                    = $AgentObjects.UserPrincipalName
                ApplicationInstances      = $AIObjects.Userprincipalname
              }
            }
            else {
              # Displays all except reserved Parameters (Microsoft Internal)
              $QueueObject = [PSCustomObject][ordered]@{
                Identity                                       = $Q.Identity
                Name                                           = $Q.Name
                UseDefaultMusicOnHold                          = $Q.UseDefaultMusicOnHold
                MusicOnHoldAudioFileName                       = $Q.MusicOnHoldFileName
                WelcomeMusicAudioFileName                      = $Q.WelcomeMusicFileName
                RoutingMethod                                  = $Q.RoutingMethod
                PresenceBasedRouting                           = $Q.PresenceBasedRouting
                AgentAlertTime                                 = $Q.AgentAlertTime
                AllowOptOut                                    = $Q.AllowOptOut
                ConferenceMode                                 = $Q.ConferenceMode
                OverflowThreshold                              = $Q.OverflowThreshold
                OverflowAction                                 = $Q.OverflowAction
                OverflowActionTarget                           = $OAT
                OverflowActionTargetType                       = $Q.OverflowActionTarget.Type
                OverflowSharedVoicemailAudioFilePrompt         = $Q.OverflowSharedVoicemailAudioFilePrompt
                OverflowSharedVoicemailAudioFilePromptFileName = $Q.OverflowSharedVoicemailAudioFilePromptFileName
                OverflowSharedVoicemailTextToSpeechPrompt      = $Q.OverflowSharedVoicemailTextToSpeechPrompt
                EnableOverflowSharedVoicemailTranscription     = $Q.EnableOverflowSharedVoicemailTranscription
                TimeoutThreshold                               = $Q.TimeoutThreshold
                TimeoutAction                                  = $Q.TimeoutAction
                TimeoutActionTarget                            = $TAT
                TimeoutActionTargetType                        = $Q.TimeoutActionTarget.Type
                TimeoutSharedVoicemailAudioFilePrompt          = $Q.TimeoutSharedVoicemailAudioFilePrompt
                TimeoutSharedVoicemailAudioFilePromptFileName  = $Q.TimeoutSharedVoicemailAudioFilePromptFileName
                TimeoutSharedVoicemailTextToSpeechPrompt       = $Q.TimeoutSharedVoicemailTextToSpeechPrompt
                EnableTimeoutSharedVoicemailTranscription      = $Q.EnableTimeoutSharedVoicemailTranscription
                LanguageId                                     = $Q.LanguageId
                #LineUri = $Q.LineUri
                MusicOnHoldAudioFileId                         = $Q.MusicOnHoldAudioFileId
                WelcomeMusicAudioFileId                        = $Q.WelcomeMusicAudioFileId
                Users                                          = $UserObjects.UserPrincipalName
                DistributionLists                              = $DLObjects.DisplayName
                DistributionListsLastExpanded                  = $Q.DistributionListsLastExpanded
                AgentsInSyncWithDistributionLists              = $Q.AgentsInSyncWithDistributionLists
                AgentsCapped                                   = $Q.AgentsCapped
                Agents                                         = $AgentObjects.UserPrincipalName
                ApplicationInstances                           = $AIObjects.Userprincipalname
              }

            }
            #endregion

            # Output
            if ($Warnings) {
              Write-Warning -Message $Warnings
            }
            Write-Output $QueueObject
          }
        }
      }
    }
    catch {
      Write-Error -Message 'Could not query Call Queues' -Category OperationStopped
      Write-ErrorRecord $_ #This handles the error message in human readable format.
      return
    }
  } #process

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

  } #end
} #Get-TeamsCallQueue