Public/Deploy-ADCSGoat.ps1
|
function Deploy-ADCSGoat { <# .SYNOPSIS Runs the ADCSGoat deploy spine: select the CA, run the preflight report, and write the state file before any AD write. .DESCRIPTION Implements the deploy spine settled in ADCSGoat issue #26. The four scenario deploys plug into this spine; on its own it performs zero AD writes and is safe to run in any forest. Ordering contract (every failure aborts before any AD write): 1. CA selection Resolve the one issuing CA via -CAName, or autodetect when the forest has exactly one enterprise CA. Multi-CA without -CAName and an invalid name both fail here. 2. Preflight report Hard prerequisites abort with a prerequisite error naming the check + remediation; soft observations warn and continue. Nothing is ever changed to satisfy a check. 3. State capture The selected CA's certificateTemplates list and nTSecurityDescriptor are captured for byte-for- byte restoration at teardown. 4. State file Written to disk before any AD change. Scenario deploys append per-clone records as they run. .PARAMETER CAName The cn of the enterprise CA's pKIEnrollmentService object. Optional; autodetected when the forest has exactly one enterprise CA. .PARAMETER StatePath Where the state file is written. Defaults to ADCSGoat.State.xml next to the module root. .PARAMETER SelectedCA Internal seam: a pre-resolved CA object (Name + DistinguishedName). When supplied, CA selection is skipped. Exists so orchestration tests can inject a broken CA without mutating AD. .PARAMETER Server The domain controller to contact. Defaults to the logon server. .OUTPUTS System.Management.Automation.PSCustomObject with SelectedCA, PreflightReport, State, and StatePath. .EXAMPLE Deploy-ADCSGoat Autodetects the single CA, runs preflight, writes the state file. .EXAMPLE Deploy-ADCSGoat -CAName 'LabRootCA1' Selects the named CA explicitly. #> [CmdletBinding()] param ( [Parameter()] [ValidateNotNullOrEmpty()] [string]$CAName, [Parameter()] [ValidateNotNullOrEmpty()] [string]$StatePath, [Parameter()] [ValidateNotNull()] [pscustomobject]$SelectedCA, [Parameter()] [ValidateNotNullOrEmpty()] [string]$Server ) begin { if ([string]::IsNullOrEmpty($StatePath)) { $StatePath = Join-Path -Path $PSScriptRoot -ChildPath '..\ADCSGoat.State.xml' } } process { # Forward -Server to the helpers only when the caller supplied it; # the helpers default to the logon server themselves and reject an # explicit null/empty value. $helperParams = @{} if ($PSBoundParameters.ContainsKey('Server')) { $helperParams['Server'] = $Server } # 1. CA selection. Fails (CANotFound / MultipleCAsFound / # NoEnterpriseCAFound) before anything else happens. if ($null -eq $SelectedCA) { if ($PSBoundParameters.ContainsKey('CAName')) { $SelectedCA = Get-AGEnrollmentService -CAName $CAName @helperParams } else { $SelectedCA = Get-AGEnrollmentService @helperParams } } Write-Verbose "Deploying ADCSGoat to '$($SelectedCA.FullName)'." # 2. Preflight report. A failed hard prerequisite aborts here; soft # observations warn and continue. No AD writes yet. $preflightReport = Test-AGDeployPreflight -SelectedCA $SelectedCA @helperParams # 3. State capture. Reads only. $state = New-AGDeployState -SelectedCA $SelectedCA @helperParams # 4. State file, written before any AD change. Save-AGDeployState -State $state -Path $StatePath Write-Verbose "State file written to '$StatePath' before any AD change." Write-Output ([pscustomobject]@{ SelectedCA = $SelectedCA PreflightReport = $preflightReport State = $state StatePath = $StatePath }) } } |