Join PowerShell Hash Tables

handshakeI received a lot of feedback and interest in my ConvertTo-Hashtable function. One question I received was “Why?” Well, one reason might be that you want to combine two objects into a single object. Joining them as two hashtables makes this an easier process.

First off, combining two hashtables is as simple as adding them together.


PS C:\> $a=@{Name="Jeff";Count=3;Color="Green"}
PS C:\> $b=@{Computer="HAL";Enabled=$True;Year=2020}
PS C:\> $a+$b

Name Value
---- -----
Name Jeff
Color Green
Year 2020
Computer HAL
Count 3
Enabled True

This works fine as long as there are no duplicate keys.


PS C:\> $b=@{Computer="HAL";Enabled=$True;Year=2020;Color="Red"}
PS C:\> $a+$b
+ : Bad argument to operator '+': Item has already been added. Key in dictionary: 'Color' Key bein
g added: 'Color'.
At line:1 char:4
+ $a+ <<<< $b + CategoryInfo : InvalidOperation: (:) [], RuntimeException + FullyQualifiedErrorId : BadOperatorArgument

So as part of my continued fun with hashtables, I decided to put together a function to join hashtables and handle these key conflicts.


Function Join-Hashtable {

#comment based help is here
[cmdletbinding()]
Param (
[hashtable]$First,
[hashtable]$Second,
[switch]$Force
)

#create clones of hashtables so originals are not modified
$Primary = $First.Clone()
$Secondary = $Second.Clone()

#check for any duplicate keys
$duplicates = $Primary.keys | where {$Secondary.ContainsKey($_)}
if ($duplicates) {
foreach ($item in $duplicates) {
if ($force) {
#force primary key, so remove secondary conflict
$Secondary.Remove($item)
}
else {
Write-Host "Duplicate key $item" -ForegroundColor Yellow
Write-Host "A $($Primary.Item($item))" -ForegroundColor Yellow
Write-host "B $($Secondary.Item($item))" -ForegroundColor Yellow
$r = Read-Host "Which key do you want to KEEP [AB]?"
if ($r -eq "A") {
$Secondary.Remove($item)
}
elseif ($r -eq "B") {
$Primary.Remove($item)
}
Else {
Write-Warning "Aborting operation"
Return
}
} #else prompt
}
}

#join the two hash tables
$Primary+$Secondary

} #end Join-Hashtable

The function takes two hash tables and attempts to join them together. Because I might be removing duplicate keys, I didn't want to change the original hashtables so I first create clones.


$Primary = $First.Clone()
$Secondary = $Second.Clone()

If I didn't, removing a key from $Primary would also remove it from the original hashtable. Next, I check to see if any of the keys from the first hashtable are also in the second hashtable.


$duplicates = $Primary.keys | where {$Secondary.ContainsKey($_)}

If there are none, I simply add the two hashtables together and write the result to the pipeline. But if there are duplicates, and there could be more than one, the function will prompt you for which one to keep and then remove the item from the other hashtable.


Write-Host "Duplicate key $item" -ForegroundColor Yellow
Write-Host "A $($Primary.Item($item))" -ForegroundColor Yellow
Write-host "B $($Secondary.Item($item))" -ForegroundColor Yellow
$r = Read-Host "Which key do you want to KEEP [AB]?"
if ($r -eq "A") {
$Secondary.Remove($item)
}
elseif ($r -eq "B") {
$Primary.Remove($item)
}

Once the duplicates are removed, I can go ahead and add the two together. But since I might not always want to be prompted, I added a -Force parameter which will keep any duplicates from the first hashtable without any prompting. So now, I can combine hashtables like this:


PS C:\> join-hashtable $a $b
Duplicate key Color
A Green
B Red
Which key do you want to KEEP [AB]?: a

Name Value
---- -----
Name Jeff
Color Green
Year 2020
Computer HAL
Count 3
Enabled True

Or force the first hashtable.


PS C:\> join-hashtable $a $b -Force

Name Value
---- -----
Name Jeff
Color Green
Year 2020
Computer HAL
Count 3
Enabled True

But $a and $b are never modified. Now to combine things.


$os = get-wmiobject win32_operatingsystem |
Select Caption,@{Name="computername";Expression={$_.CSName}},
OSArchitecture,ServicePackMajorVersion | ConvertTo-HashTable

$cs = Get-WmiObject win32_computersystem |
Select Manufacturer,Model,TotalPhysicalMemory |
ConvertTo-HashTable

New-object -TypeName PSObject -Property (Join-Hashtable $os $cs)

TotalPhysicalMemory : 8577851392
Manufacturer : TOSHIBA
computername : SERENITY
OSArchitecture : 64-bit
Model : Qosmio X505
Caption : Microsoft Windows 8 Pro
ServicePackMajorVersion : 0

I took two different WMI classes and created a single object. Yes, I realize there are any number of ways of accomplishing the same task. In this case I could have also simply run $os+$cs because I knew ahead of time there would be no conflicting keys. Here's a variation for PowerShell 3.0.


$exclude = "__*","Scope","Path","Options","ClassPath","*Properties","Qualifiers"

$os = get-wmiobject win32_operatingsystem |
Select * -ExcludeProperty $exclude | ConvertTo-HashTable -NoEmpty

$cs = get-wmiobject win32_computersystem |
Select * -ExcludeProperty $exclude | ConvertTo-HashTable -NoEmpty

$final = [pscustomobject](Join-Hashtable $os $cs -Force)

This gets all properties from the 2 WMI classes, except for the exceptions, that have a value. I then combine the two and create a new object. If there are any conflicts the $os hashtable will "win".

I'm sure there are other situations where you might want to convert objects to hashtables, or join hashtables. I hope you'll let me know how you use these tools. In the meantime, download Join-Hashtable. It should work in PowerShell 2.0 or 3.0.

Convert PowerShell Object to Hashtable Revised

squarepatternA while back I posted an advanced PowerShell function that would take an object and convert it to a hashtable. The premise was simple enough: look at the incoming object with Get-Member to discover the property names then create a hashtable with each property name as a hashtable key. I’ve a need to use this over the last few weeks and realized the function could use a little updating.

The new version works like the old, with the addition of one new parameter. I decided I wanted a way to exclude properties from the hashtable. Of course this presumes you know in advance about the object you are working with and what properties you wish to exclude. This is the core segment of code, which has also been revised over version 1.

In the first version I was duplicating some code and I generally try to avoid that. In this snippet $names is the collection of property names and $Inputobject is the piped in object. Armed with this tool I can create hashtables from objects with just the properties I want.

This version should work in PowerShell 2.0 or 3.0. Download ConvertTo-Hashtable2.

Rename Hashtable Key

talkbubbleI use hashtables quite a bit. Often generating hashtables on the fly from other sources. But sometimes the hashtable keys that come from these external sources don’t align with what I intend to do with the hashtable. For example, one of the nifty things you can do with hashtables is splat them against a cmdlet or advanced function. Each hashtable key will bind to its corresponding parameter name. This makes it very easy to run a command and pass a bunch of parameters all at once. Here’s an example of splatting in action.


PS C:\> $hash = @{Computername="Serenity";Class="AntivirusProduct";Namespace="root\securitycenter2"}
PS C:\> get-wmiobject @haSH

__GENUS : 2
__CLASS : AntiVirusProduct
__SUPERCLASS :
__DYNASTY : AntiVirusProduct
__RELPATH : AntiVirusProduct.instanceGuid="{D68DDC3A-831F-4fae-9E44-DA132C1ACF46}"
__PROPERTY_COUNT : 6
__DERIVATION : {}
__SERVER : SERENITY
__NAMESPACE : ROOT\securitycenter2
__PATH : \\SERENITY\ROOT\securitycenter2:AntiVirusProduct.instanceGuid="{D68DDC3A
-831F-4fae-9E44-DA132C1ACF46}"
displayName : Windows Defender
instanceGuid : {D68DDC3A-831F-4fae-9E44-DA132C1ACF46}
pathToSignedProductExe : %ProgramFiles%\Windows Defender\MSASCui.exe
pathToSignedReportingExe : %ProgramFiles%\Windows Defender\MsMpeng.exe
productState : 397568
timestamp : Fri, 04 Jan 2013 17:09:42 GMT

This works because all of the hashtable keys line up with parameter names. If the names don’t match they are ignore. So sometimes I need to rename the hashtable key. This isn’t too difficult conceptually.

1. Create a new entry with the new key name and the old value
2. Remove the existing key.

But, I decided to turn this into an advanced function, probably with more bells and whistles than I really need. But maybe you will learn something new from my efforts.


Function Rename-HashTable {
#comment based help is here

[cmdletbinding(SupportsShouldProcess=$True)]

Param(
[parameter(position=0,Mandatory=$True,
HelpMessage="Enter the name of your hash table variable without the `$")]
[ValidateNotNullorEmpty()]
[string]$Name,
[parameter(position=1,Mandatory=$True,
HelpMessage="Enter the existing key name you want to rename")]
[ValidateNotNullorEmpty()]
[string]$Key,
[parameter(position=2,Mandatory=$True,
HelpMessage="Enter the NEW key name")]
[ValidateNotNullorEmpty()]
[string]$NewKey,
[switch]$Passthru,
[ValidateSet("Global","Local","Script","Private",0,1,2,3)]
[ValidateNotNullOrEmpty()]
[string]$Scope="Global"
)

#validate Key and NewKey are not the same
if ($key -eq $NewKey) {
Write-Warning "The values you specified for -Key and -NewKey appear to be the same. Names are NOT case-sensitive"
Return
}

Try {
#validate variable is a hash table
write-verbose (gv -Scope $scope | out-string)
Write-Verbose "Validating $name as a hashtable in $Scope scope."
#get the variable
$var = Get-Variable -Name $name -Scope $Scope -ErrorAction Stop

if ( $var.Value -is [hashtable]) {
#create a temporary copy
Write-Verbose "Cloning a temporary hashtable"
<# Use the clone method to create a separate copy. If you just assign the value to $temphash, the two hash tables are linked in memory so changes to $tempHash are also applied to the original object. #>
$tempHash = $var.Value.Clone()
#validate key exists
Write-Verbose "Validating key $key"
if ($tempHash.Contains($key)) {
#create a key with the new name using the value from the old key
Write-Verbose "Adding new key $newKey to the temporary hashtable"
$tempHash.Add($NewKey,$tempHash.$Key)
#remove the old key
Write-Verbose "Removing $key"
$tempHash.Remove($Key)
#write the new value to the variable
Write-Verbose "Writing the new hashtable"
Write-Verbose ($tempHash | out-string)
Set-Variable -Name $Name -Value $tempHash -Scope $Scope -Force -PassThru:$Passthru |
Select-Object -ExpandProperty Value
}
else {
Write-Warning "Can't find a key called $Key in `$$Name"
}
}
else {
Write-Warning "The variable $name does not appear to be a hash table."
}
} #Try

Catch {
Write-Warning "Failed to find a variable with a name of $Name."
}

Write-Verbose "Rename complete"
} #end Rename-Hashtable

The function assumes you have saved the hashtable to a variable. Pass the variable name as a parameter, without the $ character. The function uses Get-Variable to retrieve the variable. Remember that variables are scope specific so I need to specify the scope to search. I’m defaulting to the global scope. Otherwise, when the function runs, it is in its own scope which doesn’t include the hashtable variable. Now, I could have simply attempted to read the variable, trusting PowerShell to read up the scope until it found it, but the best practice is to avoid referencing out of scope items.

Once the variable is found, its value is the actual hashtable which I verify as a [hashtable] object.


if ( $var.Value -is [hashtable]) {

From here I create a cloned copy of the original variable.


$tempHash = $var.Value.Clone()

By the way, you’ll notice my functions supports ShouldProcess. This means I can use -WhatIf and it will be passed to any cmdlets that also support it. At the end of my command I use Set-Variable which supports ShouldProcess so if I run my function with -WhatIf, Set-Variable will also use -Whatif. This is a long way of saying I can run the command in “test” mode without having to worry about changing the original hash table. This is also why I create a clone instead of simply setting the value of my temporary hash table to the original hash table. When you do that, the two variables are actually linked so any changes in one are made to other. Here’s an example of what I’m talking about:


PS C:\> $a = @{A=1;B=2;C=3}
PS C:\> $a

Name Value
---- -----
A 1
B 2
C 3

PS C:\> $b=$a
PS C:\> $b

Name Value
---- -----
A 1
B 2
C 3

PS C:\> $b.Add("D",4)
PS C:\> $b

Name Value
---- -----
A 1
B 2
D 4
C 3

PS C:\> $a

Name Value
---- -----
A 1
B 2
D 4
C 3

Since I want my temp copy to be “unattached”, I use the hashtable’s Clone() method. From here, it is simply a matter of creating the new key with the old value, and removing the old key with a little error handling along the way.


if ($tempHash.Contains($key)) {
#create a key with the new name using the value from the old key
Write-Verbose "Adding new key $newKey to the temporary hashtable"
$tempHash.Add($NewKey,$tempHash.$Key)
#remove the old key
Write-Verbose "Removing $key"
$tempHash.Remove($Key)
#write the new value to the variable
Write-Verbose "Writing the new hashtable"
Write-Verbose ($tempHash | out-string)
Set-Variable -Name $Name -Value $tempHash -Scope $Scope -Force -PassThru:$Passthru |
Select-Object -ExpandProperty Value
}

At the end of the process I use Set-Variable to update my original hashtable variable. By default the cmdlet doesn’t write anything to the pipeline but on the chance I might need the output, I use -Passthru and send the variable value, which is the revised hashtable, back to the pipeline. With this function I can now do something like this very easily.


PS C:\> $hash = @{name="Serenity";Class="AntivirusProduct";Namespace="root\securitycenter2"}
PS C:\> $hash

Name Value
---- -----
name Serenity
Class AntivirusProduct
Namespace root\securitycenter2

PS C:\> Rename-HashTable -Name hash -Key name -NewKey Computername
PS C:\> $hash

Name Value
---- -----
Class AntivirusProduct
Namespace root\securitycenter2
Computername Serenity

Now $hash has the right key names for splatting, or whatever else I have in mind. This function can come in handy with ConvertTo-HashTable. Download Rename-Hashtable and as always I hope you’ll let me know what you think.

Hyper-V ID Hash Table

Microsoft Hyper-VIn Hyper-V, one of the challenges (at least that I’ve run into) has to do with naming. In addition to a name, Hyper-V objects such as virtual machines, are identified with a GUID. Most of the VM-related PowerShell cmdlets will let you specify a virtual machine name. But sometimes you’ll run across the GUID and wonder what is the corresponding virtual machine. Here’s a WMI query using Get-CIMInstance that shows the currently open terminal connections to a few virtual machines.


PS C:\> get-ciminstance -Namespace root\virtualization -class msvm_terminalconnection | Select connectionID,installdate

connectionID installdate
------------ -----------
Microsoft:13876524-BFD8-40A1-95E3-926E37ACFBAB\1 1/3/2013 11:27:50 AM
Microsoft:80E967E6-57F9-4ECF-998A-D07B20B2287F\2 1/3/2013 8:12:56 AM
Microsoft:E1EDE8DA-D632-4F77-81C4-B117FFF1FE15\1 1/3/2013 8:08:35 AM

The GUID in the connection ID corresponds to a virtual machine. Here’s my approach for “translating” the GUID to a name. Every VM has an ID.


PS C:\> (get-vm chi-dc01).id

Guid
----
f7d3ad8b-6329-43a3-991e-8a630a94ec40

There is also an VMID property but that is merely an alias to ID. As you can see, the ID is a GUID object so to get just the GUID string takes another step.


PS C:\> (get-vm chi-dc01).id.guid
f7d3ad8b-6329-43a3-991e-8a630a94ec40

Now that I know how to capture this information, I can build a “lookup” object using a hash table.


PS C:\> Get-VM | Group-Object -property {$_.ID.GUID} -AsHashTable -AsString

Here’s what I end up with:

vmidhash

Naturally, it makes more sense to save this to a variable.


PS C:\> $vmhash = Get-VM | Group-Object -property {$_.ID.GUID} -AsHashTable -AsString

The VM GUID is the key and the VM object is the value. I can get items a few ways.


PS C:\> $vmhash.'13876524-bfd8-40a1-95e3-926e37acfbab'

Name State CPUUsage(%) MemoryAssigned(M) Uptime Status
---- ----- ----------- ----------------- ------ ------
CHI-FP01 Running 0 761 03:34:51 Operating normally

PS C:\> $vmhash.item('13876524-bfd8-40a1-95e3-926e37acfbab')

Name State CPUUsage(%) MemoryAssigned(M) Uptime Status
---- ----- ----------- ----------------- ------ ------
CHI-FP01 Running 0 761 03:35:11 Operating normally

PS C:\> $vmhash.GetEnumerator() | where {$_.name -match '13876524-bfd8-40a1-95e3-926e37acfbab'} | se
lect -ExpandProperty Value

Name State CPUUsage(%) MemoryAssigned(M) Uptime Status
---- ----- ----------- ----------------- ------ ------
CHI-FP01 Running 0 761 03:36:29 Operating normally

Now I can add some logic to my Get-CIMInstance command to resolve the GUID to the VM name.


$vmhash = Get-VM | Group-Object -property {$_.ID.GUID} -AsHashTable -AsString

Get-CimInstance -Namespace root\virtualization -class msvm_terminalconnection |
Select @{Name="Started";Expression={$_.installdate}},
@{Name="RunTime";Expression={(Get-Date)-$_.InstallDate}},
@{Name="VM";Expression={
#define a GUID regex
[regex]$rx="(\{){0,1}[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}(\}){0,1}"

$guid = $rx.Match($_.ConnectionID).Value
$vmhash.item($guid).Name
}}

I also tweaked a few other properties. I extracted the GUID using a regular expression and then found the corresponding entry in the hash table. I could have added a little extra logic to test if the GUID existed as a key but I decided to just plow ahead.

vmidhashlookup

The hash table makes it very easy now to resolve virtual machine GUID’s to a user friendly name.

Create PowerShell Scripts with a Single Command

One of the drawbacks to using a PowerShell script or function is that you have to write it. For many IT Pros, especially those new to PowerShell, it can be difficult to know where to start. I think more people would write their own tools if there was an easy way to automatically write them. So here’s my solution. I wrote a function called New-PSCommand that is intended as a rapid development tool for a PowerShell advanced function. It should work in either PowerShell v2 or v3.

Here’s the function, although I’m not going to spend much time explaining how it works but rather how to use it.


Function New-PSCommand {
#comment base help goes here

[cmdletbinding()]

Param(
[Parameter(Mandatory=$True,HelpMessage="Enter the name of your new command")]
[ValidateNotNullorEmpty()]
[string]$Name,
[ValidateScript({
#test if using a hashtable or a v3 [ordered] hash table
($_ -is [hashtable]) -OR ($_ -is [System.Collections.Specialized.OrderedDictionary])
})]

[Alias("Parameters")]
[object]$NewParameters,
[switch]$ShouldProcess,
[string]$Synopsis,
[string]$Description,
[string]$BeginCode,
[string]$ProcessCode,
[string]$EndCode,
[switch]$UseISE
)

Write-Verbose "Starting $($myinvocation.mycommand)"
#add parameters
$myparams=""
$helpparams=""

Write-Verbose "Processing parameter names"

foreach ($k in $newparameters.keys) {
Write-Verbose " $k"
$paramsettings = $NewParameters.item($k)

#process any remaining elements from the hashtable value
#@{ParamName="type[]",Mandatory,ValuefromPipeline,ValuefromPipelinebyPropertyName,Position}

if ($paramsettings.count -gt 1) {
$paramtype=$paramsettings[0]
if ($paramsettings[1] -is [object]) {
$Mandatory = "Mandatory=`${0}," -f $paramsettings[1]
Write-Verbose $Mandatory
}
if ($paramsettings[2] -is [object]) {
$PipelineValue = "ValueFromPipeline=`${0}," -f $paramsettings[2]
Write-Verbose $PipelineValue
}
if ($paramsettings[3] -is [object]) {
$PipelineName = "ValueFromPipelineByPropertyName=`${0}" -f $paramsettings[3]
Write-Verbose $PipelineName
}
if ($paramsettings[4] -is [object]) {
$Position = "Position={0}," -f $paramsettings[4]
Write-Verbose $Position
}
}
else {
#the only hash key is the parameter type
$paramtype=$paramsettings
}

$item = "[Parameter({0}{1}{2}{3})]`n" -f $Position,$Mandatory,$PipelineValue,$PipelineName
$item +="[{0}]`${1}" -f $paramtype,$k
Write-Verbose "Adding $item to myparams"
$myparams+="$item, `n"
$helpparams+=".PARAMETER {0} `n`n" -f $k
#clear variables but ignore errors for those that don't exist
Clear-Variable "Position","Mandatory","PipelineValue","PipelineName","ParamSettings" -ErrorAction SilentlyContinue

} #foreach hash key

#get trailing comma and remove it
$myparams=$myparams.Remove($myparams.lastIndexOf(","))

Write-Verbose "Building text"
$text=@"
Function $name {
<# .SYNOPSIS $Synopsis .DESCRIPTION $Description $HelpParams .EXAMPLE PS C:\> $Name

.NOTES
Version: 0.1
Author : $env:username

.INPUTS

.OUTPUTS

.LINK
#>

[cmdletbinding(SupportsShouldProcess=`$$ShouldProcess)]

Param (
$MyParams
)

Begin {
Write-Verbose "Starting `$(`$myinvocation.mycommand)"
$BeginCode
} #begin

Process {
$ProcessCode
} #process

End {
$EndCode
Write-Verbose "Ending `$(`$myinvocation.mycommand)"
} #end

} #end $name function

"@

if ($UseISE -and $psise) {
$newfile=$psise.CurrentPowerShellTab.Files.Add()
$newfile.Editor.InsertText($Text)
}
else {
Write $Text
}

Write-Verbose "Ending $($myinvocation.mycommand)"

} #end New-PSCommand function

The premise of this function is to take a hash table of parameter definitions and create a new PowerShell advanced function.

The hash table key is the name of your parameter and the key is its type. The only other value you need is the name for your new function. The New-PSCommand function creates a new advanced function, complete with comment-based help, and writes the text to the pipeline. That way you can either review it, pipe it to Out-File or copy it to the clipboard.


PS C:\>$paramhash=@{Name="string[]";Test="switch";Path="string"}
PS C:\> New-PSCommand -name "Set-MyScript" -Newparameters $paramhash | out-file "c:\scripts\set-myscript.ps1"

The hash table of parameters for my Set-MyScript function includes an array of strings for $Name, a string for $Path, and a switch for $Test. Here’s the output:


Function Set-MyScript {
<# .SYNOPSIS .DESCRIPTION .PARAMETER Path .PARAMETER Name .PARAMETER Test .EXAMPLE PS C:\> Set-MyScript

.NOTES
Version: 0.1
Author : Jeff

.INPUTS

.OUTPUTS

.LINK
#>

[cmdletbinding(SupportsShouldProcess=$False)]

Param (
[Parameter()]
[string]$Path,
[Parameter()]
[string[]]$Name,
[Parameter()]
[switch]$Test
)

Begin {
Write-Verbose "Starting $($myinvocation.mycommand)"

} #begin

Process {

} #process

End {

Write-Verbose "Ending $($myinvocation.mycommand)"
} #end

} #end Set-MyScript function

All you need to do is fill in the script with the code you want to run. New-PSCommand does all of the grunt work for you leaving you just the fun part. I also support an alternate hashtable setup if you want to specify some parameter attributes. Create a hash table using this format:


@{ParamName="type[]",Mandatory,ValuefromPipeline,ValuefromPipelinebyPropertyName,Position}

Here’s an example:

$h = @{Name="string[]",$True,$True,$False,0;
Path="string",$false,$false,$false,1;
Size="int",$false,$false,$true;
Recurse="switch"
}

I also let you specify commands to use in the Begin, Process and End scriptblocks. You can also define values for the help synopsis and description.

The last little bell on this tool is that if you run it in the PowerShell ISE, you can use the -UseISE switch which will open your new script in a new file in the ISE. This means you could open a new PowerShell tab in the ISE and run commands like this:


$hash = [ordered]@{
Name="string[]",$True,$True,$False,0
Path="string",$false,$false,$false,1
PixieDust="int",$false,$false,$true
NoUnicorn="switch"
}

$begin={
#initialize some variables
$arr=@()
$Love=$True
$ring=1
}

$end="write-host 'Finished' -foreground Green"
$synopsis = "Get magical user data"
$desc = @"
This command will do something really amazing and magical. All you need to do is provide
the right amount of pixie dust and shavings from a unicorn horn.

This requires PowerShell v4 and a full moon.
"@

. C:\scripts\New-PSCommand.ps1
New-PSCommand -Name Get-UserData -NewParameters $hash -BeginCode $begin -EndCode $end -Synopsis $synopsis -Description $desc -UseISE

By the way, I’m running PowerShell v3 so I can use a [ordered] hash table which I encourage you to use if you can. When executed, I get a new script in the ISE ready for me to finish.


Function Get-UserData {
<# .SYNOPSIS Get magical user data .DESCRIPTION This command will do something really amazing and magical. All you need to do is provide the right amount of pixie dust and shavings from a unicorn horn. This requires PowerShell v4 and a full moon. .PARAMETER Name .PARAMETER Path .PARAMETER PixieDust .PARAMETER NoUnicorn .EXAMPLE PS C:\> Get-UserData

.NOTES
Version: 0.1
Author : Jeff

.INPUTS

.OUTPUTS

.LINK
#>

[cmdletbinding(SupportsShouldProcess=$False)]

Param (
[Parameter(Position=0,Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$False)]
[string[]]$Name,
[Parameter(Position=1,Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelineByPropertyName=$False)]
[string]$Path,
[Parameter(Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelineByPropertyName=$True)]
[int]$PixieDust,
[Parameter()]
[switch]$NoUnicorn
)

Begin {
Write-Verbose "Starting $($myinvocation.mycommand)"

#initialize some variables
$arr=@()
$Love=$True
$ring=1

} #begin

Process {

} #process

End {
write-host 'Finished' -foreground Green
Write-Verbose "Ending $($myinvocation.mycommand)"
} #end

} #end Get-UserData function

I hope that a tool like this helps cut down on your development time. Please download New-PSCommand and let me know what you think.