Automatice tareas con PowerShell PSake: Un recorrido

¡Así que has aprendido cómo automatizar tareas con un script de PowerShell!
¡Eso es genial! Pero ahora tienes un desorden de scripts y módulos con una mezcla de ejecuciones de scripts manuales, tareas programadas y más. Es hora de poner orden al caos e implementar el motor de orquestación de automatización conocido como PowerShell PSake.

PSake es un motor de orquestación escrito como un módulo de PowerShell que agrega orden a tus scripts y te brinda control total sobre lo que se ejecuta, cuándo y bajo qué condiciones. Aunque PSake es una herramienta de automatización de compilación y su caso de uso principal es en scripts de compilación (generalmente ejecutados por un servidor de compilación en escenarios de automatización de lanzamiento), generalmente se pasa por alto en escenarios de scripting diarios. Vamos a cambiar eso.

En este tutorial, aprenderás cómo funciona PSake junto con algunos excelentes ejemplos para que puedas poner en práctica hoy mismo!

Prerrequisitos

Este artículo será un tutorial en el que se espera que sigas los pasos. Si deseas seguir exactamente como se cubre, asegúrate de tener:

  • Windows PowerShell 3+. El tutorial utiliza Windows PowerShell v5.1
  • El archivo zip de PSake de Github. Este tutorial utiliza la versión 4.9.0.

Configuración del módulo PSake

Suponiendo que tienes el proyecto de PSake de GitHub en tu máquina local, primero debes seguir algunos pasos para configurarlo.

  1. Extrae el archivo zip de PSake descargado de GitHub.
  2. Mueva la carpeta src dentro del contenido del archivo ZIP extraído a una ruta bajo $env:PSModulePath para asegurarse de que PowerShell conozca el nuevo módulo.
  3. Renombre la carpeta src a PSake.
  4. Ahora ejecute Get-Module PSake -ListAvailable para confirmar que aparezca. Si no recibe un error, está listo para continuar.

Relacionado: Comprender y Construir Módulos de PowerShell

Creando un Script Básico de PSake de PowerShell

Para entender PSake, necesitas construir algo. Construyamos un script PSake simple para ver qué puede hacer.

  1. Cree un script llamado psakefile.ps1 con una única tarea. Como mínimo, una tarea debe tener un nombre y un bloque de acción. El nombre psakefile.ps1 no es obligatorio, pero es el nombre predeterminado esperado por el motor.

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.

A continuación se muestra un ejemplo de un psakefile.ps1 con una tarea bastante simple:

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

2. Ahora que ha construido el archivo PSake, puede llamarlo desde una consola de PowerShell con el comando Invoke-PSake y pasar el nombre de la tarea como valor al parámetro TaskList .

Invoke-PSake es el motor de ejecución para PSake. Este comando activa las tareas definidas en psakefile.ps1. Puedes pasar un nombre de tarea o una lista de tareas separadas por comas al parámetro TaskList. Si estás ejecutando múltiples tareas, cada tarea se ejecutará en el orden en que las hayas pasado a TaskList, independientemente de su ubicación en psakefile.ps1.

A continuación se muestra cómo puedes activar la tarea HelloWorld:

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

Mientras mantengas el nombre psakefile.ps1 y hayas configurado la consola en la carpeta donde se encuentra, puedes omitir el parámetro BuildFile y su valor.

  1. Al ejecutar Invoke-PSake, se mostrará la salida de PSake en la consola. Cuando ejecutas las tareas en psakefile.ps1, verás una salida similar a la siguiente.
Psake script output

La salida consta de estos componentes:

  1. Los detalles sobre la versión de PSake.
  2. El nombre de cada tarea de construcción justo antes de ejecutarse (PSake considera cada tarea como una tarea de construcción). En el ejemplo: Ejecutando HelloWorld en cian.
  3. Cualquier salida que la tarea haya producido. En el ejemplo: Hola Mundo en amarillo.
  4. Mensaje de éxito/fallo. En el ejemplo: psake tuvo éxito… en verde.
  5. Resumen del tiempo (llamado Informe de Tiempo de Construcción) con la duración de cada tarea, más la duración total de todo el script.

Instalando SQL con PSake

En la sección anterior, no hiciste mucho más que invocar un script ficticio de PSake. ¡Ahora amplía ese conocimiento y crea un script de PSake que instale SQL!

En este ejemplo, vas a crear un script de PSake que:

  1. Valide el espacio libre en disco en una máquina.
  2. Descargue un archivo ZIP de SQL desde un repositorio local.
  3. Extraiga el archivo ZIP.
  4. Ejecute la instalación en la unidad C o D (la que exista).

Vamos a ver a continuación cómo podemos aprovechar PSake para esta tarea.

Diseñando los Bloques de Construcción

PSake se trata de orquestar tareas. Cada tarea debe tener un nombre único y, idealmente, debe realizar una operación atómica única, como una función de PowerShell. Usando este concepto, puedes describir los pasos a continuación para construir una tarea para cada uno.

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

En esta etapa, en realidad no estás construyendo el código para hacer algo; simplemente estás estructurando las tareas y creando el archivo de PSake. Notarás las referencias de Write-Host en las tareas a continuación; agregarás más contenido a las tareas más adelante.

Siempre debes usar el parámetro Description para cada tarea. El parámetro Description proporciona más información sobre cada tarea tanto al ejecutarlas como al revisar el código.

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
	
}

Definiendo el Orden de Ejecución de Tareas

Ahora tienes un archivo de PSake que contiene un conjunto de tareas. En este punto, puedes ejecutar todas las tareas a la vez o elegir ejecutar solo algunas de ellas con el comando Invoke-PSake.

Puedes llamar a algunas (o todas las tareas) usando Invoke-PSake y el parámetro TaskList, como hiciste anteriormente en el ejemplo simple. Si tienes más de una tarea para invocar, crea un array y define el nombre de cada tarea como un elemento, como se muestra a continuación.

Invoke-PSake ejecutará cada tarea en el orden definido en el array.

$taskList = @()

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

Invoke-PSake -TaskList $taskList

Cuando ejecutas el código anterior, deberías obtener un resultado como el siguiente:

PSake script output

Añadiendo una Precondición

Quizás necesites realizar alguna acción solo si se cumple cierta condición. En el ejemplo del script de instalación de SQL de este tutorial, por ejemplo, tal vez necesitas probar si el volumen donde almacenas el instalador está disponible antes de ejecutar la tarea para invocar dicho instalador.

Puedes usar el parámetro PreCondition para ejecutar un fragmento de código que devuelva un valor booleano True o False que dicte si esa tarea se ejecuta o no.

Observa en el ejemplo a continuación las variables $installSqlOn_C_Drive y $installSqlOn_D_Drive. Cuando Invoke-PSake invoque este script, estas variables contendrán un valor True o False dependiendo de si existe un volumen C o D.

En cada línea de task, puedes ver que cada tarea tiene un parámetro de scriptblock PreCondition que contiene el valor de esas variables. En tiempo de ejecución, se ejecutará ya sea la tarea InstallSqlDriveC o la tarea InstallSqlDriveD, dependiendo de estas variables.

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

Parámetros de Tarea

Además de las etiquetas Action y Description, una tarea también admite estos parámetros:

  • PreCondition – Bloque de script que devuelve un valor booleano. En caso de ser Falso, la tarea específica se omite. (Se muestra un ejemplo de uso arriba).
  • PostCondition Paso de validación. Un bloque de script que devuelve un valor booleano. Falso significa que la validación ha fallado y causa que todo el script se detenga.
  • PreAction – Un bloque de script para ejecutar antes de la tarea.
  • PostAction Un bloque de script para ejecutar justo después de que la tarea se haya completado con éxito.
  • ContinueOnError Parámetro de interruptor. Si se usa, cualquier error que pueda ocurrir durante la ejecución de la tarea no causará que todo el script se detenga.
  • Depends Nombre de una tarea (o una lista de nombres de tareas) que debe ejecutarse antes de que se ejecute la tarea actual. PSake utilizará esta información para ejecutar las dependencias de tareas en el orden correcto. Por ejemplo, si la tarea A depende de la tarea B, entonces el motor PSake ejecutará B antes que A.

Al principio, el mecanismo de dependencia parece ser una buena idea. Ayuda a establecer tareas en un orden lógico. Sin embargo, el uso del parámetro Depends une diferentes tareas, lo que hace que sea más difícil probarlas de forma independiente más tarde. Pero, dado que el usuario puede establecer explícitamente el orden de ejecución de las tareas y pasar este orden al llamar al archivo PSake → el uso del parámetro Depends se puede evitar por completo.

A continuación, veamos un ejemplo de uso para algunos de estos parámetros de tarea:

Agregando PreAction y PostCondition

Usando la tarea InstallSqlDriveD del ejemplo anterior como punto de partida, quizás tenga una solicitud adicional para la instalación.

Tal vez necesite registrar los momentos en que comienza y termina la instalación. Debe registrar estos tiempos en dos variables de entorno llamadas SqlSetupStartDate y SqlSetupEndDate. En segundo lugar, después de que la instalación se complete, debe verificar que la carpeta D:\TempSqlFiles no exista.

Afortunadamente, los parámetros de tarea de PSake PreAction, PostAction y PostCondition (respectivamente) cumplen exactamente con estos nuevos requisitos. A continuación se muestra un ejemplo de cómo se puede hacer:

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
     # en caso de que la carpeta exista, esto devolverá Falso, provocando que todo el script se detenga
     (-not (Test-Path -Path 'D:\TempSqlFiles'))

 }

Ejecución de Scripts de PSake en Pruebas de Pester

Donde pueda invocar un script de PowerShell, puede invocar un archivo PSake. Si está construyendo pruebas de infraestructura con Pester, puede invocar PSake dentro de las pruebas.

Relacionado: Writing Pester Tests for PowerShell

Por ejemplo, tal vez tenga una prueba de Pester para confirmar que el archivo ZIP de configuración de SQL existe en una carpeta después de ejecutar la tarea DownloadSql. En ese caso, construye una prueba de Pester simple e invoca la tarea DownloadSql dentro de la prueba y verifica el archivo ZIP justo después de que se ejecuta.

Describe 'SQL install with PSake' {

    It 'Downloads Sql files' {

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

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

        # la única tarea bajo prueba aquí es DownloadSql
        Invoke-PSake -BuildFile C:\Work\psakefile.ps1 -TaskList DownloadSql
        
        $setup | Should -Exist
    }
}

Pasando parámetros a tareas

Una vez que comiences a usar PSake, es posible que desees parametrizar algunas de las tareas. Típicamente, con las funciones y scripts de PowerShell, pasarás varios parámetros con nombres a la función/script; PSake es diferente.

Para pasar parámetros a los archivos de PSake, puedes usar un bloque de Properties que define pares clave/valor que PSake luego pone a disposición dentro de cada tarea en el archivo.

Asegúrate de definir el bloque Properties en la parte superior del archivo de PSake. Todas las operaciones de PSake se leen de arriba hacia abajo.

Por ejemplo, para pasar variables dinámicas SqlYear y SqlVersion a cada tarea en el archivo de PSake, puedes definirlas como se muestra a continuación.

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

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

}

Cuando luego invoques el archivo de PSake con Invoke-PSake, verías la siguiente salida. Observa que las variables $SqlYear y $SqlVersion se han expandido con los valores definidos en el bloque 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

Usando el parámetro Properties

Si prefieres pasar parámetros a una tarea a través de un parámetro tradicional, PSake puede ayudarte. Aún necesitas mantener el bloque de Properties en la parte superior del psakefile.ps1 como en el ejemplo anterior, pero PSake te permite anular los valores.

Para hacerlo, define una hashtable con cada uno de los pares clave/valor que te gustaría sobrescribir. Luego, pasa la hashtable al parámetro Properties. El motor PSake utilizará los valores en la hashtable pasada sobre aquellos especificados en el bloque Properties dentro del script psakefile.ps1.

Observa las diferencias de sintaxis entre el bloque Properties y el parámetro Properties. En el bloque Properties, cada línea es una variable y, por lo tanto, lleva un signo de dólar como prefijo, mientras que el parámetro Properties es una hashtable, por lo que cada elemento es una clave y se escribe sin un signo de dólar inicial. Otra diferencia es que la hashtable va precedida por el carácter @.

A continuación, puedes ver un ejemplo de uso del parámetro Properties.

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

Invoke-PSake -TaskList DownloadSql -Properties $myNewProperties

Modularización de Tareas en PSake: Tareas como Archivos

En algún momento, tu archivo PSake probablemente crecerá exponencialmente, especialmente si necesitas orquestar tareas de automatización extensas. Para asegurarte de que puedas gestionar todas esas tareas, deberías centrarte en la modularización o dividir las tareas para facilitar la gestión.

Relacionado: Cómo Sobrevivir a la Refactorización de un Script de PowerShell Infernal

En el ejemplo de este tutorial, estabas trabajando con cinco tareas:

  • ValidateDiskSpace
  • DownloadSql
  • ExtractSql
  • InstallSqlDriveC
  • InstallSqlDriveD

Cada una de estas tareas está definida dentro de un único script pssakefile.ps1. Si anticipa agregar muchas más tareas con el tiempo, debería dividir esas tareas en archivos separados, con cada tarea dentro, por ejemplo, ValidateDiskSpace.ps1, DownloadSql.ps1, InstallSqlDriveD.ps1, InstallSqlDriveD.ps1, etc.

Por ejemplo, InstallSqlDriveD.ps1 contendrá solo este código:

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
	
}

Una vez que las tareas se hayan trasladado, importe los archivos a pssakefile.ps1 usando la función Include. Una vez hecho esto, el contenido de pssakefile.ps1 se reduce a este código:

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

Cuando Invoke-PSake activa el script pssakefile.ps1, Invoke-PSake no sabe ni le importa si las tareas están dentro del archivo pssake o fueron importadas por el método Include.

Siguientes Pasos

PSake es un potente orquestador de scripts que se puede utilizar para muchos propósitos: Compilaciones de software, CI/CD, despliegues de paquetes, creación de instaladores y más. El único límite es tu imaginación. Acostumbrarse a construir scripts grandes con PSake te obliga a pensar en tareas (bloques de construcción de código). El concepto de tareas aprovecha la sintaxis de PowerShell, y al usar tareas enriqueces tu conocimiento existente de línea de comandos.

El código que produces con PSake se vuelve más legible, mantenible y más fácil de probar. Después de un poco de práctica, encontrarás que dividir tus pasos en diferentes tareas los hace más fáciles de scriptar. La pequeña cantidad de trabajo extra que lleva se recupera con creces a medio y largo plazo.

¿Dónde ves que PSake encaje en tus proyectos de trabajo?

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