Tag Archives: Get-ADUser

Handle a Parameter Whether it’s Included or Not

I’m in the process of writing a new tool. It’s basically a wrapper function for Get-ADObject that only returns Active Directory (AD) contact objects. While there’s a Get-ADComputer cmdlet to get AD computer objects, and a Get-ADUser cmdlet to get AD user objects, there’s no specific cmdlet for contacts. There’s no surprise here really, and that’s likely why we have a Get-ADObject cmdlet. It’s for those AD objects that didn’t make the cut and get their own.

I’ve seen some discussion on this in the past: How do I handle running a cmdlet within a function when I don’t know if a parameter and parameter value will be included, or not? Let’s consider that my Get-ADContact function will run the Get-ADObject cmdlet, as seen below. In the Begin block, we create a $Parameter variable that will hold a hash table. We’ll populate it in such a way that the key Filter will have a corresponding value of {ObjectClass -eq ‘contact’}. In the Process block, we splat this hash table, currently with a single parameter and parameter value, to the Get-ADObject cmdlet.

Function Get-ADContact {
    [CmdletBinding()]
    Param (
    )

    Begin {
        # Create parameter hash table; add Filter parameter.
        $Parameters = @{Filter = {ObjectClass -eq 'contact'}}
    } # End Begin.

    Process {
        Get-ADObject @Parameters
    } # End Process.

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

There’s two other parameters included in Get-ADObject that I want to allow my users the ability to include. That’s the -SearchBase and -SearchScope parameters. You can read more by checking the help for Get-ADObject: Get-Help -Name Get-ADObject. There’s actually several AD cmdlets that also include these parameters. They can be quite helpful, so that’s why I’ve decided to include them.

Before we continue, I want to let the audience know that I am familiar with creating proxy functions, and I understand it might’ve been a better option. Maybe I’ll circle back sometime and see about replicating the functionality I’ve created here, in that manner. It might turn out this wasn’t worth writing and posting. No guarantee, but it’s possible.

Okay, back on track. Let’s add the additional lines inside the Param block, that make accepting a -SearchBase and -SearchScope parameter value possible.

Function Get-ADContact {
    [CmdletBinding()]
    Param (
        [Parameter()]
        [string]$SearchBase,

        [Parameter()]
        [ValidateSet(0,'Base',1,'OneLevel',2,'SubTree')]
        $SearchScope
    )

    Begin {
        # Create parameter hash table; add Filter parameter.
        $Parameters = @{Filter = {ObjectClass -eq 'contact'}}
    } # End Begin.

    Process {
        Get-ADObject @Parameters
    } # End Process.

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

Now, our Get-ADContact function will include the two additional parameters. Neither parameter is mandatory, but the -SearchScope parameter does include a ValidateSet parameter attribute to ensure it’ll only accept the values 0, 1, 2, Base, OneLevel, or SubTree. Base and 0 are equivalent, as are 1 and OneLevel, and 2 and SubTree.

The next thing I need to do is include the parameter values assigned to the -SearchBase and -SearchScope parameters to our $Parameters hash table when those are included. I decided to do this using the $PSBoundParameters variable, the ForEach-Object cmdlet, and the switch language construct.

Function Get-ADContact {
    [CmdletBinding()]
    Param (
        [Parameter()]
        [string]$SearchBase,

        [Parameter()]
        [ValidateSet(0,'Base',1,'OneLevel',2,'SubTree')]
        $SearchScope
    )

    Begin {
        # Create parameter hash table; add Filter parameter.
        $Parameters = @{Filter = {ObjectClass -eq 'contact'}}

        $PSBoundParameters.Keys | ForEach-Object {
            Switch ($_) {
                'SearchBase' {$Parameters += @{SearchBase = $SearchBase}; break}
                'SearchScope' {$Parameters += @{SearchScope = $SearchScope}}
            }
        }
    } # End Begin.

    Process {
        Get-ADObject @Parameters
    } # End Process.

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

All done. Now, we have function that only returns AD contact objects. Additionally, we have the option of narrowing down our search by including the often used -SearchBase and -SearchScope parameters. While I don’t doubt there’s a better way, I think this one will work for now.

Write-Output Gets Foreground and Background Colors and More

Every once in a while a forum post comes along that really captures my interest. Take this one for instance.

In this post, the creator wanted to build out a start up menu in the ConsoleHost that has a solid white line, another white line below that, with the words “PowerShell Project 1” in it, and a final white line beneath that. That might be difficult to imagine, so here’s a visual representation, and then what his/hers looked like. I had recommended to use a here-string to fix the problem, but unfortunately we didn’t have the same results.

write-output-gets-foreground-and-background-colors-and-more01
Note: While you can’t see them, in both this and the below image, there are spaces on the lines above, below, and before and after “PowerShell Project 1.” This is how we can ensure the white rectangle around the text.

write-output-gets-foreground-and-background-colors-and-more02

I can’t say for certain why theirs rendered differently, but I suspect version, or PSReadline, or possibly console font/size. That wasn’t important; what was, was trying to figure out a way around this problematic inconsistency. We have the option to change the background color of the area where we type, and so I wondered if I could temporarily do that inside the ConsoleHost. It turns out I could. With that new piece of knowledge, I set out to write a wrapper function around Write-Output. In my version — Write-TMOutput — it includes -ForegroundColor and -BackgroundColor parameters with an option to horizontally and vertically pad the text (object).

First, here’s a few images to show my new function in action. This first image shows the commands included in the TMOutput module.

write-output-gets-foreground-and-background-colors-and-more03

The next example image shows some basic, Write-Output type usage. One where we pipe to the function, and one where we don’t. Fairly standard.

write-output-gets-foreground-and-background-colors-and-more04

Now, the next example is where this starts to get fun: the incorporation of the -ForegroundColor and -BackgroundColor parameters. Prior to running these commands, and those in the last two examples, I removed the PSReadLine module so the colors would be easier to spot.

write-output-gets-foreground-and-background-colors-and-more05

Next is a demo of two additional parameters: -HorizontalPad and -VerticalPad. These will allow you to add spaces before and after the value supplied to the -InputObject parameter (the text), and add lines above and below the text. This is the part that gets back to the request in the Microsoft Technet forum post. I should mention that my solution in the forum post would be different now that I’ve spend some time writing this function — there are better ways to do things that I hadn’t considered at the time I replied to the post.

write-output-gets-foreground-and-background-colors-and-more06

The next example shows some usage with the Get-ADUser cmdlet. Keep in mind that if you combine this function with other cmdlets, that the -HorizontalPad and -VerticalPad parameters cannot be used. In my mind, they’re just a bonus to the option of using colors with Write-Output. I should mention it now, but this function was intentionally written to only work in the ConsoleHost. Perhaps I’ll add the option of using it in the ISE in the future.

write-output-gets-foreground-and-background-colors-and-more07

I’ve written an additional function that will allow you to quickly see the available colors from which you can choose. It’s called Show-TMOutputColor, and produces the results below. This might come in handy as you’re using the modified Write-Output.

write-output-gets-foreground-and-background-colors-and-more08

Now, on to the module. It’s the first time I’ve done it, but I’ve uploaded this to the PowerShell Gallery. I believe publishing to the PowerShell Gallery was one of my PowerShell New Year’s Resolutions for 2016, so there’s one down! You can use this direct link or, one better, download the module from your console, using this command:

Install-Module -Name TMOutput

Thanks for reading this post. Hopefully these function will be a useful way to get around the need for Write-Host’s foreground and background color options while working in the console. That, and maybe the TechNet thread creator can do whatever they wanted, too.

Check for Active Directory User in Function Parameters

I’m not sure what it is, but this might’ve been the longest I’ve gone without writing. It’s not that I’ve stopping thinking PowerShell, it’s that I’m so consumed, I’ve had a hard time focusing. My wife calls it my adult ADD. It was just last week that I attended my second PowerShell + DevOps Global Summit (that’s the new name). I learned a great deal and I had a great time. Thanks again to PowerShell.org for the contest and free 4-day pass, my employer for picking up the flight, lodging, and food, my parents for their help, and of course to my wife! The weather in Bellevue, WA was perfect and the summit was a huge success. Much gratitude to all those involved in the planning and execution.

It wasn’t long ago that I had a post about comparing Active Directory group memberships. I wanted a pleasant way to visualize and compare the group memberships of two AD users. We had a ticket earlier today, at the office, where I went back to that post and gathered the commands, so I could use them again. I knew I’d need it, I just didn’t know I’d need it so soon. I sent the output results as an image to a coworker, and it wasn’t but a few seconds later that he was interested in what was used to gather that output: it was my modified compare. Here’s an edited version of the image I sent. This image differs from the original post in that I added the users’ SamAccountNames as the property names in my collection. This is why they’re blurred out this time around.

check-for-active-directory-user-in-function-parameters01

I mentioned to him that I’d write the commands into a wrapper function. While that’s nearly done, I asked myself this question: When should I check if the users exist in Active Directory? Do I do it as part of the function (inside the Begin block, for instance), or do I check at the time the parameter is accepted, as part of a ValidateScript validation?

I’m here today to say that making it a part of the ValidateScript validation attribute is acceptable. While it has the potential to cause an error condition and dump red text to the console, that’s fine. People good at Windows PowerShell read these errors — they’re PowerShell’s errors, and we’re just allowing them to occur. While I can catch these in the function, PowerShell doesn’t always do this for us natively, so I as I see it, it’s not always necessary. Knowing the audience for your tool can help you with this determination. Do you want them to learn PowerShell and get comfortable with the native errors, or does it make sense to protect them from seeing these? It’s up to you. I’m okay with allowing my tools to display some of the native errors, especially since we’re talking about my team using these tools, and not a lower-tiered group. Again, you have to consider who will use your tools, and how they’ll handle the red text.

The test function I wrote, and some error results, can been seen below. In my mind, there were two error possibilities: the computer where the function is invoked doesn’t have the ActiveDirectory module, or one, or both, of the users don’t exist in Active Directory. Again, in many cases, these standard PowerShell errors can be delivered without the need to hide them from the user of the function. Here’s a test function now.

Function Test-ADUser {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory=$true,Position=0)]
        [ValidateScript({Get-ADUser -Identity $_})]
        [string]$User1,

        [Parameter(Mandatory=$true,Position=1)]
        [ValidateScript({Get-ADUser -Identity $_})]
        [string]$User2
    )

    Write-Output -InputObject "Success on both parameters."
}

This first image below, shows the error condition when the ActiveDirectory module isn’t available on the system where the function is invoked. It displays the standard, I-don’t-recognize-what-you-entered error: “Get-ADUser is not recognized…”

check-for-active-directory-user-in-function-parameters02

In this image, we can see the error message that occurs when one of the supplied users doesn’t exist in Active Directory. It won’t get this far unless the ActiveDirectory module is on the system where this function is invoked.

check-for-active-directory-user-in-function-parameters03

So in the end, consider when writing your tools, if a standard PowerShell error is acceptable or not. I think that there’s times it’s suitable, depending on the error and the group using the tool.

Only Return System.DateTime Properties from Get-ADComputer

I had one of those randomly appearing PowerShell questions last night. Windows PowerShell is a huge interest for me, so there’s really no surprise.

I wondered, How can only return the date time related properties from Get-ADComputer? It seems to happen quite often that I’ll need to view date and time information from Get-ADComputer (and Get-ADUser). It’s mildly cumbersome to scan though all the properties looking for dates and times in the property’s value — the ultimate reason behind this random thought.

The command I threw together was ugly, and seemed to lack a simpler approach. I stopped there, and decided to pass this one out to the community. Can you come with a better way?

Again, the idea is to return only the properties from Get-ADComputer that are System.DateTime properties (have System.DateTime in the Definition property of Get-Member). Take a look at my example and you might better understand my goal. I didn’t bother filtering out the default properties returned by Get-ADComputer (at first), but you’re welcome to do that, too. Cheers!

PS> Get-ADComputer -Identity SERVER01 -Properties ((Get-ADComputer -Identity SERVER01 -Properties * | Get-Member | Where-Object Definition -match 'System.DateTime').Name)

AccountExpirationDate  :
AccountLockoutTime     :
Created                : 9/12/2013 1:34:06 PM
createTimeStamp        : 9/12/2013 1:34:06 PM
DistinguishedName      : CN=SERVER01,OU=Finance,DC=mydomain,DC=com
DNSHostName            : SERVER01.mydomain.com
Enabled                : True
LastBadPasswordAttempt :
LastLogonDate          : 3/17/2014 10:35:35 AM
Modified               : 8/18/2014 11:48:34 AM
modifyTimeStamp        : 8/18/2014 11:48:34 AM
Name                   : SERVER01
ObjectClass            : computer
ObjectGUID             : 234cbaed59-1ab3-6ebc-9782-e9542bedaec
PasswordLastSet        : 3/14/2014 5:12:24 PM
SamAccountName         : SERVER01$
SID                    : S-1-5-21-174985627-956854884-123956358-942569
UserPrincipalName      :
whenChanged            : 8/18/2014 11:48:34 AM
whenCreated            : 9/12/2013 1:34:06 PM

Update1: I went ahead and edited the command so that it would not return the default Get-ADComputer properties (Name, SamAccountName, etc.), using the Select-Object cmdlet. I repeated the command I issued to the -Properties parameter of Get-ADComputer, as the value for Select-Object.

So, any takers? Can you come up with a better way to do this?

PS> Get-ADComputer -Identity SERVER01 -Properties ((Get-ADComputer -Identity SERVER01 -Properties * | Get-Member | Where-Object Definition -match 'System.DateTime').Name) | Select-Object (Get-ADComputer -Identity SERVER01 -Properties * | Get-Member | Where-Object Definition -match 'System.DateTime').Name

AccountExpirationDate  :
AccountLockoutTime     :
Created                : 9/12/2013 1:34:06 PM
createTimeStamp        : 9/12/2013 1:34:06 PM
LastBadPasswordAttempt :
LastLogonDate          : 3/17/2014 10:35:35 AM
Modified               : 8/18/2014 11:48:34 AM
modifyTimeStamp        : 8/18/2014 11:48:34 AM
PasswordLastSet        : 3/14/2014 5:12:24 PM
whenChanged            : 8/18/2014 11:48:34 AM
whenCreated            : 9/12/2013 1:34:06 PM

Update2: There’s a second update to this post. Jonathan Angliss tweeted a much cleaner solution — exactly what I had hoped someone might do. Here’s his contribution:

PS> Get-ADComputer -Identity SERVER01 -Properties * | ForEach-Object {$_.psobject.properties | Where-Object {$_.TypeNameofValue -eq 'System.DateTime'}} | Select-Object Name,Value

Invoke a Command from a Get-History Menu

Sometimes you line up the right cmdlets, appreciate what you’ve done for a moment, only to become mildly irritated that you never thought of that before. I just did that.

We all know, or should rather, that the Get-History cmdlet returns a list of commands that have been entered during the current Windows PowerShell session. If you open up a new console, enter a couple commands, and then run Get-History, or one of its aliases (ghy, h, history), it’ll show you what commands you’ve entered up until that point. Take this example, for instance:

PS> Get-Process | Select-Object -Last 2

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
    263      11     1680       5132    46     0.02   3356 WUDFHost
    227      12     1824       6804    47     0.22   3840 WUDFHost


PS> Get-Service | Select-Object -First 2

Status   Name               DisplayName
------   ----               -----------
Running  AdobeARMservice    Adobe Acrobat Update Service
Stopped  AdobeFlashPlaye... Adobe Flash Player Update Service


PS> Get-History

  Id CommandLine
  -- -----------
   1 Get-Process | Select-Object -Last 2
   2 Get-Service | Select-Object -First 2


PS> Get-History

  Id CommandLine
  -- -----------
   1 Get-Process | Select-Object -Last 2
   2 Get-Service | Select-Object -First 2
   3 Get-History

I use the up arrow quite often to cycle through my previous commands, and press Enter when I’ve found the one I want to run again. That’s one way to rerun a previously run command. Another, is to use the Get-History cmdlet. First, you use it to determine the ID of the command you want to rerun, such as we’ve done above. Then, you can run Get-History again with the -Id parameter and pipe that result to Invoke-History (aliases: ihy, r), such as we’ve done below. You can also just use Invoke-History -Id 1 without the use of Get-History.

PS> Get-History -Id 1 | Invoke-History
Get-Process | Select-Object -Last 2

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
    263      11     1680       5132    46     0.02   3356 WUDFHost
    227      12     1824       6804    47     0.22   3840 WUDFHost

What about making a “menu,” where we can select the command we want to run and press OK to run it? Easy. If you’ve been paying attention to PowerShell, then you’ve probably seen the Out-GridView cmdlet before. Let’s put it to good use, and I’ll show you the cmdlets I lined up.

PS> Get-History | Out-GridView -PassThru | Invoke-History

When I enter the command above, it will show the dialog box below; however, your dialog box will be the default size. I’ve resized mine so that the image better fits on this page. If it’s important to you, you can change what the title says by including Out-GridView’s -Title parameter: Get-History | Out-GridView -PassThru -Title ‘History Menu’ | Invoke-History.

Invoke-a-Command-from-Get-History-Menu01

After I select an option from the list, and press OK, the history item will be rerun.

PS> Get-History | Out-GridView -PassThru | Invoke-History
Get-Process | Select-Object -Last 2

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
    263      11     1680       5132    46     0.02   3356 WUDFHost
    227      12     1824       6804    47     0.22   3840 WUDFHost

PS>

Well, there it is. Something so simple and so obvious that I can’t believe I never thought of it before.

Report on Active Directory Objects in Abandoned Organizational Unit

Before we really start this post, I should mentioned that there’s no reason that the script discussed in this post can’t be run against an Organizational Unit (OU) that hasn’t been abandoned. It just worked out that I wrote the script in order to determine if an OU had be abandoned.

I threw a small script together in the last couple days and thought I’d share it. The reason for the script was because I may have had an Active Directory (AD) OU that was no longer being used. In order to determine if this was really the case, I wanted to check various properties on the user and computer objects in the OU, to include any nested OUs. These properties included the last logon time stamp, the last date the objects changed, and a few others.

The first couple of lines in the script set two different variables. The first one stores the Domain’s Distinguished Name, and the second one is assigned the location of the abandoned OU. The second variable is based partly on the first. This script requires the ActiveDirectory module and assumes it’s being run on PowerShell 3.0 or greater, as the AD module isn’t explicitly imported.

$DomainDN = ((Get-ADDomain).DistinguishedName)
$AbandonedOU = Get-ADObject -Filter * -SearchBase "OU=Finance,OU=Departments,$DomainDN"

In the next part of the script, we start to send the $AbandonedOU variable’s objects across the pipeline, to the Foreach-Object cmdlet. As each object passes across, we determine what type of AD object we’re dealing with. If it’s a user object, we set the $Command variable to the string, Get-ADUser. If it’s a computer object we set the $Command variable to the string, Get-ADComputer. If it’s neither, such as a nested OU, we’ll return to the $AbandonedOU variable and send the next object without assigning anything to the $Command variable (or running any of the upcoming code).

$AbandonedOU | ForEach-Object {
    If ($_.ObjectClass -eq 'user') {
        $Command = 'Get-ADUser'
    } ElseIf ($_.ObjectClass -eq 'computer') {
        $Command = 'Get-ADComputer'
    } Else {
        return
    }

Providing we have a user or computer AD object, we’ll run the code in the next example. This will execute the cmdlet, whether it be Get-ADUser or Get-ADComputer, returning the requested properties that we then calculate (think, customize).

    & $Command -Identity $_ -Properties * |
        Select-Object Name,
            @{N='Type';E={$_.ObjectClass}},
            @{N='Created';E={$_.whenCreated}},
            @{N='Last Logon TimeStamp';E={[datetime]::FromFileTime($_.LastLogonTimeStamp)}},
            @{N='Changed';E={$_.whenChanged}},
            @{N='Added To Domain By';E={$_.nTSecurityDescriptor.Owner}}
}

Finally, we sort the collection of objects we’ve returned and customized, and in my case, pump the data out to a CSV file at the root of my C:\ drive. As you’ll see below, I’ve included both the code in the previous example and the additional code.

    & $Command -Identity $_ -Properties * |
        Select-Object Name,
            @{N='Type';E={$_.ObjectClass}},
            @{N='Created';E={$_.whenCreated}},
            @{N='Last Logon TimeStamp';E={[datetime]::FromFileTime($_.LastLogonTimeStamp)}},
            @{N='Changed';E={$_.whenChanged}},
            @{N='Added To Domain By';E={$_.nTSecurityDescriptor.Owner}}
} | Sort-Object 'Last Logon TimeStamp' -Descending | Export-Csv -Path C:\AbandonedOU.csv -NoTypeInformation

I want to mention something about the line above that calculates the “Added To Domain By” property. In many environments this is going to only be <DOMAIN>\Domain Admins. The reason I added this, is because in the AD environment in which this ran, users, other than the Domain Admins, can join computers. I know this is a default; however, in many environments it is not allowed. This may or may not be a helpful property in your environment.

Cheers, and thanks for reading! I’ve included the complete script below.

$DomainDN = ((Get-ADDomain).DistinguishedName)
$AbandonedOU = Get-ADObject -Filter * -SearchBase "OU=Finance,OU=Departments,$DomainDN"

$AbandonedOU | ForEach-Object {
    If ($_.ObjectClass -eq 'user') {
        $Command = 'Get-ADUser'
    } ElseIf ($_.ObjectClass -eq 'computer') {
        $Command = 'Get-ADComputer'
    } Else {
        return
    }

    & $Command -Identity $_ -Properties * |
        Select-Object Name,
            @{N='Type';E={$_.ObjectClass}},
            @{N='Created';E={$_.whenCreated}},
            @{N='Last Logon TimeStamp';E={[datetime]::FromFileTime($_.LastLogonTimeStamp)}},
            @{N='Changed';E={$_.whenChanged}},
            @{N='Added To Domain By';E={$_.nTSecurityDescriptor.Owner}}
} | Sort-Object 'Last Logon TimeStamp' -Descending | Export-Csv -Path C:\AbandonedOU.csv -NoTypeInformation

Active Directory User Lookup Form

Download link at bottom of post.

I had some free time recently, so I decided I would write my first, hand-coded PowerShell form using Windows Forms. It took a bit to get started and feel comfortable, but before I knew it, I was adding some useful things to my form. My form’s purpose is to lookup users in Active Directory by their user logon name (think, SamAccountName) and return a specific set of properties — Name, Distinguished Name, Mail, Title, Department, and Office Phone.

Feel free to download, and use the form if you think it may be helpful. While there are a few things I’d like to add, and change, I think it’s a solid, version 1.0 effort. There’s a screen capture of the form in action below, and the download link is just beneath that.

In closing, I wouldn’t be surprised to find out that a tool, such as this, has already been developed and made available — I didn’t bother checking for that prior to writing this tool. I just wanted to completely write my own form in PowerShell with Windows Forms, and needed an idea.

Script Sharing - PowerShell Active Directory User Lookup Form01

Download the Active Directory User Lookup Form here: https://gallery.technet.microsoft.com/Active-Directory-User-d8ee6a0c

Give a Parameter a Default Value (Part II)

Part I: http://tommymaynard.com/quick-learn-give-a-parameter-a-default-value-2015
Part III: http://tommymaynard.com/quick-learn-give-a-parameter-a-default-value-part-iii-2016

An old co-worker and friend contacted me last week. We hadn’t chatted since the last time I was interviewing for a job — a job I have now (well, pretty close anyway). I remember telling him I wanted a job that allowed me to work with Windows PowerShell. While he probably wasn’t surprised to hear this job did just that, he might have been, to hear I had started this site — a site using my name, and all about PowerShell. It’s a pretty serious commitment to associate your name with a technology, and continue to provide new content.

He mentioned to me that he wasn’t aware of the $PSDefaultParameterValues preference variable until he read this post. No joke, but it’s exciting to know that something I wrote was helpful for him. You see, this isn’t your average intelligence IT Pro, no, this is walking, talking IT genius. You know the kind — one of the ones you’d hire first, if you opened your own consulting business. In fact, during that same first chat, I discovered that he spoke at the Microsoft Higher-Education Conference in October 2014. He’s definitely doing something right.

As last weekend passed, I occasionally thought about that $PSDefaultParameterValues post and decided I should add something more to it, especially since I highlighted what I would consider to be the less used approach for populating this variable. First off, for those that haven’t read the previous post, the $PSDefaultParameterValues variable allows you to specify a default value for a cmdlet’s, or function’s, parameter(s).

For example, if I wanted the Get-Help cmdlet to always include the -ShowWindow parameter, then I can add that default value to the cmdlet like so:

PS C:\> $PSDefaultParameterValues
PS C:\> # Proof the variable is empty.
PS C:\> $PSDefaultParameterValues.Add('Get-Help:ShowWindow',$true)
PS C:\> $PSDefaultParameterValues

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

With this value set, every time we use the Get-Help cmdlet, it will include the -ShowWindow parameter, even though I don’t specifically enter it at the time the command is run. In the previous example, we use the .Add() method to add a key-value pair; however, I suspect that most people will actually be more likely to use a hash table to add the cmdlet and parameter, and its new, default parameter value. Here’s the same example as above using the hash table option.

PS C:\> $PSDefaultParameterValue
PS C:\> # Proof the variable is empty.
PS C:\> $PSDefaultParameterValues = @{'Get-Help:ShowWindow' = $true}
PS C:\> $PSDefaultParameterValues

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

Now for the reason I led with with .Add() method: If you use the hash table option above, and the variable is already populated, then it’s going to overwrite the value it’s already storing, unless we use the += assignment operator. Follow along in the next few examples under the assumption that $PSDefaultParameterValues, is already populated from the previous example.

PS C:\> $PSDefaultParameterValues

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

PS C:\> $PSDefaultParameterValues = @{'Get-ADUser:Server' = 'MyClosestDC'}
PS C:\> $PSDefaultParameterValues

Name                           Value
----                           -----
Get-ADUser:Server              MyClosestDC

In the example above, using the = assignment operator overwrote our Get-Help ShowWindow key-value pair, as it would with any populated variable. The idea here is that we either need to assign all the parameter default values at once (when using a hash table), or use the += assignment operator to append other key-value pairs. Here’s both ways:

PS C:\> $PSDefaultParameterValues
PS C:\> # Proof the variable is empty.
PS C:\> $PSDefaultParameterValues = @{'Get-Help:ShowWindow' = $true;'Get-ADUser:Server' = 'MyClosestDC'}
PS C:\> $PSDefaultParameterValues

Name                           Value
----                           -----
Get-ADUser:Server              MyClosestDC
Get-Help:ShowWindow            True

PS C:\> $PSDefaultParameterValues = $null
PS C:\> $PSDefaultParameterValues
PS C:\> # Proof the variable is empty.
PS C:\> $PSDefaultParameterValues = @{'Get-Help:ShowWindow' = $true}
PS C:\> $PSDefaultParameterValues += @{'Get-ADUser:Server' = 'MyClosestDC'}
PS C:\> $PSDefaultParameterValues

Name                           Value
----                           -----
Get-ADUser:Server              MyClosestDC
Get-Help:ShowWindow            True

That’s all there is to it. If you’re going to use a hash table and assign values to your $PSDefaultParameterValues at different times, then be sure to use the correct assignment operator, or use the .Add() method — it’s up to you. For me, I populate this variable in my profile, so overwriting it, is something in which I’m not concerned. Thanks for reading!