Windows 10 includes a number of built-in apps ranging from basic apps like Calculator and Weather to more task-focused apps like Mail and Photos. While these built-in apps are fine for most situations, in a business environment, they may be inappropriate, redundant or unsupported. Very often, these apps are my pose a security risk.
The problem is that Microsoft doesn’t make it easy to uninstall some of these apps. There is no uninstall button when uninstalling using normal methods. The built-in apps must be uninstalled through PowerShell.
Before we get started, I do not recommend uninstalling all the packages. Many of them are needed for the Windows 10 “Experience” and others, like the .NET framework, are needed for other programs. Be picky about which applications to uninstall.
You can reinstall all the applications and I will have a PowerShell command to just that at the end of this article
Different sets of packages
There are actually two different kinds of applications that we will be working with.
AppXPackages – Applications installed with the operating system
AppXProvisionedPackages – Applications installed as part of the user profile first time set up.
The first step is to get an inventory of the applications that are installed. To do that start PowerShell with elevated privileges. For the AppxPackages we can enter the command Get-AppxPackage.
The provisioned packages have a slightly different command and also need the -Online parameter. The -Online parameter denotes that we want a list from the current online operating system as opposed to an image file located in a local directory.
This will present a list of all the details regarding each package. This is a rather verbose listing and all I am interested in is the Nameof the package for the AppxPackages and the DisplayName for the provisoned packages.
To make things a little easier, let’s pipe the results through Select-Object and select the Name and the DisplayName properties. This will give us a list like the one below. This list is easier to work with. Now we can easily copy and paste the applications were are interested in.
One trick that I use is to save the results to a text file and then open that file in Visual Studio Code. For example:
Now that we have our list, we can start building our script. Selecting the applications from the list that you want to be uninstalled, build a simple array and populate the names of the applications into the array as seen below. I have given my array a variable called $ProvisionedAppPackageNames.
In my current day job, I’m often asked about using PowerCLI to perform a number of tasks in a vCenter cluster. This is a story about a recent request for assistance from a colleague who needed to export a custom monitoring appliance template to a new vCenter cluster that was being built. My colleague was under a time constraint and did not have the necessary access to the template.
Never wanting to miss a chance to use PowerShell or PowerCLI, I jumped in head first to help. I gathered the necessary information from my colleague, and began connecting to the cluster:
There are two cmdlets that stand out;
Both of these cmdlets appear to be exactly what I need. But first, I’ll educate myself a little more on the proper use for each. I start with
Export-VApp This cmdlet will export the powered off VM as an OVF to the current directory my session is in by default if I do not specify a path. I have a path in mind, so I’m going to go with the following code:
I should have thought about that a bit more before running the command. You cannot export a running VM to an OVF! No worries, this is a quick fix. I’ll modify my code a little more:
That was easy. With the template appliance now offline, I could resume running the
Export-VApp cmdlet I tried to run earlier. This process took about 10 minutes, and wasn’t a very large appliance to begin with. Now I have a 3.5 GB appliance ready to be deployed into another vCenter environment. Or do I?
Feeling like I’m driving the train now, I enter and run the following code:
The notion just crossed my mind that I got ahead of myself, and failed to find out if I was actually connecting to another vCenter cluster.
Something happened when I began to import the previously exported VM appliance. A sea of red error messages.
I read the error message, and sure enough, the host is not a part of a vCenter cluster and therefore does not have proper licensing to complete the import using PowerCLI. This is a limitation that VMware enforces. No worries, I could still connect to the web interface of the host and manually import using the HTML 5 interface. The wizard walks you through each step, give the imported appliance a name, choose the OVF, datastore, deployment type (thick or thin provisioned), and verify the configuration. After that, select finish and the import begins. While the previous import attempt would work great with a vCenter cluster, it was simply not going to work in this situation. This took a little longer than expected but was straight forward. You can read more about the process here.
In the end, the import was a success, and my colleague met their deadline.
Until this exercise, I was not aware that not all PowerCLI cmdlets were available in all situations. However, Both of us learned a new skill and, while experiencing some unforeseen adversity, we still accomplished the task at hand. Too often we rush through IT projects looking for the ‘quick’ fix. Watch your speed, take another minute or two to ask questions, step back and understand the problem you are trying to solve. You may find you’ll learn something new.
Despite its lack of features and options, PowerShell ISE used to be the primary tool to develop and edit PowerShell Scripts. It offered an integrated development environment (IDE) that included some basic features to build scripts and modules.
Microsoft is no longer actively developing the PowerShell Integrated Scripting Environment (ISE) and is being replaced by the more powerful and versatile open source Visual Studio Code (VS Code). With its ever-expanding options and extensions, VS Code is quickly becoming the new standard tool for developing not only PowerShell, but just about any other language you choose.
Despite all the new features available in VS Code, leaving the familiar environment of PowerShell ISE is difficult. It is like watching your child go to college. You are proud of the achievement but sad about having left a comfortable environment.
VS Code can be intimidating at first. As the default settings of VS Code can be a little hard to work with if you are used to working with PowerShell ISE. However, it’s highly customizable, and with the addition of Extensions and a few configuration settings, you can make VS Code look and behave just like PowerShell ISE.
To get VS Code to look like PowerShell ISE, the PowerShell Extension needs to be installed. To install, select the setting gear at the bottom left, then pick Extensions.
At the search box, type in Powershell and then install. This extension adds a few features to the default settings of VS Code.
To get the distinctive look of PowerShell ISE, select the settings gear and then Color Theme. Choose the PowerShell ISE theme.
Now that you have the look of PowerShell ISE, we need to set the behavior to match ISE.
The default install of VS Code lacks some features of PowerShell ISE, such as Zoom, Tab-Completion, Intellisense, and Code Snippets.
For setting the environment to match that of PowerShell ISE, we need to add some environment settings to the VS Code settings.
Keyboard and Mouse Actions
Open the command palette using the ctrl+Shift+P key combination. In the command palate box, enter “Preferences Open Settings (JSON).” This will open up a two-pane window with the user settings on the right. Insert the following code between the brackets on the right pane.
“Quote me as saying I was mis-quoted.” -Groucho Marx
There are two types of quotes that can be used in PowerShell. Single and Double quotations. Some critical differences between the two can make or break a script. Knowing these differences will make you a more effective PowerShell scriptwriter and help you avoid a rather simple mistake.
In this post, I’ll quickly explain these differences and provide examples of each scenario.
Single quotation strings are what you will most often use and encounter when creating or troubleshooting PowerShell scripts.
Consider the following example:
# Assign a variable with a literal value of 'single'.
# Put the variable into another literal string value.
Write-Host-Message'Fun with $MyVar1 quotes.'
Now examine the output:
In the above case, PowerShell ignores
$MyVar1 and treats the variable literally as $MyVar1, exactly what was typed. There is no substitution here.
But how do you get PowerShell to recognize the variable value within a quoted string value? That’s where double quotation comes in.
Double quotation gives you a dynamic element to string values. You will encounter this type of string quotation when the string contains dynamic data from variables stored in memory or dynamically generated.
Consider the following example:
# Same as previous example. Create a variable with a simple value.
# Now to demonstrate double quotes magical power of interpretation!
Write-Host-Message"Fun with $MyVar2 quotes."
Now examine the output:
In the above case, PowerShell processes
$MyVar2 because it was enclosed by a double-quoted string. Double quotes make PowerShell parse for text (the variable) preceded by a dollar sign and substitutes the variable name the corresponding value.
Real World Scenario
Now, apply this knowledge to a real scenario. Let’s say that you need to create a small function that will give an operator on your team some real basic information:
Date / Time
Disk % Used
Disk % free
You need to return this information visually to an operator. Simple.
First, some pseudo code. We need to display the date time as today’s date and time. Think about how this string value will work. We can use
Get-Date and the
-Uformat parameter to give us the required date/time by using the correct patterns:
$date=Get-Date-UFormat"%m / %d / %Y:"
Testing the code in a PowerShell terminal confirms this works:
That takes care of the first part of the script. Now, I need to gather some disk information to also output to the terminal. The key metric I’m looking for is the percentage of free space remaining. I’ll display this information using
Write-Host again, but this time I’ll need to insert additional code inside the double-quoted string. Remember, this information will be dynamic. For the purposes of this example, I’m going to create a variable, then utilize an available member type property to get the value I’m looking for:
Testing the code in a PowerShell terminal confirms this works:
Perfect. We now have two variables that we can place in the strings that the operator will see when running this function. So let’s assemble the bits into the final script that will become our function:
Write-Host-ForegroundColorYellow"There is $($disk.PercentFree)% total disk space remaining."
Testing again in a PowerShell terminal, here is what the operator would see:
Notice what I did inside the last
Write-Host line with the
$disk variable. PowerShell evaluates the $( ) construct as an entire subexpression then replaces the result. Doing it this way also helps you avoid having to create more variables, which saves memory and can even make your script faster.
The function still needs some work. So let’s finish it off by adding some math to show a full calculation to the operator:
Write-Host-ForegroundColorRed"There is $(100 - $disk.PercentFree)% total disk utilization on drive $($disk.Name)."
Write-Host-ForegroundColorYellow"There is $($disk.PercentFree)% total disk space remaining."
The operator can now make some faster decisions while supporting a remote system by using this function.
There’s not much to quotes in PowerShell. The one key concept to remember is that you need to know when to be literal ( ‘ ‘ ), and when to be dynamic ( ” ” ). By default, you should always use single quotes unless there is a requirement for dynamic data in the string construct. I hope you found this information useful!
“Distrust and caution are the parents of security.” -Benjamin Franklin
If you’ve ever deployed Windows Updates to clients on your network, you have probably been asked by your manager(s) what KB’s were deployed, and when if an issue comes up on a workstation or server. Unfortunately, sometimes the built-in WSUS reporting tool can leave you frustrated and doesn’t have great functionality for generating them outside of the WSUS management GUI. A problem I regularly encounter is a crashing MMC, which then crashes the WSUS services, causing me to have to reset the node and start over. It’s very annoying.
Distrust & Caution
I was recently asked by a group of managers that were working on validating a security vulnerability scan for some assistance. This vulnerability scan was claiming that a set of systems were missing particular Microsoft KB’s, KB’s that were recently approved, deadlined, and showing as installed in the WSUS management console. I sent some screenshots of the console status along with my sysadmin reply. I didn’t give it much thought at the time because I was busy with other projects and this was a routine request.
A day or so went by, and another vulnerability scan was run, producing the same results. Management was not convinced that the updates were installed. Having issues with WSUS from time to time, I started to distrust the built in reports and the management console. To be cautious, and a little more diligent, I decided to bypass the WSUS management console and go straight to the workstations and servers that were showing up in the security vulnerability scan.
Some Explicit Remoting Here, A Couple of Cmdlets There….
Luckily, the security vulnerability scan only found about 4 workstations and 12 servers with these supposedly missing KB’s. So I created a simple list in a text file using the Fully Qualified Domain Name (FQDN) of each host. I also knew for a fact, that the missing KB’s would have been installed in the past 30 days as I just completed a maintenance cycle.
With this knowledge in hand, I jotted down some pseudo code to help me begin. Here’s what I outlined:
Store my text file that contains the list of hosts.
For each of the hosts in that file, run a command.
The command must gather installed KB’s installed in the last 30 days.
The output only needs to contain the hostname, KB/HotFix ID, and the install date.
The output needs to be readable, and just needs to be a simple file.
No fancy coding needed, just comparing visually to what WSUS reporting was displaying.
Based on my notes, I had a good idea of what I was looking for and what cmdlets I might need. The primary focus was on the
Get-HotFix cmdlet. What this cmdlet does is query all the hotfixes (more commonly referred to as security updates) that have been applied to a Windows host. You can read more about this cmdlet and how to use it here.
Get-HotFix does not support implicit remoting so I needed to come up with method to run this cmdlet on the systems I needed to report on.
Invoke-Command does and you can pass multiple values to the
-ComputerName parameter. I already have saved a list of hosts I am targeting, so I’ll save myself some typing and store those hosts as a variable. To do so, I’ll have to assign a variable name and make the value the list of hosts.
Get-Content will read the content of the text file line by line creating an array of sorts. Let’s call this array
$Hosts . Now I have a command, some data to feed to the next set of commands, but I need to make the resulting data readable and concise.
I want to take a moment here to emphasize “Filter First, Format Last.”. Remembering this will help you when working with these types of scripts. Now, running the
Get-Hotfix cmdlet by itself will typically result in a long list of updates that have been applied to a host. Filtering helps gather just the information you need. Without filtered data, formatting is useless at this point. Think of filtering as your data type requirements, and formatting as how you want that data displayed. For my purposes, I already had the requirements thought out. I needed to get updates installed in the past 30 days.
To filter, I will need to use the
Where-Object cmdlet and then pass along some member properties and comparison operators with a dash of math. To do this, my pseudo code will take every object returned (
Where-Object-Property installed on data is greater than (
-gt)today’s date (or whenever I run the script) minus (-30) days ago. That will get the initial data I’m looking for but I want to filter the returned objects and their properties a little more. This is where
Select-Object will help, allowing me to further trim the amount of data to be displayed to just a couple of crucial properties.
Now that I have the data properly filtered, now I can move on to formatting the results into a usable format. To do so I’ll pipe ( | ) the results from my previous filtering to
Format-Table-Autosize and output as a file type of my choosing. I’ll need to use
-ErrorActionSilentlyContinue parameters to ensure that each result is written to the next line in the output file and if an error occurs, it won’t cause the rest of the hosts to not be contacted.
I chose to go with a text file because I didn’t require anything fancy. You can change the output to meet your needs. My output looked something similar to this:
As a mentor, I’m often asked, “How do you get inspiration for a PowerShell script?”, followed by something sounding similar to, “I just don’t know what I can script or where to start.” When I’m told that, the person saying it sounds defeated and about to give up. This was a question & feeling I had myself early in my PowerShell journey too.
“So, what’s the answer, Bill?” you might ask. Well…the answer you seek, young grasshopper is…
How I Started
Let’s talk about how I started to approach scenarios and challenges by using existing documentation as my base of reference, or pseudo code. Many years ago, I struggled to make scripts. No matter the language, it was an awful feeling of imposter syndrome. I could read some code and stumble around clumsily figuring out some bits here and there, but it was a constant struggle. It wasn’t until I started documenting my IT processes that I began to correlate written word to small bits of pseudo code that I could then translate in to PowerShell one-liners. Once I started doing that, things got a lot easier.
I have some maintenance tasks that I have to perform at least twice a month for User Acceptance Testing (UAT), Quality Assurance (QA) and production environments. Lucky for me, these tasks are already written down and stored in a team KB article. With half the battle already won, I carefully read through the documented steps for taking systems and applications down gracefully for maintenance. The tasks progresses something like this:
Place monitoring agents in maintenance mode (nothing like getting email alerts for known issues)
Stop IIS application pools 1,2 & 3 on X server
Stop IIS application pool 4 on Y server
Stop services on A,B,C & D servers
Log into WSUS, approve & deadline OS updates to specific groups
Allow reboots to occur.
Looks pretty straight forward right? My predecessors were manually performing these steps for years. Well, I’m not my predecessors. There’s enough information here to begin making a script. Let’s begin.
Task 1 could be automated, but for the purposes of this post I’m skipping it because not all monitoring platforms are the same. Moving on.
Task 2. Now we have something to work with. Using key words, I begin by discovering what commands I have available that might stop an IIS application pool:
Stop-WebAppPool Appears to be exactly what I need to complete this task. Spend a minute or two reading the help if it’s the first time you’ve seen this cmdlet:Get-HelpStop-WebAppPool-Online
Now I know how to tackle Task 2andTask 3. My code now looks like this:
Stopping IIS Application Pools Example
On to Task 4. Now this one should be simple for anyone who is new to PowerShell, as it’s a common task that is demonstrated in a lot of training material. This task will make use of the
Stop-Service cmdlet. There are a few ways this can be done, but I’ll keep it simple for now so we don’t get into the weeds and detract from the overall goal.
On each host, there are two services that work concert with each other as part of an application that was hosted on the IIS servers in Tasks 2 & 3.
Stop-Service will allow us to enter multiple values in the
-ComputerName parameter, and since the naming scheme I’m using is short, it’s not a big deal to enter them all here. I’ll also be using the
-ServiceName parameter, which also accepts multiple string values. When finished assembling the code, it looks like this:
Stopping Multiple Services on Multiple Computers Example
Stop-Service-ComputerName'A','B','C','D'`#yup I used a backtick here
Great! I’ve just saved a few minutes of not having to RDP to each of these systems, or use Server Manager, or type this all out in a PowerShell terminal.
Let’s Add More Stuff!
The whole reason for shutting all these services down gracefully is to be able to apply Windows security patches to the server OS without screwing up the applications if they were still being used during a scheduled maintenance window (humor me for a moment and save the snark about Windows Updates).
Enter PoshWSUS. This handy PowerShell module contains exactly what I need for the final component in my scripted task. There are a lot of cmdlets available in this module and I’m not going to explain all of them right now.
In order to complete the last step, I need to:
Connect to my WSUS server. Connect-PSWSUSServer-WsusServerlocalhost-Port8530-Verbose
Store the KB’s to be deployed as a variable. $Updates=Get-Content'C:\PScripts\Maintenance\updates.txt'
Store a deadline of 1 hour ahead of the time the script executes as a variable. $Deadline=(get-date).addHours(1)
Get updates, approve then set install flag along with the deadline flag to assigned groups.
As you’ll see above, I’ve thought out the logical steps and created some pseudo code to get started. It’s the same process you’ll follow when trying to create your own scripts. It’s almost as if there’s a theme developing here!
Now on to what you’ve been waiting for. Let’s assemble all the bits into the final script:
This script will perform documented tasks that
were once done manually.
# I like to place variables at the top of the script to
# make it easy to change them without digging through the
# Clean up, pick up, put away. Clean up, everyday!
The key thing to remember here is, if you can write it down, you probably can script it. So go back and look at some of your documented processes and procedures, and you’ll soon discover that you’ll have enough inspiration to keep you busy for a while making PowerShell scripts.
If you’ve read my past blog post on “How I Learned Pester by Building a Domain Controller Infrastructure Test”, it should be pretty obvious that I’m a fan and love using Pester now. I even build small tests for small scripts like the one above.
I need to a quick test with some visual output since I’m typically running this script from a PowerShell terminal manually. So, with the same pseudo code used earlier, let’s build a simple test that will verify all the actions in our script did what we expected:
Basic Pester Test for My-MaintenanceTask.ps1 Example
I left out tests for servers B,C & D because they would be identical to the test shown for server A in the above example. Now all that is required to run the this test as part of the
My-MaintenanceTask.ps1 script would be to add this line at the end of that script:
SharePoint farm configurations are notoriously difficult in not only documenting accurately but also migrating those configurations to a new SharePoint farm.
Commercial tools and utilities help, but each tool has its pluses and minuses and some of them are not effective and often buggy. Additionally, the tools can be expensive and come with a high learning curve.
SharePointDSC.Reverse is a script developed by Nik Charlebois that utilizes SharePoint DSC resources to gather detailed information about the farm and outputs into a configuration file that can be consumed by PowerShell DSC and SharePointDSC resources.
The resulting PowerShell DSC configuration files can be used to create a near perfect copy of the farm to replicate in the new environment or can be used as a template for Azure automation.
SharePointDsc.Reverse currently supports SharePoint Server 2013/ 2016 and soon SharePoint 2019, running on Windows Server 2008 R2, Windows Server 2012 or Windows Server 2012 R2 or higher.
There are a few prerequisites before running the script. PowerShell v 5.1 is required. Two PowerShell DSC modules are also required and will need to be installed.
Log into the Central Administration server and open a PowerShell session as administrator. The SharePointDSC reverse script is installed with a similar command but using a script instead of module. To install the SharePoint Reverse script, we’ll use
How To Use
Now that we have all the necessary modules installed, it’s fairly easy to use. To start the process, enter sharepointdsc.reverse.
As the script runs, it asks for the credentials for the various managed accounts. Using the DSC resource provided by SharePointDSC, the script performs a detailed scan of the farm, gathering all the settings and configurations.
For a large farm, this will take several minutes to complete. Once it’s complete, It prompts for a directory to save the results. the resulting files can be consumed by SharePointDSC.
To validate the configuration, compile the spfarmconfig.ps1 file to create the .mof resources.
The resulting files from SharePointDSC.reverse can be used to duplicate the SharePoint farm in different environments, on-premises or in the cloud. The configuration file, the error log, and the environment data file, all contain detailed configuration settings of the farm. Custom solutions (.wsp files) are copied into the directory as well.
Duplicating the SharePoint farm
SPFarmConfig.ps1 file can also be uploaded to Azure Automation to duplicate farm configurations for your Azure based SharePoint farm. To duplicate the SharePoint farm in a new environment, apply the configuration to the farm by starting the DSC configuration.
In a multi-node farm, the configurationdata.ps1 file already has the node names, roles, and services that are running on each server in the farm. The file is formatted very similar to JSON and editing this file for the new environment can easily be completed using Visual Studio Code.
The spfarmconfig.ps1 file has the detailed farm configuration and also lists products installed and version numbers. It will also have details about each web application, site collection, and farms settings. Patches applied and version numbers of products installed are also displayed.
One additional benefit of these files is that they can be part of a disaster recovery plan. Restoring the farm from a complete loss can now be accomplished in hours instead of days.
It can be challenging to keep track of just what file shares have been set up in your environment. This becomes even more difficult if you have to track this information across multiple servers. Adding to the tedium is remotely connecting to each server to find the list the shares. Thankfully, using PowerShell makes this task a snap, whether you need to enumerate shares on just one server, or many.
Enumerate Shares on a Single File Server
Let’s start by connecting to a remote file server to gather this information from a single server. We will accomplish this by entering into a remote PowerShell session with our file server “FILE01”.
Once connected, it takes a single cmdlet to get file share information:
As you can see, this gives us a list of all of the share on this server. This also includes the administrative shares, whose share names are appended by $.
This does accomplish the task of getting a list of shares, but it is a little cluttered. We can clean up this list by using the -Special parameter and setting it to $false to specify that we do not wish to see the administrative shares:
There, that gives us a much clearer view of the share information we are looking for.
Now that we have our share on this server identified, it might be useful to list all of the properties for this share, especially if we are looking for specific details about our share:
This allows us to view quite a bit of information about our share, including things like the type of share, folder enumeration mode, caching mode, and of course, our share name and path, to name a few.
It is also possible to view the share permissions for this share by switching to the Get-SmbShareAccess cmdlet:
This gives us a list of the users and groups, and their current level of access to the share.
We might also have a time where we need to enumerate the share permissions to find out who has full access to a share:
With this information, it is easy to tell who has full access to the share and then take steps to remove that access if it isn’t appropriate for an individual or group.
Now that we are done enumerating shares on a single server, we need to make sure we close our remote PowerShell session:
Enumerate Shares on Multiple File Servers
It is also possible to retrieve this same information from multiple file servers, which is an area where PowerShell really shines. Using Invoke-Command to run Get-SmbShare, we can list the shares on both the FILE01 and FILE02 servers. If we also pipe the output through Format-Table, we can also get a nice organized list:
While entering the file server names manually is fine if there are only two or three servers, it becomes tedious if there are many dozens of servers to check. To get around this, we can assign the output of Get-ADComputer to the variable $FileServAD and get a list of all servers in the “File Servers” Organizational Unit (OU). From there, it’s easy to get the information:
David Lamb is a Systems Administrator managing Windows servers and clients since 1995, spending a large portion of his career in the aviation industry. His first certification was the MCSE on Windows NT 4.0, earned in 2001. David lives in Alberta, Canada, and is currently spending his free time learning PowerShell, blogging, and pursuing the MCSE certification on Windows Server.
Setting your DNS server search order with PowerShell is actually really easy. We’ll start with the Get-DNSClientServerAddress to get a look at our existing settings as you can see below.
So there you can see, we have our existing settings on ethernet interface index seven, and our addresses are 192.168.2.52 and the secondary server is at .51.
So we’ll do a quick nslookup to file01.corp.ad, to verify that our primary is in fact responding.
So there we go, you can see above that a responding DNS server is our primary at .52, and successfully returned .55 is our file server.
Now, let’s change the order of our DNS servers. To do that, we’ll use the Set-DNSClientServerAddress cmdlet. We’ll point it to interface index seven as listed above, and I’ll change our order, so 192, 168.2.51 is our primary, and .52 is now our secondary.
We’ll do a quick verification. I’ll check interface index seven.
There, now you can see above, .51 is now our primary as it’s listed first, and .52 is our secondary.
Do another quick nslookup, and you can see that that now returns from .51, which is our primary DNS server.
Adam Bertram is a 20-year veteran of IT and experienced online business professional. He’s an entrepreneur, IT influencer, Microsoft MVP, blogger, trainer and content marketing writer for multiple technology companies. Adam is also the founder of the popular IT career development platform TechSnips.
“There’s no sense in being precise when you don’t even know what you’re talking about. -John von Neumann”
I thought this was a good quote as the theme for this post. Re-read the quote, take it in, and then continue reading.
During the construction of a set of Pester tests, it can be increasingly difficult to follow the flow of each of the tests and the subjects against which these tests will be performed.
Since Pester tests PowerShell code, you are able to use a PowerShell method called Regions to separate section of code. These Regions will allow you to collapse large sections of similar code to create an overall more pleasant script reading experience. While that may assist to some extent, it does not help you find and run specific tests.
Here’s an example of what using a region would look like:
The problem is that this technique is only useful when reading code, but not necessarily nearly has helpful when executing code. A region doesn’t allow you to pick out precisely what test you want to run. That’s where using Tags comes into play.
Tagging is a Pester parameter that allows you to filter Describe blocks using a string or keyword value. When using Invoke-Pester with the -Tag & -Passthru parameters, only the Describe blocks that contain the -Tag value specified will execute.
Here is a simple example:
This is useful because you do not have to have multiple test files with only one Describe block in each, you could instead create a single master file with multiple Describe blocks, each with their own tag. This is very useful to do when you have an application or infrastructure stack you want to test, and have the ability to add any new regression tests you may need in the future.
You can use multiple tags for a Describe block, but you cannot use multiple tags when running Invoke-Pester. This took me a little time to figure out but after thinking about it, it does make sense. You want to run a single or suite of tests that have the tag, not multiple tests with multiple tags because that is what Invoke-Pester will do by itself!
Since I first started learning about Pester, I have been building a few infrastructure tests for use in the environments I work in often. One particular task involves working with domain controllers and occasionally do some investigation into replication issues.
Taking what I know and turning it into some tests that run quickly and uniformly has improved my response time to domain controller issues. Tags have been extremely helpful during a couple of troubleshooting tasks now that I could target the specific failed component(s) in my test set based on results of the initial test.
There is no reason to keep running full tests for example if, for example, DNS is not working. I can then target DNS services and infrastructure without chasing other possibilities, thus wasting time. Another benefit was that I did not have to go find additional scripts because I already had the -tag parameter set for the Describe block in question.