Posted
Comments None

PowerShell 6 finally made it out of Beta two weeks ago. That is cause for celebration, as there are a lot of quality of life improvements for those of us who work all day in the shell. But it’s… not what you think if you have not been following the team blogs or at least perusing tech news the last year or so.

PowerShell 5 and the versions before it are based on the .NET Framework. You know, those giant packages that Windows developers love because they’re so easy to work with and end users and administrators tolerate, since it occasionally means having to update something else in addition to the software we are trying to run. Version 6, on the other hand, is based on .NET Core 2.0.

That means PowerShell 6 is open source, cross-platform— hello, PowerShell on a Raspberry Pi 3 —and part of Microsoft’s recent and radical trend toward cloud-first, open source friendly business. The bad news is that it does not support some existing modules, chiefly, Active Directory. The good news is that you can run 6 and your current version side-by-side and that Microsoft will probably be supporting version 5 for years to come, but… man, I hope they fix that bad news.

Author

Posted
Comments None

For day in and day out administration, we should engage in best practices and encourage our colleagues to do the same. Write scripts for longevity and legibility; comment thorougly; test, test, and test again. Sometimes, though, we just need a snippet of PowerShell to live fast and die young.*

Recently at work I was faced with a challenge: our technicians were going to be going to a few dozen computers, logging in locally, and performing some specific steps. We developed a process and accompanying documentation for them, but because they were authenticating locally, there would be no easy access to Domain-gated resources, including a soft copy of the documentation itself (for copy-paste). The solution seemed to be a to put the instructions on a temporary webserver with anonymous auth, and then blow it away at the conclusion of the maintenance.

IIS would work fine for this, but I had so far not had a reason to install IIS on the 2012 server that hosts my customized reports and other scheduled tasks and, besides, that would not be much fun. PowerShell and easy access to .NET classes to the rescue!

The core of the disposable webserver is the .NET [HttpListener] class. The only real “innovation” my script offers over similar online examples is that it utilizes the “GetContextAsync()” method, versus the typical “GetContext().” This lets us easily start and stop the service interactively, so we can inspect any of the objects at will. This can be a fun way to learn both about PowerShell and web services.

To learn the most from it, I suggest creating a simple index.html file in the same location as the script, and firing it up in the PowerShell ISE (as admin—it will not work othewise). At any point, hit CTRL-C or the ISE stop button to stop the server and interact with the objects the script has created. Note that you will need to stop the listener by executing the following in the shell before you can restart the script.

$listener.Stop()

For some fun, try changing the authentication to NTLM or Negotiate and see how that changes the $context variable. This can be a very practical way to learn about claims and tokens. Add a query in the URL when you make the request. Try returning JSON formatted data and consuming it from PowerShell with Invoke-RESTMethod. Try Posting data.

Just make sure you close the console window when you’re done because, after all, we’re Adminning Dangerously.

*The author does not admin dangerously. The author wishes to express that this is a figure of speech and there are many more dangerous things people regularly do than this simple, temporary script that provides read-only information. The author does not condone any actual dangerous adminning that readers of this article may undertake.

Author

Posted
Comments None

Beginning last year, the company I work for as begun blocking some web traffic by geographic IP. As an admin, there are all sorts of great resources hosted worldwide that I would like access to. While my friendly networking team is more than willing to help by adding necessary exceptions, I decided to write the following function so I could easily provide the relevant information when I suspect a resource is blocked.

The function uses the simple and useful freegeoip.net site which allows 15k anonymous calls per IP per hour. I cannot recall exactly why I set it to do a separate DNS lookup rather than calling the API by name instead of IP. I think doing it this way returned more data.

I might have goofed a little on the regex where it validates IPs to see that they’re external (I’m looking at you, 172.16.0.0\12) but it’s checked out in my testing so far. Honestly… it would just return blank data if a local IP gets through and you can always manually check any IPs that get warned out as internal.

There are a lot of little tasks like this that PowerShell is great at. I mean, look at that script above. If you’ve been using PowerShell for a year or more, and were pressed, you probably could have banged out a functional version in 5 minutes or less (15 minutes with the fiddly regex, which is an art unto itself). And then you could automate it into the background and move onto something else.

Author

Posted
Comments None

Some months back I was trying to sort some Active Directory accounts so they looked nice in a report, but finding it a bit tedious using my usual methods. Consider the following fictitious accounts:

$list
GivenName Surname SamAccountName Enabled
--------- ------- -------------- -------
Steve     Jones   J3921             True
Will      Spencer S1134            False
Mary      Smith   S4498             True
Juanita   Galvez  G2059            False
Henry     Chang   C1209             True

Suppose I want to sort the list so enabled accounts appear at the top. That’s not terribly difficult.

$list | Sort-Object -Property Enabled -Descending
GivenName Surname SamAccountName Enabled
--------- ------- -------------- -------
Mary      Smith   S4498             True
Henry     Chang   C1209             True
Steve     Jones   J3921             True
Will      Spencer S1134            False
Juanita   Galvez  G2059            False

As you can see, it defaults to listing the disabled accounts first (I’m assuming this is because “F” comes before “T”), so I have to use the -Descending switch to reverse the sort. But what if I then want to sort by the last name?

$list | Sort-Object -Property Enabled,Surname -Descending
GivenName Surname SamAccountName Enabled
--------- ------- -------------- -------
Mary      Smith   S4498             True
Steve     Jones   J3921             True
Henry     Chang   C1209             True
Will      Spencer S1134            False
Juanita   Galvez  G2059            False

Well, it did sort on the Surname property… only it sorted backwards! My instinct for dealing with this dilemma was to break the list out with Group-Object or create a new property using Select-Object used solely for sorting. But that is undeniably clunky and if you’re suspecting there is a more elegant way to sort our list then you are correct correct—PowerShell lets us provide one or more hash tables to Sort-Object that utilize additional logic.

The correct way to sort the list above is really quite straightforward

$list | Sort-Object -Property @{expression='Enabled';Descending=$true},Surname
GivenName Surname SamAccountName Enabled
--------- ------- -------------- -------
Henry     Chang   C1209             True
Steve     Jones   J3921             True
Mary      Smith   S4498             True
Juanita   Galvez  G2059            False
Will      Spencer S1134            False

The only available keys we can use in the hash table are ‘Ascending,’ ‘Descending’ and ‘Expression.’ The Expression can be the name of a property, or a script block that applies some intelligent sorting. To help get the creative juices flowing, see if you can solve the “puzzles” below by figuring out how our list would be sorted.

$list | Sort-Object -Property @{expression={!$_.Enabled}},Surname
$list | Sort-Object -Property @{expression={($_.GivenName + $_.Surname).length};descending=$true},Surname
$list | Sort-Object -Property @{expression={[int]($_.SamAccountName.SubString(1,4))}}

Solutions below!

1. Does the same sort as our example in this post. Enabled accounts first, then alphabetically sorts the last name.
2. Reverse sorts by total name length, then alphabetically by Surname.
3. Sorts based on the 4-digit number in the SamAccountName. Note: this assumes that all SamAccountNames follow this pattern.

Author

Posted
Comments None

Group Policy is a strange, wonderful, and all-too-often frustrating aspect to running a Windows domain. Sometimes wading in is like turning over a log in a damp forest. There can be a lot more going on than you bargained for, especially if you as the current admin have inherited your network.

Recently at work I was faced with a network drive mapping that was popping up that I could not explain. It was not being mapped from a logon script, and looking in Group Policy Management, it became clear that a manual search would be incredibly tedious. I’m sure you’ve been there: show, show, show, show… next… show, show, show, show… and on and on. One solution is to save your Group Policy objects as an html or xml report and search them in a text editor. Because even that sounds tedious, I wrote a function to do it for me.

This requires the GroupPolicy module, which is available for newer versions of Windows, and likely requires at least PowerShell 3+. The usage is pretty straightforward. If you want to get right in and look for a specific computer, printer, etc, that exists on your domain, try

Find-MdtGPOString -Pattern "myprinter"

On a, let’s call it, mature domain, that probably will take longer than you were expecting. So to help speed up multiple searches, you can save a report to search and re-search.

$Report = Find-MdtGPOString -ReturnReportOnly
Find-MdtGPOString -Pattern "myprinter" -GPOReport $Report
Find-MdtGPOString -Pattern "myserver" -GPOReport $Report

There are some considerations here. Most importantly, we are searching through all of the XML of the object. That means you are going to want to use very specific searches and avoid generalities like “computer”, “name”, “enabled”, etc. Also, the Pattern parameter is constrained to the [regex] type. If you don’t know regular expressions, that’s okay. In most cases, just entering the string you want to find will work. Just watch out for backslashes (”\”) which are used as an escape character. To include them in your search pattern, double them up where they appear, e.g. “mydomain\\username” instead of “mydomain\username”.

Good luck and hopefully this will shed some light on the darker recesses of your network.

Author

← Older Newer →