Lessons learnt using simple PowerShell scripts with vNext Release Management

If you are using basic PowerShell scripts as opposed to DSC with Release Management there are a few gotcha’s I have found.

You cannot pass parameters

Lets look at a sample script that we would like to run via Release Manager

 1
 2
 3param  
 4(  
 5    $param1   
 6)
 7
 8 
 9
10write-verbose -verbose "Start"  
11write-verbose -verbose "Got var1 \[$var1\]"  
12write-verbose -verbose "Got param1 \[$param1\]"  
13write-verbose -verbose "End"  

In Release Manager we have the following vNext workflow

image

You can see we are setting two custom values which we intend to use within our script, one is a script parameter (Param1), the other one is just a global variable (Var1).

If we do a deployment we get the log

 1
 2
 3Copying recursively from \\storedropsrm4583e318-abb2-4f21-9289-9cb0264a3542152 to C:WindowsDtlDownloadsISS vNext Drops succeeded.
 4
 5Start
 6
 7Got var1 \[XXXvar1\]
 8
 9Got param1 \[\]
10
11End

You can see the problem, $var1 is set, $param1 is not. Took me a while to get my head around this, the problem is the RM activity’s PSSCriptPath is just that a script path, not a command line that will be executed. Unlike the PowerShell activities in the vNext build tools you don’t have a pair of settings, one for the path to the script and another for the arguments. Here we have no ways to set the command line arguments.

Note: The PSConfigurationPath is just for DSC configurations as discussed elsewhere.

So in effect the Param1 is not set, as we did not call

1test -param1 “some value”

This means there is no point using parameters in the script you wish to use with RM vNext. But wait, I bet you are thinking ‘I want to run my script externally to Release Manager to test it, and using parameters with validation rules is best practice, I don’t want to loose that advantage

The best workaround I have found is to use a wrapper script that takes the variable and makes them parameters, something like this

1$folder = Split-Path -Parent $MyInvocation.MyCommand.Definition  
2& $foldertest.ps1 -param1 $param1

Another Gotcha Note that I need to find the path the wrapper script is running in and use it to build the path to my actual script. If I don’t do this I get that the test.ps1 script can’t be found.

After altering my pipeline to use the wrapper and rerunning the deployment I get the log file I wanted

 1
 2
 3Copying recursively from \\storedropsrm4583e318-abb2-4f21-9289-9cb0264a3542160 to C:WindowsDtlDownloadsISS vNext Drops succeeded.
 4
 5Start
 6
 7Got var1 \[XXXvar1\]
 8
 9Got param1 \[XXXparam1\]
10
11End

This is all a bit ugly, but works.

Looking forward this appears to not be too much of an issue. The next version of Release Management as shown at Build is based around the vNext  TFS build tooling which seems to always allow you to pass true PowerShell command line arguments. So this problem should go away in the not too distant future.

Don’t write to the console

The other big problem is any script that writes or reads from the console. Usually this means a write-host call in a script that causes an error along the lines

1A command that prompts the user failed because the host program or the command type does not support user interaction. Try a host program that supports user interaction, such as the Windows PowerShell Console or Windows PowerShell ISE, and remove prompt-related commands from command types that do not support user interaction, such as Windows PowerShell workflows.  
2 +At C:WindowsDtlDownloadsISS vNext Dropsscriptstest.ps1:7 char:1  
3\+ Write-Host "hello 1" -ForegroundColor red

But also watch out for any CLS calls, that has caught me out. I have found the it can be hard to track down the offending lines, especially if there are PowerShell modules loading modules.

The best recommendation is to just use write-verbose and write-error.

  • write-error if your script has errored. This will let RM know the script has failed, thus failing the deployment – just what we want
  • write-verbose for any logging

Any other form of PowerShell output will not be passed to RM, be warned!

You might also notice in my sample script that I am passing the –verbose argument to the write-verbose command, again you have to have this maximal level of logging on  for the messages to make it out to the RM logs. Probably a better solution, if you think you might vary the level of logging, is to change the script to set the $VerbosePreference

 1param  
 2(  
 3    $param1   
 4)   
 5   
 6  
 7  
 8  
 9$VerbosePreference ='Continue' # equiv to -verbose 
10
11write-verbose "Start"  
12write-verbose "Got var1 \[$var1\]"  
13write-verbose "Got param1 \[$param1\]"  
14write-verbose "End"  

So hopefully a few pointers to make your deployments a bit smoother