#!/usr/bin/env pwsh <# .SYNOPSIS Build hardened Docker images for all Stella Ops services using the shared template/matrix. .DESCRIPTION PowerShell port of build-all.sh. Reads services-matrix.env (pipe-delimited) and builds each service image using Dockerfile.hardened.template (or Dockerfile.console for Angular). .PARAMETER Registry Docker image registry prefix. Default: stellaops .PARAMETER TagSuffix Tag suffix for built images. Default: dev .PARAMETER SdkImage .NET SDK base image. Default: mcr.microsoft.com/dotnet/sdk:10.0-noble .PARAMETER RuntimeImage .NET runtime base image. Default: mcr.microsoft.com/dotnet/aspnet:10.0-noble #> [CmdletBinding()] param( [string]$Registry = $env:REGISTRY ?? 'stellaops', [string]$TagSuffix = $env:TAG_SUFFIX ?? 'dev', [string]$SdkImage = $env:SDK_IMAGE ?? 'mcr.microsoft.com/dotnet/sdk:10.0-noble', [string]$RuntimeImage = $env:RUNTIME_IMAGE ?? 'mcr.microsoft.com/dotnet/aspnet:10.0-noble' ) $ErrorActionPreference = 'Continue' $Root = git rev-parse --show-toplevel 2>$null if (-not $Root) { Write-Error 'Not inside a git repository.' exit 1 } $Root = $Root.Trim() $MatrixPath = Join-Path $Root 'devops/docker/services-matrix.env' if (-not (Test-Path $MatrixPath)) { Write-Error "Matrix file not found: $MatrixPath" exit 1 } Write-Host "Building services from $MatrixPath -> ${Registry}/:${TagSuffix}" -ForegroundColor Cyan $succeeded = @() $failed = @() foreach ($line in Get-Content $MatrixPath) { $line = $line.Trim() if (-not $line -or $line.StartsWith('#')) { continue } $parts = $line -split '\|' if ($parts.Count -lt 5) { continue } $service = $parts[0] $dockerfile = $parts[1] $project = $parts[2] $binary = $parts[3] $port = $parts[4] $image = "${Registry}/${service}:${TagSuffix}" $dfPath = Join-Path $Root $dockerfile if (-not (Test-Path $dfPath)) { Write-Warning "Skipping ${service}: dockerfile missing ($dfPath)" continue } if ($dockerfile -like '*Dockerfile.console*') { Write-Host "[console] $service -> $image" -ForegroundColor Yellow docker build ` -f $dfPath $Root ` --build-arg "APP_DIR=$project" ` --build-arg "APP_PORT=$port" ` -t $image } else { Write-Host "[service] $service -> $image" -ForegroundColor Green docker build ` -f $dfPath $Root ` --build-arg "SDK_IMAGE=$SdkImage" ` --build-arg "RUNTIME_IMAGE=$RuntimeImage" ` --build-arg "APP_PROJECT=$project" ` --build-arg "APP_BINARY=$binary" ` --build-arg "APP_PORT=$port" ` -t $image } if ($LASTEXITCODE -eq 0) { $succeeded += $service } else { $failed += $service Write-Host "FAILED: $service" -ForegroundColor Red } } Write-Host '' Write-Host '=== BUILD RESULTS ===' -ForegroundColor Cyan Write-Host "Succeeded ($($succeeded.Count)): $($succeeded -join ', ')" -ForegroundColor Green Write-Host "Failed ($($failed.Count)): $($failed -join ', ')" -ForegroundColor $(if ($failed.Count -gt 0) { 'Red' } else { 'Green' }) Write-Host '' if ($failed.Count -gt 0) { Write-Error 'Some builds failed. Fix the issues and re-run.' exit 1 } Write-Host 'Build complete. Remember to enforce readOnlyRootFilesystem at deploy time and run sbom_attest.sh (DOCKER-44-002).' -ForegroundColor Cyan