src/Publish-CciModuleToBlob.ps1
|
function Publish-CciModuleToBlob { <# .SYNOPSIS Mirrors a built module into the tenant distribution store and updates its index. .DESCRIPTION The tenant Azure Artifacts feed is the canonical publish target, but reading it requires an Azure DevOps org entitlement and a paid Basic licence per user (Stakeholder has no Artifacts access). Machine builders have neither - they hold Azure RBAC on the tenant store instead. This mirrors the same published module there so `Install-CciModule` can serve them without any Azure DevOps identity. Intended for the tenant publish pipeline (running under a service connection with Storage Blob Data Contributor) or for an operator with a temporary write grant. .PARAMETER Path Folder containing the built module (the one holding its .psd1). .PARAMETER Name Published module name, e.g. cciit.wincustpayload. Defaults to the manifest's base name. .PARAMETER Version Version to publish under. Defaults to the manifest's ModuleVersion. .PARAMETER Tenant Tenant whose distribution store to publish to. Defaults to defaultFeed. .EXAMPLE Publish-CciModuleToBlob -Path .\out\stage\cciit.wincustpayload #> [CmdletBinding(SupportsShouldProcess)] param( [Parameter(Mandatory)] [ValidateScript({ Test-Path $_ -PathType Container })] [string]$Path, [string]$Name, [string]$Version, [string]$Tenant ) if (-not $Tenant) { $Tenant = (Get-CciGetConfig).defaultFeed } $feed = (_Resolve-CciGetFeed -Tenant $Tenant)[0] $psd1 = Get-ChildItem $Path -Filter '*.psd1' | Select-Object -First 1 if (-not $psd1) { throw "cciget: no module manifest found under '$Path'." } $manifest = Import-PowerShellDataFile $psd1.FullName if (-not $Name) { $Name = $psd1.BaseName } if (-not $Version) { $Version = "$($manifest.ModuleVersion)" } $ctx = _Connect-CciBlobStore -Feed $feed if (-not $ctx) { throw "cciget: could not connect to the distribution store for '$($feed.name)'." } $files = Get-ChildItem $Path -Recurse -File if (-not $files) { throw "cciget: '$Path' contains no files." } $prefix = "$($feed.blobPrefix)/$Name/$Version" if (-not $PSCmdlet.ShouldProcess("$($feed.blobAccount)/$($feed.blobContainer)/$prefix", "upload $($files.Count) files")) { return } $rootLen = (Get-Item $Path).FullName.Length + 1 foreach ($f in $files) { $rel = $f.FullName.Substring($rootLen) -replace '\\', '/' $null = Set-AzStorageBlobContent -Context $ctx -Container $feed.blobContainer ` -Blob "$prefix/$rel" -File $f.FullName -Force -ErrorAction Stop } Write-Host "cciget: mirrored $Name $Version ($($files.Count) files) to the '$($feed.name)' distribution store." # Update the index (read-modify-write; the pipeline publishes one module at a time). $index = _Get-CciBlobModuleIndex -Feed $feed -Context $ctx if (-not $index) { $index = [pscustomobject]@{ modules = [pscustomobject]@{} } } if (-not $index.PSObject.Properties['modules'] -or -not $index.modules) { $index | Add-Member -NotePropertyName modules -NotePropertyValue ([pscustomobject]@{}) -Force } $entry = [pscustomobject]@{ latest = $Version; updated = (Get-Date).ToUniversalTime().ToString('o') } $index.modules | Add-Member -NotePropertyName $Name -NotePropertyValue $entry -Force $tmp = Join-Path ([IO.Path]::GetTempPath()) "cciget-index-$([guid]::NewGuid().ToString('n')).json" try { [System.IO.File]::WriteAllText($tmp, ($index | ConvertTo-Json -Depth 6), (New-Object System.Text.UTF8Encoding $false)) $null = Set-AzStorageBlobContent -Context $ctx -Container $feed.blobContainer ` -Blob "$($feed.blobPrefix)/index.json" -File $tmp -Force -ErrorAction Stop } finally { Remove-Item $tmp -Force -ErrorAction SilentlyContinue } Write-Host "cciget: index updated - $Name latest = $Version." } |