test(lab): add the kit that deploys and runs the operating-system matrix
Tests\Lab\Acceptance gets the scripts of Decision 24, which extends the
acceptance of the live tests from one lab to several operating-system builds:
- Deploy-OsMatrixLab.ps1, Add-OsMatrixMachine.ps1, Complete-OsMatrixLab.ps1
and Repair-OsMatrixBoot.ps1 build NtfsSecurityOsMatrixLab with AutomatedLab
(Server 2019, 2022 and 2025 file servers, Windows 11 clients) and repair a
base image whose host-side bcdboot left an empty EFI system partition.
- Test-MatrixReadiness.ps1 and Test-MatrixCleanup.ps1 gate each run and prove
that it left nothing behind (fixture accounts, shares, folders, group
members, orphaned SIDs, profiles); -Mode Repair removes what is left.
- Run-MatrixSequence.ps1 runs the live controller for every cell of the matrix
and stops after an infrastructure failure.
- Run-MatrixLocalSuite.ps1 and Invoke-LocalSuite.ps1 run the module's own
Pester suite on each machine in both editions, elevated and as a basic user,
as scheduled tasks: a process started from a remoting session gets every
privilege enabled, which eight of the tests do not expect.
- Export-MatrixResults.ps1 collates the cells, the skipped tests and the
package hashes into the results table.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
3 weeks ago
|
|
|
[CmdletBinding()]
|
|
|
|
|
param (
|
|
|
|
|
[Parameter(Mandatory)] [ValidatePattern('^[\w-]+$')] [string] $Label,
|
|
|
|
|
[Parameter(Mandatory)] [string] $FileServer,
|
test(lab): check the probe residue, read the cell timeline, and model the stale managers
Test-MatrixCleanup.ps1 now counts and repairs the probe folders
(C:\NtfsProbeRecreation and C:\NtfsProbeModules), the local NtfsProbe*
users with their profiles, and the NtfsProbe* objects of the directory.
The scripts of the matrix default to the client OSWin11E.
Export-CellTimeline.ps1 writes one row for every cell, edition, and
Admin role: the module, the account and its relative ID, the times, and
the three effective-access tests. Test-StaleAuthzModel.ps1 replays such a
timeline against a model of the failures: the fit, a listing of the runs,
the cells of a controller that reuses the account name, and a permutation
test.
The comment in the controller says what the replay showed. The code of
the controller is unchanged.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
3 weeks ago
|
|
|
[string] $Client = 'OSWin11E',
|
test(lab): add the kit that deploys and runs the operating-system matrix
Tests\Lab\Acceptance gets the scripts of Decision 24, which extends the
acceptance of the live tests from one lab to several operating-system builds:
- Deploy-OsMatrixLab.ps1, Add-OsMatrixMachine.ps1, Complete-OsMatrixLab.ps1
and Repair-OsMatrixBoot.ps1 build NtfsSecurityOsMatrixLab with AutomatedLab
(Server 2019, 2022 and 2025 file servers, Windows 11 clients) and repair a
base image whose host-side bcdboot left an empty EFI system partition.
- Test-MatrixReadiness.ps1 and Test-MatrixCleanup.ps1 gate each run and prove
that it left nothing behind (fixture accounts, shares, folders, group
members, orphaned SIDs, profiles); -Mode Repair removes what is left.
- Run-MatrixSequence.ps1 runs the live controller for every cell of the matrix
and stops after an infrastructure failure.
- Run-MatrixLocalSuite.ps1 and Invoke-LocalSuite.ps1 run the module's own
Pester suite on each machine in both editions, elevated and as a basic user,
as scheduled tasks: a process started from a remoting session gets every
privilege enabled, which eight of the tests do not expect.
- Export-MatrixResults.ps1 collates the cells, the skipped tests and the
package hashes into the results table.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
3 weeks ago
|
|
|
[string] $ModulePath,
|
|
|
|
|
[string] $Version,
|
|
|
|
|
[string] $Edition = 'Desktop,Core',
|
|
|
|
|
[Parameter(Mandatory)] [string] $OutputRoot,
|
|
|
|
|
[string] $LabName = 'NtfsSecurityOsMatrixLab',
|
|
|
|
|
[string] $DomainController = 'OSDC1',
|
|
|
|
|
[string] $LabFolder,
|
test(lab): check the probe residue, read the cell timeline, and model the stale managers
Test-MatrixCleanup.ps1 now counts and repairs the probe folders
(C:\NtfsProbeRecreation and C:\NtfsProbeModules), the local NtfsProbe*
users with their profiles, and the NtfsProbe* objects of the directory.
The scripts of the matrix default to the client OSWin11E.
Export-CellTimeline.ps1 writes one row for every cell, edition, and
Admin role: the module, the account and its relative ID, the times, and
the three effective-access tests. Test-StaleAuthzModel.ps1 replays such a
timeline against a model of the failures: the fit, a listing of the runs,
the cells of a controller that reuses the account name, and a permutation
test.
The comment in the controller says what the replay showed. The code of
the controller is unchanged.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
3 weeks ago
|
|
|
[string] $Machines = 'OSFile19,OSFile22,OSFile25,OSWin11E'
|
test(lab): add the kit that deploys and runs the operating-system matrix
Tests\Lab\Acceptance gets the scripts of Decision 24, which extends the
acceptance of the live tests from one lab to several operating-system builds:
- Deploy-OsMatrixLab.ps1, Add-OsMatrixMachine.ps1, Complete-OsMatrixLab.ps1
and Repair-OsMatrixBoot.ps1 build NtfsSecurityOsMatrixLab with AutomatedLab
(Server 2019, 2022 and 2025 file servers, Windows 11 clients) and repair a
base image whose host-side bcdboot left an empty EFI system partition.
- Test-MatrixReadiness.ps1 and Test-MatrixCleanup.ps1 gate each run and prove
that it left nothing behind (fixture accounts, shares, folders, group
members, orphaned SIDs, profiles); -Mode Repair removes what is left.
- Run-MatrixSequence.ps1 runs the live controller for every cell of the matrix
and stops after an infrastructure failure.
- Run-MatrixLocalSuite.ps1 and Invoke-LocalSuite.ps1 run the module's own
Pester suite on each machine in both editions, elevated and as a basic user,
as scheduled tasks: a process started from a remoting session gets every
privilege enabled, which eight of the tests do not expect.
- Export-MatrixResults.ps1 collates the cells, the skipped tests and the
package hashes into the results table.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
3 weeks ago
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# 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
|
test(lab): give each new fixture a new account for the effective-access case
When an account is deleted and created again with the same name, a Kerberos S4U
logon for it keeps returning the SID and the groups of the deleted account for
a while, on the domain controller, the client, and the file server. The matrix
deletes the fixture after each cell and creates it for the next, so the
effective-access tests of the Admin role found no access for the new account in
cells that followed within minutes (Windows Server 2022 cell, candidate and
baseline alike, shown by a probe that creates the accounts in a loop). A new
fixture now gets NtfsLiveSubject and four digits; a fixture that exists keeps
its account.
The end-state check of the matrix also reports leftover scheduled tasks, stage
folders, standard users, and probe accounts, which the review asked for.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
3 weeks ago
|
|
|
($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]')
|
test(lab): add the kit that deploys and runs the operating-system matrix
Tests\Lab\Acceptance gets the scripts of Decision 24, which extends the
acceptance of the live tests from one lab to several operating-system builds:
- Deploy-OsMatrixLab.ps1, Add-OsMatrixMachine.ps1, Complete-OsMatrixLab.ps1
and Repair-OsMatrixBoot.ps1 build NtfsSecurityOsMatrixLab with AutomatedLab
(Server 2019, 2022 and 2025 file servers, Windows 11 clients) and repair a
base image whose host-side bcdboot left an empty EFI system partition.
- Test-MatrixReadiness.ps1 and Test-MatrixCleanup.ps1 gate each run and prove
that it left nothing behind (fixture accounts, shares, folders, group
members, orphaned SIDs, profiles); -Mode Repair removes what is left.
- Run-MatrixSequence.ps1 runs the live controller for every cell of the matrix
and stops after an infrastructure failure.
- Run-MatrixLocalSuite.ps1 and Invoke-LocalSuite.ps1 run the module's own
Pester suite on each machine in both editions, elevated and as a basic user,
as scheduled tasks: a process started from a remoting session gets every
privilege enabled, which eight of the tests do not expect.
- Export-MatrixResults.ps1 collates the cells, the skipped tests and the
package hashes into the results table.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
3 weeks ago
|
|
|
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
|