Public/Test-VmPowerSchedule.ps1
|
function Test-VmPowerSchedule { <# .SYNOPSIS Check a schedule or a whole catalogue, and report what is wrong rather than throwing .DESCRIPTION Every path into the catalogue goes through this, which is why it reports instead of failing at the first problem: somebody validating twelve schedules wants all twelve answers, not the first one. Set-VmPowerSchedule calls it and refuses to store anything that does not pass. It also checks the things a schema cannot: two schedules with the same name, and a catalogue whose names would not survive being turned into the allowedValues of a policy. Pass -Detailed to get the expanded schedule back alongside the verdict, which is what Show-VmPowerScheduleCalendar consumes. .PARAMETER Schedule One schedule or a catalogue of them, in either the shorthand or the long form. .PARAMETER Detailed Include the expanded schedule on each result. .EXAMPLE # Validate a catalogue read out of a file before it goes anywhere near a deployment Get-Content ./catalog.json -Raw | ConvertFrom-Json | Test-VmPowerSchedule | Format-Table Name, Valid, Problem .EXAMPLE # The one-liner before storing New-VmPowerSchedule -Name office-hours-ch -TimeZone 'Europe/Zurich' -Weekdays '07:30-18:30' | Test-VmPowerSchedule .INPUTS Schedules, as objects or hashtables .OUTPUTS AzureVMPowerManagement.VmPowerScheduleValidation .NOTES RequiredPermissions: None. This command reads objects in memory. Prerequisites: PowerShell 7.2 or later. Writes: Nothing. #> [CmdletBinding()] [OutputType('AzureVMPowerManagement.VmPowerScheduleValidation')] param( [Parameter(Mandatory, ValueFromPipeline)] [AllowNull()] [object[]]$Schedule, [Parameter()] [switch]$Detailed ) begin { $collected = [System.Collections.Generic.List[object]]::new() } process { foreach ($entry in @($Schedule)) { if ($null -ne $entry) { $collected.Add($entry) } } } end { $results = [System.Collections.Generic.List[object]]::new() $seen = [System.Collections.Generic.Dictionary[string, int]]::new([System.StringComparer]::OrdinalIgnoreCase) foreach ($entry in $collected) { $name = [string](ConvertTo-VmPowerHashtable -InputObject $entry)['name'] $expanded = $null $problem = '' try { $expanded = Expand-VmPowerSchedule -Schedule $entry } catch { $problem = $_.Exception.Message } if (-not $problem -and $expanded) { $name = $expanded.Name if ($seen.ContainsKey($name)) { # A duplicate is not a schema error, and it is the one that bites: the second # entry silently wins and nobody can see which of the two is running. $problem = "Duplicate name. '$name' appears more than once in this catalogue, and only one of them can apply." } else { $seen[$name] = 1 } } $result = [ordered]@{ PSTypeName = $script:TypeName.Validation Name = if ($name) { $name } else { '(unnamed)' } Valid = [bool](-not $problem) Problem = $problem } # @(...)[0] because a collection landing on this property becomes a nested array in # anything that stores the result, and the read side can only report a schedule with # no name. Expand-VmPowerSchedule returns one object; this makes that true here too. if ($Detailed) { $result['Schedule'] = if ($null -eq $expanded) { $null } else { @($expanded)[0] } } $results.Add([pscustomobject]$result) } $results } } |