Public/Functions/Support/AutoAttendant/New-TeamsHolidaySchedule.ps1
# Module: TeamsFunctions # Function: AutoAttendant # Author: David Eberhardt # Updated: 13-JUN-2021 # Status: Live #TODO Add If Called, run with -inmemory, otherwise return the created object! function New-TeamsHolidaySchedule { <# .SYNOPSIS Creates a Teams Schedule for each Country and Year specified .DESCRIPTION Queries the Nager.Date API for public Holidays for Country and year and creates a CsOnlineSchedule object for each. .PARAMETER CountryCode Required. ISO3166-Alpha-2 Country Code. One or more Countries from the list of Get-PublicHolidayCountry .PARAMETER Year Optional. Year for which the Holidays are to be listed. One or more Years between 2000 and 3000 If not provided, the current year is taken. If the current month is December, the coming year is taken. .EXAMPLE New-TeamsHolidaySchedule -CountryCode CA -Year 2022 Creates Schedule Objects in Teams for Canada for the year 2022. One Schedule object per 10 Holidays. They are named as "CA 2022 #1" and "CA 2022 #2" respectively .EXAMPLE New-TeamsHolidaySchedule -CountryCode CA -Year 2022,2023,2024 Creates Schedule Objects in Teams for Canada for the years 2022 to 2024. One Schedule object per 10 Holidays .EXAMPLE New-TeamsHolidaySchedule -CountryCode CA,MX,GB,DE -Year 2022 Creates Schedule Objects in Teams for Canada, Mexico, Great Britain & Germany for the year 2022. One Schedule object per 10 Holidays. They are named as "CA 2022 #1", "CA 2022 #2", "MX 2022 #1", etc. respectively .EXAMPLE New-TeamsHolidaySchedule -CountryCode CA,MX,GB,DE -Year 2022,2023,2024 Creates Schedule Objects in Teams for Canada, Mexico, Great Britain & Germany for the years 2022 to 2024. One Schedule object per 10 Holidays. They are named as "CA 2022 #1", "CA 2022 #2", "CA 2023 #1", etc. respectively .EXAMPLE New-TeamsHolidaySchedule -CountryCode CA -Year 2022 -FilterPastDates Creates Schedule Objects in Teams for Canada, Mexico, Great Britain & Germany for the years 2022 to 2024. Holidays for this year that are in the past are not considered, if more than 10 holidays remain, one Schedule object is created per 10 Holidays. .INPUTS System.String .OUTPUTS System.Object .NOTES The Nager.Date API currently supports a bit over 100 Countries. Please query with Get-PublicHolidayCountry Evaluated the following APIs: Nager.Date: Decent coverage (100+ Countries). Free & Used Coverage: https://date.nager.at/Home/RegionStatistic TimeAndDate: Great coverage. Requires license. Also a bit clunky. Not considering implementation. Calendarific: Great coverage. Requires license for commercial use. Currently not considering development Utilising the Calendarific API could be integrated if licensed and the API key is passed/registered locally. Teams only supports fixed schedules with maximum 10 dates. This CmdLet therefore splits all found holidays for a year into multiple Schedules and returns each object. When running this CmdLet, the Schedule is created in the Teams tenant. .COMPONENT SupportingFunction TeamsAutoAttendant .FUNCTIONALITY Queries available Holidays for a specific Country from the Nager.Date API .LINK https://github.com/DEberhardt/TeamsFunctions/tree/master/docs/New-TeamsHolidaySchedule.md .LINK https://github.com/DEberhardt/TeamsFunctions/tree/master/docs/about_TeamsAutoAttendant.md .LINK https://github.com/DEberhardt/TeamsFunctions/tree/master/docs/ #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] #[Alias('')] [OutputType([PSCustomObject])] param ( [Parameter(Mandatory, ValueFromPipelineByPropertyName, HelpMessage = 'ISO 3166-alpha2 Country Code (2-digit CC)')] [ValidateScript( { $Countries = Get-PublicHolidayCountry if ($_ -in $Countries.CountryCode) { $true } else { throw [System.Management.Automation.ValidationMetadataException] "Country '$_' not supported (yet), sorry. Please provide a CountryCode from the output of Get-PublicHolidayCountry or check https://date.nager.at/" $false } })] [Alias('CC', 'Country')] [String[]]$CountryCode, [Parameter(ValueFromPipelineByPropertyName, HelpMessage = 'Year(s)')] [Alias('Y')] [ValidateRange(2000, 3000)] [int[]]$Year, [Parameter(ValueFromPipelineByPropertyName, HelpMessage = 'Only consideres dates that are in the future')] [switch]$FilterPastDates ) begin { Show-FunctionStatus -Level Live Write-Verbose -Message "[BEGIN ] $($MyInvocation.MyCommand)" # Setting Preference Variables according to Upstream settings if (-not $PSBoundParameters.ContainsKey('Verbose')) { $VerbosePreference = $PSCmdlet.SessionState.PSVariable.GetValue('VerbosePreference') } if (-not $PSBoundParameters.ContainsKey('Debug')) { $DebugPreference = $PSCmdlet.SessionState.PSVariable.GetValue('DebugPreference') } else { $DebugPreference = 'Continue' } if ( $PSBoundParameters.ContainsKey('InformationAction')) { $InformationPreference = $PSCmdlet.SessionState.PSVariable.GetValue('InformationAction') } else { $InformationPreference = 'Continue' } # Preparing Splatting Object $NewCsTeamsAutoAttendantScheduleParams = $null $NewCsTeamsAutoAttendantScheduleParams = @{ 'Fixed' = $True 'InformationAction' = 'SilentlyContinue' 'ErrorAction' = 'Stop' } # Handling Year if (-not $PSBoundParameters.ContainsKey('Year')) { $Today = Get-Date $Year = $Today.Year $null = $Today.Datetime -match '\d\d (.*?) \d' If ($Today.Month -eq 12) { $Year++ } Write-Information "INFO: $($MyInvocation.MyCommand) - Parameter Year not provided, as it is $($matches[1]), using year: $Year" } } #begin process { Write-Verbose -Message "[PROCESS] $($MyInvocation.MyCommand)" foreach ($C in $CountryCode) { #$Cname = Get-RegionFromCountryCode -CountryCode $C -Output Country $Cname = $C # Override to shorten Schedule Name Write-Verbose -Message "[PROCESS] $($MyInvocation.MyCommand) - Country '$Cname'" foreach ($Y in $Year) { Write-Verbose -Message "[PROCESS] $($MyInvocation.MyCommand) - Country '$Cname', Year '$Y'" try { $AllFoundHolidays = $null #$AllFoundHolidays = Get-PublicHolidayList -CountryCode $C -Year $Y -ErrorAction Stop | Where-Object Global # Limiting to universal holidays only. $AllFoundHolidays = Get-PublicHolidayList -CountryCode $C -Year $Y -ErrorAction Stop if ($PSBoundParameters.ContainsKey('Debug')) { " Function: $($MyInvocation.MyCommand.Name) - AllFoundHolidays:", ($AllFoundHolidays | Format-Table -AutoSize | Out-String).Trim() | Write-Debug } } catch { Write-Warning -Message "Get-PublicHolidayList for Country '$C' failed with: $($_.Exception.Message)" } #Manually adding DateTimeRanges from Parameter #TEST whether this would enhance the function #Filtering if ( $FilterPastDates ) { $AllFoundHolidays = $AllFoundHolidays | Where-Object Date -GT (Get-Date -Format u) } #region Slicing and Creating Schedules #TEST Grouping of 20 based on Microsoft Documentation with what is delivered in Admin Center (administrability!) #Slicing in groups of 10 as a holiday Set only supports max 10 Holidays per set #$group = if ( $AllFoundHolidays.Count -ge 10 ) { 10 } else { $AllFoundHolidays.Count } $group = 10 # was 10 to be in harmony with Teams Admin Center #$group = 20 # is now 20 as supported by PowerShell $i = 0 do { $Hols = $AllFoundHolidays[$i..(($i += $group) - 1)] if ($PSBoundParameters.ContainsKey('Debug')) { " Function: $($MyInvocation.MyCommand.Name) - Current Set: Hols:", ($Hols | Format-Table -AutoSize | Out-String).Trim() | Write-Debug } [System.Collections.ArrayList]$Holidays = @() foreach ($H in $Hols) { $Date = $H.date | Get-Date -UFormat '%d/%m/%Y' $DateTimeRange = New-CsOnlineDateTimeRange -Start $Date if ( $Holidays.Start -notcontains $DateTimeRange.Start ) { Write-Verbose -Message "Country '$C', Year '$Y': Date: $Date`: $($H.Name) - OK, adding Date" [void]$Holidays.Add($DateTimeRange) } else { Write-Verbose -Message "Country '$C', Year '$Y': Date: $Date`: $($H.Name) - Already present, skipping" } } # Filtering Unique DateTimeRanges if ($PSCmdlet.ShouldProcess("Creating Online Schedule '$Cname $Y' with $($Holidays.Count) Holidays", "$($Schedule.Name)", 'New-TeamsAutoAttendantSchedule')) { try { $Name = "$Cname $Y" + $(if ( $i / $group -ge 1 ) { ' #' + [math]::Round($($i / $group), 0) }) $Schedule = New-TeamsAutoAttendantSchedule -Name $Name -DateTimeRanges $Holidays @NewCsTeamsAutoAttendantScheduleParams Write-Information "INFO: Schedule '$($Schedule.Name)' created with $($Holidays.Count) entries" Write-Output $Schedule } catch { Write-Error -Message "Schedule '$Cname $Y' failed to create. Exception: $($_.Exception.Message)" } } } until ($i -ge $AllFoundHolidays.count - 1) # Loops for all Holidays #endregion } } } #process end { Write-Verbose -Message "[END ] $($MyInvocation.MyCommand)" } #end } #New-TeamsHolidaySchedule |