Mr Andersson

Archive for the ‘scripting’ Category

How to get $VerbosePreference applied on remote commands

leave a comment »

If you use Write-Verbose in a script executed on a remote computer, you may not get the result you expect.

Example TestRemoteCommand.ps1:

Invoke-Command -ComputerName comp1 -ScriptBlock { 
    Write-Host "hello"
    Write-Verbose "there"
}

The above will only output “hello” and not “there” in the host window.

I thought I was clever by just passing along the $VerbosePreference value:

Invoke-Command -ComputerName comp1 -ArgumentList $VerbosePreference -ScriptBlock { 
    param( $VerbosePreference )
    Write-Host "hello"
    Write-Verbose "there"
}

But, still no luck! Only the statement using Write-Host is included in the output when the command is invoked on the remote computer.
What’s going on here is that the argument sent to the remotely executed script block is converted to an integer, which is the base integral type of the underlying enum type of $VerbosePreference.
So I figured that typing the argument in the param() clause will bind the parameter to the correct data type, and voila, it now works as expected.

Invoke-Command -ComputerName comp1 -ArgumentList $VerbosePreference -ScriptBlock { 
    param( [System.Management.Automation.ActionPreference] $VerbosePreference )
    Write-Host "hello"
    Write-Verbose "there"
}

Now you can toggle the verbose output of your remote script using the switch of your local script:

.\TestRemoteCommand.ps1 # Default, or -Verbose:$false
.\TestRemoteCommand.ps1 -Verbose
Advertisements

Written by anderssonjohan

November 19, 2009 at 14:25

Posted in scripting

Tagged with ,

Enable-PSRemoting is broken in PowerShell?

with 2 comments

Yesterday I installed the RTM bits of WinRM 2.0 on a Windows Server 2003 machine and it were really no issues at all getting it working.

Today I tried to do the same thing but on a Windows 2008 Server (R1) that is not joined to any domain, and it fail on step 1 of the configuration.
In both cases I started out downloading the patch. Then I run “Enable-PSRemoting” in an elevated PowerShell prompt, which succeeded on the Windows 2003 Server but not on the Windows 2008 Server.
The error I got was “Access denied”.
I thought I followed the troubleshooting guide for WinRM, but apparently there is slight difference of two different ways I found on how you set this up. You can either run “Enable-PSRemoting” or you can run “winrm qc”. If I run the quick config tool using the “winrm” command everything works out just fine, but if I use “Enable-PSRemoting” I get “Access denied”.
The most mysterious part of this is that “Enable-PSRemoting” WORKED on Windows Server 2003, but not on Windows Server 2008. Strange!
If you have any clues about this, please leave me a comment!

Written by anderssonjohan

November 18, 2009 at 07:57

Posted in scripting, troubleshooting

Tagged with ,

Pimping PsUnit with constraint based assertions

leave a comment »

Using PsUnit one can set up scripted “unit tests” for PowerShell scripts. I have sort of just started using PsUnit at work and right now I feel that a lot of the basic features of other test frameworks are missing.

PsUnit has one function for doing constraint based assertions. This may be familar to those who are using NUnit. Basically the syntax for this is:
<![CDATA[
$result = DoSomething
Assert-That $result { $ActualValue -eq “expected value” }
]]>

Using NUnit and this constraint based model, a simple assertion can look like this:
<![CDATA[
Assert.That( myString, Is.EqualTo(“Hello”) );
]]>

I quite don’t like the “barebone” scriptblock you need to write to use Assert-That in PsUnit, so here is what I came up with (using my example of above):
<![CDATA[
$result = DoSomething
Assert-That $result (IsEqualTo “expected value”)
]]>

I also added another constraint which allows you to assert that some code throws something:
<![CDATA[
# The following assertion will always pass
Assert-That { throw “blah!” } (ThrowsException)
]]>
This complements the already built-in feature of PsUnit which allows you to add a parameter, $ExpectedException, to the test. If you have been using MSTest you would use the method attribute ExpectedException for this.

Here is another example which tweaks the constraint to a somewhat more specific type of exception:
<![CDATA[
# The following assertion will fail since throw in PowerShell creates a System.Management.Automation.ErrorRecord
Assert-That { throw “blah!” } (ThrowsException -Exception $([System.ArgumentException]))
]]>

A more usable test using the ThrowsException constraint could be:
<![CDATA[
Assert-That {
# The following web resource does not exist, thus an error should be the result
Get-Http “http://blogger.com/asdfg&#8221;
} (ThrowsException -Exception $([System.Net.WebException]))
]]>

The biggest improvement using these new constraints is the output in the test result.
While using PsUnit out-of-the-box…
<![CDATA[
$foo = “bar”
Assert-That $foo { $ActualValue -eq “foo” }
]]>
…results with “Assert-That returned false!” in the test result.
Using the constraint in this blog post…:
<![CDATA[
$foo = “bar”
Assert-That $foo (IsEqualTo “foo”)
]]>
…results with “ActualValue ‘bar’ is not equal to expected value ‘foo’“.

The same level of detail is provided by the ThrowsException constraint, which may result in “No exception was thrown, expected exception of type ‘System.ArgumentException’“.

Any ideas, suggestions etc.? Please don’t hesitate to drop a comment!

Written by anderssonjohan

November 11, 2009 at 20:41

Posted in scripting

Tagged with ,

Integrating PsUnit and MSBuild

leave a comment »

The last month I have been digging into, adding new and refactoring the Powershell scripts we use in RemoteX Applications. We primarily use PowerShell to configure and deploy our product.

More often or too many times – with our latest releases – our fellow collegues at Customer Operations have been telling us “The xyz powershell script doesn’t work anymore, what have you done to it???”
I could have answered: “- We don’t have automated tests on those scripts, so what do you expect? Manual testing? Pfffff…”, but for obvious reasons I didn’t :).
So it was time to add some automated tests for those scripts so I could get this guy at Customer Operations back to work.
All compiled code (C#) is automatically tested in our Continuous Integration builds when someone checks in, but until now we haven’t had automated tests for our PowerShell scripts. With “automated” I mean that a test runner executes a test suite during a build.
In order to test our PowerShell scripts I have started using PsUnit. Since we are using Team Foundation Server and MSBuild for our CI builds, I needed to integrate PsUnit with MSBuild somehow.
There are several ways to invoke Powershell from an MSBuild script – we have chosen the Exec task which invokes powershell.exe.
First of all I created a separate build target which invokes PsUnit:

<![CDATA[
<PropertyGroup>
<CoreTestDependsOn>$(CoreTestDependsOn);RunPsUnit</CoreTestDependsOn>
<RunPsUnitDependsOn>CoreRunPsUnit</RunPsUnitDependsOn>
</PropertyGroup>

<Target Name=”RunPsUnit” DependsOnTargets=”$(RunPsUnitDependsOn)”/>
<Target Name=”CoreRunPsUnit” Condition=” ‘$(RunTest)’!=’false’ “>
<Exec Command=”$(PowerShell) -Command &quot;&amp;{ .\runpsunit.ps1 -PsUnitTestFile %(PsUnitTest.FullPath) }&quot;”/>
</Target>
]]>
Using this batch target, all that needs to be done is to add PsUnitTest items to the build script. Just like the way we set up TestContainer items for use with MSTest:
<![CDATA[
<ItemGroup>
<PsUnitTest Include=”$(SolutionRoot)\Scripts\Tests\*.Test.ps1″/>
<PsUnitTest Include=”$(SolutionRoot)\References\PsUnit\*.Test.ps1″/>
</ItemGroup>
]]>
So far so good, but when tests are failing, the build is successful. This is because the Exec task only reports a build error when the executed command returns an exit code other than zero (0x0). So I started looking into the test runner of PsUnit (PsUnit.Run.ps1) and discovered that it didn’t send any output nor produced some other exit code than 0x0. The only output PsUnit will send you is some nice colorful information written to the PowerShell host.

So I ended up with the following:
1. this tiny tweak at the end of PsUnit.Run.ps1:

<![CDATA[
$TestResults
]]>
This makes PsUnit.Run.ps1 send the test results along the pipeline.

2. A small wrapper for the test runner which returns some exit code other than 0x0 to the Exec task in MSBuild:

<![CDATA[
$results = Invoke-Expression “$psUnitRun -PsUnitTestFile $PsUnitTestFile”
$results | % {
if( $_.Result -eq “FAIL” ) {
throw “Test run failed: $($_.Test) – $($_.Reason)”
}
}
]]>
This makes the Exec task report a build error if we find any failing test in the test result from PsUnit – and last but not least – the name of the failing test and the reason ends up nicely in the MSBuild log file.

So now when somebody checks in a change to compiled code, which affects the result of our setup scripts, we will be able to detect errors ASAP! Yay!

Written by anderssonjohan

November 11, 2009 at 19:50

Posted in scripting

Tagged with , ,

Yay, PowerShell v2 is finally RTMed!

leave a comment »

PowerShell v2 is finally RTMed for the rest of us who still has a couple of boxes not running Windows 2008 Server R2 or Windows 7.

Downloads and info for Windows Server 2003, Windows Server 2008 (R1), Windows Vista and Windows XP.

Written by anderssonjohan

November 9, 2009 at 16:01

Posted in scripting

Tagged with

PowerTip of the day – Variable descriptions

leave a comment »

I just got this in my inbox from Powershell.com:
Add Descriptions to Variables

Keeping track of a variable’s purpose can be accomplished by assigning a clear text description:

$ip= '10.10.10.10'
Set-Variable ip -description 'Server IP or Name'

When you now list your variables, you can output the variable description as well:

dir variable:ip | Format-Table Name, Value, Description

Playing around a little I found out that you easily can get the same information about system/environment variables using Get-Variable and Format-Table:

gv | ft Name, Description
Name                                    Description
----                                    -----------
$
?                                       Execution status of last command.
^
_
args
bla                                     Bla bla bla
ConfirmPreference                       Dictates when confirmation should be...
ConsoleFileName                         Name of the current console file.
DebugPreference                         Dictates action taken when an Debug ...
Error
ErrorActionPreference                   Dictates action taken when an Error ...
ErrorView                               Dictates the view mode to use when d...

Written by anderssonjohan

October 29, 2009 at 14:54

Posted in scripting

Tagged with

PowerShell Scripting Guidelines

leave a comment »

Lately I have been doing some work related to how we automate the process of how we install, configure and deploy our product into a server environment. Since our pre-requisites are Windows Server 2008 and Windows 7, we can totally go crazy with PowerShell scripts. Next thing to sort out after upgrading to Windows Server 2008 R2 is of course PowerShell Remoting.. 🙂

However, using quite an amount of PowerShell scripts, we need some guidelines to structure them. This to complement our guidelines covering OO design and C# code style.

So I have started out with the following draft and I’m looking for some tips and comments about this and what you (yes, you!) believe is a good rule of thumb to use when developing and maintaining a bunch of PowerShell scripts.

(Sorry for the ugly formatting. Code was pasted from PowerGUI to a word doc and then to blogger :))

1 Scripting Guidelines

When there is a need of a script, use your mighty PowerShell skills!
Here is a couple of guidelines which should be followed to speed up development, maintenance and usage of PowerShell scripts.

Apply the above mentioned principles when applicable.

1.1 Function files

Package common functions into function files and dot-source them in the script which needs them.
Example:
. ..\DB\Common.ps1

1.2 File names

Name PS1 files using standard verbs like get, set, remove, new etc., or a verb that fits the purpose or the script. Do not use verbs in file names for files that have generic purpose, like a function library file. Also consider using PowerShell v2 modules when applicable.

Scripts that are to be called from the prompt are easier to understand if they are named in such way that no “usage” or get-help is needed when invoking the script. Like add-user.ps1, remove-database.ps1 and so on.

Avoid complex scripts with multiple purposes which becomes complex with parameter sets (get-help about_functions_advanced_parameters)

1.3 Script and function parameters

Use built-in support to handle function and script parameters. Avoid index-based unnamed parameters using $args.

Reference: get-help about_arguments; get-help about_functions; get-help about_functions_advanced_parameters
MSDN:
http://msdn.microsoft.com/en-us/library/ms714433(VS.85).aspx

Example, testme.ps1:

param(

[Switch] $ver = [Switch]::Present

)

Write-Host $args

Write-Host “Testing!”

if( $ver )

{

Write-Host “Testing even more!”

}

Output:

PS > .\testme.ps1 –ver
PS > .\testme.ps1 -ver:$false

1.4 Decorate function parameters appropriately using validation rules

Decorate your parameters with validation rules and help messages to avoid tedious work on custom “script usage” functions. Your script users will be able to use “get-help yourscript.ps1”, or the equivalent “yourscript.ps1 -?” to get script usage information. Users will also be prompted if mandatory parameters are left out blank.

Get-help will also print out common parameters (get-help about_commonparameters).

Example:

param(

[Switch] $ver = [Switch]::Present,

[Parameter(mandatory=$true, helpmessage=“Your name”)]

[ValidateNotNullOrEmpty()]

[string] $name = “John Doe”

)

Write-Host $args

Write-Host “Hello $name!”

if( $ver )

{

Write-Host “Your name is $($name.Length) characters long”

}

Output:

PS > get-help .\testme.ps1

testme.ps1 [-name] [-ver] [-Verbose] [-Debug] [-ErrorAction ] [-WarningAction ] [-ErrorVariable ] [-WarningVariable ] [-OutVariable ] [-OutBuffer ]

1.5 Document script usage using PowerShell comment based help

When it is necessary to document how a script can be used, or when you want to provide more information about your script’s parameters than just the name, document it using the PowerShell help syntax. This can be compared to using XML comments in languages such as C#/VB or javadoc tags in Java.

See “get-help about_comment_based_help” for more information.

Example:

<#

.Synopsis

Synopsis text here

.Description

Usage description here

.Parameter ver

Enabling this switch prints out the length of the given name

.Parameter name

The name to say hello to

#>

param(

[Switch] $ver = [Switch]::Present,

[Parameter(mandatory=$true, helpmessage=“Your name”)]

[ValidateNotNullOrEmpty()]

[string] $name = “John Doe”

)

Write-Host $args

Write-Host “Hello $name!”

if( $ver )

{

Write-Host “Your name is $($name.Length) characters long”

}

Output:
PS> get-help -full .\testme.ps1

NAME
C:\temp\testme.ps1

SYNOPSIS
Synopsis text here

SYNTAX
C:\temp\testme.ps1 [-ver] [-name] []

DESCRIPTION
Usage description here

PARAMETERS
-ver []
Enabling this switch prints out the length of the given name

Required? False
Position? Named
Default value
Accept pipeline input? False
Accept wildcard characters?

-name
The name to say hello to

Required? True
Position? 1
Default value
Accept pipeline input? False
Accept wildcard characters?


This cmdlet supports the common parameters: Verbose, Debug,
ErrorAction, ErrorVariable, WarningAction, WarningVariable,
OutBuffer and OutVariable. For more information, type,
“get-help about_commonparameters”.

INPUTS

OUTPUTS

RELATED LINKS

1.6 Use validation rules to perform parameter input validation

PowerShell has several built-in validation rules for input validation. Use them as long as they add more value than doing custom validation using if-statements.

The built-in validation rules are: AllowNull, AllowEmptyString, AllowEmptyCollection, ValidateCount, ValidateLength, ValidatePattern, ValidateRange, ValidateScript, ValidateSet, ValidateNotNull, ValidateNotNullOrEmpty
See “
get-help about_functions_advanced_parameters” for further reference.

1.7 Putting the right stuff on the pipeline

· Use write-host for console information with fancy colors

· Use write-output to be able to pipe your script/function to another script/function.
Example pipeline scenario, sending a user from add-user to new-guestbookentry:
. .\guestbooks-functions.ps1;
add-user “foo” “password” | new-guestbookentry “Welcome to our guestbooks.com!”

· Avoid unwanted objects on the pipeline using termination with $null assignments or piping the output to NULL, example:
add-user “foo” “password” # put the added user on the pipeline
$null = add-user “foo” “password” # added user object “terminated”
add-user “foo” “password” | out-null # added user object piped to NULL instead of the output

Written by anderssonjohan

October 21, 2009 at 12:57

Posted in scripting

Tagged with