Category Archives: Quick Learn

Practical examples of PowerShell concepts gathered from specific projects, forum replies, or general PowerShell use.

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.

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.

PSMonday #24: October 10, 2016

Topic: $PSDefaultParameterValues

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 have some understanding about commands, cmdlets, functions, and parameters, we can start to discuss the $PSDefaultParameterValues preference variable. This variable allows you to specify default parameter values for cmdlet or function parameters.

This is to say, that you can ensure a specific parameter value is always used for a specific parameter name, for a specific cmdlet or function, even when you don’t explicitly include them. Let’s say you use the Get-ADComputer cmdlet and you always want to use the DC01 Domain Controller to perform the lookup. That’s probably not the best idea, but without $PSDefaultParameterValues, you’d have to type the parameter each time.

Get-ADComputer -Identity membersrv05 -Server DC01

If we defined a default parameter value, we could drop the -Server DC01 from the command and it would still be included. There’s a couple different ways to add an entry to $PSDefaultParameterValues. We’ll use the Add method this week.

$PSDefaultParameterValues.Add('Get-ADComputer:Server','DC01')
$PSDefaultParameterValues

Name                           Value
----                           -----
Get-ADComputer:Server          DC01

Again, with this value set, the command below would still be certain to use DC01.

Get-ADComputer -Identity membersrv05

While we’re here, let’s also look at the Remove method. This is how we’d remove an already existing entry in the $PSDefaultParameterValues variable.

$PSDefaultParameterValues.Remove('Get-ADComputer:Server')

All gone, and all done. We’ll discuss this topic some more next Monday.

PSMonday #23: October 3, 2016

 Topic: Commands, Cmdlets, and Functions

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.

Before we jump into the next topic, I think it would be wise to ensure we discuss the differences between commands, cmdlets, and functions. In doing that, we’ll also learn, or clarify, the differences between parameters, parameter names, and parameter values.

As we’ve learned, in a roundabout way, cmdlets and functions have the potential to include parameters. Scripts in fact, can also include parameters. A parameter is made up of a parameter name and a parameter value, or values.

A cmdlet, such as Get-Service, Stop-Process, Get-EC2Instance, and Set-ADUser, is likely written in C#, and is compiled code. A function, which can be written in a simple text editor, isn’t compiled, as the source can be easily read. These include Get-Volume, Get-Verb, New-SmbShare, and many more, including those we might write ourselves using the ISE, or Visual Studio Code. Those products are both better alternatives to using a simple text editor.

Let’s take a look at a command, break down each part, and then call it a day.

Get-EventLog -ComputerName DC01,DC02 -LogName Application -Verbose

Get-EventLog
This is the command. The term command can be used for just the cmdlet or function, or a cmdlet or function that includes parameters. You might also just call it by its CommandType: cmdlet or function. I’m not completely sure if it’s been mentioned before or not, but cmdlet is pronounced command-let.

-ComputerName DC01,DC02
This is a parameter.

-ComputerName
This is the parameter name.

DC01,DC02
These are the parameter values.

-LogName Application
This is also a parameter.

-LogName
This is the parameter name.

Application
This is the parameter value.

-Verbose
Even this, is a parameter.

Before we wrap up, I want to quickly discuss the last parameter mentioned: Verbose. There are parameter names that don’t require any specified parameter value be included. These are called switch parameters. If they’re included, their parameter value — even though you can’t see it — is True ($true), and if they’re not included, their parameter value is False ($false). Therefore, their default value is always $false.

Alright, back with more next time.

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!