Public/AutoAttendant/Update-TeamsAutoAttendantHoliday.ps1

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

#TODO Change Parameters to have the prefix removed as it is clear that it is BusinessHours/AfterHours/HolidaySet anyway?


function Update-TeamsAutoAttendantHoliday {
  <#
  .SYNOPSIS
    Changing, amending or replacing the Holiday 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 HolidaySetGreeting
    Optional. Creates a Greeting for the Holiday Set Call Flow utilising New-TeamsAutoAttendantPrompt
    A supported Audio File or a text string that is parsed by the text-to-voice engine in the Language specified
    The last 4 digits will determine the type. For an AudioFile they are expected to be the file extension: '.wav', '.wma' or '.mp3'
    If no Schedule is provided, this setting will be applied to all Holiday Call Flows
  .PARAMETER HolidaySetCallFlowOption
    Optional. Disconnect, TransferCallToTarget, Menu.
    TransferCallToTarget requires HolidaySetCallTarget. Menu requires HolidaySetMenu
    If no Schedule is provided, this setting will be applied to all Holiday Call Flows
  .PARAMETER HolidaySetCallTarget
    Optional. Requires HolidaySetCallFlowOption to be TransferCallToTarget. Creates a Callable entity for this Call Target
    Expected are UserPrincipalName (User, ResourceAccount), a TelURI (ExternalPstn), an Office 365 Group Name (SharedVoicemail)
    If no Schedule is provided, this setting will be applied to all Holiday Call Flows
  .PARAMETER HolidaySetMenu
    Optional. Requires HolidaySetCallFlowOption to be Menu and a HolidaySetCallTarget
    If no Schedule is provided, this setting will be applied to all Holiday Call Flows
  .PARAMETER HolidaySetSchedule
    Optional. Default Schedule to apply: Either a 2-digit Country Code to create the schedule for the next three years for,
    a Schedule Object created beforehand or an existing Schedule Object ID already created in the Tenant
    If not provided, an alternative behavior is triggered. Please see Notes for details!
  .PARAMETER SetMenuOption
    Sets or replaces a Menu Option with the provided Menu Options Object.
  .PARAMETER Replace
    Replaces all Holiday Call Flows with one created through this Cmdlet. If not provided, will add a Call Flow instead.
  .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 PassThru
    Optional. Displays Auto Attendant Object after action.
  .PARAMETER Force
    Suppresses confirmation prompt where applicable. Handle with care!
  .EXAMPLE
    Update-TeamsAutoAttendantHoliday -Name "My Auto Attendant" -HolidayCallFlowOption TransferCallToTarget -HolidayCallTarget User@domain.com -EnableTranscription
 
    Changes the Auto Attendant "My Auto Attendant", adding a Holiday Call Flow to TransferCallToTarget, forwarding
    the call to the User User@domain.com and instructs the Command to enable transcription for all eligible call targets
    This only is available for SharedVoicemail so will be ignored for this command.
    As no HolidaySetSchedule is provided, this will create an empty schedule that is never in effect.
    Please amend manually in the Admin Center afterwards
  .EXAMPLE
    Update-TeamsAutoAttendantHoliday -Name "My Auto Attendant" -HolidayGreeting "Welcome to Contoso" -HolidayCallFlowOption Menu -HolidayMenu $MenuObject -HolidaySetSchedule US
 
    Changes the Auto Attendant "My Auto Attendant", adding a Holiday Call Flow to Menu, forwarding
    the call to the Menu provided with the MenuObject and also changing the Greeting to the Text-to-voice string provided.
    This will create a new Schedule for public Holidays for the US for the current year and the following two years
    and apply all provided settings to each HolidaySet
  .INPUTS
    System.String
  .OUTPUTS
    System.Object
  .NOTES
    Provides an alternative to Set-CsAutoAttendant, allowing for some options to be replaced more easily.
    This CmdLet has two main functions:
    With the HolidaySetSchedule parameter it can generate a new Call Flow and either add it or replace all existing Flows.
    Without the HolidaySetSchedule parameter it can replace existing functionality on ALL Holiday Call Flows.
    Handle with care.
  .COMPONENT
    TeamsAutoAttendant
  .FUNCTIONALITY
    Creates a Auto Attendant with custom settings and friendly names as input
  .LINK
    https://github.com/DEberhardt/TeamsFunctions/tree/main/docs/Update-TeamsAutoAttendantHoliday.md
  .LINK
    https://github.com/DEberhardt/TeamsFunctions/tree/main/docs/about_TeamsAutoAttendant.md
  .LINK
    https://github.com/DEberhardt/TeamsFunctions/tree/main/docs/
  #>


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

    [Parameter(HelpMessage = 'Holiday Set Greeting - Text String or Recording')]
    [ArgumentCompleter( { '<Your Text-to-speech-string>', 'C:\Temp\' })]
    [string]$HolidaySetGreeting,

    [Parameter(Mandatory, ParameterSetName = 'Schedule', HelpMessage = 'Holiday Set Call Flow Option')]
    [Parameter(Mandatory, ParameterSetName = 'CallTarget', HelpMessage = 'Holiday Set Call Flow Option')]
    [ValidateSet('Disconnect', 'TransferCallToTarget', 'Menu')]
    [string]$HolidaySetCallFlowOption,

    [Parameter(ParameterSetName = 'Schedule', HelpMessage = 'Holiday Set Call Target - HolidaySetCallFlowOption = TransferCallToTarget')]
    [Parameter(Mandatory, ParameterSetName = 'CallTarget', HelpMessage = 'Holiday Set Call Target - HolidaySetCallFlowOption = TransferCallToTarget')]
    [string]$HolidaySetCallTarget,

    [Parameter(ParameterSetName = 'Schedule', HelpMessage = 'Holiday Set Call Target - HolidaySetCallFlowOption = Menu')]
    [Parameter(Mandatory, ParameterSetName = 'Menu', HelpMessage = 'Holiday Set Call Target - HolidaySetCallFlowOption = Menu')]
    [object]$HolidaySetMenu,

    [Parameter(Mandatory, ParameterSetName = 'Schedule', HelpMessage = 'Default Schedule to apply, can be a 2-digit CountryCode a ScheduleObject or an ID of one')]
    $HolidaySetSchedule,

    [Parameter(ParameterSetName = 'Schedule', HelpMessage = 'Replaces all Holiday Flows with the current. If not provided, will add a Flow instead')]
    [switch]$Replace,

    [Parameter(Mandatory, ParameterSetName = 'MenuOption', HelpMessage = 'MenuOptions Object to be added or replaced')]
    [object[]]$SetMenuOption,

    [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 = 'By default, no output is generated, PassThru will display the Object changed')]
    [switch]$PassThru,

    [Parameter(HelpMessage = 'Suppresses confirmation prompt whereever applicable')]
    [switch]$Force

  ) #param

  begin {
    Show-FunctionStatus -Level RC
    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

    $StatusID0 = 'Verifying input'
    $CurrentOperationID0 = 'Validating CallFlows'
    Write-BetterProgress -Id 0 -Activity $ActivityID0 -Status $StatusID0 -CurrentOperation $CurrentOperationID0 -Step ($private:CountID0++) -Of $private:StepsID0
    #region Processing HolidaySet Parameters
    if ( $PSBoundParameters.ContainsKey('HolidaySetCallFlowOption') ) {
      switch ($HolidaySetCallFlowOption) {
        'TransferCallToTarget' {
          # Must contain Target
          if (-not $PSBoundParameters.ContainsKey('HolidaySetCallTarget')) {
            Write-Error -Message "HolidaySetCallFlowOption (TransferCallToTarget) - Parameter 'HolidaySetCallTarget' missing"
            break
          }

          # Must not contain a Menu
          if ($PSBoundParameters.ContainsKey('HolidaySetMenu')) {
            Write-Verbose -Message 'HolidaySetCallFlowOption (TransferCallToTarget) - Parameter HolidaySetMenu cannot be used and will be omitted!' -Verbose
            $PSBoundParameters.Remove('HolidaySetMenu')
          }
        }

        'Menu' {
          # Must contain a Menu
          if (-not $PSBoundParameters.ContainsKey('HolidaySetMenu')) {
            Write-Error -Message 'HolidaySetCallFlowOption (Menu) - HolidaySetMenu missing'
            break
          }
          else {
            if (($HolidaySetMenu | Get-Member | Select-Object -First 1).TypeName -notmatch 'Microsoft.Rtc.Management.Hosted.OAA.Models.Menu') {
              Write-Error -Message "HolidaySetCallFlowOption (Menu) - HolidaySetMenu not of the Type 'Microsoft.Rtc.Management.Hosted.OAA.Models.Menu'" -Category InvalidType
              break
            }
          }

          # Must not contain Target
          if ($PSBoundParameters.ContainsKey('HolidaySetCallTarget')) {
            Write-Verbose -Message "HolidaySetCallFlowOption (Menu) - Parameter 'HolidaySetCallTarget' cannot be used and will be omitted!"-Verbose
            $PSBoundParameters.Remove('HolidaySetCallTarget')
          }
        }

        'Disconnect' {
          # Must not contain a Menu
          if ($PSBoundParameters.ContainsKey('HolidaySetMenu')) {
            Write-Verbose -Message 'HolidaySetCallFlowOption (Disconnect) - Parameter HolidaySetMenu cannot be used and will be omitted!' -Verbose
            $PSBoundParameters.Remove('HolidaySetMenu')
          }

          # Must not contain Target
          if ($PSBoundParameters.ContainsKey('HolidaySetCallTarget')) {
            Write-Verbose -Message "HolidaySetCallFlowOption (Disconnect) - Parameter 'HolidaySetCallTarget' cannot be used and will be omitted!"-Verbose
            $PSBoundParameters.Remove('HolidaySetCallTarget')
          }
        }
      }
    }
    #endregion

    # 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
    $StatusID0 = 'Querying Object'

    #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

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


    #region SETTINGS
    #NOTE All Options in Region Settings use ID 1 for showing progress
    $ActivityID1 = 'Holiday Call Flow'
    $StatusID1 = 'Applying Settings'

    #region Finding Artefacts
    $AllCHA = $AAInstance.CallHandlingAssociations
    $AllCallFlows = $AAInstance.CallFlows
    if ( $PSBoundParameters.ContainsKey('Replace') ) {
      Write-Verbose -Message "Parameter 'Replace' - Replacing all existing Call Flows & Call Handling Associations"
      Write-Warning -Message 'This operation is performed against the InMemory Object'
      Write-Information 'INFO: This will only be applied to the Auto Attendant if Set-CsAutoAttendant is called at the end!'
      # Finding All Non-Holiday Call Flows
      $NonHolidayCHA = $AllCHA | Where-Object Type -NE 'Holiday'
      $NonHolidayCFs = $AllCallFlows | Where-Object Id -EQ $NonHolidayCHA.CallFlowId
      if ($PSBoundParameters.ContainsKey('Debug')) {
        " Function: $($MyInvocation.MyCommand.Name) - CFs before removal:", ($AAInstance.CallFlows | Format-Table -AutoSize | Out-String).Trim() | Write-Debug
        " Function: $($MyInvocation.MyCommand.Name) - CHA before removal:", ($AAInstance.CallHandlingAssociations | Format-Table -AutoSize | Out-String).Trim() | Write-Debug
      }

      if ( $Force -or $PSCmdlet.ShouldProcess("$AAName", 'Remove existing Holiday Call Flows & Call Handling Associations') ) {
        Write-Verbose -Message 'Resetting CFs to only AfterHours CFs'
        $AAInstance.CallFlows = @($NonHolidayCFs)
        Write-Verbose -Message 'Resetting CHA to only AfterHours CHA'
        $AAInstance.CallHandlingAssociations = @($NonHolidayCHA)
        Write-Information 'INFO: Pending Application: Holiday CallFlow changed: Other Call Flows removed'
        Write-Information 'INFO: Pending Application: Call Handling Associations changed: Other CHA removed'
      }
    }
    if ($PSBoundParameters.ContainsKey('Debug')) {
      " Function: $($MyInvocation.MyCommand.Name) - CFs:", ($AAInstance.CallFlows | Format-Table -AutoSize | Out-String).Trim() | Write-Debug
      " Function: $($MyInvocation.MyCommand.Name) - CHA:", ($AAInstance.CallHandlingAssociations | Format-Table -AutoSize | Out-String).Trim() | Write-Debug
    }
    #endregion

    #region Processing HolidaySetSchedule
    if ( $PSBoundParameters.ContainsKey('HolidaySetSchedule') ) {
      if ( $HolidaySetSchedule -match $script:TFMatchGuid ) {
        # Holiday Schedule provided as ID of existing Schedule in the Tenant - Taken as is.
        $HolidaySchedule = @{
          Id   = $HolidaySetSchedule
          Name = 'Holiday'
        }
      }
      elseif ( $HolidaySetSchedule.Id -match $script:TFMatchGuid) {
        # Holiday Schedule provided as Schedule Object in the Tenant
        $HolidaySchedule = $HolidaySetSchedule
      }
      elseif ( $HolidaySetSchedule -match '^[a-z][a-z]$') {
        # Holiday Schedule provided is a Country for which a Schedule Object will be created
        [int]$CurrentYear = Get-Date -Format yyyy
        $Year = @($CurrentYear, $($CurrentYear + 1), $($CurrentYear + 2))
        $HolidaySchedule = New-TeamsHolidaySchedule -CountryCode $HolidaySetSchedule -Year $Year
      }
      else {
        Write-Warning -Message 'HolidaySchedule provided does not match an ID, Object or CountryCode! Creating empty Schedule'
        $HolidaySchedule = $null
      }
    }
    else {
      Write-Warning -Message 'HolidaySchedule not provided! Triggering alternative Flow'
      $HolidaySchedule = $null
    }
    #endregion

    #region HolidaySet Call Flow
    if ( $HolidaySchedule ) {
      if ($PSBoundParameters.ContainsKey('Debug') -or $DebugPreference -eq 'Continue') {
        " Function: $($MyInvocation.MyCommand.Name) - Holiday Schedules (current)", ($AAInstance.Schedules | Format-Table -AutoSize | Out-String).Trim() | Write-Debug
      }

      # Preparing Call Flow String (to adhere to 64 Character limit)
      $RandomString = '{0:d4}' -f $(Get-Random -Minimum 0000 -Maximum 9999)
      if ($AAName.length -gt 40) {
        Write-Verbose 'Auto Attendant Name is too long and cannot be used for Call Flow Name(s) as-is. Name will be shortened'
        $CallFlowNamePrefix = -join "$AAName"[0..38]
        $CallFlowNamePrefix = $CallFlowNamePrefix + $RandomString
      }
      else {
        $CallFlowNamePrefix = "$AAName"
      }
      Write-Verbose "Auto Attendant Call Flow Name Prefix used: '$CallFlowNamePrefix'"

      # Processing HolidaySetsCallFlowOption
      $private:StepsID1 = $HolidaySchedule.Count
      foreach ( $HolSchedule in $HolidaySchedule ) {
        $CurrentOperationID1 = "Processing Holiday Schedule $($HolSchedule.Name)"
        Write-BetterProgress -Id 1 -Activity $ActivityID1 -Status $StatusID1 -CurrentOperation $CurrentOperationID1 -Step ($private:CountID1) -Of $private:StepsID1
        # Validating Holiday Schedule Name is not currently used
        if ( $HolSchedule.Name -in $AAInstance.Schedules.Name ) {
          $HolSchedule.Name = "$($HolSchedule.Name) $(Get-Date -Format 'dd-MMM-yyyy HH:mm:ss')"
          Write-Information "INFO: Holiday Schedule Name amended to allow processing (Name must be unique): $($HolSchedule.Name)"
        }

        # Initialising Variables for Call Handling Association
        $HolidaySetCallHandlingAssociationParams = @{}
        $HolidaySetCallHandlingAssociationParams.Type = 'Holiday'
        if ($PSBoundParameters.ContainsKey('Debug') -or $DebugPreference -eq 'Continue') {
          " Function: $($MyInvocation.MyCommand.Name) - Holiday Schedule (to add)", ($HolSchedule | Format-Table -AutoSize | Out-String).Trim() | Write-Debug
        }
        $AAInstance.Schedules += $HolSchedule
        Write-Information 'INFO: Pending Application: Holiday Schedule changed: New Schedule added'

        # Option Selected
        Write-Verbose -Message "'$AAName' CallFlow - No Custom Object - Processing 'HolidaySetCallFlowOption'..."
        $HolidaySetCallFlowParameters = @{}
        $HolidaySetCallFlowParameters.Name = "$CallFlowNamePrefix - $($HolSchedule.Name)"

        #region Processing HolidaySetCallFlowOption
        switch ($HolidaySetCallFlowOption) {
          'TransferCallToTarget' {
            Write-Verbose -Message "'$AAName' CallFlow - Transferring to Target"

            # Process HolidaySetCallTarget
            try {
              $HolidaySetCallTargetEntity = New-TeamsCallableEntity "$HolidaySetCallTarget" @TeamsCallableEntityParams
              if ( -not $HolidaySetCallTargetEntity) { throw } # Building Menu Only if Successful
              $HolidaySetMenuOptionTransfer = New-CsAutoAttendantMenuOption -Action TransferCallToTarget -CallTarget $HolidaySetCallTargetEntity -DtmfResponse Automatic
              $HolidaySetMenuObject = New-CsAutoAttendantMenu -Name 'Holiday Set Menu' -MenuOptions @($HolidaySetMenuOptionTransfer)
              Write-Information "INFO: '$AAName' Holiday Set Call Flow - Menu (TransferCallToTarget) created"
            }
            catch {
              Write-Warning -Message 'HolidaySetCallTarget - Error creating Call Target - Defaulting to disconnect'
              $HolidaySetMenuOptionDefault = New-CsAutoAttendantMenuOption -Action DisconnectCall -DtmfResponse Automatic
              $HolidaySetMenuObject = New-CsAutoAttendantMenu -Name 'Business Hours Menu' -MenuOptions @($HolidaySetMenuOptionDefault)
            }
          }

          'Menu' {
            Write-Verbose -Message "'$AAName' CallFlow - HolidaySetCallFlow - Menu"
            if ($PSBoundParameters.ContainsKey('HolidaySetMenu')) {
              # Menu is passed on as-is - $HolidaySetMenu is defined and attached
              $HolidaySetMenuObject = $HolidaySetMenu
              Write-Information "INFO: '$AAName' Holiday Set Call Flow - Menu set"
            }
            else {
              # No custom / default Menu is currently created
              # $HolidaySetMenu is Mandatory. If this is built out, the check against this must also be removed!
            }
          }

          'Disconnect' {
            Write-Verbose -Message "'$AAName' CallFlow - HolidaySetCallFlow not provided or Disconnect. Using Disconnect"
            $HolidaySetMenuOptionDefault = New-CsAutoAttendantMenuOption -Action DisconnectCall -DtmfResponse Automatic
            $HolidaySetMenuObject = New-CsAutoAttendantMenu -Name 'Holiday Set Menu' -MenuOptions @($HolidaySetMenuOptionDefault)
          }
        }
        #endregion

        #region HolidaySetGreeting
        if ($PSBoundParameters.ContainsKey('HolidaySetGreeting')) {
          Write-Verbose -Message "'$AAName' CallFlow - HolidaySetCallFlow - Greeting"
          try {
            $HolidaySetGreetingObject = New-TeamsAutoAttendantPrompt -String "$HolidaySetGreeting"
            if ($HolidaySetGreetingObject) {
              $HolidaySetCallFlowParameters.Greetings = @($HolidaySetGreetingObject)
              Write-Information 'INFO: Pending Application: Holiday CallFlow changed: Greeting'
            }
          }
          catch {
            Write-Warning -Message "'$AAName' CallFlow - HolidaySetCallFlow - Greeting not enumerated. Omitting Greeting"
          }
        }
        #endregion

        #region Building Call Flow
        Write-Verbose -Message "'$AAName' CallFlow - HolidaySetCallFlow - Building Call Flow"
        # Adding Holiday Set Call Flow
        $HolidaySetCallFlowParameters.Menu = $HolidaySetMenuObject
        # ForceListenMenuEnabled
        if ( $ForceListenMenuEnabled ) {
          $HolidaySetCallFlowParameters.ForceListenMenuEnabled = $ForceListenMenuEnabled
          Write-Information "INFO: '$AAName' Holiday Set Call Flow - ForceListenMenuEnabled set to: $ForceListenMenuEnabled"
        }

        if ($PSBoundParameters.ContainsKey('Debug') -or $DebugPreference -eq 'Continue') {
          " Function: $($MyInvocation.MyCommand.Name) - Parameters for HolidaySetCallFlow (New-CsAutoAttendantCallFlow)", ($HolidaySetCallFlowParameters | Format-Table -AutoSize | Out-String).Trim() | Write-Debug
        }
        $HolidaySetCallFlow = New-CsAutoAttendantCallFlow @HolidaySetCallFlowParameters
        Write-Verbose "Auto Attendant '$AAName' Holiday Set Call Flow - Call Flow created"
        $AAInstance.CallFlows += $HolidaySetCallFlow
        Write-Information 'INFO: Pending Application: Holiday CallFlow changed: New Holiday Call Flow added'

        # Adding Call Flow ID(s) to Call handling Associations
        $HolidaySetCallHandlingAssociationParams.CallFlowId = $HolidaySetCallFlow.Id # Adding singular Flow
        #endregion

        #region Holiday Set Schedule & Call Handling Association
        Write-Verbose -Message "'$AAName' CallFlow - HolidaySetCallFlow - Building Call Handling Association"
        $HolidaySetCallHandlingAssociationParams.ScheduleId = $HolSchedule.Id
        if ($PSBoundParameters.ContainsKey('Debug') -or $DebugPreference -eq 'Continue') {
          " Function: $($MyInvocation.MyCommand.Name) - Parameter for HolidaySetCallHandlingAssociation (New-CsAutoAttendantCallHandlingAssociation)", ($HolidaySetCallHandlingAssociationParams | Format-Table -AutoSize | Out-String).Trim() | Write-Debug
        }

        $HolidaySetCallHandlingAssociation = New-CsAutoAttendantCallHandlingAssociation @HolidaySetCallHandlingAssociationParams
        Write-Verbose "Auto Attendant '$AAName' Holiday Set Call Flow - Call Handling Association created with Holiday Schedule"

        $AAInstance.CallHandlingAssociations += $HolidaySetCallHandlingAssociation
        Write-Information 'INFO: Pending Application: Call Handling Association changed: New Holiday CHA added'
        #endregion

      }
    }
    else {
      Write-Verbose -Message 'HolidaySchedule not present or not enumerated. Processing alternative Path'
      $NonHolidayCHA = $AllCHA | Where-Object Type -NE 'Holiday'
      $HolidayCFs = $AllCallFlows | Where-Object Id -NE $NonHolidayCHA.CallFlowId

      $private:StepsID1 = $HolidayCFs.Count
      foreach ($CF in $HolidayCFs) {
        $CurrentOperationID1 = "Processing $($CF.Name)"
        Write-BetterProgress -Id 1 -Activity $ActivityID1 -Status $StatusID1 -CurrentOperation $CurrentOperationID1 -Step ($private:CountID1) -Of $private:StepsID1

        # HolidaySetGreeting
        if ( $PSBoundParameters.ContainsKey('HolidaySetGreeting') ) {
          Write-Verbose -Message "'$AAName' CallFlow '$($CF.Name)'- Processing 'HolidaySetGreeting'"
          if ( $Force -or $PSCmdlet.ShouldProcess("$AAName", "Replace Greeting for Call Flow '$($CF.Name)'") ) {
            try {
              $HolidaySetGreetingObject = New-TeamsAutoAttendantPrompt -String "$HolidaySetGreeting"
              if ($HolidaySetGreetingObject) {
                $CF.Greetings = $HolidaySetGreetingObject
                Write-Information 'INFO: Pending Application: Holiday CallFlows changed: Greeting'
              }
            }
            catch {
              Write-Warning -Message "'$AAName' CallFlow - HolidaySetCallFlow - Greeting not enumerated. Omitting Greeting"
            }
          }
        }

        # HolidaySetCallTarget
        if ( $PSBoundParameters.ContainsKey('HolidaySetCallTarget') ) {
          Write-Verbose -Message "'$AAName' CallFlow '$($CF.Name)'- Processing 'HolidaySetCallTarget'"
          if ( $Force -or $PSCmdlet.ShouldProcess("$AAName", "Replace Call Target for Call Flow '$($CF.Name)'") ) {
            try {
              $HolidaySetCallTargetEntity = New-TeamsCallableEntity "$HolidaySetCallTarget" @TeamsCallableEntityParams
              if ( -not $HolidaySetCallTargetEntity) { throw } # Building Menu Only if Successful
              $HolidaySetMenuOptionTransfer = New-CsAutoAttendantMenuOption -Action TransferCallToTarget -CallTarget $HolidaySetCallTargetEntity -DtmfResponse Automatic
              $HolidaySetMenuObject = New-CsAutoAttendantMenu -Name 'Holiday Set Menu' -MenuOptions @($HolidaySetMenuOptionTransfer)
              $CFs.Menu = $HolidaySetMenuObject
              Write-Information "INFO: '$AAName' Holiday Set Call Flow(s) - Menu (TransferCallToTarget) created"
            }
            catch {
              Write-Warning -Message 'HolidaySetCallTarget - Error creating Call Target - No changes made'
            }
          }
        }

        # HolidaySetMenu
        if ( $PSBoundParameters.ContainsKey('HolidaySetMenu') ) {
          Write-Verbose -Message "'$AAName' CallFlow '$($CF.Name)'- Processing 'HolidaySetMenu'"
          if ( $Force -or $PSCmdlet.ShouldProcess("$AAName", "Replace Menu for Call Flow '$($CF.Name)'") ) {
            # Menu is passed on as-is - $HolidaySetMenu is defined and attached
            $CFs.Menu = $HolidaySetMenu
            Write-Information "INFO: '$AAName' Holiday Set Call Flow(s) - Menu set"
          }
        }

        # HolidaySetMenuOption
        if ( $PSBoundParameters.ContainsKey('SetMenuOption') ) {
          Write-Verbose -Message "'$AAName' CallFlow '$($CF.Name)'- Processing 'SetMenuOption'"
          try {
            $NewMenu = $CF.Menu | Update-TeamsAutoAttendantMenu -MenuOptions $SetMenuOption
            $CF.Menu = $NewMenu
            Write-Information "INFO: '$AAName' Holiday Set Call Flow(s) - Menu - SetMenuOption applied"
          }
          catch {
            if ($_.Exception.Message.Contains('Menu option not applicable here')) {
              Write-Verbose -Message "'$AAName' Holiday Set Call Flow $($CF.Name) - Menu set to '$($CF.Menu.MenuOptions.Action)' - Menu option not applicable here. Skipping application"
            }
            else {
              Write-Error "Error Calling Update-TeamsAutoAttendantMenu: $($_.Exception.Message)"
            }
          }
        }

        # ForceListenMenuEnabled
        if ( $ForceListenMenuEnabled ) {
          $CF.ForceListenMenuEnabled = $ForceListenMenuEnabled
          Write-Information "INFO: '$AAName' Holiday Set Call Flow(s) - ForceListenMenuEnabled set to: $ForceListenMenuEnabled"
        }
      }
    }
    #endregion
    #endregion
    Write-Progress -Id 1 -Activity $ActivityID1 -Completed


    #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 ( $Force -or $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
        if ( $_.Exception.Message.contains('Auto attendant cannot contain multiple schedules with the same ID or name') ) {
          Write-Verbose -Message 'Schedules cannot be re-used. To do so, please use Export/Import-CsAutoAttendantHolidays or create a new Schedule with a unique name!' -Verbose
        }
        if ($PSBoundParameters.ContainsKey('Debug') -or $DebugPreference -eq 'Continue') {
          Write-Debug -Message 'Detailed output for all aspects of the Call Flow relevant to Holiday Call Flow'
          Write-Verbose 'Auto Attendant Instance Object' -Verbose
          $AAInstance
          Write-Verbose 'Auto Attendant Instance Object - Schedules' -Verbose
          $AAInstance.Schedules
          Write-Verbose 'Auto Attendant Instance Object - Call Flows' -Verbose
          $AAInstance.Callflows
          Write-Verbose 'Auto Attendant Instance Object - Call Handling Associations' -Verbose
          $AAinstance.CallHandlingAssociations
          Write-Debug -Message 'Detailed output for all aspects of the Call Flow relevant to Holiday Call Flow'
        }
        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 -Detailed -WarningAction SilentlyContinue
      #region Limiting output to Holiday Call Flows only
      [System.Collections.Generic.List[object]]$AAFinalCHA = $AAFinal.CallHandlingAssociations
      [System.Collections.Generic.List[object]]$AAFinalCFs = $AAFinal.CallFlows
      [System.Collections.Generic.List[object]]$AAFinalSchedule = $AAFinal.Schedules

      $AHCHA = $AAFinal.CallHandlingAssociations | Where-Object Type -EQ AfterHours
      $AHCF = $AAFinal.CallFlows | Where-Object Name -EQ $($AHCHA.CallFlow)
      $AHSC = $AAFinal.Schedules | Where-Object Name -EQ $($AHCHA.Schedule)

      [void]$AAFinalCHA.Remove($AHCHA)
      [void]$AAFinalCFs.Remove($AHCF)
      [void]$AAFinalSchedule.Remove($AHSC)

      $AAFinal.CallFlows = $AAFinalCFs
      $AAFinal.Schedules = $AAFinalSchedule
      $AAFinal.CallHandlingAssociations = $AAFinalCHA
      #endregion
      Write-Output $AAFinal | Select-Object Callflows, Schedules, Callhandlingassociations | Format-List
    }
    #endregion
    Write-Progress -Id 0 -Activity $ActivityID0 -Completed

  } #process

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

  } #end
} # Update-TeamsAutoAttendantHoliday