src/_Connect-CciBlobStore.ps1
|
function _Connect-CciBlobStore { <# .SYNOPSIS Establishes an Entra-authenticated context for the tenant distribution store. .DESCRIPTION Machine builders hold `Storage Blob Data Reader` on the tenant store via an Entra group and need no Azure DevOps entitlement. Auth order: 1. A live Azure CLI session, if present AND signed in to the tenant that owns the store (no prompt). 2. Device-code sign-in over plain REST (_Get-CciEntraToken). No Az PowerShell modules are used - see _Get-CciEntraToken for why. Returns a context object (Account/Token/TenantId) or $null. #> [CmdletBinding()] param( [Parameter(Mandatory)]$Feed ) $accountProp = $Feed.PSObject.Properties['blobAccount'] $account = if ($accountProp) { $accountProp.Value } if ([string]::IsNullOrWhiteSpace($account)) { Write-Verbose "cciget: tenant '$($Feed.name)' has no blobAccount configured." return $null } # Read through PSObject.Properties throughout: the module loads under # Set-StrictMode -Version Latest, where touching an absent property throws. $tenantProp = $Feed.PSObject.Properties['tenantId'] $tenantId = if ($tenantProp) { $tenantProp.Value } if ($Script:CciGetBlobContext -and $Script:CciGetBlobContext.Account -eq $account) { return $Script:CciGetBlobContext } $token = $null # 1. existing Azure CLI session - but ONLY when it is signed in to the # tenant that owns the store. az mints a storage token from whatever # tenant the session happens to be in; the store then rejects it, and # before 0.6.2 that arrived as a false "this store has no module index # yet" against a store that was healthy and full. A technician whose # default az context is some other tenant hit it every time, while a # machine with no az session at all worked - because the device-code # path below always signs in against $Feed.tenantId explicitly. # A feed with no tenantId keeps the old take-what-az-gives behaviour; # there is nothing to compare against. if (Get-Command az -ErrorAction SilentlyContinue) { $azTenant = $null try { $azTenant = az account show --query tenantId -o tsv 2>$null } catch { } # -o tsv hands back a string[] if anything ever emits more than one line. if ($azTenant -is [System.Array]) { $azTenant = @($azTenant)[0] } $azTenant = "$azTenant".Trim() if (-not $azTenant) { Write-Verbose 'cciget: no Azure CLI session to borrow a storage token from.' } elseif ($tenantId -and $azTenant -ne $tenantId) { Write-Verbose ("cciget: the Azure CLI session is signed in to tenant '$azTenant' but the " + "'$($Feed.name)' distribution store is in '$tenantId'; ignoring that session and signing in separately.") } else { try { $token = az account get-access-token --resource 'https://storage.azure.com' --query accessToken -o tsv 2>$null if ($token) { Write-Verbose 'cciget: storage token from the Azure CLI session.' } } catch { } } } # 2. device code over REST if (-not $token) { if (-not $tenantId) { Write-Warning "cciget: tenant '$($Feed.name)' has no tenantId; cannot sign in to the distribution store." return $null } $clientProp = $Feed.PSObject.Properties['clientId'] $clientId = if ($clientProp -and $clientProp.Value) { $clientProp.Value } else { '04b07795-8ddb-461a-bbee-02f9e1bf7b46' } Write-Host 'cciget: signing in to the tenant distribution store...' $token = _Get-CciEntraToken -TenantId $tenantId -Scope 'https://storage.azure.com/.default' -ClientId $clientId if (-not $token) { return $null } } $ctx = [pscustomobject]@{ Account = $account Token = $token TenantId = $tenantId } $Script:CciGetBlobContext = $ctx return $ctx } |