Private/Set-VmPowerCatalogVariable.ps1
|
function Set-VmPowerCatalogVariable { <# .SYNOPSIS Write a catalogue into one Automation variable. .DESCRIPTION Only ever called for the custom variable. The deployment owns PM_ScheduleCatalog and overwrites it on every run; nothing in this module writes there, which is what stops a redeployment eating somebody's work - see docs/decisions/0005. The value is written as JSON and read back with ConvertFrom-Json, so what goes in is what comes out. Never encrypted: the workbook reads this variable over ARM, and an encrypted variable does not return its value there. .PARAMETER ResourceGroupName Resource group holding the Automation Account. .PARAMETER AutomationAccountName The Automation Account. .PARAMETER VariableName The variable to write. .PARAMETER Catalog The schedules to store. .PARAMETER SubscriptionId Subscription holding the account. Defaults to the current context. #> [CmdletBinding()] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = 'Private helper. The confirmation belongs at the public boundary, in Set-VmPowerSchedule and Remove-VmPowerSchedule, where the caller can see which schedules are involved. Asking twice for one action is worse than asking once in the right place.')] [OutputType([void])] param( [Parameter(Mandatory)] [string]$ResourceGroupName, [Parameter(Mandatory)] [string]$AutomationAccountName, [Parameter(Mandatory)] [string]$VariableName, [Parameter(Mandatory)] [AllowEmptyCollection()] [object[]]$Catalog, [Parameter()] [string]$SubscriptionId ) # A catalogue entry that is itself a collection serialises as a nested array, and the reader # then sees a schedule with no name. That reached a real Automation Account once, on # 2026-09-10, and the read side only said "name '' is not usable" - true, and useless. Refusing # here names the entry instead. $flat = [System.Collections.Generic.List[object]]::new() foreach ($entry in @($Catalog)) { if ($null -eq $entry) { continue } if ($entry -is [System.Collections.IEnumerable] -and $entry -isnot [string]) { throw ("A catalogue entry is a collection rather than a schedule, so the value would be " + 'a nested array that nothing can read back. This is a bug in the caller, not in the ' + 'catalogue: pass one schedule per entry.') } $flat.Add($entry) } $subscription = if ($SubscriptionId) { $SubscriptionId } else { Resolve-VmPowerSubscription } $uri = ('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Automation/automationAccounts/{2}/variables/{3}?api-version={4}' -f $subscription, $ResourceGroupName, $AutomationAccountName, $VariableName, $script:AutomationApiVersion) $json = ConvertTo-Json -InputObject @($flat.ToArray()) -Depth 8 -Compress $limit = 1048576 if ($json.Length -gt $limit) { throw "The catalogue is $($json.Length) characters and an Automation variable holds $limit. That is thousands of schedules, so this is far more likely to be a loop than a catalogue." } $body = @{ name = $VariableName properties = @{ value = $json isEncrypted = $false description = 'Schedules added with Set-VmPowerSchedule. The deployment never writes here.' } } | ConvertTo-Json -Depth 8 -Compress $response = Invoke-AzRestMethod -Path $uri -Method PUT -Payload $body -ErrorAction Stop if ($response.StatusCode -notin 200, 201) { throw "Writing the Automation variable $VariableName returned $($response.StatusCode): $($response.Content)" } Write-Verbose "Wrote $($flat.Count) schedule(s), $($json.Length) characters, to $VariableName." } |