Tag Archives: Get-Member

Variables, and More About Note Properties

I have been thinking a lot about variables and note properties recently. You know that, if you read A Better Way to Solve the Same Problem. It sets the stage for what we are going to see today. The question that started that post was, whether or not I could add note properties to a single variable, such as PowerShell does with the $PROFILE variable. I could. I ended up creating a variable that held one URL when you returned the variable but also held four other URLs, one in each additional note property.

I want to take this further, and the best part is that all the things I was worried about trying to do myself, PowerShell just does for me without any thinking about it. Follow along and you will see.

Let’s set up the variable I was using last time to help this recap. Then we will create another variable and work with it to explore some new things I learned. Building this variable is being done a little differently than it was previously. It incorporates some concepts from another recent post, There is a Difference: Arrays Versus Hash Tables. First, we will assign our initial value to the variable.

$PSSites = 'https://docs.microsoft.com/en-us/powershell/scripting/overview'

Next, we will create a hash table that includes four key-value pairs. Each pair includes a website name and an associated URL, because as we learned in that post, a hash table is an associative array.

$Sites = @{
    GitHub  = 'https://github.com/PowerShell/PowerShell'
    Docs    = 'https://docs.microsoft.com/en-us/powershell/'
    Gallery = 'https://www.powershellgallery.com/'
    Reddit  = 'https://www.reddit.com/r/PowerShell/'
}

Following that, we will use the foreach statement to loop through the key-value pairs in the hash table, running Add-Member against each of them, and thusly adding each as a note property on our $PSSites variable.

foreach ($Site in $Sites.GetEnumerator()) {
    $PSSites = $PSSites | Add-Member -NotePropertyName $Site.Key -NotePropertyValue $Site.Value -PassThru
}

We can use Get-Member to ensure the previous command added the additional URLs as note properties.

$PSSites | Get-Member -MemberType NoteProperty

   TypeName: System.String

Name    MemberType   Definition
----    ----------   ----------
Docs    NoteProperty string Docs=https://docs.microsoft.com/en-us/powershell/
Gallery NoteProperty string Gallery=https://www.powershellgallery.com/
GitHub  NoteProperty string GitHub=https://github.com/PowerShell/PowerShell
Reddit  NoteProperty string Reddit=https://www.reddit.com/r/PowerShell/

At this point, we have our own variable like the $PROFILE variable that contains multiple string values. We can access the note properties of $PSSites using dot notation.

$PSSites.GitHub
$PSSites.docs
$PSSites.Gallery
$PSSites.Reddit

https://github.com/PowerShell/PowerShell
https://docs.microsoft.com/en-us/powershell/
https://www.powershellgallery.com/
https://www.reddit.com/r/PowerShell/

Now, let’s create another variable and add some members to it, as well. These will be a little different and you will see that momentarily. We will start by assigning a string value to the variable, as we did above.

$TestingVariable = 'A string value'
$TestingVariable
A string value

Before we add the rest, I suppose I should remind everyone about my end goal written at the bottom of the first post linked above: Save the previous value of a variable to a note property when assigning a new value to the variable itself.

This would be much easier if we could guarantee that every new value assigned to the variable was a string, and therefore, every old value property would also be a string. My worries, as mentioned earlier, were about arrays, hash tables, ordered dictionaries, and other data structures I may have to save to a note property. Can other data structures like those be put in a note property? Let’s work through these data structures and see if we can get them into note properties, to begin with. In this example, we will add two arrays to two separate note properties, as Array01 and Array02. One of these will and one will not use the array, sub-expression operator (@()).

$TestingVariable = $TestingVariable | Add-Member -NotePropertyName 'Array01' -NotePropertyValue 1,2,3 -PassThru
$TestingVariable = $TestingVariable | Add-Member -NotePropertyName 'Array02' -NotePropertyValue @(4,5,6) -PassThru

Here, we will create a hash table and store it in the $HashTable variable. We will then use that variable as a part of another Add-Member command.

$HashTable = @{Dad = 'David'; Mom = 'Betty'; Daughter = 'Janice'; Son = 'Bryan'}
$TestingVariable = $TestingVariable | Add-Member -NotePropertyName 'HashTable' -NotePropertyValue $HashTable -PassThru

Finally, we’ll create an ordered dictionary and add it as a note property, as well. If the hash table takes, then it is probably safe to assume this will take, too.

$OrderedDictionary = [ordered]@{
    Monday    = 'Chipotle'
    Tuesday   = 'Pizza'
    Wednesday = 'Mahi Mahi';
    Thursday  = 'Ravioli'
    Friday    = 'Orange Chicken'
}
$TestingVariable = $TestingVariable | Add-Member -NotePropertyName 'OrderedDictionary' -NotePropertyValue $OrderedDictionary -PassThru

At this point, we should probably see what Get-Member returns about the note properties we have assigned to our $TestingVariable. We have yet to test that anything has actually been added so far, although we have not received any errors stating otherwise. In the below example, we can see the four, note properties that we have added. The best part, the definition property indicates it knew the data structure of these when they were added as note properties. The arrays show object[], the hash table, hashtable, and the ordered dictionary, OrderedDictionary.

$TestingVariable | Get-Member -MemberType NoteProperty

   TypeName: System.String

Name               MemberType   Definition
----               ----------   ----------
Array01            NoteProperty Object[] Array01=System.Object[]
Array02            NoteProperty Object[] Array02=System.Object[]
HashTable          NoteProperty hashtable Hash Table=System.Collections.Hashtable
OrderedDictionary  NoteProperty OrderedDictionary Ordered Dictionary=System.Collections.Specialized.OrderedDictionary

Let’s take a closer look at each of our new note properties. For each of them, we will run GetType() against the note property and then return the value it has stored. When we return a note property that contains an array, it returns that. Same for the others; the data structure was recognized and retained when it was added as a member. And it is respected on the way out, too. I cannot tell you how grateful I am that I do not have to be the one to figure out the underlying data structure of a value going in and then coming back out. This is great news for some of the other things I have planned.

$TestingVariable.Array01.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object[]                                 System.Array

$TestingVariable.Array01
1
2
3

$TestingVariable.Array02.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object[]                                 System.Array

$TestingVariable.Array02
4
5
6

$TestingVariable.HashTable.GetType()       

IsPublic IsSerial Name                                     BaseType     
-------- -------- ----                                     --------     
True     True     Hashtable                                System.Object

$TestingVariable.HashTable

Name                           Value
----                           -----
Daughter                       Janice
Son                            Bryan
Dad                            David 
Mom                            Betty

$TestingVariable.OrderedDictionary.GetType()

IsPublic IsSerial Name                                     BaseType      
-------- -------- ----                                     --------      
True     True     OrderedDictionary                        System.Object 

$TestingVariable.OrderedDictionary

Name                           Value
----                           -----
Monday                         Chipotle
Tuesday                        Pizza
Wednesday                      Mahi Mahi
Thursday                       Ravioli
Friday                         Orange Chicken

A Better Way to Solve the Same Problem

There once was a time I wrote a post. Realistically, there have been many times. Like many posts though, I was proud of it. I can’t show it to you, as it wasn’t written on my site and it has since been lost. I am actually kind of embarrassed by it these days, so I’m mostly glad it is gone.

Someone, on some forum, wanted to know the previous value of a variable. Not the current value, but the last value it had been assigned before its current value. My idea then, which seems ridiculous now, was to store its last value in its description property. If you don’t know, variables have a writable description property; really, they do. It is a pretty nice feature, were anyone to actually use it. I don’t imagine many people do. I know about it, and honestly, I don’t use it.

Instead of using a variable’s description property to store its last value, I got to thinking about the $PROFILE variable and how it works. Let’s have a look, but before we do, let’s make sure we all know what this variable holds. This variable contains a path to your profile script, whether your profile script exists or not.

[PS7.2.0][C:\] $PROFILE
C:\Users\tommymaynard\Documents\PowerShell\Microsoft.PowerShell_profile.ps1

The profile script is invoked when PowerShell launches, allowing you to create variables, define functions, and modify your PowerShell environment to suit you. In a recent post, I discussed creating a work-based ASCII image and sending in some keys. This made sure the first command I want to invoke was ready when PowerShell launched; all I had to do was press Enter. Oh, and here is every time I mentioned the $PROFILE variable on tommymaynard.com going all the way back to 2014. It has come up quite a few times over the years.

We have seen the value the $PROFILE variable holds, but there’s more. Let’s run it another way.

[PS7.2.0][C:\] $PROFILE | Select-Object -Property *

AllUsersAllHosts : C:\Program Files\PowerShell\7\profile.ps1
AllUsersCurrentHost : C:\Program Files\PowerShell\7\Microsoft.PowerShell_profile.ps1
CurrentUserAllHosts : C:\Users\tommymaynard\Documents\PowerShell\profile.ps1
CurrentUserCurrentHost : C:\Users\tommymaynard\Documents\PowerShell\Microsoft.PowerShell_profile.ps1
Length : 71


Using the Select-Object cmdlet produced three additional paths: AllUsersAllHosts, AllUsersCurrentHost, and CurrentUserAllHosts. I am not going to go into what these all mean, but host refers to the program hosting the PowerShell engine. Read more about profiles here. With these three additional paths, there are four total locations where a profile script can be located and loaded by default. So, what do we have here? We have a variable with the ability to hold more than a single value (without making it an array or a hash table). That’s just how this variable works and I want the same thing for my example variable.

In this example, we’ve returned the same values as we did above, however, in this instance, we’re using the Get-Member cmdlet. I think it is important to see the values returned a couple of different ways.

[PS7.2.0][C:\] $PROFILE | Get-Member -MemberType NoteProperty

   TypeName: System.String

Name                   MemberType   Definition
----                   ----------   ----------
AllUsersAllHosts       NoteProperty string AllUsersAllHosts=C:\Program Files\PowerShell\7\profile.ps1
AllUsersCurrentHost    NoteProperty string AllUsersCurrentHost=C:\Program Files\PowerShell\7\Microsoft.PowerShell_profile.ps1
CurrentUserAllHosts    NoteProperty string CurrentUserAllHosts=C:\Users\tommymaynard\Documents\PowerShell\profile.ps1
CurrentUserCurrentHost NoteProperty string CurrentUserCurrentHost=C:\Users\tommymaynard\Documents\PowerShell\Microsoft.PowerShell_profile.ps1

Let’s start the remainder of this post by creating my example variable. Like the $PROFILE variable, we’re just going to work with string values.

[PS7.2.0][C:\] $PROFILE.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     String                                   System.Object

[PS7.2.0][C:\] 
[PS7.2.0][C:\] $PSSites = 'https://docs.microsoft.com/en-us/powershell/scripting/overview'
[PS7.2.0][C:\] $PSSites
https://docs.microsoft.com/en-us/powershell/scripting/overview
[PS7.2.0][C:\] 
[PS7.2.0][C:\] $PSSites.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     String                                   System.Object

[PS7.2.0][C:\]

At this point, we’ve assigned a docs.microsoft.com URL directly to the $PSSites variable. Now, let’s take a few new values and assign each of them as a NoteProperty on the same variable. This is done by using the Add-Member cmdlet. We assign the current value of the $PSSites variable back to the $PSSites variable, while also adding the new note property.

[PS7.2.0][C:\] $PSSites = $PSSites | Add-Member -NotePropertyName 'GitHub' -NotePropertyValue 'https://github.com/PowerShell/PowerShell' -PassThru
[PS7.2.0][C:\] $PSSites = $PSSites | Add-Member -NotePropertyName 'Docs' -NotePropertyValue 'https://docs.microsoft.com/en-us/powershell/' -PassThru
[PS7.2.0][C:\] $PSSites = $PSSites | Add-Member -NotePropertyName 'Gallery' -NotePropertyValue 'https://www.powershellgallery.com/' -PassThru
[PS7.2.0][C:\] $PSSites = $PSSites | Add-Member -NotePropertyMembers @{Reddit='https://www.reddit.com/r/PowerShell/'} -PassThru

You may have noticed, but in the last of the above four examples, we used the NotePropertyMembers parameter with a hash table for the associated value. This option allows us to supply both the name and value at the same time. It does exactly what the first three examples did — it is just another way to do it.

I should mention the PassThru switch parameter. This is a requirement because Add-Member cannot add types to strings, so this will generate an object. It’s worth keeping this in mind. Without it, you’ll drive yourself crazy trying to determine why it isn’t working — always, read the help!

Using dot notation we can see the additional values (note properties), that our variable has been assigned.

[PS7.2.0][C:\] $PSSites.GitHub
https://github.com/PowerShell/PowerShell
[PS7.2.0][C:\] 
[PS7.2.0][C:\] $PSSites.Docs
https://docs.microsoft.com/en-us/powershell/
[PS7.2.0][C:\] 
[PS7.2.0][C:\] $PSSites.Gallery
https://www.powershellgallery.com/
[PS7.2.0][C:\] 
[PS7.2.0][C:\] $PSSites.Reddit
https://www.reddit.com/r/PowerShell/

Like we did with the $PROFILE variable, we can use the Select-Object cmdlet to return all of our URLs. We can also use Get-Member.

[PS7.2.0][C:\] $PSSites | Select-Object -Property *

GitHub   : https://github.com/PowerShell/PowerShell
Docs     : https://docs.microsoft.com/en-us/powershell/
Gallery  : https://www.powershellgallery.com/
Reddit   : https://www.reddit.com/r/PowerShell/
Length   : 62


[PS7.2.0][C:\] $PSSites | Get-Member -MemberType NoteProperty

   TypeName: System.String

Name     MemberType   Definition
----     ----------   ----------
Docs     NoteProperty string Docs=https://docs.microsoft.com/en-us/powershell/
Gallery  NoteProperty string Gallery=https://www.powershellgallery.com/
GitHub   NoteProperty string GitHub=https://github.com/PowerShell/PowerShell
Reddit   NoteProperty string Reddit=https://www.reddit.com/r/PowerShell/

Now, with the PowerShell site URLs stored in the $PSSites variable, I can easily open whichever website I prefer using Start-Process and the corresponding property. No real idea if I’ll use this, but I’m grateful to know how to use a note property to store additional data along with a simple string variable.

[PS7.2.0][C:\] Start-Process -Path $PSSites.Gallery

Join me next time, perhaps, if I can devise a way to save the previous value of a variable to a note property when assigning a new value to the variable itself. It will take a little thought and work, but for now, I think it is possible.

A Variable’s Current and Previous Value

How to Store a Variable’s Previous Value with the New Value

There was a recent post on the PowerShell Facebook group that said this: “Is there a function or option to check previous $Global:variable -ne Current $Global:variable?” The person that created the post wanted to know if there’s a way to recall the last value a variable stored, once it had already been assigned something new. Here’s my answer, and potential solution.

You have to think about a variable’s previous assignment, as if it never existed, even though you know that variable held something, or was assigned something, previously. Once its value is gone, it’s gone. Now, all that said, I came up with an option. The option, is to use a variable’s description property to store its previous value.

Let’s begin by checking the value of an uninitialized variable, and then assign it something.

PS > $x
PS > # No value.
PS > $x = 'first value'
PS > $x
first value

Now that our variable has a value, let’s take a look at all the variable’s properties using the Get-Variable cmdlet. Notice the Description property; we’re going to use this in a less than conventional way.

PS > Get-Variable -Name x | Select-Object -Property *

Name        : x
Description :
Value       : first value
Visibility  : Public
Module      :
ModuleName  :
Options     : None
Attributes  : {}

Using the Set-Variable cmdlet, we’ll make two modifications to our variable, at nearly the same time. First, we’ll update the Description property, so that it’s holding the original variable assignment (the string ‘first value’). In the same, Set-Variable command, we’ll modify the current value of the variable to the string ‘second value.’ Notice in the final below command, that our Description property has a value now, too.

PS > Set-Variable -Name x -Value 'second value' -Description $x
PS > $x
second value
PS > Get-Variable -Name x | Select-Object -Property *

Name        : x
Description : first value
Value       : second value
Visibility  : Public
Module      :
ModuleName  :
Options     : None
Attributes  : {}

In the next example, you’ll see how we can return the original value and the variable’s updated value. Beneath that, I’ve included a couple ways to compare these values. That gets back to what the person on Facebook was trying to determine. Again, this is fairly unconventional use of the Description property, but it does avoid the need for a second variable to hold the first variable’s original value. That said, one of my examples uses a second, comparison variable.

PS > (Get-Variable -Name x).Description
first value
PS > (Get-Variable -Name x).Value
second value
PS > $x
second value

PS > (Get-Variable -Name x).Description -ne (Get-Variable -Name x).Value
True
PS > (Get-Variable -Name x).Description -ne $x
True
PS > $y = (Get-Variable -Name x).Description
PS > $y -ne $x
True

In these last examples, we’re running into a bit of a problem, we are going to have to keep in mind (if anyone even dares use this approach). When we take a value that isn’t a string, and place it into the Description property, it becomes a string. That means, that when we take it back out, we’ll need to cast it back to its proper type. Take a look at the next series of examples for some assistance with this concept.

PS > $z = 5
PS > Set-Variable -Name z -Value 10 -Description $z
PS > Get-Variable -Name z | Select-Object -Property *

Name        : z
Description : 5
Value       : 10
Visibility  : Public
Module      :
ModuleName  :
Options     : None
Attributes  : {}

PS > (Get-Variable -Name z).Description | Get-Member |
>>> Select-Object -Property TypeName -Unique

TypeName
--------
System.String

PS > [int](Get-Variable -Name z).Description
5
PS > [int](Get-Variable -Name z).Description | Get-Member |
>>> Select-Object -Property TypeName -Unique

TypeName
--------
System.Int32

This goes for decimals values, too. If you use the Description property to store something that’s not a string, then you’re going to have to properly cast it when you’re taking it out, or you’re going to have a string. In these next examples, I used the GetType method to determine the value’s type, as opposed to Get-Member and Select-Object used above.

PS > $m = 2.5
PS > $m.GetType().Name
Double
PS > Set-Variable -Name m -Value 3.5 -Description $m
PS > $m
3.5
PS > $m.GetType().Name
Double
PS > (Get-Variable -Name m).Description
2.5
PS > (Get-Variable -Name m).Description.GetType().Name
String
PS > [double](Get-Variable -Name m).Description
2.5
PS > ([double](Get-Variable -Name m).Description).GetType().Name
Double

And that’s it. It’s may not be the first choice for saving a previous variable’s value, but it’s a choice. I rather liked the array option that was recommended; however, like the $Error array, I’d be tempted to put the newest/current value of a variable into index 0 and not at the end of the array. Anyway, back to real life now.

PSMonday #22: September 26, 2016

Topic: Get-Member Continued IV

Notice: This post is a part of the PowerShell Monday series — a group of quick and easy to read mini lessons that briefly cover beginning and intermediate PowerShell topics. As a PowerShell enthusiast, this seemed like a beneficial way to ensure those around me at work were consistently learning new things about Windows PowerShell. At some point, I decided I would share these posts here, as well. Here’s the PowerShell Monday Table of Contents.

Now that we’ve had a brief introduction to arrays, let’s quickly get back and discuss the difference of piping to Get-Member, and not piping to Get-Member. Let’s begin by creating a new array, but this time, we’ll make sure it’s holding both string and integer values.

$array = 1,'string1',2,'string2'
$array

1
String1
2
String2

Let’s pipe this to Get-Member. I’ve modified the below command, so it takes up less space; I’m only returning the TypeName. I’ve also included the -Unique parameter as a part of the Select-Object command. Without it, it would return the TypeName for each of the properties and methods. Try it without, and see what I mean.

$array | Get-Member | Select-Object TypeName -Unique

TypeName
--------
System.Int32
System.String

When we pipe to Get-Member, we get a result for each of the different types of objects in the array. Although our array has four total values, there’s still only two different types of objects: integer and string.

Let’s set up the next command, but this time we’ll use Get-Member without piping.

Get-Member -InputObject $array | Select-Object TypeName -Unique

TypeName
--------
System.Object[]

Notice that there’s nothing about integers or strings. In this next example, we’ll apply one of the methods. I happen to know there’s a method called GetType that will provide a bit more information about our $array variable. If you didn’t know about the GetType method, you could’ve removed the pipe and Select-Object command, to see all of the available properties and methods, and tested some out.

(Get-Member -InputObject $array).GetType()

IsPublic IsSerial Name                  BaseType
-------- -------- ----                  --------
True     True     Object[]              System.Array

Based on the above results, you can see that PowerShell knows that this is an array. So what’s all this mean? It means that when we don’t pipe to Get-Member, we evaluated the variable as a whole — whatever it might be. When we pipe to Get-Member, we’re evaluating the type of each element contained in the array, or rather, each object in a collection.

The pipeline in PowerShell is used for much more than Get-Member, but the concept is the same. Each object has a turn to go across, or through, the pipeline, in order to be evaluated. This is a key concept and I’m sure we’ll see it in the future, in topics unrelated to the Get-Member cmdlet.

Add, Concatenate, and a Little More

My desire to know Windows PowerShell from beginning to end isn’t just for me. It’s also for those around me — online, and in real life. I want to be a go-to-guy for all things PowerShell. I’ve found something I really love about the industry right now, and so I want know it well, and share it often. I truly believe that learning the fundamentals of PowerShell make one much more prepared to use the language interactively, as well as inside scripts and functions. I’ve said it before: It’s easy to spot someone that doesn’t know the fundamentals of PowerShell, and is only out to learn as much as is required to complete their current task.

A couple weeks ago a coworker asked me why their conditional wasn’t working (think, the part inside the parentheses in an If statement, for example). While I never saw the code, as the conversation happen over cube walls and by instant message, I suspected they were trying to compare a numeric value with something that wasn’t numeric. It turned out I was right. I determined this, the moment they said they had quotes around their numeric value.

It’s simple. The moment we put numeric values inside quotes — single or otherwise — we are no longer working numbers — we’re working with strings. Take a look at the example below. In the first command, we use addition to add the numeric values 5 and 10, which results in the (numeric) value of 15. In the second command, we concatenate (think, combine or join) — using the same operator (+). When we concatenate the string value of 5 with the string value of 10, we create the (string) value of 510.

PS> 5 + 10
15
PS> '5' + '10'
510

The information I sent to my colleague by IM is listed below. I thought it would best explain the difference between a “numeric value” inside quotes, and one not inside quotes. In this example, 123 is an integer (System.Int32) without quotes around it, and a string (System.String) with quotes around it. A quick note about Select-Object’s -Unique switch parameter: This parameter returns duplicated results once, and only once. Had this not been included, it would have returned the same result for each member of the object (each method, property, etc.).

PS> 123 | Get-Member | Select-Object TypeName -Unique

TypeName
--------
System.Int32

PS> '123' | Get-Member | Select-Object TypeName -Unique

TypeName
--------
System.String

In our first example in this post, we saw what happens when the add two numeric values and what happens when we concatenate two strings. If you think like me, you’re probably wondering what happens when we concatenate a string value to a numeric value, and add a numeric value to a string character. It depends. Take a look at the slightly, modified example below, and then we’ll discuss it.

PS> 5 + '10'
15
PS> '5' + 10
510

Did you figure it out what’s happened in those examples? In the first command, 5 + ’10’, the numeric on the left of the equation, has forced the string on the right side, to switch from a string to a numeric data type. With two numeric values, it then added the two values and returned 15.

The second command, or equation, works much like the first one, except in reverse. The string character 5 on the left, forces the numeric value on the right into being a string and then concatenates the two, which results in 510 again. Whatever is on the left is going to try and change the type of whatever is on the right — when it can. So, when can’t it?

PS> 5 + '1a2b3c'
Cannot convert value "1a2b3c" to type "System.Int32". Error: "Input string was not in a correct format."
...
PS> '1a2b3c' + 10
1a2b3c10

The first command in the example above fails because it cannot convert the string, ‘1a2b3c,’ into a numeric value because it includes non-numeric characters — makes sense to me. Notice that this error helps prove what I said earlier: The data type of value on the left is being used to try and change the data type of the value on the right. The second equation works, because again, we’re simply joining, or concatenating, two values.

PS> 'Have a great ' + 2016 + '!'
Have a great 2016!

In closing, I’d like to add a word about the concatenation operation just above. While the plus sign, or rather, the concatenation operator does what it does well, it’s not used nearly as much as it was in the past for building strings — at least, I don’t see it much anymore. In fact, when I do see it, I almost always assume that what I’m looking at was written long ago and then borrowed. What you’re more likely to see these days, is in the example below.

PS> $Year = 2016 # It's a numeric value (no quotes).
PS> # Use double-quotes (below) to display the variable's value inside the string.
PS> "Have a great $Year!" # No concatenation operators needed!
Have a great 2016!

Thanks for the reading my first post of 2016!

Only Return System.DateTime Properties from Get-ADComputer

I had one of those randomly appearing PowerShell questions last night. Windows PowerShell is a huge interest for me, so there’s really no surprise.

I wondered, How can only return the date time related properties from Get-ADComputer? It seems to happen quite often that I’ll need to view date and time information from Get-ADComputer (and Get-ADUser). It’s mildly cumbersome to scan though all the properties looking for dates and times in the property’s value — the ultimate reason behind this random thought.

The command I threw together was ugly, and seemed to lack a simpler approach. I stopped there, and decided to pass this one out to the community. Can you come with a better way?

Again, the idea is to return only the properties from Get-ADComputer that are System.DateTime properties (have System.DateTime in the Definition property of Get-Member). Take a look at my example and you might better understand my goal. I didn’t bother filtering out the default properties returned by Get-ADComputer (at first), but you’re welcome to do that, too. Cheers!

PS> Get-ADComputer -Identity SERVER01 -Properties ((Get-ADComputer -Identity SERVER01 -Properties * | Get-Member | Where-Object Definition -match 'System.DateTime').Name)

AccountExpirationDate  :
AccountLockoutTime     :
Created                : 9/12/2013 1:34:06 PM
createTimeStamp        : 9/12/2013 1:34:06 PM
DistinguishedName      : CN=SERVER01,OU=Finance,DC=mydomain,DC=com
DNSHostName            : SERVER01.mydomain.com
Enabled                : True
LastBadPasswordAttempt :
LastLogonDate          : 3/17/2014 10:35:35 AM
Modified               : 8/18/2014 11:48:34 AM
modifyTimeStamp        : 8/18/2014 11:48:34 AM
Name                   : SERVER01
ObjectClass            : computer
ObjectGUID             : 234cbaed59-1ab3-6ebc-9782-e9542bedaec
PasswordLastSet        : 3/14/2014 5:12:24 PM
SamAccountName         : SERVER01$
SID                    : S-1-5-21-174985627-956854884-123956358-942569
UserPrincipalName      :
whenChanged            : 8/18/2014 11:48:34 AM
whenCreated            : 9/12/2013 1:34:06 PM

Update1: I went ahead and edited the command so that it would not return the default Get-ADComputer properties (Name, SamAccountName, etc.), using the Select-Object cmdlet. I repeated the command I issued to the -Properties parameter of Get-ADComputer, as the value for Select-Object.

So, any takers? Can you come up with a better way to do this?

PS> Get-ADComputer -Identity SERVER01 -Properties ((Get-ADComputer -Identity SERVER01 -Properties * | Get-Member | Where-Object Definition -match 'System.DateTime').Name) | Select-Object (Get-ADComputer -Identity SERVER01 -Properties * | Get-Member | Where-Object Definition -match 'System.DateTime').Name

AccountExpirationDate  :
AccountLockoutTime     :
Created                : 9/12/2013 1:34:06 PM
createTimeStamp        : 9/12/2013 1:34:06 PM
LastBadPasswordAttempt :
LastLogonDate          : 3/17/2014 10:35:35 AM
Modified               : 8/18/2014 11:48:34 AM
modifyTimeStamp        : 8/18/2014 11:48:34 AM
PasswordLastSet        : 3/14/2014 5:12:24 PM
whenChanged            : 8/18/2014 11:48:34 AM
whenCreated            : 9/12/2013 1:34:06 PM

Update2: There’s a second update to this post. Jonathan Angliss tweeted a much cleaner solution — exactly what I had hoped someone might do. Here’s his contribution:

PS> Get-ADComputer -Identity SERVER01 -Properties * | ForEach-Object {$_.psobject.properties | Where-Object {$_.TypeNameofValue -eq 'System.DateTime'}} | Select-Object Name,Value

Parsing nslookup: Only Return the Resolved IP

It was late last Friday evening when a vendor and I worked though some maintenance. As a part of the work (which actually lasted well into the weekend), we needed to remove two DNS A records and replace them with CNAME records. I figured that before the change, I would return the current resolution results, so I would be able to ensure the DNS change took place, after the night’s weekend’s work.

You’re probably familiar with the standard output of nslookup.

PS> nslookup subdomain.mydomain.com
Server:  mydomain.com
Address:  10.10.10.1

Name:    subdomain.mydomain.com
Address:  10.20.30.40

These results indicate the name, subdomain.mydomain.com, resolves to an IP address of 10.20.30.40 when using the mydomain.com nameserver, at 10.10.10.1. I wanted to clean up this output, and since I had time to kill while fighting off sleep, I figured — why not? I started by piping the results of nslookup to the Select-String cmdlet looking for the string, ‘Address.’ As you can see in the example below, it returns both the IP of the server where the resolution took place, and the results. After that, there doesn’t seem to be a way to distinguish one line from the other. I needed another way to only return the results.

PS> nslookup subdomain.mydomain.com | Select-String Address

Address: 10.10.10.1
Address: 10.20.30.40

I decided to pipe the command above to the Get-Member cmdlet to see what kind of object the Select-String cmdlet returned. The property that jumped out at me right away was LineNumber — was it really going to be that easy?

   TypeName: Microsoft.PowerShell.Commands.MatchInfo

Name         MemberType Definition
----         ---------- ----------
Equals       Method     bool Equals(System.Object obj)
GetHashCode  Method     int GetHashCode()
GetType      Method     type GetType()
RelativePath Method     string RelativePath(string directory)
ToString     Method     string ToString(), string ToString(string directory)
Context      Property   Microsoft.PowerShell.Commands.MatchInfoContext Context {get;set;}
Filename     Property   string Filename {get;}
IgnoreCase   Property   bool IgnoreCase {get;set;}
Line         Property   string Line {get;set;}
LineNumber   Property   int LineNumber {get;set;}
Matches      Property   System.Text.RegularExpressions.Match[] Matches {get;set;}
Path         Property   string Path {get;set;}
Pattern      Property   string Pattern {get;set;}

Here’s the thing, the output of nslookup is always the same: this on a this line, and that on that line. This is likely why the LineNumber property looked so promising, so quickly. Now that we know we have this property, let’s try and use it. While my first line number guess worked (read, dumb luck), let’s do it wrong first. Consider again our most recent output below.

PS> nslookup subdomain.mydomain.com | Select-String Address

Address: 10.10.10.1
Address: 10.20.30.40

You might think you would want line 3 of our output, but that’s not correct. Even though Select-String has trimmed down the output, the line numbers are all still intact. Look at the very first example at the top of this post. Line 3 is blank — it’s line 5 that we want! Take a look.

PS> nslookup subdomain.mydomain.com | Select-String Address | Where-Object LineNumber -eq 5

Address: 10.20.30.40

If you want to store this as as a string, use the .ToString() method.

PS> (nslookup subdomain.mydomain.com | Select-String Address | Where-Object LineNumber -eq 5).ToString()
Address: 10.20.30.40

And, if you want to remove the word “address,” the colon, and the space, in order to only return the IP address, then you can parse the string by using the .Split() method. The example below splits the string at the space and return the last element — what’s on the right side of the space.

PS> (nslookup subdomain.mydomain.com | Select-String Address | Where-Object LineNumber -eq 5).ToString().Split(' ')[-1]
10.20.30.40

I hope this was helpful for someone. Until next time.

Write-Host, Does it Have a Place?

I’ve been working on an advanced function that I can’t wait to share (and no, it’s not the one in this post). I really think it’s something that the Windows PowerShell community has been missing. Okay fine, maybe it’s just something I’ve been missing.

I noticed in development that my Write-Output messages to the user were crossing the pipeline, when the custom object (created by the function), was passed to Select-Object (in certain manners). I have a function below that does the same thing as the one in development.

Here’s how this thing works: The function requires the user to provide the value Write-Output (or, wo), or Write-Host (or, wh) for the -Option parameter. This will determine how the message to the user is written inside the Begin block. The only other thing that’s happening in this function, is that a custom object is being created in the Process block, based on some properties of Win32_BIOS.

Function Test-TMWriteOutputVsHost {
    [CmdletBinding()]
    Param (
    [Parameter(Mandatory=$true,
        HelpMessage="Enter Write-Output or Write-Host")]
    [ValidateSet('Write-Output','wo','Write-Host','wh')]
    [string]$Option
    )

    Begin {
        If ($Option -eq 'Write-Output' -or $Option -eq 'wo') {
            Write-Output 'Inside the Begin block (using Write-Output).'

        } ElseIf ($Option -eq 'Write-Host' -or $Option -eq 'wh') {
            Write-Host 'Inside the Begin block (using Write-Host).'
        }
    } # End Begin

    Process {
        $CollectionVariable = Get-WmiObject -Class Win32_BIOS
        $Object = @()
        $Object += [pscustomobject]@{
            Manufacturer = $CollectionVariable.Manufacturer;
            Name = $CollectionVariable.Name;
            Version = $CollectionVariable.Version
        }
    } # End Process

    End {
        Write-Output $Object
    }
    # End, End
} # End Function

As we can see below, everything works great with both Write-Output or Write-Host, when we don’t pipe the function to the Select-Object cmdlet.

PS C:\> Test-TMWriteOutputVsHost -Option Write-Output
Inside the Begin block (using Write-Output).

Manufacturer                            Name                                    Version
------------                            ----                                    -------
Dell Inc.                               BIOS Date: 08/27/13 11:12:44 Ver: A1... DELL   - 1072009

PS C:\> Test-TMWriteOutputVsHost -Option Write-Host
Inside the Begin block (using Write-Host).

Manufacturer                            Name                                    Version
------------                            ----                                    -------
Dell Inc.                               BIOS Date: 08/27/13 11:12:44 Ver: A1... DELL   - 1072009

Now, let’s pipe our object to some variations of the Select-Object cmdlet and watch some things blow up (when using Write-Output).

PS C:\> # Use the horizontal scrollbar to see the results...
PS C:\> Test-TMWriteOutputVsHost -Option Write-Output | select *

                                                                                                                 Length
                                                                                                                 ------
                                                                                                                     44

PS C:\> Test-TMWriteOutputVsHost -Option Write-Output | select Name,Ver*

Name                                                        Ver*
----                                                        ----

BIOS Date: 08/27/13 11:12:44 Ver: A13.00

PS C:\> Test-TMWriteOutputVsHost -Option Write-Host | select *
Inside the Begin block (using Write-Host).

Manufacturer                            Name                                    Version
------------                            ----                                    -------
Dell Inc.                               BIOS Date: 08/27/13 11:12:44 Ver: A1... DELL   - 1072009

PS C:\> Test-TMWriteOutputVsHost -Option Write-Host | select Name,Ver*
Inside the Begin block (using Write-Host).

Name                                                        Version
----                                                        -------
BIOS Date: 08/27/13 11:12:44 Ver: A13.00                    DELL   - 1072009

The problem with Write-Output, is that the object it’s producing is crossing our pipeline and causing unpredictable behavior—something we can’t include in a function we want to distribute. Here’s the proof: When we pipe our function to Get-Member, we reveal the objects that show up on the other side, and we don’t want the string object coming over with us.

PS C:\> Test-TMWriteOutputVsHost -Option Write-Output | Get-Member | Select-Object TypeName -Unique

TypeName
--------
System.String
System.Management.Automation.PSCustomObject

PS C:\> Test-TMWriteOutputVsHost -Option Write-Host | Get-Member | Select-Object TypeName -Unique
Inside the Begin block (using Write-Host).

TypeName
--------
System.Management.Automation.PSCustomObject

I don’t profess to know it all, so if there’s a way to get around this using Write-Output, then I’d love to hear about it. While I haven’t tried it, I suspect I may be able to create an embedded function—that might be the trick I need. Perhaps I’ll play with that option at another time. Thanks for reading!

Oh, and before someone mentions it, I explained how I feel about Write-Verbose in the comments on a post by Adam Bertram: http://www.adamtheautomator.com/use-write-host-lot.

Clear-Host, Without Clearing the Host

After you read this, read part 2 (and download the TMModule)

I use the Clear-Host cmdlet alias, cls, throughout the day to clear out whatever typing I have inside my Windows PowerShell console. It does its job well, but recently I’ve wanted it to work differently. I wanted it to appear that the console host has been cleared, but still allow me to scroll back up to see what was on the screen before it was cleared. I started playing around with the console class, [Console]. While not necessary, this can also be written using the namespace, System, such as [System.Console]. I like the idea of being as complete as possible and so you’ll see me use the namespace even though it’s not necessary.

Before I could write something reusable, such as a function, I had to figure out if what I wanted to accomplish, was even possible. I knew I was working with [System.Console] and so I piped that to Get-Member, but it returned the methods and properties of System.RuntimeType, seen below.

PS C:\> [System.Console] | Get-Member

    TypeName: System.RuntimeType

I struggled for a moment until I remembered an article I had read on using static classes. I found that page again, http://technet.microsoft.com/en-us/library/dd347632.aspx, and was quickly reminded that using the -Static parameter of the Get-Member cmdlet would get me the correct results.

PS C:\> [System.Console] | Get-Member -Static

    TypeName: System.Console

Running the command above produces the TypeName as shown, but it also produces all the methods and properties. I started looking over the properties and a couple about the cursor quickly caught my eye, especially the CursorTop property. After the Get-Member command from above, and based on the results of returning the CursorTop property, my cursor was positioned at line 59 inside my console, as can been seen in the example below on line 2. I cleared the screen, and beginning on line 4 below, I reran the command three more times. Each time, it gave me the location where the cursor was last positioned.

PS C:\> [System.Console]::CursorTop
59
PS C:\> cls
PS C:\> [System.Console]::CursorTop
1
PS C:\> [System.Console]::CursorTop
3
PS C:\> [System.Console]::CursorTop
5
PS C:\>

I decided I would assign the value, 0, to the CursorTop property and suddenly I was writing over the text on the top line. Take a close look at line 1 below.

PS C:\> blahblahConsole]::CursorTop
59
PS C:\> cls
PS C:\> [System.Console]::CursorTop
1
PS C:\> [System.Console]::CursorTop
3
PS C:\> [System.Console]::CursorTop
5
PS C:\> [System.Console]::CursorTop = 0

I could move my cursor, great, but this wasn’t exactly what I wanted. What I wanted was to push that scroll bar down so that anything that was already on the screen was pushed off the top of my console, and all that was left was my PowerShell prompt. I still believed there was a way to do this and so I spent a little more time looking over the properties. I found four that began with Window – WindowHeight, WindowLeft, WindowTop, and WindowWidth – and began to experiment with them. I didn’t suspect I’d be doing anything with the height and width but I thought I would check out their values anyway – 50 and 120, respectively.

PS C:\> [System.Console]::WindowHeight
50
PS C:\> [System.Console]::WindowWidth
120
PS C:\>

WindowLeft didn’t seem to be that important, because no matter how much was typed before I entered [System.Console]::WindowLeft, the property value was still set to 0. Then I entered in [System.Console]::WindowTop and it was also 0 every time. Then it dawned on me, what if I changed its value like I did with CursorTop. I tried it and my scroll bar started jumping all over. I’m getting close!

PS C:\> [System.Console]::WindowTop = 10
PS C:\> [System.Console]::WindowTop = 200
PS C:\> [System.Console]::WindowTop = 2000
PS C:\> [System.Console]::WindowTop = 0

We know the CursorTop value changes, so what would happen if we set the value of WindowTop to CursorTop? I tried it, and it worked!

PS C:\> [System.Console]::WindowTop = [System.Console]::CursorTop
PS C:\>

I thought I was done when I took another moment and scanned over the methods. I found one called SetWindowPosition. Instead of simply assigning a new value to the property WindowTop, I decided I would use the method to do the work for me. I eventually ran both of these options through the Measure-Command cmdlet and determined that there was no gain in speed by using one option over the other.

So, once I knew what to do, I opened my profile ($PROFILE) and created an empty function. For whatever reason, I called it clx thinking that this would be a good option for me. Turns out that while clx has little meaning, I was able to quickly remember it and start using it right away. Now, every time I want it to appear that I’ve cleared my host, but didn’t really, I type clx and press Enter.

Function clx {
    [System.Console]::SetWindowPosition(0,[System.Console]::CursorTop)
}

I added one additional feature to this function as is seen in the example below. This option allowed me to run the clx function and leave the last n number of rows on the screen. Try it out by ensuring your have some output in your console and then entering clx 2. This will “clear” the console screen but still allow you to view the last two rows without scrolling back up. Try it and it may make more sense.

Function clx($SaveRows) {
    If ($SaveRows) {
        [System.Console]::SetWindowPosition(0,[System.Console]::CursorTop-($SaveRows+1))
    } Else {
        [System.Console]::SetWindowPosition(0,[System.Console]::CursorTop)
   }
}

Here’s a video of the function in action. The first thing we do is return 5 processes and then 5 services. Then we use cls and notice that we cannot scroll back up to see what was cleared. This is the typical behavior. When we add the processes and services back, and then use the clx function, we can see that we have the option to scroll back up and see what was on the screen, before we “cleared” it.