Overview
The Set-PowerTimeoutProfile
Syncro PowerShell function provides a structured way to configure and verify Windows power timeout settings for display, disk, sleep, and hibernate. Instead of manually running powercfg
commands or editing registry entries, this script applies a chosen profile, ensures the changes are in effect, and optionally manages hibernation itself.
This Syncro PowerShell script is especially useful for IT administrators, MSPs, and power users who need consistent, automated configuration of Windows power management policies across multiple systems.
Basic Functionality Flow
- Accept Parameters
-SleepOption
: Choose a predefined profile (Standard
,Disabled
, orCustom
).-CustomProfile
: Provide a hashtable with detailed timeout values ifCustom
is selected.-ManageHibernate
: Ensure hibernation is enabled/disabled depending on the profile.-PassThru
: Output a structured object with applied settings and verification results.-Strict
: Enforce strict mode, throwing an error if verification fails.
- Apply Power Profile
- Executes
powercfg
commands to set display, disk, sleep, and hibernate timeouts. - Applies settings for both AC (plugged in) and DC (on battery) modes.
- Executes
- Verify Settings
- Parses
powercfg /q
to confirm values were applied. - If parsing fails, falls back to reading registry indices (
ACSettingIndex
/DCSettingIndex
).
- Parses
- Return Results
- Displays or returns verification results.
- Throws if
-Strict
is used and mismatches are found.
Profiles
Standard
- AC (Plugged In):
- Display: 5 min
- Disk: 10 min
- Sleep: 60 min
- Hibernate: 120 min
- DC (Battery):
- Display: 5 min
- Disk: 10 min
- Sleep: 10 min
- Hibernate: 15 min
Disabled
- AC (Plugged In):
- Display: 5 min
- Disk: 10 min
- Sleep: Never
- Hibernate: Never
- DC (Battery):
- Display: 5 min
- Disk: 10 min
- Sleep: 10 min
- Hibernate: 10 min
Custom
Define your own profile using a hashtable:
$profile = @{
MonitorAC = 10
MonitorDC = 5
DiskAC = 20
DiskDC = 15
StandbyAC = 45
StandbyDC = 20
HibernateAC = 90
HibernateDC = 30
}
Set-PowerTimeoutProfile -SleepOption Custom -CustomProfile $profile -ManageHibernate -PassThru
How to Use
- Run with Default (Standard) Profile
Set-PowerTimeoutProfile
- Apply Disabled Profile
Set-PowerTimeoutProfile -SleepOption Disabled
- Use Custom Settings
Set-PowerTimeoutProfile -SleepOption Custom -CustomProfile $profile
- Return Results for Logging or Reporting
Set-PowerTimeoutProfile -PassThru
- Strict Mode Enforcement
Set-PowerTimeoutProfile -Strict
If verification fails, an error is thrown.
Additional Information
- Verification Logic: Settings are validated by parsing
powercfg /q
output. If that fails, registry values are used. All values are managed in seconds internally. - Hibernation Management: When
-ManageHibernate
is used, the script automatically enables or disables hibernation to match the profile’s intent. - Safety & Idempotence: The script won’t blindly overwrite—settings are confirmed after being applied.
- Supports Automation: Ideal for RMM, Intune, GPO replacements, or local administration scripts.
- Error Handling: With
-Strict
, administrators, can enforce compliance with configured policies.
The Code
function Set-PowerTimeoutProfile {
<#
.SYNOPSIS
Applies a sleep/hibernate/display/disk timeout profile using powercfg and verifies results.
.DESCRIPTION
Profiles:
- Standard: AC Display 5, Disk 10, Sleep 60, Hibernate 120 | DC Display 5, Disk 10, Sleep 10, Hibernate 15
- Disabled: AC Display 5, Disk 10, Sleep 0, Hibernate 0 | DC Display 5, Disk 10, Sleep 10, Hibernate 10
Verification is done by parsing 'powercfg /q' (active scheme dump). If that fails,
fall back to the registry (ACSettingIndex/DCSettingIndex). Note that these indices
are stored/reported in SECONDS for these settings.
.PARAMETER SleepOption
Standard, Disabled, or Custom (default Standard)
.PARAMETER CustomProfile
Hashtable/object with keys: MonitorAC, MonitorDC, DiskAC, DiskDC, StandbyAC, StandbyDC, HibernateAC, HibernateDC
.PARAMETER ManageHibernate
If set, toggles hibernation (powercfg -h on/off) to align with profile intent.
.PARAMETER PassThru
Return a structured result object.
.PARAMETER Strict
Throw if any verification step fails.
#>
[CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
param(
[ValidateSet('Standard','Disabled','Custom')]
[string]$SleepOption = 'Standard',
[hashtable]$CustomProfile,
[switch]$ManageHibernate,
[switch]$PassThru,
[switch]$Strict
)
# region Internal helpers (approved verbs)
function Get-PowerCfgPath {
$sys32 = Join-Path $env:WINDIR 'System32\powercfg.exe'
$sysnative = Join-Path $env:WINDIR 'Sysnative\powercfg.exe'
if (-not [Environment]::Is64BitProcess -and (Test-Path $sysnative)) { return $sysnative }
if (Test-Path $sys32) { return $sys32 }
if (Test-Path $sysnative) { return $sysnative }
throw "powercfg.exe not found. Cannot continue."
}
function Get-ActivePowerSchemeGuid {
param([Parameter(Mandatory)][string]$PowerCfg)
$out = & $PowerCfg -getactivescheme 2>&1 | Out-String
if ($LASTEXITCODE -ne 0) { throw "Failed to get active scheme. Output $out" }
$guidPattern = '\b[0-9A-Fa-f]{8}(?:-[0-9A-Fa-f]{4}){3}-[0-9A-Fa-f]{12}\b'
$m = [regex]::Match($out, $guidPattern)
if ($m.Success) { return "{0}{1}{2}" -f '{', $m.Value, '}' }
throw "Could not parse active scheme GUID from output $out"
}
function Get-PowerCfgDump {
<# Return the active scheme dump as an array of lines #>
param([Parameter(Mandatory)][string]$PowerCfg)
# Query active scheme without parameters (most reliable)
$text = & $PowerCfg /q 2>&1 | Out-String
if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($text)) {
# Try alternate switch form
$text = & $PowerCfg -q 2>&1 | Out-String
if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($text)) {
throw "powercfg /q failed. Output $text"
}
}
return ($text -split "`r?`n")
}
function Get-PowerCfgSettingIndexFromDump {
<# Parse /q dump for the Current AC/DC Power Setting Index (hex), return integer seconds #>
param(
[Parameter(Mandatory)][string[]]$DumpLines,
[Parameter(Mandatory)][string]$SubgroupGuid,
[Parameter(Mandatory)][string]$SettingGuid,
[Parameter(Mandatory)][ValidateSet('AC','DC')][string]$Source
)
$inSubgroup = $false
$inSetting = $false
$subPattern = "(?i)Subgroup\s+GUID:\s*\{?$([regex]::Escape($SubgroupGuid))\}?(\s|\Z)"
$setPattern = "(?i)Power\s+Setting\s+GUID:\s*\{?$([regex]::Escape($SettingGuid))\}?(\s|\Z)"
$idxPattern = if ($Source -eq 'AC') {
'(?i)Current\s+AC\s+Power\s+Setting\s+Index:\s*0x([0-9a-fA-F]+)'
} else {
'(?i)Current\s+DC\s+Power\s+Setting\s+Index:\s*0x([0-9a-fA-F]+)'
}
foreach ($line in $DumpLines) {
if (-not $inSubgroup) {
if ([regex]::IsMatch($line, $subPattern)) { $inSubgroup = $true; continue }
} elseif (-not $inSetting) {
if ([regex]::IsMatch($line, $setPattern)) { $inSetting = $true; continue }
if ($line -match '(?i)^Subgroup\s+GUID:') { $inSubgroup = $false }
} else {
$m = [regex]::Match($line, $idxPattern)
if ($m.Success) { return [Convert]::ToInt32($m.Groups[1].Value, 16) }
if ($line -match '(?i)^Subgroup\s+GUID:') { $inSubgroup = $false; $inSetting = $false }
}
}
throw "Could not parse current $Source index for $SettingGuid"
}
function Get-RegistrySettingIndex {
<# Registry fallback: AC/DC indices live under User\PowerSchemes\<scheme>\<sub>\<setting> #>
param(
[Parameter(Mandatory)][string]$SchemeGuidWithBraces,
[Parameter(Mandatory)][string]$SubgroupGuid,
[Parameter(Mandatory)][string]$SettingGuid,
[Parameter(Mandatory)][ValidateSet('AC','DC')][string]$Source
)
$schemeNoBraces = $SchemeGuidWithBraces.Trim('{}')
$path = "HKLM:\SYSTEM\CurrentControlSet\Control\Power\User\PowerSchemes\$schemeNoBraces\$SubgroupGuid\$SettingGuid"
$name = if ($Source -eq 'AC') { 'ACSettingIndex' } else { 'DCSettingIndex' }
try {
$val = Get-ItemPropertyValue -Path $path -Name $name -ErrorAction Stop
return [int]$val
} catch {
throw "Failed to read $name at $path. $_"
}
}
function Set-PowerCfgTimeoutValue {
param(
[Parameter(Mandatory)][string]$PowerCfg,
[Parameter(Mandatory)][ValidateSet('Monitor','Disk','Standby','Hibernate')][string]$Item,
[Parameter(Mandatory)][ValidateSet('AC','DC')][string]$Source,
[Parameter(Mandatory)][ValidateRange(0,1440)][int]$Minutes
)
$switchName = $script:Guids[$Item].Switch
$cmdArgs = @('-change', "-$switchName-$($Source.ToLower())", $Minutes)
Write-Verbose "Applying $Item on $Source to $Minutes minutes"
if ($PSCmdlet.ShouldProcess("$Item $Source","Set to $Minutes")) {
$null = & $PowerCfg @cmdArgs 2>&1
if ($LASTEXITCODE -ne 0) { throw "Failed to set $Item on $Source to $Minutes" }
}
}
function Write-VerificationResult {
param([string]$Name,[string]$Target,[int]$ExpectedSeconds,[int]$ActualSeconds,[bool]$Success)
if ($Success) {
Write-Host "[OK] $Name on $Target verified $ActualSeconds seconds" -ForegroundColor Green
} else {
Write-Host "[WARN] $Name on $Target expected $ExpectedSeconds seconds but found $ActualSeconds seconds" -ForegroundColor Yellow
}
}
# endregion Internal helpers
# region Startup checks and context
if ($env:SyncroModule) {
try {
Import-Module -Name $env:SyncroModule -ErrorAction Stop -DisableNameChecking
Write-Verbose "Imported Syncro module"
} catch {
Write-Verbose "Syncro module import failed. $_"
}
}
$isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
if (-not $isAdmin) { throw "This function must run elevated. Please re-run in an Administrator PowerShell." }
Write-Host "Setting Sleep option to: $SleepOption"
$powercfg = Get-PowerCfgPath
Write-Verbose "Using powercfg at $powercfg"
# Well-known GUIDs and units (seconds) per Microsoft docs:
# VIDEOIDLE (Display) -> 3c0bc021-c8a8-4e07-a973-6b14cbcb2b7e in SUB_VIDEO 7516b95f-f776-4464-8c53-06167f40cc99 (seconds)
# DISKIDLE (Disk) -> 6738e2c4-e8a5-4a42-b16a-e040e769756e in SUB_DISK 0012ee47-9041-4b5d-9b77-535fba8b1442 (seconds)
# STANDBYIDLE (Sleep timeout) -> 29f6c1db-86da-48c5-9fdb-f2b67b1f44da in SUB_SLEEP 238c9fa8-0aad-41ed-83f4-97be242c8f20 (seconds)
# HIBERNATEIDLE (Hibernate) -> 9d7815a6-7ee4-497e-8888-515a05f02364 in SUB_SLEEP 238c9fa8-0aad-41ed-83f4-97be242c8f20 (seconds)
# refs: Display idle, Disk idle, Sleep idle, Hibernate idle
$script:Guids = [ordered]@{
Monitor = @{ Subgroup = '7516b95f-f776-4464-8c53-06167f40cc99'; Setting = '3c0bc021-c8a8-4e07-a973-6b14cbcb2b7e'; Switch = 'monitor-timeout' } # [7](https://learn.microsoft.com/en-us/windows-hardware/customize/power-settings/display-settings)[2](https://learn.microsoft.com/en-us/windows-hardware/customize/power-settings/display-settings-display-idle-timeout)
Disk = @{ Subgroup = '0012ee47-9041-4b5d-9b77-535fba8b1442'; Setting = '6738e2c4-e8a5-4a42-b16a-e040e769756e'; Switch = 'disk-timeout' } # [3](https://learn.microsoft.com/en-us/windows-hardware/customize/power-settings/disk-settings-disk-idle-timeout)
Standby = @{ Subgroup = '238c9fa8-0aad-41ed-83f4-97be242c8f20'; Setting = '29f6c1db-86da-48c5-9fdb-f2b67b1f44da'; Switch = 'standby-timeout' } # [4](https://learn.microsoft.com/en-us/windows-hardware/customize/power-settings/sleep-settings-sleep-idle-timeout)
Hibernate = @{ Subgroup = '238c9fa8-0aad-41ed-83f4-97be242c8f20'; Setting = '9d7815a6-7ee4-497e-8888-515a05f02364'; Switch = 'hibernate-timeout' } # [5](https://learn.microsoft.com/en-us/windows-hardware/customize/power-settings/sleep-settings-hibernate-idle-timeout)
}
$timeoutProfile = switch ($SleepOption) {
'Standard' {
@{
Monitor = @{ AC = 5; DC = 5 }
Disk = @{ AC = 10; DC = 10 }
Standby = @{ AC = 60; DC = 10 }
Hibernate = @{ AC = 120; DC = 15 }
}
}
'Disabled' {
@{
Monitor = @{ AC = 5; DC = 5 }
Disk = @{ AC = 10; DC = 10 }
Standby = @{ AC = 0; DC = 10 }
Hibernate = @{ AC = 0; DC = 10 }
}
}
'Custom' {
if (-not $CustomProfile) { throw "CustomProfile must be provided for SleepOption Custom." }
@{
Monitor = @{ AC = $CustomProfile['MonitorAC']; DC = $CustomProfile['MonitorDC'] }
Disk = @{ AC = $CustomProfile['DiskAC']; DC = $CustomProfile['DiskDC'] }
Standby = @{ AC = $CustomProfile['StandbyAC']; DC = $CustomProfile['StandbyDC'] }
Hibernate = @{ AC = $CustomProfile['HibernateAC']; DC = $CustomProfile['HibernateDC'] }
}
}
}
$scheme = Get-ActivePowerSchemeGuid -PowerCfg $powercfg
Write-Verbose "Active power scheme is $scheme"
# Try to retrieve dump once; if it fails later, we refresh or fall back to registry
try { $dump = Get-PowerCfgDump -PowerCfg $powercfg } catch { $dump = @() }
# endregion
# region Apply & verify
$results = New-Object System.Collections.Generic.List[object]
$failList = New-Object System.Collections.Generic.List[string]
foreach ($item in $script:Guids.Keys) {
foreach ($src in @('AC','DC')) {
$expectedMinutes = $timeoutProfile[$item][$src]
$expectedSeconds = [int]($expectedMinutes * 60)
$registryPath = "HKLM:\SYSTEM\CurrentControlSet\Control\Power\User\PowerSchemes\$($scheme.Trim('{}'))\$($script:Guids[$item].Subgroup)\$($script:Guids[$item].Setting)"
$tolerance = 60 # seconds
try {
Set-PowerCfgTimeoutValue -PowerCfg $powercfg -Item $item -Source $src -Minutes $expectedMinutes
# Refresh dump after change (best effort)
try { $dump = Get-PowerCfgDump -PowerCfg $powercfg } catch { $dump = @() }
$actualSeconds = $null
$verified = $false
# Primary verification via /q
if ($dump.Count -gt 0) {
try {
$actualSeconds = Get-PowerCfgSettingIndexFromDump -DumpLines $dump `
-SubgroupGuid $script:Guids[$item].Subgroup -SettingGuid $script:Guids[$item].Setting -Source $src
$verified = $true
} catch {
Write-Verbose "Dump parse failed for $item $src. Falling back to registry. $_"
}
}
# Registry fallback
if (-not $verified) {
$actualSeconds = Get-RegistrySettingIndex -SchemeGuidWithBraces $scheme `
-SubgroupGuid $script:Guids[$item].Subgroup -SettingGuid $script:Guids[$item].Setting -Source $src
Write-Verbose "Registry fallback for $item $src path $registryPath, value $actualSeconds, expected $expectedSeconds"
if ($item -eq 'Hibernate' -and $src -eq 'DC') {
Write-Verbose "Troubleshooting: If value is not matching, check for OEM power utilities, Fast Startup, or firmware restrictions."
}
}
# Fuzzy comparison: allow difference within tolerance
$ok = ([math]::Abs($actualSeconds - $expectedSeconds) -le $tolerance)
Write-VerificationResult -Name $item -Target $src -ExpectedSeconds $expectedSeconds -ActualSeconds $actualSeconds -Success $ok
if (-not $ok) { [void]$failList.Add("$item $src expected $expectedSeconds seconds found $actualSeconds seconds (tolerance $tolerance)") }
$results.Add([pscustomobject]@{
Item = $item
Source = $src
ExpectedMinutes = $expectedMinutes
ExpectedSeconds = $expectedSeconds
ActualSeconds = $actualSeconds
Match = $ok
Scheme = $scheme
}) | Out-Null
}
catch {
Write-Error "Error while setting $item on $src. $_"
[void]$failList.Add("$item $src error")
$results.Add([pscustomobject]@{
Item = $item
Source = $src
ExpectedMinutes = $expectedMinutes
ExpectedSeconds = $expectedSeconds
ActualSeconds = $null
Match = $false
Scheme = $scheme
Error = $_.Exception.Message
}) | Out-Null
}
}
}
# endregion
# region Optional hibernation alignment
if ($ManageHibernate.IsPresent) {
try {
$acHib = $timeoutProfile.Hibernate.AC
$dcHib = $timeoutProfile.Hibernate.DC
$shouldDisable = ($acHib -eq 0 -or $dcHib -eq 0)
$hibCmd = if ($shouldDisable) { 'off' } else { 'on' }
if ($PSCmdlet.ShouldProcess("Hibernation","Turn $hibCmd")) {
$null = & $powercfg -h $hibCmd 2>&1
if ($LASTEXITCODE -ne 0) {
Write-Warning "Failed to switch hibernation $hibCmd"
} else {
Write-Host "Hibernation state changed to $hibCmd" -ForegroundColor Cyan
}
}
} catch {
Write-Warning "Could not adjust hibernation state. $_"
}
}
# endregion
# region Summary & output
$success = ($failList.Count -eq 0)
if ($success) {
Write-Host "All power settings applied successfully for option $SleepOption" -ForegroundColor Green
} else {
Write-Warning "Completed with issues. Items with problems $($failList -join '; ')"
if ($Strict.IsPresent) { throw "Verification failed for one or more settings" }
}
if ($PassThru.IsPresent) {
return [pscustomobject]@{
Function = 'Set-PowerTimeoutProfile'
Option = $SleepOption
SchemeGuid = $scheme
Success = $success
Results = $results
}
}
# endregion
}
$result = Set-PowerTimeoutProfile -SleepOption ($SleepOption.Trim()) -PassThru
if (-not $result.Success) {
Exit 1
} else {
Log-Activity -Message "Power and sleep settings set to: $SleepOption" -EventName "System Configuration"
Exit 0
}
Exit 0
Let’s talk.
Book a free consultation with us today and discover how the right IT partner can transform your operations and future-proof your business.
Reach out to us today to learn how we can help optimize your IT infrastructure and ensure your business runs smoothly. 561-556-2000
Are you interested in more articles? Check out How to restrict users from creating Teams and Groups in Microsoft 365