Author Archive

Iron Scripter 2019 Prelude Challenge 4

The Chairman is very taken with the idea of automation and deployment pipelines. He is intrigued by all of the options available to IT Pros and his Iron Scripters. To that end, he is proposing this warm-up challenge to continue preparing you for the Iron Scripter event at the upcoming PowerShell + DevOps Global Summit.

The Challenge

Using Azure, AWS or any virtualization technology you prefer, i.e. VMware or Hyper-V, create a PowerShell solution you can initiate from a PowerShell prompt to create and start a virtual machine that meets the following configuration criteria:


  • System event log size set to 2GB
  • Create a folder called C:\Data with sub-folders of abbreviated month names
  • Install the following Windows Features
    • Windows Server Backup
    • Telnet Client
    • FTP Server
    • Enhanced Storage
  • Make sure the following Windows Features are NOT installed
    • SNMP
    • PowerShell v2
  • Create a local administrator account for RoyGBiv with a decent password.
  • Set the PowerShell Execution policy to RemoteSigned
  • Add 172.16.100.* to TrustedHosts
  • Install the PSScriptTools module from the PowerShell Gallery

Other than manually initiating the process, this should be a completely hands-free experience.


You don’t have to necessarily create the virtual machine entirely from scratch using PowerShell. It is perfectly acceptable to assume you have some sort of baseline server image that you can launch and configure. Using a container may be another option.

Good Automating!

Jeffery Hicks

Iron Scripter Prelude 3 Solution

Last week the Chairman provided another prelude challenge. This challenge was intended to get you familiar with Just Enough Administration (JEA). This is an admittedly advanced topic, but the Chairman expects nothing less from his Iron Scripters. To assist you in your quest the Chairman has graciously provided another sample solution. JEA is not a cookie-cutter technology as every organization is different and every business case is slightly different.

To get started you would have needed to create a RoleCapability file and a PSSessionConfigurationFile. Your first step might have been to ask PowerShell for help.

Given the requirements, you might have created a role capability file like this.

This file would most likely be placed in a RoleCapabilities folder in a module that you will deploy to the target server.

a JEA module layout

The psm1 file can be empty with no functions exported. The proxy and helper functions are defined in the psrc file but they could also have been defined in the module. Otherwise, the manifest is pretty simple.

To use this will require a PSSessionConfiguration file.

Here is the completed file.

It is a good idea to test it.

At this point you will want to set up the Active Directory domain with the necessary groups and user accounts.

Next, the node needs to be setup.

Once setup you might want to verify what the user can and cannot do.

The JEA endpoint in action

Creating and deploying a JEA configuration takes some planning, testing and refinement.

The Chairman will be back with another prelude challenge.

Jeffery Hicks

Iron Scripter Prelude Challenge #3

In 2019 the security of your network and data center should be a primary responsibility. As beneficial as it is to use PowerShell to manage things, the default implementation, especially from a remote administration perspective, may be overly broad. To remotely manage a server with PowerShell requires more administrative access than you may feel comfortable giving.

This is the reason for Just Enough Administration, also known as JEA. With this technology you can create secure remoting connections that designated non-administrators can use to fulfill a job role. The Chairman is a vocal JEA proponent and expects his Iron Scripters to be able to understand the PowerShell concepts and commands behind it.

The Challenge

Create a PowerShell module to be used in a JEA configuration with a role capability for BITS administration. If you have previous JEA experience you should understand this request. If not, you will need to do some homework.

The JEA solution should only allow the following activities in a remote session:

  • full Access to the BitsTransfer cmdlets
  • Access to Get-CimInstance but only for the Win32_Service class and the Bits service
  • Access to Get-Date
  • Access to the gsv and gcim aliases
  • Access to all of the -Service cmdlets, except New-Service, and only for the Bits service
  • Access to the Bitsadmin.exe command line utility
  • Access to the netstat.exe command line utility
  • Access to the filesystem but only to the C:\BitsDownloads folder

You will need a RoleCapabilities folder with a corresponding  .psrc file. Set up the JEA configuration and endpoint on a remote server.


  • This will be easier to create in an Active Directory environment
  • Create an Active Directory group for BITS Administrators and put the account for the BITS administrator in this group
  • You may want to create your own proxy functions.

New to JEA? The Chairman recommends you start at

Jeffery Hicks

Prelude #2 Solution

The last Chairmans’s challenge was based on a task you might have to face in real life – fixing someone else’s code. Although, that someone may be you! Of course it helps to know what should happen, which the Chairman so nicely provided. When run, the function should have produced output like this:

Expected output

A Solution

Without debating different techniques or commands, here is a solution for a working version of the function.

The Chairman will let go through the code and compare it to your solution.

A Pester Test

Of course, once modified an Iron Scripter will ensure that any future changes won’t break the code. For that, a Pester test is the best choice of defensive weapon. Here is a test file that is assumed to be in the same directory as the script file.

Creating a Pester test is as much of an art as anything so your solution will most likely vary. But when run you should get a result like this.

A Pester test result

If your Pester skills are soft, the Chairman recommends a hearty workout regimen. If you require assistance, seek out masters in the forums at In the mean time keep flexing your PowerShell skills as another Chairman’s prelude challenge looms.

Jeffery Hicks

Iron Scripter 2019 Prelude Challenge #2

To continue preparing you for the upcoming battle, the Chairman has arranged another prelude challenge. If the Chairman is feeling generous, a sample solution may be provided in about a week’s time.


You have been given a function from a previous co-worker that you need to maintain. The code does not work as expected any more. Correct the function according to your faction’s scripting philosophy and create a Pester test file to unit test the code. You do not necessarily have to re-write the function from scratch. This is the code that needs to be corrected and serve as the basis of your Pester unit test.


This function should get disk information from one or more computers. It should accept computer names via a parameter and from the pipeline and should only get a drive C through G. Errors should be logged to a text file with a file name that includes a timestamp value in the form YearMonthDayHourMinute.

For non-North Americans feel free to adjust the date time format in the function and Pester tests.

Correct output should look like this:

sample correct output

Be sure to verify that the logging errors feature works as expected.

Allez de code!

Jeffery Hicks

Iron Scripter Prelude 1 Solutions

So you had a little bit of time to work out a solution the first prelude challenge. Even though you might be tempted to answer all solutions as a script, sometimes you just need a command or two that will run successfully at a console prompt. Once you have the essential PowerShell commands working, then you can build a script or function around it. For the first prelude challenge most of you (hopefully) figured out a way to use the WindowsFeature cmdlets and Compare-Object. The Chairman has collected some code samples that are more or less representative of the different factions.


For clarity it might help to define variables for the source and target servers.

Using Get-WindowsFeature for the source computer would seem the obvious step. But here is more elegant approach.

The challenge is to install features that are installed but missing on the target and remove features installed on the target not installed on the source. By grouping the features based on their installed property as a hashtable, it is easier to reference features.

Windows Features as a hashtable

You can use the hashtable to install missing features.

Installing missing Windows features

And likewise to remove features that shouldn’t be installed.

Removing unwanted Windows Features


The Battle faction might take a more direct approach.

Brutal but it gets the job done.


The Flawless faction will invest a bit more time and might come up with a re-usable function like this:

The Flawless function in action

The Chairman hopes you find the sample solutions informative. Of course there are other ways to achieve the desired result for each faction. Hopefully you are sharing those results with your faction-mates.

Stay tuned for another Iron Scripter prelude challenge.

Jeffery Hicks

Iron Scripter 2019 Prequel 1

The Chairman has arranged for another prequel challenge to prepare you for the upcoming Iron Scripter battle. A suggested solution will eventually be provided. In the mean time, see what you can do with this problem.

Using a set of Windows features, like Windows Internal Database, on a baseline server, configure a target server to match. Remove features that aren’t installed on the baseline and install features that are missing on the target. You can configure the source server with any combination of Windows features you’d like. Your goal is to copy the feature set to a target server.

This does not need to be a one-liner or even a function, although you are welcome to do so. What commands would you run in a PowerShell remoting session to accomplish this task? While it is true you could use a technology like Desired State Configuration, see if you can accomplish this task through traditional PowerShell scripting techniques.

Jeffery Hicks

A Warm-Up Solution

To prepare you for the upcoming battle you were offered a warm-up challenge. The basic challenge was to take the output from Get-Counter and pipe it through a command you created to output more user-friendly output. As a bonus you were also challenged  to display the default results as a table. As with almost everything in the PowerShell world there are options and alternatives.Today the Chairman shares one possible solution and some of the reasoning behind it. This solution is not necessarily better than yours but rather should be something you can learn from.


Because this is going to be a stand-alone function it has to live in .ps1 file. You might have to consider who might be using this script. What operating system or version of PowerShell will they be running? The suggested solution requires that the user be running PowerShell 5.1 or later, even though technically you could get by with earlier versions. The script is also requiring the Microsoft.PowerShell.Diagnostics module.

This is the module that contains the Get-Counter cmdlet which you need in order for you command to work. Requiring it also has the added benefit of loading it into your PowerShell session if it is not running. This is important for parameters definitions.


The only parameter the sample solution requires is one for the performance counter samples. This should be mandatory and come in from the pipeline.

It is often useful to indicate the object type which we’ve done here. Note that without the requirement for the diagnostics module, PowerShell throws an error when loading this function because the type has not been loaded yet. But using the requires statement imports the module if not already loaded which adds the necessary type.


We were able to see a few people’s efforts on this challenge. One major variance was in naming the function. There is no reason not use a verb from the list created by Get-Verb. There are plenty of choices that should work such as Optimize, Format, or Convert. If you wanted to use a non-standard verb, you could create it as an alias.

There is nothing wrong with providing an alias that someone can use at the command prompt to run your code.

Creating Custom Objects

The core of this challenge was restructuring the output from Get-Counter into something easier to read and consume. You would piped Get-Counter to Get-Member to discover property names. In the work we saw many of you figured out that you needed to process the CounterSamples property. Parsing the values is where you can get creative. You could use regular expression patterns to get the computername and other values. In the sample solution we took the easy way and split the Path property into an array.

One thing we noticed using this technique was an extra blank element. Piping the array to Where-Object is telling PowerShell to only keep objects where something exists. If there is a value then $_ will implicitly be True.

Because the array is consistent, it is easy to generate a custom object.

We’ll come back to the use of PSTypename in a bit.

The Result

Here is our sample solution in action.

Converting Get-Counter samples

The function can consume counter information and write objects to the pipeline.

Consuming Converted Counters


The formatting challenge was to take the default output which is a list and present it as a table. In other words, the output of your command should display results in a table, not a list. In order to achieve this you need to create a format.ps1xml file using the object’s typename. In the sample solution we are adding a PSTypename property.

Viewing the custom type name

You might also have used a PowerShell class or created a custom object and inserted a new typename.

Regardless of technique you now need to create a custom xml file. Often the best thing to do is fine an object type in an existing file $pshome\DotNetTypes.format.ps1xml that is close the output you want, copy and paste it into a new file and modify to fit your needs. This is admittedly a tedious process. Another option is to use the New-PSFormatXML command that is part of the PSScriptTools module which you can install from the PowerShell gallery. This functions will create a format.ps1xml file based on an object.

You only need to give it a single instance of the object. Here is what the result can look like.

You can modify the file as you need to changing column headings or creating custom values. Here is the final format.ps1xml file we came up with.

Our solution creates a default view grouped by a custom scriptblock that shows the computername and timestamp. It also creates a second view called ‘TimeStamp’. This comes in handy when processing counters over a period of time. In order to use this file, it has to be imported into PowerShell. We put the file in the same folder as the ps1 file and add this command:

One thing to point out if you are new to working with format files is that because there is often a bit of trial and error you may need to start a new PowerShell session to load each revision. Here is the new result.

Formatted Output

When processing multiple computers you need to sort on the Computername property.

Processing multiple computers

The other custom view we defined is useful when monitoring samples over a period of time.

Formatting results over time

The Final Result

Here is the complete script file of our sample solution.

Remember, these warm-up exercises and preludes aren’t really a competition. They are designed to get you ready for the Iron Scripter event at the PowerShell+DevOps Global Summit and hopefully teach you something new along the way.

Stay tuned for the next prelude exercise.

Jeffery Hicks

Preparing for Battle

Over the course of the next several weeks there will be even more preparatory challenges. There is not necessarily any right answer as long as it meets the criteria of your chosen faction and produces the desired result. As you prepare yourself, you are encouraged to share and discuss your work with others in the community and in your faction. Use whatever social media platform and online resources that you prefer.

You might create a public GitHub repository and spread the word via Twitter. Or you might post your work on your own blog and invite the world to marvel at your work.

The choice is yours.

Jeffery Hicks

Iron Scripter 2019 is Coming

It is a new year and the Chairman is preparing a fiendish new challenge for this year’s PowerShell + DevOps Summit. That time is rapidly. If you partook in last year’s battle then you understand what is expected of you. if you have not already done so align yourself with the faction that best suits you.

In order to prepare you for battle the Chairman has prepared a series of preparatory challenges. It is a foolish warrior who goes into battle unprepared. Just sayin’.

To prepare you for the upcoming battle, see what you can create according to the guiding principles of your faction that solves the following problem. The bottom line is that your solution should work.

The Challenge

The Get-Counter cmdlet is very useful in obtaining performance counter information. Unfortunately, the Chairman feels the output is less than friendly. Especially if you wish to consume the output such as with additional filtering, exporting to a json file or writing to a database.

Your challenge is to create a PowerShell function that takes the output from Get-Counter and transforms it into a more user friendly object. Each counter sample should be a separate object with these properties:

  • Timestamp
  • Computername
  • CounterSet
  • Counter
  • Value

You are free to use whatever property names you want as long as it is clear what the values represent. A sample might look like this:

Advanced Challenge

If you want to really challenge yourself, define a custom type and format view that gives you a result like this:

All the information you need to know is included in this post. The Chairman will share a possible solution within the week. Good Luck and Good Scripting!

Jeffery Hicks