Public/AutoAttendant/New-TeamsAutoAttendant.ps1

# Module: TeamsFunctions
# Function: AutoAttendant
# Author: David Eberhardt
# Updated: 01-DEC-2020
# Status: Live

#TODO VoiceId Parameter is not yet functional!


function New-TeamsAutoAttendant {
  <#
  .SYNOPSIS
    Creating Auto Attendants with certain defaults or simplified input; Calling New-CsAutoAttendant
  .DESCRIPTION
    This script tries to simplify the creation of Auto Attendants by providing reasonable defaults and simplified input.
    It should be seen as an extension rather than a replacement of New-CsAutoAttendant.
    UserPrincipalNames can be provided instead of IDs, FileNames (FullName) can be provided instead of IDs
  .PARAMETER Name
    Name of the Auto Attendant. Name will be normalised (unsuitable characters are filtered)
    Used as the DisplayName - Visible in Teams
  .PARAMETER TimeZone
    Required. 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
    Required. 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 BusinessHoursGreeting
    Optional. Creates a Greeting for the Default Call Flow (during business hours) 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 DefaultCallFlow is provided, this parameter will be ignored.
  .PARAMETER BusinessHoursCallFlowOption
    Optional. Disconnect, TransferCallToTarget, Menu. Default is Disconnect.
    TransferCallToTarget requires BusinessHoursCallTarget. Menu requires BusinessHoursMenu
    If DefaultCallFlow is provided, this parameter will be ignored.
  .PARAMETER BusinessHoursCallTarget
    Optional. Requires BusinessHoursCallFlowOption 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 DefaultCallFlow is provided, this parameter will be ignored.
  .PARAMETER BusinessHoursMenu
    Optional. Requires BusinessHoursCallFlowOption to be Menu and expects an AutoAttendantMenuObject
    If DefaultCallFlow is provided, this parameter will be ignored.
  .PARAMETER AfterHoursGreeting
    Optional. Creates a Greeting for the After Hours 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 CallFlows and CallHandlingAssociations are provided, this parameter will be ignored.
  .PARAMETER AfterHoursCallFlowOption
    Optional. Disconnect, TransferCallToTarget, Menu. Default is Disconnect.
    TransferCallToTarget requires AfterHoursCallTarget. Menu requires AfterHoursMenu
    If CallFlows and CallHandlingAssociations are provided, this parameter will be ignored.
  .PARAMETER AfterHoursCallTarget
    Optional. Requires AfterHoursCallFlowOption 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 CallFlows and CallHandlingAssociations are provided, this parameter will be ignored.
  .PARAMETER AfterHoursMenu
    Optional. Requires AfterHoursCallFlowOption to be Menu and expects an AutoAttendantMenuObject
    If CallFlows and CallHandlingAssociations are provided, this parameter will be ignored.
  .PARAMETER AfterHoursSchedule
    Optional. Default Schedule to apply: One of: MonToFri9to5 (default), MonToFri8to12and13to18, Open24x7
    A more granular Schedule can be used with the Parameter -Schedule
    If CallFlows and CallHandlingAssociations are provided, this parameter will be ignored.
  .PARAMETER Schedule
    Optional. Custom Schedule object to apply for After Hours Call Flow
    Object created with New-TeamsAutoAttendantSchedule or New-CsAutoAttendantSchedule
    If CallFlows and CallHandlingAssociations are provided, this parameter will be ignored.
    Using this parameter to provide a Schedule Object will override the Parameter -AfterHoursSchedule
  .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 CallFlows and CallHandlingAssociations are provided, this parameter will be ignored.
  .PARAMETER HolidaySetCallFlowOption
    Optional. Disconnect, TransferCallToTarget, Menu. Default is Disconnect.
    TransferCallToTarget requires HolidaySetCallTarget. Menu requires HolidaySetMenu
    If CallFlows and CallHandlingAssociations are provided, this parameter will be ignored.
  .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 CallFlows and CallHandlingAssociations are provided, this parameter will be ignored.
  .PARAMETER HolidaySetMenu
    Optional. Requires HolidaySetCallFlowOption to be Menu and a HolidaySetCallTarget
    If CallFlows and CallHandlingAssociations are provided, this parameter will be ignored.
  .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 empty Schedule Object will be created which will never be in effect.
    If CallFlows and CallHandlingAssociations are provided, this parameter will be ignored.
  .PARAMETER DefaultCallFlow
    Optional. Call Flow Object to pass to New-CsAutoAttendant (used as the Default Call Flow)
    Using this parameter to define the default Call Flow overrides all -BusinessHours Parameters
  .PARAMETER CallFlows
    Optional. Call Flow Object to pass to New-CsAutoAttendant
    Using this parameter to define additional Call Flows overrides all -AfterHours & -HolidaySet Parameters
    Requires Parameter CallHandlingAssociations in conjunction
  .PARAMETER CallHandlingAssociations
    Optional. Call Handling Associations Object to pass to New-CsAutoAttendant
    Using this parameter to define additional Call Flows overrides all -AfterHours & -HolidaySet Parameters
    Requires Parameter CallFlows in conjunction
  .PARAMETER InclusionScope
    Optional. DialScope Object to pass to New-CsAutoAttendant
    Object created with New-TeamsAutoAttendantDialScope or New-CsAutoAttendantDialScope
  .PARAMETER ExclusionScope
    Optional. DialScope Object to pass to New-CsAutoAttendant
    Object created with New-TeamsAutoAttendantDialScope or New-CsAutoAttendantDialScope
  .PARAMETER EnableVoiceResponse
    Optional Switch to be passed to New-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 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 Force
    Suppresses confirmation prompt to enable Users for Enterprise Voice, if Users are specified
    Currently no other impact
  .EXAMPLE
    New-TeamsAutoAttendant -Name "My Auto Attendant"
 
    Creates a new Auto Attendant "My Auto Attendant" with Defaults
    TimeZone is UTC, Language is en-US and Schedule is Mon-Fri 9to5.
    Business hours and After Hours action is Disconnect
  .EXAMPLE
    New-TeamsAutoAttendant -Name "My Auto Attendant" -TimeZone UTC-05:00 -LanguageId pt-BR -AfterHoursSchedule MonToFri8to12and13to18 -EnableVoiceResponse
 
    Creates a new Auto Attendant "My Auto Attendant" and sets the TimeZone to UTC-5 and the language to Portuguese (Brazil)
    The Schedule of Mon-Fri 8to12 and 13to18 will be applied. Also enables VoiceResponses
  .EXAMPLE
    New-TeamsAutoAttendant -Name "My Auto Attendant" -Operator "tel:+1555123456"
 
    Creates a new Auto Attendant "My Auto Attendant" with default TimeZone and Language, but defines an Operator as a Callable Entity (Forward to Pstn)
  .EXAMPLE
    New-TeamsAutoAttendant -Name "My Auto Attendant" -BusinessHoursGreeting "Welcome to Contoso" -BusinessHoursCallFlowOption TransferCallToTarget -BusinessHoursCallTarget $CallTarget
 
    Creates a new Auto Attendant "My Auto Attendant" with defaults, but defines a Text-to-Voice Greeting, then forwards the Call to the Call Target.
    The CallTarget is queried based on input and created as required. UserPrincipalname for Users or ResourceAccount, Group Name for SharedVoicemail, provided as a string in the Variable $UPN
    This example is equally applicable to AfterHours.
  .EXAMPLE
    New-TeamsAutoAttendant -Name "My Auto Attendant" -DefaultCallFlow $DefaultCallFlow -CallFlows $CallFlows -CallHandlingAssociations $CallHandlingAssociations -InclusionScope $InGroups -ExclusionScope $OutGroups
 
    Creates a new Auto Attendant "My Auto Attendant" and passes through all objects provided.
    In this example, provided Objects are passed on through tto New-CsAutoAttendant and override other respective Parmeters provided:
    A DefaultCallFlow Object is passed on which overrides all "-BusinessHours"-Parmeters. One or more CallFlows and
    one or more CallHandlingAssociation Objects are passed on overriding all "-AfterHours" and "-HolidaySet" Parameters
    An InclusionScope and an ExclusionScope are defined. These are passed on as-is
    All other values, like Language and TimeZone are defined with their defaults and can still be defined with the Objects.
  .INPUTS
    System.String
  .OUTPUTS
    System.Object
  .NOTES
    BusinessHours Parameters aim to simplify input for the Default Call Flow
    AfterHours Parameters aim to simplify input for the After Hours Call Flow
    HolidaySet Parameters aim to simplify input for the Holiday Set Call Flow
    Use of CsAutoAttendant Parameters will override the respective '-BusinessHours', '-AfterHours' and '-HolidaySet' Parameters
 
    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
    Creates a Auto Attendant with custom settings and friendly names as input
  .LINK
    https://github.com/DEberhardt/TeamsFunctions/tree/main/docs/New-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('New-TeamsAA')]
  [OutputType([System.Object])]
  param(
    #region Required Parameters
    [Parameter(Mandatory, Position = 0, ValueFromPipeline, HelpMessage = 'Name of the Auto Attendant')]
    [string]$Name,

    [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 = 'UTC',

    [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 = 'en-US',
    #endregion

    #region Business Hours Parameters
    [Parameter(HelpMessage = 'Business Hours Greeting - Text String or Recording')]
    [ArgumentCompleter( { '<Your Text-to-speech-string>', 'C:\Temp\' })]
    [string]$BusinessHoursGreeting,

    [Parameter(HelpMessage = 'Business Hours Call Flow - Default options')]
    [ValidateSet('Disconnect', 'TransferCallToTarget', 'Menu')]
    [string]$BusinessHoursCallFlowOption,

    [Parameter(HelpMessage = 'Business Hours Call Target - BusinessHoursCallFlowOption = TransferCallToTarget')]
    [string]$BusinessHoursCallTarget,

    [Parameter(HelpMessage = 'Business Hours Call Target - BusinessHoursCallFlowOption = Menu')]
    [object]$BusinessHoursMenu,
    #endregion

    #region After Hours Parameters
    [Parameter(HelpMessage = 'After Hours Greeting - Text String or Recording')]
    [ArgumentCompleter( { '<Your Text-to-speech-string>', 'C:\Temp\' })]
    [string]$AfterHoursGreeting,

    [Parameter(HelpMessage = 'After Hours Call Flow - Default options')]
    [ValidateSet('Disconnect', 'TransferCallToTarget', 'Menu')]
    [string]$AfterHoursCallFlowOption,

    [Parameter(HelpMessage = 'After Hours Call Target - AfterHoursCallFlowOption = TransferCallToTarget')]
    [string]$AfterHoursCallTarget,

    [Parameter(HelpMessage = 'After Hours Call Target - AfterHoursCallFlowOption = Menu')]
    [object]$AfterHoursMenu,

    [Parameter(HelpMessage = 'Default Schedule to apply')]
    [ValidateSet('Open24x7', 'MonToFri9to5', 'MonToFri8to12and13to18')]
    [string]$AfterHoursSchedule,
    #endregion

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

    [Parameter(HelpMessage = 'Holiday Set Call Flow - Default options')]
    [ValidateSet('Disconnect', 'TransferCallToTarget', 'Menu')]
    [string]$HolidaySetCallFlowOption,

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

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

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

    #region Default Parameters of New-CsAutoAttendant for Pass-through application
    [Parameter(HelpMessage = 'Schedule Object created with New-TeamsAutoAttendantSchedule to apply')]
    [object]$Schedule,

    [Parameter(HelpMessage = 'Default Call Flow')]
    [object]$DefaultCallFlow,

    [Parameter(HelpMessage = 'Call Flows')]
    [object]$CallFlows,

    [Parameter(HelpMessage = 'CallHandlingAssociations')]
    [object]$CallHandlingAssociations,
    #endregion

    [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')]
    [switch]$EnableVoiceResponse,

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

    [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 = 'Suppresses confirmation prompt to enable Users for Enterprise Voice, if Users are specified')]
    [switch]$Force

  ) #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

    $StatusID0 = 'Verifying input'
    #region Parameter validation
    $CurrentOperationID0 = 'Validating Language, Voice capabilities & Time Zone'
    Write-BetterProgress -Id 0 -Activity $ActivityID0 -Status $StatusID0 -CurrentOperation $CurrentOperationID0 -Step ($private:CountID0++) -Of $private:StepsID0

    # 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'"
    # 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"
    $VoiceResponsesSupported = $LanguageRecord.VoiceResponseSupported

    # TimeZoneId - Generated from $TimeZone. Default value: UTC
    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.'
    }

    #region BusinessHours
    # Main Call Flow -- DefaultCallFlow VS BusinessHours*
    $CurrentOperationID0 = 'Validating DefaultCallFlow'
    Write-BetterProgress -Id 0 -Activity $ActivityID0 -Status $StatusID0 -CurrentOperation $CurrentOperationID0 -Step ($private:CountID0++) -Of $private:StepsID0
    if ($PSBoundParameters.ContainsKey('DefaultCallFlow')) {
      # DefaultCallFlow
      Write-Information 'DefaultCallFlow - Overriding all BusinessHours-Parameters'

      if ($PSBoundParameters.ContainsKey('BusinessHoursGreeting')) { $PSBoundParameters.Remove('BusinessHoursGreeting') }
      if ($PSBoundParameters.ContainsKey('BusinessHoursCallFlowOption')) { $PSBoundParameters.Remove('BusinessHoursCallFlowOption') }
      if ($PSBoundParameters.ContainsKey('BusinessHoursCallTarget')) { $PSBoundParameters.Remove('BusinessHoursCallTarget') }

      # Testing provided Object Type
      if (($DefaultCallFlow | Get-Member | Select-Object TypeName -First 1).TypeName -match 'Microsoft.Rtc.Management.Hosted.OAA.Models.CallFlow') {
        Write-Error "DefaultCallFlow - Type is not of 'Microsoft.Rtc.Management.Hosted.OAA.Models.CallFlow'. Please provide a Call Flow Object" -Category InvalidType
        break
      }
    }
    else {
      # BusinessHours Parameters
      if (-not $PSBoundParameters.ContainsKey('BusinessHoursCallFlowOption')) {
        Write-Verbose -Message "BusinessHoursCallFlowOption - Parameter not specified. Defaulting to 'Disconnect' No other 'BusinessHours'-Parameters are processed!"
        $BusinessHoursCallFlowOption = 'Disconnect'
      }
      elseif ($BusinessHoursCallFlowOption -eq 'TransferCallToTarget') {
        # Must contain Target
        if (-not $PSBoundParameters.ContainsKey('BusinessHoursCallTarget')) {
          Write-Error -Message "BusinessHoursCallFlowOption (TransferCallToTarget) - Parameter 'BusinessHoursCallTarget' missing"
          break
        }

        # Must not contain a Menu
        if ($PSBoundParameters.ContainsKey('BusinessHoursMenu')) {
          Write-Verbose -Message 'BusinessHoursCallFlowOption (TransferCallToTarget) - Parameter BusinessHoursMenu cannot be used and will be omitted!' -Verbose
          $PSBoundParameters.Remove('BusinessHoursMenu')
        }
      }
      elseif ($BusinessHoursCallFlowOption -eq 'Menu') {
        # Must contain a Menu
        if (-not $PSBoundParameters.ContainsKey('BusinessHoursMenu')) {
          Write-Error -Message 'BusinessHoursCallFlowOption (Menu) - BusinessHoursMenu missing'
          break
        }
        else {
          # Testing provided Object Type
          if (($BusinessHoursMenu | Get-Member | Select-Object -First 1).TypeName -notmatch 'Microsoft.Rtc.Management.Hosted.OAA.Models.Menu') {
            Write-Error -Message "BusinessHoursCallFlowOption (Menu) - BusinessHoursMenu not of the Type 'Microsoft.Rtc.Management.Hosted.OAA.Models.Menu'" -Category InvalidType
            break
          }
        }

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

    #region Default Parameters VS AfterHours & HolidaySet Parameters
    $CurrentOperationID0 = 'Validating CallFlows'
    Write-BetterProgress -Id 0 -Activity $ActivityID0 -Status $StatusID0 -CurrentOperation $CurrentOperationID0 -Step ($private:CountID0++) -Of $private:StepsID0
    # Call Flows & Call Handling Associations
    if ($PSBoundParameters.ContainsKey('CallFlows') -or $PSBoundParameters.ContainsKey('CallHandlingAssociations')) {
      # Custom Call Flows
      Write-Information 'CallFlows - Overriding all AfterHours- & HolidaySet-Parameters'
      if ($PSBoundParameters.ContainsKey('AfterHoursGreeting')) { $PSBoundParameters.Remove('AfterHoursGreeting') }
      if ($PSBoundParameters.ContainsKey('AfterHoursCallFlowOption')) { $PSBoundParameters.Remove('AfterHoursCallFlowOption') }
      if ($PSBoundParameters.ContainsKey('AfterHoursCallTarget')) { $PSBoundParameters.Remove('AfterHoursCallTarget') }
      if ($PSBoundParameters.ContainsKey('AfterHoursSchedule')) { $PSBoundParameters.Remove('AfterHoursSchedule') }
      if ($PSBoundParameters.ContainsKey('Schedule')) { $PSBoundParameters.Remove('Schedule') }
      # Removing HolidaySet
      if ($PSBoundParameters.ContainsKey('HolidaySetGreeting')) { $PSBoundParameters.Remove('HolidaySetGreeting') }
      if ($PSBoundParameters.ContainsKey('HolidaySetCallFlowOption')) { $PSBoundParameters.Remove('HolidaySetCallFlowOption') }
      if ($PSBoundParameters.ContainsKey('HolidaySetCallTarget')) { $PSBoundParameters.Remove('HolidaySetCallTarget') }
      if ($PSBoundParameters.ContainsKey('HolidaySetSchedule')) { $PSBoundParameters.Remove('HolidaySetSchedule') }

      if ($CallFlows -and -not $CallHandlingAssociations) {
        Write-Error -Message 'CallFlows - Parameter requires CallHandlingAssociation to be specified'
        break
      }

      if ($CallHandlingAssociations -and -not $CallFlows) {
        Write-Error -Message 'CallHandlingAssociations - Parameter requires CallFlows to be specified'
        break
      }

      # Testing provided Object Type
      foreach ($Flow in $CallFlows) {
        if (($Flow | Get-Member | Select-Object -First 1).TypeName -notmatch 'Microsoft.Rtc.Management.Hosted.OAA.Models.CallFlow') {
          Write-Error -Message "CallFlows - '$($Flow.Name)' -Object not of the Type 'Microsoft.Rtc.Management.Hosted.OAA.Models.CallFlow'" -Category InvalidType
          break
        }
      }

      # Testing provided Object Type
      foreach ($CHA in $CallHandlingAssociations) {
        if (($CHA | Get-Member | Select-Object -First 1).TypeName -notmatch 'Microsoft.Rtc.Management.Hosted.OAA.Models.CallHandlingAssociation') {
          Write-Error -Message "CallHandlingAssociations - '$($CHA.Name)' -Object not of the Type 'Microsoft.Rtc.Management.Hosted.OAA.Models.CallHandlingAssociation'" -Category InvalidType
          break
        }
      }
    }
    else {
      #region Processing AfterHours Parameters
      if (-not $PSBoundParameters.ContainsKey('AfterHoursCallFlowOption')) {
        Write-Warning -Message "AfterHoursCallFlowOption - Parameter not specified. Defaulting to 'Disconnect' No other 'AfterHours'-Parameters are processed!"
        $AfterHoursCallFlowOption = 'Disconnect'
      }
      elseif ($AfterHoursCallFlowOption -eq 'TransferCallToTarget') {
        # Must contain Target
        if (-not $PSBoundParameters.ContainsKey('AfterHoursCallTarget')) {
          Write-Error -Message "AfterHoursCallFlowOption (TransferCallToTarget) - Parameter 'AfterHoursCallTarget' missing"
          break
        }

        # Must not contain a Menu
        if ($PSBoundParameters.ContainsKey('AfterHoursMenu')) {
          Write-Verbose -Message 'AfterHoursCallFlowOption (TransferCallToTarget) - Parameter AfterHoursMenu cannot be used and will be omitted!' -Verbose
          $PSBoundParameters.Remove('AfterHoursMenu')
        }
      }
      elseif ($AfterHoursCallFlowOption -eq 'Menu') {
        # Must contain a Menu
        if (-not $PSBoundParameters.ContainsKey('AfterHoursMenu')) {
          Write-Error -Message 'AfterHoursCallFlowOption (Menu) - AfterHoursMenu missing'
          break
        }
        else {
          if (($AfterHoursMenu | Get-Member | Select-Object -First 1).TypeName -notmatch 'Microsoft.Rtc.Management.Hosted.OAA.Models.Menu') {
            Write-Error -Message "AfterHoursCallFlowOption (Menu) - AfterHoursMenu not of the Type 'Microsoft.Rtc.Management.Hosted.OAA.Models.Menu'" -Category InvalidType
            break
          }
        }

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

      #region Processing Schedule & AfterHoursSchedule
      if ($Schedule) {
        if ($AfterHoursSchedule) {
          Write-Information 'INFO: Schedule - Custom Schedule Object overrides AfterHoursSchedule provided'
          $PSBoundParameters.Remove('AfterHoursSchedule')
        }

        # Testing provided Object Type
        if (($Schedule | Get-Member | Select-Object TypeName -First 1).TypeName -match 'Microsoft.Rtc.Management.Hosted.Online.Models.Schedule') {
          Write-Error "Schedule - Type is not of 'Microsoft.Rtc.Management.Hosted.Online.Models.Schedule'. Please provide a Schedule Object" -Category InvalidType
          break
        }
      }
      else {
        if ( $AfterHoursSchedule) {
          Write-Information "INFO: Auto Attendant '$Name' Schedule - AfterHoursSchedule provided, Using: '$AfterHoursSchedule'"
        }
        else {
          $AfterHoursSchedule = 'MonToFri9to5'
          Write-Information "INFO: Auto Attendant '$Name' Schedule - AfterHoursSchedule not provided, Using: '$AfterHoursSchedule'"
        }

        # Creating Schedule
        $CurrentOperationID0 = 'Business Hours Call Flow - Creating Schedule'
        Write-BetterProgress -Id 0 -Activity $ActivityID0 -Status $StatusID0 -CurrentOperation $CurrentOperationID0 -Step ($private:CountID0++) -Of $private:StepsID0
        $Schedule = switch ($AfterHoursSchedule) {
          'Open24x7' {
            New-TeamsAutoAttendantSchedule -Name 'After Hours Schedule' -WeeklyRecurrentSchedule -BusinessDays MonToSun -BusinessHours AllDay -Complement
          }
          'MonToFri9to5' {
            New-TeamsAutoAttendantSchedule -Name 'After Hours Schedule' -WeeklyRecurrentSchedule -BusinessDays MonToFri -BusinessHours 9to5 -Complement
          }
          'MonToFri8to12and13to18' {
            New-TeamsAutoAttendantSchedule -Name 'After Hours Schedule' -WeeklyRecurrentSchedule -BusinessDays MonToFri -BusinessHours 8to12and13to18 -Complement
          }
        }
        Write-Verbose -Message "Schedule - Schedule created: '$AfterHoursSchedule'"
      }
      #endregion

      #region Processing HolidaySet Parameters
      if (-not $PSBoundParameters.ContainsKey('HolidaySetCallFlowOption')) {
        Write-Warning -Message "HolidaySetCallFlowOption - Parameter not specified. Defaulting to 'Disconnect' Except 'HolidaySetSchedule', no other 'HolidaySet'-Parameters are processed!"
        $HolidaySetCallFlowOption = 'Disconnect'
      }
      elseif ($HolidaySetCallFlowOption -eq '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')
        }
      }
      elseif ($HolidaySetCallFlowOption -eq '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')
        }
      }
      #endregion

      #region Processing HolidaySetSchedule
      if ( $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! Creating empty Schedule'
        $HolidaySchedule = $null
      }
      #endregion
    }
    #endregion
    #endregion

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

  process {
    Write-Verbose -Message "[PROCESS] $($MyInvocation.MyCommand)"
    #region PREPARATION
    $StatusID0 = 'Assembling Auto Attendant Options'
    $ActivityID1 = 'Building Auto Attendant'
    $StatusID1 = 'Preparing Parameters'
    # preparing Splatting Object
    $Parameters = $null

    #region Required Parameters
    $CurrentOperationID1 = 'Name, TimeZone & Language, Voice Responses'
    Write-BetterProgress -Id 1 -Activity $ActivityID1 -Status $StatusID1 -CurrentOperation $CurrentOperationID1 -Step ($private:CountID1++) -Of $private:StepsID1
    # Normalising $Name
    $AAName = Format-StringForUse -InputString $Name -As DisplayName
    Write-Verbose -Message "'$Name' DisplayName normalised to: '$AAName'"
    $Parameters += @{'Name' = $AAName }

    # Preparing Call Flow String (to adhere to 64 Character limit)
    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]
      $RandomString = '{0:d4}' -f $(Get-Random -Minimum 0000 -Maximum 9999)
      $CallFlowNamePrefix = $CallFlowNamePrefix + $RandomString
    }
    else {
      $CallFlowNamePrefix = "$AAName"
    }
    Write-Verbose "Auto Attendant Call Flow Name Prefix used: '$CallFlowNamePrefix'"

    # Adding required parameters
    $Parameters += @{'LanguageId' = $Language }
    $Parameters += @{'TimeZoneId' = $TimeZoneId }

    # EnableVoiceResponse
    if ($PSBoundParameters.ContainsKey('EnableVoiceResponse')) {
      # Checking whether Voice Responses are available for the provided Language
      if ($VoiceResponsesSupported) {
        # Using As-Is
        Write-Verbose -Message "'$AAName' EnableVoiceResponse - Voice Responses are supported with Language '$Language' and will be activated (Switch 'EnableVoiceResponse' will be used)"
        $Parameters += @{'EnableVoiceResponse' = $true }
      }
      else {
        Write-Warning -Message "'$AAName' EnableVoiceResponse - Voice Responses are not supported for Language '$Language' and cannot be activated (Switch 'EnableVoiceResponse' will be omitted)"
      }
    }

    if ( $PSBoundParameters.ContainsKey('VoiceId') ) {
      $Voices = ($LanguageRecord.Voices | Where-Object Id -EQ "$VoiceId")
      if ( $Voices ) {
        $Parameters += @{'VoiceId' = $Voices[0] }
      }
    }
    #endregion

    #region Operator
    $CurrentOperationID1 = 'Operator'
    Write-BetterProgress -Id 1 -Activity $ActivityID1 -Status $StatusID1 -CurrentOperation $CurrentOperationID1 -Step ($private:CountID1++) -Of $private:StepsID1
    if ($PSBoundParameters.ContainsKey('Operator')) {
      try {
        $OperatorEntity = New-TeamsCallableEntity -Identity "$Operator" @TeamsCallableEntityParams
        if ($OperatorEntity) {
          $Parameters += @{'Operator' = $OperatorEntity }
        }
      }
      catch {
        Write-Warning -Message 'Operator - Error creating Call Target - skipped'
      }
    }
    #endregion


    #region Business Hours Call Flow
    $CurrentOperationID1 = 'Business Hours Call Flow - Default Call Flow & Call Flow Option'
    Write-BetterProgress -Id 1 -Activity $ActivityID1 -Status $StatusID1 -CurrentOperation $CurrentOperationID1 -Step ($private:CountID1++) -Of $private:StepsID1
    if ( $DefaultCallFlow ) {
      # Using As-Is
      Write-Information "INFO: Auto Attendant '$AAName' DefaultCallFlow - Custom Object provided. Over-riding other options (like switch 'BusinessHoursCallFlow')"
      $Parameters += @{'DefaultCallFlow' = $DefaultCallFlow }

    }
    else {
      Write-Verbose -Message "'$AAName' DefaultCallFlow - No Custom Object - Processing 'BusinessHoursCallFlowOption'..."
      $BusinessHoursCallFlowParameters = @{}
      $BusinessHoursCallFlowParameters.Name = "$CallFlowNamePrefix - Business Hours CF"

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

          # Process BusinessHoursCallTarget
          try {
            $BusinessHoursCallTargetEntity = New-TeamsCallableEntity "$BusinessHoursCallTarget" @TeamsCallableEntityParams

            # Building Menu Only if Successful
            if ($BusinessHoursCallTargetEntity) {
              $BusinessHoursMenuOptionTransfer = New-CsAutoAttendantMenuOption -Action TransferCallToTarget -CallTarget $BusinessHoursCallTargetEntity -DtmfResponse Automatic
              $BusinessHoursMenuObject = New-CsAutoAttendantMenu -Name 'Business Hours Menu' -MenuOptions @($BusinessHoursMenuOptionTransfer)
              Write-Information "INFO: Auto Attendant '$AAName' Business Hours Call Flow - Menu (TransferCallToTarget) created"
              break
            }
            else {
              # Reverting to Disconnect
              Write-Warning -Message "'$AAName' DefaultCallFlow - Business Hours Menu not created properly. Reverting to Disconnect"
              $BusinessHoursMenuOptionDefault = New-CsAutoAttendantMenuOption -Action DisconnectCall -DtmfResponse Automatic
              $BusinessHoursMenuObject = New-CsAutoAttendantMenu -Name 'Business Hours Menu' -MenuOptions @($BusinessHoursMenuOptionDefault)
            }
          }
          catch {
            Write-Warning -Message 'BusinessHoursCallTarget - Error creating Call Target - Defaulting to disconnect'
            $BusinessHoursMenuOptionDefault = New-CsAutoAttendantMenuOption -Action DisconnectCall -DtmfResponse Automatic
            $BusinessHoursMenuObject = New-CsAutoAttendantMenu -Name 'Business Hours Menu' -MenuOptions @($BusinessHoursMenuOptionDefault)
          }
        }

        'Menu' {
          Write-Verbose -Message "'$AAName' DefaultCallFlow - Menu"
          if ($PSBoundParameters.ContainsKey('BusinessHoursMenu')) {
            # Menu is passed on as-is - $BusinessHoursMenu is defined and attached
            $BusinessHoursMenuObject = $BusinessHoursMenu
            Write-Information "INFO: Auto Attendant '$AAName' Business Hours Call Flow - Menu (BusinessHoursMenu) used"
          }
          else {
            # No custom / default Menu is currently created
            # $BusinessHoursMenu is Mandatory. If this is built out, the check against this must also be removed!
          }
        }

        default {
          # Defaulting to Disconnect
          Write-Verbose -Message "'$AAName' DefaultCallFlow not provided or 'Disconnect' - Using default (Disconnect)"
          $BusinessHoursMenuOptionDefault = New-CsAutoAttendantMenuOption -Action DisconnectCall -DtmfResponse Automatic
          $BusinessHoursMenuObject = New-CsAutoAttendantMenu -Name 'Business Hours Menu' -MenuOptions @($BusinessHoursMenuOptionDefault)
        }
      }
      #endregion

      #region BusinessHoursGreeting
      #Adding optional BusinessHoursGreeting
      if ($PSBoundParameters.ContainsKey('BusinessHoursGreeting')) {
        $CurrentOperationID1 = 'Business Hours Call Flow - Greeting'
        Write-BetterProgress -Id 1 -Activity $ActivityID1 -Status $StatusID1 -CurrentOperation $CurrentOperationID1 -Step ($private:CountID1++) -Of $private:StepsID1
        try {
          $BusinessHoursGreetingObject = New-TeamsAutoAttendantPrompt -String "$BusinessHoursGreeting"
          if ( -not $BusinessHoursGreetingObject) { throw }
          Write-Information "INFO: Auto Attendant '$AAName' Business Hours Call Flow - Greeting created"
          $BusinessHoursCallFlowParameters.Greetings = @($BusinessHoursGreetingObject)
        }
        catch {
          Write-Warning -Message "'$AAName' CallFlow - BusinessHoursCallFlow - Greeting not enumerated. Omitting Greeting"
        }
      }
      #endregion

      #region Building Call Flow
      $CurrentOperationID1 = 'Business Hours Call Flow - Building Call Flow'
      Write-BetterProgress -Id 1 -Activity $ActivityID1 -Status $StatusID1 -CurrentOperation $CurrentOperationID1 -Step ($private:CountID1++) -Of $private:StepsID1
      # Adding Business Hours Call Flow
      $BusinessHoursCallFlowParameters.Menu = $BusinessHoursMenuObject
      $BusinessHoursCallFlow = New-CsAutoAttendantCallFlow @BusinessHoursCallFlowParameters
      Write-Information "INFO: Auto Attendant '$AAName' Business Hours Call Flow - Call Flow created"
      $Parameters += @{'DefaultCallFlow' = $BusinessHoursCallFlow }
      #endregion
    }
    #endregion

    #region Processing provided CallFlows and CallHandlingAssociations Objects
    if ($PSBoundParameters.ContainsKey('CallFlows')) {
      $CurrentOperationID1 = 'After Hours Call Flow - Objects'
      Write-BetterProgress -Id 1 -Activity $ActivityID1 -Status $StatusID1 -CurrentOperation $CurrentOperationID1 -Step ($private:CountID1++) -Of $private:StepsID1
      # Custom Option provided - Using As-Is
      Write-Information "INFO: Auto Attendant '$AAName' CallFlow - Custom Object provided. Over-riding other options (like switch 'AfterHoursCallFlow')"
      $Parameters += @{'CallFlows' = @($CallFlows) }
      $Parameters += @{'CallHandlingAssociations' = @($CallHandlingAssociations) }
    }
    #endregion

    # Processing custom Call Flows - creating Call Flow, Schedule & Call Handling Association manually
    #region After Hours Call Flow
    #Initialising Variables for Call Handling Association
    $AfterHoursCallHandlingAssociationParams = @{}
    $AfterHoursCallHandlingAssociationParams.Type = 'AfterHours'

    # Processing CallFlow
    if ($AfterHoursCallFlowOption -and -not $PSBoundParameters.ContainsKey('CallFlows')) {
      $CurrentOperationID1 = 'After Hours Call Flow - Call Flows & Call Flow Option'
      Write-BetterProgress -Id 1 -Activity $ActivityID1 -Status $StatusID1 -CurrentOperation $CurrentOperationID1 -Step ($private:CountID1++) -Of $private:StepsID1
      # Option Selected
      Write-Verbose -Message "'$AAName' CallFlow - No Custom Object - Processing 'AfterHoursCallFlowOption'..."
      $AfterHoursCallFlowParameters = @{}
      $AfterHoursCallFlowParameters.Name = "$CallFlowNamePrefix - After Hours CF"

      #region Processing AfterHoursCallFlowOption
      switch ($AfterHoursCallFlowOption) {
        'TransferCallToTarget' {
          Write-Verbose -Message "'$AAName' CallFlow - Transferring to Target"
          # Process AfterHoursCallTarget
          try {
            $AfterHoursCallTargetEntity = New-TeamsCallableEntity "$AfterHoursCallTarget" @TeamsCallableEntityParams
            # Building Menu Only if Successful
            if ($AfterHoursCallTargetEntity) {
              $AfterHoursMenuOptionTransfer = New-CsAutoAttendantMenuOption -Action TransferCallToTarget -CallTarget $AfterHoursCallTargetEntity -DtmfResponse Automatic
              $AfterHoursMenuObject = New-CsAutoAttendantMenu -Name 'After Hours Menu' -MenuOptions @($AfterHoursMenuOptionTransfer)
              Write-Information "INFO: Auto Attendant '$AAName' After Hours Call Flow - Menu (TransferCallToTarget) created"
              break
            }
            else {
              # Reverting to Disconnect
              Write-Warning -Message "'$AAName' Call Flow - After Hours Menu not created properly. Reverting to Disconnect"
              $AfterHoursMenuOptionDefault = New-CsAutoAttendantMenuOption -Action DisconnectCall -DtmfResponse Automatic
              $AfterHoursMenuObject = New-CsAutoAttendantMenu -Name 'After Hours Menu' -MenuOptions @($AfterHoursMenuOptionDefault)
            }
          }
          catch {
            Write-Warning -Message 'AfterHoursCallTarget - Error creating Call Target - Defaulting to disconnect'
            $AfterHoursMenuOptionDefault = New-CsAutoAttendantMenuOption -Action DisconnectCall -DtmfResponse Automatic
            $AfterHoursMenuObject = New-CsAutoAttendantMenu -Name 'After Hours Menu' -MenuOptions @($AfterHoursMenuOptionDefault)
          }
        }
        'Menu' {
          Write-Verbose -Message "'$AAName' CallFlow - AfterHoursCallFlow - Menu"
          if ($PSBoundParameters.ContainsKey('AfterHoursMenu')) {
            # Menu is passed on as-is - $AfterHoursMenu is defined and attached
            $AfterHoursMenuObject = $AfterHoursMenu
            Write-Information "INFO: Auto Attendant '$AAName' After Hours Call Flow - Menu (AfterHoursMenu) used"
          }
          else {
            # No custom / default Menu is currently created
            # $AfterHoursMenu is Mandatory. If this is built out, the check against this must also be removed!
          }
        }
        default {
          # Defaulting to Disconnect
          Write-Verbose -Message "'$AAName' CallFlow - AfterHoursCallFlow not provided or Disconnect. Using default (Disconnect)"
          $AfterHoursMenuOptionDefault = New-CsAutoAttendantMenuOption -Action DisconnectCall -DtmfResponse Automatic
          $AfterHoursMenuObject = New-CsAutoAttendantMenu -Name 'After Hours Menu' -MenuOptions @($AfterHoursMenuOptionDefault)
        }
      }
      #endregion

      #region AfterHoursGreeting
      if ($PSBoundParameters.ContainsKey('AfterHoursGreeting')) {
        $CurrentOperationID1 = 'After Hours Call Flow - Greeting'
        Write-BetterProgress -Id 1 -Activity $ActivityID1 -Status $StatusID1 -CurrentOperation $CurrentOperationID1 -Step ($private:CountID1++) -Of $private:StepsID1
        try {
          $AfterHoursGreetingObject = New-TeamsAutoAttendantPrompt -String "$AfterHoursGreeting"
          if ($AfterHoursGreetingObject) {
            Write-Information "INFO: Auto Attendant '$AAName' After Hours Call Flow - Greeting created"
            $AfterHoursCallFlowParameters.Greetings = @($AfterHoursGreetingObject)
          }
        }
        catch {
          Write-Warning -Message "'$AAName' CallFlow - AfterHoursCallFlow - Greeting not enumerated. Omitting Greeting"
        }
      }
      #endregion

      #region Building Call Flow
      $CurrentOperationID1 = 'After Hours Call Flow - Building Call Flow'
      Write-BetterProgress -Id 1 -Activity $ActivityID1 -Status $StatusID1 -CurrentOperation $CurrentOperationID1 -Step ($private:CountID1++) -Of $private:StepsID1
      # Adding After Hours Call Flow
      $AfterHoursCallFlowParameters.Menu = $AfterHoursMenuObject
      $AfterHoursCallFlow = New-CsAutoAttendantCallFlow @AfterHoursCallFlowParameters
      Write-Information "INFO: Auto Attendant '$AAName' After Hours Call Flow - Call Flow created"
      if ($Parameters.ContainsKey('CallFlows')) {
        $Parameters.CallFlows += $AfterHoursCallFlow
        #$Parameters.CallFlows.Add($AfterHoursCallFlow)
      }
      else {
        $Parameters += @{'CallFlows' = @($AfterHoursCallFlow) }
      }

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

      #region After Hours Schedule & Call Handling Association
      $CurrentOperationID1 = 'Schedule & Call Handling Association'
      Write-BetterProgress -Id 1 -Activity $ActivityID1 -Status $StatusID1 -CurrentOperation $CurrentOperationID1 -Step ($private:CountID1++) -Of $private:StepsID1
      $AfterHoursCallHandlingAssociationParams.ScheduleId = $Schedule.Id
      $AfterHoursCallHandlingAssociation = New-CsAutoAttendantCallHandlingAssociation @AfterHoursCallHandlingAssociationParams
      Write-Information "INFO: Auto Attendant '$AAName' After Hours Call Flow - Call Handling Association created with Schedule"
      if ($Parameters.ContainsKey('CallHandlingAssociation')) {
        $Parameters.CallHandlingAssociation += $AfterHoursCallHandlingAssociation
        #$Parameters.CallHandlingAssociation.Add($AfterHoursCallHandlingAssociation)
      }
      else {
        $Parameters += @{'CallHandlingAssociation' = @($AfterHoursCallHandlingAssociation) }
      }
      #endregion
    }
    #endregion

    #region HolidaySet Call Flow
    if ( $HolidaySchedule ) {
      # Processing HolidaySetsCallFlowOption
      $CurrentOperationID1 = 'Holiday Call Flow'
      Write-BetterProgress -Id 1 -Activity $ActivityID1 -Status $StatusID1 -CurrentOperation $CurrentOperationID1 -Step ($private:CountID1++) -Of $private:StepsID1
      foreach ( $HolSchedule in $HolidaySchedule ) {
        #Initialising Variables for Call Handling Association
        $HolidaySetCallHandlingAssociationParams = @{}
        $HolidaySetCallHandlingAssociationParams.Type = 'Holiday'
        if ($HolidaySetCallFlowOption -and -not $PSBoundParameters.ContainsKey('CallFlows')) {
          if ($PSBoundParameters.ContainsKey('Debug') -or $DebugPreference -eq 'Continue') {
            " Function: $($MyInvocation.MyCommand.Name) - HolSchedule:", ($HolSchedule | Format-Table -AutoSize | Out-String).Trim() | Write-Debug
          }

          $CurrentOperationID1 = 'Holiday Call Flow - Call Flows & Call Flow Option'
          Write-BetterProgress -Id 1 -Activity $ActivityID1 -Status $StatusID1 -CurrentOperation $CurrentOperationID1 -Step ($private:CountID1) -Of $private:StepsID1
          # 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

                # Building Menu Only if Successful
                if ($HolidaySetCallTargetEntity) {
                  $HolidaySetMenuOptionTransfer = New-CsAutoAttendantMenuOption -Action TransferCallToTarget -CallTarget $HolidaySetCallTargetEntity -DtmfResponse Automatic
                  $HolidaySetMenuObject = New-CsAutoAttendantMenu -Name 'Holiday Set Menu' -MenuOptions @($HolidaySetMenuOptionTransfer)
                  Write-Information "INFO: Auto Attendant '$AAName' Holiday Set Call Flow - Menu (TransferCallToTarget) created"
                  break
                }
                else {
                  # Reverting to Disconnect
                  Write-Warning -Message "'$AAName' Call Flow - Holiday Set Menu not created properly. Reverting to Disconnect"
                  $HolidaySetMenuOptionDefault = New-CsAutoAttendantMenuOption -Action DisconnectCall -DtmfResponse Automatic
                  $HolidaySetMenuObject = New-CsAutoAttendantMenu -Name 'Holiday Set Menu' -MenuOptions @($HolidaySetMenuOptionDefault)
                }
              }
              catch {
                Write-Warning -Message 'HolidaySetCallTarget - Error creating Call Target - Defaulting to disconnect'
                $HolidaySetMenuOptionDefault = New-CsAutoAttendantMenuOption -Action DisconnectCall -DtmfResponse Automatic
                $HolidaySetMenuObject = New-CsAutoAttendantMenu -Name 'Holiday 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: Auto Attendant '$AAName' Holiday Set Call Flow - Menu (BusinessHoursMenu) used"
              }
              else {
                # No custom / default Menu is currently created
                # $HolidaySetMenu is Mandatory. If this is built out, the check against this must also be removed!
              }
            }

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

          #region HolidaySetGreeting
          if ($PSBoundParameters.ContainsKey('HolidaySetGreeting')) {
            $CurrentOperationID1 = 'Holiday Call Flow - Greeting'
            Write-BetterProgress -Id 1 -Activity $ActivityID1 -Status $StatusID1 -CurrentOperation $CurrentOperationID1 -Step ($private:CountID1) -Of $private:StepsID1
            try {
              $HolidaySetGreetingObject = New-TeamsAutoAttendantPrompt -String "$HolidaySetGreeting"
              if ($HolidaySetGreetingObject) {
                Write-Information "INFO: Auto Attendant '$AAName' Holiday Set Call Flow - Greeting created"
                $HolidaySetCallFlowParameters.Greetings = @($HolidaySetGreetingObject)
              }
            }
            catch {
              Write-Warning -Message "'$AAName' CallFlow - HolidaySetCallFlow - Greeting not enumerated. Omitting Greeting"
            }
          }
          #endregion

          #region Building Call Flow
          $CurrentOperationID1 = "Holiday Call Flow - $($HolSchedule.Name) - Building Call Flow"
          Write-BetterProgress -Id 1 -Activity $ActivityID1 -Status $StatusID1 -CurrentOperation $CurrentOperationID1 -Step ($private:CountID1) -Of $private:StepsID1
          # Adding Holiday Set Call Flow
          $HolidaySetCallFlowParameters.Menu = $HolidaySetMenuObject
          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-Information "INFO: Auto Attendant '$AAName' Holiday Set Call Flow - Call Flow created"
          if ($Parameters.ContainsKey('CallFlows')) {
            $Parameters.CallFlows += $HolidaySetCallFlow
            #$Parameters.CallFlows.Add($HolidaySetCallFlow)
          }
          else {
            #VALIDATE - ADD only if any of the Holiday Parameters are specified? Especially Schedule!
            $Parameters += @{'CallFlows' = @($HolidaySetCallFlow) }
          }

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

          #region Holiday Set Schedule & Call Handling Association
          $CurrentOperationID1 = "Holiday Call Flow - $($HolSchedule.Name) - Call Handling Association"
          Write-BetterProgress -Id 1 -Activity $ActivityID1 -Status $StatusID1 -CurrentOperation $CurrentOperationID1 -Step ($private:CountID1) -Of $private:StepsID1

          $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-Information "INFO: Auto Attendant '$AAName' Holiday Set Call Flow - Call Handling Association created with Holiday Schedule"
          if ($Parameters.ContainsKey('CallHandlingAssociation')) {
            $Parameters.CallHandlingAssociation += $HolidaySetCallHandlingAssociation
            #$Parameters.CallHandlingAssociation.Add($HolidaySetCallHandlingAssociation)
          }
          else {
            #VALIDATE - ADD only if any of the Holiday Parameters are specified? Especially Schedule!
            $Parameters += @{'CallHandlingAssociation' = @($HolidaySetCallHandlingAssociation) }
          }
          #endregion
        }
      }
    }
    #endregion


    #region Inclusion and Exclusion Scope
    $CurrentOperationID1 = 'Dial Scopes - Inclusion & Exclusion Scope'
    Write-BetterProgress -Id 1 -Activity $ActivityID1 -Status $StatusID1 -CurrentOperation $CurrentOperationID1 -Step ($private:CountID1++) -Of $private:StepsID1
    # Inclusion Scope
    if ($PSBoundParameters.ContainsKey('InclusionScope')) {
      Write-Verbose -Message "'$AAName' InclusionScope provided. Using as-is"
      $Parameters += @{'InclusionScope' = $InclusionScope }

    }
    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"
      $Parameters += @{'ExclusionScope' = $ExclusionScope }

    }
    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
    $CurrentOperationID1 = 'Parsing AuthorizedUsers'
    Write-BetterProgress -Id 1 -Activity $ActivityID1 -Status $StatusID1 -CurrentOperation $CurrentOperationID1 -Step ($private:CountID1++) -Of $private:StepsID1
    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
        $Parameters += @{'AuthorizedUsers' = if ( $AuthorizedUserIdList.Count -eq 1 ) { "$AuthorizedUserIdList" } else { @($AuthorizedUserIdList) } }
      }
    }
    #endregion
    #endregion
    Write-Progress -Id 1 -Activity $ActivityID1 -Completed

    #region ACTION
    $Parameters += @{'WarningAction' = 'Continue' }
    $Parameters += @{'ErrorAction' = 'Stop' }
    Write-Verbose -Message '[PROCESS] Creating Auto Attendant'
    if ($PSBoundParameters.ContainsKey('Debug') -or $DebugPreference -eq 'Continue') {
      " Function: $($MyInvocation.MyCommand.Name) - Parameters (New-CsAutoAttendant)", ($Parameters | Format-Table -AutoSize | Out-String).Trim() | Write-Debug
    }

    # Create AA (New-CsAutoAttendant)
    $StatusID0 = 'Creating Auto Attendant'
    $CurrentOperationID0 = "'$AAName'"
    Write-BetterProgress -Id 0 -Activity $ActivityID0 -Status $StatusID0 -CurrentOperation $CurrentOperationID0 -Step ($private:CountID0++) -Of $private:StepsID0
    if ($PSCmdlet.ShouldProcess("$AAName", 'New-CsAutoAttendant')) {
      try {
        # Create the Auto Attendant with all enumerated Parameters passed through splatting
        $null = (New-CsAutoAttendant @Parameters)
        Write-Information "INFO: Auto Attendant '$AAName' created with all Parameters"
      }
      catch {
        Write-Error -Message "Error creating the Auto Attendant: $($_.Exception.Message)" -Category InvalidResult
        return
      }
    }
    else {
      return
    }
    #endregion


    #region OUTPUT
    $StatusID0 = 'Validation'
    $CurrentOperationID0 = 'Querying Object'
    Write-BetterProgress -Id 0 -Activity $ActivityID0 -Status $StatusID0 -CurrentOperation $CurrentOperationID0 -Step ($private:CountID0++) -Of $private:StepsID0
    $AAFinal = Get-TeamsAutoAttendant -Name "$AAName" -WarningAction SilentlyContinue
    Write-Output $AAFinal
    #endregion
    Write-Progress -Id 0 -Activity $ActivityID0 -Completed

  } #process

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

  } #end
} #New-TeamsAutoAttendant