tech.guitarrapc.cóm

Technical updates

Active Directory の GPO で ExecutionPolicy が設定されているとVS2015 RTM と VS2013 Update 5 において Package Manager Consoleでエラーが出る場合の対処

VS2015 RTM と VS2013 Update 5 がリリースされました。

さて、これらをActiveDirectoryに参加したWindowsで起動すると PackageManagerConsole の起動に失敗する場合があります。今回はこの対処。

Package Manager Console がここで停止するので、一切 Install-Package などが触れないのでツライですね。

追記(2015/7/25)

NuGet チームが問題に対処したVS Extension を配布します。これにより問題は修正されます。

週明けには、VS gallery に配布が予定されています。が、緊急で必要な場合は、以下のGitHub 上のリリースからどうぞ。

visualstudiogallery.msdn.microsoft.com

visualstudiogallery.msdn.microsoft.com

GitHub Release リンク

2.8.7 for VS 2013: https://github.com/NuGet/Home/releases/download/2.8.7/NuGet.Tools.vsix

3.1.1 for VS 2015: https://github.com/NuGet/Home/releases/download/3.1.1/NuGet.Tools.vsix

目次

バグ報告と対処

NuGet の GitHub Issue でやり取りされています。Issue を見るといろんな報告があり...さて。

github.com

発生するマシン

この問題は、以下の条件がすべて満たされた時に発生します。

  • Active Directory に参加している

  • Group Policy の「コンピュータの構成 > ポリシー > 管理用テンプレート > Windows コンポーネント > Windows PowerShell > スクリプトの実行を有効にする > 有効/無効 のいずれかになっている」(未構成なら問題ありません)

GPOの何が問題なのか

「コンピュータの構成 > ポリシー > 管理用テンプレート > Windows コンポーネント > Windows PowerShell > スクリプトの実行を有効にする > 有効/無効 のいずれかになっている」

これが設定されていることが面倒な理由です。

このGPOを設定すると、Set-ExecutionPolicy の実行がPowerShell Consoleからは拒否されます。*1

具体的には、「GPOによってExecutionPolicy の ComputerPolicy / UserPolicyが設定される」のですが、ComputerPolicy が設定されているとSet-ExecutionPolicyで設定可能な LocalMachine ポリシーの設定が拒否されます。

さらに、AD環境に置いては通常はローカルマシンの管理者権限 (Administrators) を許可していないことも多いのも原因です。(Set-ExecutionPolicy は Administrators 権限が必要です)

原因

NuGet 2.8.6 (VS2013 Update 5) / 3.0 (VS2015 RTM) でのバグ修正の副作用によるものと説明されています。

NuGet 3.0 Released - The NuGet Blog

Visual Studio Extension appears to fail to run PowerShell if execution policy set in GPO, 2.8.6 and 3.0 · Issue #974 · NuGet/Home · GitHub

実際に、Package Manager Console で Set-ExecutionPolicy が実行されていることがわかります。

ようは、Set-ExecutionPolicyが実行されても問題ないようにすればいいのです。

対処

いくつかあります。

LocalMachine Policy のレジストリを削除する

先ほど説明した通り、LocalMachinePolicy の設定がSet-ExecutionPolicy を拒否する原因なので、これを一時的に解除する方法もあります。

GPO では、実質的にレジストリが設定されるだけなので、以下のレジストリを削除すればokです。

Remove-Item registry::HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\PowerShell

しかしこれも管理者に昇格 して実行が必要なので、GPOによっては難しいでしょう。

GPO で 「スクリプト実行を有効にする」を未構成にする

LocalMachinePolity が設定されていなければ、NuGet が Set-ExeuctionPolicy をやろうとしても影響ありません。

ただしこの場合、ユーザー自身が Set-ExecutionPolicy を実行できる必要があります。

CurrentUser の権限なら、PowerShell を管理者に昇格しなくてもいいのでいけるかも?

Set-ExecutionPolicy -Scope CurrentUser RemoteSigned

とはいえ、Package Manager Console は LocalMachine の Policy をを昇格しているので、AD環境で 管理者に昇格できない場合はツライでしょう。

Set-ExecutionPolicy -Scope LocalMachine RemoteSigned

他にも、GPOのポリシー的にここを無効にするのはちょっとというところも多いでしょう。

GPO で「スクリプト実行を有効にする」を未構成にする + レジストリでLocalMachineポリシーを設定する

管理者にも昇格できない。GPOで制御は維持したい。そこで、「MachinePolicy / UserPolicy を設定するのではなく、LocalMachine ポリシーのデフォルトを設定する」という考えです。

LocalMachine のポリシーも、レジストリで設定されているので、ここをGPOで設定すれば維持可能でしょう。かつ、ユーザーやPackageManagerConsole での変更も受け入れる柔軟な状態です。*2

  • キーパス
HKEY_LOCAL_MACHINE\Software\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell

PowerShell x86用に以下のキーにも同様にやりましょう。

HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell
  • キー名
ExeuctionPolicy
  • 値のタイプ
REG_SZ
  • 値 (ここに設定したい Executionpolicy を直接いれます)
RemoteSigned
ExecutionPolicy を回避して設定する

ハックに近いですが。ここに紹介されています。

https://www.nivot.org/blog/post/2012/02/10/Bypassing-Restricted-Execution-Policy-in-Code-or-in-Script

InitialSessionState initial = InitialSessionState.CreateDefault(); 

// Replace PSAuthorizationManager with a null manager
// which ignores execution policy 
initial.AuthorizationManager = new
      System.Management.Automation.AuthorizationManager("NuGet"); 

// Extract psm1 from resource, save locally 
// ... 

// load my extracted module with my commands 
initial.ImportPSModule(new[] { <path_to_psm1> }); 

// open runspace 
Runspace runspace = RunspaceFactory.CreateRunspace(initial); 
runspace.Open(); 

RunspaceInvoke invoker = new RunspaceInvoke(runspace); 

// execute a command from my module 
Collection<PSObject> results = invoker.Invoke("my-command"); 

// or run a ps1 script     
Collection<PSObject> results = invoker.Invoke(@"c:\program files\myapp\my.ps1");

これを利用して、こんなコードで ExecutionPolicy を回避できます。管理者権限も回避可能です。

gist.github.com

ExecutionPolicy に null を突っ込む手法ですね。管理者権限もいらないので、この手法が許されるならいいかもしれません。*3

まとめ

緊急度は上がりそうなので、修正がリリースされるの待ちましょう。

そして、リリースが発表されました!やったね。

*1:例外の方法もあります

*2:時間がたつとGPOで再度上書きれて維持されます。

*3:もう一個抜け道がありますがここでは触れません