Files
AutopilotMonitor/.github/scripts/Get-StorageAuthHeaders.ps1
T
Oliver Kieselbach 9f46adcdfa ci: the storage auth helper masks its token in the run log, like the Key Vault token
GitHub masks only secrets.* by itself. The bearer token Get-StorageAuthHeaders.ps1
mints at run time was never registered as a mask, unlike the Key Vault signing token,
which build-agent, build-msi and publish-scripts mask before exporting it. No caller
prints it today -- every call site captures the hashtable and passes it only as
-Headers -- but this repository's run logs are public, and a single debugging line
would have published it: an uncaptured call of the helper, or Get-Error /
Format-List on a failed request, whose TargetObject is the HttpRequestMessage
including the Authorization header (reproduced locally with a dummy token).

The helper now emits ::add-mask:: for every token it hands out, which covers all six
workflows in one place. Write-Host, never Write-Output: the output stream is the
script's return value. Verified with a mocked az that the result stays one hashtable
and that a nested caller in the Request-BuildNumber.ps1 pattern still returns only
its build number. Only under GITHUB_ACTIONS -- locally the line would print the token
instead of hiding it.

Prompted by an external AI review of the public scripts.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-04 12:52:01 +02:00

62 lines
3.0 KiB
PowerShell

<#
.SYNOPSIS
Returns the request headers that authorize an Azure Storage REST call with Microsoft Entra.
.DESCRIPTION
One place for the storage OAuth contract shared by every publisher in this repo -- the
agent ZIP and MSI, the customer bootstrap scripts, the version manifests and the build
counters. Resource id, the az invocation, the service version and the failure message
live here so they cannot drift between call sites.
Identity comes from whatever `az login` established: the federated GitHub OIDC principal
in CI (azure/login, subject repo:okieselbach/AutopilotMonitor:ref:refs/heads/main), the
operator's own account for the local build. No secret is read, stored or passed around.
Under GitHub Actions the token is masked in the run log before it is returned.
x-ms-version is MANDATORY here, not decoration: Entra-authorized requests are rejected
below 2017-11-09, and Table requests without a SAS must carry the header at all. 2021-08-06
is old enough to be deployed in every region and new enough for everything we send.
Callers merge the result into their own headers -- `$auth + @{ ... }` yields a new
hashtable per request, so nothing is shared across uploads:
$auth = & "$PSScriptRoot/Get-StorageAuthHeaders.ps1"
Invoke-RestMethod -Uri $url -Method Put -Headers ($auth + @{ 'x-ms-blob-type' = 'BlockBlob' }) -Body $bytes
Acquire once per step and reuse: every call shells out to az (~1s) and the token is valid
for the lifetime of a job.
.OUTPUTS
[hashtable] Authorization + x-ms-version.
#>
[CmdletBinding()]
param()
$ErrorActionPreference = 'Stop'
# The data-plane resource, not management.azure.com -- a management token is accepted by
# neither the blob nor the table endpoint.
$token = az account get-access-token --resource https://storage.azure.com --query accessToken -o tsv 2>$null
if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($token)) {
throw ('Could not acquire an Azure Storage token. In CI this means the azure/login step ' +
'is missing or its federated credential does not cover this ref; locally, run ' +
'`az login`. A token alone is not enough: the principal also needs Storage Blob ' +
'Data Contributor on the container it writes, and Storage Table Data Contributor ' +
'on the table -- a missing role shows up later as HTTP 403 AuthorizationPermissionMismatch.')
}
# GitHub masks only secrets.* on its own; a token minted at run time is clear text in the
# (public) run log the moment anything prints it -- an uncaptured call of this script, or
# Get-Error / Format-List on a failed request, whose TargetObject carries this header.
# Write-Host, never Write-Output: the output stream is this script's return value. Only
# under Actions -- anywhere else the line would print the token instead of hiding it.
if ($env:GITHUB_ACTIONS -eq 'true') {
Write-Host "::add-mask::$token"
}
return @{
'Authorization' = "Bearer $token"
'x-ms-version' = '2021-08-06'
}