October 11th, 2010

Use WMI and PowerShell to Get a User’s SID

Summary: Microsoft Scripting Guy Ed Wilson shows how to use Windows PowerShell and WMI to translate a SID to a user name, or a user name to a SID.

 

Hey, Scripting Guy! Question

Hey, Scripting Guy! I do a lot of work with Active Directory Domain Services (AD DS), and quite often I need to find the security identifier (SID) of a user. I know that I can find the SID in Active Directory Users and Computers, but unfortunately, I have not found a good way to copy the SID from there. Is there a good way to obtain a user’s SID with a Windows PowerShell script? I have an old VBScript that uses WMI, but I would like to have a pure Windows PowerShell environment. Can you help?

— AW

 

Hey, Scripting Guy! Answer

Hello AW,

Microsoft Scripting Guy Ed Wilson here, it has been a really interesting day today. I had a three-hour conference call this morning, spent some time on Twitter and the Official Scripting Guys Forum, and then jumped into the scripter@microsoft.com inbox. There was the usual spam, and then I ran across your email.

There is an old Hey, Scripting Guy! post that illustrates using WMI and VBScript to obtain a user’s SID. The article is called How Can I Determine the SID for a User Account? That article uses WMI to obtain the SID and to resolve the SID. The two scripts listed in the article could each be boiled down to a single-line command. This is shown here:

PS C:\> [wmi]"win32_userAccount.Domain=’nwtraders’,Name=’testuser’"

AccountType : 512
Caption     : nwtraders\testuser
Domain      : nwtraders
SID         : S-1-5-21-3746122405-834892460-3960030898-1217
FullName    : test user
Name        : testuser

PS C:\>

If you wanted to return only the SID and nothing else, place the command inside a set of parentheses, and retrieve only the SID property. This is shown here (of course, you will need to replace the domain name and the user name for your environment):

PS C:\> ([wmi]"win32_userAccount.Domain=’nwtraders’,Name=’testuser’").sid
S-1-5-21-3746122405-834892460-3960030898-1217
PS C:\>

Going the other way is just as easy. You have to use the Win32_Sid WMI class, as shown here:

PS C:\> [wmi]"win32_SID.SID=’S-1-5-21-3746122405-834892460-3960030898-1217’"

__GENUS              : 2
__CLASS              : Win32_SID
__SUPERCLASS         :
__DYNASTY            : Win32_SID
__RELPATH            : Win32_SID.SID="S-1-5-21-3746122405-834892460-3960030898-1217"
__PROPERTY_COUNT     : 5
__DERIVATION         : {}
__SERVER             : MRED1
__NAMESPACE          : root\cimv2
__PATH               : \\MRED1\root\cimv2:Win32_SID.SID="S-1-5-21-3746122405-8348924
                       60-3960030898-1217"
AccountName          : testuser
BinaryRepresentation : {1, 5, 0, 0…}
ReferencedDomainName : NWTRADERS
SID                  : S-1-5-21-3746122405-834892460-3960030898-1217
SidLength            : 28

To retrieve only the user’s account name, retrieve the AccountName property by using the same technique that was used to get the user’s SID in the first place. This is shown here. (Keep in mind this is a single-line command. Of course, this user does not exist on your system. You will need to use a user’s SID that does exist in your domain.)

PS C:\> ([wmi]"win32_SID.SID=’S-1-5-21-3746122405-834892460-3960030898-1217’").Accoun
tName
testuser
PS C:\>

When using the WMI type accelerator, the syntax uses the path property that is shown in the __Path system property. For example, the __Path system property from the query to Win32_Sid is shown here:

__PATH     &#1 60;         : \\MRED1\root\cimv2:Win32_SID.SID="S-1-5-21-3746122405-8348924
                       60-3960030898-1217"

It comprises several elements. The first includes the computer name, the WMI namespace, the WMI class, the key property of the WMI class, and the specific value for the WMI class. This is shown in the following table.

Computer

Namespace

Class

Key

Value

Mred1

Root\cimv2

Win32_SID

SID

S-1-5-21-3746122405-834892460-3960030898-1217

WMI key properties can be obtained by using the WMI test tool (WBEMTEST). One way to do this is to click the Instances button shown in the following image.

Image of clicking Instances button to obtain WMI key properties

When the Instances button is clicked, all users will fill the dialog shown in the following image. In a large domain or even in a medium sized domain, this is not recommended.

Image of results of clicking Instances button

The Win32_useraccount WMI class uses a compound key. This key comprises two properties: domain and name. When connecting to a specific user account, both the domain name and the user name must be supplied.

Instead of using the [WMI] type accelerator, the Get-WmiObject cmdlet can be used. However, in a large domain, such a query would take an extended amount of time to complete. In addition, if there were more than one domain, there could be more than one user returned. This is shown here:

PS C:\> Get-WmiObject win32_useraccount -Filter "name = ‘testuser’"

AccountType : 512
Caption     : NWTRADERS\testuser
Domain      : NWTRADERS
SID         : S-1-5-21-3746122405-834892460-3960030898-1217
FullName    : test user
Name        : testuser

PS C:\>

Of course, a compound query can be used, and this would take advantage of the compound key. The command is shown here. Keep in mind that this command can be typed on a single line. I have used the line continuation (backtick character) due to line length limitations on the blog:

PS C:\> Get-WmiObject win32_useraccount `
-Filter "name = ‘testuser’ AND domain = ‘nwtraders’"

AccountType : 512
Caption     : NWTRADERS\testuser
Domain      : NWTRADERS
SID         : S-1-5-21-3746122405-834892460-3960030898-1217
FullName    : test user
Name        : testuser

PS C:\>

I can test the efficiency of a command, by using the Measure-Command cmdlet. On my computer and on my network it takes 167 milliseconds to complete the command:

PS C:\> measure-command {Get-WmiObject win32_useraccount `
>> -Filter "name = ‘testuser’ AND domain = ‘nwtraders’"}
>>

Days              : 0
Hours       &#16 0;     : 0
Minutes           : 0
Seconds           : 0
Milliseconds      : 167
Ticks             : 1676152
TotalDays         : 1.93999074074074E-06
TotalHours        : 4.65597777777778E-05
TotalMinutes      : 0.00279358666666667
TotalSeconds      : 0.1676152
TotalMilliseconds : 167.6152

PS C:\>

I rebooted my computer to ensure that no information is cached, and then tested the [WMI] type accelerator. The results were surprising and almost exactly the same—167 milliseconds. (The difference is in the number of ticks. This is a good time to point out that you should be skeptical of the results of Measure-Command because millisecond measurements are not likely to be accurate.)

PS C:\> measure-command {[wmi]"win32_userAccount.Domain=’nwtraders’,Name=’testuser’"}

Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 0
Milliseconds      : 167
Ticks             : 1678489
TotalDays         : 1.94269560185185E-06
TotalHours        : 4.66246944444444E-05
TotalMinutes      : 0.00279748166666667
TotalSeconds      : 0.1678489
TotalMilliseconds : 167.8489

PS C:\>

Two WMI functions can be created that use these WMI classes. Because the test I ran told me that the Get-WmiObject command and the [WMI] type accelerator returned the same information in the same amount of time, I decided to incorporate the Get-WmiObject cmdlet into my script. The code is easier to read. The UserSidWMIFunctions.ps1 does this. The comment-based help increases the length of the script, but will allow you to use Get-Help to get help in using and understanding the functions. I used my Add-Help function that I have added to my Windows PowerShell ISE profile to quickly add the help code. The complete UserSidWMIFunctions.ps1 script is shown here.

UserSidWMIFunctions.ps1

Function Get-UserSidFromWMI
{
  <#
   .Synopsis
    This function translates a user name into a SID
   .Description
    This function accepts a user name, and returns the SID
   .Example
    Get-UserSidFromWMI -domain nwtraders -name testuser
    returns SID for nwtraders\testuser. Returned SID will look like:
    S-1-5-21-3746122405-834892460-3960030898-1217
   .Notes
    NAME:  Get-UserSidFromWMI
    AUTHOR: ed wilson, msft
    LASTEDIT: 10/05/2010 14:59:37
    KEYWORDS: Active Directory, User Accounts
    HSG: HSG-10-11-10
   .Link
     Http://www.ScriptingGuys.com
 #Requires -Version 2.0
 #>
 Param(
  [string]$domain,
  [string]$name
 )
 (Get-WmiObject -Class Win32_UserAccount `
   -Filter "Domain = ‘$domain’ AND Name = ‘$name’").SID
} #end function Get-UserSidFromWMI

Function Get-UserNameFromWMI
{
  <#
   .Synopsis
    This function returns a user name when given a SID
   .Description
    This function returns a user name when provided a SID
   .Example
    Get-UserNameFromWMI -sid "S-1-5-21-3746122405-834892460-3960030898-1217"
    returns doma in and user name as shown here:  NWTRADERS\testuser
   .Parameter SID
    The users SID
   .Notes
    NAME:  Get-UserNameFromWMI
    AUTHOR: ed wilson, msft
    LASTEDIT: 10/05/2010 14:59:45
    KEYWORDS: Active Directory, User Accounts
    HSG: HSG-10-11-10
   .Link
     Http://www.ScriptingGuys.com
 #Requires -Version 2.0
 #>
 Param(
  [string]$sid
 )
 $wmi = (Get-WmiObject -Class Win32_UserAccount -Filter "Sid = ‘$sid’")
 "{0}\{1}" -f $wmi.domain, $wmi.name
} #end function Get-UserNameFromWMI

To use these functions you can dot-source the UserSidWMIFunctions.ps1, add them to your profile, or add them to a module. In addition, you can simply run the script inside your Windows PowerShell ISE. After the script has run, the two functions will be available for use. I then typed the commands in the Windows PowerShell ISE execution pane. The results appeared in the output pane. This is shown in the following image.

Image of results in output pane

If you were to use this script as a “script” and not a container for two functions, you would probably want to add command-line parameters to the script itself, and a little bit of code that would decide which function to call. Here is the way I would make these changes.

UserSidWMIScript.ps1

Param(
 [string]$domain,
 [string]$name,
 [string]$sid
)
Function Get-UserSidFromWMI
{
  <#
   .Synopsis
    This function translates a user name into a SID
   .Description
    This function accepts a user name, and returns the SID
   .Example
    Get-UserSidFromWMI -domain nwtraders -name testuser
    returns SID for nwtraders\testuser. Returned SID will look like:
    S-1-5-21-3746122405-834892460-3960030898-1217
   .Notes
    NAME:  Get-UserSidFromWMI
    AUTHOR: ed wilson, msft
    LASTEDIT: 10/05/2010 14:59:37
    KEYWORDS: Active Directory, User Accounts
    HSG: HSG-10-11-10
   .Link
     Http://www.ScriptingGuys.com
 #Requires -Version 2.0
 #>
 Param(
  [string]$domain,
  [string]$name
 )
 (Get-WmiObject -Class Win32_UserAccount `
   -Filter "Domain = ‘$domain’ AND Name = ‘$name’").SID
} #end function Get-UserSidFromWMI

Function Get-UserNameFromWMI
{
  <#
   .Synopsis
    This function returns a user name when given a SID
   .Description
    This function returns a user name when provided a SID
   .Example
    Get-UserNameFromWMI -sid "S-1-5-21-3746122405-834892460-3960030898-1217"
    returns domain and user name as shown here:  NWTRADERS\testuser
   .Parameter SID
    The users SID
   .Notes
    NAME:  Get-UserNameFromWMI
    AUTHOR: ed wilson, msft
    LASTEDIT: 10/05/2010 14:59:45
    KEYWORDS: Active Directory, User Accounts
    HSG: HSG-10-11-10
   .Link
     Http://www.ScriptingGuys.com
 #Requires -Version 2.0
 #>
 Param(
  [string]$sid
 )
 $wmi = (Get-WmiObject -Class Win32_UserAccount -Filter "Sid = ‘$sid’")
 "{0}\{1}" -f $wmi.domain, $wmi.name
} #end function Get-UserNameFromWMI

# *** Entry Point To Script ***

if($sid) { Get-UserNameFromWMI -sid $sid ; exit }
if($domain -AND $name) { Get-UserSidFromWMI -domain $domain -name $name ; exit }

When I test the script, the following output is displayed:

PS C:\> C:\fso\UserSidWMIScript.ps1
PS C:\> C:\fso\UserSidWMIScript.ps1 -domain nwtraders -name testuser
S-1-5-21-3746122405-834892460-3960030898-1217
PS C:\> C:\fso\UserSidWMIScript.ps1 -sid "S-1-5-21-3746122405-834892460-3960030898-1217"
NWTRADERS\testuser
PS C:\>

AW, that is all there is to using WMI to translate a user name to a SID, and vice versa. Active Directory Week will continue tomorrow when we will talk about using .NET Framework methods to perform this conversion.

We invite you to follow us on Twitter and Facebook. If you have any questions, send email to us at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

 

Ed Wilson and Craig Liebendorfer, Scripting Guys

Author

0 comments

Discussion are closed.