使用PowerShell PSake自动化任务:一步步指南

所以你学会了如何使用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文件内容中的路径下$env:PSModulePath,以确保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 的顺序执行,而不考虑它们在 psakefile.ps1 中的位置。

以下是如何触发 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 成功… 以绿色显示。
  5. 时间摘要(名为 构建时间报告),其中包括每个任务的持续时间以及整个脚本的总持续时间。

使用 PSake 安装 SQL

在前一节中,您没有做太多事情,只是调用了一个虚拟的 PSake 脚本。现在在此基础上构建知识,创建一个安装 SQL 的 PSake 脚本吧!

在这个示例中,您将创建一个 PSake 脚本,完成以下任务:

  1. 验证计算机上的空闲磁盘空间。
  2. 从本地仓库下载一个 SQL 压缩文件。
  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调用此脚本时,这些变量将包含一个True或False值,具体取决于C或D卷是否存在。

然后,您可以看到每个任务的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 – 返回布尔值的脚本块。如果为假,则跳过特定任务。(示例用法如上所示)。
  • PostCondition 验证步骤。返回布尔值的脚本块。假表示验证失败,导致整个脚本停止。
  • 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可以帮助您。您仍然需要保持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脚本时生存

在本教程的示例中,您正在处理五个任务:

  • ValidateDiskSpace
  • DownloadSql
  • ExtractSql
  • InstallSqlDriveC
  • InstallSqlDriveD

每个任务都在单个psakefile.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/