Craft and PowerShell: Why Software Engineering Practices Need to Be Applied to Infrastructure

Cloud platforms are becoming increasingly complex and sensitive to setup errors. Their management also requires many scripts, and scripting means maintenance and changes.
Organizational models are now moving toward teams that focus on management and evolution, and their size is no longer directly related to the infrastructure size. Because of this, these teams need tools to reduce risks during changes and maintenance and to be able to automate as many tasks as possible. As a result, they face the same problem as conventional software development teams: a small team with a huge codebase.
The first element in this list of tools is the scripting language they use.
All of the practices discussed in this post can be found in the DevCon #17 video (in French, starting at 1 hr. 37):
PowerShell, the Ops Swiss Army Knife
When it was first released, PowerShell could only be used in Windows because it was based on the .NET Framework. This limitation has been rectified in the latest versions, which were built first on .NET Core and now on .NET 6 and 7.
PowerShell is now available on all platforms: Windows, Linux, macOS, and even in containers.
The fact that it’s still based on a standard language introduces development paradigms to Ops. These include:
- Structured types
- Error handling
- Debugging
PowerShell is an excellent platform for writing large-scale scripts because of these features, which allow for a high level of maintenance and changes… as long as you adhere to some best practices.
Best Practice No. 1: Always Use Structured Types
Structured types let you store data in its native format and avoid common integer or date-type string interpolation errors.
All .NET basic types can be used. Strings, integers, Booleans, and dates are used most often.
Lists and hash tables can be stored natively in data structures. For example, converting a JSON file into a hash table to browse its contents is quite common.
Best Practice No. 2: Good Error Handling
As with any language, error handling is essential, especially when the scripts run in service accounts or agents in the background. Therefore, you must ensure that any errors are identified and corrected quickly.
PowerShell lets you decide what to do when an error occurs.
You can use the “-ErrorAction” parameter in the cmdlets to specify which action to take. The options include:
- Display the error and continue (default mode)
- Continue and suppress the error
- Throw an exception
Doing this for each command can be time-consuming. So, you can use this variable to implement it for all instances:
$ErrorActionPreference = "Stop"
See the Microsoft documentation for more information.
Here, if the exception is not handled, the script stops automatically. Handling exceptions is actually quite simple:
As with any advanced language, you can even say which exception you want to catch.
See the Microsoft documentation to learn more.
Best Practice No. 3: Avoid Surprises with Strict Mode
By default, PowerShell returns the value “$null” if the variable or property you are looking for does not exist. This can be helpful when browsing a JSON file, but leaves you open to many errors, including variable name errors. You can avoid this by telling PowerShell how to behave. This is called strict mode.
It’s advisable to start scripts with strict mode frozen at the highest level to avoid unpleasant surprises. For existing scripts, you must be more careful and look at each one individually.
See the Microsoft documentation on strict mode.
Best Practice No. 4: Testing
A good language is one that allows testing. Pester is an open-source library that lets you create comprehensive test suites. Among other things, it has:
- The ability to have test case generators
- Isolation for testing modules
- A set of assertions for better test readability
- Commands for creating mocks
Here’s what a set of tests looks like in Pester:
Best Practice No. 5: Controlled Environments
The other problem with infra is controlling the local and remote execution environment. Generally, you need a whole suite of command line tools on top of the scripting language to manage the infrastructure. Each tool has its own versions and limits. For the script to run the same way every time, ensuring everyone is working in the same environment is essential.
To maintain this control, the development and runtime environments must be containerized.
Visual Studio Code has a plugin system that makes this isolation possible.
Find Out More About the Craft
Want to learn more about Craftsmanship? See all the posts in our Craft Month series here:
- Is the Craft Still Relevant?
- How to Choose the Best Software Architecture with Architectural Drivers?
- How to Build an Infrastructure with Terraform?
- PySpark Unit Test Best Practices
- Telemetry: Ensuring Code That Works
- How to Boost Your Apps’ Performance with Asyncio: A Practical Guide for Python Developers