Public/Functions/AutoAttendant/Get-TeamsAutoAttendant.ps1
# Module: TeamsFunctions # Function: AutoAttendant # Author: David Eberhardt # Updated: 01-OCT-2020 # Status: BETA function Get-TeamsAutoAttendant { <# .SYNOPSIS Queries Auto Attendants and displays friendly Names (UPN or DisplayName) .DESCRIPTION Same functionality as Get-CsAutoAttendant, but display reveals friendly Names, like UserPrincipalName or DisplayName for the following connected Objects Operator and ApplicationInstances (Resource Accounts) .PARAMETER Name Optional. Searches all Auto Attendants for this name (multiple results possible). If omitted, Get-TeamsAutoAttendant acts like an Alias to Get-CsAutoAttendant (no friendly names) .PARAMETER Detailed Optional Switch. Displays nested Objects for all Parameters of the Auto Attendant By default, only Names of nested Objects are shown. .EXAMPLE Get-TeamsAutoAttendant Same result as Get-CsAutoAttendant .EXAMPLE Get-TeamsAutoAttendant -Name "My AutoAttendant" Returns an Object for every Auto Attendant found with the String "My AutoAttendant" Operator and Resource Accounts are displayed with friendly name. .INPUTS System.String .OUTPUTS System.Object .NOTES Main difference to Get-CsAutoAttendant (apart from the friendly names) is how the Objects are shown. The connected Objects DefaultCallFlow, CallFlows, Schedules, CallHandlingAssociations and DirectoryLookups are all shown with Name only, but can be queried with .<ObjectName> This also works with Get-CsAutoAttendant, but with the help of "Display" Parameters. .FUNCTIONALITY Get-CsAutoAttendant 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-TeamsAA')] [OutputType([System.Object[]])] param( [Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Partial or full Name of the Auto Attendant to search')] [AllowNull()] [string]$Name, [switch]$Detailed ) #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-CsAutoAttendant" -Verbose Write-Verbose -Message "Warnings are suppressed for this operation. Please query with -Name to display them" -Verbose Get-CsAutoAttendant -WarningAction SilentlyContinue -ErrorAction STOP } else { foreach ($DN in $Name) { Write-Verbose -Message "[PROCESS] $($MyInvocation.MyCommand) - '$DN'" # Finding all AAs with this Name (Should return one Object, but since it IS a filter, handling it as an array) #$AAs = Get-CsAutoAttendant -NameFilter "$DN" -WarningAction SilentlyContinue -ErrorAction STOP $AAs = Get-CsAutoAttendant -NameFilter "$DN" -WarningAction SilentlyContinue -ErrorAction STOP | Select-Object * # Initialising Arrays [System.Collections.ArrayList]$AIObjects = @() # Reworking Objects Write-Verbose -Message "[PROCESS] Finding parsable Objects for $($AAs.Count) Auto Attendants" foreach ($AA in $AAs) { #region Finding Operator Write-Verbose -Message "'$($AA.Name)' - Parsing Operator" if ($null -eq $AA.Operator) { $OperatorObject = $null } else { # Parsing Callable Entity #TODO Resolve-TeamsCallableEntity can be used to do this, if it can search by type (needs to be extended first though) switch ($AA.Operator.Type) { "User" { try { $OperatorObject = Get-AzureADUser -ObjectId "$($AA.Operator.Id)" -WarningAction SilentlyContinue -ErrorAction STOP $Operator = $OperatorObject.UserPrincipalName } catch { Write-Warning -Message "'$($AA.Name)' Operator: Not enumerated" } } "OrganizationalAutoAttendant" { try { $OperatorObject = Get-CsOrganizationalAutoAttendant -Identity "$($AA.Operator.Id)" -WarningAction SilentlyContinue -ErrorAction STOP $Operator = $OperatorObject.Name } catch { Write-Warning -Message "'$($AA.Name)' Operator: Not enumerated" } } "HuntGroup" { try { $OperatorObject = Get-CsHuntGroup -Identity "$($AA.Operator.Id)" -WarningAction SilentlyContinue -ErrorAction STOP $Operator = $OperatorObject.Name } catch { Write-Warning -Message "'$($AA.Name)' Operator: Not enumerated" } } "ApplicationEndpoint" { try { $OperatorObject = Get-CsOnlineApplicationInstance -ObjectId "$($AA.Operator.Id)" -WarningAction SilentlyContinue -ErrorAction STOP $Operator = $OperatorObject.UserPrincipalName } catch { Write-Warning -Message "'$($AA.Name)' Operator: Not enumerated" } } "ExternalPstn" { try { $Operator = $AA.Id } catch { Write-Warning -Message "'$($AA.Name)' Operator: Not enumerated" } } "SharedVoicemail" { try { $OperatorObject = Get-AzureADGroup -ObjectId "$($AA.Operator.Id)" -WarningAction SilentlyContinue -ErrorAction STOP $Operator = $OperatorObject.DisplayName } catch { Write-Warning -Message "'$($AA.Name)' Operator: Not enumerated" } } default { try { $OperatorObject = Get-AzureADUser -ObjectId "$($AA.Operator.Id)" -WarningAction SilentlyContinue -ErrorAction STOP $Operator = $OperatorObject.UserPrincipalName if ($null -eq $Operator) { try { $OperatorObject = Get-AzureADGroup -ObjectId "$($AA.Operator.Id)" -WarningAction SilentlyContinue -ErrorAction STOP $Operator = $OperatorObject.DisplayName if ($null -eq $Operator) { throw } } catch { Write-Warning -Message "'$($AA.Name)' Operator: Not enumerated" } } } catch { Write-Warning -Message "'$($AA.Name)' Operator: Not enumerated" } } } } # Output: $Operator, $OperatorTranscription #endregion #region Application Instance UPNs Write-Verbose -Message "'$($AA.Name)' - Parsing Resource Accounts" foreach ($AI in $AA.ApplicationInstances) { $AIObject = $null $AIObject = Get-CsOnlineApplicationInstance -WarningAction SilentlyContinue | 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 # Building custom Object with Friendly Names Write-Verbose -Message "'$($AA.Name)' - Constructing Output Object" $AAObject = [PsCustomObject][ordered]@{ Identity = $AA.Identity Name = $AA.Name LanguageId = $AA.LanguageId TimeZoneId = $AA.TimeZoneId VoiceId = $AA.VoiceId VoiceResponseEnabled = $AA.VoiceResponseEnabled OperatorName = $Operator OperatorType = $AA.Operator.Type DefaultCallFlowName = $AA.DefaultCallFlow.Name CallFlowNames = $AA.CallFlows.Name ScheduleNames = $AA.Schedules.Name CallHandlingAssociationNames = $AA.CallHandlingAssociations.Type DirectoryLookupScope = $AA.DirectoryLookupScope.Name GreetingsSettingAuthorizedUsers = $AA.GreetingsSettingAuthorizedUsers } #endregion #region Extending Output Object with Switch Detailed if ($PSBoundParameters.ContainsKey('Detailed')) { Write-Verbose -Message "'$($AA.Name)' - Constructing Output Object with Switch 'Detailed' - This may take a bit..." -Verbose #region Operator Write-Verbose -Message "Parsing Operator" if ($AA.Operator) { $AAOperator = @() $AAOperator = [PsCustomObject][ordered]@{ 'Entity' = $Operator 'Type' = $AA.Operator.Type 'EnableTranscription' = $AA.Operator.EnableTranscription 'Id' = $AA.Operator.Id } Add-Member -Force -InputObject $AAOperator -MemberType ScriptMethod -Name ToString -Value { [System.Environment]::NewLine + (($this | Format-List * | Out-String) -replace '^\s+|\s+$') } } else { $OperatorObject = $null } #endregion #region DefaultCallFlow Write-Verbose -Message "Parsing DefaultCallFlow" # Default Call Flow Menu Prompts if ($AA.DefaultCallFlow.Menu.Prompts) { $AADefaultCallFlowMenuPrompts = Merge-AutoAttendantArtefact -Type Prompt -Object $AA.DefaultCallFlow.Menu.Prompts } else { $AADefaultCallFlowMenuPrompts = $null } # Default Call Flow Menu Options $AADefaultCallFlowMenuOptions = Merge-AutoAttendantArtefact -Type MenuOption -Object $AA.DefaultCallFlow.Menu.MenuOptions # Default Call Flow Menu $AADefaultCallFlowMenu = Merge-AutoAttendantArtefact -Type Menu -Object $AA.DefaultCallFlow.Menu -Prompts $AADefaultCallFlowMenuPrompts -MenuOptions $AADefaultCallFlowMenuOptions # Default Call Flow Greetings if ($AA.DefaultCallFlow.Greetings) { $AADefaultCallFlowGreetings = Merge-AutoAttendantArtefact -Type Prompt -Object $AA.DefaultCallFlow.Greetings } else { $AADefaultCallFlowGreetings = $null } # Default Call Flow $AADefaultCallFlow = Merge-AutoAttendantArtefact -Type CallFlow -Object $AA.DefaultCallFlow -Prompts $AADefaultCallFlowGreetings -Menu $AADefaultCallFlowMenu #endregion #region CallFlows Write-Verbose -Message "Parsing CallFlows" $AACallFlows = @() foreach ($Flow in $AA.CallFlows) { # Call Flow Prompts if ($Flow.Menu.Prompts) { $AACallFlowMenuPrompts = Merge-AutoAttendantArtefact -Type Prompt -Object $Flow.Menu.Prompts } else { $AACallFlowMenuPrompts = $null } # Call Flow Menu Options $AACallFlowMenuOptions = Merge-AutoAttendantArtefact -Type MenuOption -Object $Flow.Menu.MenuOptions # Call Flow Menu $AACallFlowMenu = Merge-AutoAttendantArtefact -Type Menu -Object $Flow.Menu -Prompts $AACallFlowMenuPrompts -MenuOptions $AACallFlowMenuOptions # Call Flow Greetings if ($Flow.Greetings) { $AACallFlowGreetings = Merge-AutoAttendantArtefact -Type Prompt -Object $Flow.Greetings } else { $AACallFlowGreetings = $null } # Call Flow $AACallFlows += Merge-AutoAttendantArtefact -Type CallFlow -Object $Flow -Prompts $AACallFlowGreetings -Menu $AACallFlowMenu } #endregion #region Schedules Write-Verbose -Message "Parsing Schedules" $AASchedules = @() foreach ($Schedule in $AA.Schedules) { $AASchedule = Get-CsOnlineSchedule -Id $Schedule.Id $AASchedules += Merge-AutoAttendantArtefact -Type Schedule -Object $AASchedule } #endregion #region CallHandlingAssociations Write-Verbose -Message "Parsing CallHandlingAssociations" $AACallHandlingAssociations = @() foreach ($item in $AA.CallHandlingAssociations) { # Determine Call Flow Name $AACallHandlingAssociationCallFlowName = ($AA.CallFlows | Where-Object Id -EQ $item.CallFlowId).Name # CallHandlingAssociations $AACallHandlingAssociations += Merge-AutoAttendantArtefact -Type CallHandlingAssociation -Object $item -CallFlowName $AACallHandlingAssociationCallFlowName } #endregion # Adding nested Objects $AAObject | Add-Member -MemberType NoteProperty -Name Operator -Value $AAOperator $AAObject | Add-Member -MemberType NoteProperty -Name DefaultCallFlow -Value $AADefaultCallFlow $AAObject | Add-Member -MemberType NoteProperty -Name CallFlows -Value $AACallFlows $AAObject | Add-Member -MemberType NoteProperty -Name Schedules -Value $AASchedules $AAObject | Add-Member -MemberType NoteProperty -Name CallHandlingAssociations -Value $AACallHandlingAssociations } # Adding Resource Accounts $AAObject | Add-Member -MemberType NoteProperty -Name ApplicationInstances -Value $AIObjects.UserPrincipalName #endregion # Output Write-Output $AAObject } } } } catch { Write-Error -Message 'Could not query Auto Attendants' -Category OperationStopped Write-ErrorRecord $_ #This handles the error message in human readable format. return } } #process end { Write-Verbose -Message "[END ] $($MyInvocation.MyCommand)" } #end } #Get-TeamsAutoAttendant |