Public/AutoAttendant/New-TeamsAutoAttendantMenu.ps1
# Module: TeamsFunctions # Function: AutoAttendant # Author: David Eberhardt # Updated: 12-DEC-2020 # Status: Live function New-TeamsAutoAttendantMenu { <# .SYNOPSIS Creates a Menu Object to be used in Auto Attendants .DESCRIPTION Creates a Menu Object with Prompt and/or MenuOptions to be used in Auto Attendants Wrapper for New-CsAutoAttendantMenu with friendly names Combines New-CsAutoAttendantMenu, New-CsAutoAttendantPrompt and New-CsAutoAttendantMenuOption .PARAMETER Name Optional. Name of the Menu if desired. Otherwise generated automatically. .PARAMETER Action Required. TransferToMenu, Disconnect, TransferToCallTarget. Determines the type of Menu to be created. .PARAMETER Prompts Required for Action "TransferToMenu" only. A Prompts Object, String or Full path to AudioFile. A Prompts Object will be used as is, otherwise it will be created dependent of the provided String A String will be used as Text-to-Voice. A File path ending in .wav, .mp3 or .wma will be used to create a recording. .PARAMETER MenuOptions Required for Action "TransferToMenu" only. Mutually exclusive with CallTargetsInOrder. MenuOptions objects created with either New-TeamsAutoAttendantMenuOption or New-CsAutoAttenantMenuOption. .PARAMETER CallTargetsInOrder Required for Action "TransferToMenu" only. Mutually exclusive with MenuOptions. Call Targets for Menu Options. Expected UserPrincipalName (User, ResourceAccount), Group Name (Shared Voicemail), Tel Uri (ExternalPstn) Allows to skip options with empty strings ("") or "$null". See Examples for details DTMF tones are assigned in order with 0 being TransferToOperator .PARAMETER CallTarget Required for Action "TransferToCallTarget" only. Single Call Target to redirect Calls to. UserPrincipalName (User, ResourceAccount), Group Name (Shared Voicemail), Tel Uri (ExternalPstn) .PARAMETER AddOperatorOnZero Optional for Action "TransferToMenu" when used with CallTargetsInOrder only. This switch is ignored if more than nine (9) CallTargetsInOrder are specified Adds one more menu option to Transfer the Call to the Operator on pressing 0. The AutoAttendant which will receive a Menu with this option, must have an Operator defined. Creating or Updating an Auto Attendant with an Operator that is not defined will lead to errors. .PARAMETER EnableDialByName Boolean. Required for Action "TransferToMenu" only. Enables Dial by Name for the Menu. .PARAMETER DirectorySearchMethod String. Required for Action "TransferToMenu" only. Sets the Directory Search Method: None, ByName, ByExtension .PARAMETER EnableTranscription Optional. Where possible, tries to enable Voicemail Transcription. Effective only for SharedVoicemail Targets as an Operator or MenuOption. Otherwise has no effect. .PARAMETER EnableSharedVoicemailSystemPromptSuppression Optional. Where possible, tries to suppress System Prompts. Effective only for SharedVoicemail Targets as an Operator or MenuOption. Otherwise has no effect. .EXAMPLE New-TeamsAutoAttendantMenu -Name "My Menu" -Action MenuOptions -Prompts $Prompts -MenuOptions $MenuOptions [-EnableDialByName $True] [-DirectorySearchMethod ByName] Classic behaviour, mostly synonymous with functionality provided by New-CsAutoAttendantMenu. Please see parameters there. Creates Menu with the MenuOptions Objects provided and applies the Prompts Object as the Greeting. Parameters EnableDialByName and DirectorySearchMethod can be used as outlined in New-CsAutoAttendantMenu .EXAMPLE New-TeamsAutoAttendantMenu -Action MenuOptions -Prompts "Press 1 for Sales..." -MenuOptions $MenuOptions -DirectorySearchMethod ByExtension Creates a Menu with a Prompt and MenuOptions. Creates a Prompts Object with the provided Text-to-voice string. Creates Menu with the MenuOptions Objects provided. DirectorySearchMethod is set to ByExtension .EXAMPLE New-TeamsAutoAttendantMenu -Action MenuOptions -Prompts "C:\temp\Menu.wav" -CallTargetsInOrder "MyCQ@domain.com","MyAA@domain.com","$null","tel:+15551234567","Please listen for our opening hours..." Creates a Menu with a Prompt and MenuOptions. Creates a Prompts Object with the provided Path to the Audio File. Creates a Menu Object with the Call Targets provided in order of application depending on identified ObjectType: Option 1 and 2 will be TransferToCallTarget (ResourceAccount), Call Queue and Auto Attendant respectively. Option 3 will not be assigned ($null), Option 4 will be TransferToCallTarget (ExternalPstn) Option 5 will be an Announcement (Text-to-Voice) - returns to main menu afterwards. This method does not allow specifying User Object that are intended to forward to the Users Voicemail! .EXAMPLE New-TeamsAutoAttendantMenu -Action MenuOptions -Prompts "Press 1 for John, Press 3 for My Group" -CallTargetsInOrder "John@domain.com","","My Group","C:\temp\OpeningHours.wav" Creates a Menu with a Prompt and MenuOptions. Creates a Prompts Object with the provided Text-to-voice string. Creates a Menu with MenuOptions Objects provided in order with the Parameter CallTargetsInOrder depending on identified ObjectType: Option 1 will be TransferToCallTarget (User), Option 2 is unassigned (empty string), Option 3 is TransferToCallTarget (Shared Voicemail) Option 4 will be an Announcement (AudioFile) - returns to main menu afterwards. Maximum 12 options are supported, if more than 9 are provided, the Switch AddOperatorOnZero (if used) is ignored. This method does not allow specifying User Object that are intended to forward to the Users Voicemail! .EXAMPLE New-TeamsAutoAttendantMenu -Action Disconnect Creates a default Menu, disconnecting the Call .EXAMPLE New-TeamsAutoAttendantMenu -Action TransferToOperator Creates a default Menu, transferring the Call to the Operator .EXAMPLE New-TeamsAutoAttendantMenu -Action TransferToCallTarget -CallTarget "John@domain.com" Creates a default Menu, transferring the Call to the Call target. Expected UserPrincipalName (User, ResourceAccount), Group Name (Shared Voicemail), Tel Uri (ExternalPstn) .INPUTS System.String .OUTPUTS System.Object .NOTES Limitations: CallTargetsInOrder are Menu Options integrated and their type is parsed with Get-TeamsCallableEntity This provides the following limitation: Provided UPNs that are found to be AzureAdUsers are limited to be used as "Person in the Organisation" If forwarding to the Users Voicemail is required, please change this in the Admin Center afterwards. Alternatively, please define MenuOptions yourself and use with the MenuOptions Parameter. CallTargetsInOrder now supports a maximum 12 options. If Position 10 of the Object contains a valid target and a Menu Option is created for 'Press 0', the switch AddOperatorOnZero (if used) is ignored. Position 11 is mapped to Pound ('#'), Position 12 is mapped to Star ('*') To define Menu Options manually, please see: https://docs.microsoft.com/en-us/powershell/module/skype/new-csautoattendantmenuoption?view=skype-ps Please see 'Set up an auto attendant' for details: https://docs.microsoft.com/en-us/MicrosoftTeams/create-a-phone-system-auto-attendant?WT.mc_id=TeamsAdminCenterCSH .COMPONENT TeamsAutoAttendant .FUNCTIONALITY Creates a Menu Object to be used in Auto Attendants .LINK https://github.com/DEberhardt/TeamsFunctions/tree/main/docs/New-TeamsAutoAttendantMenu.md .LINK https://github.com/DEberhardt/TeamsFunctions/tree/main/docs/about_TeamsAutoAttendant.md .LINK https://github.com/DEberhardt/TeamsFunctions/tree/main/docs/ .LINK https://docs.microsoft.com/en-us/MicrosoftTeams/create-a-phone-system-auto-attendant?WT.mc_id=TeamsAdminCenterCSH .LINK https://docs.microsoft.com/en-us/powershell/module/skype/new-csautoattendantmenuoption?view=skype-ps #> [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'Disconnect', ConfirmImpact = 'Low')] [Alias('New-TeamsAAMenu')] [OutputType([System.Object])] param( [Parameter(HelpMessage = 'Optional Name of the Menu')] [ValidateLength(5, 63)] [string]$Name, [Parameter(Mandatory, HelpMessage = 'Action determines Type of Menu to be built')] [ValidateSet('TransferToMenu', 'Disconnect', 'TransferToCallTarget')] [string]$Action, [Parameter(Mandatory, ParameterSetName = 'MenuOptions', HelpMessage = 'Prompt object, Text-To-Voice String or Full path to AudioFile')] [Parameter(Mandatory, ParameterSetName = 'CallTargetsToProcess', HelpMessage = 'Prompt object, Text-To-Voice String or Full path to AudioFile')] [ArgumentCompleter( { '<Prompts-Object>', '<Your Text-to-speech-string>', 'C:\Temp\' })] $Prompts, [Parameter(Mandatory, ParameterSetName = 'MenuOptions', HelpMessage = 'MenuOptions Object')] [object[]]$MenuOptions, [Parameter(Mandatory, ParameterSetName = 'CallTargetsToProcess', HelpMessage = 'Up to 9 Call Targets in order to be applied as MenuOptions')] [AllowNull()] [AllowEmptyString()] [Alias('MenuOptionsInOrder')] [string[]]$CallTargetsInOrder, [Parameter(Mandatory, ParameterSetName = 'TransferToCallTarget', HelpMessage = 'Up to 9 Call Targets in order to be applied as MenuOptions')] [string]$CallTarget, [Parameter(ParameterSetName = 'CallTargetsToProcess', HelpMessage = 'Adds a Menu Option for option 0 to Transfer to Operator')] [switch]$AddOperatorOnZero, [Parameter(HelpMessage = 'Enables directory search by recipient name and get transferred to the party')] [switch]$EnableDialByName, [Parameter(HelpMessage = 'Directory Search Method for the Auto Attendant menu')] [ValidateSet('None', 'ByName', 'ByExtension')] [string]$DirectorySearchMethod, [Parameter(HelpMessage = 'Tries to Enable Transcription wherever possible')] [switch]$EnableTranscription, [Parameter(HelpMessage = 'Tries to Suppress System Prompts wherever possible')] [switch]$EnableSharedVoicemailSystemPromptSuppression ) #param begin { Show-FunctionStatus -Level Live Write-Verbose -Message "[BEGIN ] $($MyInvocation.MyCommand)" # Asserting AzureAD Connection if ( -not $script:TFPSSA) { $script:TFPSSA = Assert-AzureADConnection; if ( -not $script:TFPSSA ) { 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' } # Preparing Splatting Object $Parameters = $null } #begin process { Write-Verbose -Message "[PROCESS] $($MyInvocation.MyCommand)" #region Routing - Menu Options switch ($Action) { 'TransferToMenu' { #region Prompt Write-Verbose -Message 'TransferToMenu - Creating Greeting' $PromptsType = ($Prompts | Get-Member | Select-Object TypeName -First 1).TypeName switch ($PromptsType) { 'Deserialized.Microsoft.Rtc.Management.Hosted.OAA.Models.Prompt' { Write-Verbose -Message 'Call Flow - Prompts provided is a Prompt Object' $Parameters += @{'Prompts' = $Prompts } } 'Microsoft.Rtc.Management.Hosted.OAA.Models.Prompt' { Write-Verbose -Message 'Call Flow - Prompts provided is a Prompt Object' $Parameters += @{'Prompts' = $Prompts } } 'System.String' { Write-Verbose -Message 'Call Flow - Greeting provided as a String' # Process Greeting try { $PromptsObject = New-TeamsAutoAttendantPrompt -String "$Prompts" if ($PromptsObject) { Write-Verbose -Message 'Prompts - Adding 1 Prompt (Greeting)' $Parameters += @{'Prompts' = $PromptsObject } } } catch { Write-Warning -Message "Call Flow - Menu - Greeting - Error creating prompt. Omitting Greeting. Exception Message: $($_.Exception.Message)" } } default { Write-Error -Message 'Type not accepted as a Greeting/Prompt, please provide a Prompts Object or a String' -ErrorAction Stop } } #endregion #region MenuOptions or CallTargetsInOrder switch ($PSCmdlet.ParameterSetName) { 'MenuOptions' { # No Action required } 'CallTargetsToProcess' { # Process Ordered Write-Verbose -Message 'CallTargetsToProcess - Creating Menu Options for Call Targets as provided (CallTargetsInOrder)' $Option = 1 $MaxOptions = 12 #$MaxOptions = if ($AddOperatorOnZero) { 9 } else { 10 } if ( $CallTargetsInOrder.Count -gt 0 -and $DirectorySearchMethod -eq 'ByExtension' ) { Write-Warning -Message 'MenuOptions - No Menu Options are supported if DirectorySearchMethod DialbyExtension is selected. Objects are ignored (CallTargetsInOrder)' $CallTargetsInOrder = $null } if ( $CallTargetsInOrder.Count -gt 1 -and $DirectorySearchMethod -eq 'ByName') { Write-Warning -Message 'MenuOptions - Only one Menu Option is supported if DirectorySearchMethod DialbyName is selected. Objects are ignored (CallTargetsInOrder)' $CallTargetsInOrder = $CallTargetsInOrder | Select-Object -First 1 } if ($CallTargetsInOrder.Count -gt 12) { Write-Warning -Message 'MenuOptions - Max 12 options are supported as Call Targets. Additional Objects are ignored (CallTargetsInOrder)' } [System.Collections.Generic.List[object]]$CreatedMenuOptions = @() foreach ($Target in $CallTargetsInOrder) { Write-Verbose -Message "CallTargetsToProcess - Processing '$Target'" if ($Option -le $CallTargetsInOrder.Count -and $Option -le $MaxOptions) { $Selection = switch ( $Option ) { 10 { '0' } 11 { 'Pound' } 12 { 'Star' } default { $Option } } if ( $Target ) { $MenuOptionToAdd = $null #Identifying the Target is delegated to New-TeamsAutoAttendantMenuOption for all but Announcement try { $CallableEntity = Get-TeamsObjectType "$Target" if ($CallableEntity -eq 'Unknown') { # Assume it is an announcement $MenuOptionToAdd = New-TeamsAutoAttendantMenuOption -Press $Selection -Announcement "$Target" -ErrorAction Stop } else { # Process as a Call Target $TeamsAutoAttendantMenuOption = @{ Press = $Selection CallTarget = "$Target" ErrorAction = 'Stop' Verbose = $Verbosepreference } if ( $PSBoundParameters.ContainsKey('EnableTranscription') ) { $TeamsAutoAttendantMenuOption += @{ EnableTranscription = $true } } if ( $PSBoundParameters.ContainsKey('EnableSharedVoicemailSystemPromptSuppression') ) { $TeamsAutoAttendantMenuOption += @{ EnableSharedVoicemailSystemPromptSuppression = $true } } $MenuOptionToAdd = New-TeamsAutoAttendantMenuOption @TeamsAutoAttendantMenuOption #$MenuOptionToAdd = New-TeamsAutoAttendantMenuOption -Press $Selection -CallTarget "$Target" -ErrorAction Stop } if ($MenuOptionToAdd) { Write-Information "INFO: MenuOptions - Creating Option 'Press $Selection' to '$Target' - OK" [void]$CreatedMenuOptions.Add($MenuOptionToAdd) } } catch { Write-Warning -Message "MenuOptions - Creating Option 'Press $Selection' to '$Target' - Creation unsuccessful! Omitting Call Target" } } else { Write-Verbose -Message "MenuOptions - Creating Option 'Press $Selection' not provided, is empty or NULL - OK (omitted)" -Verbose } $Option++ } } # AddOperatorOnZero if ($AddOperatorOnZero) { if ( $DirectorySearchMethod -eq 'None' ) { if ( $null -eq ($CreatedMenuOptions | Where-Object DtmfResponse -EQ Tone0) ) { # Create Menu Option on "Press 0" to forward to Operator Write-Verbose -Message 'MenuOptions - Creating additional Option for Operator (AddOperatorOnZero)' $MenuOptionToAdd = $null $MenuOptionToAdd = New-TeamsAutoAttendantMenuOption -Press 0 -TransferToOperator if ($MenuOptionToAdd) { Write-Information "INFO: MenuOptions - Creating Option 'Press 0' to 'Operator' - OK" [void]$CreatedMenuOptions.Add($MenuOptionToAdd) } } else { Write-Warning -Message "MenuOptions - AddOperatorOnZero is not parsed as CallTargetsInOrder already contains an Option for 'Press 0'" } } } $MenuOptions = $CreatedMenuOptions } } #endregion } #MenuOption 'Disconnect' { $MenuOptions = New-TeamsAutoAttendantMenuOption -DisconnectCall } 'TransferToCallTarget' { Write-Verbose -Message 'TransferToCallTarget - Creating Menu Option' $TeamsAutoAttendantMenuOption = @{ CallTarget = "$CallTarget" Verbose = $Verbosepreference } if ( $PSBoundParameters.ContainsKey('EnableTranscription') ) { $TeamsAutoAttendantMenuOption = @{ EnableTranscription = $true } } if ( $PSBoundParameters.ContainsKey('EnableSharedVoicemailSystemPromptSuppression') ) { $TeamsAutoAttendantMenuOption = @{ EnableSharedVoicemailSystemPromptSuppression = $true } } $MenuOptions = New-TeamsAutoAttendantMenuOption @TeamsAutoAttendantMenuOption } } # Adding MenuOptions if ( -not $MenuOptions -and $DirectorySearchMethod -ne 'None' ) { Write-Verbose -Message "MenuOptions - Action '$Action' - DirectorySearchMethod '$DirectorySearchMethod' - Omiting Menu Options" } else { Write-Verbose -Message "MenuOptions - Action '$Action' - Adding $($MenuOptions.Count) Menu Options" $Parameters += @{'MenuOptions' = $MenuOptions } } #endregion #region Other Parameters if ( -not $Name) { $Name = "Menu with $($Parameters.MenuOptions.Count) Options" + $(if ($Parameters.Prompts) { ' and Greeting' }) } $Parameters += @{'Name' = "$Name" } Write-Information "INFO: Menu Name selected: '$Name'" #VALIDATE EnableDialByName may not be needed if DirectorySearchMethod is not 'None' - simplify? if ( $EnableDialByName ) { $Parameters += @{'EnableDialByName' = $true } } if ( $DirectorySearchMethod ) { $Parameters += @{'DirectorySearchMethod' = $DirectorySearchMethod } } #endregion # Create Menu Write-Verbose -Message '[PROCESS] Creating Menu' if ($PSBoundParameters.ContainsKey('Debug') -or $DebugPreference -eq 'Continue') { " Function: $($MyInvocation.MyCommand.Name) - Parameters (New-CsAutoAttendantMenu)", ($Parameters | Format-Table -AutoSize | Out-String).Trim() | Write-Debug } if ($PSCmdlet.ShouldProcess("$($Menu.Name)", 'New-CsAutoAttendantMenu')) { New-CsAutoAttendantMenu @Parameters } } end { Write-Verbose -Message "[END ] $($MyInvocation.MyCommand)" } #end } #New-TeamsAutoAttendantMenu |