Improve how to provide parameters in your PowerShell scripts
Tags: powershell, programming, script
Published: 2022-02-13 09:23:59

> _My nickname in the networks is [__Coding Bugs__][Dashboard], and I have been working as a software developer and architect for more than 15 years. In my [dashboard][Dashboard], you will find articles about programming languages, programming techniques, and other concepts that will help you improve._

Microsoft describes [PowerShell][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][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. 

```PowerShell
# 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
```

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][GitHub Profile] in a folder named equal to the current year, month and day.

```PowerShell
# 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"
```

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][PowerShell] to use the file format we are most comfortable with: [JSON][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][JSON]:

```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" }
    ]
}
```

To get the existing information in the configuration file we have to use the `ConvertFrom-Json` command that transforms a [JSON][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:

```PowerShell
# 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
```

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.

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

```PowerShell
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
```

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.

```CSV
"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"
```

```PowerShell
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
```

__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][Twitter] account.

__Credits__

The header image comes from [Unsplash][Unsplash] and is courtesy of [Markus Spiske](https://unsplash.com/@markusspiske).

[PowerShell]: https://docs.microsoft.com/es-es/powershell/scripting/overview?view=powershell-7.2 "PowerShell"
[GitHub Profile]: https://github.com/codbugs "Mi perfil en GitHub"
[JSON]: https://json.org/json-es.html "JSON"
[Unsplash]: https://unsplash.com/ "Unsplash"
[Twitter]: https://twitter.com/BugsCoding
[Dashboard]: https://dev.to/codbugs "Dashboard"