4

PowerShell 密技 - 自動改用管理者權限執行 .ps1

 1 year ago
source link: https://blog.darkthread.net/blog/ps1-requireadministrator/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

自動改用管理者權限執行 .ps1-黑暗執行緒

有些 PowerShell 指令必須以管理者權限執行,當 .ps1 包含需要高權限動作,執行時要記得開「Windows PowerShell (系統管理者)」下指令。

Fig1_637965489621106585.png

例如以下這個 Restart-WinService.ps1:

param (
	[string]$svcName,
	[int]$delay = 5
)
$ErrorActionPreference = 'STOP'
Write-Host "Restarting $svcName..."
Stop-Service $svcName
Start-Sleep -Seconds $delay
Start-Service $svcName
Write-Host "$svcName restarted"

用一般 Windows PowerShell 視窗跑會以紅字收場,必須要用 Windows PowerShell (系統管理者) 才會成功:

Fig2_637965489622485691.png

EXE 程式我們可以設定 manifest 強迫以管理者權限開啟,在 EXE 檔圖示加上小盾牌,點擊執行時主動詢問使用者是否要改以管理者身分執行。
延伸閱讀:应用程序清单 Manifest 中各种 UAC 权限级别的含义和效果 by 吕毅

Fig3_637965489622868773.png

我想要在 PowerShell .ps1 實現相似的效果,當使用者用一般身分執行 .ps1 時,系統彈出提示,使用者確認後改以管理者身分執行。甚至,在檔案總管按右鍵選「用 PowerShell 執行」開啟時,也能自動改用管理者權限執行,例如以下示範:

展示影片

感覺還不錯吧,這是怎麼做到的?

我們在 PowerShell 先加入一段檢查,WindowsIdentity.GetCurrent() 取得目前執行身分的 WindowsPrincipal,呼叫 IsInRole(WindowsBuiltInRole.Adminstrator) 檢查是否已具管理者身分,若還不是就利用之前介紹過的 Start-Process -Verb RunAs 技巧,切換成管理者身分呼叫 powershell.exe 重新執行我們的 .ps1。

重新執行時,需忠實還原輸入參數,PowerShell 有個 $MyInvocation.Line 包含啟動腳本的命令,包括所有參數和值,會傳回 ".\Restart-WinService W32Time" 整行字串。但這裡有個問題,當用管理者權限重跑時,.\Restart-WinService.ps1 相對路徑會失效,故我用了簡單做法把它換成 .ps1 的絕對路徑($PSCommandPath)。另外,我發現從檔案總管「用 PowerShell 執行」開啟時,背後的指令是 if((Get-ExecutionPolicy ) -ne 'AllSigned') { Set-ExecutionPolicy -Scope Process Bypass }; & 'D:\Restart-WinService.ps1',此時不會有參數。

完整程式範例如下:

param (
	[string]$svcName = 'W32Time',
	[int]$delay = 5
)
$ErrorActionPreference = 'STOP'
$wp = [Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()
if (-Not $wp.IsInRole([Security.Principal.WindowsBuiltInRole]"Administrator")) {
    $rawCmd = $MyInvocation.Line
    $rawArgs = $rawCmd.Substring($rawCmd.IndexOf('.ps1') + 4)
	# When run with file explorer context menu,
	# if((Get-ExecutionPolicy ) -ne 'AllSigned') { Set-ExecutionPolicy -Scope Process Bypass }; & 'D:\Restart-WinService.ps1'
	if ($rawCmd.StartsWith('if')) { $rawArgs = '' }
    Start-Process Powershell -Verb RunAs -ArgumentList "$PSCommandPath $rawArgs" 
}
else {
	Write-Host "Restarting $svcName..."
	Stop-Service $svcName
	Start-Sleep -Seconds $delay
	Start-Service $svcName
	Write-Host "$svcName restarted"
}

這樣子,我們就寫好一隻會自動轉用管理者身分執行的 PowerShell 腳本了,很酷吧!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK