Tag Archives: AWSPowerShell

Those AWS Region Commands

More and more, Amazon Web Services has become a significant part of my day. Luckily for me, PowerShell and AWS, work well together. There’s long been the AWSPowerShell module, which much like the AWS CLI, allows us to issue API calls to AWS from a command line.

As a part of continuing my journey into AWS, and maintaining my mild obsession with PowerShell, I’ve decided to better learn a few PowerShell cmdlets, from the AWSPowerShell module throughout at least a couple of posts. As of version 3.3.365, the module only contains a few thousand cmdlets. It seems like AWS has gone ahead and made an investment here in PowerShell. Especially, when it wasn’t even terribly long ago that there were only 2,000.

(Get-Command -Module AWSPowershell | Measure-Object).Count
4499

Oh yeah, that reminds me, Lambda supports PowerShell (Core) now, too. As I read somewhere recently, “It’s not Bash, it’s not Ruby. It’s PowerShell.”

In a few previous, AWS-specific posts I was able to point out some things I thought should be changed. And somehow, AWS paid close enough attention, that some changes were actually made. It’s still hard to believe; it’s a bit surreal.

Hashtag AWS Tweet Prompts Fix to AWSPowerShell Module
AWS Stop-EC2Instance Needs Complimentary Cmdlet
More AWS PowerShell Changes Due to Twitter (and Me)

I’m mostly writing this evening to help solidify a few commands for my own education, and anyone else who is reading along. But… as I experimented with some of these AWS PowerShell cmdlets, I couldn’t help but feel that some changes were in order. So, with that knowledge, let’s review a few Region-specific cmdlets and touch on some potential changes, as they make the most sense to me.

Get-AWSRegion: “Returns the set of available AWS regions.”

When the Get-AWSRegion cmdlet is invoked, it returns the Region Name (the full name), the Region (abbreviated Region Name), and whether or not the Region is the default in the shell. In this first example, you can see that the default output returns all the Regions.

Get-AWSRegion

Region         Name                      IsShellDefault
------         ----                      --------------
ap-northeast-1 Asia Pacific (Tokyo)      False
ap-northeast-2 Asia Pacific (Seoul)      False
ap-south-1     Asia Pacific (Mumbai)     False
ap-southeast-1 Asia Pacific (Singapore)  False
ap-southeast-2 Asia Pacific (Sydney)     False
ca-central-1   Canada (Central)          False
eu-central-1   EU Central (Frankfurt)    False
eu-west-1      EU West (Ireland)         False
eu-west-2      EU West (London)          False
eu-west-3      EU West (Paris)           False
sa-east-1      South America (Sao Paulo) False
us-east-1      US East (Virginia)        False
us-east-2      US East (Ohio)            False
us-west-1      US West (N. California)   False
us-west-2      US West (Oregon)          False

Wrong. Be sure to take a look at the examples further below. As you’ll see there are a couple of parameters—IncludeChina and IncludeGovCloud—that add some Regions that aren’t there by default. I’m not suggesting a change here, mostly, but Get-AWSRegion should return all the Regions, right?

Based on the cmdlet name alone, I suspected that all Regions were going to be listed in the output that’s returned. Good thing I looked into this cmdlet’s parameters, instead of assuming that the Regions were actually all included. And why China? I get why we might make an exception for GovCloud—we shouldn’t—but what was the thought in regard to China? You’ll see what I mean in the following examples.

(Get-AWSRegion | Measure-Object).Count
15
(Get-AWSRegion -IncludeChina | Measure-Object).Count
17
(Get-AWSRegion -IncludeChina -IncludeGovCloud | Measure-Object).Count
18

Now, let’s take a look at the SystemName parameter included in Get-AWSRegion. This is where it becomes quickly evident to me that we can definitely do better. Why does the Region property use a parameter called SystemName? I think maybe that parameter needs a Region parameter alias, at minimum.

Get-AWSRegion -SystemName us-west-1

Region    Name                    IsShellDefault
------    ----                    --------------
us-west-1 US West (N. California) False

Get-AWSRegion -SystemName us-west-2

Region    Name             IsShellDefault
------    ----             --------------
us-west-2 US West (Oregon) False

Get-AWSRegion -SystemName us-west-*

Region    Name    IsShellDefault
------    ----    --------------
us-west-* Unknown False

I didn’t spend any time reading the help for Get-AWSRegion, but as you can see directly above, the wildcard character isn’t supported with the SystemName parameter. That’s too bad. That would’ve been a great addition to this cmdlet (and one that can still be added). To use a wildcard character against this value, you’re required to pipe your output to the Where-Object cmdlet and therefore, filter it further down the pipeline. Yes, this does mean that all results are piped to Where-Object, whereas a wildcard character built in to Get-AWSRegion, would filter immediately and avoid the need for the pipeline. The pipeline is crucial to the language, but when it’s not needed, we’re better off.

Get-AWSRegion | Where-Object Region -like 'us-west-*'

Region    Name                    IsShellDefault
------    ----                    --------------
us-west-1 US West (N. California) False
us-west-2 US West (Oregon)        False

And if you’re wondering, Get-AWSRegion doesn’t include a Name parameter, so there’s no checking there for the ability to use wildcards.

Get-DefaultAWSRegion: “Returns the current default AWS region for this shell, if any, as held in the shell variable $StoredAWSRegion.”

This command’s purpose, as indicated, returns the Region the AWSPowerShell module’s commands should use by default. If the command returns nothing, then a default hasn’t been set. If it does, then someone has likely already used the command we’ll discuss after Get-DefaultAWSRegion: Set-DefaultAWSRegion.

Get-DefaultAWSRegion
# Nothing here yet...

Before we move on, the commands that include the string “Default” before “AWS,” should have instead maintained the same noun prefix as Get-AWSRegion. That’s right, each of these three cmdlets should’ve included the verb, the dash, and then the string AWS, before the remaining portion of the included nouns. Why in the world would we stuff “AWS” into the middle of some command names and at the beginning of others? Amazon should have maintained a consistent prefix. Every Microsoft Active Directory command uses an AD prefix, right after the dash. You’ll never find it anywhere else. Even in the office, the prefix we use on our self-written functions is right where we’ve been trained to expect it:

<ApprovedVerb>-<Dept><SingularNoun(s)InCamelCase>

In my experience, AWS isn’t afraid of using command aliases—so one command can resolve to another—discontinuing the use of parameter names at times, and changing cmdlet names altogether. Therefore, I suspect someone needs to revisit these three. It’s not like Get-AWSRegionDefault, Set-AWSRegionDefault, and Clear-AWSRegionDefault don’t make sense and are already being used. The current commands should be aliases to these three, keeping the prefixes in the proper place.

Get-DefaultAWSRegion -> Get-AWSRegionDefault
Set-DefaultAWSRegion -> Set-AWSRegionDefault
Clear-DefaultAWSRegion -> Clear-AWSRegionDefault

While we’re here, we need to stop using the plural form of any nouns. That said, I do recognize this move is happening, such that Get-AWSCredentials is an alias that resolves to Get-AWSCredential. Oh look at that, the AWS prefix is in the right place on those two!

Set-DefaultAWSRegion: “Sets a default AWS region system name (e.g. us-west-2, eu-west-1 etc) into the shell variable $StoredAWSRegion. AWS cmdlets will use the value of this variable to satisfy their -Region parameter if the parameter is not specified.”

The first thing to notice here is that we have a Region parameter. That’s what Get-AWSRegion should’ve been included (in addition to SystemName [as not to create a breaking change]). Maybe make Region the actual parameter, and SystemName an alias to the Region parameter. That sounds like the best way to phase out that parameter.

Set-DefaultAWSRegion -Region ca-central-1
Get-DefaultAWSRegion

Region       Name             IsShellDefault
------       ----             --------------
ca-central-1 Canada (Central) True 

Clear-DefaultAWSRegion: “Clears any default AWS region set in the shell variable $StoredAWSRegion.”

Get-DefaultAWSRegion
Region       Name             IsShellDefault
------       ----             --------------
ca-central-1 Canada (Central) True
Clear-DefaultAWSRegion
Get-DefaultAWSRegion
# Nothing here again.

This evening we covered some Region cmdlets from the AWSPowerShell module. They all do what they should in the end, but in my mind, there’s some room for some changes for consistency’s sake and overall improvement. Perhaps we’ll do this again… there are some credential-related AWS cmdlets I’m going to need to learn once. and. for. all.

Get the AWS Noun Prefixes

One of the nice things about the Get-AWSPowerShellVersion cmdlet is the ListServiceVersionInfo switch parameter. It returns properties for the Service (as in the AWS Service offering name), the Noun Prefix, and API Version. Yes, there really are spaces in those last two property names; however, they’ve been fixed in my function. I had been hoping for an easier way to determine the prefixes used in the AWS cmdlets, and here we have it. I actually considered parsing cmdlet names myself, so a huge thanks to AWS, for making sure that wasn’t necessary.

It’s almost as though this should have been its own cmdlet—a get the version cmdlet and a get the noun prefixes cmdlet. Therefore, I’ve wrapped this command and its parameter in a quick and easy-to-use function for my user base. Copy, paste, and try it out; it’s all yours. Super simple.

Function Get-AWSNounPrefix {
<#
.SYNOPSIS
    The function returns the AWS PowerShell cmdlet noun prefixes, along with the corresponding AWS Service.

.DESCRIPTION
    The function returns the AWS PowerShell cmdlet noun prefixes, along with the corresponding AWS Service name. This function utilizes the AWSPowerShell module's Get-AWSPowerShellVersion function.

.EXAMPLE
    -------------------------- EXAMPLE 1 --------------------------
    PS > Get-AWSNounPrefix
    This examples returns all the noun prefixes and their corresponding AWS Service name.

.EXAMPLE
    -------------------------- EXAMPLE 2 --------------------------
    PS > Get-AWSNounPrefix | Where-Object NounPrefix -match 'cf'
    NounPrefix Service            APIVersion
    ---------- -------            ----------
    CF         Amazon CloudFront  2016-09-29
    CFG        AWS Config         2014-11-12
    CFN        AWS CloudFormation 2010-05-15

    This example uses the Where-Object cmdlet and the Match operator to filter the results by the NounPrefix.

.EXAMPLE
    -------------------------- EXAMPLE 3 --------------------------
    PS > Get-AWSNounPrefix | Where-Object Service -like '*formation*'
    NounPrefix Service            APIVersion
    ---------- -------            ----------
    CFN        AWS CloudFormation 2010-05-15

    This example uses the Where-Object cmdlet and the Like operator to filter the results by the service name.

.EXAMPLE
    -------------------------- EXAMPLE 4 --------------------------
    PS > Get-AWSNounPrefix | Where-Object -Property APIVersion -like '2016*' | Sort-Object -Property APIVersion -Descending
    NounPrefix Service                          APIVersion
    ---------- -------                          ----------
    SMS        Amazon Server Migration Service  2016-10-24
    BGT        AWS Budgets                      2016-10-20
    CF         Amazon CloudFront                2016-09-29
    EC2        Amazon Elastic Compute Cloud     2016-09-15
    SNOW       AWS Import/Export Snowball       2016-06-30
    CGIP       Amazon Cognito Identity Provider 2016-04-18
    INS        Amazon Inspector                 2016-02-16
    AAS        Application Auto Scaling         2016-02-06
    MM         AWS Marketplace Metering         2016-01-14
    DMS        AWS Database Migration Service   2016-01-01

    This example uses the Where-Object, and Sort-Object cmdlet, to find services updated in 2016 and sorts by the most recently added and updated.

.NOTES
    Name: Get-AWSNounPrefix
    Author: Tommy Maynard
    Comments: Current version of AWSPowerShell module at the first, last edit: 3.3.20.0.
    Last Edit: 11/18/2016
    Version 1.0
#>
    [CmdletBinding()]
    Param (
    )

    Begin {
        $AWSServices = Get-AWSPowerShellVersion -ListServiceVersionInfo | Sort-Object -Property 'Noun Prefix'
    } # End Begin.

    Process {
        Foreach ($AWSService in $AWSServices) {
            [PSCustomObject]@{
                NounPrefix = $AWSService.'Noun Prefix'
                Service = $AWSService.Service
                APIVersion = $AWSService.'API Version'
            }
        } # End Foreach.
    } # End Process.

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

 

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.

More AWS PowerShell Changes Due to Twitter (and Me)

It happened again. My Tweet, linked to a post here, became the catalyst for a change in the AWS PowerShell module. I’m 2 for 2. This time, AWS didn’t simply correct the spelling of a parameter name; they wrote a new cmdlet. Remove-EC2Instance works as advertised. It’ll prompt for confirmation before terminating an EC2 instance unless the Force parameter is also included with the command. It is flawless. You can always get the newest AWSPowerShell module at http://aws.amazon.com/powershell.

What this didn’t do, however, is fix Stop-EC2Instance. Steve, the individual that spotted this and my last AWS Tweet, and likely made these corrections and additions, said they couldn’t remove the malfunctioning Terminate parameter from Stop-EC2Intance. For those that didn’t read my last AWS post, the Terminate parameter never prompted for confirmation before terminating an instance (unless a change has been made since Remove-EC2Instance was released).

I read an interesting article recently that indicated to use termination protection:

Use termination protection for non-auto-scaling instances. Thank me later.
If you have any instances which are one-off things that aren’t under auto-scaling, then you should probably enable termination protection, to stop anyone from accidentally deleting the instance. I’ve had it happen, it sucks, learn from my mistake!”

While it was still quite amazing to get a second change made to the AWSPowerShell module, I’d recommend that everyone continue to be careful with Stop-EC2Instance. Perhaps you can write your own wrapper function, with the same name as the cmdlet (and therefore, it’ll be the default when Stop-EC2Instance is called), that forces confirmation. Then again, you can just use Remove-EC2Instance now. We’ll hope that when people look to terminate an EC2 instance, they find Remove-EC2Instance before they find the Terminate parameter in Stop-EC2Instance, such as I would have, had it previously existed. 🙂

AWS Stop-EC2Instance Needs Complimentary Cmdlet

Well, it’s time to try this again: Can a Tweet get another change to the AWSPowerShell module? Maybe you’ve read a recent post about an inconsistent parameter name between the same parameter, in two corresponding cmdlets. One tweet later and that was on its way to being fixed. You can read about that here: http://tommymaynard.com/twitter-reply-hashtag-aws-tweet-prompts-fix-to-awspowershell-module-2016.

I get that a tweet probably isn’t the preferred method of reporting problems, but it worked before, so why shouldn’t it again? That said, if there is a preferred method, then I’d be happy to use that, although it doesn’t make for as good a story.

So, here’s where we need a fix. The Stop-EC2Instance cmdlet includes a Terminate switch parameter that indicates it will terminate the instance (think, remove) in addition to stopping it. It works; it really does terminate the instance. The problem here is that the help file indicates it will prompt the user for confirmation unless the Force parameter is also included. Nope; it doesn’t prompt. Not for me at least. It does terminate the instance, however, and it does so with no questions asked. It didn’t get me, but it might ruin someone’s day someday.

I didn’t try it, but it seems that there’s some built-in protection, although it’s not the default.

aws-stop-ec2instance-needs-complimentary-cmdlet01

So you can see it yourself, here are two PowerShell commands I ran against two different EC2 instances. In the first command, I simply stop an EC2 instance. In the second, I added the Terminate switch. As I mentioned, it works, but there wasn’t a confirmation prompt. This was tested on AWS Tools for Windows PowerShell 3.1.66.0 and the newest version as of this writing, 3.1.71.0.

aws-stop-ec2instance-needs-complimentary-cmdlet02

aws-stop-ec2instance-needs-complimentary-cmdlet03

My suggestion to resolve this problem is to remove the Terminate switch from the Stop-EC2Instance cmdlet, and make a Remove-EC2Instance cmdlet. Maybe alias it with Terminate-EC2Instance, as Terminate isn’t an approved verb. In fact, when I first wanted to remove an EC2 instance, I started by hunting for a Remove-EC2Instance cmdlet. Tracking down the Terminate switch of Stop-EC2Instance was the last place I would’ve looked. It seems to go against all PowerShell conventions. Does Stop-Transcript have an option to remove a transcript as part of its usage? Does Stop-VM in Hyper-V delete the VM? Does Stop-Cluster include a way to delete the cluster? No. Terminating an instance shouldn’t be a part of stopping an instance unless Stop-EC2Instance can be piped to Remove-EC2Instance. Think about the safety that a separate cmdlet would provide (and it’s not like the AWS Tools team is shy about adding new cmdlets).

There are a couple of cmdlets out there that can be used to get the metadata from a compiled PowerShell cmdlet. We use it as a part of writing proxy functions. By using this, I was able to determine the reason why it doesn’t prompt. The problem, as best I can tell, is that Stop-EC2Instance‘s ConfirmImpact CmdletBinding attribute is set to Medium and the default value of the $ConfirmPreference variable is High.

aws-stop-ec2instance-needs-complimentary-cmdlet04

This means that no matter how the Stop-EC2Instance cmdlet is run, it’s never going to prompt for confirmation to terminate, unless someone has modified their $ConfirmPreference value to Medium, and that’s not likely at all. Since a cmdlet, or advanced function, can’t have more than one ConfirmImpact argument, we need the terminate functionality removed from Stop-EC2Instance and the development of a Remove-EC2Instance cmdlet. For comparison, here’s the CmdletBinding attribute for the Remove-ADGroupMembership Active Directory cmdlet. This one always prompts unless we include -Confirm:$false.

aws-stop-ec2instance-needs-complimentary-cmdlet05

I get this fix is much more involved than the last one I brought up (changing the name of a parameter), and would require more work. Additionally, it’s probably one of those things that have to be discussed among the AWS Tools team members and management. Before I close, I should make sure everyone knows that AWS is still new to me, and as such, there may be things that weren’t considered, or known, when this post was written.

Thanks for reading, and enjoy the upcoming weekend.