Private/Set-ProcessPrivilege.ps1

# Copyright: (c) 2018, Jordan Borean (@jborean93) <jborean93@gmail.com>
# MIT License (see LICENSE or https://opensource.org/licenses/MIT)

Function Set-ProcessPrivilege {
    <#
    .SYNOPSIS
    Enables, disables, or removes privilege(s) on the current process.
 
    .DESCRIPTION
    This cmdlet will enable, disable, or remove a privilege on the current
    process. Only privileges that are set on the process can be enabled or
    disabled. Trying to enable/disable a removed privilege will result in an
    error.
 
    .PARAMETER Name
    [String[]] Privilege(s) to enable, disable, or remove. See
    https://docs.microsoft.com/en-us/windows/desktop/SecAuthZ/privilege-constants
    for a list of valid privilege constants.
 
    .PARAMETER Value
    [Boolean] Set to $true to enable, $false to disable, and $null to remove
    the privilege(s).
 
    .INPUTS
    [String] The privilege name(s) to set.
 
    .EXAMPLE
    # enable a privilege
    Set-ProcessPrivilege -Name SeDebugPrivilege -Value $true
 
    # enable multiple privileges
    Set-ProcessPrivilege -Name SeUndockPrivilege, SeTimeZonePrivilege -Value $true
 
    # disable a privilege
    Set-ProcessPrivilege -Name SeDebugPrivilege -Value $false
 
    # disable multiple privileges
    Set-ProcessPrivilege -Name SeUndockPrivilege, SeTimeZonePrivilege -Value $false
 
    # remove a privilege
    Set-ProcessPrivilege -Name SeDebugPrivilege -Value $null
 
    # remove multiple privileges
    Set-ProcessPrivilege -Name SeUndockPrivilege, SeTimeZonePrivilege -Value $null
 
    .NOTES
    If the privilege specified is an invalid constant, an error is written to
    the error stream. If the privilege constant is valid but not held on the
    current process, an error is written to the error stream if not trying to
    remove it.
    #>

    [CmdletBinding(SupportsShouldProcess=$true)]
    param(
        [Parameter(Position=0, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$true)][String[]]$Name,
        [Parameter(Position=1, Mandatory=$true)][Boolean]$Value,
        [Parameter()][Switch]$Remove
    )

    Begin {
        Write-Verbose -Message "Getting current process handle"
        $process_token = [PSPrivilege.Privileges]::GetCurrentProcess()

        Write-Verbose -Message "Getting privilege info for all privileges on the current process"
        $privilege_info = [PSPrivilege.Privileges]::GetAllPrivilegeInfo($process_token)

        $set_info = New-Object -TypeName 'System.Collections.Generic.Dictionary`2[[System.String], [System.Nullable`1[System.Boolean]]]'
        if ($Remove.IsPresent) {
            $action = "remove"
        } elseif ($Value -eq $true) {
            $action = "enable"
        } else {
            $action = "disable"
        }
    }

    Process {
        foreach ($privilege_name in $Name) {
            if (-not [PSPrivilege.Privileges]::CheckPrivilegeName($privilege_name)) {
                Write-Error -Message "Invalid privilege name '$privilege_name'" -Category ObjectNotFound
                continue
            } elseif (-not $privilege_info.ContainsKey($privilege_name)) {
                if (-not $Remove.IsPresent) {
                    Write-Error -Message "Cannot $action privilege '$privilege_name' as it is not set on the current process" -Category InvalidOperation
                }
                continue
            }

            $enabled = $privilege_info.$privilege_name.HasFlag([PSPrivilege.PrivilegeAttributes]::Enabled)
            if ($Remove.IsPresent) {
                Write-Verbose -Message "The privilege '$privilege_name' is current set, removing from process token"
                $set_info.Add($privilege_name, $null)
            } elseif ($enabled -eq $true -and $Value -eq $false) {
                Write-Verbose -Message "The privilege '$privilege_name' is currently enabled, setting new state to disabled"
                $set_info.Add($privilege_name, $false)
            } elseif ($enabled -eq $false -and $value -eq $true) {
                Write-Verbose -Message "The privilege '$privilege_name' is currently disabled, setting new state to enabled"
                $set_info.Add($privilege_name, $true)
            } else {
                Write-Verbose -Message "The privilege '$privilege_name' is already $($action)d, no action necessary"
            }
        }
    }

    End {
        if ($set_info.Count -gt 0) {
            Write-Verbose -Message "Setting token privileges on the current process"
            if ($PSCmdlet.ShouldProcess($set_info.Keys, "$action the specified privilege(s)")) {
                $null = [PSPrivilege.Privileges]::SetTokenPrivileges($process_token, $set_info)
            }
        }
    }
}