James O'Neill's blog

Windows Platform, Virtualization and PowerShell with a little Photography for good measure.

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

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

  • Comments 3
  • Likes

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 

 

Comments
  • In which we see how to set the number of CPUs I started with getting MSVM Computer System objects - which

  • In an earlier post in this series (several posts ago now) I showed how I could use the Msvm_virtualSystemManagementService

  • I've said a number of times that I think technical people are rarely secure in their own abilities; that

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment