Browse Source

test: extend the live tests in the lab to all cmdlet groups

Add the cases 4b (orphaned audit entry), 5 (owner), 6 (audit inheritance,
Clear-NTFSAudit, Get-NTFSInheritance), 7 (item cmdlets on the share),
8 (link cmdlets on the share), and 9 (Get-NTFSSimpleAccess), and the
accounts of three other domains and forests (-ForeignDomainController).

The fixture writes the folders with SetAccessControl instead of Set-Acl,
which also wrote an empty SACL and dropped the inherited audit entries.
The delegated account expects the denial of Get-NTFSEffectiveAccess
-ServerName, which the file server answers only for its administrators
and the members of Access Control Assistance Operators.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
ai/release-5.0.0-rc6
Raimund Andree 4 days ago
parent
commit
9e6af75b5e
  1. 178
      Tests/Lab/Invoke-NTFSSecurityLabTest.ps1
  2. 417
      Tests/Lab/NTFSSecurity.Live.Tests.ps1
  3. 25
      Tests/Lab/README.md

178
Tests/Lab/Invoke-NTFSSecurityLabTest.ps1

@ -25,6 +25,11 @@
.PARAMETER Client
The computer that runs the tests. Its domain must be the domain of the domain controller.
.PARAMETER ForeignDomainController
Domain controllers of other domains or forests, one per domain. The script creates the account NtfsLiveForeign in
each of their domains, and the tests grant it access to share folders by SID and by name. The domains need a trust
with the domain of the file server. Pass an empty array to leave these tests out.
.PARAMETER Version
The versions of NTFSSecurity on the PowerShell Gallery to test, such as 5.0.0-rc4.
@ -75,6 +80,11 @@ param (
[string]
$Client = 'F1AFile1',
[Parameter()]
[AllowEmptyCollection()]
[string[]]
$ForeignDomainController = @('F1BDC1', 'F2DC1', 'F3DC1'),
[Parameter(ParameterSetName = 'Test')]
[ValidatePattern('^\d+\.\d+\.\d+(-[0-9A-Za-z]+)?$')]
[string[]]
@ -120,6 +130,9 @@ $roleAccounts = [ordered]@{
}
$subjectAccount = 'NtfsLiveSubject'
$orphanAccount = 'NtfsLiveOrphan'
$foreignAccount = 'NtfsLiveForeign'
# The rights that the entries of the foreign accounts grant on the folder of case 9, by position
$foreignRights = 'ReadAndExecute', 'Modify', 'Write'
$localGroupName = 'NtfsLiveLocal'
$groupMembers = @{
NtfsLiveDelegates = @('NtfsLiveDelegate')
@ -410,6 +423,40 @@ $removeOrphanScript = {
}
}
# Runs on a domain controller of another domain or forest: the account that the tests of case 9 grant access to.
$foreignAccountScript = {
param ($OrganizationalUnitName, $Name, [securestring] $Password)
$ErrorActionPreference = 'Stop'
Import-Module -Name ActiveDirectory
$domain = Get-ADDomain
$server = $domain.PDCEmulator
$path = 'OU={0},{1}' -f $OrganizationalUnitName, $domain.DistinguishedName
if (-not (Get-ADOrganizationalUnit -LDAPFilter "(ou=$OrganizationalUnitName)" -SearchBase $domain.DistinguishedName -SearchScope OneLevel -Server $server)) {
New-ADOrganizationalUnit -Name $OrganizationalUnitName -Path $domain.DistinguishedName -ProtectedFromAccidentalDeletion $false -Server $server
}
$user = Get-ADUser -LDAPFilter "(sAMAccountName=$Name)" -Server $server
if ($user -and $user.DistinguishedName -notlike "*,$path") {
throw "The account '$Name' exists outside '$path'."
}
if ($user) {
Set-ADAccountPassword -Identity $user -Reset -NewPassword $Password -Server $server
Enable-ADAccount -Identity $user -Server $server
}
else {
$user = New-ADUser -Name $Name -SamAccountName $Name -UserPrincipalName "$Name@$($domain.DNSRoot)" -Path $path -AccountPassword $Password -Enabled $true -PasswordNeverExpires $true -Server $server -PassThru
}
[pscustomobject]@{
DomainName = $domain.DNSRoot
Name = '{0}\{1}' -f $domain.NetBIOSName, $Name
UserPrincipalName = '{0}@{1}' -f $Name, $domain.DNSRoot
Sid = $user.SID.Value
}
}
# Runs on the file server once: the local group, the members of Administrators, the share, and the tools folder.
$fileServerSetupScript = {
param ($ShareName, $ShareLocalPath, $PayloadPath, $LocalGroupName, $SubjectSid, $AdministratorSid, $DelegatesSid)
@ -495,7 +542,7 @@ $clientSetupScript = {
# Runs on the file server for each run: creates the folders of the cases below the share and returns what the
# configuration needs. Each case and operation gets its own folder, so that the tests don't depend on each other.
$fixtureScript = {
param ($HelperScript, $ShareLocalPath, $RunId, $Sid, $SubjectPrincipalName, $LongPathSegment)
param ($HelperScript, $ShareLocalPath, $RunId, $Sid, $SubjectPrincipalName, $LongPathSegment, $ForeignAccount)
$ErrorActionPreference = 'Stop'
. ([scriptblock]::Create($HelperScript))
@ -521,7 +568,7 @@ $fixtureScript = {
'PSUseShouldProcessForStateChangingFunctions', '', Justification = 'Runs in the fixture that the script confirmed.'
)]
param ([string] $RelativePath, [object[]] $AccessRule = @(), [switch] $Protected, [switch] $RemoveInherited,
[switch] $Audit, [switch] $LegacyDacl)
[switch] $Audit, [switch] $LegacyDacl, [switch] $InheritableAudit, [switch] $ProtectedAudit)
$path = Join-Path -Path $root -ChildPath $RelativePath
$null = New-Item -ItemType Directory -Path $path -Force
@ -535,12 +582,26 @@ $fixtureScript = {
$acl.AddAccessRule($rule)
}
Set-Acl -LiteralPath $path -AclObject $acl
if ($Audit) {
# Not Set-Acl: in Windows PowerShell, it also writes an empty, protected SACL, which drops the audit entries
# that the folder inherits. SetAccessControl writes only the sections that changed.
[System.IO.Directory]::SetAccessControl($path, $acl)
if ($Audit -or $InheritableAudit -or $ProtectedAudit) {
$auditAcl = Get-Acl -LiteralPath $path -Audit
$everyone = New-Object -TypeName 'System.Security.Principal.SecurityIdentifier' -ArgumentList 'S-1-1-0'
if ($Audit) {
$auditAcl.AddAuditRule((New-Object -TypeName 'System.Security.AccessControl.FileSystemAuditRule' -ArgumentList $everyone, 'Delete', 'None', 'None', 'Success'))
Set-Acl -LiteralPath $path -AclObject $auditAcl
}
# An entry that the subfolders created afterwards inherit
if ($InheritableAudit) {
$auditAcl.AddAuditRule((New-Object -TypeName 'System.Security.AccessControl.FileSystemAuditRule' -ArgumentList $everyone, 'Delete', 'ContainerInherit, ObjectInherit', 'None', 'Failure'))
}
if ($ProtectedAudit) {
$auditAcl.SetAuditRuleProtection($true, $false)
}
[System.IO.Directory]::SetAccessControl($path, $auditAcl)
}
# Set-Acl adds the auto-inherit flag, so the DACL is stored again without it at the end.
@ -595,6 +656,11 @@ $fixtureScript = {
# Case 4: an entry for the account that the domain controller deletes next, and a file that inherits it.
$orphanPath = New-FixtureFolder -RelativePath 'Case4\OrphanedAccess' -AccessRule (New-AccessRule -Sid $Sid.Orphan -Rights 'Modify')
Set-Content -LiteralPath (Join-Path -Path $orphanPath -ChildPath 'File.txt') -Value 'Orphan'
$orphanAuditPath = New-FixtureFolder -RelativePath 'Case4\OrphanedAudit' -AccessRule $delegatesFullControl
$orphanAuditAcl = Get-Acl -LiteralPath $orphanAuditPath -Audit
$orphanIdentity = New-Object -TypeName 'System.Security.Principal.SecurityIdentifier' -ArgumentList $Sid.Orphan
$orphanAuditAcl.AddAuditRule((New-Object -TypeName 'System.Security.AccessControl.FileSystemAuditRule' -ArgumentList $orphanIdentity, 'Delete', 'None', 'None', 'Success'))
[System.IO.Directory]::SetAccessControl($orphanAuditPath, $orphanAuditAcl)
# A file whose path on the share is longer than 260 characters, created by PowerShell 7, which handles such paths.
$longPath = New-FixtureFolder -RelativePath 'LongPath'
@ -609,11 +675,68 @@ $fixtureScript = {
Set-Content -LiteralPath (Join-Path -Path $whatIfPath -ChildPath 'Source.txt') -Value 'Source' -NoNewline
Set-Content -LiteralPath (Join-Path -Path $whatIfPath -ChildPath 'Destination.txt') -Value 'Destination' -NoNewline
# Case 5: the owner cmdlets, and case 6: the audit inheritance cmdlets and Clear-NTFSAudit below a folder whose
# audit entry the subfolders inherit, on folders that Administrators own and the delegated group fully controls.
foreach ($role in 'Admin', 'ServerAdmin', 'Delegate') {
foreach ($operation in 'GetOwner', 'TakeOwnership', 'AssignOwner') {
$null = New-FixtureFolder -RelativePath "Case5\$role\$operation" -AccessRule $delegatesFullControl
}
$null = New-FixtureFolder -RelativePath "Case6\$role" -AccessRule $delegatesFullControl -InheritableAudit
$null = New-FixtureFolder -RelativePath "Case6\$role\DisableAuditInheritance"
$null = New-FixtureFolder -RelativePath "Case6\$role\EnableAuditInheritance" -ProtectedAudit
$null = New-FixtureFolder -RelativePath "Case6\$role\ClearAudit" -Audit
$null = New-FixtureFolder -RelativePath "Case6\$role\GetInheritance" -Protected
}
# Case 7: the item cmdlets in a folder that the delegated group fully controls.
$itemsPath = New-FixtureFolder -RelativePath 'Case7\Items' -AccessRule $delegatesFullControl
foreach ($name in 'Source', 'Move', 'Remove') {
Set-Content -LiteralPath (Join-Path -Path $itemsPath -ChildPath "$name.txt") -Value $name -NoNewline
}
$null = New-Item -ItemType Directory -Path (Join-Path -Path $itemsPath -ChildPath 'Folder')
Set-Content -LiteralPath (Join-Path -Path $itemsPath -ChildPath 'Folder\File.txt') -Value 'File' -NoNewline
# Case 8: the link cmdlets.
$linksPath = New-FixtureFolder -RelativePath 'Case8\Links' -AccessRule $delegatesFullControl
Set-Content -LiteralPath (Join-Path -Path $linksPath -ChildPath 'Target.txt') -Value 'Target' -NoNewline
$null = New-Item -ItemType Directory -Path (Join-Path -Path $linksPath -ChildPath 'TargetFolder')
# Case 9: a subfolder with an entry of its own for Get-NTFSSimpleAccess, and the accounts of other domains.
$null = New-FixtureFolder -RelativePath 'Case9\Simple' -AccessRule $delegatesFullControl
$null = New-FixtureFolder -RelativePath 'Case9\Simple\Child' -AccessRule (New-AccessRule -Sid $Sid.Subject -Rights 'Modify')
$foreignPath = New-FixtureFolder -RelativePath 'Case9\Foreign' -AccessRule @(
foreach ($account in $ForeignAccount) {
New-AccessRule -Sid $account.Sid -Rights $account.Rights
}
)
$null = New-FixtureFolder -RelativePath 'Case9\ForeignAdd' -AccessRule $delegatesFullControl
$null = New-FixtureFolder -RelativePath 'Case9\ForeignRemove' -AccessRule @(
foreach ($account in $ForeignAccount) {
New-AccessRule -Sid $account.Sid -Rights 'ReadAndExecute'
}
)
# The rights that the file server's own token of each foreign account gets on the folder, like case 3. A token
# that the file server can't create is reported as -1, which fails only the effective-access test of the account.
$foreignDescriptor = Get-LabSecurityDescriptor -Path $foreignPath
$foreignEffectiveRights = @{}
foreach ($account in $ForeignAccount) {
try {
$foreignEffectiveRights[$account.Sid] = Get-LabGrantedRight -Descriptor $foreignDescriptor -Sid @(Get-LabTokenSid -UserPrincipalName $account.UserPrincipalName)
}
catch {
$foreignEffectiveRights[$account.Sid] = -1L
}
}
[pscustomobject]@{
ServerPath = $root
FileServerRights = $fileServerRights
EffectiveAccessSddl = $effectiveDescriptor.GetSddlForm('All')
LongPath = $longRelativePath
ForeignEffectiveRights = $foreignEffectiveRights
}
}
@ -778,6 +901,21 @@ if (@($machines | ForEach-Object -Process { $_.DomainName } | Select-Object -Uni
throw 'The domain controller, the file server, and the client must belong to one domain.'
}
foreach ($name in $ForeignDomainController) {
$machine = Get-LabVM -ComputerName $name
if (-not $machine) {
throw "The lab '$LabName' has no machine '$name'."
}
if ($machine.DomainName -eq $machines[0].DomainName) {
throw "The foreign domain controller '$name' must belong to another domain than the file server."
}
}
if (@($ForeignDomainController | ForEach-Object -Process { (Get-LabVM -ComputerName $_).DomainName } | Select-Object -Unique).Count -ne @($ForeignDomainController).Count) {
throw 'Each foreign domain controller must belong to a domain of its own.'
}
$helperScript = Get-Content -LiteralPath (Join-Path -Path $PSScriptRoot -ChildPath 'NTFSSecurity.LabHelpers.ps1') -Raw
$labCommand = @{
NoDisplay = $true
@ -794,6 +932,10 @@ if ($RemoveFixture) {
$null = Invoke-LabCommand -ComputerName $FileServer -ActivityName 'Remove the share and the folders' -ScriptBlock $removeFileServerScript -ArgumentList $shareName, $shareLocalPath, $payloadPath, $localGroupName, $accountSids @labCommand
$null = Invoke-LabCommand -ComputerName $Client -ActivityName 'Remove the members and the folder' -ScriptBlock $removeClientScript -ArgumentList $payloadPath, $accountSids @labCommand
$null = Invoke-LabCommand -ComputerName $DomainController -ActivityName 'Remove the accounts' -ScriptBlock $removeAccountScript -ArgumentList $organizationalUnitName @labCommand
foreach ($computer in $ForeignDomainController) {
$null = Invoke-LabCommand -ComputerName $computer -ActivityName 'Remove the account of another domain' -ScriptBlock $removeAccountScript -ArgumentList $organizationalUnitName @labCommand
}
Write-LabProgress "Removed the live tests from the lab '$LabName'."
return
}
@ -837,6 +979,18 @@ foreach ($name in @($roleAccounts.Values) + $subjectAccount) {
$directory = Invoke-LabCommand -ComputerName $DomainController -ActivityName 'Prepare the accounts' -ScriptBlock $accountScript -ArgumentList $organizationalUnitName, $passwords, $groupMembers @labCommand
$sids = $directory.Sids
$foreignAccounts = @(
for ($index = 0; $index -lt @($ForeignDomainController).Count; $index++) {
$account = Invoke-LabCommand -ComputerName $ForeignDomainController[$index] -ActivityName 'Prepare the account of another domain' -ScriptBlock $foreignAccountScript -ArgumentList $organizationalUnitName, $foreignAccount, (New-LabPassword) @labCommand
[pscustomobject]@{
DomainName = $account.DomainName
Name = $account.Name
UserPrincipalName = $account.UserPrincipalName
Sid = $account.Sid
Rights = $foreignRights[$index % $foreignRights.Count]
}
}
)
$localGroupSid = Invoke-LabCommand -ComputerName $FileServer -ActivityName 'Prepare the file server' -ScriptBlock $fileServerSetupScript -ArgumentList $shareName, $shareLocalPath, $payloadPath, $localGroupName, $sids[$subjectAccount], @($sids['NtfsLiveServerAdmin'], $sids['NtfsLiveAdmin']), $sids['NtfsLiveDelegates'] @labCommand
$null = Invoke-LabCommand -ComputerName $Client -ActivityName 'Prepare the client' -ScriptBlock $clientSetupScript -ArgumentList $payloadPath, @($sids['NtfsLiveDelegate'], $sids['NtfsLiveAdmin']), @($sids['NtfsLiveServerAdmin']) @labCommand
foreach ($computer in $Client, $FileServer) {
@ -880,8 +1034,9 @@ foreach ($module in $modules) {
NtfsLiveOuter = $sids['NtfsLiveOuter']
LocalGroup = [string]$localGroupSid
Orphan = $orphan.Sid
Subject = $sids[$subjectAccount]
}
$fixture = Invoke-LabCommand -ComputerName $FileServer -ActivityName 'Create the folders of the run' -ScriptBlock $fixtureScript -ArgumentList $helperScript, $shareLocalPath, $runId, $fixtureSids, $subjectPrincipalName, $longPathSegments @labCommand
$fixture = Invoke-LabCommand -ComputerName $FileServer -ActivityName 'Create the folders of the run' -ScriptBlock $fixtureScript -ArgumentList $helperScript, $shareLocalPath, $runId, $fixtureSids, $subjectPrincipalName, $longPathSegments, $foreignAccounts @labCommand
$null = Invoke-LabCommand -ComputerName $DomainController -ActivityName 'Delete the orphan account' -ScriptBlock $removeOrphanScript -ArgumentList $orphan.Guid @labCommand
$oracle = Invoke-LabCommand -ComputerName $Client -ActivityName 'Calculate the rights on the client' -ScriptBlock $clientOracleScript -ArgumentList $helperScript, $fixture.EffectiveAccessSddl, $subjectPrincipalName, $orphan.Sid @labCommand
if ($oracle.OrphanResolved) {
@ -911,6 +1066,17 @@ foreach ($module in $modules) {
FileServerRights = [long]$fixture.FileServerRights
ClientRights = [long]$oracle.ClientRights
}
ForeignAccounts = @(
foreach ($account in $foreignAccounts) {
[ordered]@{
Name = $account.Name
DomainName = $account.DomainName
Sid = $account.Sid
Rights = $account.Rights
EffectiveRights = [long]$fixture.ForeignEffectiveRights[$account.Sid]
}
}
)
Accounts = [ordered]@{
Delegate = [ordered]@{ Name = $credentials['Delegate'].UserName; Sid = $sids['NtfsLiveDelegate']; ClientAdministrator = $true; FileServerAdministrator = $false }
ServerAdmin = [ordered]@{ Name = $credentials['ServerAdmin'].UserName; Sid = $sids['NtfsLiveServerAdmin']; ClientAdministrator = $false; FileServerAdministrator = $true }

417
Tests/Lab/NTFSSecurity.Live.Tests.ps1

@ -61,6 +61,49 @@ BeforeDiscovery {
@{ Folder = "Case2\$auditRole\RemoveAudit"; Count = [int](-not $mayWrite) }
}
)
# Case 5: only the administrators of the file server hold the Restore privilege there, which assigning an owner
# other than the account itself needs.
$ownerCases = @(
@{ OwnerRole = 'Admin'; Description = 'an administrator of the file server and the client'; MayAssign = $true }
@{ OwnerRole = 'ServerAdmin'; Description = 'an administrator of the file server only'; MayAssign = $true }
@{ OwnerRole = 'Delegate'; Description = 'the delegated account, an administrator of the client only'; MayAssign = $false }
)
$ownerExpectations = @(
foreach ($ownerCase in $ownerCases) {
@{ Folder = "Case5\$($ownerCase.OwnerRole)\GetOwner"; Owner = 'Administrators' }
@{ Folder = "Case5\$($ownerCase.OwnerRole)\TakeOwnership"; Owner = $ownerCase.OwnerRole }
@{ Folder = "Case5\$($ownerCase.OwnerRole)\AssignOwner"; Owner = if ($ownerCase.MayAssign) { 'Subject' } else { 'Administrators' } }
}
)
# Case 6: the state of the SACL that each folder has after the runs. The folders inherit one audit entry; the
# administrators of the file server change them, the delegated account changes nothing.
$auditInheritanceExpectations = @(
foreach ($auditRole in 'Admin', 'ServerAdmin', 'Delegate') {
$mayWrite = $auditRole -ne 'Delegate'
@{ Folder = "Case6\$auditRole\DisableAuditInheritance"; Protected = $mayWrite; Explicit = [int]$mayWrite; Inherited = [int](-not $mayWrite) }
@{ Folder = "Case6\$auditRole\EnableAuditInheritance"; Protected = -not $mayWrite; Explicit = 0; Inherited = [int]$mayWrite }
@{ Folder = "Case6\$auditRole\ClearAudit"; Protected = $false; Explicit = [int](-not $mayWrite); Inherited = 1 }
@{ Folder = "Case6\$auditRole\GetInheritance"; Protected = $false; Explicit = 0; Inherited = 1 }
}
)
$ownedFolders += @(
foreach ($auditRole in 'Admin', 'ServerAdmin', 'Delegate') {
foreach ($operation in 'DisableAuditInheritance', 'EnableAuditInheritance', 'ClearAudit', 'GetInheritance') {
@{ Folder = "Case6\$auditRole\$operation" }
}
}
)
# Case 9: the accounts of other domains and forests that the script created, if any.
$foreignAccounts = @(
if ($configured) {
foreach ($account in (Get-Content -LiteralPath $ConfigurationPath -Raw | ConvertFrom-Json).ForeignAccounts) {
@{ Name = $account.Name; Sid = $account.Sid; Rights = $account.Rights; EffectiveRights = [long]$account.EffectiveRights }
}
}
)
}
BeforeAll {
@ -377,6 +420,23 @@ Describe 'Get-NTFSEffectiveAccess for a domain account on a share folder' -Tag '
}
}
Describe 'Get-NTFSEffectiveAccess as an account that is not an administrator of the file server' -Tag 'Delegate' -Skip:(-not $configured) {
# The cmdlet page: the authorization manager of a computer answers only its administrators and the members of its
# group Access Control Assistance Operators. The error must name the denial; no access instead of an error would be
# a wrong result.
It 'Should write a GetEffectiveAccessError that names the denial with -ServerName, and no result' {
$path = Get-LabPath -RelativePath 'Case5\Delegate\GetOwner'
$result = @(Get-NTFSEffectiveAccess -Path $path -Account $configuration.Accounts.Delegate.Sid -ServerName $configuration.FileServerFqdn -WarningVariable operationWarnings -WarningAction SilentlyContinue -ErrorVariable operationErrors -ErrorAction SilentlyContinue)
$result | Should -BeNullOrEmpty
$operationWarnings | Should -BeNullOrEmpty
@(Format-LabError -ErrorRecord $operationErrors) | Should -HaveCount 1
$operationErrors[0].FullyQualifiedErrorId | Should -BeLike 'GetEffectiveAccessError,*'
$operationErrors[0].Exception.InnerException.NativeErrorCode | Should -Be 5
}
}
Describe 'Get-NTFSOrphanedAccess with the entry of a deleted domain account on a share folder' -Tag 'Admin' -Skip:(-not $configured) {
BeforeAll {
$folder = Get-LabPath -RelativePath 'Case4\OrphanedAccess'
@ -455,6 +515,324 @@ Describe 'Copy-Item2 and Move-Item2 with -WhatIf onto an existing file on a shar
}
}
Describe 'Owner cmdlets on share folders' -Skip:(-not $configured) {
# The file server decides: taking ownership needs the Take Ownership right, which Full Control includes, and
# assigning another account needs the Restore privilege there. The folders start owned by Administrators.
foreach ($ownerCase in $ownerCases) {
Context 'As <Description>' -Tag $ownerCase.OwnerRole -ForEach @($ownerCase) {
BeforeAll {
$folder = Get-LabPath -RelativePath "Case5\$OwnerRole"
$accountSid = $configuration.Accounts.$OwnerRole.Sid
}
It 'Get-NTFSOwner should return Administrators' {
$owners = @(Get-NTFSOwner -Path (Join-Path -Path $folder -ChildPath 'GetOwner') -ErrorVariable operationErrors -ErrorAction SilentlyContinue)
Format-LabError -ErrorRecord $operationErrors | Should -BeNullOrEmpty
$owners | Should -HaveCount 1
$owners[0].Owner.Sid | Should -Be $administrators
}
It 'Set-NTFSOwner should make the account itself the owner' {
$path = Join-Path -Path $folder -ChildPath 'TakeOwnership'
Set-NTFSOwner -Path $path -Account $accountSid -ErrorVariable operationErrors -ErrorAction SilentlyContinue
Format-LabError -ErrorRecord $operationErrors | Should -BeNullOrEmpty
Get-LabOwner -Path $path | Should -Be $accountSid
}
It 'Set-NTFSOwner should assign another account only with the Restore privilege of the file server' {
$path = Join-Path -Path $folder -ChildPath 'AssignOwner'
Set-NTFSOwner -Path $path -Account $configuration.Accounts.Subject.Sid -ErrorVariable operationErrors -ErrorAction SilentlyContinue
$written = (Format-LabError -ErrorRecord $operationErrors) -join ' | '
if ($MayAssign) {
$written | Should -BeNullOrEmpty
Get-LabOwner -Path $path | Should -Be $configuration.Accounts.Subject.Sid
}
else {
@(Format-LabError -ErrorRecord $operationErrors) | Should -HaveCount 1 -Because "the cmdlet wrote: $written"
$operationErrors[0].FullyQualifiedErrorId | Should -BeLike 'SetOwnerError,*'
Get-LabOwner -Path $path | Should -Be $administrators
}
}
}
}
}
Describe 'Audit inheritance cmdlets and Clear-NTFSAudit on share folders' -Skip:(-not $configured) {
# The subfolders of Case6\<role> inherit one audit entry; the role Server checks the SACLs that the runs left.
foreach ($auditCase in $auditSuccessCases) {
Context 'As <Description>' -Tag $auditCase.AuditRole -ForEach @($auditCase) {
BeforeAll {
$folder = Get-LabPath -RelativePath "Case6\$AuditRole"
}
It 'Disable-NTFSAuditInheritance should protect the audit entries and keep the owner' {
$path = Join-Path -Path $folder -ChildPath 'DisableAuditInheritance'
Disable-NTFSAuditInheritance -Path $path -ErrorVariable operationErrors -ErrorAction SilentlyContinue
Format-LabError -ErrorRecord $operationErrors | Should -BeNullOrEmpty
Get-LabOwner -Path $path | Should -Be $administrators
(Get-NTFSInheritance -Path $path).AuditInheritanceEnabled | Should -BeFalse
}
It 'Enable-NTFSAuditInheritance should let the folder inherit the audit entries and keep the owner' {
$path = Join-Path -Path $folder -ChildPath 'EnableAuditInheritance'
Enable-NTFSAuditInheritance -Path $path -ErrorVariable operationErrors -ErrorAction SilentlyContinue
Format-LabError -ErrorRecord $operationErrors | Should -BeNullOrEmpty
Get-LabOwner -Path $path | Should -Be $administrators
(Get-NTFSInheritance -Path $path).AuditInheritanceEnabled | Should -BeTrue
}
It 'Clear-NTFSAudit should remove the explicit audit entry, keep the inherited one, and keep the owner' {
$path = Join-Path -Path $folder -ChildPath 'ClearAudit'
Clear-NTFSAudit -Path $path -ErrorVariable operationErrors -ErrorAction SilentlyContinue
Format-LabError -ErrorRecord $operationErrors | Should -BeNullOrEmpty
Get-LabOwner -Path $path | Should -Be $administrators
@(Get-NTFSAudit -Path $path -ExcludeInherited) | Should -BeNullOrEmpty
@(Get-NTFSAudit -Path $path -ExcludeExplicit) | Should -HaveCount 1
}
It 'Get-NTFSInheritance should report the protected DACL and the inherited audit entries' {
$states = @(Get-NTFSInheritance -Path (Join-Path -Path $folder -ChildPath 'GetInheritance') -ErrorVariable operationErrors -ErrorAction SilentlyContinue)
Format-LabError -ErrorRecord $operationErrors | Should -BeNullOrEmpty
$states | Should -HaveCount 1
$states[0].AccessInheritanceEnabled | Should -BeFalse
$states[0].AuditInheritanceEnabled | Should -BeTrue
}
}
}
Context 'As the delegated account, an administrator of the client only' -Tag 'Delegate' {
BeforeAll {
$folder = Get-LabPath -RelativePath 'Case6\Delegate'
}
It '<Command> should write a <ErrorId> that names the missing privilege and leave the folder unchanged' -ForEach @(
@{ Command = 'Disable-NTFSAuditInheritance'; SubFolder = 'DisableAuditInheritance'; ErrorId = 'ModifySdError' }
@{ Command = 'Enable-NTFSAuditInheritance'; SubFolder = 'EnableAuditInheritance'; ErrorId = 'ModifySdError' }
@{ Command = 'Clear-NTFSAudit'; SubFolder = 'ClearAudit'; ErrorId = 'ClearAclError' }
) {
$path = Join-Path -Path $folder -ChildPath $SubFolder
$before = (Get-LabSecurityDescriptor -Path $path).GetSddlForm('All')
& $Command -Path $path -ErrorVariable operationErrors -ErrorAction SilentlyContinue
$written = (Format-LabError -ErrorRecord $operationErrors) -join ' | '
(Get-LabSecurityDescriptor -Path $path).GetSddlForm('All') | Should -Be $before -Because "the cmdlet wrote: $written"
@(Format-LabError -ErrorRecord $operationErrors) | Should -HaveCount 1 -Because "the cmdlet wrote: $written"
$operationErrors[0].FullyQualifiedErrorId | Should -BeLike "$ErrorId,*"
$operationErrors[0].Exception.Message | Should -Match 'privilege'
}
# The cmdlet page: without the Security privilege, AuditInheritanceEnabled is $null and no error is written.
It 'Get-NTFSInheritance should report the protected DACL, no audit state, and no error' {
$states = @(Get-NTFSInheritance -Path (Join-Path -Path $folder -ChildPath 'GetInheritance') -ErrorVariable operationErrors -ErrorAction SilentlyContinue)
Format-LabError -ErrorRecord $operationErrors | Should -BeNullOrEmpty
$states | Should -HaveCount 1
$states[0].AccessInheritanceEnabled | Should -BeFalse
$states[0].AuditInheritanceEnabled | Should -BeNullOrEmpty
}
}
}
Describe 'Item cmdlets on a share folder' -Tag 'Delegate' -Skip:(-not $configured) {
# The delegated account fully controls the folder through its domain group.
BeforeAll {
$folder = Get-LabPath -RelativePath 'Case7\Items'
$source = Join-Path -Path $folder -ChildPath 'Source.txt'
}
It 'Get-Item2 should return the file with its path on the share' {
$item = @(Get-Item2 -Path $source -ErrorVariable operationErrors -ErrorAction SilentlyContinue)
Format-LabError -ErrorRecord $operationErrors | Should -BeNullOrEmpty
$item | Should -HaveCount 1
$item[0].FullName | Should -Be $source
$item[0].Length | Should -Be 6
}
It 'Test-Path2 should find the file and the folder, and not a missing item' {
Test-Path2 -Path $source -PathType Leaf | Should -BeTrue
Test-Path2 -Path (Join-Path -Path $folder -ChildPath 'Folder') -PathType Container | Should -BeTrue
Test-Path2 -Path (Join-Path -Path $folder -ChildPath 'Missing.txt') | Should -BeFalse
}
It 'Get-FileHash2 should return the hash that Get-FileHash returns' {
$hash = @(Get-FileHash2 -Path $source -Algorithm SHA256 -ErrorVariable operationErrors -ErrorAction SilentlyContinue)
Format-LabError -ErrorRecord $operationErrors | Should -BeNullOrEmpty
$hash | Should -HaveCount 1
$hash[0].Hash | Should -Be (Get-FileHash -LiteralPath $source -Algorithm SHA256).Hash
}
It 'Copy-Item2 should copy a file, and a folder with its file' {
Copy-Item2 -Path $source -Destination (Join-Path -Path $folder -ChildPath 'Copy.txt') -ErrorVariable operationErrors -ErrorAction SilentlyContinue
Copy-Item2 -Path (Join-Path -Path $folder -ChildPath 'Folder') -Destination (Join-Path -Path $folder -ChildPath 'FolderCopy') -ErrorVariable +operationErrors -ErrorAction SilentlyContinue
Format-LabError -ErrorRecord $operationErrors | Should -BeNullOrEmpty
Get-Content -LiteralPath (Join-Path -Path $folder -ChildPath 'Copy.txt') -Raw | Should -Be 'Source'
Get-Content -LiteralPath (Join-Path -Path $folder -ChildPath 'FolderCopy\File.txt') -Raw | Should -Be 'File'
Get-Content -LiteralPath $source -Raw | Should -Be 'Source'
}
It 'Move-Item2 should move a file' {
$moving = Join-Path -Path $folder -ChildPath 'Move.txt'
Move-Item2 -Path $moving -Destination (Join-Path -Path $folder -ChildPath 'Moved.txt') -ErrorVariable operationErrors -ErrorAction SilentlyContinue
Format-LabError -ErrorRecord $operationErrors | Should -BeNullOrEmpty
Test-Path -LiteralPath $moving | Should -BeFalse
Get-Content -LiteralPath (Join-Path -Path $folder -ChildPath 'Moved.txt') -Raw | Should -Be 'Move'
}
It 'Remove-Item2 should remove a file' {
$removing = Join-Path -Path $folder -ChildPath 'Remove.txt'
Remove-Item2 -Path $removing -ErrorVariable operationErrors -ErrorAction SilentlyContinue
Format-LabError -ErrorRecord $operationErrors | Should -BeNullOrEmpty
Test-Path -LiteralPath $removing | Should -BeFalse
}
It 'Get-ChildItem2 should list the file and the folder that the tests leave in place' {
$names = @(Get-ChildItem2 -Path $folder -ErrorVariable operationErrors -ErrorAction SilentlyContinue).Name
Format-LabError -ErrorRecord $operationErrors | Should -BeNullOrEmpty
$names | Should -Contain 'Source.txt'
$names | Should -Contain 'Folder'
}
}
Describe 'Link cmdlets on a share folder' -Tag 'Admin' -Skip:(-not $configured) {
BeforeAll {
$folder = Get-LabPath -RelativePath 'Case8\Links'
$target = Join-Path -Path $folder -ChildPath 'Target.txt'
$hardLink = Join-Path -Path $folder -ChildPath 'HardLink.txt'
}
It 'New-NTFSHardLink should give the file a second name' {
New-NTFSHardLink -Path $hardLink -Target $target -ErrorVariable operationErrors -ErrorAction SilentlyContinue
Format-LabError -ErrorRecord $operationErrors | Should -BeNullOrEmpty
Get-Content -LiteralPath $hardLink -Raw | Should -Be 'Target'
}
# The cmdlet pages: Windows can't list the names of a file on a share, so the cmdlets write a GetHardLinkError.
It 'Get-NTFSHardLink should write a GetHardLinkError, because Windows cannot list the names on a share' {
$links = @(Get-NTFSHardLink -Path $target -ErrorVariable operationErrors -ErrorAction SilentlyContinue)
$links | Should -BeNullOrEmpty
@(Format-LabError -ErrorRecord $operationErrors) | Should -HaveCount 1
$operationErrors[0].FullyQualifiedErrorId | Should -BeLike 'GetHardLinkError,*'
}
It 'New-NTFSHardLink -PassThru should create the link and write a GetHardLinkError instead of the names' {
$passThruLink = Join-Path -Path $folder -ChildPath 'PassThruLink.txt'
$result = @(New-NTFSHardLink -Path $passThruLink -Target $target -PassThru -ErrorVariable operationErrors -ErrorAction SilentlyContinue)
$result | Should -BeNullOrEmpty
@(Format-LabError -ErrorRecord $operationErrors) | Should -HaveCount 1
$operationErrors[0].FullyQualifiedErrorId | Should -BeLike 'GetHardLinkError,*'
Get-Content -LiteralPath $passThruLink -Raw | Should -Be 'Target'
}
It 'New-NTFSSymbolicLink should create a link to a file and a link to a folder' {
New-NTFSSymbolicLink -Path (Join-Path -Path $folder -ChildPath 'FileLink.txt') -Target $target -ErrorVariable operationErrors -ErrorAction SilentlyContinue
New-NTFSSymbolicLink -Path (Join-Path -Path $folder -ChildPath 'FolderLink') -Target (Join-Path -Path $folder -ChildPath 'TargetFolder') -ErrorVariable +operationErrors -ErrorAction SilentlyContinue
Format-LabError -ErrorRecord $operationErrors | Should -BeNullOrEmpty
}
}
Describe 'Get-NTFSSimpleAccess on share folders' -Tag 'Delegate' -Skip:(-not $configured) {
It 'Should report the parent folder first and for the subfolder only the entry of its own' {
$parent = Get-LabPath -RelativePath 'Case9\Simple'
$child = Join-Path -Path $parent -ChildPath 'Child'
$entries = @(Get-NTFSSimpleAccess -Path $child -ErrorVariable operationErrors -ErrorAction SilentlyContinue)
Format-LabError -ErrorRecord $operationErrors | Should -BeNullOrEmpty
$entries | Should -Not -BeNullOrEmpty
$entries[0].FullName | Should -Be $parent
@($entries | Where-Object -Property FullName -EQ -Value $child).Identity.Sid | Should -Be $configuration.Accounts.Subject.Sid
}
}
Describe 'Get-NTFSOrphanedAudit with the audit entry of a deleted domain account on a share folder' -Tag 'Admin' -Skip:(-not $configured) {
It 'Should return the audit entry of the deleted account with its SID' {
$entries = @(Get-NTFSOrphanedAudit -Path (Get-LabPath -RelativePath 'Case4\OrphanedAudit') -WarningVariable operationWarnings -WarningAction SilentlyContinue -ErrorVariable operationErrors -ErrorAction SilentlyContinue)
Format-LabError -ErrorRecord $operationErrors | Should -BeNullOrEmpty
$operationWarnings | Should -BeNullOrEmpty
$entries | Should -HaveCount 1
$entries[0].Account.Sid | Should -Be $configuration.Accounts.Orphan.Sid
}
}
Describe 'Accounts of another domain and of other forests on share folders' -Tag 'Admin' -Skip:(-not $configured -or $foreignAccounts.Count -eq 0) {
# Through the trusts, the client resolves the names of the accounts, and the file server creates their tokens.
BeforeAll {
$folder = Get-LabPath -RelativePath 'Case9\Foreign'
}
It 'Get-NTFSAccess should return the entry of <Name> with its name' -ForEach $foreignAccounts {
$entries = @(Get-NTFSAccess -Path $folder -ExcludeInherited -ErrorVariable operationErrors -ErrorAction SilentlyContinue |
Where-Object -FilterScript { $_.Account.Sid -eq $Sid })
Format-LabError -ErrorRecord $operationErrors | Should -BeNullOrEmpty
$entries | Should -HaveCount 1
$entries[0].Account.AccountName | Should -Be $Name
}
It 'Get-NTFSOrphanedAccess should not report the entries of the accounts' {
$entries = @(Get-NTFSOrphanedAccess -Path $folder -ErrorVariable operationErrors -ErrorAction SilentlyContinue)
Format-LabError -ErrorRecord $operationErrors | Should -BeNullOrEmpty
$entries | Should -BeNullOrEmpty
}
It 'Add-NTFSAccess should add an entry for <Name> by its name' -ForEach $foreignAccounts {
$path = Get-LabPath -RelativePath 'Case9\ForeignAdd'
Add-NTFSAccess -Path $path -Account $Name -AccessRights ReadAndExecute -ErrorVariable operationErrors -ErrorAction SilentlyContinue
Format-LabError -ErrorRecord $operationErrors | Should -BeNullOrEmpty
@(Get-LabExplicitAccessRule -Path $path -Sid $Sid) | Should -HaveCount 1
}
It 'Remove-NTFSAccess should remove the entry of <Name> by its name' -ForEach $foreignAccounts {
$path = Get-LabPath -RelativePath 'Case9\ForeignRemove'
Remove-NTFSAccess -Path $path -Account $Name -AccessRights ReadAndExecute -InheritanceFlags 'ContainerInherit, ObjectInherit' -PropagationFlags None -ErrorVariable operationErrors -ErrorAction SilentlyContinue
Format-LabError -ErrorRecord $operationErrors | Should -BeNullOrEmpty
Get-LabExplicitAccessRule -Path $path -Sid $Sid | Should -BeNullOrEmpty
}
It 'Get-NTFSEffectiveAccess should return the rights of <Name> on the file server with -ServerName, without a warning' -ForEach $foreignAccounts {
$EffectiveRights | Should -BeGreaterThan 0 -Because 'the file server must create a token for the account to calculate the expected rights'
$result = @(Get-NTFSEffectiveAccess -Path $folder -Account $Name -ServerName $configuration.FileServerFqdn -WarningVariable operationWarnings -WarningAction SilentlyContinue -ErrorVariable operationErrors -ErrorAction SilentlyContinue)
Format-LabError -ErrorRecord $operationErrors | Should -BeNullOrEmpty
$operationWarnings | Should -BeNullOrEmpty
$result | Should -HaveCount 1
Format-LabRight -Right $result[0].AccessRights | Should -Be (Format-LabRight -Right $EffectiveRights)
}
}
Describe 'Security descriptors on the file server after the runs on the client' -Tag 'Server' -Skip:(-not $configured) {
It 'Should keep Administrators as the owner of <Folder>' -ForEach $ownedFolders {
Get-LabOwner -Path (Get-LabPath -RelativePath $Folder) | Should -Be $administrators
@ -465,4 +843,43 @@ Describe 'Security descriptors on the file server after the runs on the client'
@($acl.GetAuditRules($true, $false, $sidType)).Count | Should -Be $Count
}
It 'Should have the owner <Owner> on <Folder>' -ForEach $ownerExpectations {
$expected = if ($Owner -eq 'Administrators') { $administrators } else { $configuration.Accounts.$Owner.Sid }
Get-LabOwner -Path (Get-LabPath -RelativePath $Folder) | Should -Be $expected
}
It 'Should have <Explicit> explicit and <Inherited> inherited audit entries on <Folder>, protected: <Protected>' -ForEach $auditInheritanceExpectations {
$acl = Get-Acl -LiteralPath (Get-LabPath -RelativePath $Folder) -Audit
$acl.AreAuditRulesProtected | Should -Be $Protected
@($acl.GetAuditRules($true, $false, $sidType)).Count | Should -Be $Explicit
@($acl.GetAuditRules($false, $true, $sidType)).Count | Should -Be $Inherited
}
It 'Should have the items that the item cmdlets left' {
$folder = Get-LabPath -RelativePath 'Case7\Items'
foreach ($name in 'Source.txt', 'Copy.txt', 'Moved.txt', 'FolderCopy\File.txt') {
Test-Path -LiteralPath (Join-Path -Path $folder -ChildPath $name) -PathType Leaf | Should -BeTrue -Because $name
}
foreach ($name in 'Move.txt', 'Remove.txt') {
Test-Path -LiteralPath (Join-Path -Path $folder -ChildPath $name) | Should -BeFalse -Because $name
}
}
It 'Should have the links that the link cmdlets created' {
$folder = Get-LabPath -RelativePath 'Case8\Links'
@(& fsutil.exe hardlink list (Join-Path -Path $folder -ChildPath 'Target.txt') | Where-Object -FilterScript { $_ }) | Should -HaveCount 3
(Get-Item -LiteralPath (Join-Path -Path $folder -ChildPath 'FileLink.txt') -Force).LinkType | Should -Be 'SymbolicLink'
(Get-Item -LiteralPath (Join-Path -Path $folder -ChildPath 'FolderLink') -Force).LinkType | Should -Be 'SymbolicLink'
}
It 'Should have the entry of <Name> that Add-NTFSAccess added, and not the one that Remove-NTFSAccess removed' -ForEach $foreignAccounts {
@(Get-LabExplicitAccessRule -Path (Get-LabPath -RelativePath 'Case9\ForeignAdd') -Sid $Sid) | Should -HaveCount 1
Get-LabExplicitAccessRule -Path (Get-LabPath -RelativePath 'Case9\ForeignRemove') -Sid $Sid | Should -BeNullOrEmpty
}
}

25
Tests/Lab/README.md

@ -13,11 +13,16 @@ without a lab they skip every test.
| --- | --- | --- |
| 1, [#34][issue-34] | Delegate | `Add-NTFSAccess`, `Remove-NTFSAccess`, `Clear-NTFSAccess`, `Disable-NTFSAccessInheritance`, `Enable-NTFSAccessInheritance`, `Set-NTFSInheritance`, and `Set-NTFSSecurityDescriptor` on share folders that Administrators own and on which a domain group has Full Control, run by a member of that group who isn't an administrator of the file server. They succeed and keep the owner. |
| 2 | Admin, ServerAdmin, Delegate | `Get-NTFSAudit`, `Add-NTFSAudit`, and `Remove-NTFSAudit` on share folders. Over SMB, the file server checks the Security privilege of the account. The administrators of the file server read and change the audit entries; the delegated account gets the errors that the cmdlet pages describe, and the folders stay unchanged. |
| 3 | Admin | `Get-NTFSEffectiveAccess` for a domain account with rights through two nested domain groups and through a local group of the file server. With `-ServerName`, the result includes the local group, without a warning; without it, the client doesn't know that group. With an unreachable server, the cmdlet falls back to the client and warns. |
| 4 | Admin | `Get-NTFSOrphanedAccess` returns the entry of a deleted domain account with its SID, on the folder and as inherited entry on a file in it. |
| 3 | Admin, Delegate | `Get-NTFSEffectiveAccess` for a domain account with rights through two nested domain groups and through a local group of the file server. With `-ServerName`, the result includes the local group, without a warning; without it, the client doesn't know that group. With an unreachable server, the cmdlet falls back to the client and warns. The authorization manager of the file server refuses the delegated account, which isn't an administrator there, and the cmdlet reports that as an error, not as no access. |
| 4 | Admin | `Get-NTFSOrphanedAccess` returns the entry of a deleted domain account with its SID, on the folder and as inherited entry on a file in it; `Get-NTFSOrphanedAudit` returns the audit entry of that account. |
| 5 | Admin, ServerAdmin, Delegate | `Get-NTFSOwner` and `Set-NTFSOwner` on share folders that Administrators own. Every role makes itself the owner; only the administrators of the file server, which hold the Restore privilege there, assign another account. The delegated account gets a `SetOwnerError`, and the owner stays. |
| 6 | Admin, ServerAdmin, Delegate | `Disable-NTFSAuditInheritance`, `Enable-NTFSAuditInheritance`, `Clear-NTFSAudit`, and `Get-NTFSInheritance` on share folders that inherit an audit entry. The administrators of the file server change the audit entries; the delegated account gets the errors that the cmdlet pages describe, and `Get-NTFSInheritance` reports no audit state for it. |
| 7 | Delegate | `Get-Item2`, `Test-Path2`, `Get-FileHash2`, `Copy-Item2`, `Move-Item2`, `Remove-Item2`, and `Get-ChildItem2` in a share folder. |
| 8 | Admin | `New-NTFSHardLink`, `Get-NTFSHardLink`, and `New-NTFSSymbolicLink` in a share folder. Windows can't list the names of a file on a share, so `Get-NTFSHardLink` and `New-NTFSHardLink -PassThru` write the `GetHardLinkError` that their pages describe. |
| 9 | Delegate, Admin | `Get-NTFSSimpleAccess` compares a share folder with its parent. For the accounts of another domain and of other forests, `Get-NTFSAccess` returns their names, `Get-NTFSOrphanedAccess` doesn't report them, `Add-NTFSAccess` and `Remove-NTFSAccess` find them by name, and `Get-NTFSEffectiveAccess -ServerName` returns the rights that the file server's own token of each account gets. |
| Long paths | Admin | `Get-ChildItem2` and `Get-NTFSAccess` with a share path longer than 260 characters. |
| [#108][issue-108] | Admin | `Copy-Item2` and `Move-Item2` with `-WhatIf` onto an existing file on the share write no error. |
| State | Server | After the runs on the client, the file server checks the owners and the audit entries of the folders itself, without the module. |
| State | Server | After the runs on the client, the file server checks the owners, the audit entries, the items, the links, and the entries of the foreign accounts itself, without the module. |
Case 1 uses two kinds of folders. Before 5.0.0-rc3, the cmdlets wrote back the
owner that Windows returns with a DACL without the auto-inherit flag, and the
@ -29,7 +34,8 @@ Windows 2000. `Set-NTFSSecurityDescriptor` wrote the owner on both kinds.
The expected rights of case 3 come from the tokens that the file server and the
client create for the account with a Kerberos S4U logon, the way the Effective
Access tab of the advanced security settings does.
Access tab of the advanced security settings does. Case 9 calculates the rights
of the foreign accounts the same way, on the file server.
## Roles
@ -43,7 +49,8 @@ Access tab of the advanced security settings does.
The script also creates `NtfsLiveSubject`, the account of case 3, which is a
member of `NtfsLiveInner`, a member of `NtfsLiveOuter`, and of the local group
`NtfsLiveLocal` of the file server, and `NtfsLiveOrphan`, which it deletes in
every run.
every run. For case 9, it creates `NtfsLiveForeign` in the organizational unit
`NTFSSecurityLive` of each domain of `-ForeignDomainController`.
## Lab
@ -55,11 +62,15 @@ use the lab of
`tests/Lab/Deploy-WindowsAccessControlLab.ps1` in that repository deploys:
`F1ADC1` as domain controller, `F1AFile2` as file server, and `F1AFile1` as
client, all in `a.forest1.net`. `-DomainController`, `-FileServer`, and
`-Client` select other machines.
`-Client` select other machines. Case 9 uses `F1BDC1` of `b.forest1.net`, a
domain of the same forest, and `F2DC1` and `F3DC1` of the forests
`forest2.net` and `forest3.net`, which have forest trusts with `forest1.net`;
`-ForeignDomainController @()` leaves it out.
The script adds to the lab:
- the organizational unit `NTFSSecurityLive` with the accounts and groups
- the organizational unit `NTFSSecurityLive` with the accounts and groups, and
with `NtfsLiveForeign` in the domains of the foreign domain controllers
- the local group `NtfsLiveLocal` and members of Administrators on the file
server, and members of Administrators and Remote Management Users on the
client

Loading…
Cancel
Save