Over the past few years I have been writing various tools that my team and I use to accelerate and automate various systems administration tasks. Previously I had been publishing these eclectic tools as individual script files on the TechNet script repository. Now I decided to collect the tools into a toolbox module called MrAToolbox (for Mr. Automaton). Now as I update the module you can automatically get the new version through PowerShell (Update-Module). Here’s the list of functions included as of version 1.0.2.
I’ve been working on a project to roll out automated patching to the servers in our environment. We have been using a combination of group policy and WSUS, which is a great solution, but it doesn’t allow for multiple maintenance windows. Since we are primarily a Windows shop we chose to use System Center Configuration Manger (SCCM) as our solution. If you’ve worked with SCCM you know that “it’s a full time job.” So over the last few months we’ve been carving off some servers from our WSUS and moving them to SCCM for patching. During the changeover we needed to verify if the server was actually talking to SCCM instead of our existing WSUS infrastructure. For the first few servers I logged on and launched regedit.exe to look at the policy key HKLM\Software\Policies\Microsoft\Windows\WindowsUpdate. After doing this two (too many) times I realized this wasn’t going to scale. I put together a PowerShell script to read the registry keys we needed from one or more remote servers.
Hello. My name is [redacted], and I’m a performance junkie. It’s been five days since I wrote a PowerShell script to make me work faster.
Sometimes I feel like a NASCAR pit crew chief constantly trying to find ways to shave off seconds of inefficiencies in the way that I work. I’ve learned that the less I leave the keyboard to use the mouse the more I can get done, especially if I just need to start an app.
The Start Menu was introduced to the world with Windows 95 and has gone through many iterations since. Since Windows Vista, Microsoft built search into the Start Menu to help us “quickly” find the apps we wanted to use. Unfortunately it hasn’t been very fast, perhaps because the search is also accessing an index of files and email as well. In 2007 Launchy was created to be a single purpose application launcher to quickly start apps based upon keystrokes. Once I taught myself to use it, I have never looked back. I still use the Start Menu for various purposes, but to start my apps I just press Alt + Space, type a few letters and press Enter.
With Windows 8/8.1 and Windows 10 Microsoft is moving to a new style of app which has gone by many names, Metro, Modern, Windows Store, and now Universal Windows Platform (UWP). These apps are packaged into a Appx format and run in isolated ‘containers’. These UWP apps are registered into the modern Start Menu, but not as shortcut files which means Launchy can’t index them. I set out on a journey to figure out a way to get Launchy to “see” these apps so that I can keep my hands on the keyboard.
My first thought was to find the exe that associated with the UWP app. UWP apps are installed in two locations.
Paths to UWP Apps
However running those exe’s didn’t seem to do anything, or it popped up the SmartScreen warning.
I did find the processes suspended in the task manager.
It seems like these exe’s have to be launched via the container or host process. I hit a dead end, or it was time for another approach.
I found some information on the Googles (actually Duckduckgo) on UWP apps registering protocols which can be used to launch the app. Now we were on to something. The blogs and documentation I found listed example apps and their protocols, but not how to retrieve them. I decided to look into the PowerShell Appx module.
Cmdlets for Appx Module
CommandType Name Version Source
I started with Get-AppxPackage to see what properties the cmdlet would output. The only properties I found that were useful were Name and InstallLocation.
From there I browsed the xml tree until I could find and isolate the protocol. Now that I had a found a way to get the protocol for UWP apps, I had to look up some code for creating shortcuts from PowerShell using the COMObject.
Instead of making one “big” script to do the whole process I decided to make one script per tool. One script’s job was to “get” the protocol of an UWP app and then another script to create the shortcuts.
Hyper-V, Microsoft’s hypervisor platform, has really grown up in the last few years. Introduced with Windows Server 2008, Microsoft has iterated its hypervisor platform reaching version “3.0” with Windows Server 2012. Version 3.0 has often been considered the magic version number for Microsoft as to when the product is not only good enough, but also a contender. Then they further fine-tuned the product with Windows Server 2012 R2 making it a worthy VMware competitor. Hyper-V does not match feature for feature with VMware, but on most of the features that matter the hypervisors are equals.
To implement Hyper-V host clusters in your environment requires many elements to be integrated properly. Thankfully Microsoft PFE’s and the community maintain a Best Practices Checklist on TechNet that you can use to make your Hyper-V deployment a success. I recommend you read through it carefully a number of times during your design phase of your Hyper-V deployment. Missing a crucial component could make the difference between success and failure of the project. I’m going to highlight a few key points from the checklist.
Since a hypervisor is actually going to be running on bare metal, we must select hardware that will provide the power, features, and stability that we need. We need to begin by selecting the hardware from a vendor that is listed as being officially supported for running Hyper-V. Start with the Windows Server Catalog to see if your preferred vendor hardware and components are certified. Verify the CPU model meets the virtualization requirements. Windows Server 2012 R2 is licensed by socket, but with Windows Server 2016 Microsoft will switch to a per-core licensing model similar to SQL. Until Windows Server 2016 is released (and your EA renews) the strategy is to buy as many cores per socket as you can afford to maximize your licensing. The similar strategy should be used for memory: buy as much memory as you can afford. Typically the first resource to be used up is memory. Use the Datacenter edition if you plan to implement many more than two VM’s as it gives you unlimited guest Windows server licenses. The standard edition includes licenses for two VM’s.
Strongly consider using 10 GbE or higher network interface cards, if you can afford it, to allow for fast live migration of large VM’s, and especially if you plan to use SMB or iSCSI SAN. Also consider buying NIC’s that support RDMA (RoCE) for SMB direct for higher performance, especially if implementing SoFS SAN. Select NIC’s from manufacturers who are known to have reliable drivers. Bad drivers will destroy your Hyper-V environment. We will dive deeper into networking of Hyper-V hosts and guests in a later post.
When installing the OS, aim to use the server core interface to minimize patches and vulnerabilities. Manage the server from an administrative system with the RSAT tools and PowerShell. Ensure rollups and applicable hotfixes are applied to your hosts. And just to state the obvious, keep your Hyper-V clustered hosts at the same patch level. The parent partition or management OS should have the minimal roles and features necessary including Hyper-V, Failover Clustering, MPIO, and SNMP where applicable. The only other installed items should be manufacturer’s drivers and utilities along with backup, operations, and antivirus agents. Be extremely careful about installing antivirus on your Hyper-V hosts. The antivirus exceptions must be setup correctly or you may at best degrade performance and at worst corrupt your virtual machines. One list tip about the OS of your Hyper-V hosts: protect your hosts by limiting who can log on to them. Using server core will keep the away the server admins “who know enough to be dangerous.”
In later blog posts we will cover network configuration for the host and for guests, including converged networking with iSCSI.
This post is part of the #PSBlogWeek PowerShell blogging series. #PSBlogWeek is a regular event where anyone interested in writing great content about PowerShell is welcome to volunteer for. The purpose is to pool our collective PowerShell knowledge together over a 5-day period and write about a topic that anyone using PowerShell may benefit from. #PSBlogWeek is a Twitter hashtag so feel free to stay up to date on the topic on Twitter at the #PSBlogWeek hashtag. For more information on #PSBlogWeek or if you’d like to volunteer for future sessions, contact Adam Bertram (@adbertram) on Twitter.
Once you’re done getting schooled on everything this post has to offer head on over to the powershell.org announcement for links to the other four past and upcoming #PSBlogWeek articles this week!
The skilled PowerShellers who authored the previous PowerShell blog week introduced and developed the practice of advanced functions.
As you mature in your PowerShell skills from one-liners to scripts, advanced functions, and eventually modules, you may realize the need to log the activities you are performing with your tools. Logging your PowerShell scripts can be extremely important for tools that change settings or data so that you can audit who made changes and when.
Surprisingly, logging functionality isn’t a built-in PowerShell feature (yet) so we’re left with building our own logging tools. Hit up your favorite search engine, the TechNet Gallery, or the PowerShell Gallery, and you’ll find quite a few people who have built their own logging functions that may meet your needs.
I’m going to walk you through an advanced function that you can use to add logging to your scripts, but first, I want to show you how I got there.
When I first realized the need for logging in my scripts I started by using the Add-Content cmdlet, which I would sprinkle here and there in my scripts to capture key points in my operations.
When I realized that I was repeating too much code, I decided to create a helper function in my scripts to make it simpler for me.
Sample Log File:
Querying system foroperating system information
It saved me a little work, and it looks a little cleaner. However, the log file is only lines of text without any specific information about when each action happened or how much time elapsed between actions. Let’s add a time stamp to our helper function.
Here’s the log:
2015-11-2222:17:21Querying system foroperating system information
Better right? We can do better. We should offer a way to differentiate the type of message we are writing to a log file. Sometimes we just have informational messages, but we also might have warning or error messages we would like to log. With our current log format, we can’t tell what is an error or simply informational without reading the log file message line by line. Let’s add a Level parameter.
2015-11-2310:02:23Info Querying system foroperating system information
2015-11-2310:02:23Info Microsoft Windows8.1Enterprise
Now that’s starting to look like a decent log file that we can we can search through using grep Select-String.
Let’s promote this helper function to an advanced function, add some logic, and sanity checks so that we can have a versatile logging tool for our PowerShell toolbox.
Write-Log PowerShell Advanced Function
Write-Log writes a message to a specified log file with the current time stamp.
The Write-Log function is designed to add logging capability to other scripts.
In addition to writing output and/or verbose you can write to a log file for
Created by: Jason Wasser @wasserja
Modified: 11/24/2015 09:30:19 AM
* Code simplification and clarification - thanks to @juneb_get_help
* Added documentation.
* Renamed LogPath parameter to Path to keep it standard - thanks to @JeffHicks
* Revised the Force switch to work as it should - thanks to @JeffHicks
* Add error handling if trying to create a log file in a inaccessible location.
* Add ability to write $Message to $Verbose or $Error pipelines to eliminate
Message is the content that you wish to add to the log file.
The path to the log file to which you would like to write. By default the function will
create the path and file if it does not exist.
Specify the criticality of the log information being written to the log (i.e. Error, Warning, Informational)
Use NoClobber if you do not wish to overwrite an existing file.
Stepping through the code, we see our comment-based help for the “next guy” (including myself who tends to forget how my own code works after a few weeks). Then, I added the magic keyword CmdletBinding to transform our lowly function into an advanced function with cmdlet superpowers. I attempt to use standard parameter names where applicable and use the ValidateSet attribute on the Level parameter to ensure the user enters a proper level.
The Begin block sets the VerbosePreference to Continue so that our Write-Verbose statements are sent to the Verbose pipeline.
The Process block starts by creating a new log file, including requisite path, ensuring that we don’t clobber (overwrite) the file when the NoClobber switch is present.
Based upon the selected Level, with Info being the default, we then write the message to our log file as well as send the supplied message to the Verbose, Warning, or Error pipeline where applicable.
That’s great, but how do I use this in my scripts? I’m glad you asked. After the function is loaded, either via dot-sourcing or added to a module, you can begin adding the Write-Log function to your scripts.
2015-11-2310:16:48INFO:Restarting Windows Time Service
Although this is just a basic example of how you could use Write-Log in your scripts, you can incorporate it in a way that works best for you. I found it helpful to include a header, footer, and log rotation as part of my template script.
PowerTip: Read more about setting default parameter values $PSDefaultParameterValues so that you don’t have to specify the Path every time you call the Write-Log function (#Requires -Version 3.0).
Here’s an example of a log generated from my Set-IPAddress function which shows the name of the script, where it was run, and who ran it (names were changed to protect the innocent).
If you think you can use this Write-Log function, feel free to download it and add it to your tool belt. If you see ways you could improve the script, feel free to send along your suggestions via Twitter or improve it yourself on github. If you don’t like the script, feel free to download it anyway, and then you can have the pleasure of putting it in your Recycle Bin.
In summary, we find, at times, that our scripts, functions, and modules need some basic text logging, especially if the code is going to modify a system configuration or data. We walked through Write-Log, an advanced function that we can use to create readable text logs.
Taking screenshots is necessary in the life of an IT worker. Every day we run into error messages that require investigation and resolution. I often find when I run into an issue that chances are I will run into the issue again (and usually a day or two after I completely forgot how I fixed it). That’s why I use screen shots in combination with OneNote to document what I do on a daily basis to fix recurring problems that my brain has forgot about. (PowerTip: OneNote can index text inside of screenshots.) Screenshots should also be used for documenting processes, installations, and changes. Although there are many spectacular screenshot tools out there, I’ve always tried to build my workflow around in-the-box apps when they are sufficient. For my screen shots I use the built-in Snipping Tool which has been around since Windows Vista. However when launching the Snipping Tool you have to switch to the app and then tell it to start a capture. In my never-ending quest for efficiency I want to find ways to keep my hands on my keyboard and use the mouse less and less. I needed a way to use Launchy to not only launch Snipping Tool, but to start the capture. I found and modified an Autohotkey script to help me accomplish my goal. Now I can press Alt+Space, type “snipit” and immediately begin my screenshot selection.