You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

105 lines
8.0 KiB

[CmdletBinding()]
param (
[Parameter(Mandatory)] [ValidatePattern('^[\w-]+$')] [string] $Label,
[Parameter(Mandatory)] [string] $FileServer,
[string] $Client = 'OSWin11E',
[string] $ModulePath,
[string] $Version,
[string] $Edition = 'Desktop,Core',
[Parameter(Mandatory)] [string] $OutputRoot,
[string] $LabName = 'NtfsSecurityOsMatrixLab',
[string] $DomainController = 'OSDC1',
[string] $LabFolder,
[string] $Machines = 'OSFile19,OSFile22,OSFile25,OSWin11E'
)
# One detached sequence of the operating-system matrix (Decision 24) in Windows PowerShell 5.1 on the Hyper-V host. For each cell, a file
# server with the client, it runs: readiness of every machine, the unmodified controller of the repository for the source (a build
# folder or an exact Gallery version) in both editions, the validation of every role from the result files, a snapshot of the fixture
# SIDs, the removal of the fixture, and an independent check of the end state. An infrastructure failure (no summary of the controller)
# stops the later cells; failing tests don't. Case 9 (accounts of other forests) needs trusts that this lab doesn't have, so the cells
# run with -ForeignDomainController @(). Nothing secret is written: the controller keeps the passwords in memory.
$ErrorActionPreference = 'Stop'
$ProgressPreference = 'SilentlyContinue'
# Windows PowerShell 5.1 leaves $PSScriptRoot empty in a parameter default when the script runs with -File.
if (-not $LabFolder) { $LabFolder = Split-Path -Path $PSScriptRoot -Parent }
$stamp = '[{0:yyyy-MM-dd HH:mm:ss}Z]'
$kit = $PSScriptRoot
$cells = @($FileServer -split ',' | Where-Object -FilterScript { $_ })
$editions = @($Edition -split ',' | Where-Object -FilterScript { $_ })
$allMachines = @($Machines -split ',' | Where-Object -FilterScript { $_ })
if ([bool] $ModulePath -eq [bool] $Version) { throw 'Pass exactly one of -ModulePath and -Version.' }
New-Item -ItemType Directory -Path $OutputRoot -Force | Out-Null
$sequenceLog = Join-Path -Path $OutputRoot -ChildPath "$Label-sequence.log"
function Write-Sequence { param ([string] $Message) ($stamp -f [DateTime]::UtcNow) + ' ' + $Message | Add-Content -LiteralPath $sequenceLog }
$source = if ($ModulePath) { "module=$ModulePath dll=$((Get-FileHash -Algorithm SHA256 -LiteralPath (Join-Path -Path $ModulePath -ChildPath 'NTFSSecurity.dll')).Hash)" } else { "version=$Version" }
($stamp -f [DateTime]::UtcNow) + " START matrix-sequence-$Label client=$Client cells=$($cells -join ',') editions=$($editions -join ',') $source" | Set-Content -LiteralPath $sequenceLog
Write-Sequence ('controller blob {0}' -f ((& git -C (Split-Path -Path (Split-Path -Path $LabFolder -Parent) -Parent) hash-object (Join-Path -Path $LabFolder -ChildPath 'Invoke-NTFSSecurityLabTest.ps1')) -join ''))
Write-Sequence ('live tests blob {0}' -f ((& git -C (Split-Path -Path (Split-Path -Path $LabFolder -Parent) -Parent) hash-object (Join-Path -Path $LabFolder -ChildPath 'NTFSSecurity.Live.Tests.ps1')) -join ''))
$controller = Join-Path -Path $LabFolder -ChildPath 'Invoke-NTFSSecurityLabTest.ps1'
$infrastructureFailed = $false
foreach ($fileServerName in $cells) {
if ($infrastructureFailed) { Write-Sequence "cell $fileServerName SKIPPED after an infrastructure failure"; continue }
$cell = Join-Path -Path $OutputRoot -ChildPath "$Label-$fileServerName"
New-Item -ItemType Directory -Path $cell -Force | Out-Null
$runLog = Join-Path -Path $cell -ChildPath 'run.log'
$ran = $false
Write-Sequence "cell $fileServerName START (client $Client)"
try {
$readinessLog = Join-Path -Path $cell -ChildPath 'readiness.log'
& (Join-Path -Path $kit -ChildPath 'Test-MatrixReadiness.ps1') -LabName $LabName -DomainController @($DomainController) -Member @($allMachines) -OutFile $readinessLog
$readiness = Get-Content -LiteralPath $readinessLog -Raw
$notReady = 'wsman=failed|secure channel False|PowerShell 7 missing|Core missing|Pester Desktop (?!5\.7\.1)|=False|kerberos: .*Error'
$problem = [regex]::Match($readiness, $notReady).Value
if ($readiness -notmatch 'matrix-readiness-DONE' -or $problem) { throw "Readiness of cell $fileServerName failed ('$problem'); see $readinessLog" }
Write-Sequence "cell $fileServerName readiness ok"
$arguments = @{
LabName = $LabName; DomainController = $DomainController; FileServer = $fileServerName; Client = $Client
ForeignDomainController = @(); Edition = $editions; OutputPath = $cell; Confirm = $false
}
if ($ModulePath) { $arguments.Version = @(); $arguments.ModulePath = $ModulePath } else { $arguments.Version = @($Version) }
($stamp -f [DateTime]::UtcNow) + " START controller cell=$fileServerName" | Set-Content -LiteralPath $runLog
$ran = $true
& { & $controller @arguments } *>&1 | ForEach-Object -Process { '{0}' -f $_ } | Out-File -LiteralPath $runLog -Append -Encoding utf8 -Width 500
$summaryPath = Get-ChildItem -LiteralPath (Join-Path -Path $cell -ChildPath 'Results') -Filter 'Summary.json' -Recurse -ErrorAction SilentlyContinue |
Sort-Object -Property LastWriteTimeUtc -Descending | Select-Object -First 1 -ExpandProperty FullName
if (-not $summaryPath) { throw "The controller wrote no Summary.json for cell $fileServerName; see $runLog" }
Write-Sequence "cell $fileServerName controller done: $summaryPath"
$validationLog = Join-Path -Path $cell -ChildPath 'validation.log'
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File (Join-Path -Path $kit -ChildPath 'Validate-LabResults.ps1') -ResultsFolder (Split-Path -Path $summaryPath -Parent) -OutputPrefix (Join-Path -Path $cell -ChildPath "$Label-$fileServerName") -Edition ($editions -join ',') -Expect Candidate *>&1 |
Out-File -LiteralPath $validationLog -Encoding utf8 -Width 400
Write-Sequence "cell $fileServerName validation exit code $LASTEXITCODE (see validation.log)"
}
catch {
Write-Sequence "cell $fileServerName FAILED before the cleanup: $_"
if (-not $ran) { Write-Sequence "cell ${fileServerName}: the controller did not start" }
$infrastructureFailed = $true
}
try {
$sidFile = Join-Path -Path $cell -ChildPath 'fixture-sids.json'
$common = @{ LabName = $LabName; DomainController = @($DomainController); Machine = @($allMachines) }
if ($ran) {
& (Join-Path -Path $kit -ChildPath 'Test-MatrixCleanup.ps1') -Mode Snapshot -SidFile $sidFile -OutFile (Join-Path -Path $cell -ChildPath 'cleanup-1-snapshot.log') @common
& { & $controller -RemoveFixture -LabName $LabName -DomainController $DomainController -FileServer $fileServerName -Client $Client -ForeignDomainController @() -Confirm:$false } *>&1 |
ForEach-Object -Process { '{0}' -f $_ } | Out-File -LiteralPath (Join-Path -Path $cell -ChildPath 'cleanup-2-remove.log') -Encoding utf8 -Width 500
& (Join-Path -Path $kit -ChildPath 'Test-MatrixCleanup.ps1') -Mode Verify -SidFile $sidFile -OutFile (Join-Path -Path $cell -ChildPath 'cleanup-3-verify.log') @common
$verify = Get-Content -LiteralPath (Join-Path -Path $cell -ChildPath 'cleanup-3-verify.log') -Raw
$clean = ($verify -match 'matrix-cleanup-Verify-DONE') -and ($verify -notmatch 'OU NTFSSecurityLive: True') -and ($verify -notmatch 'accounts: NtfsLive') -and
($verify -notmatch 'share=True|C:\\NTFSSecurityLive=True|C:\\NTFSSecurityLab=True|NtfsLiveLocal=True') -and ($verify -notmatch 'profiles=[1-9]') -and ($verify -notmatch ': [1-9]\d* fixture member') -and
($verify -notmatch 'probe accounts: [1-9]') -and ($verify -notmatch 'residue: [^\r\n]*=[1-9]')
Write-Sequence ("cell $fileServerName cleanup verdict from the verify log: {0}" -f $(if ($clean) { 'CLEAN' } else { 'DIRTY (read cleanup-3-verify.log)' }))
}
}
catch {
Write-Sequence "cell $fileServerName cleanup FAILED: $_"
}
Write-Sequence "cell $fileServerName END"
}
Write-Sequence "matrix-sequence-$Label-DONE"
exit 0