Use PowerShell to Find User Profiles on a Computer

Use PowerShell to Find User Profiles on a Computer

  • Comments 3
  • Likes

Summary: Learn how to use Windows PowerShell to find all user profiles on a computer, and to display the date when each profile was last used.

 

Hey, Scripting Guy! QuestionHey, Scripting Guy! I would like to find a good way to see which profiles exist on my laptop. I found a Hey, Scripting Guy! post to do this, but it uses VBScript. Can this be done using Windows PowerShell?

—CW

 

Hey, Scripting Guy! AnswerHello CW,

Microsoft Scripting Guy Ed Wilson here. A few years ago (actually more like six years ago), there was a Hey, Scripting Guy! Blog post entitled Hey, Scripting Guy! How Can I List All the User Profiles on a Computer? That post talks about enumerating a registry key to find the profile information. The registry location did not change in Windows 7, so the VBScript would still work. Here is the registry location:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList

The registry location viewed in the Registry Editor appears in the following figure.

Image of registry location

Using Windows PowerShell, it is really easy to get and to display registry keys. I can enumerate the profile keys in a single command. However, due to the length of registry keys, I am going to do it in two lines. In the code that follows, I first store the path to the registry (using the HKLM Windows PowerShell drive) in a variable. Next, I use the Get-ChildItem cmdlet (dir is an alias) to list the registry profile keys:

$profileList = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList'

dir $profilelist

The following illustrates using these two commands, as well as shows the results on my Windows 7 laptop:

PS C:\> $profileList = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList'

PS C:\> dir $profilelist

 

    Hive: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList

 

SKC                  VC Name                                              Property

  0                    5 S-1-5-18                                             {Flags, State, RefCount, Sid...}

  0                    3 S-1-5-19                                             {ProfileImagePath, Flags, State}

  0                    3 S-1-5-20                                             {ProfileImagePath, Flags, State}

  0                    10 S-1-5-21-124525095-70825963       {ProfileImagePath, Flags, State, Sid...}

  0                    7 S-1-5-21-1266540227-3509270         {ProfileImagePath, Flags, State, Sid...}

  0                    8 S-1-5-21-1266540227-3509270         {ProfileImagePath, Flags, State, Sid...}

Now that I have a listing of the profiles on the machine, I need to expand a couple of properties, such as ProfileImagePath and Sid. This should be a simple matter of using the Get-ItemProperty cmdlet to retrieve the name property from the list above. When I do this, however, an error arises. The command and associated error are shown here.

Image of command and associated error

There are actually two problems going on here. The first is that what is displayed under the Name column in the default output from the Get-Childitem cmdlet is not the actual value stored in the actual name property. The second problem is that even if that were fixed, the value HKEY_LOCAL_MACHINE that the name property contains as part of the value is not the name of the Windows PowerShell drive used by the Get-ItemProperty cmdlet. I discovered this by piping the results of the Get-ChildItem command to the Format-List cmdlet (fl is an alias for Format-List) and analyzing the output. The following figure illustrates this process.

Image of result of piping Get-ChildItem to Format-List

From the results discovered via Format-List, I ascertain I need to use the pspath property because it includes the registry provider. I can then pipe the results to the Select-Object cmdlet, and choose the ProfileImagePath property and the Sid property. This code and associated output are shown here (the % character is an alias for the Foreach-Object cmdlet and Select is an alias for the Select-Object cmdlet):

PS C:\> $profileList = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList'

PS C:\> Get-childItem 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList' | % {Get-ItemProperty $_.pspath } | Select profileImagePath, sid

 

ProfileImagePath                                                            Sid

C:\windows\system32\config\systemprofile                   {1, 1, 0, 0...}

C:\Windows\ServiceProfiles\LocalService

C:\Windows\ServiceProfiles\NetworkService

C:\Users\edwils                                                                {1, 5, 0, 0...}

C:\Users\UpdatusUser                                                     {1, 5, 0, 0...}

C:\Users\Administrator                                                    {1, 5, 0, 0...}

CW, that is an interesting excursion into working with the registry to retrieve user profile information. However, if it were me, I would just use Windows Management Instrumentation (WMI) instead. In fact, it returns even more information that is obtainable via the registry. Here is a simple command to return exactly the same information we just got from the registry (gwmi is an alias for the Get-WmiObject cmdlet):

gwmi win32_userprofile | select localpath, sid

The command and associated output are shown here:

PS C:\> gwmi win32_userprofile | select localpath, sid

localpath                                                              sid

---------                                                                ---

C:\Users\Administrator                                         S-1-5-21-1266540227-3509270964-2815946151-500

C:\Users\UpdatusUser                                           S-1-5-21-1266540227-3509270964-2815946151-1001

C:\Users\edwils                                                      S-1-5-21-124525095-708259637-1543119021-179756

C:\Windows\ServiceProfiles\NetworkService         S-1-5-20

C:\Windows\ServiceProfiles\LocalService               S-1-5-19

C:\windows\system32\config\systemprofile           S-1-5-18

If I run the WMI command with administrator rights, I can find the last time each profile was used. This information might be useful from a cleanup perspective. Here is the command to do that. Here are the aliases the following command uses:

PS C:\> Get-Alias gwmi, select, ft

 

CommandType              Name                           Definition

Alias                              gwmi                             Get-WmiObject

Alias                              select                             Select-Object

Alias                              ft                                    Format-Table

Here is the code to get the user profiles, convert the time from WMI format to a regular date time value, and display the path and SID:

gwmi win32_userprofile |

select @{LABEL="last used";EXPRESSION={$_.ConvertToDateTime($_.lastusetime)}},

LocalPath, SID | ft -a

 

CW, that is all there is to finding profiles on a computer. Join me tomorrow for more cool Windows PowerShell stuff.

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

Ed Wilson, Microsoft Scripting Guy

 

 

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • Hi Ed,

    thanks for showing again, how easy we can access the registry and retrieve WMI (here we are again :-) information with powershell.

    Though I still haven't fallen in love with the registry provider and its items and itemproperties this example is definitely simple and quite straight if you know that "PsPath" property is the key to success!

    WMI is still becoming more and more a friend of mine!!!

    And the real deal here is, apart from simplicity(!), that we have objects again!

    And as you showed here, the resulting WMI object from "gwmi Win32_Userprofile" comes along with two ready to use script methods:

    ConvertFromDateTime ScriptMethod System.Object ConvertFromDateTime();

    ConvertToDateTime   ScriptMethod System.Object ConvertToDateTime();

    OK, you might argue that it would be even better, if we won't need to do the conversion between DateTime formats ourselves because Get-WMI could already have done that for us ... but that depends on what we are going to do with the information later on and this conversion may be undesirable at times.

    Klaus.

  • @Klaus Shulte You are welcome. Yes, I intended to just show the Registry portion, and then I just could not help myself and I felt I needed to contrast it with the WMI. In this case, in my mind, the WMI class is the hands down winner. And you are correct, the two convert* methods on the WMI object are way powerful and greatly simplify working with dates. Sure, it might be better if the Get-WmiObject automatically converted the date, except for those occasions when you need to provide a cim datetype BACK to another WMI class. In those cases, WMI requires the cim type ... of course, there are converters for those as well ...

  • This is excellent - I'm looking to do this exact concept on remote machines.  Can you help with modifying this code to use a txt/csv file of machine names / IP addresses?

    Something along the lines of:

    Get-Content C:\Scripts\PCnames.txt | Foreach-Object{

    do your WMI magic

    }

    I can't seem to get it to work though....thanks!