DEV Community

Excalibra
Excalibra

Posted on

1

PowerShell - Convert NTFS Folder Security Permissions to Corresponding Share Permissions (Project)

Background

Requirements:

The company is upgrading its domain controllers and shared server hardware, replacing standard desktop machines with professional servers. This process requires setting up backup domain controllers and performing a complete migration of shared files.


File Migration Tool Comparison:

FSMT:

There are guides on using FSMT for file server migration and consolidation. However, just gathering the resources took ages. Using FSMT on Server 2019 was inconvenient and it required installing additional components like .NET 2.0. After installation, error messages during operation discouraged further exploration, so I opted for another approach.

Windows Migration Tools:

Windows Migration Tools for NTFS and share permissions migration. However, upon installation, the tool frequently displayed the following errors, leading me to abandon it:

Opening connection. Please run Receive-SmigServerData on the target server. Opening connection. Please run Send-SmigServerData on the source server. Send-SmigServerData: The transport failed to connect to the destination. This cmdlet could not connect to the destination server. For details, refer to the "Troubleshooting Data Migration Connections" section in the File Services Migration Guide on Windows Server 2016 TechCenter.
Enter fullscreen mode Exit fullscreen mode

Other Methods:

Other approaches, such as the use of Permcopy or methods involving registry imports, were equally unsatisfactory.


Writing a Script to Achieve the Goal:

While Robocopy can preserve NTFS security permissions, it cannot retain share permissions, requiring manual setup. Given the shortcomings of other tools, I decided to write a script to achieve this.


The Solution: Robocopy + PowerShell

Share Folder Permission Rules:

  1. Shared Permission: "Read"Security Permission: "Read-only".
  2. Shared Permission: "Read" + "Change"Security Permissions: "Read," "List Folder Contents," "Read & Execute," "Modify," "Write".

The goal was to map security permissions to the corresponding share permissions:

  • Security permissions set to "Read-only" should translate to the share permission "Read."

A script was created to convert security permissions into corresponding share permissions. The first step was enabling sharing settings.

Image description

The name of a shared folder is often the same as the actual folder name. Firstly, this is the system default, and secondly, it makes management more convenient.

Key Points:

  • The shared folder name often matches the actual folder name by default, making it easier to manage.

Source Code and Repository:

Find the source code and implementation details here:

GitHub Repository: Convert NTFS Permissions to Shared Permissions

Script File: Convert NTFS Permissions to Shared Permissions.ps1

# Accept user input for the folder path
$folderPath = Read-Host -Prompt "Please enter the folder path (e.g., C:\SharedFolder\QualityCheck)"

# Accept user input for the share name
$shareName = Read-Host -Prompt "Please enter the share name (e.g., QualityCheck)"

try {
    # Retrieve the folder's NTFS security permissions
    $acl = Get-Acl -Path $folderPath

    # Disable inheritance of permissions for the folder and convert existing inherited permissions to explicit ones
    $acl.SetAccessRuleProtection($true, $false)
    Set-Acl -Path $folderPath -AclObject $acl
    Write-Output "Permission inheritance has been disabled and explicit permissions have been set."
} catch {
    Write-Output "An error occurred while retrieving folder permissions or disabling inheritance: $_"
    exit
}

# Build a valid list of NTFS user permissions
$ntfsUsers = @{ }

foreach ($access in $acl.Access) {
    $user = $access.IdentityReference.Value
    $permissions = $access.FileSystemRights.ToString()

    if ($permissions.Contains("FullControl")) {
        $ntfsUsers[$user] = "Full"
    }
    elseif ($permissions.Contains("Modify")) {
        $ntfsUsers[$user] = "Change"
    }
    elseif ($permissions.Contains("ReadAndExecute") -or $permissions.Contains("Read")) {
        $ntfsUsers[$user] = "Read"
    }
    else {
        Write-Output "Unmatched permission type: ${permissions}. Skipping shared permission settings for user ${user}."
    }
}

# Define an exception list, applicable only for NTFS permissions (not added to shared permissions)
$exceptionList = @(
    "CREATOR OWNER", 
    "SYSTEM", 
    "Users", 
    "Administrators", 
    "BUILTIN\Administrators", 
    "NT AUTHORITY\SYSTEM", 
    "NT AUTHORITY\Authenticated Users"
)

try {
    # Clear existing shared permissions to ensure only non-exception NTFS users are added
    Get-SmbShareAccess -Name $shareName | ForEach-Object {
        Revoke-SmbShareAccess -Name $shareName -AccountName $_.AccountName -Force
    }
    Write-Output "Existing shared permissions have been cleared."

    # Set shared permissions for NTFS users who are not in the exception list
    foreach ($user in $ntfsUsers.Keys) {
        if (-not $exceptionList.Contains($user)) {
            try {
                # Check if the user is in SID format (avoid errors due to lack of account names)
                if ($user -match "^S-\d-\d+-(\d+-){1,14}\d+$") {
                    Write-Output "Skipping SID-format user ${user} for shared permission settings."
                } else {
                    Grant-SmbShareAccess -Name $shareName -AccountName $user -AccessRight ${ntfsUsers[$user]} -Force
                    Write-Output "Shared permission set for user ${user}: ${ntfsUsers[$user]}"
                }
            } catch {
                Write-Output "Error setting shared permissions for user ${user} (no mapping between account name and SID): $_"
            }
        }
    }
} catch {
    Write-Output "An error occurred while clearing shared permissions: $_"
}

try {
    # Remove users not in the NTFS list and add custom permissions for exception users
    foreach ($access in $acl.Access) {
        $user = $access.IdentityReference.Value
        if (-not $ntfsUsers.ContainsKey($user) -and -not $exceptionList.Contains($user)) {
            $acl.RemoveAccessRule($access)
            Write-Output "NTFS permission removed for user ${user}."
        }
    }

    # Remove 'Everyone' from shared and NTFS permissions
    Revoke-SmbShareAccess -Name $shareName -AccountName "Everyone" -Force
    $acl.Access | Where-Object { $_.IdentityReference -eq "Everyone" } | ForEach-Object { $acl.RemoveAccessRule($_) }
    Write-Output "'Everyone' has been removed from shared and NTFS permissions."

    # Add system account permissions from the exception list
    $usersRule = New-Object System.Security.AccessControl.FileSystemAccessRule("Users", "ReadAndExecute, ListDirectory, Read", "ContainerInherit, ObjectInherit", "None", "Allow")
    $adminsRule = New-Object System.Security.AccessControl.FileSystemAccessRule("BUILTIN\Administrators", "FullControl", "ContainerInherit, ObjectInherit", "None", "Allow")
    $systemRule = New-Object System.Security.AccessControl.FileSystemAccessRule("NT AUTHORITY\SYSTEM", "FullControl", "ContainerInherit, ObjectInherit", "None", "Allow")
    $creatorOwnerRule = New-Object System.Security.AccessControl.FileSystemAccessRule("CREATOR OWNER", "FullControl", "ContainerInherit, ObjectInherit", "InheritOnly", "Allow")

    # Add the new rules to the ACL
    $acl.AddAccessRule($usersRule)
    $acl.AddAccessRule($adminsRule)
    $acl.AddAccessRule($systemRule)
    $acl.AddAccessRule($creatorOwnerRule)

    # Apply the modified ACL to the folder
    Set-Acl -Path $folderPath -AclObject $acl
    Write-Output "Shared and NTFS permissions have been set. System accounts are retained, and unnecessary members have been removed."
} catch {
    Write-Output "An error occurred while setting or updating NTFS permissions: $_"
}
Enter fullscreen mode Exit fullscreen mode

Do your career a big favor. Join DEV. (The website you're on right now)

It takes one minute, it's free, and is worth it for your career.

Get started

Community matters

Top comments (0)

👋 Kindness is contagious

Dive into an ocean of knowledge with this thought-provoking post, revered deeply within the supportive DEV Community. Developers of all levels are welcome to join and enhance our collective intelligence.

Saying a simple "thank you" can brighten someone's day. Share your gratitude in the comments below!

On DEV, sharing ideas eases our path and fortifies our community connections. Found this helpful? Sending a quick thanks to the author can be profoundly valued.

Okay