Public/Set-PowercfgSettings.ps1

<#
.Synopsis
   Sets PowerCfg settings
.DESCRIPTION
   Set setting values to PowerCfg or pipe settings from Get-PowercfgSettings and set values.
.PARAMETER ComputerName
   Target a specified computer. When piping results from Get-PowercfgSettings, the original targeted computer is retained and this parameter is not necessary.
.PARAMETER PowerScheme
   Specify the PowerScheme of which settings to be modified. When not used, the currently active power plan is used by default.
.PARAMETER SubGroup
   Specified SubGroup that holds the settings that can be changed.
.PARAMETER Setting
   The setting to be modified. See Get-PowercfgSettings to see valid ranges and options.
.PARAMETER Value
   Value of which to set the specified setting to.
.PARAMETER SetAC
   Set value to the AC mode. Can be used with SetDC to modify both values.
.PARAMETER SetDC
   Set value to the DC mode. Can be used with SetAC to modify both values.
.PARAMETER Force
   Ignore prompt from ShouldProcess, attempt to complete action silently.
.EXAMPLE
   Set-PowercfgSettings -ComputerName $computername -PowerScheme 'High Performance' -SubGroup display -setting 'Turn off' -SetAC -SetDC -Value 120
 
   Sets "Turn off display after" from the "Display" SubGroup to 120.
.EXAMPLE
   Get-PowerCfgSettings -SubGroup display -Setting "Display Brightness" | Set-PowerCfgSettings -SetAC -Value 400 -Force
 
   Gets the "Display Brightness" setting from the "Display" Subgroup and passes it to Set-PowerCfgSettings where it's AC value is set to 400.
.INPUTS
   ComputerName
   PowerScheme
   SubGroup
   Setting
   [PowerCfgSetting]
.OUTPUTS
   [PowerCfgSetting]
.NOTES
   Relies on WinRM to use Invoke-Command when targeting remote computers.
.FUNCTIONALITY
   Configures powercfg
#>

function Set-PowercfgSettings
{
   [CmdletBinding(
      SupportsShouldProcess,
      ConfirmImpact="High",
      DefaultParameterSetName="Manual",
      HelpUri="https://github.com/KeithB0/PowerCfg/wiki/Set%E2%80%90PowercfgSettings"
   )]
   [Alias("spcs")]
   Param
   (
      [Parameter(
         ValueFromPipelineByPropertyName,
         ParameterSetName="Pipeline"
      )]
      [Parameter(
         ValueFromPipeline=$false,
         ParameterSetName="Manual"
      )]
      [ValidateNotNullOrEmpty()]
      [Alias("CN")]
      [String]
      $ComputerName,
      
      [Parameter(
         ParameterSetName="Manual",
         Position=0
      )]
      [String]
      $PowerScheme,

      [Parameter(
         ParameterSetName="Manual",
         Position=1,
         Mandatory
      )]
      [String]
      $SubGroup,

      [Parameter(
         ParameterSetName="Manual",
         Position=2,
         Mandatory
      )]
      [String]
      $Setting,

      [Parameter(
         Mandatory,
         Position=3
      )]
      [int]
      $Value,

      [Parameter(
         DontShow,
         ValueFromPipeline,
         ParameterSetName="Pipeline"
      )]
      [PowerCfgSetting]
      $p_Setting,

      [Switch]
      $SetAC,

      [Switch]
      $SetDC,

      [Switch]
      $Force#,

      #[Switch]
      #$PassThru
   )

      Begin{
         if(!($SetAC -or $SetDC)){
            $PSCmdlet.ThrowTerminatingError(
               [System.Management.Automation.ErrorRecord]::new(
                  [System.ArgumentNullException]::new(
                     "-SetAC, -SetDC",
                     "Setting type not specified. Use one or both."
                  ),
                  "PowerScheme.TypeSetting",
                  [System.Management.Automation.ErrorCategory]::NotSpecified,
                  ""
               )
            )
         }
      }
   Process
   {
      # Computername handler first in Process block for pipeline compatibility
      if($ComputerName){
         Try{
            $cfg = Invoke-Command $ComputerName {
               powercfg /l
            } -ErrorAction Stop
            $DescList = Invoke-Command $ComputerName {
               (gcim Win32_PowerPlan -Namespace root\cimv2\power)
           } -ErrorAction SilentlyContinue
         }
         Catch{
            throw
         }
      }
      Else{
         $cfg = powercfg /l
         $DescList = (gcim Win32_PowerPlan -Namespace root\cimv2\power)
      }
      # Parse out the heading
      $cfg = $cfg[3..(($cfg.count)-1)]

      # Manual entry (no pipeline) requires dedicated string parsing as if we were using Get-PowercfgSetting.
      if($PSCmdlet.ParameterSetName -eq "Manual"){
         # Get PowerScheme
         if(!$PowerScheme){
            $cfg = $cfg.where({$_ -match "(.+)\s{1}\*$"})
         }

         $schemeTable = @()
         foreach($scheme in $cfg){
            $null = $scheme -match "\((.+)\)";$name = $Matches[1]
            $null = $scheme -match "\s{1}(\S+\d+\S+)\s{1}";$guid = $Matches[1]

            $Desc = $DescList.where({$_.ElementName -eq $name}).Description

            if($scheme -match "\*$"){$active = $true}
            elseif($scheme -notmatch "\*$"){$active = $false}

            $temp = [PSCustomObject]@{
               Name=$name
               Description=$Desc
               Guid=[Guid]$guid
               Active=[bool]$active
            }
            [PowerCfgPlan]$temp = $temp
            $schemeTable += $temp
            $null = Remove-Variable temp -Force
         }
         if(!$PowerScheme){
            $PowerScheme = $schemeTable.Name
         }

         $selPowerScheme = ($schemeTable.Where({$_.Name -like "*$PowerScheme*"}).Guid.Guid)
         if($selPowerScheme.count -gt 1){
            $PSCmdlet.ThrowTerminatingError(
               [System.Management.Automation.ErrorRecord]::new(
                  [System.ArgumentOutOfRangeException]::new(
                     "-PowerScheme",
                     "$PowerScheme matches multiple values."
                  ),
                  "PowerScheme.>1",
                  [System.Management.Automation.ErrorCategory]::LimitsExceeded,
                  $PowerScheme
               )
            )
         }

         # The error if a bad PowerScheme name is entered.
         if ((!$?) -or ($null -eq $selPowerScheme)) {
            $PSCmdlet.ThrowTerminatingError(
               [System.Management.Automation.ErrorRecord]::new(
                  [System.ArgumentException]::new(
                     "$PowerScheme not found",
                     "-PowerScheme"
                  ),
                  "PowerScheme.notfound",
                  [System.Management.Automation.ErrorCategory]::ObjectNotFound,
                  $PowerScheme
               )
            )
         }

         # Get Power Plan
         if($ComputerName){
            Try{
               $QueryScheme = Invoke-Command $ComputerName {
                  powercfg /q $using:selPowerScheme
               }
            }
            Catch{
               throw
            }
         }
         Else{
            $QueryScheme = powercfg /q $selPowerScheme
         }

         # Get SubGroup
         $subgroups = (($QueryScheme) -match "SubGroup GUID: ").TrimStart().Trim()

         # $subgroups being an array doesn't assign matches to $Matches, but instead returns the result.
         # We forced a boolean return and then run it again to collect the string output.
         if([bool]($subgroups -match $SubGroup)){
            $p_SubGroup = $subgroups -match $SubGroup
         }
         else{
            $PSCmdlet.ThrowTerminatingError(
                  [System.Management.Automation.ErrorRecord]::new(
                     [System.ArgumentException]::new(
                        "$SubGroup not found",
                        "-SubGroup"
                     ),
                     "SubGroup.notfound",
                     [System.Management.Automation.ErrorCategory]::ObjectNotFound,
                     $SubGroup
                  )
            ) # The error for if a bad SubGroup name is specified.
         }

         # Get Setting
         $settings = (($QueryScheme) -match "Power Setting Guid: ").TrimStart().Trim()

         if([bool]($settings -match $Setting)){
            Remove-Variable p_Setting -Force
            $p_Setting = $settings -match $Setting
         }
         else{
            $PSCmdlet.ThrowTerminatingError(
               [System.Management.Automation.ErrorRecord]::new(
                  [System.ArgumentException]::new(
                     "$Setting not found",
                     "-Setting"
                  ),
                  "Setting.notfound",
                  [System.Management.Automation.ErrorCategory]::ObjectNotFound,
                  $Setting
               )
            ) # The error for if a bad SubGroup name is specified.
         }

         # Get GUIDs
         $null = $p_SubGroup[0] -match "\s{1}(\S+\d+\S+)\s{1}";$Groupguid = $Matches[1]
         $null = $p_Setting[0] -match "\s{1}(\S+\d+\S+)\s{1}";$Settingguid = $Matches[1]

         # Get Names
         $null = $p_SubGroup[0] -match "\((.+)\)";$Groupname = $Matches[1]
         $null = $p_Setting[0] -match "\((.+)\)";$Settingname = $Matches[1]

         # $selPowerScheme, $Groupguid, $Settingguid
         # Until renamed, these are the vars for Plan, SubGroup, and Setting
         
         $commands = @()

         if($ComputerName){
            $Target = "$ComputerName -> $Groupname -> $Settingname"
         }
         else{
            $Target = "$Groupname -> $Settingname"
         }

         if($SetAC){
            if(($Force) -or ($pscmdlet.ShouldProcess($Target, "Set AC value to $value"))){
               $commands += {powercfg /setacvalueindex ($selPowerScheme) ($Groupguid) ($Settingguid) $value}
            }
         }
         if($SetDC){
            if(($Force) -or ($PSCmdlet.ShouldProcess($Target, "Set DC value to $value"))){
               $commands += {powercfg /setdcvalueindex ($selPowerScheme) ($Groupguid) ($Settingguid) $value}
            }
         }

         $PassThru = @{PowerScheme = $PowerScheme;SubGroup = $Groupname;Setting = $Settingname}

         if(!($ComputerName)){
            $commands | ForEach-Object{
               & $_
               Get-PowercfgSettings @PassThru
            }
         }
         else{
            $PassThru += @{ComputerName = $ComputerName}
            Try{
               Invoke-Command -ComputerName $ComputerName {
                  param(
                     $selPowerScheme,
                     $Groupguid,
                     $Settingguid,
                     $Value
                  )
                  if($using:SetDC){
                     & powercfg /setdcvalueindex $selPowerScheme $Groupguid $Settingguid $Value
                  }
                  if($using:SetAC){
                     & powercfg /setacvalueindex $selPowerScheme $Groupguid $Settingguid $Value
                  }
               } -ArgumentList $selPowerScheme,$Groupguid,$Settingguid,$Value
               Get-PowercfgSettings @PassThru
            }
            Catch{
               throw
            }
         }
      }

      # Pipeline entry - where a [PowerCfgSetting] is sent over, it holds all necessary arguments for local execution.
      if($PSCmdlet.ParameterSetName -eq "Pipeline"){
         if($p_Setting.count -gt 1){
            $PSCmdlet.ThrowTerminatingError(
               [System.Management.Automation.ErrorRecord]::new(
                  [System.ArgumentOutOfRangeException]::new(
                     "Multiple Settings Found. Specify One."
                  ),
                  "Setting.>1",
                  [System.Management.Automation.ErrorCategory]::NotSpecified,
                  ""
               )
            )
         } # Currently, not handling multiple settings. Plan to add piping entire subgroups and plans.

         # Breaking up the variable properties into their own vars
         $p_PowerScheme = $p_Setting.Plan
         $p_SubGroup = $p_Setting.SubGroup

         $commands = @()

         if($ComputerName){
            $Target = "$ComputerName -> $($p_SubGroup.Name) -> $($p_Setting.Name)"
         }
         else{
            $Target = "$($p_SubGroup.Name) -> $($p_Setting.Name)"
         }

         if($SetAC){
            if(($Force) -or ($pscmdlet.ShouldProcess($Target, "Set AC value to $value"))){
               $commands += {powercfg /setacvalueindex $p_PowerScheme.guid.guid $p_SubGroup.guid.guid $p_Setting.guid.guid $value}
            }
         }
         if($SetDC){
            if(($Force) -or ($PSCmdlet.ShouldProcess($Target, "Set DC value to $value"))){
               $commands += {powercfg /setdcvalueindex $p_PowerScheme.guid.guid $p_SubGroup.guid.guid $p_Setting.guid.guid $value}
            }
         }

         $PassThru = @{PowerScheme = $p_PowerScheme.name;SubGroup = $p_SubGroup.name;Setting = $p_Setting.name}

         if(!($ComputerName)){
            $commands | ForEach-Object{
               & $_
               Get-PowercfgSettings @PassThru
            }
         }
         else{
            $PassThru += @{ComputerName = $ComputerName}
            Try{
               Invoke-Command -ComputerName $ComputerName {
                  param(
                     $p_PowerScheme,
                     $p_SubGroup,
                     $p_Setting,
                     $Value
                  )
                  if($using:SetDC){
                     & powercfg /setdcvalueindex $p_PowerScheme $p_SubGroup $p_Setting $Value
                  }
                  if($using:SetAC){
                     & powercfg /setacvalueindex $p_PowerScheme $p_SubGroup $p_Setting $Value
                  }
               } -ArgumentList $p_PowerScheme.guid.guid,$p_SubGroup.guid.guid,$p_Setting.guid.guid,$Value
               Get-PowercfgSettings @PassThru
            }
            Catch{
               throw
            }
         }
      }
   }
   End
   {
   }
}