Dynamic parameters… OMG… Lets just say, when I first started using them, my brain melted. These things take so much effort to get going. Then the way to document them is even harder. After using PowerShell for many years, I finally figured them out and it’s time to help you out. There are many blogs out there and many posts on redit about Dynamic Paramters. All of them fall short one way or another. I am going to try and not do that today. By the end of this blog we will create a Add-SHDUserToGroup command with Two Dynamic Parameters of User and Group.

Structure

So, Dynamic Parameters is a parameter that is more fluid. You can pull the structure of Active Directory and tab through users as a parameter or you can look at the inside of a file. The limits are bound to a single column array. This means you can’t use all of the Get-ADuser command. You have to parse it out with something like select-object. We will get more into that later. First thing first. Structure. Where does a Dynamic Parameter start? It starts after the param() and before your Begin {} tag.

function Add-SHDUserToGroup {
    [cmdletbinding()]
    param ()
    DynamicParam {}
    Begin {}
    Process {}
    End {}
}

To use the Dynamic Parameters you have to have the param() tag. This tag can stay blank. As scripters we always want our functions to be advanced functions. This way we can see the verbose tags as we run our tests. Thus me, you will need to run an test more than once.

Planning Stage

Before you go any farther. You need to plan out your Dynamic Parameter. In our example, I will be working with a company that has over active 4000 users in their AD. So, a get-aduser -filter * would bring running the command to it’s knees. The object you target to gather information from will execute a command and it will effect the PS session you are in. It may even crash your powershell. I learned this the hard way when calling a script to gather every user’s name from Azure AD. I always suggest using the # and list out the commands you want to run. Study those commands and find the fastest approach. You don’t want your user waiting 5 minutes to hit tab or spend 5 minutes tabbing through options they don’t need. The name should also be reflective. In this example we will be using one a single object and not an array. So no string[].

The Setup

Lets go line by line. I found the original part of this bad boy online on github. However, where it failed was how to use it. Then I found a script that showed how to use the dynamic parameter. I was confussed, but Hopefully you will not be. The first thing we want to do is name it.

$ParamName = "User"

The next item is to create a collection of attributes. The first part of this command creates the array. Kind of like an $a =@() deal, but not. We are going to use the System > Collections > Object Model > collections and select the system attributes. Inside the object Model there are many other types of collection items. Avoid those for now.

$AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]

Now we need to create and set the parameter’s attributes. We create the attribute set with the new-object cmdlet like before. System > Management > Automation > ParameterAttribute. This is the life of the parameter’s attribute. Then we set the attribute to mandatory by adding the .Mandatory = $true flag. Then we finally add this to our AtrributeCollection object we made in the previous steps using the .Add() method.

$ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
$ParameterAttribute.Mandatory = $true
$AttributeCollection.Add($ParameterAttribute) 

Now it’s time to create the Directory. Once again, this is an object like before, thus new-object. We are living inside the System > Management > Automation world. This time we want the RuntimeDefinedParameterDictionary.

$RuntimeParameterDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary

The dirctionary has been created. Now we need to fill that dictionary up with information to select from. So, lets create an array object and fill it it up. Then we will place that object into the dictionary. This is also the important part because this is the “Dynamic” part of the dynamic parameters. We will be selecting all the enabled user. Then we create a new object for the validation set. Similar to the [validateset()] inside the parameters. Then we add the vlidateset to the attributecollection again using the .add() option.

$arrSet = (Get-ADuser -Filter {enabled -eq $true}).name
$ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($arrSet)
$AttributeCollection.Add($ValidateSetAttribute)

Now we need the run time parameter set. We do this using the new-object. This set will basically hold the object’s name, its type, and the collection. Imagine it as the [validateset(“A”,”b”,”c”)][string]$users. Once again System > management > Automation > RuntimeDefinedParameter. Name, type, and collection. Once we have made this with the previous information, we need to add it to our dictionary with an add() once again.

$RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($ParamName, [string], $AttributeCollection)
$RuntimeParameterDictionary.Add($ParamName_portgroup, $RuntimeParameter)

Finally we want to return the RunTimeParameterDictionary.

return $RuntimeParameterDictionary

Phew, that’s a lot for one parameter! However, now we have a parameter that gives us a userful list of users with their system names we can call from. How do we use that information?

Begin Tag

Inside the begin tag, lets put the parameter into something that we can use inside the process tag.

$UserName = $PsBoundParameters[$ParamName]

Process tag

Now in the process tag all we have to do is use the information above ($username) and use that instead of using the same old information over and over again. We search for the username of the user.

if ($PSBoundParameters.ContainsKey('Credential')) {
            $ReturnInfo = Get-ADUser -Filter {name -like $username} -Credential $Credential | Select-Object Samaccountname
        } else {
            $ReturnInfo = Get-ADUser -Filter {name -like $username} -Credential $Credential | Select-Object Samaccountname
        }

End Tag

We finally release the data with the $returnInfo at the end.

end {
     $ReturnInfo
}

The Script

function Get-SHDADUsername {
    [cmdletbinding()]
    param (
        [securestring]$Credential
    )
    DynamicParam {
               
        # Set the dynamic parameters' name
        $ParamName = 'Users'
        # Create the collection of attributes
        $AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
        # Create and set the parameters' attributes
        $ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
        $ParameterAttribute.Mandatory = $true
        # Add the attributes to the attributes collection
        $AttributeCollection.Add($ParameterAttribute)  
        # Generate and set the ValidateSet 
        $arrSet = (Get-ADUser -Filter *).name
        $ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($arrSet)
        # Add the ValidateSet to the attributes collection
        $AttributeCollection.Add($ValidateSetAttribute)
        # Create and return the dynamic parameter
        $RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($ParamName, [string], $AttributeCollection)
        $RuntimeParameterDictionary.Add($ParamName, $RuntimeParameter)
        return $RuntimeParameterDictionary
    }

    begin{
        
        $username = $PsBoundParameters[$ParamName] 
    }
    process {
        if ($PSBoundParameters.ContainsKey('Credential')) {
            $ReturnInfo = Get-ADUser -Filter {name -like $username} -Credential $Credential | Select-Object Samaccountname
        } else {
            $ReturnInfo = Get-ADUser -Filter {name -like $username} -Credential $Credential | Select-Object Samaccountname
        }
    }
    end {$ReturnInfo}
}