Automation-Pipeline-Examples/update-module-and-pipelines.ps1

#Requires -Version 5.1
# AZLOCAL-UPDATER-VERSION: 1.1.0
<#
.SYNOPSIS
    Refresh the AzLocal.UpdateManagement CI/CD pipeline files in THIS repository
    to match the latest published module version, then commit and push.
 
.DESCRIPTION
    This script was dropped into your repository root by
    Copy-AzLocalPipelineExample. Run it whenever a new AzLocal.UpdateManagement
    version is published to PowerShell Gallery to:
 
      1. Install/upgrade the module to the latest published version (only when
         the gallery is newer than what is already installed), then import it.
      2. Refresh the bundled pipeline YAMLs via Update-AzLocalPipelineExample -
         a MARKER-AWARE merge that preserves any operator customisations you
         placed inside the AZLOCAL-CUSTOMIZE marker regions (schedule CRONs,
         service-connection names, runner labels, ITSM secret bindings, etc.).
      3. Stage ONLY the workflow folder, the repo-root config\ folder, and
         THIS script itself (which Update-AzLocalPipelineExample may have
         refreshed in place when the module shipped a newer template), then
         commit and push when (and only when) something actually changed.
 
    The target platform and workflow folder were baked in at drop time by
    Copy-AzLocalPipelineExample, so the script is turnkey for this repo. You
    can still override them via parameters.
 
.PARAMETER RepoRoot
    Repository root. Defaults to the folder this script lives in (the repo
    root, where Copy-AzLocalPipelineExample dropped it).
 
.PARAMETER Platform
    Pipeline platform this repo uses ('GitHub' or 'AzureDevOps'). Baked in at
    drop time.
 
.PARAMETER WorkflowSubPath
    Workflow / pipeline folder, RELATIVE to the repo root (for example
    '.github/workflows' on GitHub, or 'pipelines' on Azure DevOps). Baked in
    at drop time.
 
.PARAMETER Scope
    Install scope passed to Install-Module when an upgrade is required.
    'CurrentUser' (default) needs no elevation; 'AllUsers' requires an elevated
    session.
 
.PARAMETER NoPush
    Refresh the YAMLs only - skip the git add / commit / push. Review the
    result yourself with 'git status' / 'git diff'.
 
.EXAMPLE
    .\Update-Module-And-Pipelines.ps1
 
    Upgrade the module if needed, refresh the pipelines, then commit and push
    any changes.
 
.EXAMPLE
    .\Update-Module-And-Pipelines.ps1 -NoPush
 
    Refresh the pipelines but leave the commit/push to you.
 
.NOTES
    Platform : __PLATFORM__
    Workflow folder : __WORKFLOW_SUBPATH__
    Module : AzLocal.UpdateManagement
    Generated by : Copy-AzLocalPipelineExample
    Template version: 1.1.0 (managed file - Copy/Update-AzLocalPipelineExample
                      auto-refresh this script in place when the module ships a
                      newer template version. Tune behaviour via PARAMETERS,
                      not by editing the body; edits to the body are replaced on
                      refresh. Pass -SkipStarterUpdater to freeze it.)
#>

[CmdletBinding(SupportsShouldProcess = $true)]
param(
    [string]$RepoRoot = $PSScriptRoot,

    [ValidateSet('GitHub', 'AzureDevOps')]
    [string]$Platform = '__PLATFORM__',

    [string]$WorkflowSubPath = '__WORKFLOW_SUBPATH__',

    [ValidateSet('CurrentUser', 'AllUsers')]
    [string]$Scope = 'CurrentUser',

    [switch]$NoPush
)

$ErrorActionPreference = 'Stop'
$moduleName = 'AzLocal.UpdateManagement'

# ---------------------------------------------------------------------------
# 1. Ensure the latest published module version is installed and imported.
# We deliberately do NOT Uninstall-Module first - that needs elevation for
# AllUsers installs and would needlessly remove a pinned/side-by-side
# version. Install-Module -Force lays the new version down alongside.
# ---------------------------------------------------------------------------
$installed = Get-Module -ListAvailable -Name $moduleName |
    Sort-Object Version -Descending | Select-Object -First 1

$latest = $null
try {
    $latest = (Find-Module -Name $moduleName -ErrorAction Stop).Version
}
catch {
    Write-Warning "Could not query PowerShell Gallery for '$moduleName' ($($_.Exception.Message)). Falling back to the installed version."
}

if ($latest -and (-not $installed -or [version]$latest -gt [version]$installed.Version)) {
    $fromText = if ($installed) { $installed.Version } else { '(not installed)' }
    Write-Host "Installing $moduleName $latest (was $fromText)..." -ForegroundColor Cyan
    Install-Module -Name $moduleName -Scope $Scope -RequiredVersion $latest -Force -AllowClobber
}
elseif ($installed) {
    Write-Host "$moduleName is already up to date ($($installed.Version))." -ForegroundColor Green
}
else {
    throw "$moduleName is not installed and PowerShell Gallery could not be reached. Install it manually, then re-run."
}

Get-Module -Name $moduleName | Remove-Module -Force -ErrorAction SilentlyContinue
Import-Module -Name $moduleName -Force

$version = (Get-Module -Name $moduleName |
    Sort-Object Version -Descending | Select-Object -First 1).Version.ToString()
Write-Host "Using $moduleName $version." -ForegroundColor Green

# ---------------------------------------------------------------------------
# 2. Refresh the pipeline YAMLs (marker-aware merge; preserves customisations).
# ---------------------------------------------------------------------------
$workflowFull = Join-Path -Path $RepoRoot -ChildPath $WorkflowSubPath
if (-not (Test-Path -LiteralPath $workflowFull)) {
    throw "Workflow folder not found: '$workflowFull'. Pass -RepoRoot / -WorkflowSubPath to override."
}

Write-Host "Refreshing $Platform pipelines under '$workflowFull'..." -ForegroundColor Cyan
Update-AzLocalPipelineExample -Destination $workflowFull -Platform $Platform

# ---------------------------------------------------------------------------
# 3. Stage ONLY the workflow folder, the repo-root config\, and THIS script,
# then commit and push when there is something to commit. Scoping the
# 'git add' keeps unrelated working-tree changes out of this automated
# commit. '-A -- <path>' also captures the canonical-name renames/deletes
# that Update may perform.
# ---------------------------------------------------------------------------
if ($NoPush) {
    Write-Host "-NoPush set; skipping git commit/push. Review with 'git status' / 'git diff'." -ForegroundColor Yellow
    return
}

# Include THIS script too: Update-AzLocalPipelineExample may have refreshed it
# in place (version-gated self-update) when the module shipped a newer
# template. Staging it commits/pushes that improvement instead of leaving it as
# a dirty working-tree change. Only added when the script actually lives inside
# the repo (the normal dropped-at-repo-root case); resolved to a repo-relative,
# forward-slashed path so it survives the Test-Path filter below.
$selfRel = $null
if ($PSCommandPath) {
    try {
        $selfFull = (Resolve-Path -LiteralPath $PSCommandPath).ProviderPath
        $repoFull = (Resolve-Path -LiteralPath $RepoRoot).ProviderPath.TrimEnd('\', '/')
        if ($selfFull.StartsWith($repoFull, [System.StringComparison]::OrdinalIgnoreCase)) {
            $selfRel = $selfFull.Substring($repoFull.Length).TrimStart('\', '/') -replace '\\', '/'
        }
    }
    catch {
        $selfRel = $null
    }
}

$candidatePaths = @($WorkflowSubPath, 'config')
if ($selfRel) { $candidatePaths += $selfRel }
$gitPaths = $candidatePaths |
    Where-Object { Test-Path -LiteralPath (Join-Path -Path $RepoRoot -ChildPath $_) }
if ($gitPaths.Count -eq 0) {
    Write-Warning "Neither '$WorkflowSubPath' nor 'config' exists under '$RepoRoot'; nothing to stage."
    return
}

& git -C $RepoRoot add -A -- $gitPaths
if ($LASTEXITCODE -ne 0) { throw "git add failed (exit code $LASTEXITCODE)." }

& git -C $RepoRoot diff --cached --quiet
if ($LASTEXITCODE -eq 0) {
    Write-Host "No changes to commit - pipelines already match $moduleName $version." -ForegroundColor Green
    return
}

if ($PSCmdlet.ShouldProcess($RepoRoot, "git commit and push AzLocal pipeline refresh ($moduleName $version)")) {
    & git -C $RepoRoot commit -m "Refresh AzLocal update pipelines for $moduleName $version"
    if ($LASTEXITCODE -ne 0) { throw "git commit failed (exit code $LASTEXITCODE)." }
    & git -C $RepoRoot push
    if ($LASTEXITCODE -ne 0) { throw "git push failed (exit code $LASTEXITCODE)." }
    Write-Host "Pushed pipeline refresh for $moduleName $version." -ForegroundColor Green
}