When trying to list all profiles, you need a script that can account for a variety of scenarios.
An everyday task that many admins must perform when working with both Windows server and client operating systems is listing all user profiles. A user's profile is the usual place that every installed application, as well as Windows itself, places files and folders specific to a particular user. If an admin needs to remove, add or modify individual files for all users via a script, the usual reaction is to enumerate all of the folders in C:\Users. Since C:\Users is the folder location for most users, simply identifying each of these folders is sufficient. But what if we need to listall profiles, including those owned by SYSTEM, Network Service and other operating system-specific profiles? Or, what happens if we're working with Windows XP, and the profiles are stored in C:\Documents and Settings instead? We need a script that can account for all of these scenarios.
To create a more robust script to list user profiles, we can't simply list all of the folders in C:\Users. We need to query a location that will always point to all user profiles on a system, and for that, we need to head to the registry.
Read: Working With Windows Services In PowerShell
The registry contains a key called ProfileList located in HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion. This registry key contains one subkey for each user profile on a Windows machine. Inside of each of these subkeys is a registry value called ProfileImagePath that includes the profile path for all users.
Once we know this location, it is then a matter of figuring out how to get PowerShell to enumerate all of these values. Since PowerShell has a registry provider already built-in, we can use Get-ChildItem to enumerate all of these subkeys. You can see below that besides the standard system profiles, I just have a single Adam user on my system.
PS> Get-ChildItem 'HKLM:\Software\Microsoft\Windows NT\CurrentVersion\ProfileList'
Hive: HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\ProfileList
Name Property
---- --------
S-1-5-18 Flags : 12
ProfileImagePath : C:\Windows\system32\config\systemprofile
RefCount : 1
Sid : {1, 1, 0, 0...}
State : 0
S-1-5-19 ProfileImagePath : C:\Windows\ServiceProfiles\LocalService
Flags : 0
State : 0
S-1-5-20 ProfileImagePath : C:\Windows\ServiceProfiles\NetworkService
Flags : 0
State : 0
S-1-5-21-3385963305-808838737- ProfileImagePath : C:\Users\defaultuser0
1911667508-1000 Flags : 0
State : 0
Sid : {1, 5, 0, 0...}
ProfileAttemptedProfileDownloadTimeLow : 0
ProfileAttemptedProfileDownloadTimeHigh : 0
ProfileLoadTimeLow : 0
ProfileLoadTimeHigh : 0
RefCount : 0
RunLogonScriptSync : 0
S-1-5-21-3385963305-808838737- ProfileImagePath : C:\Users\Adam
1911667508-1002 Flags : 0
State : 0
Sid : {1, 5, 0, 0...}
ProfileAttemptedProfileDownloadTimeLow : 0
ProfileAttemptedProfileDownloadTimeHigh : 0
ProfileLoadTimeLow : 0
ProfileLoadTimeHigh : 0
RefCount : 15
RunLogonScriptSync : 0
Being able to see these profiles is a start, but we will need to use the specific folder paths in our script somewhere. For that, we only have to see the values of ProfileImagePath for each user. To return the registry value for each of the user's subkeys, we will invoke the GetValue() method on each of the registry keys to just see the user profile path.
PS> Get-ChildItem 'HKLM:\Software\Microsoft\Windows NT\CurrentVersion\ProfileList' | ForEach-Object { $_.GetValue('ProfileImagePath') }
C:\Windows\system32\config\systemprofile
C:\Windows\ServiceProfiles\LocalService
C:\Windows\ServiceProfiles\NetworkService
C:\Users\defaultuser0
C:\Users\Adam
By looping over each registry key with ForEach-Object and then calling the GetValue() method on each ProfileImagePath value, it will now return only the paths we're after. Once you've got the paths to each user profile, additional checks or codes can be performed on each folder. For example, each user's temp folder path is located in the AppData\Local\Temp folder. To enumerate every user's temp folder, you could add a command inside of your loop to list those files.
PowerShell PS> Get-ChildItem 'HKLM:\Software\Microsoft\Windows
NT\CurrentVersion\ProfileList' | ForEach-Object { $profilePath =
$_.GetValue('ProfileImagePath') Get-ChildItem -Path
"$profilePath\AppData\Local\Temp" }
Once you've got a loop that includes every user profile path, you're open to doing anything you'd like to files or folders inside of each of those paths!
Adam Bertram
Adam Bertram is a 25+ year IT veteran and an experienced online business professional. He’s a successful blogger, consultant, 6x Microsoft MVP, trainer, published author and freelance writer for dozens of publications. For how-to tech tutorials, catch up with Adam at adamtheautomator.com, connect on LinkedIn or follow him on X at @adbertram.