DEV Community

Cover image for Mejora el paso de parámetros en tus scripts de PowerShell
Coding Bugs
Coding Bugs

Posted on

Mejora el paso de parámetros en tus scripts de PowerShell

Mi apodo en las redes es Coding Bugs y llevo más de 15 años trabajando como desarrollador y arquitecto de software. En mi dashboard encontrarás artículos con información sobre lenguajes de programación, técnicas de programación y otros conceptos que te ayudarán a mejorar.

El propio Microsoft describe PowerShell como "una solución de automatización de tareas multiplataforma compuesta por un shell de línea de comandos, un lenguaje de scripting y un marco de gestión de la configuración". En otras palabras, es una solución para la creación de scripts que realicen tareas repetitivas, por ejemplo, la creación de una cuenta en el sistema cuando un empleado se incorpora en la compañía, o el borrado de documentos que tienen más de 5 años de antigüedad en una carpeta. Por tanto, es una herramienta más que tenemos a nuestra disposición para automatizar tareas recurrentes.

En este artículo mostraré como utilizar configuraciones de ámbito general o global para nuestros scripts. Desde una configuración ad hoc en la que establecer de forma fija los valores, hasta la lectura de un fichero externo de configuración con un formato específico como JSON, XML o CSV. El objetivo es hacer la llamada a los scripts de PowerShell lo más sencilla posible y evitar cualquier tipo de error. En definitiva, la persona o automatismo que finalmente ejecutará los procesos no tendrá conocimiento del código propio del script y, seguramente, tampoco recordará el valor de los parámetros a pasar después de unas semanas. Cuanto más fácil sea, mejor para todos.

Ad hoc

Comenzando con un enfoque en el que desarrollar código basado en parámetros, nos va a permitir tener una batería de scripts que podrán ser utilizados en otras implementaciones simplemente indicando un valor u otro. Un ejemplo de un script genérico puede ser la descarga de información procedente de Internet. Este comprenderá 2 parámetros:

  • el primero será la dirección URL donde obtener la información y,
  • el segundo dónde queremos guardarla en disco.
# Download-Data.ps1

param(
    [Parameter(Mandatory=$true)]
    [String] $Url,
    [Parameter(Mandatory=$true)]
    [String] $FilePath
)

# Save data to the file path specified and returns the response
Invoke-WebRequest -Uri $Url -OutFile $FilePath

# For downloading my Github profile to your machine use the following line
# .\Download-Data.ps1 -Url https://github.com/codbugs -FilePath CodingBugs_Github_Profile.html
Enter fullscreen mode Exit fullscreen mode

De esta forma, podremos invocar este proceso tantas veces como queramos y guardar la información de las direcciones URL que le pasemos. El siguiente paso lógico será desarrollar código específico para las necesidades o requisitos que tengamos. En este caso, diremos que queremos guardar una versión de una página web cada día. Siguiendo el ejemplo anterior, tenemos que crear un nuevo script en el que utilizar siempre la misma dirección URL y generar una carpeta en base al día actual. El script guardará la página web de mi perfil en GitHub en una carpeta con nombre igual al año, mes y día actual.

# Download-MyGithubProfile.ps1

# My GitHub profile page
$MyGitHubProfileUrl = "https://github.com/codbugs"

# Folder generation based on the current date
$FolderName = Get-Date -Format "yyyyMMdd"
$Folder = New-Item -Path $FolderName -ItemType Directory -Force

# File name
$FileName = "CodingBugsProfile.html"

# Invoking the script for downloading my GitHub profile
.\Download-Data.ps1 -Url $MyGitHubProfileUrl -FilePath "$FolderName\$FileName"
Enter fullscreen mode Exit fullscreen mode

Por lo tanto, la necesidad se ha cubierto generando 2 scripts: el primero con una implementación genérica con la posibilidad de ser utilizado en el caso que haya otro requisito parecido y, el segundo, con una implementación específica que no requiere que el operador tenga conocimiento de los parámetros que debe pasar al script.

¿En qué situaciones podemos utilizar un modelo de scripts como este? A mí me resulta muy útil cuando tengo varios entornos diferentes y tengo que desplegar los mismos elementos en cada uno de ellos. En este caso, creo un script de despliegue genérico con los parámetros que necesito. Después, creo un nuevo script por cada entorno con los parámetros específicos de estos. Cuando tenga que desplegar en un entorno u otro sólo tengo que ejecutar el script correspondiente y estoy seguro que la información de los parámetros es la correcta.

Fichero de configuración

Usar ficheros de configuración es una forma sencilla de abstraer el código de aquellos valores necesarios para su ejecución. En vez de pasar todos los parámetros podemos incorporarlos en un fichero de texto y leerlos desde el propio script.

En este caso, aprovecharemos los comandos existentes en PowerShell para utilizar el formato de fichero con el que nos sintamos más a gusto: JSON, CSV o XML. Todos ellos tienen una forma rápida de leer ficheros y acceder a su información.

Partimos del siguiente fichero JSON:

// coding_bugs_configuration.json
{
    "version": "1.0",
    "author": {
        "name": "Coding Bugs",
        "email": "coding.bugs@outlook.com",
        "github": "https://github.com/codbugs"
    },
    "gists": [
        { "name": "README.md", "url": "https://gist.githubusercontent.com/codbugs/3e3abe4c7709e992c819327cb0b9c0c0/raw/67b1a5e520d0ebaac8c678a43b9dda5638a147e1/README.md" },
        { "name": "coding_bugs_configuration.csv", "url": "https://gist.githubusercontent.com/codbugs/3e3abe4c7709e992c819327cb0b9c0c0/raw/67b1a5e520d0ebaac8c678a43b9dda5638a147e1/coding_bugs_configuration.csv" },
        { "name": "coding_bugs_configuration.json", "url": "https://gist.githubusercontent.com/codbugs/3e3abe4c7709e992c819327cb0b9c0c0/raw/67b1a5e520d0ebaac8c678a43b9dda5638a147e1/coding_bugs_configuration.json" },
        { "name": "coding_bugs_configuration.xml", "url": "https://gist.githubusercontent.com/codbugs/3e3abe4c7709e992c819327cb0b9c0c0/raw/67b1a5e520d0ebaac8c678a43b9dda5638a147e1/coding_bugs_configuration.xml" },
        { "name": "Download-CsvGistFiles.ps1", "url": "https://gist.githubusercontent.com/codbugs/3e3abe4c7709e992c819327cb0b9c0c0/raw/67b1a5e520d0ebaac8c678a43b9dda5638a147e1/Download-CsvGistFiles.ps1" },
        { "name": "Download-Data.ps1", "url": "https://gist.githubusercontent.com/codbugs/3e3abe4c7709e992c819327cb0b9c0c0/raw/67b1a5e520d0ebaac8c678a43b9dda5638a147e1/Download-Data.ps1" },
        { "name": "Download-JsonGistFiles.ps1", "url": "https://gist.githubusercontent.com/codbugs/3e3abe4c7709e992c819327cb0b9c0c0/raw/67b1a5e520d0ebaac8c678a43b9dda5638a147e1/Download-JsonGistFiles.ps1" },
        { "name": "Download-MyGitHubProfile.ps1", "url": "https://gist.githubusercontent.com/codbugs/3e3abe4c7709e992c819327cb0b9c0c0/raw/67b1a5e520d0ebaac8c678a43b9dda5638a147e1/Download-MyGitHubProfile.ps1" },
        { "name": "Download-XmlGistFiles.ps1", "url": "https://gist.githubusercontent.com/codbugs/3e3abe4c7709e992c819327cb0b9c0c0/raw/67b1a5e520d0ebaac8c678a43b9dda5638a147e1/Download-XmlGistFiles.ps1" }
    ]
}
Enter fullscreen mode Exit fullscreen mode

Para obtener la información existente en él tenemos que usar el comando ConvertFrom-Json que transforma un código JSON en un objeto nativo de PowerShell. El objetivo es utilizar una notación sencilla e intuitiva para leer los datos. En el siguiente script, utilizamos la información existente para descargarnos cada uno de los gists de este artículo:

# Download-JsonGistFiles.ps1

param(
    [Parameter(Mandatory=$true)]
    [String]  $ConfigurationFilePath
)

# Reading the configuration file
$Configuration = Get-Content $ConfigurationFilePath | ConvertFrom-Json 

# Creating the folder to download de gists
$FolderName = $Configuration.author.name
$Folder = New-Item -Path $FolderName -Type Directory -Force

# Downloading the gists files
$Configuration.gists | Foreach { .\Download-Data.ps1 -Url $_.url -FilePath "$FolderName\$($_.name)" }

# .\Download-JsonGistFiles.ps1 -ConfigurationFilePath coding_bugs_configuration.json
Enter fullscreen mode Exit fullscreen mode

Si queremos tener un fichero XML porque nos resulta más sencillo, no hay problema. El código resultante es bastante similar, sólo tenemos que modificar cómo leer el fichero de configuración y cómo iterar por cada uno de los elementos gists.

<!-- coding_bugs_configuration.xml -->
<xml>
    <version>1.0</version>
    <author>
        <name>Coding Bugs</name>
        <email>coding.bugs@outlook.com</email>
        <github>https://github.com/codbugs</github>
    </author>
    <gists>
        <gist>
            <name>README.md</name>
            <url>https://gist.githubusercontent.com/codbugs/3e3abe4c7709e992c819327cb0b9c0c0/raw/67b1a5e520d0ebaac8c678a43b9dda5638a147e1/README.md</url>
        </gist>
        <gist>
            <name>coding_bugs_configuration.csv</name>
            <url>https://gist.githubusercontent.com/codbugs/3e3abe4c7709e992c819327cb0b9c0c0/raw/67b1a5e520d0ebaac8c678a43b9dda5638a147e1/coding_bugs_configuration.csv</url>
        </gist>
        <gist>
            <name>coding_bugs_configuration.json</name>
            <url>https://gist.githubusercontent.com/codbugs/3e3abe4c7709e992c819327cb0b9c0c0/raw/67b1a5e520d0ebaac8c678a43b9dda5638a147e1/coding_bugs_configuration.json</url>
        </gist>
        <gist>
            <name>coding_bugs_configuration.xml</name>
            <url>https://gist.githubusercontent.com/codbugs/3e3abe4c7709e992c819327cb0b9c0c0/raw/67b1a5e520d0ebaac8c678a43b9dda5638a147e1/coding_bugs_configuration.xml</url>
        </gist>
        <gist>
            <name>Download-CsvGistFiles.ps1</name>
            <url>https://gist.githubusercontent.com/codbugs/3e3abe4c7709e992c819327cb0b9c0c0/raw/67b1a5e520d0ebaac8c678a43b9dda5638a147e1/Download-CsvGistFiles.ps1</url>
        </gist>
        <gist>
            <name>Download-Data.ps1</name>
            <url>https://gist.githubusercontent.com/codbugs/3e3abe4c7709e992c819327cb0b9c0c0/raw/67b1a5e520d0ebaac8c678a43b9dda5638a147e1/Download-Data.ps1</url>
        </gist>
        <gist>
            <name>Download-JsonGistFiles.ps1</name>
            <url>https://gist.githubusercontent.com/codbugs/3e3abe4c7709e992c819327cb0b9c0c0/raw/67b1a5e520d0ebaac8c678a43b9dda5638a147e1/Download-JsonGistFiles.ps1</url>
        </gist>
        <gist>
            <name>Download-MyGitHubProfile.ps1</name>
            <url>https://gist.githubusercontent.com/codbugs/3e3abe4c7709e992c819327cb0b9c0c0/raw/67b1a5e520d0ebaac8c678a43b9dda5638a147e1/Download-MyGitHubProfile.ps1</url>
        </gist>
        <gist>
            <name>Download-XmlGistFiles.ps1</name>
            <url>https://gist.githubusercontent.com/codbugs/3e3abe4c7709e992c819327cb0b9c0c0/raw/67b1a5e520d0ebaac8c678a43b9dda5638a147e1/Download-XmlGistFiles.ps1</url>
        </gist>
    </gists>
</xml>
Enter fullscreen mode Exit fullscreen mode
param(
    [Parameter(Mandatory=$true)]
    [String] $ConfigurationFilePath
)

# Reading the configuration file
[XML]$Configuration = Get-Content  $ConfigurationFilePath

# Creating the folder to download de gists
$FolderName = $Configuration.xml.author.name
$Folder = New-Item -Path $FolderName -Type Directory -Force

# Downloading the gists files
$Configuration.xml.gists.gist | Foreach { .\Download-Data.ps1 -Url $_.url -FilePath "$FolderName\$($_.name)" }

# .\Download-XmlGistFiles.ps1 -ConfigurationFilePath coding_bugs_configuration.xml
Enter fullscreen mode Exit fullscreen mode

Por último, si queremos tener un fichero CSV tenemos que modificar cómo leer el fichero de configuración y cómo iterar por cada una de las filas.

"version";"author_name";"author_email";"author_github";"gist_name";"gist_url"
"1.0";"Coding Bugs";"coding.bugs@outlook.com";"https://github.com/codbugs";"README.md";"https://gist.githubusercontent.com/codbugs/3e3abe4c7709e992c819327cb0b9c0c0/raw/67b1a5e520d0ebaac8c678a43b9dda5638a147e1/README.md"
"1.0";"Coding Bugs";"coding.bugs@outlook.com";"https://github.com/codbugs";"coding_bugs_configuration.csv";"https://gist.githubusercontent.com/codbugs/3e3abe4c7709e992c819327cb0b9c0c0/raw/67b1a5e520d0ebaac8c678a43b9dda5638a147e1/coding_bugs_configuration.csv"
"1.0";"Coding Bugs";"coding.bugs@outlook.com";"https://github.com/codbugs";"coding_bugs_configuration.json";"https://gist.githubusercontent.com/codbugs/3e3abe4c7709e992c819327cb0b9c0c0/raw/67b1a5e520d0ebaac8c678a43b9dda5638a147e1/coding_bugs_configuration.json"
"1.0";"Coding Bugs";"coding.bugs@outlook.com";"https://github.com/codbugs";"coding_bugs_configuration.xml";"https://gist.githubusercontent.com/codbugs/3e3abe4c7709e992c819327cb0b9c0c0/raw/67b1a5e520d0ebaac8c678a43b9dda5638a147e1/coding_bugs_configuration.xml"
"1.0";"Coding Bugs";"coding.bugs@outlook.com";"https://github.com/codbugs";"Download-CsvGistFiles.ps1";"https://gist.githubusercontent.com/codbugs/3e3abe4c7709e992c819327cb0b9c0c0/raw/67b1a5e520d0ebaac8c678a43b9dda5638a147e1/Download-CsvGistFiles.ps1"
"1.0";"Coding Bugs";"coding.bugs@outlook.com";"https://github.com/codbugs";"Download-Data.ps1";"https://gist.githubusercontent.com/codbugs/3e3abe4c7709e992c819327cb0b9c0c0/raw/67b1a5e520d0ebaac8c678a43b9dda5638a147e1/Download-Data.ps1"
"1.0";"Coding Bugs";"coding.bugs@outlook.com";"https://github.com/codbugs";"Download-JsonGistFiles.ps1";"https://gist.githubusercontent.com/codbugs/3e3abe4c7709e992c819327cb0b9c0c0/raw/67b1a5e520d0ebaac8c678a43b9dda5638a147e1/Download-JsonGistFiles.ps1"
"1.0";"Coding Bugs";"coding.bugs@outlook.com";"https://github.com/codbugs";"Download-MyGitHubProfile.ps1";"https://gist.githubusercontent.com/codbugs/3e3abe4c7709e992c819327cb0b9c0c0/raw/67b1a5e520d0ebaac8c678a43b9dda5638a147e1/Download-MyGitHubProfile.ps1"
"1.0";"Coding Bugs";"coding.bugs@outlook.com";"https://github.com/codbugs";"Download-XmlGistFiles.ps1";"https://gist.githubusercontent.com/codbugs/3e3abe4c7709e992c819327cb0b9c0c0/raw/67b1a5e520d0ebaac8c678a43b9dda5638a147e1/Download-XmlGistFiles.ps1"
Enter fullscreen mode Exit fullscreen mode
param(
    [Parameter(Mandatory=$true)]
    [String] $ConfigurationFilePath
) 

# Reading the configuration file
$Configuration = Get-Content $ConfigurationFilePath
$Configuration = Import-Csv -Path $ConfigurationFilePath -Delimiter ';'

$Configuration | Foreach  {
    # Creating the folder to download de gists
    $FolderName = $_.author_name
    $Folder = New-Item -Path $FolderName -Type Directory -Force

    # Downloading the gists files
    .\Download-Data.ps1 -Url $_.gist_url -FilePath "$FolderName\$($_.gist_name)"
}

# .\Download-CsvGistFiles.ps1 -ConfigurationFilePath coding_bugs_configuration.csv
Enter fullscreen mode Exit fullscreen mode

Resumiendo

A la hora de proporcionar los valores a los parámetros de nuestros scripts en PowerShell, podemos seguir 2 estrategias que nos faciliten la labor de su ejecución:

  1. Generar un script genérico que nos sirva en cualquier situación que necesitemos y un conjunto de scripts con los parámetros establecidos directamente en código e invocando al primero,
  2. Introducir un fichero de configuración con todos los parámetros necesarios para el script.

Os estaréis imaginando en una tercera posibilidad . Efectivamente, se trata de la combinación de las dos anteriores, es decir, tener un script genérico con una implementación que cubra la ejecución del propio proceso mediante un fichero de configuración, y un conjunto de scripts especificando el fichero de configuración concreto a utilizar. De esta forma, facilitamos mucho más las acciones que haya que tomar en la ejecución o modificación de nuestros scritps.

El código de este artículo lo tenéis en este gist.

Feedback

Si te ha gustado este artículo no olvides clicar en el corazón o el unicornio y compartirlo con tus amigos y compañeros. Déjame tus comentarios en la parte inferior sobre lo que te ha gustado o como mejorar y si quieres que escriba acerca de otros temas mándame un mensaje directo a mi cuenta de twitter.

Créditos

La imagen de cabecera procede de Unsplash y es cortesía de Markus Spiske

Discussion (0)