Summary: Microsoft Scripting Guy, Ed Wilson, talks about using Windows PowerShell Desired State Configuration to set the time zone.
Microsoft Scripting Guy, Ed Wilson, is here. The weather is cold today in Charlotte. Not exactly freezing, but cold enough to merit a nice cup of English Breakfast tea with bits of strawberry leaf, blueberry leaf, orange peel, hibiscus flower, and a cinnamon stick. The tea is robust, and it goes well with homemade blueberry scones. It is a good morning for reflection.
Believe it or not, time zone configuration is still an issue at times. Especially, when it comes to correlating logging, or running scheduled tasks or other such activities. What normally happens is that the person configuring the server simply forgets to change the time zone from the default setting (-8 GMT). Everything continues to hum along swimmingly until...boom!—it does not. Luckily, the Wave 9 release of the Desired State Configuration Resource Kit contains a TimeZone resource.
But before I get into that, I want to first do a little checking up on the time zone settings.
Working with time zones in Windows PowerShell
In my life as a computer professional, dates and times have been a continuous issue for me. I can reach all the way back to the days when a date was stored in three fields in a database, and a time was stored in three more fields. Creating a report mandated munging together these six fields.
Sure, the creation of a DateTime object has been great. Sure, using the Get-Date cmdlet is easy and wonderful. However, there are still issues and challenges. Time zones, for one, are a major pain— well, not a major pain, but they are not as simple as they could be.
Let's start with checking the date. It would appear that all I need to do is to run the Get-Date cmdlet in a remote session, and everything is groovy. So first I query Active Directory and return a list of computers. Next, I use the Invoke-Command cmdlet to run the Get-Date cmdlet inside a script block. Everything is easy, everything is good. Here is the command and the results:
PS C:\> $cn = (Get-ADComputer -Filter *).name
PS C:\> icm -cn $cn {get-date}
Monday, January 5, 2015 1:52:02 PM
Monday, January 5, 2015 1:52:02 PM
Monday, January 5, 2015 1:52:02 PM
Monday, January 5, 2015 1:52:02 PM
Hmmm…I know that at least one of the remote servers is set to the wrong time zone, but using Get-Date does not tell me that. Why? Well, Windows PowerShell is smart. In this case, it is too smart because it automatically displays a remote date and time by using the local time zone information. Most of the time, this is great. Just not today.
The next thing I do is use Windows PowerShell remoting to figure out what time zones the remote servers are configured with. I call use the TimeZoneInfo .NET Framework class. Following is the command I use. (Note that I collected my server names in the previous command, and I stored the names in the $cn variable. This permits me to reuse it easily.)
PS C:\> icm -cn $cn -ScriptBlock {[timezoneinfo]::Local.DisplayName}
(UTC-08:00) Pacific Time (US & Canada)
(UTC-05:00) Eastern Time (US & Canada)
(UTC-05:00) Eastern Time (US & Canada)
(UTC-05:00) Eastern Time (US & Canada)
Ah! I see that one of my servers is in fact set to Pacific Time. If I want to get the actual display time from the remote servers, I need to use an old-fashioned command. To run this command, I am going to use the Invoke-Expression cmdlet. Invoke-Expression is not recommended because it can have unintended consequences if one is not careful.
Invoke-Expression executes a string as if it were a command. This is actually great for what I am about to do (which is a real left handed way of doing things such as creating a cmd prompt, and then running the time command). There is almost never a good reason for doing this.
Note The Windows PowerShell team has a blog post titled Invoke-Expression considered harmful.
PS C:\> icm -cn $cn -ScriptBlock {Invoke-Expression "cmd.exe /c time /t"}
11:01 AM
02:01 PM
02:01 PM
02:01 PM
The DSC TimeZone resource
Enter the world of Windows PowerShell Desired State Configuration (DSC). By using the TimeZone resource, I can create a simple script to set the time zone configuration on my local and remote servers easily.
Note Today’s script follows the same pattern as the SetPowerShellExecutionPolicy.ps1 script I wrote for yesterday’s post,
Use DSC Resource to Configure PowerShell Execution Policy. Refer to that post for more details.
The first thing I do is use the Configurationkeyword and specify a name for my configuration. I next define a couple of parameters—one for the computers (target nodes) and one for the specific time-zone name. I then specifically import the DSC resource. This is required because the xTimeZone DSC resource is not a standard resource, and therefore, it is not automatically loaded. Here is that portion of the script:
#Requires -version 4.0
Configuration SetTimeZone
{
Param
(
#Target nodes to apply the configuration
[String[]]$NodeName = ((Get-ADComputer -Filter *).name),
[Parameter(Mandatory = $true)]
[ValidateNotNullorEmpty()]
[String]$SystemTimeZone
)
Import-DSCResource -ModuleName xTimeZone
Now I define my action for each node. To do this, I use the Nodekeyword, and I specify the computer names stored in the $NodeName variable that I populated with my Get-ADComputer command. I call the xTimeZone resource, and I specify my time zone. Here is that portion of the code:
Node $NodeName
{
xTimeZone TimeZoneExample
{
TimeZone = $SystemTimeZone
}
}
I call the configuration and specify an output path for the creation of the MOF files. I also specify my time zone via the SystemTimeparameter. This value takes the ID property that is available from the following command:
[timezoneinfo]::Local.id
If I want an enumeration of all of the available time zones, I can use GetSystemTimeZonesstatic method as shown here:
[timezoneinfo]::GetSystemTimeZones().id
The last thing I need to do is to start the actual DSC configuration. To do this, I use the Start-DSCConfiguration cmdlet while specifying the path that I used earlier in addition to a few parameters:
SetTimeZone -output C:\serverConfig -SystemTime "Eastern Standard Time"
Start-DscConfiguration -Path C:\serverConfig -Wait -Force -Verbose
The complete configuration script is shown here:
#Requires -version 4.0
Configuration SetTimeZone
{
Param
(
#Target nodes to apply the configuration
[String[]]$NodeName = ((Get-ADComputer -Filter *).name),
[Parameter(Mandatory = $true)]
[ValidateNotNullorEmpty()]
[String]$SystemTimeZone
)
Import-DSCResource -ModuleName xTimeZone
Node $NodeName
{
xTimeZone TimeZoneExample
{
TimeZone = $SystemTimeZone
}
}
}
SetTimeZone -output C:\serverConfig -SystemTime "Eastern Standard Time"
Start-DscConfiguration -Path C:\serverConfig -Wait -Force -Verbose
As shown here, when I run the script for the first time, a series of progress bars appear that state it is initializing the resource:
As the script runs, the verbose output lets me know what is going on with the script. This is shown here:
The last thing I do is press the UP arrow to see if my times are now correct. Here are the results:
That is all there is to using Windows PowerShell DSC to set the time zone. DSC Week will continue tomorrow when I will talk about more way cool stuff.
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