Public/Set-PowercfgSettings.ps1

<#
.Synopsis
   Sets PowerCfg settings
.DESCRIPTION
   Set setting values to PowerCfg or pipe settings from Get-PowercfgSettings and set values.
.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"
   )]
   [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
            }
         }
         Catch{
            throw
         }
      }
      Else{
         $cfg = powercfg /l
      }
      # 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]

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

            $temp = [PSCustomObject]@{
               Name=$name
               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
               )
            )
         }

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

         # 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 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
   {
   }
}