Resolving SIDs with WMI, WSMAN and PowerShell

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.

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:

Well, no. As you can see in the figure, you can’t query this particular class.

win32_sid-fail

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-sid

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.

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.

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.

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-1

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.

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.

Adding System Path to CIMInstance Objects

The other night when I presented for the Mississippi PowerShell Users’ Group, one of the members showed some PowerShell 3.0 code using the CIM cmdlets. At issue is how the CIM cmdlets handle the WMI system properties like __SERVER and __RELPATH. By default, those properties aren’t displayed, but they are captured in the CimSystemProperties property.

gcim-systemproperties

The problem is that the __PATH property is not captured when using Get-CIMInstance as you can see in the screen shot. This is apparently a known issue. Using Get-WMIObject still works.

Most of the time this probably isn’t a big deal. But perhaps there are situations where you need the __PATH property. I saw some code the other night that constructed the __PATH property. I was intrigued and decided to figure this out for myself. The path needs the computer name, the namespace-class path, the name of the class, the class key and the value of that key. I have most of this already from the

I switched the direction of the slash in the namespace. The only part I’m missing is the key property. But I can find it by looking at the qualifiers for the class properties. Think of a qualifier as a tag to denotes a special use. What is really cool about the CIMInstance, is that class information is included. This means I can look at the class from the object itself.

get-ciminstance-classproperties

All I need to do is find the property with a KEY qualifier.

Now that I know the Name property is the key, I can finish building my path.

And this is the exact path I would get using Get-WMIObject.

Of course, I don’t want to have to do all that typing so I created a function to do the work for me.

The main part of the function is essentially what I just demonstrated. However, there are some special cases where there is no key property or there are multiple keys, so I had to add some logic to take that into account. I wrote the function assuming you would pipe a CIMInstance object to it and you want to add __PATH.

And here is what the special cases look like.
ciminstance-addpath

I’ll be the first to admit this is a bit of a brute force hack and I can’t guarantee I’ve covered every oddball use case. So if you try this and come across a class that doesn’t give the correct __PATH, I hope you’ll let me know.

Find Files with PowerShell 3.0

My last few articles have looked at using WMI and CIM_DATAFILE class to find files, primarily using Get-WmiObject in PowerShell. But now that we have PowerShell 3.0 at our disposal, we can use the new CIM cmdlets. So I took my most recent version of Get-CIMFile and revised it specifically to use Get-CimInstance. I also took the liberty of adding a few more refinements, some of which you could integrate into previous versions. Here’s the new v3 function.

This version let’s you search remote computers by name or CIM session. Because they are mutually exclusive options, this function uses parameter sets, defaulting to using a computername. I also added a validation check on the drive name using regular expressions. The function will fail if the value is not a letter followed by a colon.

Another major change was modifying code to search for filenames without an extension. What if you are looking for a file like README? The WQL query turned out to be more complicated than I imagined. It would be easier if the extension property was NULL, but it isn’t. It is a 0 length string. I found that in order to make this work, I needed to create a query like this:

So I modified my code to adjust operators and variables that I use to build the filter string.

Now I can find files without an extension, or with.

The last major change you’ll notice is that I build a hash table of parameter values and then splat it.

This is a terrific technique when you are dynamically generating parameters.

Get-CimInstance can be used to query remote computers, assuming they are also running PowerShell 3.0. However, you can also use CIM Sessions which allow you to establish a connection to an older system using the DCOM protocol.

The end result is that I can still use my PowerShell 3.0 function to query a PowerShell 2.0 machine as long as I have a pre-created session.

get-cimfile3

Now I have a very powerful tool that can search just about any computer in my domain.

Oh, one more thing I realized in working on this. Initially I was only paying attention to the file name and version. Then I noticed that the Size property in the default output was always empty. That struck me as odd and not very useful. So I looked at the actual object with Get-Member and it turns out there is a FileSize property which is populated. It looks like the default property set for CIM_DATAFILE uses the Size property, when it should really be FileSize. So keep that in mind as you are working with the results.

Download Get-CIMFile3 and try it out for yourself.

Query Local Administrators with CIM

Yesterday I posted an article on listing members of the local administrators group with PowerShell and Get-WmiObject. PowerShell 3.0 offers an additional way using the CIM cmdlets. The CIM cmdlets query the same WMI information, except instead of using the traditional RPC/DCOM connection, these cmdlets utilize PowerShell’s remoting endpoint so they are much more firewall friendly and a little faster in my testing. I’ll be covering these cmdlets over time as PowerShell v3 is released.

The code I wrote yesterday can be re-used with only a few modifications. We can still easily get the group object.


PS C:\> Get-CimInstance Win32_Group -filter "Name='Administrators'" -computername 'Quark'

SID Name Caption Domain
--- ---- ------- ------
S-1-5-32-544 Administrators QUARK\Administrators QUARK

We can connect either by a computername or a CIMSession. We still need to get associated objects. There is a cmdlet called Get-CIMAssociatedInstance which you could use like this:


Get-CimInstance Win32_Group -filter "Name='Administrators'" -computername 'Quark' | Get-CimAssociatedInstance -ComputerName 'Quark'

However, this will return all associations and I have not been able to find a way with this cmdlet to limit results to user and group objects as I did with WMI. However, we can still use the Associators Of query.


PS C:\> $computer='Quark'
PS C:\> $query="Associators of {Win32_Group.Domain='$computer',Name='Administrators'} where Role=GroupComponent"
PS C:\> Get-CimInstance -Query $query -ComputerName $computer

Name Caption AccountType SID Domain
---- ------- ----------- --- ------
Administrator QUARK\Admini... 512 S-1-5-21-139... QUARK
Jeff QUARK\Jeff 512 S-1-5-21-139... QUARK

These objects have some slightly different property names so to tweak the output I had to make a few changes. Here’s my complete demo script.


#requires -version 3.0

#use CIM to list members of the local admin group

[cmdletbinding()]
Param([string]$computer=$env:computername)

$query="Associators of {Win32_Group.Domain='$computer',Name='Administrators'} where Role=GroupComponent"

write-verbose "Querying $computer"
write-verbose $query

Get-CIMInstance -query $query -computer $computer |
Select @{Name="Member";Expression={$_.Caption}},Disabled,LocalAccount,
@{Name="Type";Expression={([regex]"User|Group").matches($_.Class)[0].Value}},
@{Name="Computername";Expression={$_.ComputerName.ToUpper()}}

Here’s the code in action.


PS C:\> C:\scripts\Get-CIMLocalAdmin.ps1 -Verbose
VERBOSE: Querying QUARK
VERBOSE: Associators of {Win32_Group.Domain='QUARK',Name='Administrators'}
where Role=GroupComponent
VERBOSE: Perform operation 'Query CimInstances' with following parameters,
''namespaceName' = root\cimv2,'queryExpression' = Associators of
{Win32_Group.Domain='QUARK',Name='Administrators'} where
Role=GroupComponent,'queryDialect' = WQL'.

Member : QUARK\Administrator
Disabled : False
LocalAccount : True
Type : User
Computername : QUARK

Member : QUARK\Jeff
Disabled : False
LocalAccount : True
Type : User
Computername : QUARK

It wouldn’t be much to turn this into a function, although all computers will need to be running Powershell 3.0. Download Get-CIMLocalAdmin and try it out for yourself.

Disclaimer: All of this is based on pre-released software. No guarantees that it will work when PowerShell 3.0 officially ships.