Category Archives: Script Sharing

Add AccessKey and SecretKey Parameters to AWS Function

I’m spending more, and more, time with AWS. As a PowerShell enthusiast, I’ve therefore kept myself busy during a few of the week and weekend evenings. And sometimes during the day, too, when it makes sense.

As of today, I’ve written four AWS specific functions for work. It’s everything from comparing my version of the AWSPowerShell module with the one in the PowerShell Gallery, returning the AWS service noun prefixes (EC2, CFN, etc.), returning the EC2 instance type counts, and then there’s the newest one.

My most recent function can create an RDP (.rdp) file to connect to an EC2 instance (it gets the EC2’s IP address and places that in an RDP file). As I wrote up an email to share the function with a coworker — the one that mentioned Azure having an option to download RDP files — it occurred to me that none of my functions include an option to provide AccessKey and SecretKey parameters. All of them, that need it (two of them now), rely on persistent credentials having been saved.

Well, I’ll be going to go back through these two functions and add a way to accept an AccessKey and SecretKey. First, however, was to figure out how. I literally typed “PowerShell add AccessKey and SecretKey to function” into Google (because I was being lazy,… and didn’t feel the need to write something that must surely be out there). Well, there wasn’t anything I could use, so I’ve done the work myself. Now, if anyone else types that, or something like that into Google, or Bing, maybe they’ll get this answer.

Before I consider moving this to an existing function, I needed to make it work. I did just that, using parameter sets. I’ve long know about them, but haven’t really had a need for them in more than a few instances. This of course led me to view the parameter sets on an AWS cmdlet. Ugh, they aren’t even parameter sets on something like Get-EC2Instance. Try it yourself: Show-Command -Name Get-EC2Instance. I suspect there’s internal logic in the cmdlet itself to handle whether or not an AccessKey and SecretKey have been provided. Don’t have persistent, shell credentials: error*. Only provide an AccessKey: error*. Only provide a SecretKey: error*. Well, I went with parameter sets anyway.

* “No credentials specified or obtained from persisted/shell defaults.”

Here’s the complete function used to work through this desire. Take a look and continue reading about it further below.

Function Test-AWSParameterSets {
    [CmdletBinding(DefaultParameterSetName='NoAccessKeys')]
    Param(
        [Parameter(ParameterSetName='NoAccessKeys',Mandatory = $true)]
        [Parameter(ParameterSetName='AccessKeys',Mandatory=$true)]
        [String]$InstanceID,
 
        [Parameter(ParameterSetName='AccessKeys',Mandatory=$true)]
        [string]$AccessKey,
 
        [Parameter(ParameterSetName='AccessKeys',Mandatory=$true)]
        [string]$SecretKey

    )

    $PSBoundParameters
    '-----------------------------------------'
    "InstanceID: $InstanceID"
    "AccessKey: $AccessKey"
    "SecretKey: $SecretKey"
    "ParameterSet: $($PSCmdlet.ParameterSetName)"
}

In this first example, we’ll run the function without supplying any parameter names, or values. Because the InstanceID parameter is mandatory, and the NoAccessKeys parameter set is the default, the PowerShell engine prompts me to enter a value for InstanceID. After I entered i-1234554321, the function returns the $PSBoundParameters hash tables. This include the parameter names and values that were supplied to the function at run time. Additionally, it returns the values for the InstanceID, AccessKey, SecretKey, and which of the two parameter sets were used: NoAccessKeys or AccessKeys.

PS > Test-AWSParameterSets
cmdlet Test-AWSParameterSets at command pipeline position 1
Supply values for the following parameters:
InstanceID: i-1234554321

Key        Value       
---        -----       
InstanceID i-1234554321
-----------------------------------------
InstanceID: i-1234554321
AccessKey: 
SecretKey: 
ParameterSet: NoAccessKeys

The next example includes an InstanceID parameter when the function is invoked. Therefore, I’m not prompted to enter any additional information. It produces the same output as it did above.

PS > Test-AWSParameterSets -InstanceID i-1234554321

Key        Value       
---        -----       
InstanceID i-1234554321
-----------------------------------------
InstanceID: i-1234554321
AccessKey: 
SecretKey: 
ParameterSet: NoAccessKeys

This below example includes both an InstanceID parameter and an AccessKey parameter when the function is invoked. The moment the AccessKey parameter name was included, we switched to using the AccessKeys parameter set. Therefore, I was prompted to enter a SecretKey parameter value, as it’s a required parameter in that parameter set.

PS > Test-AWSParameterSets -InstanceID i-1234554321 -AccessKey aacccceesssskkeeyy

cmdlet Test-AWSParameterSets at command pipeline position 1
Supply values for the following parameters:
SecretKey: ssseeecccrrreeetttkkkeeeyyy

Key        Value                      
---        -----                      
InstanceID i-1234554321               
AccessKey  aacccceesssskkeeyy         
SecretKey  ssseeecccrrreeetttkkkeeeyyy
-----------------------------------------
InstanceID: i-1234554321
AccessKey: aacccceesssskkeeyy
SecretKey: ssseeecccrrreeetttkkkeeeyyy
ParameterSet: AccessKeys

This is basically the opposite of the above example. I included the SecretKey parameter and it prompted me to enter the AccessKey parameter — we can’t have one without the other; they’re both mandatory in the AccessKeys parameter set.

PS > Test-AWSParameterSets -InstanceID i-1234554321 -SecretKey ssseeecccrrreeetttkkkeeeyyy

cmdlet Test-AWSParameterSets at command pipeline position 1
Supply values for the following parameters:
AccessKey: aacccceesssskkeeyy

Key        Value                      
---        -----                      
InstanceID i-1234554321               
SecretKey  ssseeecccrrreeetttkkkeeeyyy
AccessKey  aacccceesssskkeeyy         
-----------------------------------------
InstanceID: i-1234554321
AccessKey: aacccceesssskkeeyy
SecretKey: ssseeecccrrreeetttkkkeeeyyy
ParameterSet: AccessKeys

The final example includes values for all the parameter names at the time the function is invoked. If someone wasn’t using a persistent set of credentials, then this is how you might expect an AWS function you’ve written to be used.

PS > Test-AWSParameterSets -InstanceID i-1234554321 -AccessKey aacccceesssskkeeyy -SecretKey ssseeecccrrreeetttkkkeeeyyy

Key        Value                      
---        -----                      
InstanceID i-1234554321               
SecretKey  ssseeecccrrreeetttkkkeeeyyy
AccessKey  aacccceesssskkeeyy         
-----------------------------------------
InstanceID: i-1234554321
AccessKey: aacccceesssskkeeyy
SecretKey: ssseeecccrrreeetttkkkeeeyyy
ParameterSet: AccessKeys

Well, that’s the first part of the challenge. Now to incorporate what I’ve done here, into the functions that need it. Maybe, I’ll be back with that.

Extract Media Folder from PowerPoint Files

On Wednesday, I wrote a response to something on Stack Overflow. So I don’t have to chase it down one day, I though I’d briefly discuss it here and include the updated code I would be more likely to use, providing I ever need to use it.

If you open a PowerPoint .pptx file in something like 7-Zip, you’ll quickly realize that the .pptx is a compressed, or archived, file format. While this brings down the file size (1.65MB file vs. 3.27MB when expanded), this is more more to say that there are a few folders, and several files, that make up a single PowerPoint file.

A person on Stack Overflow wanted to automate the expansion of a series of .pptx files and extract the media folder. This folder holds all the images in a PowerPoint file. I’m not going to bother to explain the code, but let me set the stage. I created a folder on my desktop called pptx. Inside the folder were four PowerPoint files using the .pptx file extension. My code created a new folder for each file in the pptx folder, expanded each PowerPoint file in their own new folders, located the nested media folder, moved it to the top level of their new folder, and then deleted all the other folders and files inside the new folder. If there wasn’t a media folder, it would still create the new folder; however, it would create a single text file in there called “No media folder.txt.” This was to help ensure the user didn’t think the code didn’t work, when they didn’t find the media folder inside the new folder.

I’ll included the original code from the forum post further below, but for now here’s an updated and cleaner version.

$Path = 'C:\users\tommymaynard\Desktop\pptx'
$Files = Get-ChildItem -Path $Path

Foreach ($File in $Files) {
    New-Item -Path $File.DirectoryName -ItemType Directory -Name $File.BaseName | Out-Null
    $NewFolder = $File.FullName.Split('.')[0]

    Add-Type -AssemblyName System.IO.Compression.FileSystem
    [System.IO.Compression.ZipFile]::ExtractToDirectory($File.FullName,$NewFolder)
    
    If (Test-Path -Path "$($NewFolder)\ppt\media") {
        Move-Item -Path "$($NewFolder)\ppt\media" -Destination $NewFolder
        Get-ChildItem -Path $NewFolder | Where-Object {$_.Name -ne 'media'} | Remove-Item -Recurse

    } Else {
        Get-ChildItem -Path $NewFolder | Remove-Item -Recurse
        New-Item -Path $NewFolder -ItemType File -Name 'No media folder.txt' | Out-Null
    }
}

And, here’s the original code and a link to the post itself. This below version didn’t use the $NewFolder variable, so there’s overwhelming inclusion of this piece of code: $File.FullName.Split(‘.’)[0]. It also defaulted to use the Expand-Archive cmdlet, if that was available. As I mentioned in the Stack Overflow post itself, it’s so slow. I’ve removed that so that it’s much faster now, which reminded me, I’ve actually written about this time difference before. It was just last October.

$Path = 'C:\users\tommymaynard\Desktop\pptx'
$Files = Get-ChildItem -Path $Path

Foreach ($File in $Files) {
    New-Item -Path $File.DirectoryName -ItemType Directory -Name $File.BaseName | Out-Null

    If (Get-Command -Name Expand-Archive) {
        Expand-Archive -Path $File.FullName -OutputPath $File.FullName.Split('.')[0]

    } Else {
        Add-Type -AssemblyName System.IO.Compression.FileSystem
        [System.IO.Compression.ZipFile]::ExtractToDirectory($File.FullName,$File.FullName.Split('.')[0])
    } # End If-Else.

    If (Test-Path -Path "$($File.FullName.Split('.')[0])\ppt\media") {
        Move-Item -Path "$($File.FullName.Split('.')[0])\ppt\media" -Destination $File.FullName.Split('.')[0]
        Get-ChildItem -Path $File.FullName.Split('.')[0] | Where-Object {$_.Name -ne 'media'} | Remove-Item -Recurse

    } Else {
        Get-ChildItem -Path $File.FullName.Split('.')[0] | Remove-Item -Recurse
        New-Item -Path $File.FullName.Split('.')[0] -ItemType File -Name 'No media folder.txt' | Out-Null
    } # End If-Else.
} # End Foreach.

Modify the Time Zone Without tzutil.exe

Note: This post has been updated below (3/9/2017).

Earlier today (2/27/2017), I wanted a purely PowerShell answer to setting the time zone. That meant that I didn’t want to rely on tzutil.exe, as I had forever now. The best I could do was to use PowerShell with the registry, in order to update the TimeZoneKeyName key. So I can refer to the code, as I’ll forget it soon enough, I thought I’d place it here with the possibility it’ll also be helpful for someone else, someday.

I do want to mention that this method would require elevation to a local administrator, as would any modification to HKLM. Using tzutil.exe, however, does not include this same restriction. On that alone, I may actually stick with the Windows Time Zone (command line) Utility.

$RegistryPath = 'HKLM:\SYSTEM\CurrentControlSet\Control\TimeZoneInformation'
$RegistryKeyName = 'TimeZoneKeyName'
$RegistryValue = 'US Mountain Standard Time'
Set-ItemProperty -Path $RegistryPath -Name $RegistryKeyName -Value $RegistryValue


Update
: I saw a recent Tweet from Jeffery Hicks about the newer, PowerShell 5.1 function, Get-ComputerInfo. I ran it, as I have at least once before, and ended up staring right at a TimeZone property. So with that, I jumped back to this post to do an update. I think it’s coming…

Convert Inches to Feet, and Inches

My blog serves several purposes. One of which I’ve mentioned before, is to keep things around I may want later. That’s why we’re back today. While helping on a TechNet forum post, I wrote the below function.

This function’s purpose is to convert inches between 56 and 76 to their <feet>'<inches>” equivalent. This means if you send in 56, you’ll get back 4’8″, if you send in 60 you’ll get 5’0″, if it’s 73 you’ll get 6’1″. The reason it’ll only take 56 to 76 inches is due to the initial requirements in the forum post. If you had a purpose for this function and wanted it to accept more or less inches, you’d have to modify the ValidateRange attribute to different minimum and maximum values, or remove it altogether. If you can use this function, then here it is.

Function Convert-InchToFeetInch {
    Param (
        [Parameter(Mandatory=$true)]
        [ValidateRange(56,76)]
        [int]$Inches
    )

    $TotalFeet = [int](($Inches / 12).ToString().Split('.')[0])
    $RemainingInches = $Inches - ($TotalFeet * 12)

    "$TotalFeet'$RemainingInches`""
}

This first example will loop through all the possible values from 56 to 76 using the ForEach-Object cmdlet. As it does that, it’ll write out the number of inches it’s converting, as well as the feet and remaining inches in the aforementioned format.

56..76 | ForEach-Object {
    "$_ inches is $(Convert-InchToFeetInch -Inches $_)"
}

56 inches is 4'8"
57 inches is 4'9"
58 inches is 4'10"
59 inches is 4'11"
60 inches is 5'0"
61 inches is 5'1"
62 inches is 5'2"
63 inches is 5'3"
64 inches is 5'4"
65 inches is 5'5"
66 inches is 5'6"
67 inches is 5'7"
68 inches is 5'8"
69 inches is 5'9"
70 inches is 5'10"
71 inches is 5'11"
72 inches is 6'0"
73 inches is 6'1"
74 inches is 6'2"
75 inches is 6'3"
76 inches is 6'4"

These next examples simply use the function outside a looping construct. You provide a number of inches to the Inches parameter, and the function will return the converted value. That’s it. Maybe it helps you, and maybe I can find it if I ever need it.

Convert-InchToFeetInch -Inches 56 # 4'8"
Convert-InchToFeetInch -Inches 60 # 5'0"
Convert-InchToFeetInch -Inches 68 # 5'8"
Convert-InchToFeetInch -Inches 73 # 6'1"
Convert-InchToFeetInch -Inches 76 # 6'4"

4'8"
5'0"
5'8"
6'1"
6'4"

Get-TMVerbSynonym 1.4

Notes: Download link at the bottom of this post.

One of my favorite PowerShell community members, June Blender, posted a question on Twitter. While it wasn’t directed at me, I couldn’t resist. She wanted the approved verb options for the verbs “populate”, “fill in”, or “load”. I couldn’t help myself, because I wrote a function for this very task!

The Get-TMVerbSynonym function’s purpose is to find synonyms for verbs and return whether they’re approved, or not. Well, her Tweet was all it took to finally make some overdue changes, to include getting it posted on the PowerShell Gallery. As I called it in one of my follow up tweets, when June asked where it was then published, I said it was a “TechNet leftover.”

Well, not anymore.

It’s since been updated to version 1.4 and placed on the PowerShell Gallery. The above image was what I posted when it was in version 1.3. This means that it doesn’t reflect two of the newest, view able changes. The Verb property now indicates the verb it’s checking, and the old Verb property has been renamed to Synonym. This was included to support some other potential updates for an even newer version. Anyway, this means it’s up to five properties, so pipe to Format-Table -AutoSize in order that the results can be easily read.

Additionally, it now includes something I always wanted it to have: an Approved, switch parameter. This means there’s no piping to Where-Object to filter the function to only show the approved synonyms. Here’s an example of the function in action, both with, and without the Approved, switch parameter.

Download it now manually, or use Install-Script -Name Get-TMVerbSynonym.

The 1 to 100 Game, Updated

Notes: Download link at the bottom of this post.

My just turned, five-year-old daughter was introduced to my PowerShell 1 to 100 game around two years ago. As I watched her play, I always thought I should make some changes to the game. Well today, I have a newer version, that I’d like to briefly share, and discuss.

First off, you may need to know how to play the 1 to 100 game. It was common in my household as a child, but it’s possible it wasn’t in everyone’s. It works this way: The computer — the function, more or less — chooses a random number between 1 and 100 and you have to try and guess the number. If you choose a number lower than the computer’s number, it will tell you to choose a higher number. If you choose a number higher than the computer’s number, it will tell you to choose a lower number. You continue to do this, until you guess the correct number.

What I always thought was necessary for this game was to make it possible to choose whether you even want to use 1 and 100. This 3.0 version of the function will allow you to choose the minimum and maximum numbers. That way, littler kids can play 1 to 10, or 1 to 20. Maybe your kiddo is learning their fifties: You could even play 50 to 59, if you desired. Maybe you have a little downtime while you’re waiting for a server restart; you can play it too. There’s some value here, but not perhaps the value you might typically expect with a PowerShell function. It does have an example of thrice nested Do-While loops. That’s how I pushed the previous version. Skip downloading this 2.0 version, and use the bottom link for version 3.0.

In the older version, it showed the aggregate totals by default. Now, you have to include the ShowTotals switch parameter, if you want to see that collected information. You probably do, if you’re going to play more than once or twice in a row. This information includes nuggets of information like how many games you’ve played, your total number of attempts to guess the right number across all games, and you average attempts per game. These totals have drastically improved, too. In fact, it’ll continue to show the previous totals, and the new totals, after each game. If this is confusing, then take a look at game play images further below, and try it out for yourself.

This first image, is a game from 1 to 10 that doesn’t include the ShowTotals parameter.

This next image is from two back-to-back games from 1 to 3, that does include the ShowTotals parameter.

So it’s been said, the function defaults to 1 and 100, if you don’t supply minimum and maximum numbers. Try it out when you have a minute, and definitely show it to your kiddos, too. If my daughter is any indication, your kids will enjoy it, as well. I should mention, that she was pestering me to update this function, after I first mentioned wanting to make some changes for her. A four-year-old pestering me to write PowerShell: I loved it.

On a final note, there is some redundant code in the function. Since this is just a game, and I have bigger projects, I’m leaving it as is for now. Maybe I’ll come back around some time and clean it up… we’ll see.

You can download it from the PowerShell Gallery, or better yet, from your PowerShell host application using PowerShellGet and the Install-Script function: Install-Script -Name Start-1to100Game3.0

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.'
    }
}

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.

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 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 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 was returning 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.

PS > 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.

PS > 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.

PS > Compare-AWSPowerShellVersion

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

AWS EC2 Instance Type Count

There’s a project that I’m on that allows me to work in AWS. It’s pretty important stuff at work, and since there’s an AWS PowerShell module, I couldn’t be more interested. As of today, I’m going to start to include worthy AWS PowerShell work, right here. As I become more involved, so will my blog.

I’ve said it a bunch of times now, but this site isn’t just about helping others — although that’s a big part — but it’s about giving myself a place to store the things, I think I’ll want one day. With over 2,000 cmdlets in version 3.3.0.0 of the AWSPowerShell module, I’m going to need a place to store this, and any help I can get in retaining this information, I’ll take. Writing helps with that; you know, the whole writing to remember concept.

So, here’s the error I was up against today:

“Your quota allows for 0 more running instance(s). You requested at least 1 – You can request an increase of the instance limit here: http://aws.amazon.com/contact-us/ec2-request/.”

As relayed by our upcoming, but practically already there, AWS expert, “Each instance size (t2.small, c3.large, etc) has a separate quota in each region in AWS.” The decision to do this was preventative in that we wouldn’t accidentally spin up too many instances and cost ourselves dearly. I get that.

I’m a curious type, so I couldn’t help but wonder, how many of each instance type do we have in the current AWS account? I switched over to my PowerShell console and started writing. This is straightforward and basic PowerShell, but it still seemed like something worth keeping around, and sharing, as well.

$Instances = Get-EC2Instance

Foreach ($Instance in $Instances) {
    [array]$Types += $Instance.Instances.InstanceType
}

$Types | Group-Object -NoElement

The example works this way: Line 1: Capture all the EC2 instances, Line 3: Begin to iterate over the returned collection. With each pass, in line 8, append the current instance’s instance type to the $Types variable — an array. Finally, in line 7, we group the $Types variable in that it’ll automatically provide the Count property for each Instance Type.

I’ve included a slight modification in the example below. In this instance, I didn’t cast the $Types variable as an array, and instead created the variable as an empty array prior to the Foreach. Same end goal, however, I wanted to highlight how others might be inclined to write something similar.

$Instances = Get-EC2Instance

$Types = @()
Foreach ($Instance in $Instances) {
    $Types += $Instance.Instances.InstanceType
}

$Types | Group-Object -NoElement

If you run this repeatedly, you’ll quickly realize that it’ll continue to add to the $Types variable, thus making the results incorrect as soon as the second run. You can add this as the last line: Remove-Variable -Name Types, or better, make it a function.

Function Get-EC2InstanceTypeCount {
<# #>
    [CmdletBinding()]
    Param (
    )

    Begin {
        $Instances = Get-EC2Instance
    } # End Begin.

    Process {
        Foreach ($Instance in $Instances) {
            [array]$Types += $Instance.Instances.InstanceType
        }

        $Types | Group-Object -NoElement
    } # End Process.

    End {
    } # End End.
}

Remember, you’re going to need to have already used Set-AWSCredentials and stored a set of credentials to a persistent store. This function is dependent, just like any AWS cmdlet, on there being a set of stored credentials. I did not write the function to accept an AccessKey and SecretKey parameter, as this isn’t a recommended scenario in much of the documentation I’ve read from AWS. Here’s the function — with comment-based help — if might be useful for your team.

Function Get-EC2InstanceTypeCount {
<# .SYNOPSIS This advanced function will return the AWS Instance Types used in an AWS account. .DESCRIPTION This advanced function will return the AWS Instance Types used in an AWS account. It will return a name property, such as t2.medium, m4.large, etc., and a Count property. The results are sorted on the count, as they are produced using the Sort-Object cmdlet. .EXAMPLE PS > Get-EC2InstanceTypeCount
    This example returns the EC2 Instance Types and how many of each are being used.

    Count Name
    ----- ----
    32    t2.medium
    18    t2.micro
     6    c3.large
     6    m4.large
     7    t2.small
     2    r3.large
     4    r3.xlarge
     5    g2.2xlarge
     5    t2.large
     1    t2.nano

.EXAMPLE
    PS > Get-EC2InstanceTypeCount | Sort-Object -Property Name
    This example returns the EC2 Instance Types and how many of each are being used, sorted by Name.

.EXAMPLE
    PS > Get-EC2InstanceTypeCount | Where-Object -Property Name -like *large*
    This example returns the EC2 Instance Types that include the term "large" in the Name property.

    Count Name
    ----- ----
        6 c3.large                 
        6 m4.large                 
        2 r3.large                 
        4 r3.xlarge                
        5 g2.2xlarge               
        5 t2.large

.NOTES
    NAME: Get-EC2InstanceTypeCount
    AUTHOR: Tommy Maynard
    COMMENTS: --
    LASTEDIT: 09/27/2016
    VERSION 1.0
#>
    [CmdletBinding()]
    Param (
    )

    Begin {
        $Instances = Get-EC2Instance
    } # End Begin.

    Process {
        Foreach ($Instance in $Instances) {
            [array]$Types += $Instance.Instances.InstanceType
        }

        $Types | Group-Object -NoElement
    } # End Process.

    End {
    } # End End.
}