Apr 082019
 

I was on a customer case where SharePoint requests were taking significant time to complete. The issue eventually turned out to be because of a low value for MaxConcurrentAPI which in turn caused incoming SID resolution calls to pile up at the DC. This post is not about MaxConcurrentAPI, if you want to know more about MaxConcurrentAPI please read following article:
https://blogs.msdn.microsoft.com/spatdsg/2006/01/05/maxconcurrentapi-or-how-fast-can-you-authenticate-users/.

But the reason I’m writing this post is because of a script that I wrote to reproduce the SID resolution issue with a DC. At the heart of this script is the following script block….

#Script block which will be used by Powershell Jobs
$ADSidResolve = {
    param($sid)
    Write-Host "SID: $sid"
    $objSID = New-Object System.Security.Principal.SecurityIdentifier($sid)
    $objUser = $objSID.Translate( [System.Security.Principal.NTAccount])
    $objUser.Value
}

I’m using the System.Security.Principal.SecurityIdentifier.Translate API to translate a SID to username. To translate a username to its SID, use System.Security.Principal.NTAccount.Translate API instead. Here’s a sample script which accomplishes that…

#Script to translate currentuser's username to SID
$currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$currentUserName = $currentUser.User.Translate([System.Security.Principal.NTAccount])
$objSID = New-Object System.Security.Principal.NTAccount($currentUserName)
$objUser = $objSID.Translate( [System.Security.Principal.SecurityIdentifier])

Write-Host "Current user: $currentUserName, SID: $($objUser.Value)"

Here’s the full script to translate a file full of SIDs to username, I used this script to stress test DC. Note that this script executes SID resolution in parallel using PowerShell jobs so this script is a good tutorial for using PowerShell jobs as well.

When running this script please make sure you’ve got C:\Sids.txt created with all the SIDs in it. Also note that there is a better way (using System.Security.Principal.IdentityReferenceCollection.Translate) for translating these SIDs but since I am simulating a customer scenario so I had to follow this route.

#follow this script on github: https://github.com/nibubt/powershellsnips/blob/master/ADSIDResolve.ps1

$filePath = "C:\sids.txt" #make sure this file contains SIDs that needs to be resolved.

# Verify file status #
if((Test-Path $filePath) -eq $false)
{
    Write-Error -Message "File $filePath does not exist. Please correct file path before using this script. Exiting!"
    return
}

#Read in UserName and Password securely
$username = Read-Host -Prompt "Please enter username: "
$pwd = Read-Host -Prompt "Please enter your password: " -AsSecureString
$cred = New-Object System.Management.Automation.PSCredential($username,$pwdz
if($null -eq $cred)
{
    Write-Error -Message "Invalid credentials! Exiting!"
    return;
}

# Count of lines to show progress
$sidlines = Get-Content -Path $filePath | ForEach-Object{$_.Trim()}
$totalLines = ( $sidlines | Measure-Object -Line).Lines

if(0 -ge $totalLines)
{
    Write-Error -Message "File $filePath empty?! This file must contain the SIDs you're intending to resolve! Exiting!"
    return    
}


#Script block which will be used by Powershell Jobs
$ADSidResolve = {
    param($sid)
    Write-Host "SID: $sid"
    $objSID = New-Object System.Security.Principal.SecurityIdentifier($sid)
    $objUser = $objSID.Translate( [System.Security.Principal.NTAccount])
    $objUser.Value
}

$alljobs = @()
$curLine = 0
foreach($line in $sidlines)
{
    ++$curLine;
    $JobName = "PSJOB-SIDResolve-$line"    
    $alljobs += Start-Job -ScriptBlock $ADSIDResolve -Name $JobName -Credential $cred -ArgumentList $line
  
    $PercentComplete = [math]::round((($curLine/$totalLines)*100),0)
    Write-Progress -Activity "Progress resolving SID... (Current line: $curLine of $totalLines)" -Status "$PercentComplete% Complete:" -PercentComplete $PercentComplete
}

#Wait for jobs to finish, in the end print the output
Write-Host "Waiting on jobs to finish..."
Get-Job | Wait-Job | Receive-Job

Remove-Variable alljobs,sidlines

Since output of this script is confidential, I’m skipping output but note that this does work :-).

Also note that there is a SysInternals tool that does a similar job. Its called PsGetSid: https://docs.microsoft.com/en-us/sysinternals/downloads/psgetsid

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.