Private/Send-AACBlobFile.ps1
|
function Send-AACBlobFile { <# .SYNOPSIS Uploads files to a blob container with the Azure Storage REST API (Put Blob) and an Entra ID token - no Az.Storage, no account key. .DESCRIPTION For Invoke-AACAssessment -StorageAccount/-StorageContainer (a scheduled run's reports): each file becomes a block blob named <-Prefix>/<its path under -Root>, replacing one of the same name. The signed-in identity needs Storage Blob Data Contributor on the account or container; the token is for https://storage.azure.com (Get-AACAccessToken -Resource), the managed identity's or service principal's in Azure Automation. Returns a line per file: @{ File; Blob; Url; Status; Error }. #> [CmdletBinding()] [OutputType([hashtable])] param( [Parameter(Mandatory)] [string] $StorageAccount, [Parameter(Mandatory)] [string] $Container, [Parameter(Mandatory)] [string[]] $Path, [Parameter(Mandatory)] [string] $Root, [string] $Prefix = '' ) $ProgressPreference = 'SilentlyContinue' $token = Get-AACAccessToken -Resource 'https://storage.azure.com' $types = @{ '.html' = 'text/html; charset=utf-8'; '.csv' = 'text/csv; charset=utf-8'; '.pdf' = 'application/pdf'; '.json' = 'application/json' } $rootFull = [System.IO.Path]::GetFullPath($Root).TrimEnd('\', '/') foreach ($file in $Path) { $full = [System.IO.Path]::GetFullPath($file) $relative = if ($full.StartsWith($rootFull, [System.StringComparison]::OrdinalIgnoreCase)) { $full.Substring($rootFull.Length).TrimStart('\', '/') } else { [System.IO.Path]::GetFileName($full) } $blob = (@($Prefix.Trim('/'), ($relative -replace '\\', '/')) | Where-Object { $_ }) -join '/' $url = "https://$StorageAccount.blob.core.windows.net/$Container/$(($blob -split '/' | ForEach-Object { [uri]::EscapeDataString($_) }) -join '/')" $extension = [System.IO.Path]::GetExtension($full).ToLowerInvariant() $headers = @{ Authorization = "Bearer $token"; 'x-ms-version' = '2023-11-03'; 'x-ms-blob-type' = 'BlockBlob'; 'x-ms-blob-content-type' = $(if ($types.Contains($extension)) { $types[$extension] } else { 'application/octet-stream' }) } try { $null = Invoke-WebRequest -Uri $url -Method Put -Headers $headers -InFile $full -ContentType 'application/octet-stream' -ErrorAction Stop -Verbose:$false @{ File = $full; Blob = $blob; Url = $url; Status = 'Uploaded'; Error = '' } } catch { $reason = $_.Exception.Message if ($_.ErrorDetails -and $_.ErrorDetails.Message -match '<Message>([^<]+)') { $reason = ($Matches[1] -split '\r?\n')[0] } @{ File = $full; Blob = $blob; Url = $url; Status = 'Failed'; Error = $reason } } } } |