<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://powershell.com/cs/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>Search results for 'app:weblogs' matching tags 'Windows PowerShell', 'operating system', 'Brian Wilhite', and 'Scripting Guy!'</title><link>http://powershell.com/cs/search/SearchResults.aspx?q=app:weblogs&amp;tag=Windows+PowerShell,operating+system,Brian+Wilhite,Scripting+Guy!&amp;orTags=0&amp;o=DateDescending</link><description>Search results for 'app:weblogs' matching tags 'Windows PowerShell', 'operating system', 'Brian Wilhite', and 'Scripting Guy!'</description><dc:language>en-US</dc:language><generator>CommunityServer 2008.5 (Build: 30929.2835)</generator><item><title>Use PowerShell to Find Last Logon Times for Virtual Workstations</title><link>http://powershell.com/cs/blogs/hey-scriptingguy/archive/2012/02/19/use-powershell-to-find-last-logon-times-for-virtual-workstations.aspx</link><pubDate>Sun, 19 Feb 2012 06:00:00 GMT</pubDate><guid isPermaLink="false">f421715f-7aba-45f0-8a8d-44de5318a3a7:14765</guid><dc:creator>Anonymous</dc:creator><description>&lt;p&gt;&lt;b&gt;Summary&lt;/b&gt;: Learn how to Use Windows PowerShell to find the last logon times for virtual workstations.&lt;/p&gt;
&lt;p&gt;Microsoft Scripting Guy, Ed Wilson, is here. Welcome back guest blogger, Brian Wilhite. Brian was our guest blogger yesterday when he wrote about &lt;a href="http://blogs.technet.com/b/heyscriptingguy/archive/2012/02/18/use-powershell-to-save-time-with-win32-timezone-and-dst-time-changes.aspx" target="_blank"&gt;detecting servers that will have a problem with an upcoming time change due to daylight savings time&lt;/a&gt;. Here is a little bit about Brian.&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;Brian Wilhite works as a Windows System Administrator for a large health-care provider in North Carolina. He has over 15 years of experience in IT. In his current capacity as a Windows SysAdmin, he leads a team of individuals that have responsibilities for Microsoft Exchange Server, Windows Server builds, and management and system performance. Brian also supports and participates in the &lt;a href="http://powershellgroup.org/charlotte.nc" target="_blank"&gt;Charlotte PowerShell Users Group&lt;/a&gt;. &lt;br /&gt; Twitter: &lt;a href="http://twitter.com/bwilhite1979" target="_blank"&gt;Brian Wilhite&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Take it away, Brian&amp;hellip;&lt;/p&gt;
&lt;p&gt;Several weeks ago our virtual guy asked me if there was a way to determine which virtual workstations have been recently used. I started thinking, and of course, the first place I turned was to Windows PowerShell. I did some research and found the &lt;a href="http://msdn.microsoft.com/en-us/library/windows/desktop/ee886409(v=vs.85).aspx" target="_blank"&gt;Win32_UserProfile WMI class&lt;/a&gt;. However, the &amp;ldquo;minimum supported client&amp;rdquo; is Windows Vista with SP1, and the majority of our virtual workstations are running Windows&amp;nbsp;XP. So the dilemma was to create a function that would provide the same type of information for computers running Windows&amp;nbsp;XP and later. I evaluated the information that was returned from the Win32_UserProfile class. As you see in the following image, I indexed into the third object of the Win32_UserProfile array for brevity, and this is the information that&amp;rsquo;s available.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://blogs.technet.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-76-18/5102.hsg_2D00_2_2D00_19_2D00_12_2D00_1.jpg"&gt;&lt;img src="http://blogs.technet.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-76-18/5102.hsg_2D00_2_2D00_19_2D00_12_2D00_1.jpg" alt="Image of command output" title="Image of command output" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I wanted to provide the following information:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The computer from which the function was run against&lt;/li&gt;
&lt;li&gt;The user account that was logged on last (security identifier or SID)&lt;/li&gt;
&lt;li&gt;The last use time (LastUseTime)&lt;/li&gt;
&lt;li&gt;Is the user currently logged on? (Loaded)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I&amp;rsquo;m going to use two methods to gather these four pieces of information. First, I&amp;rsquo;m going to use WMI to collect the information on computers running Windows Vista with SP1 and later. For computers running Windows Vista and earlier, I&amp;rsquo;m going to use user profile file properties and registry information to collect the needed data. We will discuss the WMI method first.&lt;/p&gt;
&lt;p&gt;I am using the Win32_OperatingSystem WMI class to collect the build number to determine which method to use.&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$Win32OS = Get-WmiObject -Class Win32_OperatingSystem -ComputerName $Computer&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$Build = $Win32OS.BuildNumber&lt;/p&gt;
&lt;p&gt;The &amp;ldquo;If ($Build -ge 6001)&amp;rdquo; is the first decision point. If the build number is 6001 and above, the script block will run.&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;If ($Build -ge 6001)&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;{&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$Win32User = Get-WmiObject -Class Win32_UserProfile -ComputerName $Computer&lt;/p&gt;
&lt;p&gt;I am using &lt;b&gt;RegEx&lt;/b&gt; to filter the LocalService, NetworkService, and System profiles because they aren&amp;rsquo;t needed, and I am sorting by &lt;b&gt;LastUseTime&lt;/b&gt; to pick the one most recently used.&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$Win32User = $Win32User | Where-Object {($_.SID -notmatch &amp;quot;^S-1-5-\d[18|19|20]$&amp;quot;)}&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$Win32User = $Win32User | Sort-Object -Property LastUseTime -Descending&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$LastUser = $Win32User | Select-Object -First 1&lt;/p&gt;
&lt;p&gt;The Win32_UserProfile &lt;b&gt;Loaded&lt;/b&gt; property determines if the user was logged on at the time the query was run. I&amp;rsquo;m casting that value into a new variable (&lt;b&gt;$Loaded&lt;/b&gt;). I will create a &lt;b&gt;New-Object&lt;/b&gt; with that property and value later.&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$Loaded = $LastUser.Loaded&lt;/p&gt;
&lt;p&gt;So now we&amp;rsquo;re looking at the &lt;b&gt;LastUseTime&lt;/b&gt; property&amp;mdash;the value is a &amp;ldquo;System.String&amp;rdquo; (20120209035107.508000+000), but I need to convert it to a &amp;ldquo;System.DateTime&amp;rdquo; object, so it&amp;rsquo;s readable, I will use the WMI &lt;b&gt;ConvertToDateTime&lt;/b&gt; method to accomplish this.&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$Time = ([WMI] &amp;#39;&amp;#39;).ConvertToDateTime($LastUser.LastUseTime)&lt;/p&gt;
&lt;p&gt;One of the things I need to do is take the SID that is collected via Win32_UserProfile and convert it to Domain\samAccountName format.&lt;/p&gt;
&lt;p&gt;So I created a &lt;b&gt;New-Object&lt;/b&gt; with the &lt;a href="http://msdn.microsoft.com/en-us/library/system.security.principal.securityidentifier.aspx" target="_blank"&gt;.NET Security Identifier Class Provider&lt;/a&gt;, and I specified the &lt;b&gt;$LastUser.SID&lt;/b&gt; variable.&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$UserSID = New-Object System.Security.Principal.SecurityIdentifier($LastUser.SID)&lt;/p&gt;
&lt;p&gt;When &lt;b&gt;New-Object&lt;/b&gt; is created with the SID value, there is a translate method that can be used to convert the SID to the Domain\samAccountName.&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$User = $UserSID.Translate([System.Security.Principal.NTAccount])&lt;/p&gt;
&lt;p&gt;Instead of using &lt;b&gt;Write-Host&lt;/b&gt; or some string-type output, I prefer to use object-based output. The following code snippet shows the four pieces of information that I wanted to gather and return.&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$UserProf = New-Object PSObject -Property @{&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;Computer=$Computer&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;User=$User&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;Time=$Time&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;CurrentlyLoggedOn=$Loaded&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;}&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;ve ever created custom objects in Windows PowerShell, you know that without any special XML formatting, when you return the object, it will place the properties in an order that you may not like. To quickly remedy this, what I usually do is pipe my variable that contains the custom object to &lt;b&gt;Select-Object&lt;/b&gt; and type the names of the properties in the order in which I want them returned.&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$UserProf = $UserProf | Select-Object Computer, User, Time, CurrentlyLoggedOn&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$UserProf&lt;/p&gt;
&lt;p&gt;Now when &lt;b&gt;$UserProf&lt;/b&gt; is returned, the following is displayed:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://blogs.technet.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-76-18/0841.hsg_2D00_2_2D00_19_2D00_12_2D00_2.jpg"&gt;&lt;img src="http://blogs.technet.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-76-18/0841.hsg_2D00_2_2D00_19_2D00_12_2D00_2.jpg" alt="Image of command output" title="Image of command output" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now that we&amp;rsquo;ve taken care of any computer that has the Win32_UserProfile WMI class, beginning with Windows Vista with SP1, let&amp;rsquo;s take a look at those computers that do not have that WMI class. I started thinking about how to figure out the last person to log on, what time they logged on, and if they were currently logged on. I observed my profile as I logged on, and I noticed that the&lt;b&gt; &lt;/b&gt;NTUSER.DAT.LOG file was immediately modified. This file is intermittently updated throughout the user&amp;rsquo;s session. The NTUSER.DAT.LOG is used for fault tolerance purposes if Windows can&amp;rsquo;t update the NTUSER.DAT file. Obviously, as soon as the user logs off, the file is no longer updated.&lt;/p&gt;
&lt;p&gt;The &lt;b&gt;If &lt;/b&gt;statement checks for the build number 6000 and below, meaning Windows Vista without SP1 and earlier.&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;If ($Build -le 6000)&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;{&lt;/p&gt;
&lt;p&gt;To scan the user profile directories for the NTUSER.DAT.LOG,&lt;b&gt; &lt;/b&gt;I am making the assumption that the Documents and Settings folder is residing on the system drive. I&amp;rsquo;m querying the system drive information from the Win32_OperatingSystem WMI class and isolating only the drive letter by using the &lt;b&gt;Replace&lt;/b&gt; method. When we have that information, we can put the &lt;b&gt;$Computer&lt;/b&gt; and system drive letter together and make a UNC path for scanning &amp;ldquo;Documents and Settings&amp;rdquo;.&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$SysDrv = $Win32OS.SystemDrive&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$SysDrv = $SysDrv.Replace(&amp;quot;:&amp;quot;,&amp;quot;$&amp;quot;)&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$ProfDrv = &amp;quot;\\&amp;quot; + $Computer + &amp;quot;\&amp;quot; + $SysDrv&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$ProfLoc = Join-Path -Path $ProfDrv -ChildPath &amp;quot;Documents and Settings&amp;quot;&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$Profiles = Get-ChildItem -Path $ProfLoc&lt;/p&gt;
&lt;p&gt;When we have all of the user profiles, we want to search for the NTUSER.DAT.LOG files. After we capture all the NTUSER.DAT.LOG in the &lt;b&gt;$LastProf&lt;/b&gt; variable, we need to sort by the &lt;b&gt;LastWriteTime&lt;/b&gt; property in descending order, and select the first one.&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$LastProf = $Profiles | ForEach-Object -Process {$_.GetFiles(&amp;quot;ntuser.dat.LOG&amp;quot;)}&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$LastProf = $LastProf | Sort-Object -Property LastWriteTime -Descending | Select-Object -First 1&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;ve isolated the most recent NTUSER.DAT.LOG, so I&amp;rsquo;m now making another assumption that the profile folder name will equal the &lt;b&gt;UserName&lt;/b&gt;. By using the &lt;b&gt;Replace&lt;/b&gt; method, I&amp;rsquo;m going to strip the &amp;ldquo;\\$Computer\&amp;lt;System Drive&amp;gt;$\Documents and Settings&amp;rdquo; off of the &lt;b&gt;DirectoryName&lt;/b&gt;, which represents the full path of the user&amp;rsquo;s profile. I&amp;rsquo;m also going to grab the &lt;b&gt;LastAccessTime&lt;/b&gt; and cast it to the &lt;b&gt;$Time&lt;/b&gt; variable.&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$UserName = $LastProf.DirectoryName.Replace(&amp;quot;$ProfLoc&amp;quot;,&amp;quot;&amp;quot;).Trim(&amp;quot;\&amp;quot;).ToUpper()&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$Time = $LastProf.LastAccessTime&lt;/p&gt;
&lt;p&gt;We are going to use the following code to extract the user&amp;rsquo;s SID from the access control entry of the NTUSER.DAT.LOG file.&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$Sddl = $LastProf.GetAccessControl().Sddl&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$Sddl = $Sddl.split(&amp;quot;(&amp;quot;) | Select-String -Pattern &amp;quot;[0-9]\)$&amp;quot; | Select-Object -First 1&lt;/p&gt;
&lt;p&gt;Here we are formatting the SID, and assuming the sixth entry will be the user&amp;rsquo;s SID.&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$Sddl = $Sddl.ToString().Split(&amp;quot;;&amp;quot;)[5].Trim(&amp;quot;)&amp;quot;)&lt;/p&gt;
&lt;p&gt;The following code is used to convert the &lt;b&gt;$UserName&lt;/b&gt; variable to the SID to detect if the profile is loaded via the remote registry and to compare the SID queried from the NTUSER.DAT.LOG file.&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$TranSID = New-Object System.Security.Principal.NTAccount($UserName)&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$UserSID = $TranSID.Translate([System.Security.Principal.SecurityIdentifier])&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;I felt it was necessary to compare the SID queried from the NTUSER.DAT.LOG file and the &lt;b&gt;UserName&lt;/b&gt; extracted from the profile path, to ensure that the correct information is being returned.&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;If ($Sddl -eq $UserSID)&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;{&lt;/p&gt;
&lt;p&gt;If the SIDs are equal, I&amp;rsquo;m going to open the HK_USERS hive and set the &lt;b&gt;$Loaded&lt;/b&gt; variable to True if &lt;b&gt;SubKeys&lt;/b&gt; contains the SID and to False if it isn&amp;rsquo;t present. If the user&amp;rsquo;s SID is present in the HK_USERS hive, the user is currently logged on.&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$Reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]&amp;quot;Users&amp;quot;,$Computer)&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$Loaded = $Reg.GetSubKeyNames() -contains $UserSID.Value&lt;/p&gt;
&lt;p&gt;Because I have the &lt;b&gt;UserName&lt;/b&gt; and no &lt;b&gt;DomainName&lt;/b&gt;, I&amp;rsquo;m going to convert the SID to &lt;b&gt;Account&lt;/b&gt; so that it will return in the DOMAIN\USER format.&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$UserSID = New-Object System.Security.Principal.SecurityIdentifier($UserSID)&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$User = $UserSID.Translate([System.Security.Principal.NTAccount])&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;}#End If ($Sddl -eq $UserSID)&lt;/p&gt;
&lt;p&gt;If the SIDs are not equal, I will set &lt;b&gt;$User&lt;/b&gt; to the profile folder name and set &lt;b&gt;$Loaded&lt;/b&gt; to &amp;ldquo;Unknown&amp;rdquo; because I could not determine if the SID was 100% accurate.&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;Else&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;{&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$User = $UserName&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$Loaded = &amp;quot;Unknown&amp;quot;&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;}#End Else&lt;/p&gt;
&lt;p&gt;Here I am creating and formatting the custom object, like we discussed earlier for the Windows Vista with SP1 and later script block.&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;#Creating the PSObject UserProf&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$UserProf = New-Object PSObject -Property @{&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;Computer=$Computer&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;User=$User&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;Time=$Time&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;CurrentlyLoggedOn=$Loaded&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;}&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$UserProf = $UserProf | Select-Object Computer, User, Time, CurrentlyLoggedOn&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$UserProf&lt;/p&gt;
&lt;p&gt;I setup this function to accept piped input for the &lt;i&gt;ComputerName&lt;/i&gt; parameter. It will also accept an array of &lt;i&gt;ComputerNames&lt;/i&gt;. So when we run &lt;b&gt;Get-Lastlogon&lt;/b&gt;, we&amp;rsquo;ll be able to determine what workstations haven&amp;rsquo;t been used in a while, as shown in the following image.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://blogs.technet.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-76-18/6378.hsg_2D00_2_2D00_19_2D00_12_2D00_3.jpg"&gt;&lt;img src="http://blogs.technet.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-76-18/6378.hsg_2D00_2_2D00_19_2D00_12_2D00_3.jpg" alt="Image of command output" title="Image of command output" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;~Brian&lt;/p&gt;
&lt;p&gt;Thank you Brian, this is a most useful and interesting script. The complete script can be found at the &lt;a href="http://gallery.technet.microsoft.com/scriptcenter/Get-LastLogon-Determining-283f98ae" target="_blank"&gt;Script Center Repository&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I invite you to follow me on &lt;a href="http://bit.ly/scriptingguystwitter" target="_blank"&gt;Twitter&lt;/a&gt; and &lt;a href="http://bit.ly/scriptingguysfacebook" target="_blank"&gt;Facebook&lt;/a&gt;. If you have any questions, send email to me at &lt;a href="mailto:scripter@microsoft.com" target="_blank"&gt;scripter@microsoft.com&lt;/a&gt;, or post your questions on the &lt;a href="http://bit.ly/scriptingforum" target="_blank"&gt;Official Scripting Guys Forum&lt;/a&gt;. See you tomorrow. Until then, peace.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Ed Wilson, Microsoft Scripting Guy&lt;/b&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.technet.com/aggbug.aspx?PostID=3481175" width="1" height="1" alt="" /&gt;</description></item><item><title>Use PowerShell to Save Time with Win32_TimeZone and DST Time Changes</title><link>http://powershell.com/cs/blogs/hey-scriptingguy/archive/2012/02/18/use-powershell-to-save-time-with-win32-timezone-and-dst-time-changes.aspx</link><pubDate>Sat, 18 Feb 2012 06:00:00 GMT</pubDate><guid isPermaLink="false">f421715f-7aba-45f0-8a8d-44de5318a3a7:14746</guid><dc:creator>Anonymous</dc:creator><description>&lt;p&gt;&lt;b&gt;Summary&lt;/b&gt;: Write a Windows PowerShell function to determine the status of time changes to daylight savings time.&lt;/p&gt;
&lt;p&gt;Microsoft Scripting Guy, Ed Wilson, is here. At the Windows PowerShell User Group meeting in Charlotte, North Carolina, Brian Wilhite talked about a few of his scripts during the script club. I said, &amp;ldquo;Dude, that is cool, and I immediately challenged Brian to share his scripts with us. The result is two excellent guest postings. Here is the first one.&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;Brian Wilhite works as a Windows System Administrator for a large health-care provider in North Carolina. He has over 15 years of experience in IT. In his current capacity as a Windows SysAdmin, he leads a team of individuals that have responsibilities for Microsoft Exchange Server, Windows Server builds, and management and system performance. Brian also supports and participates in the &lt;a href="http://powershellgroup.org/charlotte.nc" target="_blank"&gt;Charlotte PowerShell Users Group&lt;/a&gt;. &lt;br /&gt; Twitter: &lt;a href="http://twitter.com/bwilhite1979" target="_blank"&gt;Brian Wilhite&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s Brian&amp;hellip;&lt;/p&gt;
&lt;p&gt;Just prior to the latest time change in the fall, I was asked to run a time verification script on all of our computers running Windows Server. Typically, this was done after the time change, between 1:00 AM and 2:00 AM. Being an IT Guy, I&amp;rsquo;m used to long days and/or long, late nights, but I thought to myself, &amp;ldquo;I wonder if I can be proactive in knowing which servers may have problems making the change this year.&amp;rdquo; So I researched, and found the Win32_TimeZone WMI Class and all the goodness therein. Here is some of what I found:&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;Get-WMIObject &amp;ndash;Class Win32_TimeZone&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;DaylightDay&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : 2&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;DaylightDayOfWeek&amp;nbsp;&amp;nbsp; : 0&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;DaylightMonth&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : 3&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;DaylightName&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : Eastern Daylight Time&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;Description&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : (UTC-05:00) Eastern Time (US &amp;amp; Canada)&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;StandardDay&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : 1&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;StandardDayOfWeek&amp;nbsp;&amp;nbsp; : 0&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;StandardMonth&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : 11&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;StandardName&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : Eastern Standard Time&lt;/p&gt;
&lt;p&gt;So&amp;hellip;&lt;/p&gt;
&lt;p&gt;Like I always do when I evaluate WMI classes, I performed a BING search on &lt;a href="http://msdn.microsoft.com/en-us/library/windows/desktop/aa394498(v=vs.85).aspx" target="_blank"&gt;MSDN Win32_TimeZone&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;After I reviewed all of the information there, specifically DaylightDay, DaylightDayOfWeek, etc., I determined that I could, in fact, proactively identify servers that could have time change issues on the day of the time change.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;This excerpt from the MSDN website sums it all up:&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;If the transition day (DaylightDayOfWeek) occurs on a Sunday, then the value &amp;quot;1&amp;quot; indicates the first Sunday of the DaylightMonth, &amp;quot;2&amp;quot; indicates the second Sunday, and so on. The value &amp;quot;5&amp;quot; indicates the last DaylightDayOfWeek in the month.&lt;/p&gt;
&lt;p&gt;I took this information and started thinking, &amp;ldquo;I can write a function that will gather this information and report what it finds.&amp;rdquo; So here&amp;rsquo;s what I did&amp;hellip;&lt;/p&gt;
&lt;p&gt;First, I created the framework for an advanced function. It contains the usual items, such as: &lt;b&gt;CmdletBinding&lt;/b&gt;, defining parameters, setting up the &lt;b&gt;Begin&lt;/b&gt;, &lt;b&gt;Process&lt;/b&gt;, &lt;b&gt;Try&lt;/b&gt;, &lt;b&gt;Catch&lt;/b&gt;, and &lt;b&gt;End&lt;/b&gt; &lt;b&gt;Script&lt;/b&gt; blocks. This is shown in the following image.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://blogs.technet.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-76-18/5127.hsg_2D00_2_2D00_18_2D00_12_2D00_1.jpg"&gt;&lt;img src="http://blogs.technet.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-76-18/5127.hsg_2D00_2_2D00_18_2D00_12_2D00_1.jpg" alt="Image of function" title="Image of function" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Next, I started thinking of all the data that I wanted to capture. I know I will need the Win32_TimeZone WMI class. Because in the past, we always checked for current time, I will also query the Win32_LocalTime. Therefore, here is the start of the processing part of the &lt;b&gt;Get-DSTInfo&lt;/b&gt; function:&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;a href="http://blogs.technet.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-76-18/2133.hsg_2D00_2_2D00_18_2D00_12_2D00_2.jpg"&gt;&lt;img src="http://blogs.technet.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-76-18/2133.hsg_2D00_2_2D00_18_2D00_12_2D00_2.jpg" alt="Image of function" title="Image of function" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;As you see in the previous image, I casted the &lt;b&gt;$LocalTime&lt;/b&gt; variable as a &lt;b&gt;DateTime&lt;/b&gt; object by passing it a string that is comprised of variables made up from the Win32_LocalTime WMI class. I will use this later to create a custom &lt;b&gt;PSObject&lt;/b&gt;, which will allow you to treat this property as a &lt;b&gt;DateTime&lt;/b&gt; object, if needed.&lt;/p&gt;
&lt;p&gt;I also captured the WMI class that&amp;rsquo;s going to gather all the data to make the magic happen, the Win32_TimeZone class. You may have noticed the switch statement for the &lt;b&gt;$TimeZone.DaylightDay&lt;/b&gt; property. Because the value is numeric and not &amp;ldquo;display friendly,&amp;rdquo; I set up several switch statements. They are shortened for brevity, but you get the idea:&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;Switch ($TimeZone.DaylightDay)&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;{&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;1 {$DSTDay = &amp;quot;First&amp;quot;}&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;2 {$DSTDay = &amp;quot;Second&amp;quot;}&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;#From 1 to 5 signifying the First through Last week in the month.&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;}&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;Switch ($TimeZone.DaylightDayOfWeek)&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;{&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;0 {$DSTWeek = &amp;quot;Sunday&amp;quot;}&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;1 {$DSTWeek = &amp;quot;Monday&amp;quot;}&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;#From 0 to 6 to signify the days of the week.&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;}&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;Switch ($TimeZone.DaylightMonth)&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;{&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;1 {$DSTMonth = &amp;quot;January&amp;quot;}&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;2 {$DSTMonth = &amp;quot;February&amp;quot;}&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;3 {$DSTMonth = &amp;quot;March&amp;quot;}&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;#From 1 to 12 to signify months of the year.&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;}&lt;/p&gt;
&lt;p&gt;OK. So consider the following properties of the &lt;b&gt;$TimeZone&lt;/b&gt; object, which is an instance of Win32_TimeZone.&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$TimeZone.DaylightDay = 2&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$TimeZone.DaylightDayOfWeek = 0&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$TimeZone.DaylightMonth = 3&lt;/p&gt;
&lt;p&gt;These property values will mean the &amp;ldquo;spring ahead.&amp;rdquo; The time change will occur on the &amp;ldquo;Second &lt;i&gt;(2)&lt;/i&gt; Sunday &lt;i&gt;(0)&lt;/i&gt; of March &lt;i&gt;(3)&lt;/i&gt;&amp;rdquo;.&amp;nbsp; The same thing holds true with the &lt;b&gt;$TimeZone.StandardDay&lt;/b&gt;, &lt;b&gt;$TimeZone.StandardDayOfWeek&lt;/b&gt;, and &lt;b&gt;$TimeZone.StandardMonth&lt;/b&gt;&amp;mdash;but obviously, they will have different values.&lt;/p&gt;
&lt;p&gt;When all of my switch statements were setup (six in total), I created several objects depending on what parameters were passed when running the function. The two parameters that I defined were &lt;i&gt;-Standard&lt;/i&gt; and &lt;i&gt;-Daylight&lt;/i&gt;. Neither parameter is mandatory because running the function without parameters will return both Standard and Daylight time change information in the form of a &lt;b&gt;PSObject&lt;/b&gt; for the local host that is stated. Here is the &lt;b&gt;If &lt;/b&gt;statement that I used to accomplish neither parameter being passed:&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;If ((-not $Standard) -and (-not $Daylight))&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;{&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$STND_DL = New-Object PSObject -Property @{&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;Computer=$Computer&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;StandardName=$STDTime&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;StandardDay=$STDDay&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;StandardDayOfWeek=$STDWeek&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;StandardMonth=$STDMonth&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;DaylightName=$DayTime&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;DaylightDay=$DSTDay&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;DaylightDayOfWeek=$DSTWeek&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;DaylightMonth=$DSTMonth&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;CurrentTime=$LocalTime&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;}#End $DL New-Object&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$STND_DL = $STND_DL | Select-Object -Property Computer, StandardName, StandardDay, StandardDayOfWeek, StandardMonth, DaylightName, DaylightDay, DaylightDayOfWeek, DaylightMonth, CurrentTime&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$STND_DL&lt;/p&gt;
&lt;p&gt;As you may have noticed, I did something pretty cool. If you have played with custom objects in Windows PowerShell, you know that they don&amp;rsquo;t always display the properties the way you would like them to. So, I piped my custom &lt;b&gt;PSObject&lt;/b&gt; &lt;b&gt;($STND_DL&lt;/b&gt;) to the &lt;b&gt;Select-Object&lt;/b&gt; cmdlet to display the object properties in logical order as you read it&amp;mdash;something that makes logical sense to a variable.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;This is what you get when you run the function; the output is an object that you can have your way with:&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;a href="http://blogs.technet.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-76-18/5315.hsg_2D00_2_2D00_18_2D00_12_2D00_3.jpg"&gt;&lt;img src="http://blogs.technet.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-76-18/5315.hsg_2D00_2_2D00_18_2D00_12_2D00_3.jpg" alt="Image of command output" title="Image of command output" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;i&gt;Yes, you read that right, 3:49AM. Like I said, I&amp;rsquo;m not a stranger to long late nights&amp;mdash;after all, I am an IT Guy.&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;Getting back to the topic&amp;hellip;&lt;/p&gt;
&lt;p&gt;You could also pipe computer names to &lt;b&gt;Get-DSTInfo&lt;/b&gt;, something like the following:&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;Get-ADComputer &amp;ndash;Filter * | Select &amp;ndash;Expandproperty Name | Get-DSTInfo | Export-Csv &amp;ndash;Path C:\TimeZoneInfo.csv&lt;/p&gt;
&lt;p&gt;This would gather all the computers in your domain and run the &lt;b&gt;Get-DSTInfo&lt;/b&gt; function against them, then export the findings to a .csv file. The way you would read the object output is that the time change would occur the First Sunday of November and Second Sunday of March.&lt;/p&gt;
&lt;p&gt;This function allowed me to proactively review the time change information of all 1500+ servers in our domain. The great thing is that I did find a few that did not have the correct time change patch installed, and I was able to remediate that before it became an issue when the clocks turned back last fall.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Thanks for listening to me ramble on about my simple but useful function. Happy PowerShelling!&lt;/p&gt;
&lt;p&gt;~Brian&lt;/p&gt;
&lt;p&gt;Thank you, Brian. The full script can be found on the &lt;a href="http://gallery.technet.microsoft.com/scriptcenter/Get-DSTInfo-Determine-Time-8f7f9f91" target="_blank"&gt;Script Center Repository&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I invite you to follow me on &lt;a href="http://bit.ly/scriptingguystwitter" target="_blank"&gt;Twitter&lt;/a&gt; and &lt;a href="http://bit.ly/scriptingguysfacebook" target="_blank"&gt;Facebook&lt;/a&gt;. If you have any questions, send email to me at &lt;a href="mailto:scripter@microsoft.com" target="_blank"&gt;scripter@microsoft.com&lt;/a&gt;, or post your questions on the &lt;a href="http://bit.ly/scriptingforum" target="_blank"&gt;Official Scripting Guys Forum&lt;/a&gt;. See you tomorrow. Until then, peace.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Ed Wilson, Microsoft Scripting Guy&lt;/b&gt;&amp;nbsp;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.technet.com/aggbug.aspx?PostID=3480925" width="1" height="1" alt="" /&gt;</description></item></channel></rss>