The PowerShell script discussed in this article will help you in querying for information about Windows user profiles on remote computers. The script returns user name, profile type, and in-use status of Windows profiles.

I will show you how to query Windows user profile information for a remote computer using WMI and PowerShell. Since Windows Vista SP1, Microsoft has offered a WMI class named Win32_UserProfile to facilitate querying profile information for a remote computer. There are other ways to accomplish this task, such as listing the directories in the c:\users folder, but these methods are not efficient and might fail in cases where user profiles are stored in different drives or directories.

Querying using the Win32_UserProfile class will return a list of profile objects. These objects contain information such as the SID of the user to whom the profile belongs and the type of profile. Because the SID is not in a human-friendly format for identifying the user names, the script tries to convert the SID to a user name. This conversion will work for both domain and local user accounts when run locally on the computer. The SID-to-name conversion fails when you query profile information for local user accounts remotely. In such cases, you will see the SID number instead of a user name in the script output.

Another thing the script does is translate the profile type. The profile objects returned by the WMI query contain an attribute called Status. Per the Technet page, this attribute should contain values of 0, 1, 2, and 3. But what people have noticed is that the attribute stores values of 0, 1, 2, 4, and 8, where 1 = Temporary, 2 = Roaming, 4 = Mandatory, and 8 = Corrupted. The script output contains this profile type information as well.

Look at the PowerShell script below to understand how SID translation and profile type determination is done using PowerShell.

[cmdletbinding()]
param (
[parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
[string[]]$ComputerName = $env:computername
)            

foreach ($Computer in $ComputerName) {
 $Profiles = Get-WmiObject -Class Win32_UserProfile -Computer $Computer -ea 0
 foreach ($profile in $profiles) {
  try {
	  $objSID = New-Object System.Security.Principal.SecurityIdentifier($profile.sid)
	  $objuser = $objsid.Translate([System.Security.Principal.NTAccount])
	  $objusername = $objuser.value
  } catch {
		$objusername = $profile.sid
  }
  switch($profile.status){
   1 { $profileType="Temporary" }
   2 { $profileType="Roaming" }
   4 { $profileType="Mandatory" }
   8 { $profileType="Corrupted" }
   default { $profileType = "LOCAL" }
  }
  $User = $objUser.Value
  $ProfileLastUseTime = ([WMI]"").Converttodatetime($profile.lastusetime)
  $OutputObj = New-Object -TypeName PSobject
  $OutputObj | Add-Member -MemberType NoteProperty -Name ComputerName -Value $Computer.toUpper()
  $OutputObj | Add-Member -MemberType NoteProperty -Name ProfileName -Value $objusername
  $OutputObj | Add-Member -MemberType NoteProperty -Name ProfilePath -Value $profile.localpath
  $OutputObj | Add-Member -MemberType NoteProperty -Name ProfileType -Value $ProfileType
  $OutputObj | Add-Member -MemberType NoteProperty -Name IsinUse -Value $profile.loaded
  $OutputObj | Add-Member -MemberType NoteProperty -Name IsSystemAccount -Value $profile.special
  $OutputObj
  
 }
}

The script also gives you information such as whether the profile is in use or not and if the profile belongs to a system account such as SYSTEM. In addition, the script indicates whether the profile is in use or not.

Sample usage and output:

Query profile information on a local computer:

.\Get-WindowsProfiles.ps1

Query profile information for a remote computer:

.\Get-WindowsProfiles.ps1 -ComputerName SRVTIB1

Query profile information for multiple remote computers:

.\Get-WindowsProfiles.ps1 -ComputerName (get-content c:\temp\servers.txt)

Get user profile information with PowerShell

Get user profile information with PowerShell

38 Comments
  1. Björn 10 years ago

    Hi,
    Found an error on line 17 in your code.

    should be:
    switch($profile.status)

  2. Björn, thanks! The line is now correct.

  3. MIKEs84 9 years ago

    It’s very nice script, thank you for this.

    As I am not familiar to scripting in PowerShell I will appreciate, if anyone could modify script to add some more feature which is usable for audit purposes.
    In my environment, we need to review accounts and also check if disabled or locked out.

    Could you, please, add 2 more columns into script to verify if account is disabled or locked out – on DC as well as on local computer ?

    Thanks for your effort. Appreciate it!

    David

  4. Rebecca Stanke 8 years ago

    awesome script, can you add each users folder size?

  5. kevin 8 years ago

    AWESOME !!!! just what im looking for! thank you 1000x !!!

  6. Rupa 6 years ago

    Hi,

    Can u send a script to delete those user profiles which is not used from more than 30 days excluding service, admin and network profiles.

  7. Josh 6 years ago

    I’d also like to have sizes included in the results.  Great job though.  Very useful for the environment I just came into.  Profile cleanups had never occurred before and this with the size should help me justify accomplishing it.

  8. Paul C 6 years ago

    Hi there

    Will it show temporary profiles by default if found?

    Thank you

    Paul

  9. Ravi D 5 years ago

    I am not able to run this file. I copied code in to notepad and saved as a Ps1 . but when i am trying to run it using powershell, i am getting multiple error where in it is working on powershell ISE. can you please help?

    • What error messages do you get?

      • Ravi D 5 years ago

        I saved above script in notepad and given name as “WindowsProfiles.ps1”

        If i try to type Get-WindowsProfiles.ps1 then it gives me below error

        PS D:\> Get-WindowsProfiles.ps1
        Get-WindowsProfiles.ps1 : The term ‘Get-WindowsProfiles.ps1’ is not recognized as the name of a cmdlet, function,
        script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path
        correct and try again.
        At line:1 char:1
        + Get-WindowsProfiles.ps1
        + ~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo : ObjectNotFound: (Get-WindowsProfiles.ps1:String) [], CommandNotFoundException
        + FullyQualifiedErrorId : CommandNotFoundException

        if i run script without “Get-” then it gives me result

        but my main question is how i can run same script for multiple servers

      • D (Rank 1) 5 years ago

        I saved above script in notepad and given name as “WindowsProfiles.ps1”

        If i try to type Get-WindowsProfiles.ps1 then it gives me below error

        PS D:\> Get-WindowsProfiles.ps1
        Get-WindowsProfiles.ps1 : The term ‘Get-WindowsProfiles.ps1’ is not recognized as the name of a cmdlet, function,
        script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path
        correct and try again.
        At line:1 char:1
        + Get-WindowsProfiles.ps1
        + ~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo : ObjectNotFound: (Get-WindowsProfiles.ps1:String) [], CommandNotFoundException
        + FullyQualifiedErrorId : CommandNotFoundException

        if i run script without “Get-” then it gives me result

        but my main question is how i can run same script for multiple servers

        • Clayton (Rank 2) 5 years ago

          Since you are saving the file as “WindowsProfiles.ps1” that will be what you need to call in the console. in the example above, the author saved the file as “Get-WindowsProfiles.ps1”. That is why you are getting the error you stated.

          avataravatar
          • Ravi d 5 years ago

            Thank you so much for guidance ….I thought he used get command

  10. Joseph 5 years ago

    How do we add this line to get the remote computers? I’m a bit of a Powershell noob and could use some assistance. 🙂

    \Get-WindowsProfiles.ps1 -ComputerName (get-content c:\temp\servers.txt)

  11. Joseph 5 years ago

    I figured it out. I made a ps1 file for the program itself, a text servers list and then used the code for executing it and everything works perfect.

    I have another question though. Is there a way to get all this information output in to an Excel spreadsheet or at least a notepad for easy searching and access?

    Thank you very much! 🙂

  12. Aidan (Rank 1) 5 years ago

    Hi,

    Apologies just a novice at this stage using powershell. Love your scrip exactly what I needed for my program I am working on.

    With your script is there away to output the output to an array that I can add not a list box (Win Gui)

    Thanks heaps

     

    • Aidan, there is no need to apologize for being a newbie. 🙂 The script’s output is not a GUI, the output is an object and you can use the object’s properties in your script. Objects are much easier to manage than arrays. If you want to work with PowerShell, I highly recommend to learn how to use classes and objects.

      avatar
  13. Robert 5 years ago

    Script works great. I had one question though.  Is there a way i can get the output of the command to go into a CSV with each output object is its own colum?

    Thanks

  14. Robert 5 years ago

    Nevermind my question above, i figured it out.

    I was able to export and have it format as i wanted by assigning the output a variable and then outputting the variable to csv:

    $results = .\Get-WindowsProfiles.ps1 -ComputerName (get-content c:\temp\test.txt)
    $results | export-csv -Path C:\temp\test.csv

  15. Nandeesha 5 years ago

    its not working for 2003 servers. what need to modify ?

  16. It won’t work with 2003.  The WMI class didn’t exist in Win2k3.  :-\

    David F.

  17. John B. 5 years ago

    When I try to run the script for a remote computer by adding the ComputerName variable, nothing happens. It works fine on the local machine.

    There’s no error, no output in powershell at all. I’m attempting to query other computers on our domain.

    • Maybe the Windows Firewall blocks the connection. Check if you can access the remote machine with the Computer Management tool.

      • John B. 5 years ago

        Thank you. I tried using the Computer Management tool as you suggested and cannot connect that way either. I think you’re right in that it is being blocked by our company antivirus.

        That solves the mystery, thanks again!

  18. warren klaus 4 years ago

    Great script!

    when i run it against a list of computers, it does nothing.   if i run against individual computers, it works.   

    Also, how can i exclude the system profiles.

     

    thanx again.  

  19. This is a tangent question, but this script is the perfect source for it.. 

    Given this section:

     switch($profile.status){
       1 { $profileType="Temporary" }
       2 { $profileType="Roaming" }
       4 { $profileType="Mandatory" }
       8 { $profileType="Corrupted" }
       default { $profileType = "LOCAL" }
      }
      $User = $objUser.Value
      $ProfileLastUseTime = ([WMI]"").Converttodatetime($profile.lastusetime)
      $OutputObj = New-Object -TypeName PSobject
      $OutputObj | Add-Member -MemberType NoteProperty -Name ComputerName -Value $Computer.toUpper()
      $OutputObj | Add-Member -MemberType NoteProperty -Name ProfileName -Value $objusername
      $OutputObj | Add-Member -MemberType NoteProperty -Name ProfilePath -Value $profile.localpath
      $OutputObj | Add-Member -MemberType NoteProperty -Name ProfileType -Value $ProfileType
      $OutputObj | Add-Member -MemberType NoteProperty -Name IsinUse -Value $profile.loaded
      $OutputObj | Add-Member -MemberType NoteProperty -Name IsSystemAccount -Value $profile.special
      $OutputObj

    Would this perform better with  hashtables?

    $ProfileTypes = @{
        0 = 'Healthy'
        1 = 'Temporary'
        2 = 'Roaming'
        4 = 'Mandatory'
        8 = 'Corrupted'
    }
    
    $UserProperties = @{
        ProfileType = $ProfileTypes.Item($Profile.Status)
        LastUseTime = ($Profile.ConvertToDateTime($Profile.LastUseTime)).ToString()
        ProfilePath = $Profile.LocalPath
        IsInUse = $Profile.Loaded
        IsSystemAccount = $Profile.Special
        ComputerName = $Profile.__Server.ToUpper()
    }
    $OutputObj = New-Object -Type PSCustomObject -Properties $UserProperties 

    David F. 

  20. Warren Klaus:

    I can't help you with why you aren't getting remote results, I can help you with the Special Profiles..

    Change this line:

    $Profiles = Get-WmiObject -Class Win32_UserProfile -Computer $Computer -ea 0

    to this: 

    $Profiles = Get-WmiObject -Class Win32_UserProfile -Computer $Computer -Filter 'Special = False' -ea SilentlyContinue

    David F.

    avatar
  21. Jon 4 years ago

    Is it possible to list the date when each profile was created?

    I need a script that will identify profiles that were created after a certain date so that I can then remove them.

    (For OS rollback)

  22. dc 3 years ago

    I’m fairly new with powershell. I’m an old Winbatch person with a lot of WiseScript usage. I came across this thread looking for something similar to all of you and like many of you, I find things on various sites and modify those items to fit my needs.
    I could be wrong but it seems like an awful lot of code that I’ve found relies on the registry SID or some convoluted WMI stuff. So, I share with you something I created with the help of this site and various other sites.

    I like simple… C:\users\

    ForEach ($line in (get-content -Path "C:\temp\powershell\servers.txt")){
     $Computername = get-childitem \\$Line\c$\users |SELECT Root, LastAccessTime, Name  |Out-File C:\temp\powershell\output.txt -Append
    }

    I think if we simply find the last access dates of profiles, that will give us a fairly good indication of the last logon time. It’s probably not perfect but it’s good enough.

    Instead of “SELECT Root, LastAccessTime, Name” you can use “SELECT *” and that will give you full list of items you can include. Maybe you want them all. My script includes only what I needed.

    avatar
    • Leos Marek (Rank 4) 3 years ago

      Im not sure when this will work well. I am just logged on my server via RDP, the server is up for few days, and query like yours shows this:

      PS C:\Users> Get-ChildItem | select root,lastaccesstime,name
      
      Root LastAccessTime        Name
      ---- --------------        ----
      C:\  3/28/2020 6:39:43 PM  Administrator

      Last access time is 4 days ago.

  23. dc 3 years ago

    I read your reply and went back to my script to take a look.  I've only run it on Win10 VM machines.

    One thing I changed but didn't seem to matter when comparing two different output files was

    $Computername = get-childitem \\$Line\c$\users |SELECT Root, LastAccessTime, Name

    $Computername = get-childitem \\$Line\c$\users |SELECT "\\$Line\c$\users", LastAccessTime, Name

    This producted the same exact results except for the path.  Kinda concerned me but I thought I was pulling the path from the wrong place but it wasn't.

    Maybe change "LastAccessTime" to "LastWriteTime".  On my systems, the times for both values match.

    As a PS newbie, maybe I'm just not getting something but for me, it appears to be working.  If anyone can comment on why what I'm doing isn't working and can supply something different, I'd love to try it out.

     

     

    • Leos Marek (Rank 4) 3 years ago

      I dont think this is about Powershell, but rather how Windows works. In below example you can see that even that I am just now logged under Administrator user and I have access my Documents folder, the root folder access date shows old date:

      PS C:\Users\Administrator> Get-ChildItem | select root,lastaccesstime,name
      
      Root LastAccessTime        Name
      ---- --------------        ----
      C:\  3/11/2020 11:26:16 AM 3D Objects
      C:\  3/11/2020 11:26:16 AM Contacts
      C:\  3/23/2020 11:14:08 AM Desktop
      C:\  4/2/2020 6:38:38 PM   Documents
      C:\  3/11/2020 11:26:16 AM Downloads
      C:\  3/11/2020 11:26:16 AM Favorites
      C:\  3/11/2020 11:26:16 AM Links
      C:\  3/11/2020 11:26:16 AM Music
      C:\  3/11/2020 11:26:16 AM Pictures
      C:\  3/11/2020 11:26:16 AM Saved Games
      C:\  3/11/2020 11:26:16 AM Searches
      C:\  3/11/2020 11:26:16 AM Videos
      
      
      PS C:\Users\Administrator> cd..
      PS C:\Users> Get-ChildItem | select root,lastaccesstime,name
      
      Root LastAccessTime        Name
      ---- --------------        ----
      C:\  3/28/2020 6:39:43 PM  Administrator
      C:\  3/11/2020 11:26:16 AM Public

      What are you trying to achieve? It seems for me a little strange to pull information when someone was last time logged like this. Instead, I would user Active Directory user information. If you speak about W10 machines, those are usually used by single user.

      The profile thing would make sense to me on a RDS server with hundreds of profiles that consumes disk space.

  24. dc 3 years ago

    Just looking to all users who logged into these remote machines and at what dates.

    avatar
    • Leos Marek (Rank 4) 3 years ago

      Well in that case I guess you might use this approach. Just remember, if the user has active session (even in disconnected state) for several days, the date might be old as I showed.

  25. Sridhar 3 years ago

    Hello Sitaram,

    When I created a windows batch to fetch the information from the SQL servers and then sending the mail to mail box using powershell, I am getting report as well as company related information in the mail body.

    Could you please let me know how to suppress the unwanted messages apart from the report which is getting from windows batch files.

Leave a reply to Clayton (Rank 2) Click here to cancel the reply

Please enclose code in pre tags

Your email address will not be published. Required fields are marked *

*

© 4sysops 2006 - 2023

CONTACT US

Please ask IT administration questions in the forums. Any other messages are welcome.

Sending

Log in with your credentials

or    

Forgot your details?

Create Account