Building a PowerShell Module - Part 3 - JSON Config Files are Awesome
JSON support in PowerShell is a beautiful thing. Creating a configuration file for PSSpeedTest to store default iPerf3 server information in is a breeze with the usage of two fairly straightforward functions: Get-SpeedTestConfig
and Set-SpeedTestConfig
.
Part 3 - JSON Configuration File Getting/Setting
Get-SpeedTestConfig
The source for Get-SpeedTestConfig
can be found here on GitHub, but let’s dive in to the meat of the function.
try {
Write-Verbose -Message "Getting content of config.json and returning as a PSCustomObject."
$config = Get-Content -Path "$($PSScriptRoot | Split-Path -Parent)\config.json" -ErrorAction "Stop" | ConvertFrom-Json
if ($PassThru){
return $config
}
else {
Write-Host "Internet Server: $($config.defaultInternetServer.defaultServer)"
Write-Host "Internet Port: $($config.defaultInternetServer.defaultPort)"
Write-Host "Local Server: $($config.defaultLocalServer.defaultServer)"
Write-Host "Local Port: $($config.defaultLocalServer.defaultPort)"
}
}
catch {
throw "Can't find the JSON configuration file. Use ‘Set-SpeedTestConfig' to create one."
}
First and foremost, there’s a chance that a configuration file for PSSpeedTest does not already exist; blindly pulling from a path to a JSON file would be bad practice without error handling, so a try/catch
block is the first thing that should be put in place. Depending on your own thoughts and opinions on error handling, you may want to use Test-Path
for this purpose rather than try/catch
.
If a configuration file is present at the expected path and no error is thrown, the rest of the function is simply just reading a text file and converting it to a navigable object with Get-Content
and ConvertFrom-Json
.
In the full source of the function you will see that there is a $PassThru
switch parameter present; if this is specified by the user, the object resulting from ConvertFrom-Json
will be returned and output will be silenced. This is so that administrators can manipulate this object without directly affecting the stored configuration if they so choose.
Set-SpeedTestConfig
Source for Set-SpeedTestConfig
is found here. Let’s view this function as three distinct sections; two for error handling and one for finalizing and setting the configuration.
No Worries About a Non-Existent Configuration
As we discussed before with Get-SpeedTestConfig
, it’s never safe to assume that a configuration file already exists. This function is all about setting a config, however, so I’d rather not throw an error if one doesn’t exist; I’d rather create a blank one that will then be set by the calling user.
try {
Write-Verbose -Message "Trying Get-SpeedTestConfig before Set-SpeedTestConfig."
$config = Get-SpeedTestConfig -PassThru
Write-Verbose -Message "Stored config.json found."
}
catch {
Write-Verbose -Message "No configuration found - starting with empty configuration."
$jsonString = @"
{ "defaultPort" : "5201",
"defaultLocalServer": {
"defaultServer" : "",
"defaultPort" : ""
},
"defaultInternetServer" : {
"defaultServer" : "",
"defaultPort" : ""
}
}
"@
$config = $jsonString | ConvertFrom-Json
}
If the call to Get-SpeedTestConfig
doesn’t find an existing configuration file, an empty config is generated via a multi-line JSON string that is piped to ConvertFrom-Json
. This allows for compatibility with the rest of the function as if a file was found and read with Get-Content
.
No Ports Without Servers
Next, it’s important to consider what combinations of parameters make sense when setting a configuration as well as what configuration elements are already set. Having ports defined without an associated server doesn’t make sense because there is no default server for iPerf3, of course. Having servers without ports is fine because iPerf3 can run on a default port which is accounted for in my module.
# Detailed parameter validation against current configuration
if ($InternetPort `
-and (!($InternetServer)) `
-and (!($config.defaultInternetServer.defaultServer))) {
throw "Cannot set an Internet port with an empty InternetServer setting."
}
if ($LocalPort `
-and (!($LocalServer)) `
-and (!($config.defaultLocalServer.defaultServer))) {
throw "Cannot set a Local port with an empty LocalServer setting."
}
If either type of port is being set and an associated server is not also being set or is not pre-existing, an error is thrown.
Set and Write Out the Object
Finally, the JSON object is modified to store any arguments given by the calling user, converted to a JSON string, and saved in the configuration file.
if ($InternetServer) {$config.defaultInternetServer.defaultServer = $InternetServer}
if ($InternetPort) {$config.defaultInternetServer.defaultPort = $InternetPort}
if ($LocalServer) {$config.defaultLocalServer.defaultServer = $LocalServer}
if ($LocalPort) {$config.defaultLocalServer.defaultPort = $LocalPort}
Write-Verbose -Message "Setting config.json."
$config `
| ConvertTo-Json `
| Set-Content -Path "$($PSScriptRoot | Split-Path -Parent)\config.json"
Comments