Private/Invoke-AACStoragePlan.ps1
|
function Invoke-AACStoragePlan { <# .SYNOPSIS Applies a storage plan's changes in order - the apply step of Deploy-AACStorageAccount. .DESCRIPTION Create, Update and Delete changes, in the plan's order (the account, its services, containers and the rest, private endpoints, diagnostic settings, role assignments, the lock, then blobs): Resource Manager PUT, PATCH or DELETE (Invoke-AACArmWrite, which waits for long-running operations) blobs Put Blob with the file and its Content-MD5, with the Connect-AAC sign-in (Entra ID) - Storage Blob Data Contributor needed Not a transaction: the first failure stops the run - what came before stays applied (running again carries on from there, as every step is idempotent), what comes after is Skipped. A write a policy denies says so (RequestDisallowedByPolicy). Returns AAC.StorageApplyResult rows: Order, Resource, Action, Status (Applied, Failed, Skipped), Detail, Seconds. #> [CmdletBinding()] [OutputType([object[]])] param( [Parameter(Mandatory)] [AllowEmptyCollection()] [object[]] $Change ) $writes = @($Change | Where-Object Action -In 'Create', 'Update', 'Delete') $results = [System.Collections.Generic.List[object]]::new() $failed = $false Update-AACProgress -Id 'apply' -Total ([Math]::Max($writes.Count, 1)) -Description "Applying $($writes.Count) change(s)" foreach ($item in $writes) { if ($failed) { $results.Add([pscustomobject]@{ PSTypeName = 'AAC.StorageApplyResult'; Order = $item.Order; Resource = $item.Resource; Action = $item.Action; Status = 'Skipped'; Detail = 'An earlier change failed.'; Seconds = 0 }) continue } Update-AACProgress -Id 'apply' -Description "$($item.Action): $($item.Resource)" $clock = [System.Diagnostics.Stopwatch]::StartNew() try { if ($item.Kind -eq 'blob') { $bytes = [System.IO.File]::ReadAllBytes($item.Source) $null = Invoke-AACHttp -Method Put -Uri $item.Id -Resource 'https://storage.azure.com' -BodyBytes $bytes -ContentType $item.ContentType -Header @{ 'x-ms-version' = '2023-11-03'; 'x-ms-blob-type' = 'BlockBlob'; 'Content-MD5' = $item.ContentMD5 } } else { $null = Invoke-AACArmWrite -Method $(if ($item.Action -eq 'Delete') { 'Delete' } else { $item.Method }) -Uri $item.Uri -Body $item.Body } $results.Add([pscustomobject]@{ PSTypeName = 'AAC.StorageApplyResult'; Order = $item.Order; Resource = $item.Resource; Action = $item.Action; Status = 'Applied'; Detail = ''; Seconds = [Math]::Round($clock.Elapsed.TotalSeconds, 1) }) } catch { $failed = $true $code = [string]$_.Exception.Data['Code'] $detail = if ($code -eq 'RequestDisallowedByPolicy') { "Denied by Azure Policy: $($_.Exception.Message)" } else { $_.Exception.Message } $results.Add([pscustomobject]@{ PSTypeName = 'AAC.StorageApplyResult'; Order = $item.Order; Resource = $item.Resource; Action = $item.Action; Status = 'Failed'; Detail = $detail; Seconds = [Math]::Round($clock.Elapsed.TotalSeconds, 1) }) } Update-AACProgress -Id 'apply' -Increment 1 } $applied = @($results | Where-Object Status -EQ 'Applied').Count Update-AACProgress -Id 'apply' -Complete -Description "Applied $applied of $($writes.Count) change(s)$(if ($failed) { ' - stopped at a failure' })" $results.ToArray() } |