Posey's Tips & Tricks
Using Configuration Files to Control PowerShell Scripts, Part 2: Building Blocks
Learn how to use PowerShell's GUI capabilities to dynamically create configuration files that enhance script flexibility and usability.
In my previous article in this series, I explained that you can make your PowerShell scripts far more flexible and dynamic by leveraging a configuration file as opposed to hard coding all of the values. I also explained that you don't necessarily have to create the configuration file manually. Instead, you can build a script that instructs PowerShell as to how to create the desired configuration file. In this article, I want to walk you through the script that I am using to generate the configuration file used by my Hello World script. I will initially be discussing individual blocks of code within the script, but I will provide you with the full script at the end of the article.
The first thing that my script does is to load a couple of .NET assemblies. These assemblies are necessary because of the fact that the script uses a GUI interface. I'm not going to cover the GUI in extensive detail because there is a lot of GUI related code and I really want to focus on the code that produces the configuration file. Here is the code that loads the assemblies:
# Load necessary assemblies for GUI
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
The next thing that the script does is to create a form. A form is essentially just a GUI window. The code block below creates the form, provides a window title, and defines the form's size:
# Create the form
$Form = New-Object System.Windows.Forms.Form
$Form.Text = "Hello World Setup Tool"
$Form.Size = New-Object System.Drawing.Size(400,400)
The next section of the code defines several labels. Labels are the mechanism used to display text within a PowerShell GUI. My script contains labels instructing the user to select a language, select a font size and that sort of thing. You can see an example of the code used to create a label below:
$LanguageLabel = New-Object System.Windows.Forms.Label
$LanguageLabel.Text = "Select Language"
$LanguageLabel.Location = New-Object System.Drawing.Point(10,10)
$LanguageLabel.AutoSize = $true
As you can see, we are creating a label object, assigning a text string to the object, defining the label's position on the screen, and then telling PowerShell that we want to automatically adjust the label's size.
The next section of the code creates all of the drop down menus that are used for selecting the various configuration items. Here is an example of the code used to create one of these menus:
$LanguageMenu = New-Object system.Windows.Forms.ComboBox
$LanguageMenu.Items.AddRange(@("English", "Spanish", "Italian", "French"))
$LanguageMenu.Location = New-Object System.Drawing.Point(150, 10)
$LanguageMenu.SelectedIndex = 0
Creating a drop down menu involves creating a .NET ComboBox object and then adding a series of items to this object. In this case, I am adding languages such as English and French. The next line of code shown above defines where the drop down will appear within the window. The last line of code makes it so that the first item within the list of choices becomes the default value. This line of code is necessary, because without a default value the user could potentially click Save without making a choice, which would result in an incomplete configuration file. This would likely cause the Hello World script to crash.
Once I have defined the various menus, the next section of code creates the Save and Exit buttons. Here is the code:
# Create a Save button
$SaveButton = New-Object System.Windows.Forms.Button
$SaveButton.Text = "Save"
$SaveButton.Location = New-Object System.Drawing.Point(100, 250)
$ExitButton = New-Object System.Windows.Forms.Button
$ExitButton.Text = "Exit"
$ExitButton.Location = New-Object System.Drawing.Point(180, 250)
$ExitButton.Add_Click({$Form.Close()})
Buttons work in much the same way as any other GUI object in that you have to create the object and define its position on the screen. However, if you look at the last line of code above, you can see that I have added a click action to the exit button. In this case, the click action tells PowerShell to close the form, which has the effect of terminating the script.
I have also created a click action for the Save button. It is this click action that actually creates the configuration file. Here is the Save button's click action code:
$SaveButton.Add_Click({
# Create a hashtable for configuration
$Config = @{
Language = $LanguageMenu.SelectedItem
FontSize = [int]$FontSizeMenu.SelectedItem
FontColor = $FontColorMenu.SelectedItem
BackgroundColor = $BackgroundColorMenu.SelectedItem
Resolution = $ResolutionMenu.SelectedItem
DateFormat = $DateFormatMenu.SelectedItem
}
# Convert to JSON and save to file
$Path = "C:\Scripts\Hello-World-With-Config-File\Config.json"
$Config | ConvertTo-Json | Set-Content -Path $Path
# Display a message that the config file has been saved
[System.Windows.Forms.MessageBox]::Show("Configuration saved to `config.json`.")
})
This block of code begins by assigning variable values to text labels (not the GUI labels that I talked about earlier. For example, a label called Languages is assigned the value of $LanguageMenu.SelectedItem. In other words, what ever value has been selected from the Language menu is being added to the Language value.
Once all of the menu choices have been processed, this section of the code converts the data to JSON format and then writes it out to the configuration file. The last line of the code block above causes a popup message to be displayed, indicating that the data has been saved. You can see what this popup looks like in Figure 1.
The last thing that has to happen is that all of the individual GUI elements have to be added to the form and then PowerShell must display the form. Each GUI element has a variable associated with it. For example, the label that asks the user to select their language is associated with a variable named $LanguageLabel. You can see what this looks like in one of the blocks of code that I showed you earlier. Here is the command to add the Language label to the form:
$Form.Controls.Add($LanguageLabel)
Finally, the command below causes the form to be displayed:
$Form.ShowDialog()
Now that I have shown you how my setup script works, I want to conclude the series in Part 3 by showing you how my Hello World script works. As promised however, I want to wrap up this article by giving you the full source code associated with my Setup.ps1 script. Here is the code:
# Load necessary assemblies for GUI
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
# Create the form
$Form = New-Object System.Windows.Forms.Form
$Form.Text = "Hello World Setup Tool"
$Form.Size = New-Object System.Drawing.Size(400,400)
# Create Labels for the various options
$LanguageLabel = New-Object System.Windows.Forms.Label
$LanguageLabel.Text = "Select Language"
$LanguageLabel.Location = New-Object System.Drawing.Point(10,10)
$LanguageLabel.AutoSize = $true
$FontSizeLabel = New-Object System.Windows.Forms.Label
$FontSizeLabel.Text = "Select Font Size"
$FontSizeLabel.Location = New-Object System.Drawing.Point(10,50)
$FontSizeLabel.AutoSize = $true
$FontColorLabel = New-Object System.Windows.Forms.Label
$FontColorLabel.Text = "Select Font Color"
$FontColorLabel.Location = New-Object System.Drawing.Point(10,90)
$FontColorLabel.AutoSize = $true
$BackgroundColorLabel = New-Object System.Windows.Forms.Label
$BackgroundColorLabel.Text = "Select Background Color"
$BackgroundColorLabel.Location = New-Object System.Drawing.Point(10,130)
$BackgroundColorLabel.AutoSize = $true
$ResolutionLabel = New-Object System.Windows.Forms.Label
$ResolutionLabel.Text = "Select Window Size"
$ResolutionLabel.Location = New-Object System.Drawing.Point(10,170)
$ResolutionLabel.AutoSize = $true
$DateFormatLabel = New-Object System.Windows.Forms.Label
$DateFormatLabel.Text = "Select the Date Format"
$DateFormatLabel.Location = New-Object System.Drawing.Point(10,210)
$DateFormatLabel.AutoSize = $true
# Create a series of droplists that will act as menus
$LanguageMenu = New-Object system.Windows.Forms.ComboBox
$LanguageMenu.Items.AddRange(@("English", "Spanish", "Italian", "French"))
$LanguageMenu.Location = New-Object System.Drawing.Point(150, 10)
$LanguageMenu.SelectedIndex = 0
$FontSizeMenu = New-Object system.Windows.Forms.ComboBox
$FontSizeMenu.Items.AddRange(@(12,14,16,18,20,22))
$FontSizeMenu.Location = New-Object System.Drawing.Point(150, 50)
$FontSizeMenu.SelectedIndex = 0
$FontColorMenu = New-Object system.Windows.Forms.ComboBox
$FontColorMenu.Items.AddRange(@("Black", "White", "Red", "Blue", "Green", "Purple"))
$FontColorMenu.Location = New-Object System.Drawing.Point(150, 90)
$FontColorMenu.SelectedIndex = 0
$BackgroundColorMenu = New-Object system.Windows.Forms.ComboBox
$BackgroundColorMenu.Items.AddRange(@("White", "LightGray", "Black", "Red", "Blue"))
$BackgroundColorMenu.Location = New-Object System.Drawing.Point(150, 130)
$BackgroundColorMenu.SelectedIndex = 0
$ResolutionMenu = New-Object system.Windows.Forms.ComboBox
$ResolutionMenu.Items.AddRange(@("640 x 480", "800 x 600", "1024 x 768"))
$ResolutionMenu.Location = New-Object System.Drawing.Point(150, 170)
$ResolutionMenu.SelectedIndex = 0
$DateFormatMenu = New-Object system.Windows.Forms.ComboBox
$DateFormatMenu.Items.AddRange(@("Long Format", "MM-DD-YYYY", "Month Year"))
$DateFormatMenu.Location = New-Object System.Drawing.Point(150, 210)
$DateFormatMenu.SelectedIndex = 0
# Create a Save button
$SaveButton = New-Object System.Windows.Forms.Button
$SaveButton.Text = "Save"
$SaveButton.Location = New-Object System.Drawing.Point(100, 250)
$ExitButton = New-Object System.Windows.Forms.Button
$ExitButton.Text = "Exit"
$ExitButton.Location = New-Object System.Drawing.Point(180, 250)
$ExitButton.Add_Click({$Form.Close()})
# Function to save configuration
$SaveButton.Add_Click({
# Create a hashtable for configuration
$Config = @{
Language = $LanguageMenu.SelectedItem
FontSize = [int]$FontSizeMenu.SelectedItem
FontColor = $FontColorMenu.SelectedItem
BackgroundColor = $BackgroundColorMenu.SelectedItem
Resolution = $ResolutionMenu.SelectedItem
DateFormat = $DateFormatMenu.SelectedItem
}
# Convert to JSON and save to file
$Path = "C:\Scripts\Hello-World-With-Config-File\Config.json"
$Config | ConvertTo-Json | Set-Content -Path $Path
# Display a message that the config file has been saved
[System.Windows.Forms.MessageBox]::Show("Configuration saved to `config.json`.")
})
# Add labels to the form
$Form.Controls.Add($LanguageLabel)
$Form.Controls.Add($FontSizeLabel)
$Form.Controls.Add($FontColorLabel)
$Form.Controls.Add($BackgroundColorLabel)
$Form.Controls.Add($ResolutionLabel)
$Form.Controls.Add($DateFormatLabel)
# Add the menu to the form
$Form.Controls.Add($LanguageMenu)
$Form.Controls.Add($FontSizeMenu)
$Form.Controls.Add($FontColorMenu)
$Form.Controls.Add($BackgroundColorMenu)
$Form.Controls.Add($ResolutionMenu)
$Form.Controls.Add($DateFormatMenu)
#Add the Save and Exit buttons to the form
$Form.Controls.Add($SaveButton)
$Form.Controls.Add($ExitButton)
# Show the form
$Form.ShowDialog()
About the Author
Brien Posey is a 22-time Microsoft MVP with decades of IT experience. As a freelance writer, Posey has written thousands of articles and contributed to several dozen books on a wide variety of IT topics. Prior to going freelance, Posey was a CIO for a national chain of hospitals and health care facilities. He has also served as a network administrator for some of the country's largest insurance companies and for the Department of Defense at Fort Knox. In addition to his continued work in IT, Posey has spent the last several years actively training as a commercial scientist-astronaut candidate in preparation to fly on a mission to study polar mesospheric clouds from space. You can follow his spaceflight training on his Web site.