使用PowerShell PSake自動執行任務:步驟解說

所以你已經學會了如何使用 PowerShell 腳本自動化任務。這太棒了!但現在你已經有了一堆雜亂無章的腳本和模塊,其中包括手動腳本執行、定時任務等等。是時候為這一切帶來一些秩序,並實施 PowerShell 的自動化協調引擎,也就是 PowerShell PSake

PSake 是一個作為 PowerShell 模塊編寫的協調引擎,可以為你的腳本添加秩序,並讓你完全控制何時以及在什麼條件下運行。儘管 PSake 是一個構建自動化工具,其主要用例是在構建腳本中(通常由構建服務器在發布自動化場景中執行),但在日常腳本場景中通常被忽視。讓我們改變這一點。

在本教程中,你將學習 PSake 的工作原理,並使用一些很棒的例子,讓你今天就可以實踐!

先決條件

本文將作為一個教程,期望你跟著操作。如果你想完全按照所述步驟進行操作,請確保你擁有以下條件:

  • Windows PowerShell 3+。本教程使用的是 Windows PowerShell v5.1
  • Github 下載的 PSake 壓縮文件。本教程使用的是 v4.9.0 版本。

設置 PSake 模塊

假設你已經在本地機器上下載了 GitHub 上的 PSake 項目,你首先需要完成幾個步驟來進行設置。

  1. 解壓縮從 GitHub 下載的 PSake 壓縮文件。
  2. src資料夾移動到解壓縮的ZIP檔案內容中的路徑下,以確保PowerShell知道新模組的存在。
  3. src資料夾重新命名為PSake
  4. 現在執行Get-Module PSake -ListAvailable來確認它是否顯示出來。如果沒有收到錯誤,則準備好繼續進行。

相關文章:了解和建立PowerShell模組

建立基本的PowerShell 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参数。如果执行多个任务,每个任务将按照您在psakefile.ps1中传递它们给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将每个任务视为构建任务)。例如:以青色执行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-PSake命令和TaskList参数来调用一些(或所有)任务,就像您之前在简单示例中所做的那样。如果您有多个任务要调用,可以创建一个数组,并将每个任务的名称定义为数组的项,如下所示。

Invoke-PSake将按照数组中定义的顺序运行每个任务。

$taskList = @()

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

Invoke-PSake -TaskList $taskList

运行上述代码时,您应该会得到以下类似的结果:

PSake script output

添加前置条件

也许您需要在满足某个特定条件时执行某些操作。例如,在本教程的SQL安装脚本中,您可能需要在执行调用安装程序任务之前测试存储安装程序的卷是否可用。

您可以使用PreCondition参数来执行一个返回布尔值True或False的代码片段,以决定该任务是否运行。

请注意下面的示例中的$installSqlOn_C_Drive$installSqlOn_D_Drive变量。当Invoke-PSake调用此脚本时,这些变量将根据C或D卷是否存在来包含True或False的值。

然后,您可以看到每个task行都有一个PreCondition脚本块参数,其中包含这些变量的值。在运行时,根据这些变量的值,将运行InstallSqlDriveCInstallSqlDriveD任务。

$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
	
}

任务参数

除了ActionDescription之外,任务还支持以下参数:

  • 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测试

例如,也许您有一个Pester测试,在运行DownloadSql任务后,确认SQL安装ZIP文件存在于文件夹中。在这种情况下,您可以构建一个简单的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將在文件中的每個任務中使用這些參數。

請確保在PSake文件的頂部定義Properties區塊。所有PSake操作都從頂部向下讀取。

例如,要將動態的SqlYearSqlVersion變量傳遞給PSake文件中的每個任務,可以如下所示定義它們。

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可以幫助您。仍然需要像上面的示例中那樣將Properties區塊放在psakefile.ps1的頂部,但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 腳本重構地獄中生存下來

在本教程的示例中,您正在使用五個任務:

  • 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/