Ways to tidy up my PowerShell - including making a hash of stuff

Please excuse the bad pun... When I first wrote the function I posted to display the state of virtual machines, I used a construction which has been familiar to programmers since time immemorial.

   If X=1 output this  If X=2 output that  etc

Most modern programming languages, including PowerShell, have some kind of switch construction which is a little tidier but they're still bulky...

I had put constants for each of the states in the .PS1 file which holds all my PowerShell VM functions. But this was more as a way of having a note of them than something I was going to use in my code. I could have written the Start-vm function (in the same post) like this 

    $VM.RequestStateChange($Running)

and re-coded my display function as

    switch ($_.EnabledState) { $Running {"Running"}
                              $Stopped {"Stopped"}
                         $Paused  {"Paused"}
                              etc

but it still needs a line for each state. For completely separate reasons I was looking at hash tables. It takes one line to create a hash-table of return codes:

     $VMState=@{"Running"=2 ; "Stopped"=3 ; "Paused"=32768 ; "Suspended"=32769 ; 
               "Starting"=32770 ; "Snapshotting"=32771 ; "Saving"=32773  ; "Stopping"=32774 }

So I changed the way I start and stop machines: one function does the work: expanding arrays, converting strings to computerSystem objects and actually changing the state: like this

    Function Set-VMState 
   {Param ($VM , $state)
    if ($VM -is [Array]) {$VM | ForEach-Object {Set-VMState -VM $_ -State $state} }
    if ($VM -is [String]) {$VM=(Get-VM -Machinename $VM) }
    if ($VM -is [System.Management.ManagementObject]) {$VM.RequestStateChange($State) } 
    $VM = $null
   } 

Using the hash Table and then I have Start, Stop and Pause functions like this:

    Filter Start-VM
   {Param ($VM)
     $if ($vm -eq $null) ($vm=$_}
     Set-VMState -VM $VM -State $vmStates.running
     $VM = $Null
   }    

I also made a change to accept input from the pipe e.g . Get-VM "James%" | start-VM , there are two changes (a) use a FILTER instead of a FUNCTION and (b) pick up the piped input in $_ . So I've got quite a few functions where I should  change this.

[Update, I'm not sure if this is the approved way of Piping, but I quickly learned that I should add the $VM=$Null at the end, other wise when 5 items  are piped in function is run 5 times, using the first one each time.]

HashTables are a one-way lookup: $VMstates.running returns the value with a key of "Running" - 2 in the Start-VM filter.  If I have "2" and want to get back to "Running" there isn't a built in way(that I know of). However PowerShell has a GetEnumerator which dumps out the whole hash table as Key/value pairs, which that makes it easy to get the name we want.

    function Convert-VMStateID
    {Param ($ID)   
     ($vmState.GetEnumerator() | where {$_.value -eq $ID}).name 
    }

and using the choose-list function I showed before before , choose-VM becomes a one liner

 Function Choose-VM

{choose-list -data (Get-VM) -fieldList @(@{Label="VM Name"; Expression={$_.ElementName}}, 
                                         @{Label="State"; Expression={Convert-VMStateID -ID $_.EnabledState}}) }

(In principle it is a one liner ... in  practice I'm going to have a -multi switch to allow single or multiple selections.

One other thing I've done in this tidying up exercise is to make sure I name my parameters in scripts. This means I really should go back to my Choose-list function and rename the "Field list" parameter to "Property" to match Powershell's built-in cmdlets (just as I have been trying to use existing Verbs and write my nouns in the singular !).  Identifying parameters by position doesn't make for readable code:  the following two lines are equivalent, but which would you rather see in a script (not the one you'd rather type at the command line !)

 Set-VMState -VM $VM -State $vmStates.running  
Set-VMState  $VM  $vmStates.running  

 

Technorati Tags: Microsoft,Powershell,Windows Server 2008,Hyper-V