PSScriptAnalyzerを使ってPowerShellのコードをキレイに保とう

# はじめに

[PSScriptAnalyzer](https://github.com/PowerShell/PSScriptAnalyzer)とはPowerShellの静的コード解析ツールです。

バグになりやすい書き方や可読性を著しく低下させている箇所を警告してくれたり、
ベストプラクティスとされる書き方へ自動修整する機能を提供してくれたりしています。

MicrosoftのPowerShell公式チームが管理しているOSSで、
そこそこ有名なPowerShellのプロジェクトにはほぼ全てこちらのツールが利用されている印象です。

# インストール方法

動作環境としては、Windows PowerShell 3.0以上、または、PowerShell 7.0.11以上（Linux, macOSも可）です。

PSScriptAnalyzerはVisual Studio Codeの[PowerShell拡張機能](https://marketplace.visualstudio.com/items?itemName=ms-vscode.PowerShell)（こちらもPowerShell公式チームより提供）に同梱されているため、
PowerShell Gallery等からインストールする必要はほぼ皆無です。

念のため、以下にインストールするコマンドだけ貼っておきます。

```powershell
Install-Module PSScriptAnalyzer -Scope CurrentUser –Force
```

PSScriptAnalyzer単体の機能だとコマンド経由で解析を実行します。
ただ、実際のところは既述の通り、Visual Studio Code経由で使用することがメジャーケースになるので、
この記事もそちらメインで使い方をまとめました。

コマンドでの操作方法を確認したい場合は、`README.md`か`Get-Help`をご参照ください。

# ルールについて

具体的な使い方を説明する前に、先にPSScriptAnalyzerの基本的な用語となる"ルール"について解説します。

ここでいう"ルール"とは、PSScriptAnalyzerが定義するPowerShellのベストプラクティスな書き方となる規約、のことです。

例えば、PowerShellスクリプト内でコマンドレットのエイリアスを使った場合、
コーディングしているときは便利ですが、使い過ぎると可読性が低下するという考えがあります。
※あくまで考えというだけで、「絶対にそうだ！」とここで言っているわけではありません。

PSScriptAnalyzerでは、このエイリアスを避ける規約を`AvoidUsingCmdletAliases`というルール名で定義しています。
これが、ここでいう"ルール"の意味です。

また、一つ一つのルールにはSeverityが設定されており、警告の重さが決められています。
このSeverityはError、Warning、Informationの3段階で管理されています。

## 利用可能なルール一覧

PSScriptAnalyzerが提供しているルールは全部で59個あり、
[PSScriptAnalyzer Rules - GitHub](https://github.com/PowerShell/PSScriptAnalyzer/blob/master/docs/Rules/README.md)から確認することができます。

Errorが10個、Warningが43個、Informationが6個あり、そのうちルールごとに設定可能なものは10個あるようです。
`READE.md`には載っていませんが、自動修整できる機能が利用できるルールは7つほどしかないようです。

# Visual Studio Code上での使用法

まず、設定ファイル等を利用せずにPowerShellの拡張機能をインストールしたVisual Studio Codeで
どの程度の機能が利用できるのかを解説していきます。

## 警告内容の表示

以下のスクショにある通り、PSScriptAnalyzerの何らかのルールに違反している箇所がある場合、
波線で違反している箇所が示されます。
※波線の色はSeverityによって異なり、Errorは赤、Warningは黄、Informationは緑となります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114728/8efc79a6-afa9-92bc-517f-fb38a01cc5bd.png)

上のスクショでは、警告されている`ls`にカーソルを持っていき、`Alt+F8`で「問題を表示」している状態です。
今回は[AvoidUsingCmdletAliases](https://github.com/PowerShell/PSScriptAnalyzer/blob/development/RuleDocumentation/AvoidUsingCmdletAliases.md)というエイリアスの使用を禁止するルールに違反していると警告が出ています。

これらの警告している箇所はエディタペインに波線で表示されるほかに
開いているファイル内の警告であれば「問題」タブ内にも一覧で表示されます。

## 自動修正の適用

警告されている箇所と理由が把握できたら、コードを修正していきます。
下のスクショは`ls`カーソルがある状態から、`Ctrl+.`で「修正プログラムを利用する」を実施した結果です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114728/a2d46ba3-516b-a304-2433-89288ffcc339.png)

「Replace ls with Get-ChildItem」をクリックすることで`ls`のエイリアス元である`Get-ChildItem`で修正されます。
「Show documentation for ...」はデフォルトのブラウザで該当のルールページが表示され、ルールの詳細を把握するのに便利です。

修正後は以下のように波線はなくなり、問題は検出されていないと表示されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114728/77dd5366-7177-622d-b6de-084cd616ce16.png)

## 検査するルールの無効／有効化

`Ctrl`+`Shift`+`P`を押し、プロンプトに`PowerShell: Select PSSc`と入力すると
PSScriptAnalyzerで検査するルール一覧が表示され、無効／有効に切り替えることができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114728/906fd670-e002-837f-e9be-f17cde53ef9d.png)

上のスクショでは、まずデフォルトで有効になっている「AvoidUsingCmdletAliases」のチェックを外して「Confirm」をクリックし、
ファイルを一旦閉じて再度開く（`Ctrl+w`, `Ctrl+Shift+t`）と`ls`の警告が表示されなくなりました。

# `PSScriptAnalyzerSettings.psd1`でプロジェクト固有の設定を定義する

Visual Studio Codeだと標準でPSScriptAnalyzerが有効になっているので、
設定ファイルなど作成しなくても利用すること自体はできます。

ただ、Visual Studio Code自体を一旦閉じてしまうと無効・有効にしているルールは標準設定に戻り、
その他細かいルールの表示設定はできません。

そのため、プロジェクトでいつも同じルールの無効／有効化を常にしたい場合や
ほかに利用できるPSScriptAnalyzerの設定項目を利用したい場合は
`PSScriptAnalyzerSettings.psd1`というファイルを作成し、そのファイル内に設定内容を追加する必要があります。

## 配置の仕方

PSScriptAnalyzerを使用する際はだいたいプロジェクトのルートディレクトリ直下に
`PSScriptAnalyzerSettings.psd1`を配置します。

Visual Studio CodeのPowerShell拡張機能の場合、「フォルダを開く」で指定されたディレクトリ直下に
こちらのファイルがあった場合、自動で設定内容を反映してくれるようです。

なお、プロジェクトのルートディレクトリ直下以外、または、明示的にどの設定ファイルを利用するか指定したい場合は、
`.vscode\settings.json`に以下のような設定をすることで反映できます。

```javascript:.vscode\settings.json
{
  // プロジェクトルートからの相対パスで指定可能です。
  "powershell.scriptAnalysis.settingsPath": "rules/PSScriptAnalyzerSettings_Strict.psd1",
  // 他にもリアルタイムの静的解析のON/OFFも設定可能です。デフォルトはtrueです。
  "powershell.scriptAnalysis.enable": false
}
```

## 検査するSeverityの指定

では、`PSScriptAnalyzerSettings.psd1`の中身について解説していきます。

Severityは`Error`、`Warning`、`Information`の3段階ありますが、
その中から検査するSeverityを絞ることができます。

```powershell:PSScriptAnalyzerSettings.psd1
@{
    # 'Error'と'Warning'しか表示しない。
    Severity = @('Error', 'Warning')
}
```

## 検査するルールを指定

必要最小限しか警告されたくない場合は`IncludeRules`で限られたルールのみ指定することが可能です。

```powershell:PSScriptAnalyzerSettings.psd1
@{
    # 4つのルールのみを警告する。
    IncludeRules = @('PSAvoidDefaultValueSwitchParameter',
                     'PSMisleadingBacktick',
                     'PSMissingModuleManifestField',
                     'PSReservedCmdletChar')
}
```

## 検査を除外するルールの指定

反対に必要最小限のルールを除外したい場合は`ExcludeRules`で指定します。
GitHubを見ると、こちらの方がよく使われている印象でした。

```powershell:PSScriptAnalyzerSettings.psd1
@{
    ExcludeRules = @(
        # 色付きのメッセージをWrite-Hostで行っているため。
        'PSAvoidUsingWriteHost',
        # 現状`Invoke-Expression`を使わざるを得ないため。
        'PSAvoidUsingInvokeExpression'
    )
}
```

ちなみに同じルールを`IncludeRules`でも`ExcludeRules`でも指定した場合は、
`ExcludeRules`が優先的に適応されるため、そのルールは除外となります。

## ルールごとに詳細な設定値を指定

一つ一つのルールにおいても、設定ファイルでカスタマイズすることができます。
GitHubを見ると、だいたい`PSAvoidUsingCmdletAliases`と`PSUseCompatibleCmdlets`が多い印象でした。

```powershell:PSScriptAnalyzerSettings.psd1
@{
    Rules = @{
        # 以下のエイリアスについては使用可能とする。
        PSAvoidUsingCmdletAliases = @{
            # (gal -d "*-Object").Name
            Whitelist = @(
                '%',
                '?',
                'compare',
                'diff',
                'foreach',
                'group',
                'measure',
                'select',
                'sort',
                'tee',
                'where'
            )
        }

        # スクリプト内のコマンドレットがPowerShell Coreと互換性があるか確認する。
        PSUseCompatibleCmdlets = @{Compatibility = @("core-6.0.0-alpha-linux")}
}
```

# Pesterとの連携

PowerShellのテスティングツールとしてお馴染みのPesterと連携し、
全てのルールを守れているかをテスト結果に含めることも可能です。

やり方としては`PSScriptAnalyzer.Tests.ps1`のようなテストファイルを作っておいて、
中で`Invoke-ScriptAnalyzer`コマンドの実行結果をアサーションするテストケースを書くことで可能となります。

LabilityというHyper-Vのプロビジョニングツールで実装されていたので、
試したい方はこちらを参考にしてみるとよさそうです。

https://github.com/VirtualEngine/Lability/blob/master/Tests/Linting/PSScriptAnalyzer.Tests.ps1

# この記事で紹介しなかったこと

他にもPowerShellのASTを使って独自でルールを追加することや自動修正を作成することも可能となっています。

その他の詳しい使い方についてはこちらのドキュメントをご参照ください。
https://github.com/PowerShell/PSScriptAnalyzer/blob/master/ScriptRuleDocumentation.md

ただ、ほとんどのプロジェクトはこの記事で解説していないような複雑なことは行っていなかったので、
無理にカスタマイズする必要はないと思います。

# まとめ

GitHubのfilename検索オプションは便利。

https://github.com/search?q=filename%3APSScriptAnalyzerSettings.psd1&type=Code