Copy the Last Command to the Clipboard

As a PowerShell blogger, I’ll often write a command in the PowerShell ConsoleHost and then, after it’s been executed, want to copy it out and paste it into a post. What this typically means is that I’ll press the up arrow to recall the last command — the one I want — and then selectively highlight the code I want and right-click to copy. Inevitably, I’ll end up copying my prompt and other unwanted lines, or parts of lines, and have to clean that up once it’s been pasted elsewhere.

Well, with a quick minute, those days are over. Type the function name Copy-LastCommand (once the function is in your PowerShell session), press Enter, and boom, the last command you entered is on the clipboard and ready to go elsewhere. Simple. Now all I do, is paste it where I want it and save myself some seconds. They add up.

Function Copy-LastCommand {
    (Get-History (Get-History | Select-Object -Last 1).Id).CommandLine | clip
}

Update: I made some changes to this little function and need to share those. Something I didn’t consider right away, was what happens if there isn’t a last command to copy? This is to say, what if the Copy-LastCommand is the first command entered into someone’s PowerShell session? Well, it won’t have anything to copy. I’ve added a fix for those times — a simple Write-Warning command. I also edited this to work using the newer, Set-Clipboard, cmdlet that was introduced in PowerShell 5.1 (I believe).

Function Copy-LastCommand {
    If (Get-History) {
        If ($PSVersionTable.PSVersion.ToString() -match '5.1') {
            Set-Clipboard -Value (Get-History (Get-History | Select-Object -Last 1).Id -ErrorAction SilentlyContinue).CommandLine
        } Else {
            (Get-History (Get-History | Select-Object -Last 1).Id).CommandLine | clip
        }
    } Else {
        Write-Warning -Message 'No command history to copy.'
    }
}

Bulk Remove Variables, Carefully

Ever have one of those situations where you need to remove, or perhaps reinitialize, a set of variables and have to deal with remembering them all? This doesn’t happen often, but there are times when I need to clear, or remove, variables between loop iterations. You either need to keep track of all your variable names, or — and I wouldn’t recommend this so much — do a Remove-Variable -Name *. While the help on PowerShell 5.1.14394.1000 (5.1 preview version), says that Remove-Variable doesn’t accept wildcards, it does, and I think it always has.

Let’s start with the below example. These few lines do the following: 1. Return the number of variables in the PowerShell session, 2. Add a new variable x, 3. Return the number of variables again, 4. Remove all the variables I can (some can’t be removed), 5. Return the number of variables again (again), and 6. See if I can return my variable x.

[tommymaynard@srv01 c/~]$ (Get-Variable).Count
51
[tommymaynard@srv01 c/~]$ New-Variable -Name x -Value 'string'
[tommymaynard@srv01 c/~]$ (Get-Variable).Count
52
[tommymaynard@srv01 c/~]$ Remove-Variable -Name * -ErrorAction SilentlyContinue
[tommymaynard@srv01 c/~]$ (Get-Variable).Count
29
[tommymaynard@srv01 c/~]$ Get-Variable -Name x
Get-Variable : Cannot find a variable with the name 'x'...

While we can do this to remove the variables we’ve created in the session, we end up removing variables that weren’t necessary to remove. We’re better than this.

In the next example, we’ll add a variable prefix “dog” to all of our variables. This will allow us to find all of the variables by prefix, and then delete just those. Not a bad idea really, but it wasn’t my first thought.

[tommymaynard@srv01 c/~]$ New-Variable -Name dog1 -Value 'string1'
[tommymaynard@srv01 c/~]$ New-Variable -Name dog2 -Value 'string2'
[tommymaynard@srv01 c/~]$ New-Variable -Name dog3 -Value 'string3'
[tommymaynard@srv01 c/~]$ Get-Variable -Name dog*

Name                           Value
----                           -----
dog1                           string1
dog2                           string2
dog3                           string3

[tommymaynard@srv01 c/~]$ Remove-Variable -Name dog*
[tommymaynard@srv01 c/~]$ Get-Variable -Name dog*
[tommymaynard@srv01 c/~]$ # No results, as they've been deleted.

Had I thought of the above option first, I might’ve just gone with that idea, but let me share what I was really thinking. It’s involves the description property. When we create our variables with New-Variable, we have the option to set more than just the Name and Value. Let’s take a look at the below example, and then discuss it.

[tommymaynard@srv01 c/~]$ New-Variable -Name a -Value 'stringA' -Description (Get-Date)
[tommymaynard@srv01 c/~]$ $StartDate = Get-Date
[tommymaynard@srv01 c/~]$ New-Variable -Name b -Value 'stringB' -Description (Get-Date)
[tommymaynard@srv01 c/~]$ New-Variable -Name c -Value 'stringC' -Description (Get-Date)
[tommymaynard@srv01 c/~]$ Get-Variable -Name a,b,c

Name                           Value
----                           -----
a                              stringA
b                              stringB
c                              stringC

[tommymaynard@srv01 c/~]$ Get-Variable -Name a,b,c | Select-Object Name,Value,Description

Name Value   Description
---- -----   -----------
a    stringA 11/03/2016 20:52:41
b    stringB 11/03/2016 20:53:05
c    stringc 11/03/2016 20:53:20

Now, it’s important to remember that a variable’s description property is a string. This means that when we put the current date in the property, it was converted to a string. That means that Thursday, November 3, 2016 20:53:20 PM became this: 11/03/2016 20:53:20. What we’ll need to do is convert it back to a datetime before we compare. We do that in the below example by casting the description property as a datetime object. If it’s not clear yet, our end goal is to remove variables that were created after a specific point in time.

[tommymaynard@srv01 c/~]$ Get-Variable -Name a,b,c | Where-Object {[datetime]$_.Description -gt $StartDate}

Name                           Value
----                           -----
b                              stringB
c                              stringC

We can also use the Get-Date cmdlet to do this, as well.

[tommymaynard@srv01 c/~]$ Get-Variable -Name a,b,c | Where-Object {(Get-Date -Format $_.Description) -gt $StartDate}

Name                           Value
----                           -----
b                              stringB
c                              stringC

Since we can now isolate the variables we created after a specific time, we can go ahead and blow them away. Here goes:

[tommymaynard@srv01 c/~]$ Get-Variable -Name a,b,c | Where-Object {(Get-Date -Format $_.Description) -gt $StartDate} | Remove-Variable
[tommymaynard@srv01 c/~]$ Get-Variable a,b,c

Name                           Value
----                           -----
a                              stringA
Get-Variable : Cannot find a variable with the name 'b'.
At line:1 char:1
+ Get-Variable a,b,c
+ ~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (b:String) [Get-Variable], ItemNotFoundException
    + FullyQualifiedErrorId : VariableNotFound,Microsoft.PowerShell.Commands.GetVariableCommand

Get-Variable : Cannot find a variable with the name 'c'.
At line:1 char:1
+ Get-Variable a,b,c
+ ~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (c:String) [Get-Variable], ItemNotFoundException
    + FullyQualifiedErrorId : VariableNotFound,Microsoft.PowerShell.Commands.GetVariableCommand

Well, that’s it for today. Now you can use description property of your variables to hold the time in which they were created. This will allow you to selective remove any of them based on a start time. I like it, although the whole prefix thing might be an easier option.

PSMonday #27: October 31, 2016

Topic: Introduction to the Language Constructs

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.

I’ve long had this feeling that people that learn and use PowerShell don’t really ever get  a fair opportunity, to learn the language constructs. Instead, they just learn them as necessary. In an effort to correct this, I believe we should spend some time in the coming weeks, covering each of them.

Well then, what’s a language construct, right? Well, it’s a control structure, of course. Okay, well then what’s that?

Within a language — PowerShell in our instance — we have to have a way to handle conditional situations, in order to take appropriate action(s). Language constructs are the If statement and its variations, it’s the switch statement, it’s the foreach loop and ForEach-Object, the for, the Do variations, and the While loop, too.

In the coming weeks, we’ll spend some time with each of the constructs, so that you’ve seen and used them all, or simply reviewed them, if you’ve used them before. Knowing which to use when, is a vital skill, and best left to second nature once you’ve had experience with them all. I’ll be sure to include some of the times you might choose one over the other, when you can do the same thing with more than just one of the constructs. We’ll start next Monday with If, If-Else, and If-ElseIf.

Resolve TinyURL Using PowerShell

I received a sketchy email today. As I briefly scanned it, I noticed that the email included a TinyURL, and I thought: It would be nice if Outlook could resolve that URL, so I had a better idea where it resolved (without the need to use the URL). That took me out to find such a service. Not for Outlook, so much, but you know, a service that could complete this URL resolution. After I had tested that GetLinkInfo.com gave me what I wanted, I chose to use it’s URL inside a PowerShell function, and so here we are.

I’m not spending much time on this post, but the following function will open the website in your browser, so you can see the URL resolution information. If you include the OpenSite parameter, the function will even open a browser window to the resolved site itself. You might use that one with caution. At current, this function will require you leave PowerShell to see the function’s results, since it was designed to open a web browser window, or windows, depending on how you use it. Perhaps in time I’ll come back around and change that one day.

Function Get-TinyUrlLinkInfo {
<#
.SYNOPSIS
    This function relies on GetLinkInfo.com to determine the redirection of a TinyUrl URL without the need to visit the URL.

.DESCRIPTION
    This function relies on GetLinkInfo.com to determine the redirection of a TinyUrl URL without the need to visit the URL. If GetLinkInfo.com changes how they form their URLs, then this function may no work without some code edits.

.PARAMETER Url
    This parameter name is mandatory and requires a valid URL, such as http://tinyurl.com/ol499jx.com, be provided as the parameter value.

.EXAMPLE
    PS > Get-TinyUrlLinkInfo -Url 'http://tinyurl.com/ol499jx'.
    This example will open a web browser to GetLinkInfo.com and indicate that the TinyUrl resolves to the PowerShell Gallery.

.EXAMPLE
    PS > Get-TinyUrlLinkInfo -Url 'http://tinyurl.com/ol499jx' -OpenSite
    This example will open a web browser to GetLinkInfo.com and indicate that the TinyUrl resolves to the PowerShell Gallery. It will also open the resolved link in a browser. It's not recommend to include this parameter until after the redirected site is considered safe and trusted. Use this parameter with caution.

.NOTES
    Name: Get-TinyUrlLinkInfo
    Author: Tommy Maynard
    Comments: -
    LASTEDIT: 10/31/2016
#>
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory=$true)]
        [string]$Url,

        [switch]$OpenSite
    )

    Begin {
    } # End Begin.

    Process {
        # Check link against GetLinkInfo.com.
        [void][Reflection.Assembly]::LoadWithPartialName("System.Web")
        $ConvertedUrl = [System.Web.HttpUtility]::UrlEncode($Url)
        Start-Process -FilePath "http://www.getlinkinfo.com/info?link=$ConvertedUrl&x=81&y=8"

        # Open resolved URL.
        If ($OpenSite) {
            Start-Process -FilePath $Url
        }
    } # End Process.

    End {
    } # End End.
} # End Function: Get-TinyUrlLinkInfo.

Use the Date and Time in File Names

I have functions here and there, that will at times, create files to store generated information. Part of this process is naming the file. For me, I’ll often add the date and time to my file’s name, in order to know it’s creation time, at a glance.

So what does this mean? It means that I’ll often have to jump over to my ConsoleHost, or an old function, I suppose, to be certain I’m using the same date time format. Well, that may end today, right after this post is published. Now, I’ll have another place to look — my own website — to ensure my consistency when it comes to including the same naming.

You can’t use the standard output of Get-Date in a file name, and you probably wouldn’t want to anyway. This is due to the colons included in the time; they’re invalid characters for a file name.

PS > Get-Date

Friday, October 28, 2016 9:21:28 PM

But when we put the Get-Date output in a file name, it changes the output. We loose the day of the week and month as words, and instead get a date with forward slashes, such as 10/28/2016. This conversion happens when we put the cmdlet inside a string. Take a look.

PS > New-Item -Path "$(Get-Date).txt" -ItemType File
New-Item : Cannot find drive. A drive with the name '10/28/2016 09' does not exist...
PS > "$(Get-Date).txt"
10/28/2016 09:27:04.txt

The error makes sense though, as it’s parsing 10/28/2016 as a path. Not really the point here, but good to know. Either way, those slashes and colons aren’t going in a file name. It’s not permitted.

I’ve had a consistent file naming convention that includes the date and time for awhile now. What I really like about my date format is that files are automatically sorted by year, then month, then day, and then the time.

PS > Get-Date -Format 'DyyyyMMddThhmmsstt'
D20161028T093059PM

PS > New-Item -Path "Get-Date -Format 'DyyyyMMddThhmmsstt'" -ItemType File

    Directory: C:\Users\tommymaynard

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----       10/28/2016   9:32 PM              0 Get-Date -Format 'DyyyyMMddThhmmsstt'

Oops, notice the problem in that second example above? I need to ensure the command inside my string is being treated as a command, and not just standard text. In the previous example, it used my actual command, as the file name. Let’s try that again.

PS > New-Item -Path "$(Get-Date -Format 'DyyyyMMddThhmmsstt').txt" -ItemType File

    Directory: C:\Users\tommymaynard

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----       10/28/2016   9:34 PM              0 D20161028T093445PM.txt

In this final example, we included some text around the date, so that I can better distinguish the reason for the file, but still have the date and time included in the name. We also used the subexpression operator —  $() — to make sure my command was treated as such. Oh, did you notice the capital D and T? I used these as separators to help better display the (D)ate and (T)ime. It makes the files name easier for me to visually parse.

PS > New-Item -Path "User_Disable_Log($(Get-Date -Format 'DyyyyMMddThhmmsstt')).txt" -ItemType File

    Directory: C:\Users\tommymaynard


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----       10/28/2016   9:37 PM              0 User_Disable_Log(D20161028T095316PM).txt

That’s all for now! Enjoy the weekend.

The Unzip Time Difference

As part of an upcoming deployment, I’ve been getting intimate with AWS OpsWorks and Chef. What I mean by Chef is, using the PowerShell resource in Chef. I was recently looking for a way to save some time on a deployment, when I considered removing the unzipping of files downloaded from S3. I wanted to know if there would be a time savings in getting the files to the EC2 Instance in their decompressed format.

This brought me over to my console to compare the newer Expand-Archive cmdlet and .NET. I’ve always considered that dropping down to .NET is a time savings.

In my Chef recipe I’m using .NET for decompression, as I’m deploying to Windows Server 2012 R2 and it includes PowerShell 4.0 by default. The Compress-Archive and Expand-Archive cmdlets were introduced in PowerShell 5.0. This isn’t to say I couldn’t get PowerShell 5.0 in place, but I needed to know if it would even be necessary.

I had a little testing to do. The below command measured the time it took to expand a 133MB zip file using .NET.

Measure-Command -Expression {
    Add-Type -AssemblyName System.IO.Compression.FileSystem
    [System.IO.Compression.ZipFile]::ExtractToDirectory('C:\Users\tommymaynard\Desktop\HCM-920-UPD-018-WIN_1of10.zip','C:\Users\tommymaynard\Desktop\unzip\')
}

When the command was run five separate times, it resulted in following times: 9 seconds 393 milliseconds, 9 seconds 117 milliseconds, 9 seconds 455 milliseconds, 8 seconds 489 milliseconds, and 10 seconds 338 milliseconds. I wasn’t loosing any time by unzipping this file.

The below command does the same thing as the one above; however, it uses the Expand-Archive cmdlet introduced in PowerShell 5.0.

Measure-Command -Expression {
    Expand-Archive -Path 'C:\Users\tommymaynard\Desktop\HCM-920-UPD-018-WIN_1of10.zip' -OutputPath 'C:\Users\tommymaynard\Desktop\unzip\'
}

I executed the above command five times, too. The results were 2 minutes 11 seconds, 2 minutes 9 seconds, 2 minutes 10 seconds, 2 minutes 13 seconds, and 2 minutes 11 seconds.

This is a huge difference in time. Now, I do want to mention that I tested this on Windows 8.1 with PowerShell 5.1 (it’s in preview). The results may be better on different versions of Windows and with different versions of PowerShell. Let me know if you see different results with different configurations, and maybe I’ll do the same. The point is this, however: If you have a reason to speed up your project, you might consider .NET over a PowerShell cmdlet, or function. It seems I’m glad I did. Be sure you test different ways, to do the same thing.

PSMonday #26: October 24, 2016

Topic: PowerShell’s Importance

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.

Today is the 26th PowerShell Monday. That means I’ve been writing these for a full half year.

I want to use this morning to point some things out. If you take the time to study each of these emails, then you’re going to be better at your job, where PowerShell can be utilized, in just a year’s time. Take the time to read these whether you already know the content, or not. Now, in conjunction, it’s important to practice what you’re reading, too. Do things in the GUI and then consider how you might complete the same task at the ConsoleHost. Ask questions if you have them, as I’m always willing to help.

I learned early on that PowerShell is going to be a requirement for Windows systems administration. Microsoft keeps proving this, as do other companies that are using it in conjunction with their own products. The inventor of PowerShell is now a Microsoft Technical Fellow — that’s the highest technical title one can receive from Microsoft. He’s the lead architect for the Enterprise Cloud Group and the Microsoft Azure Stack. He presides over Windows Server and the System Center products. Don’t think for a moment that PowerShell won’t continue to play a part at Microsoft. You’re going to need this skill. Keep in mind that we’re not going to need a bunch of click-next admins, as they’re called, in the coming years.

After writing a PSMonday each week for a half of a year, I plan to place the first 26 weeks’ worth of content into a single PDF and distribute that in the coming weeks. If you haven’t read all of them, then this might be a nice way to do that.

On a final note, PowerShell was recently introduced on Linux and Mac. My first thought was, great, soon Windows administrators can support Linux, too. It was after a recent trip to Phoenix for a PowerShell Saturday event, however, where Jason Helmick — a PowerShell MVP — made an interesting point: Linux administrators are going to be able to support Windows, too. It’s time to learn PowerShell, and maybe more about both operating systems. In fact, Jason told a room full of Windows administrators, the same thing.

Compare AWSPowerShell Versions II

Series links: Part I, Part II, Part III (Coming)

I wrote a post a day or two ago that indicated how to compare your currently installed AWSPowerShell module with the offering on the PowerShell Gallery (see Part I above). Well, as I suspected, I’m back with an update.

Before we get there, however, I want to mention that Steve Roberts, at AWS, got in touch with me via Twitter about the post. I suspect we’ll be seeing some updates to Get-AWSPowerShellVersion. What’s interesting, is that I found the ListServiceVersionInfo parameter after my post. It seems that the cmdlet does create objects for some of the results it can return, just not everything. As always, I’m looking forward to Steve’s ideas and implementation.

Today, I’m going to share a quickly written advanced function. This will do the comparison for you and return the results in a custom object. Don’t be surprised if I make some more additions and/or changes to the function and post that as well. It really should have an option to download and install the new module if you want it, right?

Function Compare-AWSPowerShellVersion {
<#
.SYNOPSIS
    This advanced function compares the currently installed version of the AWSPowerShell module and the version on the PowerShell Gallery (http://powershellgallery.com).

.DESCRIPTION
    This advanced function compares the currently installed version of the AWSPowerShell module and the version on the PowerShell Gallery (http://powershellgallery.com).

.EXAMPLE
    PS > Compare-AWSPowerShellVersion
    This example compares the currently installed version of the AWSPowerShell module and the version on the PowerShell Gallery.

.NOTES 
    NAME: Compare-AWSPowerShellVersion
    AUTHOR: Tommy Maynard
    COMMENTS: --
    LASTEDIT: 10/19/2016
    VERSION 1.1:
        - Edited notes.
        - Changed Switch to -regex and made the first option an OR for both side indicators.
    VERSION 1.2:
        - Modified results to use a custom object.
    VERSION 1.3:
        - Removed Switch statement: added version determination logic inside custom object creation.
        - Only returning the highest version number from currently installed if more than one version. This is a leftover due to AWS Toolkit install vs. PowerShell Gallery install.
#>
    [CmdletBinding()]
    Param (
    )

    Begin {
        # Set continuation variable for Process block.
        $Continue = $true

        'PowerShellGet','AWSPowerShell' | ForEach-Object {
            Write-Verbose -Message "Determining if the $_ module is available."
            If (-Not(Get-Module -Name $_ -ListAvailable)) {
                Write-Warning -Message "Unable to locate the required $_ module."
                $Continue = $false
            }
        } # End Foreach.
    } # End Begin.

    Process {
        If ($Continue) {
            Write-Verbose -Message 'Collecting the current and newest AWSPowerShell module versions.'
            $CurrentAWSPSModule = ((Get-Module -Name AWSPowerShell -ListAvailable).Version |
                Sort-Object -Descending | Select-Object -First 1).ToString()
            $NewestAWSPSModule = (Find-Module -Name AWSPowerShell).Version.ToString()

            Write-Verbose -Message 'Comparing the AWSPowerShell version and creating custom results object.'
            [PSCustomObject]@{
                Match = If ($CurrentAWSPSModule -eq $NewestAWSPSModule) {$true} Else {$false}
                Current = $CurrentAWSPSModule
                Newest = $NewestAWSPSModule
            }
        } # End If.
    } # End Process.

    End {
    } # End End.
} # End Function: Compare-AWSPowerShellVersion.

Update: After updating to the newest version, I returned a System.Object[] error for my current version, due to having multiple versions and using the ToString method. I’ve modified the code above to only select the first (and highest) module version from those on my system. I think it’s important to point out that I’ve always downloaded and installed using the .msi until this version, and that when I used Install-Module, I included the Scope parameter name with the CurrentUser parameter value. I suspect this is where the problem is originating. Well, this and the fact I haven’t uninstalled the .msi version from Programs and Features.

I shouldn’t have to wait too long to be on an old build to test some more! It seems as though AWS does at least one, daily build. Here’s the full command as I ran it to install the newest version: Find-Module -Name AWSPowerShell | Install-Module -Scope CurrentUser.

Here’s the usage and results since updating to 3.3.11.0.

Compare-AWSPowerShellVersion

Match Current  Newest  
----- -------  ------  
 True 3.3.11.0 3.3.11.0

Update: As suspected, there’s a new build this evening, and it appears the comparison function is still working, as it’s correctly indicated there isn’t a match between what’s installed and what’s available to install.

Compare-AWSPowerShellVersion

Match Current  Newest
----- -------  ------
False 3.3.11.0 3.3.12.0

I updated to the newest version of the module and ran it again. It’s looking good.

Compare-AWSPowerShellVersion

Match Current  Newest
----- -------  ------
 True 3.3.12.0 3.3.12.0

Compare AWSPowerShell Versions

Series links: Part I, Part II, Part III (Coming)

Well, I wrote enough example code inside my PowerShell ConsoleHost recently, to write another post. That’s typically how these work. Find something great… write. Find something I hate… write. Most of all, just write. Put it on Twitter and help people learn—someone did it for me once.

Today’s goal was to compare my currently installed version of the AWSPowerShell module with the newest version of the AWSPowerShell module. We’ll do this a bit backward, and start with obtaining the newest version of the AWSPowerShell Module first. Because AWS has elected to put their module on the PowerShell Gallery—thanks, guys—we don’t have to parse the AWS PowerShell home page. I’m not sure if it’s even there, but luckily for us, it doesn’t need to be, and so I don’t need to try.

Find-Module -Name AWSPowerShell

Version    Name                                Repository           Description
-------    ----                                ----------           -----------
3.3.9.0    AWSPowerShell                       PSGallery            The AWS Tools for Windows PowerShell lets develo...

(Find-Module -Name AWSPowerShell).Version

Major  Minor  Build  Revision
-----  -----  -----  --------
3      3      9      0

(Find-Module -Name AWSPowerShell).Version.ToString()
3.3.9.0

Now that we can obtain the version number of the newest release, let’s work on getting the currently installed version number. My first thought was to use Get-AWSPowerShellVersion. It was a mess, but here’s what I did. This cmdlet does not return an object, unfortunately (and should be replaced and/or corrected).

Seriously, we expect that cmdlets and functions will return a usable object or objects. If we really need additional information stuffed in the results, let’s only include it with the Verbose parameter. Maybe just drop all that text in a file and have Verbose indicate the file to open. Maybe make a secondary cmdlet. Anything really, that doesn’t require that we parse text… which is exactly what I did (at first).

((Get-AWSPowerShellVersion).Split('`n')[2])[1..7] -join ''
3.3.0.0
[version](((Get-AWSPowerShellVersion).Split('`n')[2])[1..7] -join '')

Major  Minor  Build  Revision
-----  -----  -----  --------
3      3      0      0

The problem with this approach is that it’s much too exact. It’s grabbing certain characters. What happens when the version is longer than seven characters? We would’ve lost our final zero, if I was still on a previous version, such as 3.1.66.0, 3.1.71.0, or 3.1.73.0. Not good enough.

I knew the AWSPowerShell module put something somewhere; every module does. I browsed over to C:\Program Files (x86)\AWS Tools\PowerShell\AWSPowerShell to take a look around. Maybe there was something there that would be more reliable. I quickly spotted the module manifest file: AWSPowerShell.psd1. I opened it up in a text editor, and as suspected, there was the version… right inside that beautiful hash table. I closed up the file and used Test-ModuleManifest inside my ConsoleHost.

$Path = 'C:\Program Files (x86)\AWS Tools\PowerShell\AWSPowerShell\AWSPowerShell.psd1'
Test-ModuleManifest -Path $Path
ModuleType Version    Name                                ExportedCommands
---------- -------    ----                                ----------------
Binary     3.3.0.0    AWSPowerShell                       {Clear-AWSHistory, Set-AWSHistoryConfiguration, Initialize...
(Test-ModuleManifest -Path $Path).Version

Major  Minor  Build  Revision
-----  -----  -----  --------
3      3      0      0

(Test-ModuleManifest -Path $Path).Version.ToString()
3.3.0.0

That’s way more reliable. I’d much rather blame AWS if it doesn’t return the correct version, than my parsing against the results of Get-AWSPowerShellVersion. I mean, seriously.

If you’ve been following my writings, then it’ll come as no surprise that I started with the hardest way and overlooked the obvious. I could’ve simply just used Get-Module to return the version; it’s getting its information from the .psd1 file. As obnoxious as this is, it's keeping me sharp. I get to repeatedly practice what I know and give my mind time to sort out different resolutions to the same problem. Here's how I returned the version of the currently installed AWSPowerShell module.

Get-Module -Name AWSPowerShell
ModuleType Version    Name                                ExportedCommands
---------- -------    ----                                ----------------
Binary     3.3.0.0    AWSPowerShell                       {Add-AASScalableTarget, Add-ACMCertificateTag, Add-ASAAtta...
(Get-Module -Name AWSPowerShell -ListAvailable).Version

Major  Minor  Build  Revision
-----  -----  -----  --------
3      3      0      0

(Get-Module -Name AWSPowerShell -ListAvailable).Version.ToString()
3.3.0.0

Now I can return both the currently installed version of the AWSPowerShell module and the newest version of the AWSPowerShell module from the PowerShell Gallery. Before we compare them, notice that in the above examples that I did and didn’t use the ListAvailable parameter. If you don’t use it, you better be absolutely certain the module has already been imported.

So, let’s get these version numbers into a couple of variables and compare them.

$CurrentAWSPSModule = (Get-Module -Name AWSPowerShell -ListAvailable).Version.ToString()
$CurrentAWSPSModule
3.3.0.0
$NewestAWSPSModule = (Find-Module -Name AWSPowerShell).Version.ToString()
$NewestAWSPSModule
3.3.9.0
Compare-Object -ReferenceObject $CurrentAWSPSModule -DifferenceObject $NewestAWSPSModule -IncludeEqual

InputObject SideIndicator
----------- -------------
3.3.9.0     =>
3.3.0.0     <=

As we can tell, there is a difference between my version and the one in the PowerShell Gallery. I should probably download the newest version. Watch for a follow-up to this article, as I may go ahead and write a second part, as I have some other ideas.

PSMonday #25: October 17, 2016

Topic: $PSDefaultParameterValues II

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.

To recap last week, the $PSDefaultParameterValues preference variable stores default parameter names and corresponding values for cmdlets and functions. This means we can use cmdlets and functions with modified parameters, without the need to type in the names and values each time.

Last week we used the Add and Remove methods. This week, we’ll use standard hash table syntax, since that’s all this variable holds. Hash tables were first introduced back on May 30, 2016 when we first discussed creating and splatting a parameter hash table. A hash table contains key-value pairs.

Let’s add a new entry to our empty $PSDefaultParameterValues variable.

$PSDefaultParameterValues = @{'Get-Help:ShowWindow'=$true}
$PSDefaultParameterValues

Name                           Value
----                           -----
Get-Help:ShowWindow            True

Just as we did last, when we enter the variable name after it’s populated, it’ll return the results in the standard, hash table output to include the Name and Value properties.

With the ShowWindow parameter name set to a value of True, Get-Help will always show the full help inside a GUI window and not pollute the ConsoleHost or ISE (PowerShell 3.0 and greater only), without the need to actually type the switch parameter -ShowWindow.

If your variable is already holding a value, be sure to use the += assignment operator instead of the = assignment operator, or you’ll overwrite what was already being stored in the variable.

On an entry after the first one, use this:

$PSDefaultParameterValues += @{'Get*:Verbose'=$true}

Not this:

$PSDefaultParameterValues = @{'Get*:Verbose'=$true}

Notice that in the above examples we’ve used a wildcard character. If this is added to your $PSDefaultParameterValues, then all Get-* cmdlets and functions will include the Verbose parameter.