Improve how to provide parameters in your PowerShell scripts

codbugs

Coding Bugs

Posted on February 13, 2022

Improve how to provide parameters in your PowerShell scripts

My nickname in the networks is Coding Bugs, and I have been working as a software developer and architect for more than 15 years. In my dashboard, you will find articles about programming languages, programming techniques, and other concepts that will help you improve.

Microsoft describes PowerShell as "a cross-platform task automation solution composed of a command-line shell, a scripting language and a configuration management framework". In other words, it is a solution for creating scripts that perform repetitive tasks, for example, creating an account in the system when an employee joins the company, or deleting documents that are more than 5 years old in a folder. Therefore, it is one more tool that we have at our disposal to automate recurring tasks.

In this article I will show how to use general or global configurations for our scripts. From an ad hoc configuration in which to set fixed values, to the reading of an external configuration file with a specific format such as JSON, XML or CSV. The goal is to make the call to the PowerShell scripts as simple as possible and avoid any type of error. Ultimately, the person that will finally execute the processes will have no knowledge of the script's own code and, most likely, will not remember the value of the parameters to pass after a few weeks. The easier it is, the better for everyone.

Ad hoc

Starting with an approach in which developing code based on parameters will allow us to have a set of scripts that can be used in other implementations simply by indicating one value or another. An example of a generic script could be the downloading of information from the Internet. It will comprise 2 parameters:

  • the first one will be the URL address where to obtain the information and,
  • the second one where we want to save it on disk.
# 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

In this way, we will be able to invoke this process as many times as we want and save the information of the URL addresses that we pass to it. The next logical step is to develop code specific to the needs or requirements we have. In this case, we will say that we want to save a version of a web page every day. Following the above example, we need to create a new script in which we always use the same URL and generate a folder based on the current day. The script will save the web page of my GitHub profile in a folder named equal to the current year, month and day.

# 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

Therefore, the need has been covered by generating 2 scripts: the first one with a generic implementation and, the second one, with a specific implementation that does not require the operator to have knowledge of the parameters to be passed to the script.

In what situations can we use a scripting model like this? I find it very useful when I have several different environments and I have to deploy the same elements in each of them. In this case, I create a generic deployment script with the parameters I need. Then, I create a new script for each environment with the environment-specific parameters. When I have to deploy in one environment or another I just run the corresponding script and I am sure that the parameter information is correct.

Configuration file

Using configuration files is a simple way to abstract the code of those values necessary for its execution. Instead of passing all the parameters we can incorporate them in a text file and read them from the script itself.

In this case, we will take advantage of the existing commands in PowerShell to use the file format we are most comfortable with: JSON, CSV or XML. All of them have a quick way to read files and access their information.

We start from the following file 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

To get the existing information in the configuration file we have to use the ConvertFrom-Json command that transforms a JSON code into a native PowerShell object. The goal is to use a simple and intuitive notation to read the data. In the following script, we use the existing information to download each of the gist files in this article:

# 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

If we want to have an XML file because it is easier for us, no problem. The resulting code is quite similar, we only have to modify how to read the configuration file and how to iterate through each of the gist nodes elements.

<!-- 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

Finally, if we want to have a CSV file we have to modify how to read the configuration file and how to iterate through each of the rows.

"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

Summarizing

When providing the values to the parameters of our PowerShell scripts, we can follow 2 strategies to facilitate the work of its execution:

  1. Generate a generic script that can be used in any situation we need and a set of scripts with the parameters set directly in code and invoking the first one,
  2. Enter a configuration file with all the necessary parameters for the script.

You may be imagining a third possibility . Indeed, it is the combination of the two previous ones, that is to say, to have a generic script with an implementation that covers the execution of the process itself by means of a configuration file, and a set of scripts specifying the concrete configuration file to use. In this way, we facilitate much more the actions to be taken in the execution or modification of our scripts.

The code for this article can be found in this [gist].(https://gist.github.com/codbugs/3e3abe4c7709e992c819327cb0b9c0c0).

Feedback

If you liked this article, do not forget to click on the heart or the unicorn and share it with your friends and colleagues. Leave me your comments at the bottom about what you liked or how to improve and if you want me to write about other topics send me a direct message to my Twitter account.

Credits

The header image comes from Unsplash and is courtesy of Markus Spiske.

💖 💪 🙅 🚩
codbugs
Coding Bugs

Posted on February 13, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related