Public/AutoAttendant/Update-TeamsAutoAttendant.ps1

# Module: TeamsFunctions
# Function: AutoAttendant
# Author: David Eberhardt
# Updated: 03-SEP 2022
# Status: Live




function Update-TeamsAutoAttendant {
  <#
  .SYNOPSIS
    Changing, amending or replacing the Business Hours Call Flow on existing Auto Attendants; Calling Set-CsAutoAttendant
  .DESCRIPTION
    Editing exiting Auto Attendants with Set-CsAutoAttendant requires manual creation of objects and surgical replacement.
    This script tries to simplify editing Auto Attendants by providing four functions:
    Update-TeamsAutoAttendant covers general Settings of the Auto Attendnat
    Update-TeamsAutoAttendantBusinessHours covers the Default Call Flow
    Update-TeamsAutoAttendantAfterHours covers the After Hours Call Flow
    Update-TeamsAutoAttendantHoliday covers the Holiday Call Flow
    Each individual Script can replace the full call flow with a previously created object,
    or amend some specific parts of the respective flow
  .PARAMETER Name
    Name of the Auto Attendant. Required to locate the Auto Attendant.
    Alternatively ID of the Auto Attendant for more precise location
  .PARAMETER Name
    Name of the Auto Attendant. Required to locate the Auto Attendant.
  .PARAMETER DisplayName
    Optional. Updates the Name of the Auto Attendant. Name will be normalised (unsuitable characters are filtered)
  .PARAMETER TimeZone
    Optional. TimeZone Identifier based on Get-CsAutoAttendantSupportedTimeZone, but abbreviated for easier input.
    Warning: Due to multiple time zone names with in the same relative difference to UTC this MAY produce incongruent output
    The time zone will be correct, but only specifying "UTC+01:00" for example will select the first entry.
    Default Value: "UTC"
  .PARAMETER LanguageId
    Optional. Language Identifier indicating the language that is used to play text and identify voice prompts.
    Default Value: "en-US"
  .PARAMETER Operator
    Optional. Creates a Callable entity for the Operator
    Expected are UserPrincipalName (User, ResourceAccount), a TelURI (ExternalPstn), an Office 365 Group Name (SharedVoicemail)
  .PARAMETER InclusionScope
    Optional. DialScope Object to pass to Set-CsAutoAttendant
    Object created with New-TeamsAutoAttendantDialScope or Set-CsAutoAttendantDialScope
  .PARAMETER ExclusionScope
    Optional. DialScope Object to pass to Set-CsAutoAttendant
    Object created with New-TeamsAutoAttendantDialScope or Set-CsAutoAttendantDialScope
  .PARAMETER EnableVoiceResponse
    Optional Switch to be passed to Set-CsAutoAttendant
  .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.
  .PARAMETER ForceListenMenuEnabled
    Optional. Toggles ForceListenMenuEnabled on Call Flow Objects. Only has an effect for Menus with MenuOptions.
  .PARAMETER AuthorizedUsers
    Optional. Users allowed to change certain aspects of the Auto Attendant (like Greetings or AudioFiles)
    These settings are governed by the assigned CsTeamsVoiceApplicationsPolicy (assigned to the User)
  .PARAMETER VoiceId
    Optional. Gender of the Voice for VoiceResponses
    Instructs the speech interpreter to use a specific gender. Dependent on availability for the selected Language.
  .PARAMETER PassThru
    Optional. Displays Auto Attendant Object after action.
  .EXAMPLE
    Update-TeamsAutoAttendant -Name "My Auto Attendant" -DisplayName "Main Number"
 
    Updates the Auto Attendant "My Auto Attendant" Name to "Main Number"
  .EXAMPLE
    Update-TeamsAutoAttendant -Name "My Auto Attendant" -TimeZone UTC-05:00 -LanguageId pt-BR -EnableVoiceResponse
 
    Updates the Auto Attendant "My Auto Attendant" and sets the TimeZone to UTC-5 and the language to Portuguese (Brazil)
    Enables VoiceResponses if available not yet enabled.
  .EXAMPLE
    Update-TeamsAutoAttendant -Name "My Auto Attendant" -Operator "tel:+1555123456"
 
    Updates the Auto Attendant "My Auto Attendant" with an Operator as a Callable Entity (Forward to Pstn)
  .EXAMPLE
    Update-TeamsAutoAttendant -Name "My Auto Attendant" -InclusionScope $InGroups -ExclusionScope $OutGroups
 
    Updates the Auto Attendant "My Auto Attendant" and passes through all objects provided.
    The InclusionScope and ExclusionScope parameters are set with the Scope defined prior.
  .INPUTS
    System.String
  .OUTPUTS
    System.Object
  .NOTES
    InclusionScope and ExclusionScope Objects can be created with New-TeamsAutoAttendantDialScope and the Group Names
    This was deliberately not integrated into this CmdLet
  .COMPONENT
    TeamsAutoAttendant
  .FUNCTIONALITY
    Updates an Auto Attendant with custom settings and friendly names as input
  .LINK
    https://github.com/DEberhardt/TeamsFunctions/tree/main/docs/Update-TeamsAutoAttendant.md
  .LINK
    https://github.com/DEberhardt/TeamsFunctions/tree/main/docs/about_TeamsAutoAttendant.md
  .LINK
    https://github.com/DEberhardt/TeamsFunctions/tree/main/docs/
  #>


  [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', PositionalBinding = $false)]
  [Alias('Update-TeamsAA')]
  [OutputType([System.Void])]
  param(
    [Parameter(Mandatory, Position = 0, ValueFromPipeline, HelpMessage = 'Name of the Auto Attendant')]
    [string]$Name,

    [Parameter(HelpMessage = 'Changes the Name to this DisplayName')]
    [string]$DisplayName,

    [Parameter(HelpMessage = 'TimeZone Identifier from Get-CsAutoAttendantSupportedTimeZone')]
    [ValidateScript( {
        if ($_ -in $(&$global:TfAcSbSupportedTimeZone)) { $True } else {
          throw [System.Management.Automation.ValidationMetadataException] 'Value must be a supported TimeZone. Use Intellisense for options'
        } })]
    [ArgumentCompleter({ &$global:TfAcSbSupportedTimeZone })]
    [string]$TimeZone,

    [Parameter(HelpMessage = 'Language Identifier from Get-CsAutoAttendantSupportedLanguage.')]
    [ValidateScript( {
        if ($_ -in $(&$global:TfAcSbSupportedLanguage).Id ) { $True } else {
          throw [System.Management.Automation.ValidationMetadataException] 'Value must be a supported Langauge Id. Use Intellisense for options'
        } })]
    [ArgumentCompleter({ $(&$global:TfAcSbSupportedLanguage).Id })]
    [string]$LanguageId,

    [Parameter(HelpMessage = 'Target String for the Operator (UPN, Group Name or Tel URI')]
    [string]$Operator,

    [Parameter(HelpMessage = 'Groups defining the Inclusion Scope')]
    [object]$InclusionScope,

    [Parameter(HelpMessage = 'Groups defining the Exclusion Scope')]
    [object]$ExclusionScope,

    [Parameter(HelpMessage = 'Voice Responses')]
    [boolean]$EnableVoiceResponse,

    [Parameter(HelpMessage = 'Tries to Enable Transcription wherever possible')]
    [switch]$EnableTranscription,

    [Parameter(HelpMessage = 'Tries to Suppress System Prompts wherever possible')]
    [switch]$EnableSharedVoicemailSystemPromptSuppression,

    [Parameter(HelpMessage = 'Enables ForceListen on CallFlow Objects')]
    [boolean]$ForceListenMenuEnabled,

    [Parameter(HelpMessage = 'UPN of one or more Users')]
    [ValidateScript( {
        If ($_ -match '@' -or $_ -match $script:TFMatchGuid) { $True } else {
          throw [System.Management.Automation.ValidationMetadataException] 'Value must be a valid UPN or ObjectId'
        } })]
    [string[]]$AuthorizedUsers,

    [Parameter(HelpMessage = 'Gender of the Voice')]
    [ValidateSet('Female', 'Male')]
    [string]$VoiceId,

    [Parameter(HelpMessage = 'By default, no output is generated, PassThru will display the Object changed')]
    [switch]$PassThru

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

    #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

    # Preparing Splatting Object for New-TeamsCallableEntity
    $TeamsCallableEntityParams = @{ 'ErrorAction' = 'Stop' }
    if ( $PSBoundParameters.ContainsKey('EnableTranscription') ) { $TeamsCallableEntityParams += @{ EnableTranscription = $true } }
    if ( $PSBoundParameters.ContainsKey('EnableSharedVoicemailSystemPromptSuppression') ) { $TeamsCallableEntityParams += @{ EnableSharedVoicemailSystemPromptSuppression = $true } }

  } #begin

  process {
    Write-Verbose -Message "[PROCESS] $($MyInvocation.MyCommand)"
    #region PREPARATION
    $ActivityID0 = 'Validation'
    $StatusID0 = 'Querying Object'
    # preparing Splatting Object
    $Parameters = $null

    #region Query Unique Element
    $CurrentOperationID0 = "Finding unique result for provided Name '$Name'"
    Write-BetterProgress -Id 0 -Activity $ActivityID0 -Status $StatusID0 -CurrentOperation $CurrentOperationID0 -Step ($private:CountID0++) -Of $private:StepsID0
    if ( $Name -match $script:TFMatchGuid ) {
      #Identity or ObjectId
      Write-Verbose -Message "[PROCESS] $($MyInvocation.MyCommand) - ID - '$Name'"
      $AAInstance = Get-CsAutoAttendant -Identity "$Name" -WarningAction SilentlyContinue -ErrorAction SilentlyContinue
    }
    else {
      #Name
      Write-Verbose -Message "[PROCESS] $($MyInvocation.MyCommand) - Name - '$Name'"
      # Initial Query to determine unique result (single object)
      $AAInstance = Get-CsAutoAttendant -NameFilter "$Name" -WarningAction SilentlyContinue
      $AAInstance = $AAInstance | Where-Object Name -EQ "$Name"
    }

    if ($null -eq $AAInstance) {
      Write-Error "'$Name' No Object found" -Category ParserError -RecommendedAction "Please check 'Name' provided" -ErrorAction Stop
    }
    elseif ($AAInstance.GetType().BaseType.Name -eq 'Array') {
      Write-Error "'$Name' Multiple Results found! Cannot determine unique result." -Category ParserError -RecommendedAction 'Please provide Auto Attendant GUID instead of name' -ErrorAction Stop
    }
    else {
      $AAID = $AAInstance.Identity
      Write-Information "INFO: '$Name' Auto Attendant found: Identity: $AAID"
      if ($PSBoundParameters.ContainsKey('Debug') -or $DebugPreference -eq 'Continue') {
        " Function: $($MyInvocation.MyCommand.Name) - Auto Attendant", ($AAInstance | Format-Table -AutoSize | Out-String).Trim() | Write-Debug
      }
    }
    #endregion

    #region DisplayName
    $CurrentOperationID0 = 'DisplayName'
    Write-BetterProgress -Id 0 -Activity $ActivityID0 -Status $StatusID0 -CurrentOperation $CurrentOperationID0 -Step ($private:CountID0++) -Of $private:StepsID0
    # Normalising $DisplayName
    if ( $PSBoundParameters.ContainsKey('DisplayName') ) {
      $NameNormalised = Format-StringForUse -InputString "$DisplayName" -As DisplayName
      $AAInstance.Name = "$NameNormalised"
      Write-Information "INFO: Pending Application: DisplayName changed from '$Name' to '$NameNormalised'"
      Write-Warning -Message 'Future Calls to this Auto Attendant must be against this name (or using the Identity)'
    }
    #endregion

    # Defining Name for reference below
    $AAName = "$($AAInstance.Name)"
    #endregion


    #region SETTINGS
    #NOTE All Options in Region Settings use ID 0 for showing progress
    #region TimeZone
    $CurrentOperationID0 = 'Processing TimeZone'
    Write-BetterProgress -Id 0 -Activity $ActivityID0 -Status $StatusID0 -CurrentOperation $CurrentOperationID0 -Step ($private:CountID0++) -Of $private:StepsID0
    if ( $PSBoundParameters.ContainsKey('TimeZone') ) {
      Write-Verbose -Message "TimeZone - Parsing TimeZone '$TimeZone'"
      if ($TimeZone -eq 'UTC') {
        $TimeZoneId = $TimeZone
      }
      else {
        # Query of Supported TimeZones based on previously calculated global Variable
        if ( -not $global:CsAutoAttendantSupportedTimeZone ) { $global:CsAutoAttendantSupportedTimeZone = Get-CsAutoAttendantSupportedTimeZone -WarningAction SilentlyContinue }
        $TimeZoneId = ($global:CsAutoAttendantSupportedTimeZone | Where-Object DisplayName -Like "($TimeZone)*" | Select-Object -First 1).Id
        Write-Verbose -Message "TimeZone - Found! Using: '$TimeZoneId'"
        Write-Information 'TimeZone - This is a correct match for the Time Zone, but might not be fully precise. - Please fine-tune Time Zone in the Admin Center if needed.'
      }

      if ( $TimeZoneId ) {
        $AAInstance.TimeZoneId = $TimeZoneId
        Write-Information "INFO: Pending Application: TimeZone set to: '$TimeZoneId'"
      }
      else {
        Write-Warning -Message 'TimeZone - Error determining TimeZoneId - skipped'
      }
    }
    #endregion

    #region Langauge
    $CurrentOperationID0 = 'Processing Language'
    Write-BetterProgress -Id 0 -Activity $ActivityID0 -Status $StatusID0 -CurrentOperation $CurrentOperationID0 -Step ($private:CountID0++) -Of $private:StepsID0
    if ( $PSBoundParameters.ContainsKey('Language') ) {
      # Language has to be normalised as the Id is case sensitive. Default value: en-US
      $Language = $($LanguageId.Split('-')[0]).ToLower() + '-' + $($LanguageId.Split('-')[1]).ToUpper()
      Write-Verbose "LanguageId '$LanguageId' normalised to '$Language'"

      $AAInstance.LanguageId = $Language
      Write-Information "INFO: Pending Application: Language set to: '$Language'"
    }
    else {
      $Language = $AAInstance.LanguageId
    }
    #endregion

    #region Voice capabilities
    # Query of Supported Languages based on previously calculated global Variable
    if ( -not $global:CsAutoAttendantSupportedLanguage ) { $global:CsAutoAttendantSupportedLanguage = Get-CsAutoAttendantSupportedLanguage -WarningAction SilentlyContinue }
    $LanguageRecord = $global:CsAutoAttendantSupportedLanguage | Where-Object Id -EQ "$Language"

    $CurrentOperationID0 = 'Processing EnableVoiceResponse, VoiceId & ForceListenMenuEnabled'
    Write-BetterProgress -Id 0 -Activity $ActivityID0 -Status $StatusID0 -CurrentOperation $CurrentOperationID0 -Step ($private:CountID0++) -Of $private:StepsID0
    if ($PSBoundParameters.ContainsKey('EnableVoiceResponse') -and $EnableVoiceResponse ) {
      $VoiceResponsesSupported = $LanguageRecord.VoiceResponseSupported

      # EnableVoiceResponse
      if ( $VoiceResponsesSupported ) {
        Write-Verbose -Message "'$AAName' EnableVoiceResponse - Voice Responses are supported with Language '$Language' and EnableVoiceResponse will be set to: $EnableVoiceResponse"
        $AAInstance.VoiceResponseEnabled = $EnableVoiceResponse
        Write-Information "INFO: Pending Application: VoiceResponseEnabled set to: '$EnableVoiceResponse'"
      }
      else {
        Write-Warning -Message "'$AAName' EnableVoiceResponse - Voice Responses are not supported for Language '$Language' and cannot be activated (Switch 'EnableVoiceResponse' will be omitted)"
      }
    }

    # VoiceId
    if ( $PSBoundParameters.ContainsKey('VoiceId') ) {
      $Voices = ($LanguageRecord.Voices | Where-Object Id -EQ "$VoiceId")
      if ( $Voices.Count -gt 0 ) {
        Write-Verbose -Message "'$AAName' VoiceId - Voice with gender '$VoiceId' found for '$Language'" -Verbose
        $AAInstance.VoiceId = $Voices[0]
        Write-Information "INFO: Pending Application: VoiceId set to: '$($Voices[0].Name)' (NOTE: This is experimental in v4.7.0)"
      }
      else {
        Write-Verbose -Message "'$AAName' VoiceId - No Voice with this gender found for '$Language' (using default Voice)" -Verbose
      }
    }

    # VoiceId
    if ( $PSBoundParameters.ContainsKey('ForceListenMenuEnabled') ) {


      if ( $ForceListenMenuEnabled ) {
        $AAInstance.DefaultCallFlow.ForceListenMenuEnabled = $ForceListenMenuEnabled
        Write-Information "INFO: Pending Application: Default CallFlow changed: ForceListenMenuEnabled set to: $ForceListenMenuEnabled"

        $AAInstance.CallFlows | ForEach-Object {
          $_.ForceListenMenuEnabled = $ForceListenMenuEnabled
          Write-Information "INFO: Pending Application: CallFlow '$($_.Name)' changed: ForceListenMenuEnabled set to: $ForceListenMenuEnabled"
        }
      }

      $Voices = ($LanguageRecord.Voices | Where-Object Id -EQ "$VoiceId")
      if ( $Voices.Count -gt 0 ) {
        Write-Verbose -Message "'$AAName' VoiceId - Voice with gender '$VoiceId' found for '$Language'" -Verbose
        $AAInstance.VoiceId = $Voices[0]
        Write-Information "INFO: Pending Application: VoiceId set to: '$($Voices[0].Name)' (NOTE: This is experimental in v4.7.0)"
      }
      else {
        Write-Verbose -Message "'$AAName' VoiceId - No Voice with this gender found for '$Language' (using default Voice)" -Verbose
      }
    }
    #endregion

    #region Operator
    $CurrentOperationID0 = 'Operator'
    Write-BetterProgress -Id 0 -Activity $ActivityID0 -Status $StatusID0 -CurrentOperation $CurrentOperationID0 -Step ($private:CountID0++) -Of $private:StepsID0
    if ($PSBoundParameters.ContainsKey('Operator')) {
      try {
        $OperatorEntity = New-TeamsCallableEntity -Identity "$Operator" @TeamsCallableEntityParams
        if ($OperatorEntity) {
          $AAInstance.Operator = $OperatorEntity
          Write-Information "INFO: Pending Application: Operator set to: '$Operator'"
        }
      }
      catch {
        Write-Warning -Message 'Operator - Error creating Call Target - skipped'
      }
    }
    #endregion

    #region Inclusion and Exclusion Scope
    $CurrentOperationID0 = 'Dial Scopes - Inclusion & Exclusion Scope'
    Write-BetterProgress -Id 0 -Activity $ActivityID0 -Status $StatusID0 -CurrentOperation $CurrentOperationID0 -Step ($private:CountID0++) -Of $private:StepsID0
    # Inclusion Scope
    if ($PSBoundParameters.ContainsKey('InclusionScope')) {
      Write-Verbose -Message "'$AAName' InclusionScope provided. Using as-is"
      $AAInstance.DirectoryLookupScope.InclusionScope = $InclusionScope
      Write-Information "INFO: Pending Application: InclusionScope set to Type: '$($InclusionScope.Type)'"
    }
    else {
      #Scope is optional
      Write-Verbose -Message "'$AAName' InclusionScope not defined. To create one, please run New-TeamsAutoAttendantDialScope or New-CsAutoAttendantDialScope"
    }

    # Exclusion Scope
    if ($PSBoundParameters.ContainsKey('ExclusionScope')) {
      Write-Verbose -Message "'$AAName' ExclusionScope provided. Using as-is"
      $AAInstance.DirectoryLookupScope.ExclusionScope = $ExclusionScope
      Write-Information "INFO: Pending Application: ExclusionScope set to Type: '$($ExclusionScope.Type)'"
    }
    else {
      #Scope is optional
      Write-Verbose -Message "'$AAName' ExclusionScope not defined. To create one, please run New-TeamsAutoAttendantDialScope or New-CsAutoAttendantDialScope"
    }
    #endregion

    #region AuthorizedUsers - Parsing and verifying AuthorizedUsers
    $CurrentOperationID0 = 'Parsing AuthorizedUsers'
    Write-BetterProgress -Id 0 -Activity $ActivityID0 -Status $StatusID0 -CurrentOperation $CurrentOperationID0 -Step ($private:CountID0++) -Of $private:StepsID0
    if ($PSBoundParameters.ContainsKey('AuthorizedUsers')) {
      Write-Verbose -Message "'$AAName' - Parsing AuthorizedUsers"
      [System.Collections.Generic.List[object]]$AuthorizedUserIdList = @()
      foreach ($User in $AuthorizedUsers) {
        $Assertion = $null
        $CallTarget = $null
        $CallTarget = Get-TeamsCallableEntity -Identity "$User"
        if ( $CallTarget.ObjectType -ne 'User') {
          Write-Warning -Message "'$AAName' Object '$User' is not a User, omitting Object!"
          continue
        }
        try {
          # Asserting Object - Validation of Type
          $Assertion = Assert-TeamsCallableEntity -UserPrincipalName "$($CallTarget.Entity)" -RequireEV -WarningAction SilentlyContinue -ErrorAction Stop
          if ( $Assertion ) {
            Write-Information "INFO: User '$User' will be added as an Authorized User"
            [void]$AuthorizedUserIdList.Add($CallTarget.Identity)
          }
          else {
            Write-Warning -Message "'$AAName' Object '$User' not found or in unusable state, omitting Object!"
            continue
          }
        }
        catch {
          Write-Warning -Message "'$AAName' Object '$User' not in correct state or not enabled for Enterprise Voice, omitting Object!"
          Write-Debug "Exception: $($_.Exception.Message)"
          continue
        }
      }

      if ($AuthorizedUserIdList.Count -gt 0 -and $AuthorizedUserIdList -ne '') {
        # If Object is an Empty String, Count will be 1
        Write-Verbose -Message "'$AAName' AuthorizedUsers: Adding $($AuthorizedUserIdList.Count) Users as Managers to the Auto Attendant" -Verbose
        #TEST AuthorizedUsers may suffer from 'single object trauma'
        #$Parameters += @{'AuthorizedUsers' = if ( $AuthorizedUserIdList.Count -eq 1 ) { "$AuthorizedUserIdList" } else { @($AuthorizedUserIdList) } }
        $AAInstance.AuthorizedUsers = $AuthorizedUserIdList
        Write-Information "INFO: Pending Application: AuthorizedUsers changed: $($AuthorizedUserIdList.Count) Users set"

      }
    }
    #endregion
    #endregion


    #region ACTION
    #Preparing Splatting Object
    $Parameters = @{
      'Instance'      = $AAInstance
      'WarningAction' = if ( $PSBoundParameters.ContainsKey('WarningAction') ) { $PSCmdlet.SessionState.PSVariable.GetValue('WarningAction') } else { 'Continue' }
      'ErrorAction'   = if ( $PSBoundParameters.ContainsKey('ErrorAction') ) { $PSCmdlet.SessionState.PSVariable.GetValue('ErrorAction') } else { 'Stop' }
    }

    Write-Verbose -Message '[PROCESS] Updating Auto Attendant'
    if ($PSBoundParameters.ContainsKey('Debug') -or $DebugPreference -eq 'Continue') {
      " Function: $($MyInvocation.MyCommand.Name) - Parameters (Set-CsAutoAttendant)", ($Parameters | Format-Table -AutoSize | Out-String).Trim() | Write-Debug
    }

    # Updating Auto Attendant (Set-CsAutoAttendant)
    $StatusID0 = 'Updating Auto Attendant'
    $CurrentOperationID0 = "'$AAName'"
    Write-BetterProgress -Id 0 -Activity $ActivityID0 -Status $StatusID0 -CurrentOperation $CurrentOperationID0 -Step ($private:CountID0++) -Of $private:StepsID0
    if ($PSCmdlet.ShouldProcess("$AAName", 'Set-CsAutoAttendant')) {
      try {
        # Create the Auto Attendant with all enumerated Parameters passed through splatting
        $null = (Set-CsAutoAttendant @Parameters)
        #Write-Information "INFO: All pending changes applied to Auto Attendant Instance for '$AAName'"
        Write-Host "INFO: All pending changes applied to Auto Attendant Instance for '$AAName'" -ForegroundColor Magenta
      }
      catch {
        Write-Error -Message "Error updating the Auto Attendant: $($_.Exception.Message)" -Category InvalidResult
        return
      }
    }
    else {
      return
    }
    #endregion


    #region OUTPUT
    if ( $PassThru ) {
      $StatusID0 = 'Validation/PassThru'
      $CurrentOperationID0 = 'Re-Querying Object'
      Write-BetterProgress -Id 0 -Activity $ActivityID0 -Status $StatusID0 -CurrentOperation $CurrentOperationID0 -Step ($private:CountID0++) -Of $private:StepsID0
      $AAFinal = Get-TeamsAutoAttendant -Name $AAInstance.Identity -WarningAction SilentlyContinue
      Write-Output $AAFinal
    }
    #endregion
    Write-Progress -Id 0 -Activity $ActivityID0 -Completed

  } #process

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

  } #end
} # Update-TeamsAutoAttendant