Monthly Archives: September 2016

Years Too Late: My First ISE Snippet

Every time I start to write a new PowerShell function, I manually write the same block of text. No idea how many times I’ve done it, but I’ve finally decided to stop. Today, I wrote it for the last time.

$Text = @'
Function ___-_________ {
    [CmdletBinding()]
    Param (
    )

    Begin {
    } # End Begin.

    Process {
    } # End Process.

    End {
    } # End End.
} # End Function: ___-_________.
'@

I’ve known about ISE snippets for some time, but haven’t taken a minute to get my advanced function included. Well, that finally ended today. With the $Text variable assigned above, I ran the following command.

New-IseSnippet -Title BasicFunction -Description 'Basic Advanced Function.' -Text $Text -Author 'Tommy Maynard'

Well, what did this just do? In the most basic reply to that question, it added a new snippet — a reusable chunk of text — I can add to the ISE anytime I want. All I have to do is press Ctrl + J and select BasicFunction from the available options.

That may be all you need, but I was curious what this really did. To find out, I ran Get-IseSnippet and it returned a path — helpful.

Get-IseSnippet

    Directory: C:\Users\tommymaynard\Documents\WindowsPowerShell\Snippets

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----        9/27/2016   0:32 PM            867 BasicFunction.snippets.ps1xml

Then I ran Get-Content on the file to see what it was storing.

Get-Content -Path (Get-IseSnippet).FullName

<?xml version='1.0' encoding='utf-8' ?>
    <Snippets xmlns='http://schemas.microsoft.com/PowerShell/Snippets'>
        <Snippet Version='1.0.0'





<Header>
                <Title>BasicFunction</Title>
                <Description>Basic Advanced Function.</Description>
                <Author>Tommy Maynard</Author>
                <SnippetTypes>
                    <SnippetType>Expansion</SnippetType>
                </SnippetTypes>
            </Header>






            <Code>
                <Script Language='PowerShell' CaretOffset='0'>
                    <![CDATA[Function ___-_________ { [CmdletBinding()] Param ( ) Begin { } # End Begin. Process { } # End Process. End { } # End End. } # End Function: ___-_________.]]>
                </Script>
            </Code>

    </Snippet>
</Snippets>

So, there it is. Not only do I have my basic function snippet the next time I need it, I know that New-IseSnippet is writing an XML (.ps1xml) file to my Snippets directory in my Documents/WindowsPowerShell directory in my local profile. The date on my snippet and the directory indicate they were both created when I ran this command. I told you I hadn’t used snippets before.

Me being me, I ran Get-Command against New-IseSnippet and guess what? It’s a function; it’s not compiled code. Let’s take a look at it; let’s find where it decides whether to create the directory, or not.

Get-Command -Name New-IseSnippet

CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Function        New-IseSnippet                                     1.0.0.0    ISE

(Get-Command -Name New-IseSnippet).ScriptBlock

While I didn’t include the entire results of the last command, I’ll include what’s important for how it determined whether or not to create the Snippets directory. In this first part, the function creates a $snippetPath variable. In it, it stores the current user’s WindowsPowerShell directory path. Before writing that to the variable, Join-Path appends “Snippets” — the child path — to the end. That means that in the end, the $snippetPath variable contains C:\Users\tommymaynard\Documents\WindowsPowerShell\Snippets.

$snippetPath = Join-Path (Split-Path $profile.CurrentUserCurrentHost) "Snippets"

In this section of the function, it runs Test-Path against $snippetPath, to determine if the path exists, or not. This cmdlet returns $true or $false depending on whether the path exists.

if (-not (Test-Path $snippetPath))
{
    $null = mkdir $snippetPath
}

If the path doesn’t exist, thanks to the -not, it executes the mkdir function against the path, and the directory is created. The next time New-IseSnippet function is run, the directory will already exists and this part of the function won’t be run.

Well, that’s it. I’m already looking forward to pressing Ctrl + J the next time I need to start a new advanced function.

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— 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 I’ll take any help I can get in retaining this information. 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. It’s the 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 in 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 it 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.
}

PSMonday #22: September 26, 2016

Topic: Get-Member Continued IV

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

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

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

1
String1
2
String2

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

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

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

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

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

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

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

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

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

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

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

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

Linux Prompt on Windows – Part IV

Sometime ago I wrote a Linux lookalike prompt. Since then, I’ve continued to modify it as I decided it needed changes. Today, I have a newer version, so I figured I should drop it here, as I have previously.

The difference in this version is that it adds a / between the c (C:\ drive) and ~ when I’m in my “C:\users\tommymaynard” directory, or somewhere further nested in this directory. So yeah, as of today, Monday, September 19, 2016, this is the newest version.

So you can see it before you buy it — it’s actually free — here’s a few examples of what the prompt will look like based on your location within the file system. It updates the ConsoleHost and ISE’s Window Title, too. Bonus.

# C drive: C:\
[tommymaynard@testsrv01 c/]$ 


# WSMan drive: WSMan:\
[tommymaynard@testsrv01 wsman/]$


# WSMan localhost: WSMan:\localhost
[tommymaynard@testsrv01 wsman/localhost]$


# Users folder: C:\Users
[tommymaynard@testsrv01 c/users]$ 


# Profile folder: C:\Users\tommymaynard
[tommymaynard@testsrv01 c/~]$ 


# Desktop folder: C:\Users\tommymaynard\Desktop
[tommymaynard@testsrv01 c/~/Desktop]$ 


# ProgramData folder: C:\ProgramData
[tommymaynard@testsrv01 c/ProgramData]$ 

And, here’s the prompt function.

Function Prompt {
	(Get-PSProvider -PSProvider FileSystem).Home = $env:USERPROFILE

	# Determine if Admin and set Symbol variable.
	If ([bool](([System.Security.Principal.WindowsIdentity]::GetCurrent()).Groups -match 'S-1-5-32-544')) {
		$Symbol = '#'
	} Else {
		$Symbol = '$'
	}
	 
	# Write Path to Location Variable as /.../...
	If ($PWD.Path -eq $env:USERPROFILE) {
		$Location = '/~'
	} ElseIf ($PWD.Path -like "*$env:USERPROFILE*") {
		$Location = "/$($PWD.Path -replace ($env:USERPROFILE -replace '\\','\\'),'~' -replace '\\','/')"
	} Else {
		$Location = "$(($PWD.Path -replace '\\','/' -split ':')[-1])"
	}

	# Determine Host for WindowTitle.
	Switch ($Host.Name) {
		'ConsoleHost' {$HostName = 'ConsoleHost'; break}
		'Windows PowerShell ISE Host' {$HostName = 'ISE'; break}
		default {}
	}

	# Create and write Prompt; Write WindowTitle.
	$Prompt = "[$($env:USERNAME.ToLower())@$($env:COMPUTERNAME.ToLower()) $((Get-Location).Drive.Name.ToLower())$Location]$Symbol"
	$Host.UI.RawUI.WindowTitle = "$HostName`: $Prompt"
	
	"$Prompt "
}

Here are the previous posts on this topic:

http://tommymaynard.com/quick-learn-duplicate-the-linux-prompt-2016/
http://tommymaynard.com/quick-learn-an-addition-to-the-linux-powershell-promp-2016/
http://tommymaynard.com/quick-learn-an-addition-to-the-linux-powershell-prompt-ii-2016/

Update: There’s a part V (five) now. This includes an update to indicate when you’re in debug mode.

Give a Parameter a Default Value (Part III)

Part II: http://tommymaynard.com/quick-learn-give-a-parameter-a-default-value-part-ii-2015

Didn’t know I’d be back for a third installment of this topic, but the $PSDefaultParameterValues variable is still such a huge convenience. While punching out commands today, I had a thought: Can $PSDefaultParameterValues be a bit more dynamic?

I’m not going to fully introduce the $PSDefaultParameterValues again, but I’ll leave a quick explanation. This hash table allows us to instruct cmdlets and functions to use a default value for one of their parameters. Here’s a couple examples borrowed from the first two $PSDefaultParameterValues posts. To read the first two posts, follow the links at the bottom of this post. Both of these examples do the same thing, outside of the fact they’re written for different cmdlets.

$PSDefaultParameterValues.Add('Test-Connection:Count','1')
$PSDefaultParameterValues = @{'Get-Help:ShowWindow' = $true}

After our variable is set, anytime I use the Test-Connection cmdlet, it will include the -Count parameter with the parameter value of 1. Additionally, when I use Get-Help, it will always include the -ShowWindow parameter, without the need to enter it myself. The examples show how to set the variable both by using the Add method, and as a hash table — which is really all the variable is anyway.

I had a recent need to ensure the -Server parameter value on the Get-ADPrincipalGroupMembership cmdlet used the PDC Emulator. If you see the error: “The operation being requested was not performed because the user has not been authenticated, then you may need to ensure you’re using the PDCE when using this cmdlet, too. Anyway, I didn’t want to hard code the PDCE value in $PSDefaultParameterValues, so I tried something new, and you’re reading this today, because it worked. Here’s how I updated the $PSDefaultParameterValues variable to dynamically obtain the value it should use.

$PSDefaultParameterValues.Add('Get-ADPrincipalGroupMembership:Server',"$((Get-ADDomain).PDCEmulator)")
$PSDefaultParameterValues | Format-Table -AutoSize

Name                                  Value
----                                  -----
Get-ADPrincipalGroupMembership:Server DC01.mydomain.com

Now, whenever I run the Get-ADPrincipalGroupMembership cmdlet (inside the PowerShell session where the $PSDefaultParameterValues has been set), it’ll include the -Server parameter and the value of the PDCE — which ever server that is, and without the need to hard code its name. So yeah, we can use dynamic content in $PSDefaultParameterValues.

Part I: http://tommymaynard.com/quick-learn-give-a-parameter-a-default-value-2015
Part II: http://tommymaynard.com/quick-learn-give-a-parameter-a-default-value-part-ii-2015

Update: A buddy at work alerted me to the fact that pointing to the PDCE is no longer necessary for the Get-ADPrincipalGroupMembership cmdlet. Thanks, Logan!

PSMonday #21: Monday, September 19, 2016

Topic: Get-Member Continued III (and Arrays)

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.

At this point, we’ve only ever piped to the Get-Member cmdlet, but there’s times we might want to use the cmdlet differently. Before we continue, we need to understand arrays, and so we’re going to break off from Get-Member this week.

In our previous examples, we’ve typically only assigned a single value to a variable.

$a = 'dog'
$a

dog

Using an array allows us to assign multiple values to a single variable. Here’s how we do that, and how it appears when we return the variable’s newly assigned values.

$b = 'dog','cat','bird','frog'
$b

dog
cat
bird
frog

You may see arrays created using the array operator syntax, as well: @().

$b = @('dog','cat','bird','frog')

We can access each of the values in the array variable using an index. This is the position of a value within the array. Arrays are always zero-based, so the first value in the array is always at index 0.

$b[0]

dog

Now let’s return all four values. We’ll do them out of order, so you can see that the indexes really do, return the proper value in the array.

$b[3] # 4th value.
$b[0] # 1st value.
$b[2] # 3rd value.
$b[1] # 2nd value.

frog
dog
bird
cat

We’ve learned that the array index 0 and then higher, each represent the next value in the array. We can also move backwards through an array, using negative indexes. Here’s both.

$b[0]
$b[1]
$b[2]
$b[3]
'--------'
$b[-1]
$b[-2]
$b[-3]
$b[-4]

dog
cat
bird
frog
--------
frog
bird
cat
dog

This means that index [-1] is always the last value in an array, just like index [0] will always be the first. Knowing this may come in handy one day; it was for me. Next Monday, we’ll actually get back to Get-Member, now that we have some basic understanding of arrays, if you didn’t have them already.

PSMonday #20: Monday, September 12, 2016

Topic: Get-Member Continued 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.

Back to Get-Member and some experimentation with a few methods. We’ll start by using a method that I didn’t include in my shortened results last week: PadLeft. The PadLeft method will add spaces, by default, to the left side of a string. Remember, “methods are things the object can do.”

$x = '5' + '5'
$x

55

$x.PadLeft(5)

   55

A little boring, so let’s pad it with asterisks instead.

$x.PadLeft(5,'*')

***55

You might have expected to see five asterisks. Nope. The PadLeft method indicates that any space to the left of the existing string be padded up to five total spaces, to include the string itself. So, how might we ensure there’s five asterisks before the string, if that’s what we had really wanted? Think back to the string’s length from last week. Here’s that example again.

$x.Length

2

In the next example, we’ll add five to the length (two), in order to ensure we end up with five, actual asterisks.

$x.PadLeft(5 + $x.Length,'*')

*****55

Here’s another example with a different string and method. This uses the Split method to split a string on the space between each word.

"Today is everyone's favorite day!".Split(' ')

Today
is
everyone's
favorite
day!

I should mention that the above, split example should’ve been written like it is below. The default split character is the space; it doesn’t need to be included. Now, if we had opted to split on a different character, word, or combination thereof, that would’ve needed to be included inside the single quotes.

"Today is everyone's favorite day!".Split()

We’ll stop here for now and pick it up next Monday.

PSMonday #19: Monday, September 5, 2016

Topic: Get-Member Continued

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.

If nearly everything in PowerShell is an object, then let’s look at two of the members — properties and methods — of our two results, ’55’ and 10. Typically, we pipe to Get-Member. In the first example, we’ll recreate our string value of ’55’ and see what we can learn about it.

$x = '5' + '5'
$x

55

$x | Get-Member

  TypeName: System.String

In the above results, I’ve included only the TypeName of the object for now, and haven’t yet included all the properties and methods returned by Get-Member, that we’ll see momentarily. The TypeName indicates that the object is a string object.

In the more complete, Get-Member results, I’ve included the first several methods and the one and only property. As the value in $x is a string, there’s not much to it, property wise. In this case, there’s just a length property. In the below results, I haven’t include the Definition property. If you run these commands yourself, don’t be surprised to see a third property, or column, included.

  TypeName: System.String

Name             MemberType
----             ----------
Clone            Method
CompareTo        Method
Contains         Method
CopyTo           Method
EndsWith         Method
Equals           Method
GetEnumerator    Method
GetHashCode      Method
...
Length           Property

Let’s return the length property to see the length of the string stored in the variable $x. The value is ’55’, so a length of two would make sense. Let’s double-check.

$x.Length

2

Let’s recreate our numeric value of 55, as well, and pipe it to Get-Member.

$y = 5 + 5
$y

10

$y | Get-Member

  TypeName: System.Int32

Name           MemberType
----           ----------
CompareTo      Method
Equals         Method
GetHashCode    Method
GetType        Method
GetTypeCode    Method
ToBoolean      Method
ToByte         Method
ToChar         Method
ToDateTime     Method
ToDecimal      Method
ToDouble       Method
ToInt16        Method
ToInt32        Method
ToInt64        Method
ToSByte        Method
ToSingle       Method
ToString       Method
ToType         Method
ToUInt16       Method
ToUInt32       Method
ToUInt64       Method

The variable $y is storing an integer object, and according to Get-Member, doesn’t include any properties. That’s a good start for this holiday Monday. Next week, we’ll start working with some of the different methods.