PowerShell PSakeを使用したタスクの自動化:ウォークスルー

PowerShellスクリプトでタスクを自動化する方法を学びましたね。素晴らしいです!しかし、今ではスクリプトとモジュールが乱雑になり、手動のスクリプト実行、スケジュールされたタスクなどが混在しています。この混沌を整理し、PowerShellの自動化オーケストレーションエンジンであるPSakeを導入する時が来ました。

PSakeはPowerShellモジュールとして書かれたオーケストレーションエンジンであり、スクリプトの順序を整理し、実行条件を完全に制御することができます。PSakeはビルド自動化ツールであり、主な使用ケースはビルドスクリプトです(通常はビルドサーバーでリリース自動化シナリオで実行されます)。しかし、日常のスクリプトシナリオでは一般的に見過ごされています。それを変えましょう。

このチュートリアルでは、PSakeの動作と、今日実践できる素晴らしい例を学んでいきます!

前提条件

この記事は、一緒に進めることを想定したチュートリアルです。正確に進めるためには、次のものを用意してください:

  • Windows PowerShell 3+。チュートリアルではWindows PowerShell v5.1を使用します。
  • GithubからのPSakeのzipファイル。このチュートリアルではv4.9.0を使用します。

PSakeモジュールのセットアップ

GithubからダウンロードしたPSakeプロジェクトをローカルマシンに持っていると仮定して、まずいくつかの手順を実行する必要があります。

  1. GithubからダウンロードしたPSakeのzipファイルを展開してください。
  2. srcフォルダーを展開されたZIPファイルの内容の中にある
  3. $env:PSModulePathの下のパスに移動し、PowerShellが新しいモジュールを認識するようにします。srcフォルダーの名前をPSakeに変更してください。
  4. 次に、Get-Module PSake -ListAvailable を実行して表示されるか確認してください。エラーが表示されない場合、準備ができています。

関連記事: PowerShellモジュールの理解と構築

基本的なPowerShell PSakeスクリプトの作成

PSakeを理解するために、何かを構築する必要があります。PSakeが何ができるかを確認するために、単純なPSakeスクリプトを作成しましょう。

  1. 名前がpsakefile.ps1のスクリプトを作成し、単一のタスクを追加してください。タスクは、少なくとも名前とアクションブロックを持つ必要があります。名前psakefile.ps1は必須ではありませんが、エンジンによって期待されるデフォルトの名前です。

A PSake task in its basic form is very similar to a PowerShell function:
a container for one or more commands that, when performed together, achieve a certain goal. These commands go into a script block that is passed to the Action parameter. A task has many advantages over a function. You will learn about these advantages as you read along.

以下は、比較的シンプルなタスクを持つpsakefile.ps1の例です。

task HelloWorld -Action {
    Write-Host '*** Hello World ***' -ForegroundColor Yellow
}

2. PSakeファイルを作成したので、PowerShellコンソールから Invoke-PSakeコマンドを使用して呼び出し、タスク名をTaskList パラメーターの値として渡すことができます。

Invoke-PSakeはPSakeの実行エンジンです。このコマンドはpsakefile.ps1で定義されたタスクをトリガーします。タスク名またはコンマ区切りのタスクのリストをTaskListパラメータに渡します。複数のタスクを実行する場合、TaskListに渡した順序で各タスクが実行されます。

以下はHelloWorldタスクをトリガーする方法です:

Invoke-PSake -BuildFile C:\Work\psakefile.ps1 -TaskList 'HelloWorld'

psakefile.ps1という名前のファイルを保持し、コンソールをそのフォルダに設定している限り、BuildFileパラメータとその値を省略することができます。

  1. Invoke-PSake を実行すると、PSakeの出力がコンソールに表示されます。psakefile.ps1のタスクを実行すると、以下のような出力が表示されます。
Psake script output

出力は以下のコンポーネントで構成されています:

  1. PSakeのバージョンの詳細。
  2. 各ビルドタスクの名前(PSakeではすべてのタスクをbuild タスクと見なします)。例:シアンでHelloWorldを実行します。
  3. タスクが生成した出力。例:黄色でHello Worldを表示します。
  4. 成功/失敗のメッセージ。例:緑でpsake succeeded…を表示します。
  5. 各タスクの所要時間とスクリプト全体の合計所要時間を含む時間の要約(Build Time Report)。

PSakeを使用してSQLをインストールする

前のセクションでは、ダミーのPSakeスクリプトを呼び出すだけでした。この知識を基に、SQLをインストールするPSakeスクリプトを作成してください!

この例では、次のようなPSakeスクリプトを作成します。

  1. マシン上の空きディスク容量を検証します。
  2. ローカルリポジトリからSQL ZIPファイルをダウンロードします。
  3. ZIPファイルを展開します。
  4. CドライブまたはDドライブ(どちらか存在する方)でインストールを実行します。

次に、この仕事にPSakeをどのように活用できるかを見てみましょう。

ビルディングブロックの設計

PSakeは、タスクの組み立てに関するものです。各タスクには一意の名前が必要であり、理想的にはPowerShell関数のような単一のアトミック操作を実行するべきです。このコンセプトを使用して、以下のステップを各タスクのために記述できます。

  1. ValidateDiskSpace
  2. DownloadSql
  3. ExtractSql
  4. InstallSqlDriveC
  5. InstallSqlDriveD

この段階では、実際に何かを行うためのコードを構築しているわけではありません。タスクの骨組みを作成し、PSakeファイルを作成しているだけです。後でタスクに追加するために、以下のタスク内のWrite-Hostの参照に気付くでしょう。

各タスクには常にDescriptionパラメータを使用する必要があります。Descriptionパラメータは、タスクの実行時およびコードのレビュー時に各タスクについての詳細情報を提供します。

task ValidateDiskSpace -Description 'Validate Disk Free Space' -Action {
	
	Write-Host "`n   *** Checking disk free space ***`n" -ForegroundColor Yellow
	
}

task DownloadSql -Description 'Download SQL Setup' -Action {
	
	Write-Host "`n   *** Downloading SQL Setup from LAN ***`n" -ForegroundColor Yellow
	
}

task ExtractSql -Description 'Extract SQL Setup' -Action {
	
	Write-Host "`n   *** Extracting SQL Setup files ***`n" -ForegroundColor Yellow
	
}

task InstallSqlDriveC -Description 'Install SQL on C:' -Action {
	
	Write-Host "`n   *** Installing SQL Server on C drive ... please wait... ***`n" -ForegroundColor Yellow

}

task InstallSqlDriveD -Description 'Install SQL on D:' -Action {
	
	Write-Host "`n   *** Installing SQL Server on D drive ... please wait... ***`n" -ForegroundColor Yellow
	
}

タスクの実行順序の定義

タスクのセットを含むPSakeファイルができました。この段階では、すべてのタスクを一度に実行するか、Invoke-PSakeコマンドで一部のタスクのみを実行することができます。

あなたは、Invoke-PSakeTaskListパラメーターを使用して、いくつかのタスク(またはすべてのタスク)を呼び出すことができます。前の簡単な例と同様に、タスクの名前を項目として定義した配列を作成します。

Invoke-PSakeは、配列で定義された順序で各タスクを実行します。

$taskList = @()

$taskList += 'ValidateDiskSpace'
$taskList += 'DownloadSql'
$taskList += 'ExtractSql'
$taskList += 'InstallSqlDriveC'
$taskList += 'InstallSqlDriveD'

Invoke-PSake -TaskList $taskList

上記のコードを実行すると、次のような結果が得られるはずです。

PSake script output

PreConditionの追加

特定の条件が満たされた場合にのみ、特定のアクションを実行する必要があるかもしれません。このチュートリアルの例であるSQLインストールスクリプトでは、たとえば、インストーラを格納するボリュームが利用可能かどうかをテストしてから、インストーラを起動するタスクを実行する必要があるかもしれません。

PreConditionパラメーターを使用して、タスクが実行されるかどうかを決定する、真または偽の値を返すコードを実行できます。

以下の例では、$installSqlOn_C_Drive$installSqlOn_D_Drive変数に注意してください。 Invoke-PSakeがこのスクリプトを呼び出すとき、これらの変数はCボリュームまたはDボリュームが存在するかどうかに応じて真または偽の値を含みます。

task行では、各タスクにPreConditionスクリプトブロックパラメーターがあり、これらの変数の値が含まれています。実行時には、これらの変数に応じてInstallSqlDriveCまたはInstallSqlDriveDタスクが実行されます。

$installSqlOn_C_Drive = (Test-Path -Path 'C:') -and (-not (Test-Path -Path 'D:'))
$installSqlOn_D_Drive = (-not (Test-Path -Path 'C:')) -and (Test-Path -Path 'D:')

task InstallSqlDriveC -Description 'Install SQL on C:' -PreCondition { $installSqlOn_C_Drive } -Action {
	
	Write-Host "`n   *** Installing SQL Server on C drive ... please wait... ***`n" -ForegroundColor Yellow
	
}

task InstallSqlDriveD -Description 'Install SQL on D:' -PreCondition { $installSqlOn_D_Drive } -Action {
	
	Write-Host "`n   *** Installing SQL Server on D drive ... please wait... ***`n" -ForegroundColor Yellow
	
}

タスクパラメーター

ActionおよびDescriptionに加えて、タスクは以下のパラメータをサポートしています:

  • PreCondition – ブール値を返すスクリプトブロック。Falseの場合、特定のタスクはスキップされます(使用例は上記に示されています)。
  • PostCondition – バリデーションステップ。ブール値を返すスクリプトブロックです。Falseはバリデーションが失敗し、スクリプト全体が停止することを意味します。
  • PreAction – タスクの実行前に実行されるスクリプトブロックです。
  • PostAction – タスクが成功した直後に実行されるスクリプトブロックです。
  • ContinueOnError – スイッチパラメータ。使用すると、タスクの実行中に発生する可能性のあるエラーがスクリプト全体の停止を引き起こさないようになります。
  • Depends – 実行される前に現在のタスクの実行が必要なタスクの名前(またはタスク名のリスト)です。PSakeはこの情報を使用して、タスクの依存関係を正しい順序で実行します。たとえば、タスクAがタスクBに依存している場合、PSakeエンジンはAの前にBを実行します。

最初は依存性のメカニズムは良いアイデアのように思えます。タスクを論理的な順序で設定するのに役立ちます。ただし、Dependsパラメータを使用すると、異なるタスクが結び付けられ、後で独立してテストするのが難しくなります。ただし、ユーザーはタスクの実行順序を明示的に設定し、PSakeファイルを呼び出す際にこの順序を渡すことができるため、Dependsパラメータの使用は完全に回避できます。

次に、これらのタスクパラメータの使用例を見てみましょう。

PreActionとPostConditionの追加

上記の例のInstallSqlDriveDタスクを出発点として、インストールに追加の要求があるかもしれません。

たとえば、インストールの開始時と終了時の時間を記録する必要があるかもしれません。これらの時間をSqlSetupStartDateSqlSetupEndDateという環境変数に記録する必要があります。さらに、インストールが完了した後に、D:\TempSqlFilesフォルダが存在しないことを確認する必要があります。

幸いなことに、PSakeのタスクパラメータPreActionPostAction およびPostCondition (それぞれ)は、まさにこれらの新しい要件を満たします。以下に、それがどのように行われるかの例を示します:

task InstallSqlDriveD -Description 'Install SQL on D:' -PreAction {

     Write-Host '*** Writing SQL install start time to env. var. SqlSetupStartDate ***' -ForegroundColor Yellow
     $date = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
[Environment]::SetEnvironmentVariable('SqlSetupStartDate',$date,'Machine')

 } -PreCondition { 

     $installSqlOn_D_Drive

 } -Action {  
  
     Write-Host '*** Installing SQL Server on D drive... please wait... ***' -ForegroundColor Yellow 

} -PostAction {     

    Write-Host '*** Writing SQL install end time to env. var. SqlSetupEndDate ***' -ForegroundColor Yellow     
    $date = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
[Environment]::SetEnvironmentVariable('SqlSetupEndDate',$date,'Machine') 

} -PostCondition { 
    
Write-Host '*** Verifying temp files deleted ***' -ForegroundColor Yellow
     # フォルダが存在する場合はFalseを返し、スクリプト全体を停止させる
     (-not (Test-Path -Path 'D:\TempSqlFiles'))

 }

PesterテストでPSakeスクリプトを実行する

PowerShellスクリプトを呼び出す場所なら、PSakeファイルを呼び出すこともできます。Pesterでインフラストラクチャテストを構築している場合、テスト内でPSakeを呼び出すことができます。

関連記事:PowerShellのためのPesterテストの作成

たとえば、DownloadSqlタスクを実行した後にフォルダにSQLセットアップZIPファイルが存在することを確認するPesterテストがあるかもしれません。その場合、簡単なPesterテストを作成し、テスト内でDownloadSqlタスクを呼び出し、実行後すぐにZIPファイルが存在するかどうかを確認します。

Describe 'SQL install with PSake' {

    It 'Downloads Sql files' {

        $setup = 'C:\Downloads\SqlSetup.zip'

        if(Test-Path -Path $setup)
        {
            Remove-Item -Path $setup
        }

        # ここではテスト対象のタスクはDownloadSqlだけです
        Invoke-PSake -BuildFile C:\Work\psakefile.ps1 -TaskList DownloadSql
        
        $setup | Should -Exist
    }
}

タスクへのパラメータの渡し方

PSakeを使用し始めると、いくつかのタスクにパラメータを渡すことができるようになるかもしれません。通常、PowerShellの関数やスクリプトでは、関数/スクリプトに対してさまざまな名前付きパラメータを渡しますが、PSakeは異なります。

PSakeファイルにパラメータを渡すためには、各タスク内で使用できるキー/値のペアを定義するPropertiesブロックを使用します。

PSakeファイルの先頭にPropertiesブロックを定義することを忘れないでください。すべてのPSake操作は上から下に読み込まれます。

たとえば、PSakeファイルの各タスクに動的なSqlYearおよびSqlVersion変数を渡すには、以下のように定義します。

Properties {
    $SqlYear = '2017'
    $SqlVersion = '14.0'
}

task -Name DownloadSql -Action {
    
    Write-Host "SQL version to install: SQL $SqlYear (version $SqlVersion)"

}

そして、Invoke-PSakeを使用してPSakeファイルを呼び出すと、以下の出力が表示されます。$SqlYear$SqlVersion変数がPropertiesブロックで定義された値で展開されていることに注意してください。

psake version 4.9.1
Copyright (c) 2010-2018 James Kovacs & Contributors

Executing DownloadSql
SQL version to install: SQL 2017 (version 14.0)

psake succeeded executing C:\Work\PSakefile.ps1

--------------------------------------------------
Build Time Report
--------------------------------------------------

Name        Duration
----        --------
DownloadSql 00:00:00
------      --------
Total:      00:00:00

Propertiesパラメータの使用

タスクにパラメータを従来のパラメータ経由で渡したい場合、PSakeを使用すると便利です。先に示した例のように、psakefile.ps1の先頭には引き続きPropertiesブロックを保持する必要がありますが、PSakeを使用すると値を上書きすることができます。

それを行うには、キー/値のペアごとにハッシュテーブルを定義します。次に、ハッシュテーブルをPropertiesパラメータに渡します。PSakeエンジンは、psakefile.ps1スクリプト内のPropertiesブロックで指定された値よりも、渡されたハッシュテーブル内の値を使用します。

PropertiesブロックとPropertiesパラメータの構文の違いに注意してください。 Propertiesブロックでは、各行が変数であり、したがってドル記号で始まります。一方、Propertiesパラメータはハッシュテーブルであり、各アイテムがキーであり、先頭に$が付いていません。もう一つの違いは、ハッシュテーブルの前に@文字が付いていることです。

以下に、Propertiesパラメータを使用する例を示します。

$myNewProperties = @{
    SqlYear = '2019'
    SqlVersion = '15.0'
}

Invoke-PSake -TaskList DownloadSql -Properties $myNewProperties

PSakeタスクのモジュール化: タスクをファイルとして扱う

PSakeファイルは、特に大規模な自動化タスクを組織化する必要がある場合、爆発的に成長する可能性があります。それらのタスクを管理できるようにするために、モジュール化やタスクの分割に焦点を当てるべきです。

関連記事: 地獄からのPowerShellスクリプトのリファクタリングの生き残り方

このチュートリアルの例では、以下の5つのタスクを使用しました:

  • ValidateDiskSpace
  • DownloadSql
  • ExtractSql
  • InstallSqlDriveC
  • InstallSqlDriveD

それぞれのタスクは、単一のpssakefile.ps1スクリプトの内部で定義されています。もし時間の経過とともにさらに多くのタスクを追加することが予想される場合は、それらのタスクを個別のファイルに分割し、各タスクをValidateDiskSpace.ps1DownloadSql.ps1InstallSqlDriveD.ps1InstallSqlDriveD.ps1などのようなファイルに配置するべきです。

例えば、InstallSqlDriveD.ps1には次のコードのみが含まれます:

task InstallSqlDriveD -Description 'Install SQL on D:' -PreCondition { $installSqlOn_D_Drive } -Action {
	
	Write-Host "`n   *** Installing SQL Server on D drive ... please wait... ***`n" -ForegroundColor Yellow
	
}

タスクが移動されたら、Include関数を使用してファイルをpsakefile.ps1にインポートします。これが完了すると、psakefile.ps1の内容は次のコードに簡略化されます:

$installSqlOn_C_Drive = $true
$installSqlOn_D_Drive = $false

Include "$PSScriptRoot\ValidateDiskSpace.ps1"
Include "$PSScriptRoot\DownloadSql.ps1"
Include "$PSScriptRoot\ExtractSql.ps1"
Include "$PSScriptRoot\InstallSqlOnC.ps1"
Include "$PSScriptRoot\InstallSqlOnD.ps1"

Invoke-PSakeがpsakefile.ps1スクリプトをトリガーする際、Invoke-PSakeは、タスクがpsakeファイル内にあるのか、Includeメソッドによってインポートされたのかを知らないし気にしません。

次のステップ

PSakeは、ソフトウェアビルド、CI/CD、パッケージの展開、インストーラーの作成など、多くの目的に使用できる強力なスクリプトオーケストレータです。唯一の制限はあなたの想像力です。PSakeを使って大規模なスクリプトを構築することに慣れることで、タスク(コードのビルディングブロック)について考えるようになります。タスクのコンセプトはPowerShellの構文を活用しており、タスクを使用することで既存のコマンドラインの知識を豊かにすることができます。

PSakeで生成するコードは、より読みやすく、保守しやすく、テストしやすくなります。練習を重ねることで、手順を異なるタスクに分割することがスクリプト化を容易にします。追加のわずかな作業によるメリットは、中長期的に大いに報われるでしょう。

あなたの仕事のプロジェクトにおいて、PSakeはどのように適していると思われますか?

Source:
https://adamtheautomator.com/powershell-psake/