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


4 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