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

Posted
Comments None

Last time we looked at a simple PS5 class for loading and viewing the RSS feeds from subreddits. If you’d like to look at the class definition in its entirety, you can check out that post here.

Let’s break it down piece by piece. First, after declaring the class keyword and the name of the class, we define the members. Notice that for two out of three of the members (properties), we type constrained them.

class RedditRSS {
    [string]$URI
    [datetime]$LastUpdate
    $Feed

The reason we can’t easily type constrain the $feed member is that it’s a specific XML format that PowerShell doesn’t already recognize. If we wanted, we could put [psobject] as the type, but that doesn’t really say much. The only danger here is that a user might try to directly write to the .feed member and break all the methods we already defined.

To prevent that, we can optionally hide a member by placing “hidden” in front of the definition (and type). That would look like this

class RedditRSS {
    [string]$URI
    [datetime]$LastUpdate
    hidden $Feed

I’ve chosen not to do this because it’s likely we will want to directly access the feed so it is better not to have to search for it. Hiding the member doesn’t make it inaccessible, but it will not be returned unless specified. To access a hidden member, you must specify it by name, such as

$myobject.Feed

or

$myobject | Select-Object Feed

Tip: to view hidden members of an object, pipe the object to

$myobject | Get-Member -Force

Constructors

If you recall, the easiest way to create an instance of our custom class is using the static constructor method. Methods are defined by the name of the method, parameters in parentheses, and then the code in curly braces. Our RedditRSS constructor looks like

    RedditRSS ([string]$URI) {
        SWITCH ($URI) {
            {$_.endswith("/")} {
                #If you copy the URL from your browser
                $this.URI = ($URI.Substring(0,($URI.Length -1))) + ".rss"
            }
            {!$_.EndsWith("/") -and !$_.EndsWith(".rss")} {
                #As above but missing the slash
                $this.URI = $URI + ".rss"
            }
            default {
                $this.URI = $URI
            }
        }
        $this.LastUpdate = Get-Date
        $this.Feed = Invoke-RestMethod $this.URI
    }

The only parameter it requires is the URL, which we’ve constrained to the [string] type. It could have been type constrained to [uri] and provided more robust error prevention, but this is meant to be a simple demo :) The switch statement should be self-explanatory. With it we make sure the the URL passed either ends with “.rss” and adds it if not.

Obviously what sticks out compared to creating a custom function is the $this variable. That always refers to the object we’re defining. So, we can set the internal property values and call one method from another, if we want.

Overload Definitions

You’ll see farther down we defined a method called “GetThread” with two parameters, [uint16] Index, and [boolean] (that is, true or false) Refresh. It returns a [PSObject]. There are a few things worth noting about this method.

    [PSCustomObject] GetThread ([uint16]$Index,[boolean]$Refresh) {
        IF ($Refresh) {
            $this.Refresh()
            $Output = Write-Output ($this.Feed[$Index] | Select-Object @{n='Title';e={$_.Title}},
                @{n='Author';e={$_.author.name -replace "^(/u/)"}},
                @{n='Updated';e={Get-Date $_.Updated}},
                @{n='Post';e={$this.ReplaceHTMLNumbers(($_.Content.'#Text' -replace "<.*?>"))}})
            RETURN $Output
        }
        ELSE {
            $Output = Write-Output ($this.Feed[$Index] | Select-Object @{n='Title';e={$_.Title}},
                @{n='Author';e={$_.author.name -replace "^(/u/)"}},
                @{n='Updated';e={Get-Date $_.Updated}},
                @{n='Post';e={$this.ReplaceHTMLNumbers(($_.Content.'#Text' -replace "<.*?>"))}})
            RETURN $Output
        }
    }

First, notice there are several instances of the Return keyword. That is because the class will error out unless the keyword appears at the end of each and every branch of logic. If you use IF THEN and ELSE, you must Return data in each case. This also applies to SWITCH statements like the one we used above. This means we have to be a little more thoughtful about how we define our methods.

Second, that second parameter looks a little cumbersome, doesn’t it? Wouldn’t it be better just to use a [switch] parameter like we do in custom CMDlets? Well, unfortunately we cannot use switches in methods. $myobject.GetThread($uri,switch) is improper syntax. In CMDlets, a switch is a way to set a [boolean] parameter that is false by default. To get around having to always specify every parameter, we can create “overload definitions.”

An overload definition is an alternate definition for a method that accepts a different set of parameters. We have only one overload definition in our class

    [PSCustomObject] GetThread ([uint16]$Index) {
        RETURN $this.GetThread($Index,$false)
    }

Does it make sense? If we want to use the GetThread method, but do not want the data to be refreshed first, we can omit that parameter completely. So

$myobject.GetThread(0)

is functionally identical to

$myobject.GetThread(0,$false)

My hope is that you’ll start to think of how a custom class could make some of your more complicated repeat tasks easier. PowerShell should make our lives easier, and let us figure out the complicated things once, instead of over and over.

Author

Posted
Comments None

Edit 2/5: Swapped out the wall of regex in the final method with the much simpler [Net.WebUtility]::HTMLDecode() method

Without a doubt, my favorite feature of PowerShell 5 is the new, easy way to create custom classes with the ‘class’ keyword.

That’s great, Mark, but I’ve never felt the need to make a custom class before. Why should I care now?

I’m glad you asked, hypothetical reader! Custom functions (CMDlets) are a great way reuse our code in future scripts and shell work and, even more, a great way to help others do the same. Sometimes, though, the complex nature of the data that we’re working with can become cumbersome when we’re purely using custom CMDlets and creating custom objects each time. Writing custom classes can help us with these tasks, as they let us precisely define the kind of objects we’re working with, how the data is returned, and what internal processes (methods) can be run to manipulate or update the data.

First things, first, let’s load up a custom class. Execute the following in your PS5 console or ISE window.

Kind of looks like a custom function, huh? So, now how do you use it? First, find a subreddit you want to read. “https://www.reddit.com/r/PowerShell/” seems like an easy candidate. To create an object of our new class, we use the new static constructor method provided in version 5, saving the results to a variable.

$reddit = [RedditRSS]::New("https://www.reddit.com/r/PowerShell/")

Now we can view the top thread with the GetThread method

$reddit.GetThread(0) | Format-List

The number in parentheses is the index of the thread, starting with 0. Our class caches the data from the subreddit. If we want to refresh the data, we can use the Refresh method

$reddit.Refresh()

Alternately, you can also force a refresh when using the GetThread method

$reddit.GetThread(0,$true) | Format-List

This might not seem any more useful than writing a function out. Afterall, you still need to pipe the results to Format-List to read the post. Imagine, though, that you create a second class, one that is a collection of RedditRSS objects. This second class has methods to update all of them simultaneously, or search all posts for a keyword, or look for twitter posts only. In part 2, we’ll look at how you might do this, and we’ll dissect the syntax here in a little more detail to make sure you’re well equipped to start writing your own PS5 classes.

Author

Posted
Comments None

Sooner or later you will need to learn to identify and explore different data types. This is an essential skill in writing and debugging your own scripts, and also in reverse-engineering the work of others or built-in Windows functionality. I’m not going to try to give a precise definition of what a data type is so here’s Wikipedia if this is new to you. The takeaway is that if an object or function needs a particular type of data, we need to know what that type is and how to satisfy its requirements.

To begin exploring types, let’s use a CMDlet you likely already know and love. In the console, go ahead and execute

Get-Date

You’ll see the date and time in the default format, something like “Tuesday, January 10, 2017 5:00:00 PM.” While it might appear Get-Date is returning the string above, it is actually returning a complex data type with built-in functionality we can leverage to get results really fast. Let’s do the same thing again, but this time ask for the specific date from above and save it to a variable for reuse.

$date = Get-Date "Tuesday, January 10, 2017 5:00:00 PM"

To verify that it saved the date properly, just enter the variable name and hit enter

$date
Tuesday, January 10, 2017 5:00:00 PM

1. Get-Member

You may be used to using Get-Member to figure out what the property names (members) of an object are, but it also returns another valuable piece of information: the object’s TypeName. If you execute

$date | Get-Member

and scroll to the top of the results, you’ll see the line “TypeName: System.DateTime.” Eureka. Now we know that $date is of the type System.DateTime.

PRO TIP: “System.” can be safely removed from applicable type names. [DateTime]$date is synonymous with [System.DateTime]$Date

2. GetType() Method

We can act directly on the built-in object methods to get the data type as well. If you scroll through the results from the last command, you’ll see this line

Name    MemberType
----    ----------
GetType Method

which tells us there is a method on the $date object called “GetType.” We must use the $data object directly to access this method

$date.GetType()
IsPublic IsSerial Name     BaseType
-------- -------- ----     --------
True     True     DateTime System.ValueType

3. Method Overload Definitions

If you look at the output from Get-Member above, you’ll see one of the other methods is called AddMinutes

Name       MemberType Definition
----       ---------- ----------
AddMinutes Method     datetime AddMinutes(double value)

That last column provides the information we need to call the method properly. First, it lets us know that a “datetime” object will be returned. Not all methods will return the same type as the object itself, or even any object at all, so this is not a reliable way to identify the object. What it does tell us though, is what kind of data type is expected when calling the method. The “double value” in parentheses tells us that it expects a value of type “double” (a double is a number that may contain a decimal).

$date.AddMinutes(1.5)
Tuesday, January 10, 2017 5:01:30 PM

Be precise! By dialing the correct types you can ensure reliable scripts and functions. Next time we’ll look at my favorite new feature in version 5, PowerShell 5 classes.

Author

← Older Newer →