94 lines
6.3 KiB
PowerShell
94 lines
6.3 KiB
PowerShell
$ErrorActionPreference='Stop'
|
|
$root=Split-Path -Parent $PSScriptRoot
|
|
. (Join-Path $root 'scripts\Project.Common.ps1')
|
|
Assert-PowerShell51
|
|
function Assert-BootTest { param([bool]$Condition,[string]$Message) if (-not $Condition) { throw $Message } }
|
|
function Assert-BootPending {
|
|
param([scriptblock]$Action,[string]$ExpectedMessage)
|
|
$message=$null
|
|
try { & $Action|Out-Null } catch { $message=$_.Exception.Message }
|
|
Assert-BootTest ($message -and $message -match $ExpectedMessage) ('Expected boot verification to wait or fail: '+$ExpectedMessage+'; got: '+$message)
|
|
}
|
|
$tokens=$null;$errors=$null
|
|
$ast=[System.Management.Automation.Language.Parser]::ParseFile((Join-Path $root 'Verify-BuiltImage.ps1'),[ref]$tokens,[ref]$errors)
|
|
if ($errors.Count) { throw ($errors|Out-String) }
|
|
$blocks=@($ast.FindAll({param($node)
|
|
$node -is [System.Management.Automation.Language.ScriptBlockAst] -and
|
|
$node.ParamBlock.Parameters.Count -eq 5 -and
|
|
$node.ParamBlock.Parameters[2].Name.VariablePath.UserPath -eq 'VerifyActivation'
|
|
},$true))
|
|
Assert-BootTest ($blocks.Count -eq 1) 'The guest boot verification probe was not found uniquely.'
|
|
$probe=$blocks[0].GetScriptBlock()
|
|
$fixture=Join-Path $root ('local\boot verification tests '+[guid]::NewGuid().ToString('N').Substring(0,8))
|
|
$priorProgramData=$env:ProgramData
|
|
$priorProgramFiles=$env:ProgramFiles
|
|
try {
|
|
$env:ProgramData=Join-Path $fixture 'ProgramData'
|
|
$env:ProgramFiles=Join-Path $fixture 'Program Files'
|
|
$activationPath=Join-Path $env:ProgramData 'PveImageBuilder\windows-activation.json'
|
|
$cloudbaseLog=Join-Path $env:ProgramFiles 'Cloudbase Solutions\Cloudbase-Init\log\cloudbase-init.log'
|
|
foreach ($path in @($activationPath,$cloudbaseLog)) { New-Item -ItemType Directory -Path (Split-Path -Parent $path) -Force|Out-Null }
|
|
& {
|
|
function Reset-BootTest {
|
|
$global:PveGuestBootTest=@{Host='kms.example.com';Port=1688}
|
|
$report=@{SchemaVersion=1;Outcome='Failed';Attempted=$true;KmsHost='kms.example.com';KmsPort=1688;CompletedUtc=[DateTime]::UtcNow.ToString('o');Message='Mocked unavailable KMS server.'}
|
|
[IO.File]::WriteAllText($activationPath,($report|ConvertTo-Json),[Text.UTF8Encoding]::new($false))
|
|
[IO.File]::WriteAllText($cloudbaseLog,('INFO Script "C:\LocalScripts\30-WindowsActivation.ps1" ended with exit code: 0'+[Environment]::NewLine+'INFO Plugins execution done'))
|
|
}
|
|
function Get-LocalUser {
|
|
param($Name,$ErrorAction)
|
|
if ($Name -eq 'Admin') { return $null }
|
|
[PSCustomObject]@{SID=[PSCustomObject]@{Value='S-1-5-21-1-2-3-500'};Enabled=$true}
|
|
}
|
|
function Get-Service { param($Name) [PSCustomObject]@{Name=$Name;StartType='Automatic'} }
|
|
function Get-ItemProperty { param($LiteralPath,$Name) [PSCustomObject]@{fDenyTSConnections=0;UserAuthentication=1} }
|
|
function Get-NetTCPConnection { param($LocalPort,$State,$ErrorAction) [PSCustomObject]@{LocalPort=3389;State='Listen'} }
|
|
function Get-NetFirewallProfile { [PSCustomObject]@{Enabled='False'} }
|
|
function Get-CimInstance {
|
|
param($ClassName,$Filter,$ErrorAction)
|
|
switch ($ClassName) {
|
|
'Win32_OperatingSystem' { [PSCustomObject]@{Version='10.0.26100'} }
|
|
'SoftwareLicensingService' { [PSCustomObject]@{KeyManagementServiceMachine=$global:PveGuestBootTest.Host;KeyManagementServicePort=$global:PveGuestBootTest.Port} }
|
|
'SoftwareLicensingProduct' { [PSCustomObject]@{Description='Windows Operating System, VOLUME_KMSCLIENT channel';LicenseIsAddon=$false} }
|
|
default { throw ('Unexpected guest CIM query: '+$ClassName) }
|
|
}
|
|
}
|
|
function Invoke-CimMethod { throw 'Boot verification must not issue an activation request.' }
|
|
foreach ($outcome in @('Failed','Skipped','Activated','AlreadyActivated')) {
|
|
Reset-BootTest
|
|
$report=Get-Content -Raw -LiteralPath $activationPath|ConvertFrom-Json
|
|
$report.Outcome=$outcome
|
|
[IO.File]::WriteAllText($activationPath,($report|ConvertTo-Json),[Text.UTF8Encoding]::new($false))
|
|
$proof=& $probe $false $false $true 'kms.example.com' 1688
|
|
Assert-BootTest ($proof.Boot -eq 'passed' -and $proof.CloudbaseLocalScriptsCompleted -and $proof.KmsSettingsReapplied -and $proof.WindowsKmsClientInstalled -and $proof.WindowsActivation.Outcome -eq $outcome) 'Completed initialization incorrectly required activation success.'
|
|
}
|
|
Reset-BootTest
|
|
Remove-Item -LiteralPath $activationPath
|
|
Assert-BootPending { & $probe $false $false $true 'kms.example.com' 1688 } 'Waiting for the optional activation script'
|
|
foreach ($log in @('INFO Executing plugin LocalScriptsPlugin',
|
|
'INFO Script "C:\LocalScripts\30-WindowsActivation.ps1" ended with exit code: 0',
|
|
('INFO Script "C:\LocalScripts\30-WindowsActivation.ps1" ended with exit code: 1'+[Environment]::NewLine+'INFO Plugins execution done'))) {
|
|
Reset-BootTest
|
|
[IO.File]::WriteAllText($cloudbaseLog,$log)
|
|
Assert-BootPending { & $probe $false $false $true 'kms.example.com' 1688 } 'Waiting for Cloudbase-Init LocalScripts'
|
|
}
|
|
Reset-BootTest
|
|
$global:PveGuestBootTest.Host='stale.example.com'
|
|
Assert-BootPending { & $probe $false $false $true 'kms.example.com' 1688 } 'KMS settings were not reapplied'
|
|
Reset-BootTest
|
|
$report=Get-Content -Raw -LiteralPath $activationPath|ConvertFrom-Json
|
|
$report.CompletedUtc=$null
|
|
[IO.File]::WriteAllText($activationPath,($report|ConvertTo-Json))
|
|
Assert-BootPending { & $probe $false $false $true 'kms.example.com' 1688 } 'diagnostics are incomplete'
|
|
Remove-Item -LiteralPath $activationPath,$cloudbaseLog
|
|
$proof=& $probe $false $false $false '' 1688
|
|
Assert-BootTest ($proof.Boot -eq 'passed') 'Legacy or blank KMS configurations acquired an activation requirement.'
|
|
}
|
|
}
|
|
finally {
|
|
$env:ProgramData=$priorProgramData
|
|
$env:ProgramFiles=$priorProgramFiles
|
|
Remove-Variable -Name PveGuestBootTest -Scope Global -ErrorAction SilentlyContinue
|
|
}
|
|
[PSCustomObject]@{EarlyShutdownRegression='passed';ActivationFailure='accepted after initialization completes';IncompleteLocalScripts='wait';KmsReadback='passed';LegacyAndBlank='passed';HostOperations='mocked'}
|