-
Notifications
You must be signed in to change notification settings - Fork 17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Windows settings time #109
base: main
Are you sure you want to change the base?
Changes from 7 commits
81541a8
b180c3a
51c2b63
83eb7a0
55b01a5
c3847d2
251d306
0f14f9d
b8dd560
323d624
0ba034a
7ba6a69
81b64c3
059b59b
4541bb4
102c763
657e834
5a06f85
f8270f7
5f01ee2
514868e
d74c29d
506a589
38a6184
e7a48b8
e609482
48877f6
41914ef
ba97203
8e9c8c5
d816dc3
cd15ed2
6370252
8539290
fec4047
3bf7d6b
7ed2c35
e7262b2
6dfddf3
8d8ad12
1fbe8e2
872574f
70fae5c
77cc13d
5210dd3
a00ec3e
35badfd
b115da5
ab6e2ec
85e10ef
7644177
ea7b053
fb5339a
cc1637b
671a63e
213d157
5084eb4
007bc8d
bccf27c
7d2ae98
3548d89
c2dad49
b5f0eb8
867a6b4
d31ed8a
0ac57aa
8b35f25
40ebfeb
44d7ecd
edb3dcc
8c48fd3
65bbcf1
7af2f5e
c83e56a
3f47882
0e02a99
1c6bc34
02fba3f
9be2b65
7be6cfe
96ce50d
1e577ee
75cbef0
9d0b2d0
503fe50
18feb8e
94613f4
686088e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -67,4 +67,4 @@ websites | |
wekyb | ||
Hmmss | ||
MMdd | ||
MMdd | ||
immersivecontrolpanel |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
if ([string]::IsNullOrEmpty($env:TestRegistryPath)) { | ||
$global:tzAutoUpdatePath = 'HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Parameters' | ||
$global:tzAutoUpdatePath = 'HKLM:\System\CurrentControlSet\Services\tzautoupdate' | ||
$global:w32TimePath = 'HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Parameters' | ||
$global:timeZoneInformationPath = 'HKLM:\SYSTEM\CurrentControlSet\Control\TimeZoneInformation' | ||
} else { | ||
$global:tzAutoUpdatePath = $global:timeZoneInformationPath = $env:TestRegistryPath | ||
|
@@ -39,57 +40,107 @@ function Get-ValidTimeZone { | |
|
||
return $timeZoneId | ||
} | ||
|
||
function Test-LocationSettingPermission { | ||
# On Windows 11, the HKLM is the location services, whereas HKCU is the Let apps access your location setting | ||
$registryKeys = @('HKLM:\Software\Microsoft\Windows\CurrentVersion\CapabilityAccessManager\ConsentStore\location', 'HKCU:\Software\Microsoft\Windows\CurrentVersion\CapabilityAccessManager\ConsentStore\location', 'HKCU:\Software\Microsoft\Windows\CurrentVersion\CapabilityAccessManager\ConsentStore\location\windows.immersivecontrolpanel*') | ||
foreach ($key in $registryKeys) { | ||
$res = Get-ItemProperty -Path $key -Name 'Value' -ErrorAction SilentlyContinue | ||
if ($res.Value -ne 'Allow') { | ||
# TODO: Or should we throw an error? | ||
return $false | ||
} | ||
} | ||
|
||
return $true | ||
} | ||
|
||
function Set-DaylightSavingTime { | ||
param ( | ||
[Parameter()] | ||
[switch] $Enable, | ||
|
||
[Parameter(Mandatory = $true)] | ||
[string] $Id | ||
) | ||
|
||
$command = $Enable.IsPresent ? ('tzutil /s "{0}"' -f $Id) : ('tzutil /s "{0}_dstoff"' -f $Id) | ||
|
||
Invoke-Expression -Command $command | ||
|
||
if ($LASTEXITCODE -ne 0) { | ||
throw [System.Configuration.ConfigurationException]::new("Failed to set daylight saving time. Error: $LASTEXITCODE") | ||
} | ||
} | ||
#endRegion Functions | ||
|
||
#region Classes | ||
<# | ||
.SYNOPSIS | ||
This `Time` DSC Resource allows you to manage the time zone, automatic time zone update, and system tray date/time visibility settings on a Windows machine. | ||
This `TimeZone` DSC Resource allows you to manage the time zone, automatic time zone update, and system tray date/time visibility settings on a Windows machine. | ||
|
||
.DESCRIPTION | ||
This `Time` DSC Resource allows you to manage the time zone, automatic time zone update, and system tray date/time visibility settings on a Windows machine. | ||
This `TimeZone` DSC Resource allows you to manage the time zone, automatic time zone update, and system tray date/time visibility settings on a Windows machine. | ||
|
||
.PARAMETER TimeZone | ||
The time zone to set on the machine. The value should be a valid time zone ID from the list of time zones (Get-TimeZone -ListAvailable).Id. The default value is the current time zone. | ||
.PARAMETER Id | ||
The Id to set on the machine. The value should be a valid time zone ID from the list of time zones (Get-TimeZone -ListAvailable).Id. The default value is the current time zone. | ||
|
||
.PARAMETER SetTimeZoneAutomatically | ||
Whether to set the time zone automatically. The value should be a boolean. You can find the setting in `Settings -> Time & Language -> Date & Time -> Set time automatically. | ||
Whether to set the time zone automatically. The value should be a boolean. You can find the setting in `Settings -> Time & Language -> Date & Time -> Set time zone automatically. | ||
|
||
.PARAMETER SetTimeAutomatically | ||
Whether to set the time automatically. The value should be a boolean. You can find the setting in `Settings -> Time & Language -> Date & Time -> Set time automatically. | ||
|
||
.PARAMETER AdjustForDaylightSaving | ||
Whether to adjust for daylight saving time. The value should be a boolean. You can find the setting in `Settings -> Time & Language -> Date & Time -> Adjust for daylight saving time automatically. | ||
|
||
.EXAMPLE | ||
PS C:\> Invoke-DscResource -Name Time -Method Set -Property @{ TimeZone = "Pacific Standard Time"} | ||
PS C:\> Invoke-DscResource -Name TimeZone -ModuleName Microsoft.Windows.Setting.Time -Method Set -Property @{ TimeZone = "Pacific Standard Time"} | ||
|
||
This example sets the time zone to Pacific Standard Time. | ||
|
||
.EXAMPLE | ||
PS C:\> Invoke-DscResource -Name Time -Method Get -Property {} | ||
PS C:\> Invoke-DscResource -Name TimeZone -ModuleName Microsoft.Windows.Setting.Time -Method Get -Property {} | ||
|
||
This example gets the current time settings on the machine. | ||
#> | ||
[DscResource()] | ||
class TimeZone { | ||
[DscProperty(Key)] | ||
[string] $TimeZone | ||
[string] $Id | ||
|
||
[DscProperty()] | ||
[nullable[bool]] $SetTimeZoneAutomatically | ||
|
||
[DscProperty()] | ||
[nullable[bool]] $SetTimeAutomatically | ||
|
||
Comment on lines
+115
to
+117
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How do you feel about moving things like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's precisely why I did it into one DSC resource. They bite each other. Even if we separate them both, we have to check what options are already enabled in both cases. If we set the Timezone to automatically, we might need to clean up the manual settings and vice versa. |
||
[DscProperty()] | ||
[nullable[bool]] $AdjustForDaylightSaving | ||
|
||
static hidden [string] $SetTimeZoneAutomaticallyProperty = 'Type' | ||
static hidden [string] $SetTimeZoneAutomaticallyProperty = 'Start' | ||
static hidden [string] $SetTimeAutomaticallyProperty = 'Type' | ||
static hidden [string] $AdjustForDaylightSavingProperty = 'DynamicDaylightTimeDisabled' | ||
static hidden [string] $NtpEnabled = 'NTP' | ||
static hidden [string] $NtpDisabled = 'NoSync' | ||
static hidden [bool] $SupportsDaylightSavingProperty = $false | ||
Gijsreyn marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
TimeZone() { | ||
$this.TimeZone = (Get-TimeZone).Id | ||
$timeZone = Get-TimeZone | ||
$this.Id = $timeZone.Id | ||
|
||
# We set the SupportsDaylightSavingProperty to the value that is supported by the current time zone | ||
[TimeZone]::SupportsDaylightSavingProperty = $timeZone.SupportsDaylightSavingTime ? $true : $false | ||
Gijsreyn marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
[TimeZone] Get() { | ||
$currentState = [TimeZone]::new() | ||
$currentState.SetTimeZoneAutomatically = [TimeZone]::GetTimeZoneAutoUpdateStatus() | ||
$currentState.AdjustForDaylightSaving = [TimeZone]::GetDayLightSavingStatus() | ||
$currentState.SetTimeZoneAutomatically = [TimeZone]::GetTimeZoneAutomaticallyStatus() | ||
$currentState.SetTimeAutomatically = [TimeZone]::GetTimeAutomaticallyStatus() | ||
|
||
if ($currentState::SupportsDaylightSavingProperty) { | ||
$currentState.AdjustForDaylightSaving = [TimeZone]::GetDayLightSavingStatus() | ||
} | ||
Comment on lines
+143
to
+145
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This value is not dependent on whether the time zone supports daylight saving or not. This should just be a registry check. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I didn't totally get you here. It is checking the registry with the |
||
|
||
return $currentState | ||
} | ||
|
@@ -101,55 +152,82 @@ class TimeZone { | |
|
||
$currentState = $this.Get() | ||
|
||
if ($currentState.TimeZone -ne $this.TimeZone) { | ||
Set-TimeZone -Id (Get-ValidTimeZone -TimeZone $this.TimeZone) | ||
if ($currentState.Id -ne $this.Id) { | ||
Set-TimeZone -Id (Get-ValidTimeZone -TimeZone $this.Id) | ||
} | ||
|
||
if (($null -ne $this.SetTimeZoneAutomatically) -and ($this.SetTimeZoneAutomatically -ne $currentState.SetTimeZoneAutomatically)) { | ||
$desiredState = $this.SetTimeZoneAutomatically ? 3 : 4 | ||
|
||
if (Test-LocationSettingPermission) { | ||
Set-ItemProperty -Path $global:tzAutoUpdatePath -Name ([TimeZone]::SetTimeZoneAutomaticallyProperty) -Value $desiredState | ||
} | ||
} | ||
|
||
if ($currentState.SetTimeZoneAutomatically -ne $this.SetTimeZoneAutomatically) { | ||
if (($null -ne $this.SetTimeAutomatically) -and ($this.SetTimeAutomatically -ne $currentState.SetTimeAutomatically)) { | ||
$desiredState = $this.SetTimeAutomatically ? [TimeZone]::NtpEnabled : [TimeZone]::NtpDisabled | ||
|
||
Set-ItemProperty -Path $global:tzAutoUpdatePath -Name ([TimeZone]::SetTimeZoneAutomaticallyProperty) -Value $desiredState | ||
Set-ItemProperty -Path $global:w32TimePath -Name ([TimeZone]::SetTimeAutomaticallyProperty) -Value $desiredState | ||
} | ||
|
||
if ($currentState.AdjustForDaylightSaving -ne $this.AdjustForDaylightSaving) { | ||
$desiredState = $this.AdjustForDaylightSaving ? 0 : 1 | ||
if ($currentState::SupportsDaylightSavingProperty -and ($null -ne $this.AdjustForDaylightSaving)) { | ||
$desiredState = @{ | ||
Id = $this.Id | ||
Enable = $this.AdjustForDaylightSaving | ||
} | ||
|
||
Set-ItemProperty -Path $global:timeZoneInformationPath -Name ([TimeZone]::AdjustForDaylightSavingProperty) -Value $desiredState | ||
if ($this.AdjustForDaylightSaving -ne $currentState.AdjustForDaylightSaving) { | ||
# Falling back on tzutil because the amount of registry keys to modify is too high, plus difficult to determine which one to modify | ||
Set-DaylightSavingTime @desiredState | ||
} | ||
Gijsreyn marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
} | ||
|
||
[bool] Test() { | ||
$currentState = $this.Get() | ||
|
||
if (($null -ne $this.TimeZone) -and ($this.TimeZone -ne $currentState.TimeZone)) { | ||
if ($this.Id -ne $currentState.Id) { | ||
return $false | ||
} | ||
|
||
if (($null -ne $this.SetTimeZoneAutomatically) -and ($this.SetTimeZoneAutomatically -ne $currentState.SetTimeZoneAutomatically)) { | ||
return $false | ||
} | ||
|
||
if (($null -ne $this.AdjustForDaylightSaving) -and ($this.AdjustForDaylightSaving -ne $currentState.AdjustForDaylightSaving)) { | ||
if (($null -ne $this.SetTimeAutomatically) -and ($this.SetTimeAutomatically -ne $currentState.SetTimeAutomatically)) { | ||
return $false | ||
} | ||
|
||
if ($currentState::SupportsDaylightSavingProperty -and ($null -ne $this.AdjustForDaylightSaving)) { | ||
if ($this.AdjustForDaylightSaving -ne $currentState.AdjustForDaylightSaving) { | ||
return $false | ||
} | ||
} | ||
|
||
return $true | ||
} | ||
|
||
#region Time helper functions | ||
static [bool] GetTimeZoneAutoUpdateStatus() { | ||
# key should actually always be present, but we'll check anyway | ||
#region TimeZone helper functions | ||
static [bool] GetTimeZoneAutomaticallyStatus() { | ||
$keyValue = TryGetRegistryValue -Key $global:tzAutoUpdatePath -Property ([TimeZone]::SetTimeZoneAutomaticallyProperty) | ||
if ($null -eq $keyValue) { | ||
return $true | ||
} else { | ||
return ($keyValue -eq 1) | ||
return ($keyValue -eq 3) | ||
} | ||
} | ||
|
||
static [bool] GetTimeAutomaticallyStatus() { | ||
$keyValue = TryGetRegistryValue -Key $global:w32TimePath -Property ([TimeZone]::SetTimeAutomaticallyProperty) | ||
if ($null -eq $keyValue) { | ||
return $true | ||
} else { | ||
return ($keyValue -eq 'NTP') | ||
} | ||
} | ||
|
||
static [bool] GetDayLightSavingStatus() { | ||
# key should actually always be present, but we'll check anyway | ||
$keyValue = TryGetRegistryValue -Key $global:timeZoneInformationPath -Property ([TimeZone]::SetTimeZoneAutomaticallyProperty) | ||
$keyValue = TryGetRegistryValue -Key $global:timeZoneInformationPath -Property ([TimeZone]::AdjustForDaylightSavingProperty) | ||
if ($null -eq $keyValue) { | ||
return $true | ||
} else { | ||
|
@@ -168,7 +246,7 @@ class TimeZone { | |
|
||
return $parameters | ||
} | ||
#endRegion Time helper functions | ||
#endRegion TimeZone helper functions | ||
} | ||
|
||
if ([string]::IsNullOrEmpty($env:TestRegistryPath)) { | ||
|
@@ -302,6 +380,7 @@ class Clock { | |
return $parameters | ||
} | ||
#endRegion Clock helper functions | ||
|
||
} | ||
#endRegion Classes | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel that this should be its own separate DSC resource for just Location. I'm sure there are other settings that will depend on this, not just Time/TimeZone.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Partially, as it affects the TimeZone setting, what's your suggestion? A custom module to inherit those functions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pardon my jumping into the conversation, but this felt like a good place. It seems to me that a number of these functions should be in a common PowerShell module (or similar location) of some kind. Maybe not so much this location permission testing function, as much as the Registry functions above. It seems like an unnecessary duplication to have every DSC resource independently define how to retrieve values from the Registry.
What do you think about some kind of centralization for common code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@stephengillie Welcome back and of course you can jump into the conversation. I fully agree with it. I think the DSC community does it also with the DscResource.Common module.
If you have any thoughts about implementing it, or want to have a discussion around it, I'm always open to it.