Syncro PowerShell to set Windows Power Settings

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

  1. Accept Parameters
    • -SleepOption: Choose a predefined profile (Standard, Disabled, or Custom).
    • -CustomProfile: Provide a hashtable with detailed timeout values if Custom 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.
  2. 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.
  3. Verify Settings
    • Parses powercfg /q to confirm values were applied.
    • If parsing fails, falls back to reading registry indices (ACSettingIndex / DCSettingIndex).
  4. 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:

PowerShell
$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

  1. Run with Default (Standard) Profile
Set-PowerTimeoutProfile
  1. Apply Disabled Profile
Set-PowerTimeoutProfile -SleepOption Disabled
  1. Use Custom Settings
Set-PowerTimeoutProfile -SleepOption Custom -CustomProfile $profile
  1. Return Results for Logging or Reporting
Set-PowerTimeoutProfile -PassThru
  1. 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

PowerShell
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

Require assistance?

Support from our knowledgeable help desk staff ensures your team stays productive by swiftly and accurately resolving issues.