PowerShellスクリプトの作成に慣れてくると、コードのモジュール化について学ぶ必要があります。モジュール化とは、コードをビルディングブロックで作成することを指す一般的な用語です。PowerShellの世界では、PowerShell関数がこれを行うための最良の方法の一つです。
PowerShellスクリプトを書く際には、コードを書く方法には多くのオプションがあります。単一の、途切れないコードブロックで何百ものタスクを実行するために、何千行ものコードを書くこともできますが、それは災害となるでしょう。代わりに、関数を書くべきです。
関数は、コードの使いやすさと読みやすさを大幅に向上させ、作業がはるかに容易になります。このチュートリアルでは、関数の書き方、関数のパラメーターの追加と管理、およびパイプライン入力の受け入れ設定について学びます。しかし、まずは用語について見てみましょう。
この記事は私の書籍「PowerShell for Sysadmins: Workflow Automation Made Easy」から抜粋されました。このチュートリアルで何か学んだ場合は、ぜひ書籍をチェックして、関数や他の多くのPowerShellの便利な機能についてもっと学んでください。
関数とコマンドレット
関数の概念はおそらくおなじみのものであるかもしれません。おそらくすでに使用しているStart-Service
やWrite-Host
などのコマンドは、関数と似ています。これらは、単一の問題を解決する名前付きのコードです。関数とコマンドレットの違いは、それぞれの構造がどのように作成されるかです。
A cmdlet isn’t written with PowerShell. It’s written in another language, typically something like C#. The cmdlet is then compiled and made available inside PowerShell.
一方、関数はPowerShellで書かれています。他の言語ではありません。
以下のように、Get-Command
コマンドレットとそのCommandType
パラメータを使用して、コマンドがコマンドレットか関数かを確認できます。
上記のコマンドは、PowerShellセッションに現在ロードされているすべての関数、またはPowerShellで利用可能なモジュール内の関数を返します。
関連記事: PowerShellモジュールの理解と構築
前提条件
すべての例に従いたい場合は、PowerShellのバージョンが利用可能であることを確認してください。このチュートリアルには特定のバージョン要件はありません。また、コードスニペットをコピー、貼り付け、実行するための良いコードエディタ(例:Visual Studio Code)を使用できるようにしてください。
シンプルな関数の作成
関数を使用する前に、それを定義する必要があります。関数を定義するには、関数のキーワードを使用し、その後に記述的なユーザー定義名を続け、中括弧のセットを続けます。中括弧の内側には、PowerShellが実行するスクリプトブロックがあります。
以下に基本的な関数とその関数を実行する例が示されています。この関数はInstall-Software
という名前で呼ばれ、Write-Host
を使用してコンソールにメッセージを表示します。定義されると、この関数の名前を使用して、スクリプトブロック内のコードを実行できます。
動詞-名詞で命名する
A function’s name is important. You can name your functions whatever you want, but the name should always describe what the function does. The function-naming convention in PowerShell is the Verb-Noun syntax.
関数名は常に動詞で始め、ダッシュと名詞が続くようにします。”承認済み”の動詞のリストを見つけるには、Get-Verb
コマンドレットを使用します。
関数の動作の変更
関数の動作を変更したい場合は、以下のように関数が実行するコードを変更するだけです。
関数内のコードを変更したため、表示されるメッセージが若干異なるようになりました。
高度な関数の定義
関数はさまざまな場所で定義することができます。上記のセクションでは、チュートリアルではコードをPowerShellコンソールに直接コピーして貼り付けたものとしています。しかし、これは唯一の方法ではありません。スクリプトでも関数を定義できます。
前のセクションでは、小さな関数を扱っていたので、コンソールに定義することはそれほど問題ではありませんでした。しかし、ほとんどの場合、より大きな関数を持つことになります。そのような関数をスクリプトまたはモジュールに定義し、そのスクリプトまたはモジュールを呼び出して関数をメモリにロードする方が簡単です。
大きな関数を調整するたびに再入力するのは少しイライラするかもしれません。
I suggest you now open your favorite editor and store the function in a .ps1 file as you work through the rest of the tutorial.
関数にパラメータを追加する
PowerShell関数には任意の数のパラメータを持たせることができます。独自の関数を作成する際には、パラメータを含めるかどうかを選択し、それらのパラメータがどのように機能するかを決定することができます。パラメータは必須またはオプションであり、任意のものを受け入れるか、可能な引数のリストの中から強制的に1つを受け入れるかのいずれかです。
関連記事:PowerShellパラメータについて知りたかったすべて
たとえば、以前のInstall-Software
関数でインストールしている架空のソフトウェアには多くのバージョンがあるかもしれません。しかし、現在のInstall-Software
関数ではユーザーがどのバージョンをインストールしたいかを指定する方法はありません。
もし関数を使用するのがあなただけなら、特定のバージョンが必要なたびにコードを変更することができますが、それは時間の無駄です。このプロセスは潜在的なエラーの原因にもなりますし、他の人があなたのコードを使用できるようにしたいですよね。
関数にパラメータを導入することで、その関数に変動性を持たせることができます。変数が同じ状況の多くのバージョンを処理できるスクリプトを書くことを可能にしたように、パラメータは1つのことを多くの方法で行う単一の関数を書くことができます。
この場合、同じソフトウェアのバージョンをインストールし、多くのコンピュータ上で実行したいと考えています。
まず、関数にパラメータを追加し、インストールするバージョンを指定できるようにします。
シンプルなパラメータの作成
関数にパラメータを作成するには、param
ブロックが必要です。 param
ブロックには、関数のすべてのパラメータが含まれます。 以下に示すように、param
キーワードの後にカッコを付けてparam
ブロックを定義します。
この時点では、関数の実際の機能はまったく変わりません。 パラメータを準備するために、配管をインストールしただけです。
現時点では、関数は実際にソフトウェアをインストールしていません。 ソフトウェアのインストールをシミュレートするために、単に
Write-Host
コマンドレットを使用しています。
param
ブロックを追加したら、以下のようにparam
ブロックのカッコ内にパラメータを作成できます。
上記のparam
ブロックの内部では、まずパラメータブロックを定義します。 ここで使用しているParameter()
ブロックを使用すると、「高度なパラメータ」に変換されます。 ここでの空のパラメータブロックは何もしませんが、必要です。次のセクションで使用方法を説明します。
代わりに、パラメータ名の前にある[string]
型に焦点を当てましょう。パラメータの値を特定の型にキャストするために、パラメータ変数名の前に角括弧でパラメータの型を指定します。PowerShellは、このパラメータに渡される値を常に文字列に変換しようとします – もしそれが既に文字列でない場合は。上記のコードでは、$Version
として渡されるものは常に文字列として扱われます。
パラメータを型にキャストすることは必須ではありませんが、強くお勧めします。それは明示的に型を定義し、将来のエラーを大幅に減らすでしょう。
また、$Version
をWrite-Host
ステートメントに追加します。これにより、Install-Software
関数をVersion
パラメータで実行し、バージョン番号を渡すと、以下に示すようなステートメントが表示されるはずです。
では、このパラメータで何ができるか見てみましょう。
必須パラメータ属性
パラメータブロックを使用して、パラメータの動作を変更するためのさまざまなパラメータ属性を制御することができます。たとえば、関数を呼び出す際に特定のパラメータを渡す必要があるようにしたい場合、そのパラメータを必須として定義することができます。
デフォルトでは、パラメータはオプションです。以下のように、パラメータブロック内にMandatoryキーワードを使用して、ユーザーにバージョンの指定を強制しましょう。
上記の関数を実行すると、以下のプロンプトが表示されるはずです。

Mandatory属性を設定した後、パラメーターなしで関数を実行すると、ユーザーが値を入力するまで実行が停止します。関数は、ユーザーがVersion
パラメーターに値を指定するまで待機します。入力されると、PowerShellは関数を実行します。
強制パラメータープロンプトを回避するには、以下のように関数を呼び出す際にパラメーターに値を渡すだけです。
デフォルトパラメーター値
たとえば、あるパラメーターに同じ値を何度も渡す場合、デフォルトパラメーター値を定義できます。デフォルトパラメーターは、ほとんどの場合に特定の値を期待する場合に便利です。
例えば、このソフトウェアのバージョン2を90%の場合にインストールしたい場合、毎回値を設定する必要がないように、$Version
パラメーターにデフォルト値2を割り当てることができます。以下にこの例を示します。
デフォルトパラメーターを持つことは、渡すパラメーターを防ぐものではありません。渡された値はデフォルト値を上書きします。
パラメーターの検証属性の追加
関数を介してパラメーターに渡すことができる値を制限することは常に良い考えです。そのために最適な方法は、パラメーターの検証属性を使用することです。
ユーザー(あるいは自分自身)が関数やスクリプトに渡すことができる情報を制限することは、関数内の不要なコードを排除するために役立ちます。例えば、既存のバージョンであるバージョン3の値をInstall-Software
関数に渡す場合を考えてみましょう。
あなたの機能は、すべてのユーザーが存在するバージョンを知っていることを前提としているため、バージョン4を指定しようとした場合にどうなるかを考慮していません。その場合、フォルダが存在しないため、適切なフォルダを見つけることができません。
パラメータの検証がない場合の問題
以下の例を確認してください。ファイルパスで$Version
文字列を使用する場合、既存のフォルダ名を完了しない値(例:SoftwareV3やSoftwareV4)を渡すと、コードが失敗します。

この問題に対処するためには、エラーハンドリングコードを書くか、ユーザーに既存のソフトウェアのバージョンのみを渡すように要求することで問題を事前に防ぐことができます。ユーザーの入力を制限するために、パラメータの検証を追加します。
パラメータの検証の追加
さまざまな種類のパラメータの検証が存在しますが、Install-Software
関数に関しては、[ValidateSet
属性](https://adamtheautomator.com/powershell-validateset/)が最適です。ValidateSet
検証属性を使用すると、パラメータに許可される値のリストを指定できます。たとえば、文字列1
または2
のみを考慮している場合、ユーザーはこれらの値のみを指定できるようにする必要があります。そうでない場合、関数は直ちに失敗し、ユーザーにその理由を通知します。
パラメータの検証属性を、以下に示すように、元のParameter
ブロックのすぐ下に追加します。
ValidateSet
属性の括弧内に項目(1
および2
)を追加することで、PowerShellに対してVersion
の有効な値が1
または2
であることを伝えます。セット内の値以外を指定しようとすると、以下のエラーメッセージが表示され、利用可能なオプションの数が特定されることをユーザーに通知します。

ValidateSet
属性は一般的なバリデーション属性ですが、他にも利用可能なものがあります。パラメータの値を制限する方法の詳細については、Microsoftのドキュメントをご覧ください。
パイプライン入力の受け入れ
これまで、通常の-ParameterName
関連記事:ATA PowerShellパラメータのパイプライン入力の受け入れ
最初に、コードに別のパラメータを追加して、ソフトウェアをインストールするコンピュータを指定します。また、Write-Host
の参照にもそのパラメータを追加して、インストールをシミュレートします。
関数にComputerName
パラメータを追加したら、以下のようにコンピュータ名とバージョンの値を渡してInstall-Software
関数を繰り返し実行できます。
これらすべてを行う代わりに、パイプラインを使用する方法について学ぶべきです。
関数をパイプラインに対応させるための準備
残念ながら、以前に作成した単純な関数ではPowerShellのパイプラインを活用することはできません。関数が受け入れるパイプライン入力のタイプを決定し、実装する必要があります。
A PowerShell function uses two kinds of pipeline input: ByValue
(entire object) and ByPropertyName
(a single object property). Here, because the $computers
array contains only strings, you’ll pass those strings via ByValue
.
パイプラインサポートを追加するには、以下のようにパラメータにパラメータ属性を追加します。ValueFromPipeline
またはValueFromPipelineByPropertyName
のキーワードを使用します。
Instal-Software
関数を更新したら、次のように呼び出します。
スクリプトを再実行すると、以下のような結果が得られます。
配列内の最後の文字列だけがInstall-Software
が実行されることに注意してください。次のセクションでこれを修正する方法について説明します。
processブロックの追加
PowerShellにこの関数を実行するよう指示するには、プロセスブロックを含める必要があります。プロセスブロックの中には、関数がパイプライン入力を受け取るたびに実行したいコードを記述します。以下のようにスクリプトにプロセスブロックを追加してください。
前と同様に関数を呼び出してください。今回は、Install-Software
関数は3つの行(各オブジェクトごとに1つ)を返します。
プロセスブロックには、実行したいメインのコードを記述します。また、関数呼び出しの最初と最後に実行されるコードには、begin
ブロックとend
ブロックを使用することもできます。Microsoftのドキュメントでbegin
、process
、end
ブロックについて詳しく学ぶことができます。
次のステップ
関数を使用すると、コードを独立したビルディングブロックに分割することができます。これにより、作業をより小さな、管理しやすいチャンクに分割するだけでなく、読みやすくテスト可能なコードを書くことを強制されます。
I now challenge you to look through some old scripts and see where you can add a PowerShell function. Look for patterns in code. Build a function from those patterns. Notice where you’re copying/pasting code snippets and turn those into PowerShell functions!