When working with PowerShell's Set-Acl
cmdlet, many administrators discover that ownership changes don't automatically propagate to child items. This limitation can create security vulnerabilities when applying new ownership structures across complex directory trees.
Here's an enhanced version that processes all subdirectories and files:
# Define target account and folder path
$newOwner = New-Object System.Security.Principal.NTAccount('DOMAIN\Enterprise Admins')
$targetFolder = "C:\Path\To\MyFolder"
# Function to set ownership recursively
function Set-OwnershipRecursive {
param (
[string]$path,
[System.Security.Principal.NTAccount]$owner
)
# Process current folder
$acl = Get-Acl -Path $path
$acl.SetOwner($owner)
Set-Acl -Path $path -AclObject $acl
# Process child items
Get-ChildItem -Path $path -Recurse | ForEach-Object {
$itemAcl = Get-Acl -Path $_.FullName
$itemAcl.SetOwner($owner)
Set-Acl -Path $_.FullName -AclObject $itemAcl
}
}
# Execute the function
Set-OwnershipRecursive -path $targetFolder -owner $newOwner
When dealing with massive directory structures, consider these optimizations:
# Improved version with progress tracking
function Set-OwnershipRecursive {
param (
[string]$path,
[System.Security.Principal.NTAccount]$owner,
[int]$batchSize = 1000
)
$items = Get-ChildItem -Path $path -Recurse -Force
$processed = 0
$total = $items.Count
foreach ($item in $items) {
try {
$acl = Get-Acl -Path $item.FullName
$acl.SetOwner($owner)
Set-Acl -Path $item.FullName -AclObject $acl
if (++$processed % $batchSize -eq 0) {
Write-Progress -Activity "Changing ownership" -Status "$processed of $total completed" -PercentComplete ($processed/$total*100)
}
}
catch {
Write-Warning "Failed to process $($item.FullName): $_"
}
}
}
For scenarios where you need to preserve or modify inheritance flags:
# Sample modification to preserve inheritance
$acl.SetAccessRuleProtection($false, $true)
For environments where PowerShell might be restricted:
# Using ICACLS via PowerShell
$account = "DOMAIN\Enterprise Admins"
$folder = "C:\Path\To\MyFolder"
Start-Process -FilePath "icacls" -ArgumentList ""$folder" /setowner "$account" /T /C /Q" -Verb RunAs -Wait
When managing Windows file systems, administrators often need to modify ownership permissions throughout an entire directory tree. The native PowerShell Set-ACL cmdlet doesn't natively handle recursive ownership changes, which creates a common pain point for system automation scripts.
Here's a complete implementation that handles both folders and files:
function Set-RecursiveOwner {
param (
[string]$Path,
[string]$Account
)
$acct = New-Object System.Security.Principal.NTAccount($Account)
$items = Get-ChildItem -Path $Path -Recurse -Force
foreach ($item in $items) {
try {
$acl = $item.GetAccessControl([System.Security.AccessControl.AccessControlSections]::Owner)
$acl.SetOwner($acct)
Set-Acl -Path $item.FullName -AclObject $acl
Write-Verbose "Changed owner for: $($item.FullName)"
}
catch {
Write-Warning "Failed to set owner for $($item.FullName): $_"
}
}
# Process the parent folder last
$parentAcl = (Get-Item $Path).GetAccessControl()
$parentAcl.SetOwner($acct)
Set-Acl -Path $Path -AclObject $parentAcl
}
For enterprise environments, consider these enhancements:
# Version with progress reporting and error handling
function Set-RecursiveOwnerAdvanced {
param (
[Parameter(Mandatory=$true)]
[string]$Path,
[Parameter(Mandatory=$true)]
[string]$Account,
[int]$ThrottleLimit = 5,
[switch]$SkipFiles
)
$acct = New-Object System.Security.Principal.NTAccount($Account)
$items = Get-ChildItem -Path $Path -Recurse -Force
$counter = 0
$total = $items.Count
$startTime = Get-Date
$items | ForEach-Object -ThrottleLimit $ThrottleLimit -Parallel {
if ($using:SkipFiles -and $_.PSIsContainer -eq $false) {
return
}
try {
$acl = $_.GetAccessControl([System.Security.AccessControl.AccessControlSections]::Owner)
$acl.SetOwner($using:acct)
Set-Acl -Path $_.FullName -AclObject $acl
$counter = [System.Threading.Interlocked]::Increment($using:counter)
if ($counter % 100 -eq 0) {
$elapsed = (Get-Date) - $using:startTime
$remaining = ($using:total - $counter) * ($elapsed.TotalSeconds / $counter)
Write-Progress -Activity "Changing ownership" -Status "$counter of $using:total items processed"
-PercentComplete ($counter/$using:total*100) -SecondsRemaining $remaining
}
}
catch {
Write-Warning "Error on $($_.FullName): $_"
}
}
}
For large directory trees, you might consider:
1. Using icacls.exe with inheritance:
icacls "C:\TargetFolder" /setowner "DOMAIN\Enterprise Admins" /T /C /Q
2. Combining PowerShell with robocopy for very large sets:
$tempFolder = "C:\temp\ownershipfix"
robocopy "C:\TargetFolder" $tempFolder /MIR /SECFIX /COPYALL /R:1 /W:1