How to Compare Directory Structures and NTFS Permissions Across Windows Machines Programmatically


14 views

When dealing with multiple Windows machines in an enterprise environment or during software deployment, verifying consistent directory structures and permissions becomes crucial. A common pain point emerges when:

  • Driver updates modify ACLs unexpectedly
  • File deployments miss critical permission settings
  • Security policies create inconsistent access controls

For quick comparisons without third-party tools, PowerShell provides powerful capabilities:


# Basic directory comparison with hashes
$sourceFiles = Get-ChildItem -Recurse C:\source\path
$destFiles = Get-ChildItem -Recurse \\remote\path
Compare-Object $sourceFiles $destFiles -Property Name, Length, LastWriteTime

# NTFS permission comparison
Function Compare-FolderPermissions {
    param(
        [string]$Path1,
        [string]$Path2
    )
    $acl1 = Get-Acl $Path1
    $acl2 = Get-Acl $Path2
    return Compare-Object $acl1.Access $acl2.Access -Property IdentityReference, FileSystemRights
}

# Example usage:
Compare-FolderPermissions -Path1 "C:\Program Files\App" -Path2 "\\server\C$\Program Files\App"

Microsoft's built-in Robocopy offers verbose comparison capabilities:


robocopy C:\source \\remote\destination /L /X /E /ZB /COPY:DATSOU /LOG:comparison.log /TEE /NP /R:0 /W:0

Key switches:

  • /L - List-only mode (no actual copying)
  • /COPY:DATSOU - Compare Data, Attributes, Timestamps, Security, Owner, Auditing info
  • /X - Report all extra files (not just differing ones)
  • For integration into deployment tools or custom solutions:

    
    using System.IO;
    using System.Security.AccessControl;
    
    public class PermissionComparer
    {
        public void CompareDirectories(string dir1, string dir2)
        {
            var dir1ACL = Directory.GetAccessControl(dir1);
            var dir2ACL = Directory.GetAccessControl(dir2);
            
            // Compare access rules
            var rules1 = dir1ACL.GetAccessRules(true, true, typeof(System.Security.Principal.NTAccount));
            var rules2 = dir2ACL.GetAccessRules(true, true, typeof(System.Security.Principal.NTAccount));
            
            // Implementation for detailed comparison would go here
        }
    }
    

    For GUI-based solutions consider:

    • AccessEnum (Sysinternals) - Lightweight permission snapshot tool
    • Beyond Compare - Commercial tool with permission comparison
    • TreeComp - Specialized in folder structure analysis

    Sample script for build pipelines:

    
    # PowerShell script for Azure DevOps
    $expectedAcl = Get-Acl "C:\reference\permissions"
    $targetAcl = Get-Acl $(Build.ArtifactStagingDirectory)
    
    $diff = Compare-Object $expectedAcl.Access $targetAcl.Access -Property IdentityReference, FileSystemRights
    if ($diff) {
        Write-Error "Permission mismatch detected!"
        $diff | Format-Table -AutoSize
        exit 1
    }
    

    When troubleshooting:

    1. Always check inherited vs explicit permissions separately
    2. Compare effective permissions (use whoami /priv)
    3. Remember that SYSTEM and admin permissions may differ
    4. Account for UAC virtualization in Program Files

    When troubleshooting Windows system issues, comparing directory structures and NTFS permissions between machines is a common but surprisingly complex task. Standard file comparison tools like WinMerge focus primarily on file contents rather than metadata, leaving administrators without a clear way to audit permission differences programmatically.

    Windows PowerShell provides native capabilities for comparing both directory structures and permissions. Here's a comprehensive script that outputs differences in a machine-readable format:

    
    # Compare directories and permissions between two paths
    $sourcePath = "\\Machine1\C$\Program Files\App"
    $targetPath = "\\Machine2\C$\Program Files\App"
    $outputFile = "C:\Temp\PermissionDiff.txt"
    
    function Get-PermissionHash {
        param ($path)
        Get-ChildItem $path -Recurse | ForEach-Object {
            $acl = Get-Acl $_.FullName
            [PSCustomObject]@{
                Path = $_.FullName
                Permissions = $acl.Access | 
                    Select-Object IdentityReference, FileSystemRights, AccessControlType
                LastWriteTime = $_.LastWriteTime
                Attributes = $_.Attributes
            }
        }
    }
    
    $sourceData = Get-PermissionHash $sourcePath
    $targetData = Get-PermissionHash $targetPath
    
    Compare-Object $sourceData $targetData -Property Path,Permissions,LastWriteTime,Attributes |
        Where-Object { $_.SideIndicator -eq "<=" } | 
        Export-Csv $outputFile -NoTypeInformation
    

    For quick structural comparisons, Robocopy's logging feature can identify missing files:

    
    robocopy "\\Machine1\C$\Program Files\App" "\\Machine2\C$\Program Files\App" /L /X /XD /E /NP /LOG:C:\Temp\DirDiff.txt
    

    For enterprise environments, these tools provide advanced comparison capabilities:

    • Beyond Compare (with Pro version's ACL comparison)
    • SetACL Studio - Specialized for permission auditing
    • TreeSize Professional - Includes permission reporting

    Once differences are identified, you can use icacls to synchronize permissions:

    
    # Export permissions from reference machine
    icacls "C:\Program Files\App\*" /save permExport.txt /t /c
    
    # Apply to target machine
    icacls "C:\Program Files\App" /restore permExport.txt
    

    Remember to account for:

    • Inherited vs explicit permissions
    • Local vs domain accounts (SID resolution)
    • System-reserved directories requiring TrustedInstaller access