{"id":3435,"date":"2013-06-08T00:01:00","date_gmt":"2013-06-08T00:01:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2013\/06\/08\/weekend-scripter-use-powershell-to-generate-a-recent-profile-report\/"},"modified":"2013-06-08T00:01:00","modified_gmt":"2013-06-08T00:01:00","slug":"weekend-scripter-use-powershell-to-generate-a-recent-profile-report","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/weekend-scripter-use-powershell-to-generate-a-recent-profile-report\/","title":{"rendered":"Weekend Scripter: Use PowerShell to Generate a Recent Profile Report"},"content":{"rendered":"<p><strong style=\"font-size: 12px\">Summary<\/strong><span style=\"font-size: 12px\">: Guest blogger, Bob Stevens, talks about using Windows PowerShell to generate a recent profile report.<\/span><\/p>\n<p align=\"left\">Microsoft Scripting Guy, Ed Wilson, is here. Today I am pleased to welcome back guest blogger, Bob Stevens. Be sure you check out <a href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/tags\/bob+stevens\/\" target=\"_blank\">Bob&rsquo;s other guest blog posts<\/a>. Bob is a member of the Twin Cities PowerShell User Group. Here is his contact information:<\/p>\n<p style=\"padding-left: 30px\">Blog: <a href=\"http:\/\/stuckinmypowershell.blogspot.com\/\" target=\"_blank\">Help! I&rsquo;m Stuck in My PowerShell!<\/a><br \/> Twitter: <a href=\"https:\/\/twitter.com\/B_stevens6\" target=\"_blank\">@B_stevens6<\/a><br \/> LinkedIn: <a href=\"http:\/\/www.linkedin.com\/profile\/edit?trk=hb_tab_pro_top\" target=\"_blank\">Robert Stevens<\/a>\nTake it away, Bob&hellip;\nRecently 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 <strong>C:Documents and Settings<\/strong>. Since Windows Vista, they are located in <strong>C:Users<\/strong>.<strong><\/strong>\nThe movement of user profiles from the &ldquo;Documents and Settings&rdquo; folder to the &ldquo;Users&rdquo; 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 &ldquo;IF&rdquo; 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{}.<span style=\"font-size: 12px\">&nbsp;<\/span><\/p>\n<p style=\"padding-left: 30px\">IF (this is true) {do this}<span style=\"font-size: 12px\">&nbsp;<\/span>\nInside the condition, we are going to use the WMI object &ldquo;Win32_OperatingSystem.&rdquo; This is accomplished by using the <strong>Get-WmiObject<\/strong> cmdlet (&#8220;RTUR.NET&#8221;):<span style=\"font-size: 12px\">&nbsp;<\/span><\/p>\n<p style=\"padding-left: 30px\">IF (Get-WmiObject Win32_OperatingSystem) {}<span style=\"font-size: 12px\">&nbsp;<\/span>\nBecause we want the version number, we need to furthur encapsulate the previous command<strong>, Get-WmiObject Win32_OperatingSystem<\/strong> in parathenses and append the <strong>Version <\/strong>property to it to specify exactly the property we want:<\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: 12px\">IF ((Get-WmiObject Win32_OperatingSystem).version) {}<\/span>\n<span style=\"font-size: 12px\">Different Windows operating system have different version numbers, as seen on the MSDN site, <\/span><a style=\"font-size: 12px\" href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/windows\/desktop\/ms724832(v=vs.85).aspx\" target=\"_blank\">Operating System Version<\/a><span style=\"font-size: 12px\">. 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 <\/span><strong style=\"font-size: 12px\">-like<\/strong><span style=\"font-size: 12px\"> operator, followed by the version number encapsulated in double quotes.<\/span><\/p>\n<p style=\"padding-left: 30px\">IF ((Get-WmiObject Win32_OperatingSystem).version &ndash;like &ldquo;5&rdquo;) {}\n<span style=\"font-size: 12px\">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.<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: 12px\">IF ((Get-WmiObject Win32_OperatingSystem).version &ndash;like &ldquo;5*&rdquo;) {}<\/span>\n<span style=\"font-size: 12px\">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 &ldquo;local profile.&rdquo; A local profile stores all of the relevant information specific to a user, such as the user&rsquo;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.<\/span>\nWhen 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.\nWe start with the <strong>Get-Item<\/strong> cmdlet. Because ntuser.dat is a hidden file we have to use the <strong>&ndash;force<\/strong> switch or the <strong>Get-Item<\/strong> cmdlet will fail:<\/p>\n<p style=\"padding-left: 30px\">Get-Item -force\n&nbsp;<span style=\"font-size: 12px\">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:<\/span><\/p>\n<p style=\"padding-left: 30px\">Get-Item -force &#8220;C:Documents and Settings*ntuser.dat&#8221;<\/p>\n<p style=\"padding-left: 30px\"><strong style=\"font-size: 12px\">Note<\/strong><span style=\"font-size: 12px\">&nbsp;&nbsp;Quotations must be used because the Documents and Settings directory contains spaces, and Windows PowerShell will treat it as three separate items.<\/span>\nNow 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:<\/p>\n<p style=\"padding-left: 30px\">Get-Item -force &#8220;C:Documents and Settings*ntuser.dat&#8221; |\nOur intent is to say, &ldquo;Return the list of profiles that have been accessed within the last 30 days.&rdquo; 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:<\/p>\n<p style=\"padding-left: 30px\">Where{}\n<span style=\"font-size: 12px\">We are using time, so we must use the <\/span><strong style=\"font-size: 12px\">Get-Date<\/strong><span style=\"font-size: 12px\"> cmdlet. Because it is a specific date within the properties, we need to encapsulate it in parentheses:<\/span><\/p>\n<p style=\"padding-left: 30px\">Where{<\/p>\n<p style=\"padding-left: 30px\">(Get-Date)<\/p>\n<p style=\"padding-left: 30px\">}\n<span style=\"font-size: 12px\">So far, all we have said is, &ldquo;Where Date.&rdquo; 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.<\/span>\nFor 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 <strong>$_.<\/strong> operator to say that it is a specific property of the file, and this must come first or you will get a syntax error:<\/p>\n<p style=\"padding-left: 30px\">Where {<\/p>\n<p style=\"padding-left: 30px\">(Get-Date)-$_.lastwritetime<\/p>\n<p style=\"padding-left: 30px\">}<\/p>\n<p style=\"padding-left: 30px\"><strong style=\"font-size: 12px\">Note<\/strong><span style=\"font-size: 12px\">&nbsp; The hypen is used to separate the initial command from the specification.<\/span>\nNow 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 <strong>Get-Date<\/strong> statement in another set of parentheses, and appending the <strong>Days<\/strong> property definition to it.<\/p>\n<p style=\"padding-left: 30px\">Where {<\/p>\n<p style=\"padding-left: 30px\">((Get-Date)-$_.lastwritetime).days<\/p>\n<p style=\"padding-left: 30px\">}\n<span style=\"font-size: 12px\">Now that we have defined the left-hand side of the condition (X), we must use the inequality<\/span><strong style=\"font-size: 12px\"> <\/strong><span style=\"font-size: 12px\">operator &ldquo;Less than&rdquo; (<\/span><strong style=\"font-size: 12px\">-le<\/strong><span style=\"font-size: 12px\">). We want all profiles that have been accessed within the last 30 days, so we set the right-hand side (Y) as 31:<\/span><\/p>\n<p style=\"padding-left: 30px\">Where {<\/p>\n<p style=\"padding-left: 30px\">((Get-Date)-$_.lastwritetime).days -le 31<\/p>\n<p style=\"padding-left: 30px\">}\n<span style=\"font-size: 12px\">Roll them together and you get this:<\/span><\/p>\n<p style=\"padding-left: 30px\">Get-Item -force &#8220;C:Documents and Settings*ntuser.dat&#8221; | Where {<\/p>\n<p style=\"padding-left: 30px\">((Get-Date)-$_.lastwritetime).days -le 31<\/p>\n<p style=\"padding-left: 30px\">}\n<span style=\"font-size: 12px\">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 <\/span><strong style=\"font-size: 12px\">Out-File<\/strong><span style=\"font-size: 12px\"> cmdlet. With <\/span><strong style=\"font-size: 12px\">Out-File<\/strong><span style=\"font-size: 12px\">, we need to define the actual output. I have chosen to use the name &ldquo;30dayrecentprofiles.txt.&rdquo;<\/span><\/p>\n<p style=\"padding-left: 30px\">| Out-File 30dayrecentprofiles.txt\n<span style=\"font-size: 12px\">We append that to our completed command, and the end result is:<\/span><\/p>\n<p style=\"padding-left: 30px\">Get-Item -force &#8220;C:Documents and Settings*ntuser.dat&#8221; | Where {<\/p>\n<p style=\"padding-left: 30px\">((Get-Date)-$_.lastwritetime).days -le 31<\/p>\n<p style=\"padding-left: 30px\">} | Out-File 30dayrecentprofiles.txt\nNow we nest our action inside the braces following our &ldquo;IF&rdquo; Statement.<\/p>\n<p style=\"padding-left: 30px\">IF ((Get-WmiObject Win32_OperatingSystem).version &ndash;like &ldquo;5*&rdquo;) {<\/p>\n<p style=\"padding-left: 30px\">Get-Item -force &#8220;C:Documents and Settings*ntuser.dat&#8221; | Where {<\/p>\n<p style=\"padding-left: 30px\">((Get-Date)-$_.lastwritetime).days -le 31<\/p>\n<p style=\"padding-left: 30px\">} | Out-File 30dayrecentprofiles.txt<\/p>\n<p style=\"padding-left: 30px\">}\nSo far our IF statement only says, &ldquo;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.&rdquo; To address all versions above version 5, we copy this IF statement, alter the version number to &ldquo;6,&rdquo; and change &ldquo;Documents and Settings&rdquo; to &ldquo;Users.&rdquo;<\/p>\n<p style=\"padding-left: 30px\">IF ((Get-WmiObject Win32_OperatingSystem).version &ndash;like &ldquo;<strong>6<\/strong>*&rdquo;) {<\/p>\n<p style=\"padding-left: 30px\">Get-Item -force &#8220;C:<strong>Users<\/strong>*ntuser.dat&#8221; | Where {<\/p>\n<p style=\"padding-left: 30px\">((Get-Date)-$_.lastwritetime).days -le 31<\/p>\n<p style=\"padding-left: 30px\">} | Out-File 30dayrecentprofiles.txt<\/p>\n<p style=\"padding-left: 30px\">}\nCombined, the finished script should look like this:<\/p>\n<p style=\"padding-left: 30px\">IF ((Get-WmiObject Win32_OperatingSystem).version &ndash;like &ldquo;5*&rdquo;) {<\/p>\n<p style=\"padding-left: 30px\">Get-Item -force &#8220;C:Documents and Settings*ntuser.dat&#8221; | Where {<\/p>\n<p style=\"padding-left: 30px\">((Get-Date)-$_.lastwritetime).days -le 31<\/p>\n<p style=\"padding-left: 30px\">} | Out-File 30dayrecentprofiles.txt<\/p>\n<p style=\"padding-left: 30px\">}<\/p>\n<p style=\"padding-left: 30px\">IF ((Get-WmiObject Win32_OperatingSystem).version &ndash;like &ldquo;<strong>6<\/strong>*&rdquo;) {<\/p>\n<p style=\"padding-left: 30px\">Get-Item -force &#8220;C:<strong>Users<\/strong>*ntuser.dat&#8221; | Where {<\/p>\n<p style=\"padding-left: 30px\">((Get-Date)-$_.lastwritetime).days -le 31<\/p>\n<p style=\"padding-left: 30px\">} | Out-File 30dayrecentprofiles.txt<\/p>\n<p style=\"padding-left: 30px\">}\n<span style=\"font-size: 12px\">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 <\/span><strong style=\"font-size: 12px\">Leave a Comment <\/strong><span style=\"font-size: 12px\">text box!<\/span><\/p>\n<h3><strong>Related resource<\/strong><\/h3>\n<p style=\"padding-left: 30px\"><a href=\"http:\/\/www.ifunky.net\/Blog\/post\/How-to-Determine-the-Operating-System-With-Powershell.aspx\" target=\"_blank\">How to Determine the Operating System With Windows PowerShell<\/a>\n&nbsp;~Bob\nAwesome 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.\nI invite you to follow me on <a href=\"http:\/\/bit.ly\/scriptingguystwitter\" target=\"_blank\">Twitter<\/a> and <a href=\"http:\/\/bit.ly\/scriptingguysfacebook\" target=\"_blank\">Facebook<\/a>. If you have any questions, send email to me at <a href=\"http:\/\/blogs.technet.commailto:scripter@microsoft.com\" target=\"_blank\">scripter@microsoft.com<\/a>, or post your questions on the <a href=\"http:\/\/bit.ly\/scriptingforum\" target=\"_blank\">Official Scripting Guys Forum<\/a>. See you tomorrow. Until then, peace.\n<strong>Ed Wilson, Microsoft Scripting Guy<\/strong><\/p>\n","protected":false},"excerpt":{"rendered":"<p>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&rsquo;s other guest blog posts. Bob is a member of the Twin Cities PowerShell User Group. [&hellip;]<\/p>\n","protected":false},"author":596,"featured_media":87096,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[424,16,56,3,198,61,45],"class_list":["post-3435","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-bob-stevens","tag-desktop-management","tag-guest-blogger","tag-scripting-guy","tag-users","tag-weekend-scripter","tag-windows-powershell"],"acf":[],"blog_post_summary":"<p>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&rsquo;s other guest blog posts. Bob is a member of the Twin Cities PowerShell User Group. [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/3435","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/users\/596"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/comments?post=3435"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/3435\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media\/87096"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media?parent=3435"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=3435"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=3435"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}