PowerShell CoreでAWSPowerShell.NetCoreモジュールが自動ロードされない問題と回避策について

PowerShell Core

しばたです。

完全な原因を突き止めることができずちょっと後味は悪いのですが、対処方法ははっきりしているのでこの記事を公開します。

TL;DR

割と最近Ver.3.3.270.0(2018-04-25)以降のAWS Tools for PowerShell on PowerShell Core(AWSPowerShell.NetCoreモジュール)ではモジュールの自動ロードが上手く動作しません。
モジュールを使う際はImport-Moduleを明示して実行する様にしてください。

# PowerShell CoreのAWS Tools for PowerShellではImport-Moduleを明示する
Import-Module AWSPowerShell.NetCore

起きていること

PowerShellではバージョン3.0からモジュールの自動ロード機能が導入され、PSModulePath環境変数配下にインストールされているモジュールは自動的にロードされる様になっており、コマンドレットなどを起動後直ちに使用できる様になっています。
AWSPowerShell.NetCoreモジュールもその対象なのですが、割と最近のVer.3.3.270.0以降のバージョンではPowerShellコンソールを起動してすぐにコマンドを実行すると下図の様なエラーになってしまいます。

(起動後ただちにGet-EC2Instanceを実行した例。「そんなコマンドは存在しない。」とエラーになる)

この現象がいつから発生しているかは突き止めることが出来なかったのですが、割と最近の様で、GitHubPowerShell.orgでもIssueが立てられています。

本記事を試している私の環境はPowerShell Core 6.1.2、AWSPowerShell.NetCore 3.3.450.0です。

ちなみにWindows PowerShellではこの現象は発生せず、初回のコマンド実行に時間はかかるもののエラーなく終了します。

原因

この現象の直接的な原因については、GitHubのIssueの中でAWSの中の人が次の様にコメントしています。

As noted above, this is caused by us (AWS) currently not explicitly listing the cmdlets in the module in the manifest.
Until a couple months ago we did in fact enumerate the cmdlet names in the manifest but had to switch to using wildcards when we went through 4000 cmdlets in the module. This caused the gallery publishing to reject the module as it limited modules to under 4000 cmdlets in the export statement. We considered listing only 'the most important' cmdlets but couldn't come up with a single list - every service seemed to be important to someone :-). In the face of this, switching back to wildcards and noting the issue in the release notes at the time seemed the best approach.
Now, we realize 4000+ cmdlets is a lot - and the growth in service APIs since we shipped v1.0 has surprised us too. We have backlog plans to re-modularize to per-service modules but have not yet been able to schedule the work.
Interestingly, reverting to wildcards actually improved the import time for the module too - which now that we have PowerShell support in Lambda we're not too keen on making worse either. Realistically I think a fix will need to wait until we can get the re-modularization work scheduled. I'll bring this discussion to the wider attention of the team here at AWS and see what we can do to bump the priority.

上記で言われている(As noted above)のコメントはこちら

In their module manifest they only explicitly list aliases. The CmdletsToExport field is a wildcard statement

  # Cmdlets to export from this module
  CmdletsToExport = '*-*'
  

They need to explicitly list exported cmdlets as well.

かいつまんで説明すると、

  • エクスポートするコマンドレットの数が大量になった(4000以上)になったのでロードの設定をワイルドカードを使用したもの(CmdletsToExport = '*-*')に変更した
  • こうしたのは4000以上のコマンドをエクスポートしているとPowerShell Galleryでの公開がリジェクトされてしまうため
  • これが直接的な原因となっているのは把握している
  • "重要な"コマンドレットに絞ってエクスポートすることも検討したが、何が"重要か"を決めることが出来ないため(すべてのサービスが誰かにとって重要である)断念した
  • この設定でモジュールのインポート時間が改善されており、また、LambdaでのPowerShellサポートもあることからこの状態(インポート時間の改善)を悪化させたくない
  • モジュールを小分けにすることも検討しているがスケジュールを立てるまでには至っていない

といった感じの返答となっています。

これはこれで仕方ないと思われますが、PowerShell Teamもモジュール側の問題とあっさりIssueをクローズしてしまい、個人的には「それは無いでしょう?」といった気持ちでスッキリしません。
また、何故CmdletsToExport = '*-*'が悪いのかについては一切不明で、私もPowerShellのソースを読み込んで調べてみたものの、モジュールロードに関するコードが非常にややこしく読み切ることができませんでした...

余談

これは完全に余談なのですが、PowerShellのモジュールロードでCmdletsToExport = *の様にワイルドカードを指定するのは一般的にはパフォーマンスの劣化を引き起こし推奨されない行為です。
ベストプラクティスとしてはCmdletsToExport = @(Cmdlet1, Cmdlet2)の様に公開するコマンドレットを明示するのが正しい方法です。

今回は何らかの理由でコマンドレットのロードが出来ていないので、ロードできていない分の時間が短縮されているだけだと予想されます。

対処方法

根本的な問題の解決は簡単にいかなそうですが、とりあえずの対処方法は簡単で、最初に述べた通りImport-Moduleを明示して実行する様にしてください。

# PowerShell CoreのAWS Tools for PowerShellではImport-Moduleを明示する
Import-Module AWSPowerShell.NetCore

すると下図の様にエラーなくコマンドを実行できます。

追記 : 該当バージョンについて

AWS Tools for PowerShellの変更履歴を追いかけたところ、

3.3.270.0 (2018-04-25)
* The CmdletsToExport property in the module manifest has been temporarily set to '-' instead of the individual cmdlet names to work around a limitation publishing modules that contain over 4000 cmdlets to the PowerShell Gallery. The net effect of this change is to disable tab completion for cmdlet names unless the module is explicitly imported. We are investigating approaches to work around or fix this issue and will re-instate the list of cmdlets as soon as possible.

との記述があり、本件に該当するバージョンはVer.3.3.270.0以降であることがわかりました。