RDP File Generation/Use of HERE-Strings

Over on the Everyday Nerd blog, I found a great blog entry you should read:  Create multiple RDP files with PowerShell.  He describes his environment where he has to manage a large set of servers using Remote Desktop (RDP).  Having to configure the RDP settings each time he connects to the server, he decided to automate the process by writing a script which generates the RDP files for him. 

The script itself is cool but the think I wanted to highlight was that the script is a great illustration of a very powerful scripting design pattern.  It uses:

  1. A Template file which defines the structure of the output
  2. A Script which takes input, applies it to the template and generates output
  3. A CSV file which drives the script

This pattern has a number of advantages including:

  • You can change the template file without touching the code. 
    • If the script is digitally signed, you can change the template without affecting the digital signature. 
    • The person updating the template might not have any programming skills
  • Using a CSV file allows you to have this script driven using the IT Pro’s favorite input editor:  EXCEL.

I have a couple of suggestions for how to improve the script.

Use HERE-strings

I continue to see scripts which use lots of string processing which look difficult to create and maintain and are fragile because they are complex.  Here is the example from the script:

    # Begin building RDP file
    $temp = "`nfull address:s:" + $Entry.IP
 
    switch ($resolution) {
     
      "1024×768" {
        $temp = $temp + "`nscreen mode id:i:1"
        $temp = $temp + "`ndesktopwidth:i:1024"
        $temp = $temp + "`ndesktopheight:i:768"
  $temp = $temp + "`nusername:s:" + $Entry.USER
      }

      "1152×864" {
        $temp = $temp + "`nscreen mode id:i:1"
        $temp = $temp + "`ndesktopwidth:i:1152"
        $temp = $temp + "`ndesktopheight:i:864"
  $temp = $temp + "`nusername:s:" + $Entry.USER
      }
    }

This is exactly the sort of code we hoped to eliminate by providing HERE-strings.  HERE-Strings allow you to easily create multi-line strings (with or without variable expansion and command substitution).  HERE-Strings are NOT an advanced topic – every PowerShell scripter should take the time to learn how to use HERE-Strings.  Advanced scripters should use them because they are incredibly powerful.  Beginner scripters should use them because they are incredibly easy and reduce the complexity and fragility of your scripts.

Here is the same logic refactored and written with HERE-Strings:

    # Begin building RDP file
    $temp = "`nfull address:s:" + $Entry.IP
 
    switch ($resolution) {
     
      "1024×768" {
        $width = 1024
        $height = 768
      }

      "1152×864" {
        $width = 1152
        $height = 864
      }
    }
    $temp = $temp + @"
screen mode id:i:1
desktopwidth:i:$width
desktopheight:i:$height
username:s: $($Entry.USER)
"@

In this example I used double quotes which allow variable expansion (Notice the use of $width and $Height) and command substitution (notice the use of $($Entry.USER) [you could put any command or command sequence within $() and we’ll run it and substitute it’s output into the string]).

Use Verb-Noun naming and Proper Verbs

CreateRDP should be New-RDP or New-RDPFile.  What we are trying to do is to achieve a world where people will be able to guess and type and have it be right.  At the beginning, those patterns are not yet established so it might seem like "create" is the same as "new".  The problem is that if we aren’t hardcore on this then 50 verbs will become 500 which will become 5,000 and we’ll have an incoherent world where your guesses are useless and have ourselves to blame. I know what is going on here because it happens with me all the time – you are so focused on solving the problem, you don’t want to spend the energy on getting the naming right.  At some point, that pattern will be well established and you won’t have to think about it – you’ll need a new name and you’ll naturally pick the right ones.  Until then, you need to adopt a practice of reviewing and fixing the naming any script that you share with others.  When you share a script, you’ll either be enforcing or destroying the pattern for people.  Be a force of good.

The community is the ultimate controller of whether this pattern will emerge or we’ll have chaos.  I’ll do whatever I can do to ensure that the artifacts that we produce enforce the pattern but the bulk of scripts are going to come from the community.  The community needs to politely self police itself.  When you see someone share a script that doesn’t follow the correct naming, you need to leave a comment pointing it out and ask that the post be corrected. 

Let’s be clear, that includes US.  If (when) you see use posting scripts that don’t follow the pattern, call us on it and we’ll get it fixed.

 

Use Pipelining Features (when available in PS V2)

One of the problems with the script is that it is hardwired to a CSV file.  It would be more flexible to declare your data requirements and then be flexible with how those data requirements are satisfied.  We are redoing the interfaces to this for the next version of the CTP so I want go into detail here but I didn’t want to let the point pass either.  What you’ll be able to do is to specify the parameters and then specify that their values can be retrieved from the properties of a pipelined object.  What that means is that users can create any object they want as long as it has the right property names and then pipeline that object to your command.  This is a TON more powerful/flexible then hardwiring to CSV files.  What this means is you’d be able to do things like:

PS> Import-CSV RDPLIST.CSV | New-RDPFile
PS> Invoke-Database -Query <something> | New-RDPFile
PS> Invoke-WebServer -Parameter <something> | New-RDPFile
etc

 

Enjoy!

Jeffrey Snover [MSFT]
Windows Management Partner 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