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

Posted
Comments None

After a few weeks’ break for Christmas, I’ve returned to blogging and tonight we’re looking at a couple quick rules to help the reuse of our code.

There are more proper and less proper ways to do things in many programming languages. Some have very clear conventions to follow, and some seem to be the Wild West of programming tricks and shortcuts. The former are nice because you can generally tell what something does and whether it will work by looking at it, and the latter are good because you can get results very quickly, and you will feel like you know kung-fu. (Hint: pick legible syntax over kung-fu whenever possible… even when you are not going to re-use your code, you will have an easier time retaining knowledge if you are not using “tricks” to get your task done).

Below I will cover a few good hygiene habits that will help your work stand the test of time, while also exercising and deepening your understanding of the ‘Shell.

One of the easiest shortcuts PowerShell provides are CMDlet aliases. These shortened or alternative names are nice for their brevity and familiarity with other shell environments. Two of the most common aliases are “dir” and “ls.” Both are used in lieu of Get-ChildItem and are likely familiar to DOS and Unix users respectively. Aliases are invaluable in the console window, but always prefer typing out the full command name in saved scripts. Yes, the next admin to come along can simply open up a console and type “Get-Alias sujb” but it is better if you just used “Suspend-Job” in your saved script in the first place so she does not have to. I would especially avoid the aliases “%” (ForEach-Object) and “?” (Where-Object) in saved scripts, as the novice PowerShell user may very easily mistake them for wildcards or some strange operators. Rather than confounding them, we should be enlightening and gently guiding our acolyte coworkers, helping them in their own journeys in the ‘Shell.

We must make a similar consideration when it comes to type declarations. When you create functions for re-use, type constrain and or validate your parameters. That means that your function will demand that the parameters it receives are of the exact type of data that it needs to execute. Sometimes data can be ambiguous, and errors or, worse, undetected strange behavior, can occur. PowerShell data types are declared by the type name in square brackets “[ ]” before the data or variable, e.g., “[string]$x”.

For an example of the importance of type constraints, enter the following lines individually in a console window, followed by enter, and see what is returned.

2
"2"
2 + 2
"2" + "2"
"2" + 2
2 + "2"

Does it make sense what is happening? When you use quotes, PowerShell interprets the character 2 as a string, rather than an integer. When PowerShell adds strings, it appends the second one to the first. When it adds integers, it is doing math. When mix up the line with quotes and no quotes, PS type casts the second piece of data to match the first, so that it can perform the appropriate kind of addition.

PowerShell does not enforce strong typing, but that does not mean you cannot enforce strong typing in your functions. What does it mean to enforce strong typing? It simply means we declare the type of data for our parameters within the function. Let’s make weakly typed function of the mess above.

Function AddMe ($number1,$number2) {
Write-Output ($number1 + $number2)
}

Now if you iterate over the combinations above as parameters, you get the same results.

AddMe "2" 2
22
AddMe 2 "2"
4

Now let’s type constrain the function and try again. As we are clearly looking to do arithmetic, we’ll demand that the parameters are integers. Now, even when they are implicitly specified as strings, the function will cast them as integers and return the correct value.

Function Addme ([int]$number1,[int]$number2) {
Write-Output ($number1 + $number2)
}
AddMe "2" 2
4
AddMe 2 "2"
4
AddMe "2" "2"
4

See how nice and consistent that is? Learning about the specific data types silently sliding down the pipeline is an important step in our mastery of PowerShell, and requiring data of the appropriate type can harden our scripts against human error.

Author

Posted
Comments None

PowerShell 5 includes Get-Clipboard, a handy CMDlet that had previously been up to individual users to add to their PS Profile. In my own version I added the capability to parse a region copied directly from an excel spread sheet, including column headers as property names. This serves two purposes: My coworkers were more likely to use it if they can use Excel; I am more likely to use Excel if I can use PowerShell.

To use it, select a region in Excel you wish to get and copy it. If you grabbed the column headers along with the rest of the data, just enter

Get-MdtClipBoard -SpreadSheet

If you do not have access to the column headers in your selection, you can specify them manually such as

Get-MdtClipBoard -SpreadSheet -Headers "Column1Title","Column2Title","Column3Title"

To do: re-format this as a wrapper for the PS 5 CMDlet, including the additional format types (image, audio, FileDropList).

Author

Posted
Comments None

When your computers in a corporate network are encrypted with BitLocker, you can optionally have the recovery information uploaded to Active Directory. There aren’t really any major drawbacks to doing this, as in most cases if an attacker has elevated access to AD, he can simply log into an encrypted workstation without bothering with BitLocker and your workstation hard drives are not the greatest of your problems.

The BL info is stored in an odd manner, which can make scripting retrieval a little difficult. I took the opportunity to create the following command, trying to meet these objectives:

  1. It needs to be able to verify the computer we’re looking up
  2. It should be able to accept pipeline input for batch jobs and utilize the Begin section of the function to perform any initial searches that are used again for each object
  3. It should also be able to look up the password/computer by recovery key, for when Joe Exec was given a loaner and doesn’t know the computer name and stuck a thumb drive in and tried to boot (or tried to dock it, or any of the other myriad cases where BitLocker starts acting up)
  4. It should default to NOT retrieve the password, but do so optionally
  5. Let’s use Comment Based Help for some simple documentation

This is what I came up with:

I’ve seen alternate methods of looking them up, where instead of returning all “msFVE-RecoveryInformation” objects, you use the computer’s Distinguished Name as a SearchBase. I may go back and optimize this, but I’ve been satisfied with the performance on this method. Let me know if you have any thoughts on the matter.

Author

← Older Newer →