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! 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
Hello 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.
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.
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.
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