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 ) ] [ ValidateSet ( 'Snapshot' , 'Verify' , 'Repair' ) ] [string] $Mode ,
[ Parameter ( Mandatory ) ] [string] $SidFile ,
[ Parameter ( Mandatory ) ] [string] $OutFile ,
[string] $LabName = 'NtfsSecurityOsMatrixLab' ,
[string[]] $DomainController = @ ( 'OSDC1' ) ,
[string[]] $Machine = @ ( 'OSFile19' , 'OSFile22' , 'OSFile25' , 'OSWin11' )
)
# Independent end-state check of the fixture of Invoke-NTFSSecurityLabTest.ps1 in a lab (Windows PowerShell 5.1, on the host). Snapshot
# records the SIDs of the NtfsLive* accounts while the fixture exists. Verify reads the domains and every machine again, and reports
# the organizational unit, the accounts, the share, the folders, the local group, the memberships of Administrators, Access Control
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
# Assistance Operators, and Remote Management Users, and the profiles of those SIDs, and what the suite runs and the probes of the kit leave
# behind (scheduled tasks, stage folders, standard users, probe accounts of the domain). The result is judged from this log, never from
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
# the wrapper of the controller or a global error count. Repair is for a run whose removal failed: with the SIDs of the snapshot, it
# removes what that run left on the machines (the memberships, also of orphaned SIDs, which net localgroup deletes by SID; the share; the
# local group; the folders) and then reports like Verify.
& {
$ErrorActionPreference = 'Stop'
# -File passes an array as one string, so a list may arrive as 'A,B'.
$DomainController = @ ( $DomainController | ForEach-Object -Process { $_ -split ',' } | Where-Object -FilterScript { $_ } )
$Machine = @ ( $Machine | ForEach-Object -Process { $_ -split ',' } | Where-Object -FilterScript { $_ } )
'[{0:yyyy-MM-dd HH:mm:ss}Z] START matrix-cleanup-{1} lab={2}' -f [DateTime] :: UtcNow , $Mode , $LabName
Import-Lab -Name $LabName -NoValidation -NoDisplay
$labCommand = @ { NoDisplay = $true ; PassThru = $true ; ErrorAction = 'Stop' }
$directoryScript = {
Import-Module -Name ActiveDirectory
$domain = Get-ADDomain
$unit = Get-ADOrganizationalUnit -LDAPFilter '(ou=NTFSSecurityLive)' -SearchBase $domain . DistinguishedName -SearchScope OneLevel -Server $domain . PDCEmulator
[pscustomobject] @ {
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
Domain = $domain . DNSRoot
Unit = [bool] $unit
Sids = @ ( Get-ADObject -LDAPFilter '(sAMAccountName=NtfsLive*)' -SearchBase $domain . DistinguishedName -Server $domain . PDCEmulator -Properties objectSid , sAMAccountName |
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
ForEach-Object -Process { '{0}={1}' -f $_ . sAMAccountName , $_ . objectSid . Value } )
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
ProbeAccounts = @ ( Get-ADObject -LDAPFilter '(sAMAccountName=NtfsProbe*)' -SearchBase $domain . DistinguishedName -Server $domain . PDCEmulator ) . Count
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
}
}
$directory = @ ( foreach ( $name in $DomainController ) { Invoke-LabCommand -ComputerName $name -ActivityName " Read the fixture of $ name " -ScriptBlock $directoryScript @labCommand } )
foreach ( $state in $directory ) {
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
'{0,-14} OU NTFSSecurityLive: {1,-5} NtfsLive* accounts: {2}; probe accounts: {3}' -f $state . Domain , $state . Unit , ( $ ( if ( $state . Sids ) { $state . Sids -join ', ' } else { 'none' } ) ) , $state . ProbeAccounts
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
}
if ( $Mode -eq 'Snapshot' ) {
$sids = @ ( $directory | ForEach-Object -Process { $_ . Sids } | ForEach-Object -Process { ( $_ -split '=' , 2 ) [ 1 ] } )
ConvertTo-Json -InputObject $sids | Set-Content -LiteralPath $SidFile -Encoding utf8
" saved $( $sids . Count ) SIDs to $ SidFile "
}
else {
$sids = [string[]] ( Get-Content -LiteralPath $SidFile -Raw | ConvertFrom-Json )
" checking $( $sids . Count ) SIDs of the snapshot "
$machineScript = {
param ( $Sid )
# net localgroup lists an orphaned SID, which Get-LocalGroupMember in Windows PowerShell 5.1 fails on and skips.
$groups = foreach ( $groupSid in 'S-1-5-32-544' , 'S-1-5-32-579' , 'S-1-5-32-580' ) {
$groupName = ( [System.Security.Principal.SecurityIdentifier] $groupSid ) . Translate ( [System.Security.Principal.NTAccount] ) . Value -replace '^.*\\' , ''
$members = @ ( & net . exe localgroup $groupName 2 > & 1 | ForEach-Object -Process { " $ _ " . Trim ( ) } )
$hits = @ ( $members | Where-Object -FilterScript { $_ -match 'NtfsLive' -or $_ -in $Sid } )
'{0}: {1} fixture member(s)' -f $groupSid , $hits . Count
}
[pscustomobject] @ {
Share = [bool] ( Get-SmbShare -Name 'NTFSSecurityLive' -ErrorAction SilentlyContinue )
ShareRoot = Test-Path -LiteralPath 'C:\NTFSSecurityLive'
Payload = Test-Path -LiteralPath 'C:\NTFSSecurityLab'
LocalGroup = [bool] ( Get-LocalGroup -Name 'NtfsLiveLocal' -ErrorAction SilentlyContinue )
Groups = $groups -join '; '
Profiles = @ ( Get-CimInstance -ClassName Win32_UserProfile | Where-Object -FilterScript { $_ . SID -in $Sid } ) . Count
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
# What the suite runs and the probes of the kit leave behind: scheduled tasks, stage folders, and standard users
Tasks = @ ( Get-ScheduledTask -ErrorAction SilentlyContinue | Where-Object -FilterScript { $_ . TaskName -like 'NtfsMatrix*' } ) . Count
Stages = @ ( 'C:\NtfsMatrixLocal' , 'C:\NtfsMatrixProbe' | Where-Object -FilterScript { Test-Path -LiteralPath $_ } ) . Count
Users = @ ( Get-LocalUser -ErrorAction SilentlyContinue | Where-Object -FilterScript { $_ . Name -like 'NtfsProbe*' } ) . Count
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
}
}
foreach ( $name in $Machine ) {
if ( $Mode -eq 'Repair' ) {
$repairScript = {
param ( $Sid )
$messages = New-Object -TypeName 'System.Collections.Generic.List[string]'
foreach ( $groupSid in 'S-1-5-32-544' , 'S-1-5-32-579' , 'S-1-5-32-580' ) {
$groupName = ( [System.Security.Principal.SecurityIdentifier] $groupSid ) . Translate ( [System.Security.Principal.NTAccount] ) . Value -replace '^.*\\' , ''
$named = @ ( & net . exe localgroup $groupName 2 > & 1 | ForEach-Object -Process { " $ _ " . Trim ( ) } | Where-Object -FilterScript { $_ -match 'NtfsLive' } )
foreach ( $member in @ ( $Sid ) + $named ) { $null = & net . exe localgroup $groupName $member / delete 2 > & 1 }
}
if ( Get-SmbShare -Name 'NTFSSecurityLive' -ErrorAction SilentlyContinue ) { Remove-SmbShare -Name 'NTFSSecurityLive' -Force }
$null = & net . exe localgroup 'NtfsLiveLocal' / delete 2 > & 1
foreach ( $path in 'C:\NTFSSecurityLive' , 'C:\NTFSSecurityLab' ) {
$command = '$errors = @(); Remove-Item -LiteralPath ''__PATH__'' -Recurse -Force -ErrorAction SilentlyContinue -ErrorVariable errors; $errors | ForEach-Object -Process { "$_" }' . Replace ( '__PATH__' , $path )
$encoded = [Convert] :: ToBase64String ( [System.Text.Encoding] :: Unicode . GetBytes ( $command ) )
$attempt = 0
while ( ( Test-Path -LiteralPath $path ) -and $attempt -lt 6 ) {
$attempt + +
if ( $attempt -gt 1 ) { Start-Sleep -Seconds 5 }
$null = & ( Join-Path -Path $env:ProgramFiles -ChildPath 'PowerShell\7\pwsh.exe' ) -NoProfile -NonInteractive -EncodedCommand $encoded 2 > & 1
}
$messages . Add ( ( '{0}: present after {1} attempt(s): {2}' -f $path , $attempt , ( Test-Path -LiteralPath $path ) ) )
}
$messages
}
foreach ( $message in @ ( Invoke-LabCommand -ComputerName $name -ActivityName " Repair $ name " -ScriptBlock $repairScript -ArgumentList ( , $sids ) @labCommand ) ) {
'{0,-9} repair: {1}' -f $name , $message
}
}
$state = Invoke-LabCommand -ComputerName $name -ActivityName " Check $ name " -ScriptBlock $machineScript -ArgumentList ( , $sids ) @labCommand
'{0,-9} share={1} C:\NTFSSecurityLive={2} C:\NTFSSecurityLab={3} NtfsLiveLocal={4} profiles={5}' -f $name , $state . Share , $state . ShareRoot , $state . Payload , $state . LocalGroup , $state . Profiles
' {0}' -f $state . Groups
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
' residue: scheduled tasks={0} stage folders={1} probe users={2}' -f $state . Tasks , $state . Stages , $state . Users
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
}
}
'[{0:yyyy-MM-dd HH:mm:ss}Z] matrix-cleanup-{1}-DONE' -f [DateTime] :: UtcNow , $Mode
} * > & 1 | Out-File -FilePath $OutFile -Encoding utf8 -Width 400