Skip to content

Commit ab066c4

Browse files
committed
Added TimeZone Resource
1 parent 134208a commit ab066c4

17 files changed

Lines changed: 1051 additions & 12 deletions

File tree

.vscode/RunAllTests.ps1

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
[string] $repoRoot = Split-Path -Path (Split-Path -Path $Script:MyInvocation.MyCommand.Path)
2-
if ( (-not (Test-Path -Path (Join-Path -Path $repoRoot -ChildPath 'DSCResource.Tests'))) -or `
3-
(-not (Test-Path -Path (Join-Path -Path $repoRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1'))) )
1+
$repoRoot = Split-Path -Path (Split-Path -Path $Script:MyInvocation.MyCommand.Path)
2+
$dscResourceTestsPath = Join-Path -Path $repoRoot -ChildPath '\Modules\ComputerManagementDsc\DSCResource.Tests\'
3+
4+
if ((-not (Test-Path -Path $dscResourceTestsPath)) -or `
5+
(-not (Test-Path -Path (Join-Path -Path $dscResourceTestsPath -ChildPath 'TestHelper.psm1'))))
46
{
5-
& git @('clone','https://github.com/PowerShell/DscResource.Tests.git',(Join-Path -Path $repoRoot -ChildPath '\DSCResource.Tests\'))
7+
& git @('clone', 'https://github.com/PowerShell/DscResource.Tests.git', $dscResourceTestsPath)
68
}
79

8-
Import-Module (Join-Path $PSScriptRoot "..\Tests\TestHarness.psm1" -Resolve)
9-
$dscTestsPath = Join-Path -Path $PSScriptRoot `
10-
-ChildPath "..\Modules\ComputerManagementDsc\DscResource.Tests\Meta.Tests.ps1"
10+
Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath '..\Tests\TestHarness.psm1' -Resolve)
11+
$dscTestsPath = Join-Path -Path $dscResourceTestsPath -ChildPath 'Meta.Tests.ps1'
1112
Invoke-TestHarness -DscTestsPath $dscTestsPath

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## Unreleased
44

5+
- TimeZone:
6+
- Migrated xTimeZone resource from [xTimeZone](https://github.com/PowerShell/xTimeZone)
7+
and renamed to TimeZone - fixes [Issue #157](https://github.com/PowerShell/ComputerManagementDsc/issues/157).
8+
59
## 5.0.0.0
610

711
- BREAKING CHANGE:
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
$modulePath = Join-Path -Path (Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent) -ChildPath 'Modules'
2+
3+
# Import the ComputerManagementDsc Common Modules
4+
Import-Module -Name (Join-Path -Path $modulePath `
5+
-ChildPath (Join-Path -Path 'ComputerManagementDsc.Common' `
6+
-ChildPath 'ComputerManagementDsc.Common.psm1'))
7+
8+
# Import the ComputerManagementDsc Resource Helper Module
9+
Import-Module -Name (Join-Path -Path $modulePath `
10+
-ChildPath (Join-Path -Path 'ComputerManagementDsc.ResourceHelper' `
11+
-ChildPath 'ComputerManagementDsc.ResourceHelper.psm1'))
12+
13+
# Import Localization Strings.
14+
$LocalizedData = Get-LocalizedData `
15+
-ResourceName 'MSFT_TimeZone' `
16+
-ResourcePath (Split-Path -Parent $script:MyInvocation.MyCommand.Path)
17+
18+
<#
19+
.SYNOPSIS
20+
Returns the current time zone of the node.
21+
22+
.PARAMETER IsSingleInstance
23+
Specifies the resource is a single instance, the value must be 'Yes'.
24+
25+
.PARAMETER TimeZone
26+
Specifies the time zone.
27+
#>
28+
function Get-TargetResource
29+
{
30+
[CmdletBinding()]
31+
[OutputType([Hashtable])]
32+
param
33+
(
34+
[Parameter(Mandatory = $true)]
35+
[ValidateSet('Yes')]
36+
[System.String]
37+
$IsSingleInstance,
38+
39+
[Parameter(Mandatory = $true)]
40+
[ValidateNotNullOrEmpty()]
41+
[System.String]
42+
$TimeZone
43+
)
44+
45+
Write-Verbose -Message ($LocalizedData.GettingTimeZoneMessage)
46+
47+
# Get the current time zone Id.
48+
$currentTimeZone = Get-TimeZoneId
49+
50+
$returnValue = @{
51+
IsSingleInstance = 'Yes'
52+
TimeZone = $currentTimeZone
53+
}
54+
55+
# Output the target resource.
56+
return $returnValue
57+
}
58+
59+
<#
60+
.SYNOPSIS
61+
Sets the current time zone of the node.
62+
63+
.PARAMETER IsSingleInstance
64+
Specifies the resource is a single instance, the value must be 'Yes'.
65+
66+
.PARAMETER TimeZone
67+
Specifies the time zone.
68+
#>
69+
function Set-TargetResource
70+
{
71+
[CmdletBinding()]
72+
param
73+
(
74+
[Parameter(Mandatory = $true)]
75+
[ValidateSet('Yes')]
76+
[System.String]
77+
$IsSingleInstance,
78+
79+
[Parameter(Mandatory = $true)]
80+
[ValidateNotNullOrEmpty()]
81+
[System.String]
82+
$TimeZone
83+
)
84+
85+
$currentTimeZone = Get-TimeZoneId
86+
87+
if ($currentTimeZone -ne $TimeZone)
88+
{
89+
Write-Verbose -Message ($LocalizedData.SettingTimeZoneMessage)
90+
Set-TimeZoneId -TimeZone $TimeZone
91+
}
92+
else
93+
{
94+
Write-Verbose -Message ($LocalizedData.TimeZoneAlreadySetMessage `
95+
-f $TimeZone)
96+
}
97+
}
98+
99+
<#
100+
.SYNOPSIS
101+
Tests the current time zone of the node.
102+
103+
.PARAMETER IsSingleInstance
104+
Specifies the resource is a single instance, the value must be 'Yes'.
105+
106+
.PARAMETER TimeZone
107+
Specifies the time zone.
108+
#>
109+
function Test-TargetResource
110+
{
111+
[CmdletBinding()]
112+
[OutputType([System.Boolean])]
113+
param
114+
(
115+
[Parameter(Mandatory = $true)]
116+
[ValidateSet('Yes')]
117+
[System.String]
118+
$IsSingleInstance,
119+
120+
[Parameter(Mandatory = $true)]
121+
[ValidateNotNullOrEmpty()]
122+
[System.String]
123+
$TimeZone
124+
)
125+
126+
Write-Verbose -Message ($LocalizedData.TestingTimeZoneMessage)
127+
128+
return Test-TimeZoneId -TimeZoneId $TimeZone
129+
}
130+
131+
Export-ModuleMember -Function *-TargetResource
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[ClassVersion("1.0.0.0"), FriendlyName("TimeZone")]
2+
class MSFT_TimeZone : OMI_BaseResource
3+
{
4+
[Key, Description("Specifies the resource is a single instance, the value must be 'Yes'."), ValueMap{"Yes"}, Values{"Yes"}] String IsSingleInstance;
5+
[Required, Description("Specifies the TimeZone.")] String TimeZone;
6+
};
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Description
2+
3+
The resource will use the `Get-TimeZone` cmdlet to get the current
4+
time zone. If `Get-TimeZone` is not available them CIM will be used to retrieve
5+
the current time zone. To update the time zone, .NET reflection will be used to
6+
update the time zone if required. If .NET reflection is not supported on the node
7+
(in the case of Nano Server) then tzutil.exe will be used to set the time zone.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# culture="en-US"
2+
ConvertFrom-StringData -StringData @'
3+
GettingTimeZoneMessage = Getting the time zone.
4+
ReplaceSystemTimeZoneMessage = Replace the System time zone.
5+
SettingTimeZoneMessage = Setting the time zone.
6+
TimeZoneAlreadySetMessage = Time zone already set to {0}.
7+
TestingTimeZoneMessage = Testing the time zone.
8+
'@
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<#
2+
.EXAMPLE
3+
This example sets the current time zone on the node
4+
to 'Tonga Standard Time'.
5+
#>
6+
Configuration Example
7+
{
8+
param
9+
(
10+
[Parameter()]
11+
[System.String[]]
12+
$NodeName = 'localhost'
13+
)
14+
15+
Import-DSCResource -ModuleName ComputerManagementDsc
16+
17+
Node $NodeName
18+
{
19+
TimeZone TimeZoneExample
20+
{
21+
IsSingleInstance = 'Yes'
22+
TimeZone = 'Tonga Standard Time'
23+
}
24+
}
25+
}

Modules/ComputerManagementDsc/Modules/ComputerManagementDsc.Common/ComputerManagementDsc.Common.psm1

Lines changed: 150 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,155 @@ function Test-DscObjectHasProperty
310310
return $false
311311
}
312312

313+
<#
314+
.SYNOPSIS
315+
Get the of the current time zone Id.
316+
#>
317+
function Get-TimeZoneId
318+
{
319+
[CmdletBinding()]
320+
param
321+
(
322+
)
323+
324+
if (Test-Command -Name 'Get-TimeZone' -Module 'Microsoft.PowerShell.Management')
325+
{
326+
Write-Verbose -Message ($LocalizedData.GettingTimeZoneMessage -f 'Cmdlets')
327+
328+
$timeZone = (Get-TimeZone).StandardName
329+
}
330+
else
331+
{
332+
Write-Verbose -Message ($LocalizedData.GettingTimeZoneMessage -f 'CIM')
333+
334+
$timeZone = (Get-CimInstance `
335+
-ClassName Win32_TimeZone `
336+
-Namespace root\cimv2).StandardName
337+
}
338+
339+
Write-Verbose -Message ($LocalizedData.CurrentTimeZoneMessage `
340+
-f $timeZone)
341+
342+
$timeZoneInfo = [System.TimeZoneInfo]::GetSystemTimeZones() |
343+
Where-Object StandardName -eq $timeZone
344+
345+
return $timeZoneInfo.Id
346+
} # function Get-TimeZoneId
347+
348+
<#
349+
.SYNOPSIS
350+
Compare a time zone Id with the current time zone Id.
351+
352+
.PARAMETER TimeZoneId
353+
The Id of the time zone to compare with the current time zone.
354+
#>
355+
function Test-TimeZoneId
356+
{
357+
[CmdletBinding()]
358+
param
359+
(
360+
[Parameter(Mandatory = $true)]
361+
[System.String]
362+
$TimeZoneId
363+
)
364+
365+
# Test if the expected value is the same as the current value.
366+
$currentTimeZoneId = Get-TimeZoneId
367+
368+
return $TimeZoneId -eq $currentTimeZoneId
369+
} # function Test-TimeZoneId
370+
371+
<#
372+
.SYNOPSIS
373+
Sets the current time zone using a time zone Id.
374+
375+
.PARAMETER TimeZoneId
376+
The Id of the time zone to set.
377+
#>
378+
function Set-TimeZoneId
379+
{
380+
[CmdletBinding()]
381+
param
382+
(
383+
[Parameter(Mandatory = $true)]
384+
[System.String]
385+
$TimeZoneId
386+
)
387+
388+
if (Test-Command -Name 'Set-TimeZone' -Module 'Microsoft.PowerShell.Management')
389+
{
390+
Set-TimeZone -Id $TimeZoneId
391+
}
392+
else
393+
{
394+
if (Test-Command -Name 'Add-Type' -Module 'Microsoft.Powershell.Utility')
395+
{
396+
# We can use reflection to modify the time zone.
397+
Write-Verbose -Message ($LocalizedData.SettingTimeZoneMessage `
398+
-f $TimeZoneId, '.NET')
399+
400+
Set-TimeZoneUsingDotNet -TimeZoneId $TimeZoneId
401+
}
402+
else
403+
{
404+
# For anything else use TZUTIL.EXE.
405+
Write-Verbose -Message ($LocalizedData.SettingTimeZoneMessage `
406+
-f $TimeZoneId, 'TZUTIL.EXE')
407+
408+
try
409+
{
410+
& tzutil.exe @('/s', $TimeZoneId)
411+
}
412+
catch
413+
{
414+
Write-Verbose -Message $_.Exception.Message
415+
} # try
416+
} # if
417+
} # if
418+
419+
Write-Verbose -Message ($LocalizedData.TimeZoneUpdatedMessage `
420+
-f $TimeZoneId)
421+
} # function Set-TimeZoneId
422+
423+
<#
424+
.SYNOPSIS
425+
This function sets the time zone on the machine using .NET reflection.
426+
It exists so that the ::Set method can be mocked by Pester.
427+
428+
.PARAMETER TimeZoneId
429+
The Id of the time zone to set using .NET.
430+
#>
431+
function Set-TimeZoneUsingDotNet
432+
{
433+
[CmdletBinding()]
434+
param
435+
(
436+
[Parameter(Mandatory = $true)]
437+
[System.String]
438+
$TimeZoneId
439+
)
440+
441+
# Add the [TimeZoneHelper.TimeZone] type if it is not defined.
442+
if (-not ([System.Management.Automation.PSTypeName] 'TimeZoneHelper.TimeZone').Type)
443+
{
444+
Write-Verbose -Message ($LocalizedData.AddingSetTimeZoneDotNetTypeMessage)
445+
446+
$setTimeZoneCs = Get-Content `
447+
-Path (Join-Path -Path $PSScriptRoot -ChildPath 'SetTimeZone.cs') `
448+
-Raw
449+
450+
Add-Type `
451+
-Language CSharp `
452+
-TypeDefinition $setTimeZoneCs
453+
} # if
454+
455+
[Microsoft.PowerShell.TimeZone.TimeZone]::Set($TimeZoneId)
456+
} # function Set-TimeZoneUsingDotNet
457+
313458
Export-ModuleMember -Function `
314459
Test-DscParameterState, `
315-
Test-DscObjectHasProperty
460+
Test-DscObjectHasProperty, `
461+
Get-TimeZoneId, `
462+
Test-TimeZoneId, `
463+
Set-TimeZoneId, `
464+
Set-TimeZoneUsingDotNet

0 commit comments

Comments
 (0)