Chapter 19Lesson 05~250 minutes

Release Orchestration, Artifacts, Machine Output, and Cross-Tool Contracts

Combine source identity, quality evidence, immutable artifacts, checksums, explicit environment selection, machine-readable manifests, approval boundaries, rollback metadata, and observability handoff into a provider-neutral release orchestration skeleton.

Release orchestrationArtifactsContractsRollback metadata

Learning objectives

  • Explain release orchestration as coordination of evidence and contracts.
  • Separate human-readable logs from stable machine-readable output.
  • Create artifact manifests with SHA-256 integrity records.
  • Model quality, target environment, approval, and rollback metadata explicitly.
  • Preserve CI exit-code and observability handoff contracts.
  • Build a provider-neutral release preparation mini-project that performs no irreversible deployment.

1. A release orchestrator coordinates evidence; it should not hide irreversible change

A release orchestrator gathers trusted inputs, runs gates, creates immutable artifacts, records checksums and metadata, selects an intended environment, and hands a clear deployment contract to the next system. It is not required to perform every deployment itself.

For a beginner-safe design, separate prepare from deploy. Preparation can be deterministic and local. Deployment can remain an approval placeholder or provider-specific adapter until the organization has appropriate credentials, policy, rollback, and observability.

A release orchestrator coordinates evidence; it should not hide irreversible change
flowchart TD
    G[Git identity] --> P[prepare release]
    T[Test/analyzer evidence] --> P
    P --> A[artifact + checksum]
    P --> M[release manifest]
    M --> Q{approval / policy gate}
    A --> Q
    Q -->|approved| D[provider deployment adapter]
    Q -->|not approved| S[stop safely]
    D --> O[observability + rollback metadata]

2. Human output and machine output serve different consumers

A terminal summary can use color, tables, and explanatory prose. A machine contract should use stable fields and formats such as JSON, CSV, JUnit XML, hashes, and artifact manifests. Do not force automation to parse your pretty console table.

Consumer Prefer Example
Operator Concise human summary Release 1.4.0 prepared; 3 artifacts; target=staging
CI engine Exit status + known artifact paths 0 plus reports/release-manifest.json
Deployment adapter Stable structured manifest Artifact SHA-256, environment, source commit
Incident/rollback workflow Traceability metadata Previous version, build ID, commit, manifest hash

3. Create one release manifest as the handoff contract

The manifest should describe what was prepared, not claim what was deployed. Include source identity, quality state, artifact paths, cryptographic hashes, target intent, and creation time. Avoid secret values.

function New-ReleaseManifest {
    param(
        [Parameter(Mandatory)][string]$Version,
        [Parameter(Mandatory)][string]$Commit,
        [Parameter(Mandatory)][string]$Environment,
        [Parameter(Mandatory)][object[]]$Artifacts,
        [Parameter(Mandatory)][object]$Quality
    )

    [pscustomobject]@{
        SchemaVersion = 1
        ReleaseId     = [guid]::NewGuid().ToString()
        Version       = $Version
        Source        = [pscustomobject]@{ Commit=$Commit }
        Target        = [pscustomobject]@{ Environment=$Environment }
        Quality       = $Quality
        Artifacts     = $Artifacts
        CreatedUtc    = [datetime]::UtcNow.ToString('o')
        Deployment    = [pscustomobject]@{
            Status = 'Prepared'
            ApprovalRequired = $true
            Mode = 'PrepareOnly'
            Checkpoint = 'manifest-created'
        }
    }
}

4. Artifact identity needs hashes, not filenames alone

A filename can be reused. A checksum detects content changes. SHA-256 is a common integrity primitive for release artifacts. Store both relative path and hash in the manifest, then verify the hash immediately before deployment or promotion.

function Get-ArtifactRecord {
    param([Parameter(Mandatory)][string]$Path,[Parameter(Mandatory)][string]$Root)

    $file = Get-Item -LiteralPath $Path -ErrorAction Stop
    $hash = Get-FileHash -LiteralPath $file.FullName -Algorithm SHA256

    [pscustomobject]@{
        Path   = [IO.Path]::GetRelativePath($Root,$file.FullName)
        Bytes  = $file.Length
        SHA256 = $hash.Hash.ToLowerInvariant()
    }
}

5. Quality gates become fields, not vague confidence

A release manifest can record whether tests passed, analyzer issues remained, and required reports exist. Coverage may be included, but Chapter 18 established that coverage percentage is evidence, not a quality score by itself.

$quality = [pscustomobject]@{
    TestsPassed       = $true
    FailedTests       = 0
    AnalyzerErrors    = 0
    CoverageAvailable = $false
    Reports           = @('reports/ci-events.json','reports/analyzer.json')
}

if (-not $quality.TestsPassed -or $quality.FailedTests -gt 0 -or $quality.AnalyzerErrors -gt 0) {
    throw 'Release preparation blocked by quality policy.'
}

6. Environment, dry-run, checkpoints, and approval must be explicit

“Deploy” without a target is unsafe. Validate environment names, keep production out of implicit defaults, and require a separate approval/control plane before irreversible change. A checkpoint records how far preparation safely progressed; a dry-run/prepare-only mode produces evidence without crossing the deployment boundary.

param(
    [Parameter(Mandatory)]
    [ValidateSet('dev','staging','production')]
    [string]$Environment
)

if ($Environment -eq 'production') {
    Write-Warning 'Production deployment is not performed by this training orchestrator.'
}

$mode = 'PrepareOnly'
$checkpoint = 'artifact-verified'
$deploymentAllowed = $false  # approval placeholder

A real system might obtain approval from a CI environment protection rule, change-management system, signed promotion record, or operator action. Do not represent a comment such as “approval later” as a security control.

7. Rollback metadata must exist before rollout

Rollback is easiest when the release records the previous known-good artifact/version, target environment, and the exact new artifact hashes. Do not wait for failure to ask “what was there before?”

$rollback = [pscustomobject]@{
    PreviousVersion = '1.3.7'
    PreviousManifest = 'releases/1.3.7/release-manifest.json'
    Strategy = 'RedeployPreviousImmutableArtifact'
}

$rollback | ConvertTo-Json -Depth 4

Some systems roll forward instead of back. The manifest still needs enough evidence to decide which recovery action is safe.

8. Release completion is an observability handoff

A deployment should emit identifiers that monitoring and incident systems can correlate: release ID, version, commit, environment, deployment timestamp, and possibly change ticket. Logs and metrics should not need to reverse-engineer a filename to discover what is running.

$handoff = [pscustomobject]@{
    ReleaseId   = $manifest.ReleaseId
    Version     = $manifest.Version
    Commit      = $manifest.Source.Commit
    Environment = $manifest.Target.Environment
    ManifestSha256 = (Get-FileHash ./release-manifest.json -Algorithm SHA256).Hash.ToLowerInvariant()
}

$handoff | ConvertTo-Json -Compress

9. Capstone lab: provider-neutral release preparation

This mini-project builds a disposable release workspace. It creates a source file, packages it, computes a checksum, records quality evidence, writes a release manifest, validates the manifest, and stops at an approval placeholder. It does not push Git, publish a package, contact a cloud, or deploy production.

$root = Join-Path ([IO.Path]::GetTempPath()) ('ps-ch19-release-' + [guid]::NewGuid())
$src = Join-Path $root 'src'
$dist = Join-Path $root 'dist'
$reports = Join-Path $root 'reports'
New-Item -ItemType Directory -Path $src,$dist,$reports -Force | Out-Null

# Simulated source identity; in a real repo use Lesson 1's Git metadata adapter.
$source = [pscustomobject]@{
    Commit   = '0123456789abcdef0123456789abcdef01234567'
    ShortSha = '0123456789ab'
    Dirty    = $false
}

if ($source.Dirty) { throw 'Dirty source cannot be released.' }

@'
function Get-ReleaseGreeting {
    param([string]$Name = 'world')
    "hello $Name"
}
'@ | Set-Content -LiteralPath (Join-Path $src 'ReleaseTools.ps1')

$package = Join-Path $dist 'release-tools.zip'
Compress-Archive -Path (Join-Path $src '*') -DestinationPath $package

$artifact = Get-ArtifactRecord -Path $package -Root $root
$quality = [pscustomobject]@{
    TestsPassed    = $true
    FailedTests    = 0
    AnalyzerErrors = 0
    Reports        = @()
}

$version = "0.1.0+$($source.ShortSha)"
$manifest = New-ReleaseManifest -Version $version -Commit $source.Commit -Environment staging -Artifacts @($artifact) -Quality $quality
$manifest | Add-Member -NotePropertyName Rollback -NotePropertyValue ([pscustomobject]@{
    PreviousVersion = '0.0.9'
    Strategy = 'RedeployPreviousImmutableArtifact'
})

$manifestPath = Join-Path $root 'release-manifest.json'
$manifest | ConvertTo-Json -Depth 10 | Set-Content -LiteralPath $manifestPath

# Verify the artifact against the manifest before handoff.
$loaded = Get-Content -Raw -LiteralPath $manifestPath | ConvertFrom-Json
foreach ($entry in $loaded.Artifacts) {
    $path = Join-Path $root $entry.Path
    $actual = (Get-FileHash -LiteralPath $path -Algorithm SHA256).Hash.ToLowerInvariant()
    if ($actual -ne $entry.SHA256) { throw "Artifact checksum mismatch: $($entry.Path)" }
}

[pscustomobject]@{
    ReleaseId   = $loaded.ReleaseId
    Version     = $loaded.Version
    Environment = $loaded.Target.Environment
    ArtifactCount = @($loaded.Artifacts).Count
    Status      = $loaded.Deployment.Status
    ApprovalRequired = $loaded.Deployment.ApprovalRequired
} | Format-List

"Release workspace retained for inspection: $root"

Expected observations

Status is Prepared, approval is required, the artifact hash verifies, and the manifest contains no credentials. Nothing has been deployed.

Verification checklist

  • The source identity is explicit.
  • The ZIP artifact is immutable evidence referenced by SHA-256.
  • Quality state is present before release preparation succeeds.
  • Environment is explicit and production is not a hidden default.
  • Rollback metadata exists before deployment.
  • The workflow stops at a clear approval/deployment boundary.

Cleanup

Remove-Item -LiteralPath $root -Recurse -Force

10. Cross-tool contracts are the main Chapter 19 skill

Git produces source identity. Pester and PSScriptAnalyzer produce quality evidence. Docker/Kubernetes/cloud clients expose deployment state. CI provides orchestration and artifact retention. PowerShell ties these together by translating each external contract into a small internal object model.

This is why the chapter does not try to teach every downstream platform. The scalable skill is boundary design: arguments, exit status, machine output, authentication/context, stable objects, artifacts, and failure behavior.

11. Failure should preserve evidence and stop at the right boundary

If packaging fails, do not fabricate a manifest. If tests fail, do not mark the release prepared. If checksum verification fails, stop before deployment. If one provider adapter is unavailable, return a structured capability failure rather than silently targeting another environment.

try {
    # Prepare-Release would return a manifest path/object on success.
    $release = $manifest
    if ($release.Deployment.Status -ne 'Prepared') {
        throw 'Unexpected release state.'
    }

    # Deliberate boundary: no production deployment in this course lab.
    Write-Verbose "Prepared release $($release.ReleaseId)"
}
catch {
    [pscustomobject]@{
        Success = $false
        Stage   = 'prepare-release'
        Error   = $_.Exception.Message
    }
    throw
}

12. Production review checklist

  • Source: exact commit recorded; dirty state policy explicit.
  • Dependencies: PowerShell/modules/tool versions pinned or verified.
  • Quality: tests/analyzer reports retained; gate policy explicit.
  • Artifacts: immutable names/paths plus checksums.
  • Target: environment/context/subscription/region explicit.
  • Secrets: injected by platform/vault, never written to manifests.
  • Approval: production promotion separated from preparation.
  • Recovery: previous version/strategy known before rollout.
  • Observability: release ID/version/commit propagated to telemetry.

13. Knowledge check

Question 1. Why separate release preparation from deployment?

Question 2. Why store a SHA-256 with each artifact?

Question 3. What should machine consumers parse?

Question 4. What does a nonzero top-level exit status communicate to CI?

Question 5. Why record rollback metadata before deployment?

14. Chapter summary and final-capstone bridge

Chapter 19 connected PowerShell to the larger delivery system without pretending PowerShell replaces Git, CI platforms, Docker, Kubernetes, Helm, or cloud SDKs. You learned to treat each integration as a contract: arguments, exit codes, machine-readable output, explicit target context, normalized objects, retained artifacts, checksums, and bounded failure behavior.

Chapter 20 turns these skills into production automation architecture: idempotency, retries/checkpoints, modern DSC context, observability/runbooks, deployment/versioning structure, and a final cross-platform DevOps automation toolkit.

15. Authoritative references

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.