Templates/customer-repo/governance/init.ps1
|
# ============================================================================ # MANAGED FILE - DO NOT EDIT IN THE CUSTOMER WORKSPACE # Source: Templates\customer-repo\governance\init.ps1 inside the # NKDAgility.AzureDevOps.Governance module. # This file is overwritten from that template on every run. Edit it there. # ============================================================================ <# .SYNOPSIS Governance capability loader for a customer workspace. .DESCRIPTION Dot-sourced by the workspace's root init.ps1 once it has materialised .system\NKDAgility.AzureDevOps.Governance. Imports that copy and reports the programs this workspace governs. A program is a folder under governance\programs\<name>\ holding: manifest.yaml program identity, organisation, accessToken variable name hierarchy.yaml the authored organisation hierarchy access.yaml roles and group catalog members\ membership per code key Build artefacts are written under the workspace output folder, never here. Secrets come from the workspace contract, not from a governance-specific file: secrets\secrets.json names an environment variable per organisation, the root init.ps1 exports them, and manifest.yaml references one by name as accessToken. .EXAMPLE Invoke-Governance build odyssey Invoke-Governance plan odyssey Invoke-Governance audit odyssey Invoke-Governance apply odyssey -WhatIf Invoke-Governance resolves the program and resolved.yaml paths from this folder and the workspace output folder. The module's own commands remain available if you want to be explicit about both. #> [CmdletBinding()] param( # Workspace root. The root init.ps1 passes this; it defaults to the parent of # this file so the capability can also be loaded on its own. [string]$WorkspaceRoot = (Split-Path -Parent $PSScriptRoot) ) $ErrorActionPreference = 'Stop' $moduleName = 'NKDAgility.AzureDevOps.Governance' $modulePath = Join-Path $WorkspaceRoot (Join-Path '.system' $moduleName) if (-not (Test-Path -LiteralPath $modulePath)) { throw "$moduleName is not materialised at '$modulePath'. Run the workspace's init.ps1, which copies it in from the tools clone." } # The module declares powershell-yaml as a requirement; importing it fails without. if (-not (Get-Module -ListAvailable -Name 'powershell-yaml')) { Write-Host '==> Installing powershell-yaml (required by the governance module)' -ForegroundColor Cyan Install-Module -Name 'powershell-yaml' -Scope CurrentUser -Force -AllowClobber } Import-Module $modulePath -Force $programsRoot = Join-Path $PSScriptRoot 'programs' # Invoke-Governance is global so it outlives this script, so it cannot close over a # script-scoped variable - by the time anyone calls it, this scope is gone. The roots go # in globals it can still reach. $Global:NkdaGovernanceProgramsRoot = $programsRoot $Global:NkdaGovernanceFallbackOut = Join-Path $PSScriptRoot 'out' # Resolve a program name to the two paths every governance command needs, so runbooks and # CI say 'odyssey' rather than repeating both. This is workspace glue, deliberately not # engine code: the module's commands stay explicit about what they read and write. function Global:Invoke-Governance { [CmdletBinding()] param( [Parameter(Mandatory, Position = 0)] [ValidateSet('build', 'validate', 'plan', 'apply', 'audit')] [string]$Command, [Parameter(Mandatory, Position = 1)] [string]$Program, [string]$Org, [switch]$WhatIf, [switch]$Prune ) $programPath = Join-Path $Global:NkdaGovernanceProgramsRoot $Program if (-not (Test-Path -LiteralPath (Join-Path $programPath 'manifest.yaml'))) { throw "No governance program '$Program' at '$programPath' (expected a manifest.yaml)." } $outputRoot = try { Join-Path (Get-AutomationWorkspace).OutputFolder 'governance' } catch { $Global:NkdaGovernanceFallbackOut } $resolvedPath = Join-Path (Join-Path $outputRoot $Program) 'resolved.yaml' New-Item -Path (Split-Path -Parent $resolvedPath) -ItemType Directory -Force | Out-Null switch ($Command) { 'build' { Invoke-GovernanceBuild -ProgramPath $programPath -OutputPath $resolvedPath } 'validate' { Test-Governance -ProgramPath $programPath } 'plan' { Invoke-GovernancePlan -ProgramPath $programPath -ResolvedPath $resolvedPath -Org $Org } 'audit' { Invoke-GovernanceAudit -ProgramPath $programPath -ResolvedPath $resolvedPath -Org $Org } 'apply' { Invoke-GovernanceApply -ProgramPath $programPath -ResolvedPath $resolvedPath -Org $Org -WhatIf:$WhatIf -Prune:$Prune } } } $programs = @(Get-ChildItem -LiteralPath $programsRoot -Directory -ErrorAction SilentlyContinue | Where-Object { Test-Path -LiteralPath (Join-Path $_.FullName 'manifest.yaml') }) if ($programs.Count) { Write-Host "==> Governance: $($programs.Count) program(s) - $($programs.Name -join ', ')" -ForegroundColor Cyan } else { Write-Host "==> Governance: no programs yet. Create governance\programs\<name>\manifest.yaml to start." -ForegroundColor DarkGray } |