PowerShellコラム:別のユーザーで処理を実行する 1

Microsoft

2022.04.14

始めに

本コラムではStart-Process コマンドレットを使用した「別のユーザーとして実行」の実装法をいくつか紹介します。
ここでは実行対象は「Powershell.exe」としています。
ユーザーは、管理者権限を持つ Adm1,Adm2 と標準アカウントの TestUser を例として使用します。

Start-Process
https://docs.microsoft.com/ja-jp/powershell/module/microsoft.powershell.management/start-process?view=powershell-5.1

別のユーザーとして実行する場合

Start-Process コマンドレットにあるCredentialパラメータを使用することで、別のユーザーを指定して実行することができます。

具体的には

Start-Process -FilePath 'Powershell.exe' -Credential 'TestUser'

のようにすると以下のようなダイアログが表示されるので、ここでTestUserのパスワードを入れます。

別のユーザとして実行

新しく開いたウィンドウに whoami と入れることで、実行者が TestUser であることがわかります。

別のユーザとして実行_結果

これだけのことではありますが、環境によってはうまく動作しない環境があります。

その場合は

Remove-Module PSReadline

コマンドを実行した後に再実行してみてください。

管理者として実行する場合

Verb パラメータに RunAs を指定して実行します。

Start-Process -Filepath Powershell.exe -Verb Runas

UAC画面を経由し、新たに立ち上がったコンソールのタイトルに「管理者」の文字が確認できます。

管理者として実行1

管理者として実行2

管理者として実行3

whomi /priv

と実行してみれば、特権がたくさん増えていることが確認できます。

管理者として実行_結果

別の管理者アカウントで「管理者として実行」する

では「別のユーザーとして、管理者として実行」はできるのでしょうか?
残念ながら、一筋縄ではいきません。単純に Credential と Verb RunAs を同時に使用すると、以下のエラーになります。

指定された名前のパラメーターを使用してパラメーター セットを解決できません

別のユーザーで管理者として実行_NG

「Get-Help Start-Process -Online」と実行してStart-Processコマンドレットのヘルプを見てみます。これは執筆時の構文ですが、Verb パラメータと Credential パラメータは排他になっています。

Start-Processの構文

そのため、

  • 別のユーザーとしてスクリプトを実行する
  • スクリプト内でさらにStart-Process をVerbパラメータ付きで実行する

という2段階の処理が必要になります。

プログラムやコマンド

Powershell.exe には任意のコマンドを実行させるオプションがあるので、そこからさらにStart-Processを呼びます。その際は最初のStart-ProcessにArgumentListパラメータを付けて実行します。
なお管理者として実行するので、別のユーザーも管理者権限が必要です。
これを実行するとUACの画面を経由して新しい権限でプログラムが開きます。

$Adm2Cred = Get-Credential -UserName 'Adm2' -Message "Adm2 の資格情報"

# 実行したいプログラム
$FilePath = "notepad.exe"
$ArgumentList = '-WindowStyle Hidden -command &{Start-Process '+ $FilePath + ' -verb runas}'

# 実行
Start-Process -FilePath Powershell.exe -Credential $Adm2Cred -ArgumentList $ArgumentList

スクリプト

長い処理を実行するのであれば、スクリプトを呼び出します。それがPowersShellスクリプトの場合はExecutionPolicy オプションを二つ目のStart-Processのパラメータに付与して呼び出すとよいでしょう。
VerbやExecutionPolicyなどPowershell.exeのパラメータは File パラメータよりも前にあることに注意してください。そのほかBool値のパラメータを明示的に指定できないなどの制限もあるので、詳細はドキュメント about_PowerShell_exe をご確認ください。

about_PowerShell_exe
https://docs.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/about/about_powershell_exe?view=powershell-5.1

サンプル

C:\Temp\CallMeWithAdm2.ps1

Param($x,$y)
Write-Host "私は '$(whoami)' です。パラメータは x = $x , y= $y です。"
pause

実行するコマンド

# 別の管理者の資格情報
$Adm2Cred = Get-Credential -UserName 'Adm2' -Message "Adm2 の資格情報"

# 呼び出したいスクリプトのパス
$ScriptFilePath = "C:\Temp\CallMeWithAdm2.ps1"

# 呼び出した管理者に実行参照権限のあるフォルダパス
$WorkingDirectory = "C:\Temp"

# スクリプトのパラメータ
$ScriptArguments = '-x 100 -y 200'

# スクリプトを呼び出すPowerShell.exeに渡すパラメータ
$PowerShellExeParams = "-ExecutionPolicy RemoteSigned -File $ScriptFilePath $ScriptArguments"

# スクリプトを呼び出すPowerShell.exeを呼び出すStart-Process のパラメータ
$StartProcessArgs2 = "-FilePath Powershell.exe -WorkingDirectory $WorkingDirectory -Verb runAs -ArgumentList '$PowerShellExeParams'"

# 別のユーザーで呼び出すStart-Proecssのパラメータ
$StartProcessArgs1 = @{
    FilePath = "PowerShell.exe"
    Credential = $Adm2Cred
    ArgumentList = '-Command &{Start-Process ' + $StartProcessArgs2 + '}'
    WorkingDirectory = $WorkingDirectory
    WindowStyle = "Hidden"
}

Start-Process @StartProcessArgs1

実行結果

別のユーザで管理者実行_結果

TIPS

別のユーザーとして実行する際にあるトラブルや使い方について、いくつか紹介します。

パスワードをスクリプト上で指定する

単に実行ユーザーの資格情報を求めるタイミングを変えたいのであれば、Get-Credential コマンドレットを使います。
コンソールやテキストファイルなどから平文で入力したパスワードを使いたい場合は、 SecureString に変換してPSCredential オブジェクトを作成する必要があります。
ただ内部的にSecureStringは平文が使用されてしまうそうなので、推奨はされていない様子ではあります。

SecureStringクラス
https://docs.microsoft.com/ja-jp/dotnet/api/system.security.securestring?view=net-6.0

$Password= ConvertTo-SecureString 'Password' -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential ('TestUser', $password)
Start-Process -FilePath 'Powershell.exe' -Credential $Cred

コンソールアプリのパフォーマンスが極めて悪い

どうもPSReadline というモジュールに問題があるようで、もしPowershellのウィンドウのレスポンスが極めて悪くなるようであれば、

Remove-Module PSReadline
Start-Process -Filepath PowerShell.exe -Credential 'ユーザ名'

のように一時的にPSReadlineを無効化してから試してみてください。

Wait オプションを付けると「アクセスが拒否されました」というエラーになる

呼び出し先は基本的に非同期で行われる処理ですが、これの完了を待つためのオプションに Wait があります。しかし「管理者として実行」を行っていない環境で

Start-Process -Filepath Powershell.exe -Credential username -Wait

とするとアクセス拒否によるエラーが発生することがあります。以下のようににPassThruを付与するとエラーは表示されません。オブジェクトを使わないなら Out-Null などで消します。

Start-Process -Filepath Powershell.exe -Credential username -Wait -PassThru | Out-Null

「ディレクトリ名が無効です」エラー

既定では、呼び出し元の実行パスが使用されます。
では、呼び出された方のユーザーが権限不足で子のパスにアクセスできなかった場合はどうなるでしょうか。親フォルダに「読み取りと実行」の拒否を付与したサブフォルダ上で Start-Process してみます。

ディレクトリ名が無効1

すると、このようなエラーになります。

Start-Process : このコマンドは、次のエラーのため実行できません: ディレクトリ名が無効です。

ディレクトリ名が無効2

環境によっては突然英語で「The directory name is invalid.」と出てくることも。
またこの現象は権限以外にも、別プロセスで実行パスが削除されてしまったような場合にも発生します。
これに備えるには WorkingDirectory パラメータを使用します。ここに、確実に存在し、かつアクセス権があるパスを指定します。

Start-Process -FilePath 'Powershell.exe' -Credential 'TestUser' -WorkingDirectory 'C:\Windows\Temp'

ディレクトリ名が無効3

ウィンドウがすぐに閉じてしまって結果が判らない

Powershell.exeに渡すパラメータに -noexit を付与するか、呼び出すスクリプトのどこかに pause など一時停止するコマンドを入れることで対応できることがあります。
そもそもスクリプトが実行されないのであれば構文エラーなどが考えられます。
"-verb RunAs"を使わないのであればStart-Processの「RedirectStandardError」「RedirectStandardOutput」パラメータを使用することができます。

終わりに

管理者が一時的に別ユーザーで処理を実行したりする方法を中心に紹介しました。
ユーザー証明書のインポートやブラウザの設定など、対象のユーザーのコンテキストで実行が必要なケースはあると思います。
次回は、標準ユーザーから管理者権限が必要なコマンドを実行する方法を紹介します。

※文中の商品名、会社名、団体名は、一般に各社の商標または登録商標です。

PowerShellコラム:別のユーザーで処理を実行する 1