Use Copy-Property to Make it Easier to Write, Read,and Review Scripts

<Updated script on 1/1/2007 to reflect fixes suggested by James Manning> 


Dennis Verwiej is doing some great PowerShell work over at his blog Just PowerShell It at http://dverweij.spaces.live.com/ .   Recently he posted a blog entry Import Citrix Published Application in which he Imports a CSV file (previously exported) and then calls a few APIs to recreate the published application.  If you at the code (in the middle of this blog entry), it the vast bulk of it is a series of lines setting properties before calling the API.  I find that this is quite common – I end up writing tons of mind-numbing housekeeping code.  Because there is so much of it, it is often hard to tell whether it is right or not.  For instance, did you get all the properties set or did you forget a couple?


Why not leverage the special capabilities of PowerShell to make this problem go away?  Below is a routine that I use for such situations.  From its name (Copy-Property), you can guess that it copies properties from one object to another based upon the names of the properties. 


You have to deal with the situation of what to do if you try to set a property that doesn’t exist.  You can either “just continue” or you can “add that property to the object” (remember that  in PowerShell, you can add members [properties, methods, propertysets, etc] to any object type or instance).  In my routine, I add the elements which in most cases is the thing you want to do or at worst – is benign.


function Copy-Property ($From, $To, $PropertyName =”*”)
{
   foreach ($p in Get-Member -In $From -MemberType Property -Name $propertyName)
   {  trap {
         Add-Member -In $To -MemberType NoteProperty -Name $p.Name -Value $From.$($p.Name) -Force
         continue
      }
      $To.$($P.Name) = $From.$($P.Name)
   }
}


Leveraging this routine allows you to transform this code:


# First the known steps to connect to the mfcom and initialize the object 
$c_farm = new-object -com metaframecom.metaframefarm
$c_farm.initialize(1)
# Then we import the csv file and for each we do..
import-csv c:\citrix\Citrix_apps.csv | foreach{  
   $c_app=$c_farm.addapplication($_.apptype)
   $c_app.Appname = $_.appname
   $c_app.description = $_.description
   $w_app = $c_app.winappobject
   $w_app.defaultencryption = $_.defaultencryption
   $w_app.defaultinitprog = $_.defaultinitprog
   $w_app.defaultworkdir = $_.defaultworkdir
   $w_app.browsername = $_.browsername
   $w_app.enableapp = $_.enableapp
   $w_app.hidefrompnenum = $_.hidefrompnenum
   $w_app.hidefrombrowserenum = $_.hidefrombrowserenum
   $w_app.defaultwindowcolor = $_.defaultwindowcolor
   $w_app.defaultwindowtype = $_.defaultwindowtype
   $w_app.defaultsoundtype = $_.defaultsoundtype
   $w_app.desktopintegrate = $_.desktopintegrate
   $w_app.mfattributes = $_.mfattributes
   $w_app.publishingflags = $_.publishingflags
   $w_app.PNAttributes = $_.PNAttributes
   $w_app.AllowMultiInstancePerUser = $_.AllowMultiInstancePerUser
   $w_app.StartMenuFolder = $_.StartMenuFolder
   $w_app.PlaceUnderProgramsFolder = $_.PlaceUnderProgramsFolder
   $w_app.WaitOnPrinterCreation = $_.WaitOnPrinterCreation
   $w_app.defaultwindowwidth = $_.defaultwindowwidth
   $w_app.defaultwindowheight = $_.defaultwindowheight
   $w_app.defaultwindowscale = $_.defaultwindowscale
   $w_app.parentfolderDN = $_.parentfolderDN
   $w_app.pnfolder = $_.pnfolder
   $c_app.savedata()
}


into this:


# First the known steps to connect to the mfcom and initialize the object 
$c_farm = new-object -com metaframecom.metaframefarm
$c_farm.initialize(1)
# Then we import the csv file and for each we do..
import-csv c:\citrix\Citrix_apps.csv | foreach{
   $c_app=$c_farm.addapplication($_.apptype)
   $c_app.Appname = $_.appname
   $c_app.description = $_.description
   $w_app = $c_app.winappobject
   Copy-Property -From $_ -To $w_app 
   $c_app.savedata()
}


See how much easier that is to Write, Read and Review? 


Enjoy! 


Jeffrey Snover [MSFT]
Windows PowerShell/MMC Architect
Visit the Windows PowerShell Team blog at:    http://blogs.msdn.com/PowerShell
Visit the Windows PowerShell ScriptCenter at:  http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx


NOTE – I didn’t run this so I can’t verify that added the extra properties to $W_APP is OK but I’m pretty sure it is.  If it isn’t, just tweak the TRAP to continue without doing the ADD-MEMBER.


 


Postscript.  10,000 thanks to James Manning who spotted (and diplomatically pointed out) a number of flaws in my Copy-Property script.  I’ve updated the script to reflect his corrections.  This reinforces the point about the benefits of sharing that I made in The Admin Development Model and Send-Signature entry:



By sharing, others get the benefit of seeing how you solve problems and it gives others the opportunity to point out how you can improve your scripting or to make you aware of things that you didn’t know that you could do. 


Thanks James!