In the world of Windows, an account SID can be a very enigmatic thing. Who is S-1-5-21-2250542124-3280448597-2353175939-1019? Fortunately, many applications, such as the event log viewer resolve the SID to an account name. The downside, is that when you are accessing that same type of information from PowerShell, you end up with the "raw' SID. And while there are a variety of command line tools, and probably even some cool .NET trick someone will share after I post this, you most likely want to find a PowerShell solution.
ManageEngine ADManager Plus - Download Free Trial
Exclusive offer on ADManager Plus for US and UK regions. Claim now!
Your initial assumption might be to use WMI. Searching Root\CIMv2 you'll even find a Win32_SID class. Woohoo! This is all I need to do:
get-wmiobject win32_sid -filter "sid='S-1-5-21-2250542124-3280448597-2353175939-1019'"
Well, no. As you can see in the figure, you can't query this particular class.
Instead, you need to directly access the instance of the Win32_SID class. In PowerShell, the easy way is to use the [WMI] type accelerator, and specify the path to the instance.
[WMI]"root\cimv2:win32_sid.sid='S-1-5-21-2250542124-3280448597-2353175939-1019'"
If you wanted to query the SID on a remote computer, the path would be \\SERVERNAME\root\cimv2:CLASSNAME.Keyproperty='Something'. But be aware that there is no way to specify alternate credentials using [WMI]. Although, you could query the Win32_Account class for the SID.
PS Scripts:\> get-wmiobject win32_account -filter "sid='S-1-5-21-2250542124-3280448597-2353175939-1019'" AccountType : 512 Caption : Serenity\localadmin Domain : Serenity SID : S-1-5-21-2250542124-3280448597-2353175939-1019 FullName : Name : localadmin
This gives you the benefit of using a cmdlet, querying a remote computer and using alternate credentials.
In PowerShell 3.0 if you want to use the new CIM cmdlets and query WMI over WSMan, it is pretty easy to turn the previous command into a CIM command.
PS Scripts:\> get-ciminstance win32_account -filter "sid='S-1-5-21-2250542124-3280448597-2353175939-1019'" Name Caption AccountType SID Domain ---- ------- ----------- --- ------ localadmin Serenity\localadmin 512 S-1-5-21-22505421... Serenity
These queries are pretty good, but I believe that if you can go straight to the instance, so much the better. Unfortunately, I can't find any CIM related accelerator that would give me the same result as using the [WMI] accelerator. Remember, my goal is to leverage the new WSMan protocol. The solution is to use the Get-WSManInstance cmdlet.
Get-WSManInstance -ResourceURI "wmicimv2/Win32_SID" -SelectorSet @{SID="S-1-5-21-2250542124-3280448597-2353175939-1019"}
I think you can tell that the ResourceUri is the path to the class and the SelectorSet is a hashtable with key property, in this case SID, and the corresponding value. The result looks a little different, but the critical parts, like the account name are there.
Get-WSManInstance also supports alternate credentials. So given all of this, I put together a function called Resolve-SID that uses this approach. But as a fallback, you can also tell it to use WMI.
Function Resolve-SID { <# .Synopsis Resolve account name from SID. .Description This command uses the WSMAN protocol to query WMI and resolve an account based on its SID. Using WMI it was possible to run a command like this: [wmi]$user="\\SERVER01\root\cimv2:Win32_SID.Sid='S-1-5-18'" But this relies on WMI and DCOM. This command uses a CIM-cmdlet approach that queries WMI over the WSMAN protocol. If the SID can't be resolved to a user name an exception will be thrown. If you want to revert back to the WMI and DCOM approach, use the -UseWMI parameter. However, you will not be able to use alternate credentials. .Parameter SID It is assumed the SID will start with S- and you must enter a complete SID. Wildcards are not allowed. .Parameter Computername The name of the computer to query. The default is the localhost. The parameter has an alias of CN. .Parameter UseWMI Revert to the legacy [WMI] command. This parameter has an alias of WMI. .Parameter Credential This parameter as an alias of RunAs. Specify either a username or a PSCredential object. .Notes Last Updated: October 15, 2013 Version : 1.0 Learn more: PowerShell in Depth: An Administrator's Guide (http://www.manning.com/jones2/) PowerShell Deep Dives (http://manning.com/hicks/) Learn PowerShell 3 in a Month of Lunches (http://manning.com/jones3/) Learn PowerShell Toolmaking in a Month of Lunches (http://manning.com/jones4/) PowerShell and WMI (http://www.manning.com/siddaway2/) **************************************************************** * DO NOT USE IN A PRODUCTION ENVIRONMENT UNTIL YOU HAVE TESTED * * THOROUGHLY IN A LAB ENVIRONMENT. USE AT YOUR OWN RISK. IF * * YOU DO NOT UNDERSTAND WHAT THIS SCRIPT DOES OR HOW IT WORKS, * * DO NOT USE IT OUTSIDE OF A SECURE, TEST SETTING. * **************************************************************** .Example PS C:\> resolve-sid S-1-5-18 Name : NT AUTHORITY\SYSTEM AccountName : SYSTEM ReferencedDomainName : NT AUTHORITY SID : S-1-5-18 Computername : WIN8-LAP .Example PS C:\> resolve-sid S-1-5-21-1199145963-1667969739-787794555-1011 -Computername chi-win8-01 -Credential globomantics\administrator Name : CHI-WIN8-01\localadmin AccountName : localadmin ReferencedDomainName : CHI-WIN8-01 SID : S-1-5-21-1199145963-1667969739-787794555-1011 Computername : CHI-WIN8-01 .Example PS C:\> resolve-sid S-1-5-18 -verbose -computername jdhit-dc01 -UseWMI VERBOSE: Starting Resolve-SID VERBOSE: Resolving SID S-1-5-18 on jdhit-dc01 VERBOSE: Reverting back to WMI VERBOSE: \\jdhit-dc01\root\cimv2:Win32_SID.SID='S-1-5-18' VERBOSE: Associated account found Name : NT AUTHORITY\SYSTEM Accountname : SYSTEM ReferencedDomainName : NT AUTHORITY SID : S-1-5-18 Computername : JDHIT-DC01 VERBOSE: Ending Resolve-SID .Link Get-WSManInstance Get-CIMInstance .Link https://jdhitsolutions.com/blog/2013/10/resolving-sids-with-wmi-wsman-and-powershell .Inputs Strings .Outputs A custom object #> [cmdletbinding(DefaultParameterSetName="CIM")] Param( [Parameter(Position=0,Mandatory=$True,HelpMessage="Enter a SID", ValueFromPipeline,ValueFromPipelineByPropertyName)] [ValidatePattern("^S-")] [string]$SID, [Parameter(ValueFromPipelineByPropertyName)] [Alias("CN","PSComputername")] [ValidateNotNullorEmpty()] [string]$Computername=$env:computername, [Alias("RunAs")] [Parameter(ParameterSetName="CIM")] [System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty, [Parameter(ParameterSetName="WMI")] [Alias("wmi")] [switch]$UseWMI ) Begin { Write-Verbose -Message "Starting $($MyInvocation.Mycommand)" } #begin Process { Write-Verbose "Resolving SID $SID on $Computername" #build a hashtable of paramters to splat to Get-WSManInstance $paramHash=@{ ErrorAction="Stop" ErrorVariable="MyError" ResourceURI="wmicimv2/win32_SID" SelectorSet=@{SID="$SID"} Computername=$Computername } If ($Credential.username) { Write-Verbose "Adding alternate credential for $($Credential.username)" $paramHash.Add("Credential",$Credential) } Try { #if UseWMI, use Get-WMIObject if ($UseWMI) { Write-Verbose "Reverting back to WMI" Write-Verbose "\\$computername\root\cimv2:Win32_SID.SID='$SID'" [WMI]$Result = "\\$computername\root\cimv2:Win32_SID.SID='$SID'" } else { $result = Get-WSManInstance @paramhash } } Catch { Write-Warning "Get-WSManInstance failed to retrieve SID from $($Computername.ToUpper())" Write-Warning $myError.ErrorRecord #bail out Return } <# if there is no account name then the SID was not resolved, but there was no error. The query will still succeed and write an object to the pipeline but it won't have any useful information. Only write the result to the pipeline if there is an associated account, otherwise an exception will be thrown. #> if ($result.AccountName) { Write-Verbose "Associated account found" $result | Select @{Name="Name";Expression={"$($_.ReferencedDomainName)\$($_.AccountName)"}}, Accountname,ReferencedDomainName,SID, @{Name="Computername";Expression={$Computername.ToUpper()}} } else { Write-Verbose "Failed to resolve SID. This is the result" Write-Verbose $($Result | Out-String) Throw "Failed to resolve SID $SID on $($Computername.ToUpper())" } } #process End { Write-Verbose -Message "Ending $($MyInvocation.Mycommand)" } #end } #close function Resolve-SID
I think between the comment based help, internal comments and verbose messages you should be able to understand how the function works. So now you have a variety of techniques for resolving SIDs. Querying locally, using [WMI] or querying the Win32_Account class for the SID should be sufficient performance-wise. But remotely, using [WMI] or Get-WSManInstance is significantly faster than querying and filtering. Using Get-WMIboject or Get-CIMInstance took between 600-750ms, where as the [WMI]approach took about 200MS and using Get-WSManInstance took 150MS.
I hope you are resolved to not let SIDS stand in your way any longer.
Jeff – Thanks. Very useful.
I’m sure there’s a very dev-centric solution to this but I try to stick to things an IT Pro would use or be able to figure out on their own.
It does what it needs to do efficiently. No need for more.
I spent some time looking at converting SIDS and didn’t think to try the [WMI] accelerator. I used the security classes. It was harder.
Thank you very much there is now an additional way I could resolve SIDs.
I used Dot.Net
System.Security.Principal.SecurityIdentifier
System.Security.Principal.NTAccount
and I am asking if there is a way to use them remotly (except PSRemoting)?
this is the function I used to resolve SIDs
function Get-NamefromSID ($sid)
{
$objSID = New-Object System.Security.Principal.SecurityIdentifier($sid)
try {
$objUser = $objSID.Translate( [System.Security.Principal.NTAccount])
$objUser.Value
} catch { $sid }
}
Thanks. I knew there was a .NET solution. Although this is limited to the local machine unless you use PowerShell remoting. I know even some IT Pros with .NET experience would opt for something like this. It works so you can’t really complain.
Good article, Jeff.