Weekend Scripter: Use PowerShell to Generate a Recent Profile Report

Weekend Scripter: Use PowerShell to Generate a Recent Profile Report

  • Comments 3
  • Likes

Summary: Guest blogger, Bob Stevens, talks about using Windows PowerShell to generate a recent profile report.

Microsoft Scripting Guy, Ed Wilson, is here. Today I am pleased to welcome back guest blogger, Bob Stevens. Be sure you check out Bob’s other guest blog posts. Bob is a member of the Twin Cities PowerShell User Group. Here is his contact information:

Blog: Help! I’m Stuck in My PowerShell!
Twitter: @B_stevens6
LinkedIn: Robert Stevens

Take it away, Bob…

Recently the IT security officer in my organization asked me if there was a way to determine which profiles on a local machine have been accessed in the last 30 days. As many of you know, in Windows XP, Windows 2000, and Windows 2003, profiles are located natively in C:\Documents and Settings\. Since Windows Vista, they are located in C:\Users\.

The movement of user profiles from the “Documents and Settings” folder to the “Users” folders complicates things if you want to avoid changing the code on a case-by-case basis. So we are going to discern between the two by using a pair of “IF” statements. If statements are always followed by two things: the condition and the action. Conditions are encapsulated in parathenses () and actions are encapsulated in braces{}. 

IF (this is true) {do this} 

Inside the condition, we are going to use the WMI object “Win32_OperatingSystem.” This is accomplished by using the Get-WmiObject cmdlet ("RTUR.NET"): 

IF (Get-WmiObject Win32_OperatingSystem) {} 

Because we want the version number, we need to furthur encapsulate the previous command, Get-WmiObject Win32_OperatingSystem in parathenses and append the Version property to it to specify exactly the property we want:

IF ((Get-WmiObject Win32_OperatingSystem).version) {}

Different Windows operating system have different version numbers, as seen on the MSDN site, Operating System Version. We only want to differentiate between version 5 and version 6 because Windows XP, Windows 2000, and Windows 2003 fall under version 5, and after Windows Vista, versions fall under version 6. So we use the -like operator, followed by the version number encapsulated in double quotes.

IF ((Get-WmiObject Win32_OperatingSystem).version –like “5”) {}

And the last thing needed for our condition is a wildcard character. Without it, Windows PowerShell is only going to look for 5 and not anything beginning with a 5, which is what we want.

IF ((Get-WmiObject Win32_OperatingSystem).version –like “5*”) {}

Now that the condition has been defined, it is time to fill in the action. To understand how the upcoming command works, one must understand what happens when a user logs on to a machine. All local and domain accounts have what is called a “local profile.” A local profile stores all of the relevant information specific to a user, such as the user’s settings, pictures, documents, temp folders. In essence it IS the user on the local machine. The only differences between a domain account and a local account is where the authentication takes place and how policies are applied and governed. Otherwise they are treated the same on a local machine.

When a user logs in, the file in the profile labeled ntuser.dat is loaded. This loads the Hkey_Current_User registry hive. This specific hive holds the entirety of the settings specific to the user, and it is loaded every time a profile is accessed. Because a folder (and thus its modified date) can be altered by anyone with Admin rights, looking at the modified date of the ntuser.dat file is a way to be reasonably certain that the profile has legitimately been accessed and loaded. As such, this makes an excellent starting point.

We start with the Get-Item cmdlet. Because ntuser.dat is a hidden file we have to use the –force switch or the Get-Item cmdlet will fail:

Get-Item -force

 Directly following this, we need to tell Windows PowerShell what to get. Because we want to access all profiles, we are going to use a wildcard character (*), and nest it in the profile path in place of an actual profile name:

Get-Item -force "C:\Documents and Settings\*\ntuser.dat"

Note  Quotations must be used because the Documents and Settings directory contains spaces, and Windows PowerShell will treat it as three separate items.

Now that we have retrieved the actual items, we are going to use the pipe (|) operator to tell Windows PowerShell what to do with the output. This must have a space on each side or it will be considered part of a command:

Get-Item -force "C:\Documents and Settings\*\ntuser.dat" |

Our intent is to say, “Return the list of profiles that have been accessed within the last 30 days.” This is accomplished by giving Windows PowerShell the conditional statement. A conditional statement, simply put, filters the data based on one or more conditions. And we will accomplish this with the Where cmdlet. Because we are following Where with a complex command, we use braces:

Where{}

We are using time, so we must use the Get-Date cmdlet. Because it is a specific date within the properties, we need to encapsulate it in parentheses:

Where{

(Get-Date)

}

So far, all we have said is, “Where Date.” Much like an incomplete sentence, this makes absolutely no sense to Windows PowerShell. We need to specify which date to get because any file has several dates, for example, the last-write time, a creation date, and an access date.

For our purposes, we will use the last-write time because it is written to every time the profile is loaded. Because of the multiple dates associated with this file, we need to use the $_. operator to say that it is a specific property of the file, and this must come first or you will get a syntax error:

Where {

(Get-Date)-$_.lastwritetime

}

Note  The hypen is used to separate the initial command from the specification.

Now we need to specify how we want the measurement of time to be expressed. For our purposes, we want the number of days. This is done by encapsulating the entire Get-Date statement in another set of parentheses, and appending the Days property definition to it.

Where {

((Get-Date)-$_.lastwritetime).days

}

Now that we have defined the left-hand side of the condition (X), we must use the inequality operator “Less than” (-le). We want all profiles that have been accessed within the last 30 days, so we set the right-hand side (Y) as 31:

Where {

((Get-Date)-$_.lastwritetime).days -le 31

}

Roll them together and you get this:

Get-Item -force "C:\Documents and Settings\*\ntuser.dat" | Where {

((Get-Date)-$_.lastwritetime).days -le 31

}

That completes our conditional statement, and it will result in retrieving all profiles that have been accessed in the past 30 days. We can stop here and it will provide an output in Windows PowerShell. But what if we want a report of some kind? For this, we need to pipe to the Out-File cmdlet. With Out-File, we need to define the actual output. I have chosen to use the name “30dayrecentprofiles.txt.”

| Out-File 30dayrecentprofiles.txt

We append that to our completed command, and the end result is:

Get-Item -force "C:\Documents and Settings\*\ntuser.dat" | Where {

((Get-Date)-$_.lastwritetime).days -le 31

} | Out-File 30dayrecentprofiles.txt

Now we nest our action inside the braces following our “IF” Statement.

IF ((Get-WmiObject Win32_OperatingSystem).version –like “5*”) {

Get-Item -force "C:\Documents and Settings\*\ntuser.dat" | Where {

((Get-Date)-$_.lastwritetime).days -le 31

} | Out-File 30dayrecentprofiles.txt

}

So far our IF statement only says, “If the host operating system is a variation on version 5, then get all profiles that have had the Hkey_Current_User registry hive loaded within the past 30 days.” To address all versions above version 5, we copy this IF statement, alter the version number to “6,” and change “Documents and Settings” to “Users.”

IF ((Get-WmiObject Win32_OperatingSystem).version –like “6*”) {

Get-Item -force "C:\Users\*\ntuser.dat" | Where {

((Get-Date)-$_.lastwritetime).days -le 31

} | Out-File 30dayrecentprofiles.txt

}

Combined, the finished script should look like this:

IF ((Get-WmiObject Win32_OperatingSystem).version –like “5*”) {

Get-Item -force "C:\Documents and Settings\*\ntuser.dat" | Where {

((Get-Date)-$_.lastwritetime).days -le 31

} | Out-File 30dayrecentprofiles.txt

}

IF ((Get-WmiObject Win32_OperatingSystem).version –like “6*”) {

Get-Item -force "C:\Users\*\ntuser.dat" | Where {

((Get-Date)-$_.lastwritetime).days -le 31

} | Out-File 30dayrecentprofiles.txt

}

As new operating system versions are released and old ones lose support, scripts like this will need to be altered. Of course, this is only one way you can do this with Windows PowerShell. There is always room for improvement. Thank you for reading, and as usual, if you have any input, please use the following Leave a Comment text box!

Related resource

How to Determine the Operating System With Windows PowerShell

 ~Bob

Awesome job, Bob. Join us tomorrow when Bob will expand on this post and talk about removing a conditional profile. It is good stuff that you do not want to miss.

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
  • Nice post Bob. One concern: would you not want to use the -Append syntax with each of your IF statements to avoid them overwriting each other in your report file?

  • Verdrangung,

    I appreciate the comment, And that was my intent.  The script is ment to over write any report with the most current data.  Additionally, the two "IF"  Statements will not conflict as the OS is either a variation of Version 6  or Version 5.

    Thank You,

    Bob Stevens

  • Easier with the following command (In V2.0)

    Get-WmiObject  Win32_UserProfile | select LocalPath, SID,@{LABEL="last used";EXPRESSION={$_.ConvertToDateTime($_.lastusetime)}}| ft