Powershell/Private/Permissions/Set-RegPermission.ps1

function Set-RegPermission {
    param (
        [Parameter(Mandatory)]
        [string]$SourceSID,
        [Parameter(Mandatory)]
        [string]$TargetSID,
        [Parameter(Mandatory)]
        [string]$FilePath,
        [Parameter(Mandatory = $false)]
        [scriptblock]$progressCallback
    )

    # Create SecurityIdentifier objects
    $SourceSIDObj = New-Object System.Security.Principal.SecurityIdentifier($SourceSID)
    $TargetSIDObj = New-Object System.Security.Principal.SecurityIdentifier($TargetSID)

    # Get NTAccount names for logging and ACLs
    $SourceAccountTranslated = $false
    $TargetAccountTranslated = $false

    try {
        $SourceAccount = $SourceSIDObj.Translate([System.Security.Principal.NTAccount]).Value
        $SourceAccountTranslated = $true
    } catch {
        Write-ToLog "Warning: Could not translate SourceSID $SourceSID to NTAccount. Using SID string instead."
        $SourceAccount = $SourceSID
    }
    try {
        $TargetAccount = $TargetSIDObj.Translate([System.Security.Principal.NTAccount]).Value
        $TargetAccountTranslated = $true
    } catch {
        Write-ToLog "Warning: Could not translate TargetSID $TargetSID to NTAccount. Using SID string instead."
        $TargetAccount = $TargetSID
    }

    # Prepare icacls-compatible account identifiers (SIDs need * prefix)
    $SourceAccountIcacls = if ($SourceAccountTranslated) { $SourceAccount } else { "*$SourceAccount" }
    $TargetAccountIcacls = if ($TargetAccountTranslated) { $TargetAccount } else { "*$TargetAccount" }

    # Add the targetAccount to the ACL if it doesn't already exist
    $acl = Get-Acl -Path $FilePath
    $targetMember = $acl.Access | Where-Object { $_.IdentityReference -eq $TargetAccount }
    if (-not $targetMember) {
        $newRule = New-Object System.Security.AccessControl.FileSystemAccessRule(
            $TargetAccount, "FullControl", "ContainerInherit,ObjectInherit", "None", "Allow"
        )
        $acl.AddAccessRule($newRule)
        Set-Acl -Path $FilePath -AclObject $acl
    }

    # Use icacls for bulk operations - much faster than PowerShell ACL cmdlets
    Write-ToLog "Starting permission migration using icacls for path: $FilePath"

    # Step 1: Grant target user full control inheritance on root folder
    Write-ToLog "Granting permissions to: $TargetAccountIcacls"
    $icaclsGrantResult = icacls $FilePath /grant "${TargetAccountIcacls}:(OI)(CI)F" /T /C /Q

    if ($LASTEXITCODE -ne 0) {
        # Only log if there are non-filtered errors
        Write-ToLog "Warning: icacls grant operation had issues. Exit code: $LASTEXITCODE"
    } else {
        Write-ToLog "Successfully granted permissions to $TargetAccountIcacls"
    }

    # Step 2: Replace source user with target user in all ACLs (preserves existing permissions)
    # Write-ToLog "Substituting $SourceAccountIcacls with $TargetAccountIcacls"
    # $icaclsSubstResult = & icacls.exe $FilePath /substitute "$SourceAccountIcacls" "$TargetAccountIcacls" /T /C /Q 2>&1
    # if ($LASTEXITCODE -ne 0) {
    # Write-ToLog "Warning: icacls substitute operation had issues. Exit code: $LASTEXITCODE"
    # Write-ToLog "icacls substitute output: $($icaclsSubstResult -join ' ')"
    # } else {
    # Write-ToLog "Successfully substituted $SourceAccountIcacls with $TargetAccountIcacls"
    # }

    # Step 3: Change ownership from source to target user
    Write-ToLog "Setting owner to $TargetAccountIcacls"
    $icaclsOwnerResult = icacls $FilePath /setowner "$TargetAccountIcacls" /T /C /Q

    if ($LASTEXITCODE -ne 0) {
        # Only log if there are non-filtered errors
        Write-ToLog "Warning: icacls setowner operation had issues. Exit code: $LASTEXITCODE"
    } else {
        Write-ToLog "Successfully set owner to $TargetAccountIcacls"
    }

    # Provide progress feedback
    if ($ProgressCallback) {
        & $ProgressCallback 100 100  # Report completion
    }

    Write-ToLog "Permission migration completed for path: $FilePath"
}