脚本自提权怎么做
需要管理员的 PowerShell 脚本被普通双击或从普通会话启动时,会在中途因权限不足失败。常见做法是脚本开头自检令牌:不是管理员就用 Start-Process -Verb RunAs 重新启动自身,然后立即退出原进程,让提权后的新进程继续执行。这个模式交互场景很好用,但必须理解它是「开启了一个新进程」,环境、工作目录、参数、退出码都需要显式传递,否则容易出现重复执行或参数丢失。
基本信息
| 适用平台 | Windows PowerShell 5.1 与 PowerShell 7(Windows) |
|---|---|
| 检测方式 | WindowsPrincipal.IsInRole 判断是否持有管理员角色 |
| 提权方式 | Start-Process -Verb RunAs(会弹 UAC 确认) |
| 常见参数 | -ArgumentList 传递命令行、-WorkingDirectory 指定工作目录、-Wait 等待、-PassThru 取回进程对象 |
| 限制 | -Verb 与 -Credential 不能同时使用;UAC 被拒绝时会抛异常 |
| 无人值守 | 计划任务勾选「使用最高权限运行」比自提权可靠 |
典型使用场景
- 运维工具脚本:需要写系统目录、改服务、调注册表,双击即可用
- 一次性初始化:装组件、配防火墙、加计划任务这类必须提权的操作
- 部署脚本:在目标机上运行安装与配置,避免手工右键提权
- 排障脚本:需要读取安全日志或系统受保护路径时自动提权
- 回归测试:反复执行提权逻辑,靠固定入口保证环境一致
自提权骨架
自提权骨架
$isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
if (-not $isAdmin) {
$argList = @('-NoProfile','-ExecutionPolicy','Bypass','-File', "`"$PSCommandPath`"") # 引号包住路径,兼容含空格的目录
try {
$p = Start-Process powershell -Verb RunAs -ArgumentList $argList -WorkingDirectory (Split-Path $PSCommandPath) -Wait -PassThru
exit $p.ExitCode # 把子进程退出码交还给调用方
} catch {
Write-Error "提权被取消或失败:$($_.Exception.Message)"
exit 1
}
}
# 以下代码只在已提权的新进程里执行
Write-Host "当前已提权,开始执行任务" -ForegroundColor Green提权方式对照
| 方式 | 适用场景 | 注意 |
|---|---|---|
| 脚本自检 + RunAs 重启自身 | 人工双击运行的运维脚本 | 弹 UAC 确认;需显式传递参数与工作目录 |
| 计划任务「最高权限运行」 | 定时或无人值守任务 | 最可靠,不依赖交互确认 |
| #Requires -RunAsAdministrator | 发布给他人使用的脚本 | 权限不足时直接报错退出,不做提权 |
| 服务账户运行 | 长期驻留的自动化 | 权限固定、便于审计,需评估最小权限 |
提权后的进程是全新的:工作目录默认在系统目录、环境变量按新进程解析、映射盘不可见、父进程传入的变量全部丢失。脚本路径本身也可能因为位于网络共享而在提权后无法访问,稳妥做法是先复制到本地再运行。
原进程必须立即退出:如果提权后原进程继续往下执行,同一次调用会变成两遍副作用(比如安装两遍、写两次日志),出现问题极难排查。
参数传递与无人值守
参数传递与无人值守
# 需要把参数带到子进程时,显式拼进参数列表
$argList = @('-NoProfile','-File', "`"$PSCommandPath`"", '-Mode', 'Full', '-Log', 'D:\ops\run.log')
Start-Process powershell -Verb RunAs -ArgumentList $argList -WorkingDirectory (Split-Path $PSCommandPath) -Wait
# 无人值守推荐:计划任务以最高权限运行,不弹任何确认
# schtasks /Create /TN OpsJob /TR "powershell.exe -NoProfile -File D:\ops\job.ps1" /SC DAILY /ST 02:00 /RU SYSTEM /RL HIGHEST
Get-ScheduledTask -TaskName OpsJob | Select-Object TaskName, @{n='RunLevel';e={$_.Principal.RunLevel}}在 Server Core、无交互桌面的会话或通过某些远程执行通道(如部分 WinRM 场景)下,RunAs 提权无法弹窗,会直接失败。这类环境必须改用计划任务、服务或带提权的远程执行方式。