Public/Support/CallQueue/New-TeamsCallQueueTrigger.ps1
# Module: TeamsFunctions # Function: CallQueue # Author: David Eberhardt # Updated: 07-AUG-2023 # Status: RC function New-TeamsCallQueueTrigger { <# .SYNOPSIS Setting one of the Exception Handling Triggers for a Call Queue .DESCRIPTION Helper function to set one of the Exception Handling Triggers for a Call Queue .PARAMETER Name Required. Name or Id of the Call Queue. Used only to display proper output message. .PARAMETER Trigger Required to define a Trigger. Either Overflow, Timeout or NoAgent .PARAMETER Threshold Optional. Available for Triggers Overflow & Timeout Overflow: Number of people waiting in the queue before the Action to trigger Timeout: Time in Seconds for the Action to trigger NoAgent: Not available .PARAMETER NoAgentApplyTo Optional. Only available for Trigger NoAgent. Instructs that the setting applies to AllCalls or only NewCalls .PARAMETER Prompt Optional. Prompt to play when is reaching threshold but before Action is taken String as a Path for a Recording or a Text-to-Voice string. Function determines Type Aliases for backward compatibility: SharedVoicemailTextToSpeechPrompt, SharedVoicemailAudioFile .PARAMETER Action Required. Action to be taken if the Queue size limit (Threshold) is reached Forward requires specification of Target Default: DisconnectWithBusy, Values: DisconnectWithBusy, Forward, VoiceMail, SharedVoiceMail .PARAMETER Target Situational. Required only if Action is not the default. UserPrincipalName of the Target For Overflow, default is DisconnectWithBusy, for Timeout it is Default, for NoAgent it is Queue .PARAMETER SetPromptForAllTypes Situational. Boolean Switch. This switch will apply any provided prompt to all valid parameters If a Prompt is used setting a Text-to-Voice string value this switch will apply the same value to TextToSpeechPrompt- parameters of the queue. If not provided, it will only set the prompt for the Action provided. This affects Disconnect, RedirectPerson, RedirectVoiceApp, RedirectPhoneNumber, RedirectVoicemail & SharedVoicemail Prompts that are not provided will not be set (in the example above, any Timeout parameters would not be affected). .PARAMETER EnableTranscription Situational. Boolean Switch. Requires specification of LanguageId Enables a transcription of the Voicemail message to be sent to the Group mailbox .PARAMETER EnableSystemPromptSuppression Situational. Boolean Switch. Requires specification of LanguageId Enables a transcription of the Voicemail message to be sent to the Group mailbox .PARAMETER LanguageId Optional Language Identifier indicating the language that is used to play shared voicemail prompts. This parameter becomes a required parameter If the Action is set to SharedVoicemail and the language is not yet set. .EXAMPLE New-TeamsCallQueueTrigger -Trigger Overflow -Action Forward -Target 'User@domain.com' Creates a new Overflow trigger that is set to Redirect to a Person in the Organisation. .EXAMPLE New-TeamsCallQueueTrigger -Trigger Timeout -Action SharedVoicemail -Target "My Group" -EnableTranscription -EnableSystemPromptSuppression Creates a new Timeout trigger that is set to SharedVoicemail forwarding to "My Group" and enabling Transcription It also enables the suppression of the System Prompt before forwarding to the Target Group .EXAMPLE New-TeamsCallQueueTrigger -Trigger NoAgent -Action Disconnect -Prompt "Sorry, we are closed" -SetPromptForAllTypes -LanguageId en-gb Creates a new NoAgent trigger that is set to Disconnect with a Text-to-Speech Prompt and Language set to English (UK) .EXAMPLE New-TeamsCallQueueTrigger -Trigger Overflow -Action Disconnect -Prompt "Sorry, we are busy" -SetPromptForAllTypes Creates a new Overflow trigger that is set to Disconnect with a Text-to-Speech Prompt. The Switch SetPromptForAllTypes will set the Prompt for all types, not only for Disconnect, in case it is to be switched later. NOTE: As this is a Text-to-Voice prompt, it requires a Language to be set. Parameter LanguageId must be set in conjunction with the output of this Cmdlet, the creation of the Call Queue will fail .INPUTS System.String .OUTPUTS System.Object .NOTES Audio Files, if not found will result in the prompt not being configured. .COMPONENT TeamsCallQueue .FUNCTIONALITY Sets one of the Exception Handling triggers for a Call Queue .LINK https://github.com/DEberhardt/TeamsFunctions/tree/master/docs/Set-TeamsCallQueueTrigger.md .LINK https://github.com/DEberhardt/TeamsFunctions/tree/master/docs/about_TeamsFunctions.md .LINK https://github.com/DEberhardt/TeamsFunctions/tree/master/docs/ #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')] [Alias('New-TeamsCQT')] [OutputType([PSCustomObject])] param ( [Parameter(Mandatory, Position = 0, HelpMessage = 'Name or ID of the Call Queue')] [string]$Name, [Parameter(Mandatory, HelpMessage = 'Name or ID of the Call Queue')] [Validateset('Overflow', 'Timeout', 'NoAgent')] [ArgumentCompleter({ param ( $commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters ) $possibleValues = @{ Overflow = @('DisconnectWithBusy', 'Forward', 'Voicemail', 'SharedVoicemail') Timeout = @('Disconnect', 'Forward', 'Voicemail', 'SharedVoicemail') NoAgent = @('Queue', 'Forward', 'Voicemail', 'SharedVoicemail') } if ($fakeBoundParameters.ContainsKey('Trigger')) { $possibleValues[$fakeBoundParameters.Trigger] | Where-Object { $_ -like "$wordToComplete*" } } else { $possibleValues.Values | ForEach-Object { $_ } } })] [string]$Trigger, [Parameter(HelpMessage = 'Overflow: # people in the Queue; Timeout: Time in seconds (0-2700s)')] [int16]$Threshold, [Parameter(HelpMessage = 'Prompt for trigger. Text-to-Voice will require the LanguageId Parameter')] [string]$Prompt, [Parameter(Mandatory, HelpMessage = 'Action to be taken')] [Validateset('Disconnect', 'Forward', 'Voicemail', 'SharedVoicemail', 'Queue', 'DisconnectWithBusy')] [string]$Action, # if Action is not DisconnectWithBusy, this is required [Parameter(HelpMessage = 'TEL URI, UPN or Group Display Name that is targeted upon trigger, only valid for forwarded calls')] [string]$Target, [Parameter(HelpMessage = 'Applies any provided prompt to all types')] [switch]$SetPromptForAllTypes, [Parameter(HelpMessage = 'Using this Parameter will make a Transcription of the Voicemail message available in the Mailbox')] [switch]$EnableTranscription, [Parameter(HelpMessage = 'Using this Parameter will make a Transcription of the Voicemail message available in the Mailbox')] [switch]$EnableSystemPromptSuppression, [Parameter(HelpMessage = 'Language Identifier from Get-CsAutoAttendantSupportedLanguage.')] [ValidateScript( { if ($_ -in $(Get-OrbitAcSbCsAutoAttendantSupportedLanguage @args) ) { $True } else { throw [System.Management.Automation.ValidationMetadataException] 'Value must be a supported Langauge Id. Use Intellisense for options' } })] # [ArgumentCompleter({ Get-OrbitAcSbCsAutoAttendantSupportedLanguage @args })] [string]$LanguageId ) begin { Show-OrbitFunctionStatus -Level RC Write-Verbose -Message "[BEGIN ] $($MyInvocation.MyCommand)" # Asserting Graph Connection if ( -not (Test-GraphConnection) ) { throw 'Connection to Microsoft Graph not established. Please validate connection' } # Asserting MicrosoftTeams Connection if ( -not (Assert-MicrosoftTeamsConnection) ) { throw 'Connection to Microsoft Teams not established. Please validate connection' } # Setting Preference Variables according to Upstream settings if (-not $PSBoundParameters['Verbose']) { $VerbosePreference = $PSCmdlet.SessionState.PSVariable.GetValue('VerbosePreference') } if (-not $PSBoundParameters['Confirm']) { $ConfirmPreference = $PSCmdlet.SessionState.PSVariable.GetValue('ConfirmPreference') } if (-not $PSBoundParameters['WhatIf']) { $WhatIfPreference = $PSCmdlet.SessionState.PSVariable.GetValue('WhatIfPreference') } $DebugPreference = if (-not $PSBoundParameters['Debug']) { $PSCmdlet.SessionState.PSVariable.GetValue('DebugPreference') } else { 'Continue' } $InformationPreference = if ( $PSBoundParameters['InformationAction']) { $PSCmdlet.SessionState.PSVariable.GetValue('InformationAction') } else { 'Continue' } $Stack = Get-PSCallStack $Called = ($stack.length -ge 3) #Initialising Counters $private:StepsID0, $private:StepsID1 = Get-WriteBetterProgressSteps -Code $($MyInvocation.MyCommand.Definition) -MaxId 1 $private:ActivityID0 = $($MyInvocation.MyCommand.Name) [int] $private:CountID0 = [int] $private:CountID1 = 1 $AudioFileMatch = '.(wav|wma|mp3)$' # Validation $StatusID0 = 'Validating Input' # Language has to be normalised as the Id is case sensitive $CurrentOperationID0 = 'LanguageId' Write-BetterProgress -Id 0 -Activity $ActivityID0 -Status $StatusID0 -CurrentOperation $CurrentOperationID0 -Step ($private:CountID0++) -Of $private:StepsID0 if ($PSBoundParameters['LanguageId']) { $LanguageId = $($LanguageId.Split('-')[0]).ToLower() + '-' + $($LanguageId.Split('-')[1]).ToUpper() Write-Verbose "'$Name' LanguageId normalised to '$LanguageId'" $VRSupported = ((Get-CsAutoAttendantSupportedLanguage -Id $LanguageId).VoiceResponseSupported) Write-Verbose "LanguageId '$LanguageId' - Voice Responses are $(if ( -not $VRSupported ) { 'no t' })supported" } else { if ( ( $PSBoundParameters['EnableTranscription'] -and $EnableTranscription ) -or ` $PSBoundParameters['Prompt'] -and $Prompt -notmatch $AudioFileMatch ) { Write-Error -Message "$Message`: Prompt or Transcription require a Language set selection. Please provide Parameter LanguageId" -ErrorAction Stop -RecommendedAction 'Add Parameter LanguageId' } } if ( $PSBoundParameters['Prompt'] -and $PSBoundParameters['SetPromptForAllTypes'] ) { Write-Warning -Message "The configuration parameters for all Prompts are currently only available in PowerShell ` and do not appear in Teams admin center. Saving a call queue configuration through Teams admin center will remove ` any of these configured items" } if ( $PSBoundParameters['Threshold'] ) { switch ( $Trigger ) { 'Overflow' { If ($_ -ge 0 -and $_ -le 200) { $True } else { throw [System.Management.Automation.ValidationMetadataException] 'OverflowThreshold: Must be a value between 0 and 200.' } } 'Timeout' { If ($_ -ge 0 -and $_ -le 2700) { $True } else { throw [System.Management.Automation.ValidationMetadataException] 'TimeoutThreshold: Must be a value between 0 and 2700.' } } 'NoAgent' { throw [System.Management.Automation.ValidationMetadataException] 'NoAgentThreshold: Parameter not available.' } } } # ensuring non-accidental default application #$ChosenAction = $Action $Disconnect = switch ( $Trigger ) { 'Overflow' { 'DisconnectWithBusy' } 'Timeout' { 'Disconnect' } 'NoAgent' { 'Disconnect' } } # SetPromptForAllTypes - Defining Actions for multi-application $AllNonDefaultActions = @('Disconnect', 'RedirectPerson', 'RedirectVoiceApp', 'RedirectPhoneNumber', 'RedirectVoicemail', 'SharedVoicemail') } #begin process { Write-Verbose -Message "[PROCESS] $($MyInvocation.MyCommand)" # re-Initialising counters for Progress bars (for Pipeline processing) [int] $private:CountID0 = 1 $Parameters = @{} #region Target if ( $PSBoundParameters['Target'] ) { try { $CallTarget = $null $CallTarget = Get-TeamsCallableEntity -Identity "$Target" -ErrorAction Stop if ($PSBoundParameters['Debug'] -or $DebugPreference -eq 'Continue') { " Function: $($MyInvocation.MyCommand.Name) - CallTarget:", ($CallTarget | Format-Table -AutoSize | Out-String).Trim() | Write-Debug } } catch { Write-Warning -Message "Call Queue '$Name' - Action '$Action': $ParameterName '$Target' not determined" } try { $ParameterName = $($Trigger + 'ActionTarget') Write-Verbose -Message "Call Queue '$Name' - Action '$Action' - $ParameterName - Parsing Target" switch ($Action) { 'Queue' { # No target required for the default action } 'Disconnect' { # No target required for the default action } 'DisconnectWithBusy' { # No target required for the default action } 'Forward' { # Forward requires a Target (Tel URI, ObjectId of UPN of a User or an Application Instance to be translated to GUID) Write-Verbose -Message "Call Queue '$Name' - Action '$Action': $ParameterName '$Target' - Querying Object" switch ( $CallTarget.ObjectType ) { 'TelURI' { #Telephone Number (E.164) Write-Verbose -Message "Call Queue '$Name' - Action '$Action': $ParameterName '$($CallTarget.Identity)' - OK" $Parameters.$ParameterName = $CallTarget.Identity } 'User' { try { $Assertion = $null $Assertion = Assert-TeamsCallableEntity -UserPrincipalName "$($CallTarget.Entity)" -RequireEV -WarningAction SilentlyContinue -ErrorAction Stop if ($Assertion) { Write-Verbose -Message "Call Queue '$Name' - Action '$Action': $ParameterName '$($CallTarget.Identity)' - OK" $Parameters.$ParameterName = $CallTarget.Identity } else { Write-Warning -Message "Call Queue '$Name' - Action '$Action': $ParameterName '$Target' not asserted" } } catch { Write-Warning -Message "Call Queue '$Name' - Action '$Action': $ParameterName '$Target' Error: $($_.Exception.Message)" } } 'ResourceAccount' { try { $Assertion = $null $Assertion = Assert-TeamsCallableEntity -UserPrincipalName "$($CallTarget.Entity)" -WarningAction SilentlyContinue -ErrorAction Stop if ($Assertion) { Write-Verbose -Message "Call Queue '$Name' - Action '$Action': $ParameterName '$($CallTarget.Identity)' - OK" $Parameters.$ParameterName = $CallTarget.Identity } else { Write-Warning -Message "Call Queue '$Name' - Action '$Action': $ParameterName '$Target' not asserted" } } catch { Write-Warning -Message "Call Queue '$Name' - Action '$Action': $ParameterName '$Target' Error: $($_.Exception.Message)" } } default { # Capturing any other specified Target that does not match for the Forward Write-Warning -Message "Call Queue '$Name' - Action '$Action': $ParameterName '$Target' is incompatible and is not processed!" Write-Verbose -Message "Call Queue '$Name' - Action '$Action': Target expected is: Tel URI or a UPN of a User or Resource Account" -Verbose } } } 'VoiceMail' { # VoiceMail requires a Target (UPN of a User to be translated to GUID) if ($CallTarget.ObjectType -eq 'User') { try { $Assertion = $null $Assertion = Assert-TeamsCallableEntity -UserPrincipalName "$($CallTarget.Entity)" -RequireEV -WarningAction SilentlyContinue -ErrorAction Stop if ($Assertion) { Write-Verbose -Message "Call Queue '$Name' - Action '$Action': $ParameterName '$($CallTarget.Identity)' - OK" $Parameters.$ParameterName = $CallTarget.Identity } else { Write-Warning -Message "Call Queue '$Name' - Action '$Action': $ParameterName '$Target' not asserted" } } catch { Write-Warning -Message "Call Queue '$Name' - Action '$Action': $ParameterName '$Target' Error: $($_.Exception.Message)" } } else { Write-Warning -Message "Call Queue '$Name' - Action '$Action': $ParameterName '$Target' is incompatible and is not processed!" Write-Verbose -Message "Call Queue '$Name' - Action '$Action': Target expected is: UPN of a User" -Verbose } } 'SharedVoiceMail' { # SharedVoiceMail requires a Target (UPN of a Group to be translated to GUID) switch ( $CallTarget.ObjectType ) { 'Group' { Write-Verbose -Message "Call Queue '$Name' - Action '$Action': $ParameterName '$($CallTarget.Identity)' - OK" $Parameters.$ParameterName = $CallTarget.Identity } 'Unknown' { Write-Warning -Message "Call Queue '$Name' - Action '$Action': $ParameterName '$Target' not set! Error enumerating Target" } default { Write-Warning -Message "Call Queue '$Name' - Action '$Action': $ParameterName '$Target' not a Group!" } } } } } catch { Write-Warning -Message "Call Queue '$Name' - Action '$Action': $ParameterName '$Target' not set! Error enumerating Target: $($_.Exception.Message)" } } #endregion #region Prompt if ( $PSBoundParameters['Prompt'] ) { $ThisAction = switch ( $Action ) { 'DisconnectWithBusy' { 'Disconnect' } 'Disconnect' { 'Disconnect' } 'Forward' { # Parameter depends on Type of Target provided. Determining correct (single) parameter to apply with CallTarget switch ( $CallTarget.ObjectType ) { 'User' { 'RedirectPerson' } 'ResourceAccount' { 'RedirectVoiceApp' } 'TelUri' { 'RedirectPhoneNumber' } Default { 'Disconnect' } # Necessary as $CallTarget could be blank! } } 'Voicemail' { 'RedirectVoicemail' } 'SharedVoicemail' { 'SharedVoicemail' } default { $null } } if ( $ThisAction ) { # Create Prompt $TeamsCallQueuePrompt = Checkpoint-TeamsCallQueuePrompt -Prompt "$Trigger`Prompt" -String "$Prompt" -ErrorAction Stop $(if ($PSBoundParameters['SetPromptForAllTypes']) { $AllNonDefaultActions } else { $ThisAction }) | ForEach-Object { $ParameterName = $TeamsCallQueuePrompt.Parameter -replace '\*', "$_" Write-Verbose -Message "Call Queue '$Name' - Parameter added: $ParameterName - Value '$($TeamsCallQueuePrompt.Value)'" $Parameters.$ParameterName = $TeamsCallQueuePrompt.Value } } } #endregion #region Action if ( $PSBoundParameters['Action'] ) { if ( $Action -EQ 'Queue' ) { Write-Verbose -Message "Call Queue '$Name' - Action: '$ParameterName' - Value: '$Action' - No Action needed - OK" } elseif ( $Action -NE $Disconnect -and -not $PSBoundParameters['Target'] ) { Write-Error -Message "$($MyInvocation.MyCommand) - Action '$Action' - Target missing. please provide a Target" -ErrorAction Stop } else { $ParameterName = $($Trigger + 'Action') if ( $Action -EQ $Disconnect ) { Write-Verbose -Message "Call Queue '$Name' - Action: '$ParameterName' - Value: '$Disconnect' - OK" $Parameters.$ParameterName = $Disconnect } elseif ($Parameters.ContainsKey("$ParameterName`Target")) { # Only adding a non-default action if a Target is present already Write-Verbose -Message "Call Queue '$Name' - Action: '$ParameterName' - Value: '$Action' - OK" $Parameters.$ParameterName = $Action } else { Write-Warning -Message "Call Queue '$Name' - Action: '$ParameterName' - Value: '$Action' - Target not enumerated. Action not set." } } } #endregion #region Language, Transcription & Suppression if ( $PSBoundParameters['LanguageId'] ) { Write-Verbose -Message "Call Queue '$Name' - LanguageId: $LanguageId - OK" $Parameters.LanguageId = $LanguageId } if ($PSBoundParameters['EnableTranscription']) { if ( $Action -NE 'SharedVoicemail' ) { Write-Verbose -Message "Call Queue '$Name' - Action '$Action' - EnableTranscription cannot be set (only for 'SharedVoicemail')" } else { $ParameterName = $('Enable' + $Trigger + 'SharedVoicemailTranscription') Write-Verbose -Message "Call Queue '$Name' - Action '$Action' - $ParameterName - OK" $Parameters.$ParameterName = $EnableTranscription } } if ($PSBoundParameters['EnableSystemPromptSuppression']) { if ( $Action -NE 'SharedVoicemail' ) { Write-Verbose -Message "Call Queue '$Name' - Action '$Action' - EnableSystemPromptSuppression cannot be set (only for 'SharedVoicemail')" } else { $ParameterName = $('Enable' + $Trigger + 'SharedVoicemailSystemPromptSuppression') Write-Verbose -Message "Call Queue '$Name' - Action '$Action' - $ParameterName - OK" $Parameters.$ParameterName = $EnableSystemPromptSuppression } } #endregion #region Settings dependent on ParameterSetName if ( $PSBoundParameters['Threshold'] ) { $ParameterName = $($Trigger + 'Threshold') Write-Verbose -Message "Call Queue '$Name' - $ParameterName - $Threshold - OK" $Parameters.$ParameterName = $Threshold } if ( $PSBoundParameters['NoAgentApplyTo'] ) { if ( $Trigger -NE 'NoAgent' ) { Write-Warning -Message "Call Queue '$Name' - NoAgentApplyTo is only available for 'NoAgent' Trigger. Omitting parameter" } else { Write-Verbose -Message "Call Queue '$Name' - NoAgentApplyTo: $NoAgentApplyTo - OK" $Parameters.NoAgentApplyTo = $NoAgentApplyTo } } #endregion # Output return $Parameters } #process end { Write-Verbose -Message "[END ] $($MyInvocation.MyCommand)" } #end } #New-TeamsCallQueueTrigger |