更新履歴(4件・最終更新 2026年08月02日)
この記事に加えた変更の記録です。アーカイブした更新前のバージョンは、DOI付きの固定URLから読めます。
- 記事の冒頭に「この記事の知識マップ」節を追加しました。本文で扱っている概念とその関係を、要約・図・詳細ページへのリンクにまとめたものです。本文の主張は変えていません。
- 外部レビュー(1283件)への対応として本文を更新しました。個々の変更内容は、この下の履歴を参照してください。
- 優先して有効にするルールの表に重大度の列を追加し、Errorだけを失敗条件にすると素通りするルールがある点を明示しました。あわせて対象読者と前提環境の表、解析結果として何が返るか、VS Codeの設定ファイルの既定値を追加しています。
- CIの失敗条件を作るコード例で、$issues がどこにも定義されないまま使われていたため、解析結果を取得する行を補いました。あわせて関連記事へのリンクの文言を、リンク先の現在のタイトルに揃えました。
- 初版公開
この記事を引用する(DOI: 10.5281/zenodo.21547453)
この記事はZenodoにアーカイブされています。常に最新版へ解決されるDOIと、いま表示している版に固定されたDOIの両方を下に示します。
小村 豪(2026)「PSScriptAnalyzerでPowerShellスクリプトの品質を守る ── ルール選定とCI導入」合同会社小村ソフト. https://doi.org/10.5281/zenodo.21547453 https://staging.comcomponent.com/blog/powershell-psscriptanalyzer-ci/
- DOI(最新版)
- 10.5281/zenodo.21547453
- DOI(この版)
- 10.5281/zenodo.21733160
社内のPowerShellスクリプトが増えてくると、必ず「品質のばらつき」の問題が出てきます。エイリアスだらけで読めないスクリプト、平文のパスワードが書かれたスクリプト、タイプミスした変数名が誰にも気づかれないまま残っているスクリプト。書いた本人がいなくなった後で、それを読む人が困るという形で問題が表面化します。
こうした問題の相当部分は、静的解析ツールで機械的に検出できます。PowerShellには公式の静的解析モジュール PSScriptAnalyzer があり、コマンド1つでスクリプト群を解析できます。テストコードを1行も書かずに、初日から効果が出るのが最大の利点です。
この記事では、PSScriptAnalyzerを社内スクリプト資産に導入する実務手順を、「まず有効にすべきルール」「既存資産への段階導入」「CIでの自動チェック」という順にまとめます。テストによる品質確保については「PesterによるPowerShellのテスト整備」を、あわせて参照してください。
対象読者・前提環境
| 項目 | 内容 |
|---|---|
| 対象読者 | 社内に増えたPowerShellスクリプトの品質を、これから機械的に管理したい情シス・開発担当 |
| 実行環境 | PSScriptAnalyzerはWindows PowerShell 5.1でもPowerShell 7でも実行できます。解析対象のスクリプトが5.1向けか7向けかは、解析する側のバージョンとは別に PSUseCompatibleSyntax で指定します(第4章) |
| CIの前提 | 第7章のCI例は GitHub Actionsの windows-latest ランナーを前提にしています。Invoke-ScriptAnalyzer を呼ぶ部分は他のCIでも同じで、解析そのものはWindows以外のランナーでも実行できます |
| 必要な権限 | 導入は Install-Module -Scope CurrentUser なので、管理者権限は不要です |
| サンプルの検証環境 | 記事末尾で配布しているサンプルコードは PowerShell 7.6 で実行して検証しています |
1. まず結論
- PSScriptAnalyzerはPowerShell公式の静的解析モジュールです。
Invoke-ScriptAnalyzerでスクリプトやモジュールを解析し、ルール違反を報告します。1 - 指摘には重大度(Severity)があります。Error / Warning / Information の3段階で、まずErrorだけをゼロにするのが現実的な出発点です。1
- 設定は
PSScriptAnalyzerSettings.psd1に集約します。SeverityIncludeRulesExcludeRulesRulesをリポジトリに置き、全員が同じ基準で解析できるようにします。2 - 個別の抑止は
SuppressMessageAttribute+ 理由の記述です。ルールごと除外する前に、範囲を絞った抑止で足りないか検討します。3 -Fixで自動修正できる指摘もあります。整形はInvoke-Formatterが担当します。14- VS CodeのPowerShell拡張機能はPSScriptAnalyzerを内蔵しています。編集中にその場で警告が出るため、CIより先に効きます。5
- CIの失敗条件は「重大度Error + 名指しした重大ルール」にします。重大度はルールごとに決まっており、平文パスワードの検出(
PSAvoidUsingPlainTextForPassword)はWarningです。Errorだけを条件にすると素通りします。6 - 既存資産には段階導入。「Errorをゼロに」→「変更ファイルのみ厳格に」→「範囲を広げる」の順です。
この記事の知識マップ
この記事は、PowerShell公式の静的解析モジュールPSScriptAnalyzerを社内スクリプトへ導入する実務手順を扱う。PSScriptAnalyzerSettings.psd1という1つの設定ファイルをリポジトリに置くことで、VS Codeの拡張機能とGitHub ActionsのCIが同じ基準で解析するように整える。重大度はルールごとに決まっており、平文パスワード検出などのルールはError指定だけでは素通りするため、資格情報系のルールは名前を明示してCIの失敗条件に加えるべきだとしている。既存資産には、まずErrorをゼロにし、次に変更ファイルだけを厳格に解析する段階導入を勧め、テストを書かずに初日から効果が出るPSScriptAnalyzerをPesterより先に導入する順序を推奨している。
flowchart LR
accTitle: PSScriptAnalyzer導入とCI化の知識マップ
accDescr: PSScriptAnalyzerがInvoke-ScriptAnalyzerや設定ファイルを介してVS CodeとGitHub Actionsの双方に基準をそろえ、重大度に関係なく落とすべきルールを名指しし、段階導入を経てPesterより先に効果を出す様子を示す図。
psscriptanalyzer["PSScriptAnalyzer"]
github_actions["GitHub Actions"]
invoke_scriptanalyzer["Invoke-ScriptAnalyzer"]
suppressmessageattribute["SuppressMessageAttribute"]
psscriptanalyzer_settings["PSScriptAnalyzerSettings.psd1"]
vscode_powershell_extension["VS CodeのPowerShell拡張機能"]
psavoidusingplaintextforpassword["PSAvoidUsingPlainTextForPassword"]
psusedeclaredvarsmorethanassignments["PSUseDeclaredVarsMoreThanAssignments"]
invoke_formatter["Invoke-Formatter"]
pester["Pester"]
psscriptanalyzer_staged_adoption["PSScriptAnalyzerの段階導入"]
changed_file_only_analysis["変更ファイルのみを対象にした差分解析"]
github_actions_fetch_depth_zero["actions/checkoutのfetch-depth: 0"]
github_base_ref["GITHUB_BASE_REF"]
psavoidusingconverttosecurestringwithplaintext["PSAvoidUsingConvertToSecureStringWithPlainText"]
psavoidusingusernameandpasswordparams["PSAvoidUsingUsernameAndPasswordParams"]
psscriptanalyzer_mustfix_rules["重大度に関係なく落とすルールの明示リスト"]
psscriptanalyzer -->|"利用する"| invoke_scriptanalyzer
psscriptanalyzer -->|"利用する"| suppressmessageattribute
psscriptanalyzer -->|"で構成できる"| psscriptanalyzer_settings
vscode_powershell_extension -->|"利用する"| psscriptanalyzer
vscode_powershell_extension -->|"で構成できる"| psscriptanalyzer_settings
github_actions -.->|"利用する"| invoke_scriptanalyzer
psscriptanalyzer -->|"利用する"| psavoidusingplaintextforpassword
psscriptanalyzer -->|"利用する"| psusedeclaredvarsmorethanassignments
invoke_scriptanalyzer -->|"利用する"| invoke_formatter
psscriptanalyzer -->|"より先に行うべき"| pester
psscriptanalyzer_staged_adoption -->|"前提とする"| psscriptanalyzer
psscriptanalyzer_staged_adoption -->|"前提とする"| changed_file_only_analysis
changed_file_only_analysis -.->|"前提とする"| github_actions_fetch_depth_zero
changed_file_only_analysis -.->|"前提とする"| github_base_ref
changed_file_only_analysis -.->|"利用する"| github_actions
changed_file_only_analysis -->|"利用する"| invoke_scriptanalyzer
psscriptanalyzer -->|"利用する"| psavoidusingconverttosecurestringwithplaintext
psscriptanalyzer -->|"利用する"| psavoidusingusernameandpasswordparams
psscriptanalyzer_mustfix_rules -->|"前提とする"| psavoidusingplaintextforpassword
psscriptanalyzer_mustfix_rules -->|"前提とする"| psavoidusingconverttosecurestringwithplaintext
psscriptanalyzer_mustfix_rules -->|"前提とする"| psavoidusingusernameandpasswordparams
github_actions -.->|"利用する"| psscriptanalyzer_mustfix_rules
図の実線は常に成り立つ関係、破線は条件付きの関係です(成立条件は詳細ページの各関係の説明に記載)。関係すべての一覧(全22件、根拠・確度つき)と主要概念の定義は知識マップ詳細ページにまとめています。データ: JSON-LD / Turtle
2. 導入 ── まずは1コマンド
Install-Module -Name PSScriptAnalyzer -Scope CurrentUser
# フォルダー配下をまとめて解析する
Invoke-ScriptAnalyzer -Path 'D:\Scripts' -Recurse |
Sort-Object Severity, RuleName |
Format-Table Severity, RuleName, ScriptName, Line, Message -AutoSize
# 重大度別の件数を把握する(棚卸しの第一歩)
Invoke-ScriptAnalyzer -Path 'D:\Scripts' -Recurse |
Group-Object Severity | Select-Object Name, Count
まずはこの2つを実行して、自社の資産がどういう状態にあるかを数字で把握します。何百件出ても驚く必要はありません。ほとんどの現場で最初はそうなります。
何が返るか。Invoke-ScriptAnalyzer は指摘1件を1オブジェクトとして返し、Severity RuleName ScriptName Line Message などのプロパティを持ちます。1つ目のコマンドは、それを重大度順に「どのファイルの何行目が、どのルールに、なぜ引っかかったか」として1行ずつ並べます。2つ目は Name(Error / Warning / Information)と Count の2列だけを返すので、まずこの数行の数字を控えてください。段階導入(第6章)が進んでいるかどうかは、この数字の推移で測ります。指摘が1件もなければどちらも何も表示しません(出力が空 = 合格です)。
利用可能なルールの一覧と説明は Get-ScriptAnalyzerRule で確認できます。1
Get-ScriptAnalyzerRule | Select-Object Severity, RuleName, CommonName | Sort-Object Severity
Get-ScriptAnalyzerRule -Name PSAvoidUsingWriteHost | Format-List * # 個別ルールの説明
3. まず効く指摘 ── 実務での優先順位
数十あるルールのうち、社内スクリプトの品質に直結するものを優先度順に挙げます。
| ルール | 重大度 | 何を検出するか | なぜ重要か |
|---|---|---|---|
PSAvoidUsingPlainTextForPassword |
Warning | パラメーターで平文パスワードを受け取っている | 資格情報の平文保持は監査でも指摘される6 |
PSAvoidUsingConvertToSecureStringWithPlainText |
Error | 平文からSecureStringを作っている | 上と同根。暗号化の意味がなくなる |
PSUseDeclaredVarsMoreThanAssignments |
Warning | 代入されたが一度も使われていない変数 | 変数名のタイプミスを検出できる。実質的なバグ検出 |
PSAvoidUsingInvokeExpression |
Warning | Invoke-Expression の使用 |
文字列をコードとして実行するため、注入の温床になる |
PSUseShouldProcessForStateChangingFunctions |
Warning | 状態変更を伴う関数に -WhatIf がない |
危険な操作を事前確認できない設計を検出 |
PSAvoidUsingCmdletAliases |
Warning | ls % ? などのエイリアス |
対話では便利でも、スクリプトでは可読性を損なう |
PSUseApprovedVerbs |
Warning | 承認されていない動詞の関数名 | Get-/Set- などの規約に乗らないと発見されにくい |
PSAvoidGlobalVars |
Warning | グローバル変数の使用 | 副作用が読めなくなる。テストも書けない |
PSUseSingularNouns |
Warning | 複数形の名詞(Get-Users など) |
PowerShellの命名規約。他人が推測できる名前にする |
重大度の列を見てください。9件のうちErrorは1件だけで、残りはすべてWarningです。7 平文パスワードの検出(PSAvoidUsingPlainTextForPassword)ですらWarningなので、CIの失敗条件を「重大度Errorのみ」にすると、この表のほとんどが素通りします。重大度はルールごとに決まっているので、重大度に関係なく落としたいルールはルール名で指定します(第6章・第7章)。手元で確認するなら Get-ScriptAnalyzerRule | Select-Object Severity, RuleName です。
とくに PSUseDeclaredVarsMoreThanAssignments は費用対効果が高い指摘です。$fileName に代入したつもりが後段で $fileNmae を参照している、といったタイプミスを「代入されたのに使われていない変数」として拾えるため、静的解析でしか見つからないバグを実際に捕まえます(なお、PowerShellの変数名は大文字小文字を区別しないため、$fileName と $filename は同一の変数です。この種の検出で拾えるのは綴り自体が違うケースです)。
4. 設定ファイルでチーム基準を固定する
各自が別の基準で解析していると意味がありません。PSScriptAnalyzerSettings.psd1 をリポジトリに置き、全員とCIが同じ設定を使うようにします。2
# PSScriptAnalyzerSettings.psd1
@{
# 既定のルールセットを使う
IncludeDefaultRules = $true
# 段階導入の第1段階では Error と Warning に限定する
Severity = @('Error', 'Warning')
# 社内方針として今は見送るルール(理由をコメントで残す)
ExcludeRules = @(
'PSAvoidUsingWriteHost' # 対話ツールが多く、当面は許容する
'PSUseSingularNouns' # 既存の関数名を一斉に変えられないため
)
# ルールごとの詳細設定
Rules = @{
PSUseCompatibleSyntax = @{
# 5.1と7の両方で動く必要があるスクリプト群を検査する。
# TargetVersionsに指定できるのはルールが構文定義を持つバージョンだけ
# (Get-ScriptAnalyzerRule で確認できる)。未対応の値を書くと
# 設定の読み込み時にエラーになるので注意
Enable = $true
TargetVersions = @('5.1', '7.0')
}
PSPlaceOpenBrace = @{
Enable = $true
OnSameLine = $true
NewLineAfter = $true
IgnoreOneLineBlock = $true
}
PSUseConsistentIndentation = @{
Enable = $true
IndentationSize = 4
Kind = 'space'
}
}
}
Invoke-ScriptAnalyzer -Path . -Recurse -Settings .\PSScriptAnalyzerSettings.psd1
この設定ファイルは、VS Codeの拡張機能も読みます。PowerShell拡張機能の設定 powershell.scriptAnalysis.settingsPath の既定値が PSScriptAnalyzerSettings.psd1 なので、この名前でリポジトリ直下に置いておけば、編集中に出る警告とCIの判定基準が自動的にそろいます。8 別の名前にする場合やサブフォルダーに置く場合は、この設定でパスを明示してください。ここがずれると「手元では何も出ないのにCIで落ちる」が起き、せっかくの即時フィードバックが信用されなくなります。
PSUseCompatibleSyntax は、5.1と7が混在する環境で特に有用です。7専用の構文(三項演算子やパイプライン連鎖演算子など)を5.1向けスクリプトに書いてしまう事故を、実行前に検出できます。移行方針そのものは「Windows PowerShell 5.1とPowerShell 7の違い」を参照してください。
5. 例外は「理由付き」で残す
どうしても指摘に従えない箇所は、ルール全体を無効化するのではなく、その場所だけ抑止します。3
function Show-KsBanner {
# 対話ツールの装飾表示が目的のため、意図的にWrite-Hostを使う
[Diagnostics.CodeAnalysis.SuppressMessageAttribute(
'PSAvoidUsingWriteHost', '',
Justification = '対話実行専用の表示関数。値は返さない設計のため')]
[CmdletBinding()]
param([string] $Title)
Write-Host ('=' * 60) -ForegroundColor Cyan
Write-Host $Title -ForegroundColor Cyan
}
Justification を必ず書くのがポイントです。理由のない抑止は、次に読む人にとって「単に警告を消しただけ」と区別がつきません。これはコード上のADR(意思決定記録)のようなもので、考え方は「ADR(アーキテクチャ決定記録)を小さなチームで使う」に通じます。
6. 既存資産への段階導入
数百件の警告を前にして「全部直してから導入」と考えると、まず頓挫します。段階を分けます。
第1段階: 出血を止める(1日)
Severity = 'Error' のみを対象にCIへ入れ、これをゼロにします。ここで注意が必要なのは、重大度はルールごとに決まっており、直感と一致しないことです。たとえば PSAvoidUsingPlainTextForPassword の重大度は Warning で、Errorだけを失敗条件にすると引っかかりません。6 資格情報まわりのように「重大度に関係なく落としたい」ルールは、次のようにルール名で明示して失敗条件に加えます。
# まず解析結果を取得する
$issues = Invoke-ScriptAnalyzer -Path . -Recurse -Settings .\PSScriptAnalyzerSettings.psd1
# 重大度Error + 個別に指定した重大ルールを、CIの失敗条件にする
$mustFix = @(
'PSAvoidUsingPlainTextForPassword'
'PSAvoidUsingConvertToSecureStringWithPlainText'
'PSAvoidUsingUsernameAndPasswordParams'
)
$blocking = $issues | Where-Object { $_.Severity -eq 'Error' -or $_.RuleName -in $mustFix }
第2段階: 新規・変更分を守る(1週間) 変更されたファイルだけを解析対象にします。既存の負債はそのままでも、新しい問題が増えるのを止められます。
CIで実行する場合、比較対象のコミットが取得済みである必要があります。actions/checkout は既定で1コミットしか取得しないため、fetch-depth: 0 を指定するか、ベースブランチを明示的にfetchしてください(指定しないと unknown revision で失敗します)。
もう1点、比較先を main に固定しないでください。git diff A...HEAD は「AとHEADの共通の祖先からの差分」を意味するため、develop やリリースブランチ宛のPRで origin/main...HEAD を使うと、そのPRが触っていない変更まで解析対象に入り、無関係なファイルの既存指摘でCIが落ちます。GitHub ActionsではPRのターゲットブランチが GITHUB_BASE_REF に入るので、これを使います。9
さらに、git の失敗を必ず検知してください。PowerShellは既定では、外部コマンドがゼロ以外の終了コードを返しても終了エラーにしません。10 そのため、ベースrefが取得できていない状態では git diff が失敗して出力が空になるだけで、後続は「変更ファイルなし」と解釈し、1ファイルも解析しないままCIが緑になります。差分チェックで最も危険な壊れ方がこれです。実行直後に $LASTEXITCODE を確認して明示的に落としてください(PowerShell 7.3以降なら $PSNativeCommandUseErrorActionPreference = $true を設定する方法もあります)。10
- uses: actions/checkout@v4
with:
fetch-depth: 0 # 差分を取るには履歴が必要
# 変更されたps1/psm1だけを解析する(CIでの差分チェック)
# 比較先をmainに固定しない。developやリリースブランチ宛のPRでは、
# mainとの差分に無関係な変更まで含まれ、触っていないファイルで落ちる。
# PRのターゲットブランチは GITHUB_BASE_REF から取れる(push時は空)
$base = if ($env:GITHUB_BASE_REF) { "origin/$($env:GITHUB_BASE_REF)" } else { 'origin/main' }
# -c core.quotePath=false を付けないと、日本語を含むパスが
# "scripts/\346..." のように引用符+8進エスケープで返り、拡張子の判定から漏れる
$diff = git -c core.quotePath=false diff --name-only "$base...HEAD"
# ネイティブコマンドの失敗は、既定では終了エラーにならない。ベースrefが未取得だと
# gitが失敗して出力が空になり、「変更なし = 解析対象ゼロ = 合格」で素通りする。
# 直後に $LASTEXITCODE を見て明示的に落とす
if ($LASTEXITCODE -ne 0) {
throw "git diff に失敗しました (exit $LASTEXITCODE)。ベースブランチ $base を取得できていない可能性があります"
}
$changed = $diff |
Where-Object { $_ -match '\.ps(m|d)?1$' } | # .ps1 / .psm1 / .psd1 を対象にする
Where-Object { Test-Path $_ }
# -Pathは単一のパスを受け取るパラメーターなので、配列をそのまま渡すと
# パラメーターバインドで失敗する。1ファイルずつ解析して結果を集約する
$issues = foreach ($file in $changed) {
Invoke-ScriptAnalyzer -Path $file -Settings .\PSScriptAnalyzerSettings.psd1
}
第3段階: 範囲を広げる(継続)
ExcludeRules を1つずつ外し、対応した分だけ厳格にしていきます。リファクタリングの機会に既存ファイルを直し、負債を減らしていきます。
自動修正が効く指摘は -Fix で一括処理できます(適用前に必ず差分を確認してください)。1 整形だけなら Invoke-Formatter が使えます。4
Invoke-ScriptAnalyzer -Path .\Scripts -Recurse -Fix -Settings .\PSScriptAnalyzerSettings.psd1
git diff # 何が変わったかを必ず目視する
7. CIで自動化する
GitHub Actionsなら、Windowsランナーで数行です。ポイントはErrorで失敗させ、Warningは表示にとどめることです。
name: powershell-lint
on:
pull_request:
paths: ['**/*.ps1', '**/*.psm1', '**/*.psd1']
jobs:
analyze:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # 差分解析に切り替える場合に必要(6章)
- name: Install PSScriptAnalyzer
shell: pwsh
run: |
Set-PSRepository -Name PSGallery -InstallationPolicy Trusted
Install-Module PSScriptAnalyzer -Scope CurrentUser -Force
- name: Analyze
shell: pwsh
run: |
$issues = Invoke-ScriptAnalyzer -Path . -Recurse `
-Settings ./PSScriptAnalyzerSettings.psd1
# 全件をログに出す(警告も見えるようにする)
$issues | Sort-Object Severity, ScriptName, Line |
Format-Table Severity, RuleName, ScriptName, Line, Message -AutoSize |
Out-String -Width 200 | Write-Host
# 失敗条件 = 重大度Error + 重大度に関係なく許容しないルール
$mustFix = @(
'PSAvoidUsingPlainTextForPassword'
'PSAvoidUsingConvertToSecureStringWithPlainText'
'PSAvoidUsingUsernameAndPasswordParams'
)
$blocking = @($issues | Where-Object { $_.Severity -eq 'Error' -or $_.RuleName -in $mustFix })
$warns = @($issues | Where-Object Severity -eq 'Warning')
Write-Host "Blocking: $($blocking.Count) / Warning: $($warns.Count)"
# 段階導入が進んだらWarningも条件に加える
if ($blocking.Count -gt 0) {
throw "$($blocking.Count) 件の要修正指摘があります"
}
Pesterのテストと同じワークフローにまとめると、「lintが通る → テストが通る → マージできる」という流れが作れます。WindowsアプリのCI/CD全般の組み方は「WinForms / WPFアプリのCI/CD実践」を参照してください。
CIがない環境でも、月次で Invoke-ScriptAnalyzer を回して結果をCSVに残すだけで、資産の状態は十分に可視化できます。
Invoke-ScriptAnalyzer -Path '\\fileserver\scripts' -Recurse |
Select-Object Severity, RuleName, ScriptName, Line, Message |
Export-Csv "D:\棚卸\lint_$(Get-Date -f yyyyMM).csv" -Encoding utf8BOM -NoTypeInformation
8. 実務の定石(判断表)
| 論点 | 選択肢 | 判断の目安 |
|---|---|---|
| 導入順序 | Pesterから / PSScriptAnalyzerから | 静的解析はテストを書かずに初日から効く |
| 最初の対象 | 全ルール / Severity=Error + 資格情報系ルールを名指し | 全部を条件にすると誰も通らない。重大度はルールごとに決まっている点に注意6 |
| 既存の大量警告 | 全部直す / 変更ファイルのみ厳格に | 増加を止めてから、機会を見て減らす |
| 個別の例外 | ExcludeRules / SuppressMessageAttribute + Justification |
影響範囲を最小に。理由を必ず残す3 |
| 設定の共有 | 各自の設定 / リポジトリの .psd1 |
CIと開発者で基準を一致させる2 |
| 5.1と7の混在 | 実行して確認 / PSUseCompatibleSyntax |
構文レベルの非互換を実行前に検出 |
| 自動修正 | 手作業 / -Fix + 差分確認 |
適用後に必ず git diff を見る1 |
| 編集時のフィードバック | CIのみ / VS Code拡張機能 | その場で直せるのが最も安い5 |
9. まとめ
- PSScriptAnalyzerは公式の静的解析モジュールで、テストを書かずに導入でき、初日から効果が出ます。
- まずはErrorをゼロにし、次に変更ファイルのみを厳格にチェックする、という段階導入が現実的です。重大度はルールごとに決まっているため、平文パスワード(Warning)のように落としたいルールはルール名で失敗条件に加えます。
PSUseDeclaredVarsMoreThanAssignmentsのように、変数名のタイプミスという実バグを拾えるルールがあります。- 設定は
PSScriptAnalyzerSettings.psd1に集約してリポジトリに置き、開発者とCIで基準を揃えます。 - 例外は
SuppressMessageAttributeに理由を書いて残します。ルールごと除外するのは最後の手段です。 - CIではErrorで失敗、Warningは可視化。Pesterと同じワークフローに載せると、品質の門番が1か所にまとまります。
サンプルコードのダウンロード
この記事で扱ったコードは、そのまま動かせる形にまとめて配布しています。設定ファイル・CI合否判定スクリプト・GitHub Actions の例が入っています。
この記事のサンプルは、PowerShell 7.6 で実際に実行して検証しています(Pester 14件)。zipに含まれる Invoke-SampleTests.ps1 を実行すれば、お手元でも同じ検証を再現できます。
# 構文解析 + 静的解析 + Pesterテスト
./Invoke-SampleTests.ps1
設定値(パス、サーバー名、テナントIDなど)は例です。そのまま本番環境で実行せず、自社の環境に合わせて読み替えてください。
関連記事
- PesterによるPowerShellのテスト整備 ── 運用スクリプトを壊しにくくする実務の型
- PowerShellスクリプトの引数設計とモジュール化 ── 「動くスクリプト」から「人に渡せるスクリプト」へ
- PowerShellでの資格情報の安全な扱い ── 平文パスワードをスクリプトから追放する
- Windows PowerShell 5.1とPowerShell 7の違い ── 社内スクリプト移行の実務ガイド
- WinForms / WPFアプリのCI/CD実践 ── GitHub Actionsでビルドから署名・配布まで自動化する
- ADR(アーキテクチャ決定記録)を小さなチームで使う
関連する相談領域
合同会社小村ソフトでは、社内スクリプト資産の棚卸しと品質基準の策定、静的解析・テストのCI導入、属人化した運用スクリプトの保守性改善を扱っています。
参考リンク
-
Microsoft Learn, PSScriptAnalyzer モジュールの概要. PSScriptAnalyzerがPowerShellスクリプト・モジュール向けの静的解析ツールであること、Invoke-ScriptAnalyzerによる解析と-Path / -Recurse / -Settings / -Fix / -ExcludeRule などのパラメーター、Get-ScriptAnalyzerRuleによるルール一覧の取得、診断結果が重大度(Error / Warning / Information)を持つことについて。 ↩ ↩2 ↩3 ↩4 ↩5 ↩6
-
Microsoft Learn, PSScriptAnalyzer の設定ファイル. 設定ファイル(.psd1)でSeverity・IncludeRules・ExcludeRules・IncludeDefaultRules・Rulesなどを指定できること、-Settingsパラメーターで設定ファイルを渡せること、ルールごとの詳細設定(PSUseCompatibleSyntaxのTargetVersionsや整形系ルールのオプション)について。 ↩ ↩2 ↩3
-
Microsoft Learn, PSScriptAnalyzer のルール抑制. System.Diagnostics.CodeAnalysis.SuppressMessageAttributeによりルール単位・対象単位で診断を抑止できること、RuleName・Target・Justificationの各引数について。 ↩ ↩2 ↩3
-
Microsoft Learn, Invoke-Formatter. 設定に基づいてスクリプトテキストを整形すること、整形ルール(インデント、開き波かっこの位置、空白の扱いなど)を設定ファイルで指定できることについて。 ↩ ↩2
-
Microsoft Learn, Visual Studio Code で PowerShell を使用する. PowerShell拡張機能がPSScriptAnalyzerを利用して編集中に警告を表示すること、書式設定機能を提供することについて。 ↩ ↩2
-
Microsoft Learn, AvoidUsingPlainTextForPassword. パスワードや秘密情報を平文の文字列型パラメーターで受け取るべきではなくSecureStringまたはPSCredentialを使うべきであること、およびこのルールの重大度(Severity Level)がWarningであり常に有効であることについて。関連ルールとしてAvoidUsingConvertToSecureStringWithPlainText(平文からSecureStringを生成すると秘密が保護されないこと)も参照。 ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, PSScriptAnalyzerのルール一覧. 組み込みルールの一覧と、ルールごとの重大度(Severity)・既定で有効かどうか・設定可能かどうかが表にまとめられていること。本文の表に挙げたルールの重大度(AvoidUsingConvertToSecureStringWithPlainTextがError、AvoidUsingPlainTextForPassword・UseDeclaredVarsMoreThanAssignments・AvoidUsingInvokeExpression・UseShouldProcessForStateChangingFunctions・AvoidUsingCmdletAliases・UseApprovedVerbs・AvoidGlobalVars・UseSingularNounsがWarning)、および第6章・第7章で名指ししているAvoidUsingUsernameAndPasswordParamsがErrorであることは、この一覧と各ルールの個別ページに基づきます。 ↩
-
PowerShell/vscode-powershell, package.json(拡張機能の設定定義). powershell.scriptAnalysis.settingsPath がPSScriptAnalyzerの設定ファイルへのパスを指定する設定であり、既定値が
PSScriptAnalyzerSettings.psd1であること、powershell.scriptAnalysis.enable で編集中のリアルタイム解析の有効・無効を切り替えられることについて。 ↩ -
GitHub Docs, Variables reference ─ Default environment variables. pull_requestイベントでGITHUB_BASE_REFにPRのターゲットブランチ名が入ること(それ以外のイベントでは空であること)について。三点リーダー記法の意味(明示した2つのrefのマージベースからの差分)はGit公式のgit diffを参照。 ↩
-
Microsoft Learn, about_Preference_Variables ─ $PSNativeCommandUseErrorActionPreference. ネイティブコマンドのゼロ以外の終了コードが既定では終了エラーにならないこと、PowerShell 7.3で導入されたこの設定を$trueにすると$ErrorActionPreferenceに従って終了エラーになること、直近の外部コマンドの終了コードが$LASTEXITCODEで取得できることについて。 ↩ ↩2
関連する記事
同じタグを共有する最新の記事です。さらに近い話題で知識を深められます。
Write-Hostをやめる ── PowerShellの出力ストリームとログ設計
PowerShellの6つの出力ストリームの使い分け、Write-Hostが抱える問題と正しい使いどころ、関数の戻り値が汚れる原因、-Verboseや-InformationVariableによる呼び出し側制御、構造化ログの残し方を整理します。
PowerShellモジュールの社内配布と更新 ── PSResourceGetと社内リポジトリ
共有フォルダーのps1をコピーして使い回す運用から卒業する方法をまとめます。モジュールマニフェストの書き方、バージョニング、PSResourceGetによる社内リポジトリの構築と配布・更新、署名との組み合わせまで解説します。
PowerShellスクリプトが遅いときに見るところ ── 配列・パイプライン・突合の勘所
PowerShellスクリプトが遅い原因の定番を整理します。配列の+=がO(n^2)になる理由、パイプラインとforeachの差、突合のハッシュテーブル化、ファイルI/Oの改善、そして正しい測り方までを実務目線で解説します。
PowerShellの並列処理 ── ForEach-Object -Parallelとジョブの使い分け
ForEach-Object -Parallel・Start-ThreadJob・Start-Jobの違いと使い分け、$using:とスレッド安全性、ThrottleLimitの決め方、かえって遅くなるケースまでを実務目線で整理します。
PowerShellから外部exeを正しく呼ぶ ── 引数のクォート・終了コード・文字化けの落とし穴
PowerShellからrobocopyや社内EXEを呼ぶと引数が壊れる、終了コードが取れない、出力が文字化けする。PowerShell 7.3の引数渡し変更、停止解析トークン--%、Start-Processの使い分けまでを実務目線で整理します。
関連トピック
このテーマと近いトピックページです。記事を起点に、関連するサービスや他の記事へ進めます。
Windows技術トピック
Windows 開発、不具合調査、既存資産活用の技術トピックをまとめた入口です。
よくある質問
この記事のテーマについて、相談時によくある質問をまとめています。
- PSScriptAnalyzerを既存スクリプトに掛けたら、警告が数百件出ました。どこから手を付ければよいですか?
- いきなり全部を直そうとしないでください。実務的な進め方は、まず重大度Errorのものだけを対象にして、それをゼロにすることです。なお、重大度はルールごとに決まっていて、たとえば平文パスワードを検出するPSAvoidUsingPlainTextForPasswordはWarningです。credentialまわりのように重大度に関係なく落としたいルールがある場合は、CIの失敗条件にルール名を明示して加えてください。次に、これから変更するファイルだけを解析対象にするルールをCIに入れ、新しく増える問題を止めます。既存の警告は「今は許容する」と決めて設定ファイルで除外し、リファクタリングのついでに1つずつ減らしていくのが現実的です。
- 特定の箇所だけ警告を抑止したいのですが、どうすればよいですか?
- その関数やスクリプトにSuppressMessageAttributeを付けます。System.Diagnostics.CodeAnalysis.SuppressMessageAttributeにルール名を指定し、Justificationに理由を書いてください。理由を書くことが重要で、後から読んだ人が「なぜ例外なのか」を判断できます。ルール全体を無効にしたいなら設定ファイルのExcludeRulesに書きますが、こちらは影響範囲が広いので、まず個別抑止で足りないかを検討してください。
- Write-Hostを使うと警告が出ます。使ってはいけないのですか?
- PSAvoidUsingWriteHostは、値を返すべき場面でWrite-Hostを使うと出力を取り出せなくなる、という設計上の指摘です。対話的なツールで装飾表示を行う目的なら、SuppressMessageAttributeに理由を書いて抑止するのが妥当です。一方、無人実行のスクリプトでWrite-Hostしか使っていない場合は、警告のとおり見直す価値があります。ルールに機械的に従うのではなく、指摘の意図を理解して判断してください。
- PesterとPSScriptAnalyzerは、どちらを先に導入すべきですか?
- PSScriptAnalyzerを先に入れるほうが、導入コストに対する効果が大きいです。テストコードを一切書かずに、コマンド1つで全スクリプトを解析でき、初日から効果が出ます。Pesterはテストを書く作業が必要なぶん立ち上がりに時間がかかりますが、ロジックの正しさを守れるのはテストだけです。順序としては、まず静的解析をCIに入れて明らかな問題を止め、次に壊れると困る処理からPesterのテストを足していく流れをおすすめします。
- CIサーバーがない小さなチームでも導入する意味はありますか?
- あります。GitやCIがなくても、共有フォルダーのスクリプト一式に対してInvoke-ScriptAnalyzer -Path . -Recurseを実行するだけで棚卸しになります。結果をCSVに出して「重大度Errorが何件あるか」を月次で見るだけでも、資産の状態が可視化されます。加えて、VS CodeのPowerShell拡張機能はPSScriptAnalyzerを内蔵しているため、編集中にその場で警告が出ます。これだけでも書き方の癖は着実に改善します。