In PowerShell we can refer to a type using a type literal, eg:
[System.DateTime]
Type literals are used when casting one type to another, eg:
[System.DateTime]'2012-04-11'
Or when acessing a static member, eg:
[System.DateTime]::UtcNow
Or declaring a parameter type in a function, eg:
function Add-OneWeek ([System.DateTime]$StartDate) {
$StartDate.AddDays(7)
}
PowerShell also provides a handful of type accelerators so we don’t have to use the full name of the type, eg:
[datetime] # accelerator for [System.DateTime] [wmi] # accelerator for [System.Management.ManagementObject]
However, unlike a C# project in Visual Studio, PowerShell will let you load two identically named types from two different assemblies into the session:
Add-Type -AssemblyName 'Microsoft.TeamFoundation.Client, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' Add-Type -AssemblyName 'Microsoft.TeamFoundation.Client, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
Both of the assemblies in my example contain a class named TfsConnection, so which version of the type is referenced by this type literal?
[Microsoft.TeamFoundation.Client.TfsConnection]
From my testing it appears to resolve to the type found in whichever assembly was loaded first, in my example above it would be version 10. However, a single script or module would be unlikely to load both versions of the assembly itself so you would likely encounter this situation when two different scripts or modules load conflicting versions of the same assembly, and in this case you won’t control the order in which each assembly is loaded so you can’t be sure which is first.
It is possible for a script to detect if a conflicting assembly version is loaded if it is expecting this scenario but the CLR won’t allow an assembly to be unloaded so all the script could do is inform the user and abort, or maybe spawn a child PowerShell process in which to execute.
It is also possible to have two identically named types loaded in PowerShell via another less obvious scenario. If you use the New-WebServiceProxy cmdlet against two different endpoints implementing the same web service interface, PowerShell generates and loads two different dynamic assemblies with identical type names (assuming you specify the same Namespace parameter to the cmdlet). I’ve run into this issue with the SQL Server Reporting Services web service.
To address this issue, referring to my first example, you can use assembly-qualified type literals, eg:
[Microsoft.TeamFoundation.Client.TfsConnection, Microsoft.TeamFoundation.Client, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a]
However these quickly create scripts which are harder to read and maintain. For accessing static members you can assign the type literal to a variable, eg:
$TfsConnection11 = [Microsoft.TeamFoundation.Client.TfsConnection, Microsoft.TeamFoundation.Client, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a] $TfsConnection11::ApplicationName
But for casting and declaring parameter types I don’t have a better solution. There are ways to add to PowerShell’s built-in type accelerator list but it involves manipulating non-public types which I wouldn’t feel comfortable using in a script or module I intend to publish for others to use.
For the New-WebServiceProxy situation, I have created a wrapper function which will reuse the existing PowerShell generated assembly if it exists.
I manage a Team Foundation Server 2010 instance with at least 30 Collections each with several Team Projects. Even after installing the TFS hotfix to reduce the size of test data in the TFS databases we still accrue many binary files care of running Code Coverage analysis during our continuous integration builds. As such it is still necessary to run the Test Attachment Cleaner Power Tool regularly to keep the database sizes manageable.
The Test Attachment Cleaner however is a command-line tool which requires the Collection Uri and the Team Project Name among several other parameters so I first needed to write a script (I chose PowerShell) to query TFS for all the Collections and Projects and call the Cleaner for each. I then needed to configure this script (and therefore the Cleaner) to run as a scheduled task and I needed to specify which user the task would run as.
The easiest answer would be to run the task as a user who has TFS Server Administrator privileges to ensure the Cleaner has access to find and delete attachments in every project in every collection but that would be overkill. I couldn’t find any existing documentation on the minimum privileges required by the Cleaner so I started with a user with zero TFS privileges and repeatedly executed the scheduled task, granting each permission mentioned in each successive error message until the task completed successfully.
For deleting attachments by extension and age I found the minimum permissions required were the following three, all at the Team Project level:
- View test runs
- Create test runs (non-intuitively, this permission allows attachments to be deleted)
- View project-level information
For deleting attachments based on linked bugs (something I didn’t try) I suspect the “View work items in this node” permission would also be required at the root Area level.
Having determined these permissions, I needed to apply them across all the Team Projects but it appears the only out-of-the-box way to set this permissions is via the Team Explorer user interface which becomes rather tedious after the first few projects. Instead I scripted the permission granting too via the TFS API and I’ve published some PowerShell cmdlets to make this easier if anyone else needs to do the same.
I think PowerShell is a great and powerful language with very few limitations and when implementing a Module for PowerShell my default language of choice has been PowerShell itself for several reasons:
- Many Modules evolve from refactoring and reuse of existing PowerShell scripts so it makes sense to use the existing code instead of rewriting it in another language.
- The Advanced Functions capability introduced by PowerShell 2.0 enables first-class Cmdlets and therefore Modules to be built, where as C# was required to achieve this in PSv1 days.
- PowerShell’s pipeline, native support for WMI, variety of drive providers, and loose type system makes it much quicker and cleaner to write certain kinds of code compared to C#.
- The feedback loop when developing and testing Modules written with PowerShell is much quicker when there is no compile step and you don’t have to worry about locking a Binary Module’s DLL file.
- Contributions such as bug fixes or new features from the wider PowerShell community are more likely if the Module is written with PowerShell as the source is immediately available and the users can use their own changes without additional steps.
However, as much as I believe in writing with PowerShell, for PowerShell wherever possible, I’ve been thinking that I may just have to consider using C# for developing PowerShell Modules more often because:
- Proxy functions are being promoted as a way for users to add features and fix “broken” behaviour in built-in Cmdlets. Modules developed with PowerShell will be affected by these changes and may introduce bugs unless every Cmdlet reference is fully qualified in the Module (eg Microsoft.PowerShell.Management\Get-ChildItem) which will lead to less maintainable code.
- Even with PSv2′s try{}catch{} structured error handling, I feel that the combination of terminating and non-terminating errors and ErrorActions can make writing resilient PowerShell scripts harder and less maintainable than the C# equivalent.
- Implementing common parameters across multiple Cmdlets is done through copy-and-paste with PowerShell instead of the cleaner inheritance approach possible in C#.
- Automated test frameworks for verifying module functionality are much more mature for C# than they are for PowerShell and even though a C# test framework could be used to test a Module implemented in PowerShell, I’m not sure this would be a pleasant development experience.
- Performance critical code can potentially be more efficient when implemented in C# instead of PowerShell (although we can embed chunks of C# directly in PowerShell scripts if necessary).
Ultimately I don’t think a definitive answer can be given one way or the other , it will depend on the nature of the module being developed. For example, one Module I have written is a wrapper for an existing .NET API and it doesn’t use many of the built-in PowerShell Cmdlets itself so it works quite well developed with PowerShell itself. As an alternate example, the PowerShell Community Extensions Module has a lot of code interacting with native APIs via P/Invoke and a large suite of automated tests that suits it well to being developed with C#.
I write a lot of PowerShell scripts. Many of these get transformed into modules for reuse and occasionally published for others. However making these reusable modules available wherever my scripts may run and propagating updates to these modules is not easily managed.
As a solution to this I’ve been designing a PowerShell module to enable scripts and modules to easily reference and use other modules without requiring these modules to be installed first. The idea is very similar to RubyGems or Nuget but I feel there are some important criteria that need to be met by a PowerShell module manager.
Here is a list of requirements from the perspective of a module-consumer:
- PowerShell already defines standard install locations for modules so a module manager should make it easy to use these by default but still allow them to be overridden.
- Scripts will often run in a least privilege environment so a module manager should not require any special permissions to install a module.
- PowerShell’s Execution Policy depends on scripts that were downloaded from the Internet to be marked as such, a module manager should honour this by marking installed modules with the appropriate Zone Identifier.
- PowerShell allows multiple versions of a module to exist side-by-side and allows scripts to reference a specific version, a module manager must support this behaviour.
- A script should be able to use the module manager to install other modules even when the module manager itself hasn’t been installed yet (eg using a secure bootstrap)
- A script should be able to reference a module by name and optional version number only.
- The module manager should operate on a clean operating system with only PowerShell installed and not have any other pre-requisites.
As a module author, there is also a set of expectations from a module manager:
- A format for modules and their metadata is already defined by PowerShell itself (a PSD1 file with a collection of PSM1, PS1, and DLL files etc in a folder) so a module manager should leverage this instead of defining a new format. The format for transporting a module over the wire is an implementation concern of the module manager and not a concern of the module author.
- Publishing a new or updated module for others to consume should be a PowerShell one-line command that can consume any locally installed module.
- There should be a standard public repository for published modules but private repositories should also be supported for publishing modules intended for personal or enterprise use only.
There will inevitably be some impacts on how authors develop their modules to support the above requirements:
- To support the common PowerShell Execution Policy setting of RemoteSigned, published modules should be signed with a valid Code Signing Certificate issued by a recognised authority. As cheap as US$75.
- Modules must assume they will be executing in a least privilege environment and only operate with the minimum permissions required to perform the task the module is designed to do.
- Related to least privilege, modules should not require a “first-use” process to register or configure the module that would require elevation.
- Modules must not assume they will be installed to a specific location and instead should use the $PSScriptRoot variable for module-relative paths.
There are already some existing solutions for PowerShell module management, some meet most of the requirements above:
- Joel Bennett‘s PoshCode module – Been around the longest but currently only supports single-file modules (ie PSM1 files)
- Andrew Nurse‘s PS-Get – Almost a direct port of Nuget to PowerShell but requires PowerShell v2 to be configured to run under .NET 4.
- Mike Chaliy‘s PSGet - Probably the closest to my ideal but currently it requires a GitHub pull-request to list a module and the module author must also find somewhere to host their module.
I think adding my own solution to this list would only add to the module management problem, especially when you consider the other PowerShell module management solutions that exist but I’m not aware of. Instead it would make more sense to have these existing solutions converge by having them each support the installation of modules published by the others and move toward meeting as many of the requirements above as feasible.
It doesn’t take long before any reasonable software project requires a build process that does more than the IDE’s default Build command. When developing software based on the .NET platform there are several different ways to extend the build process beyond the standard Visual Studio compilation steps.
Here is a list for choosing the best place to insert custom build steps into the process, with the simplest first and the least desirable last:
- Project pre- and post-build events: There is a GUI, you have access to a handful of project variables, can perform the same tasks as a Windows Batch script, and still works with Visual Studio’s F5 build and run experience.
- MSBuild target overrides within the project files (eg BeforeBuild, AfterBuild): You get the full power of MSBuild, and still works with the F5 build and run experience but it’s a little concealed from those who don’t know to look there.
- MSBuild imports within the project files: As above but more maintainable and reusable. That extra MSBuild file in the solution might grab some more attention than an inline target override. If the customization is generic enough, using it can become simpler by publishing it as a Nuget package. Using MSBuild 4′s before- and after- solution targets files fit here also.
- A parent MSBuild file to build the solution and perform additional tasks: Still works locally in the development environment but no longer plays nicely with F5. Someone is going to have to open a command prompt.
- Customising the CI server’s build pipeline. For TFS this means Team Build Process Template customization: Using the Workflow Designer destroys version history of the template file so prepare to get your hands dirty in XAML and VB, and all building requires a Team Build Agent so the feedback loop for testing customizations just exploded. For TeamCity this means your build customizations are no longer version controlled with the rest of the source, you also take a hit on the feedback loop.
Items 2 through 4 above are all MSBuild and where most build customization should live. Here is a list for choosing the best technique for implementing your build customizations within MSBuild, again in order of preferred first:
- Out of the box MSBuild tasks: find neat ways to leverage them without taking any new dependencies. If you get to know the MSBuild well, an amazing amount can be achieved.
- Use the Exec MSBuild task to call out to existing processes: the OS and .NET SDK is full of handy tools that don’t need to be installed on your build server first. Other tools can be source controlled too.
- MSBuild 4 Inline Tasks: New to MSBuild v4, custom MSBuild tasks can be defined using C# directly inside the MSBuild project. A lot more power, still no issues deploying build customisations to developers or build servers .
- Run PowerShell scripts via the Exec task: Modern OSes have PowerShell installed by default, you have all the power of .NET and more and still don’t have to deploy anything to support your customisations.
- Compiled MSBuild Tasks: Too far, go back. Now you need a process to compile your customisations then install or version-control the binaries where other developers and build servers can use them.
If after all these options you do find yourself customising the CI server’s build pipeline, the very least desirable situation is then using custom extensions to that pipeline. For TFS, this means custom Workflow Activities, for TeamCity, this means custom plugins. Make sure all other options are exhausted before it comes to this.
Update: with .NET 4.5 there is an easier way.
A very common complaint from users of Team Foundation Server’s build system is that it changes the folder structure of the project outputs. By default Visual Studio puts all the files in each project’s respective /bin/ or /bin/<configuration>/ folder but Team Build just uses a flat folder structure putting all the files in the drop folder root or, again, a /<configuration>/ subfolder in the drop folder, with all project outputs mixed together.
Additionally because Team Build achieves this by setting the OutDir property via the MSBuild.exe command-line combined with MSBuild’s property precedence this value cannot easily be changed from within MSBuild itself and the popular solution is to edit the Build Process Template *.xaml file to use a different property name. But I prefer not to touch the Workflow unless absolutely necessary.
Instead, I use both the Solution Before Target and the Inline Task features of MSBuild v4 to override the default implementation of the MSBuild Task used to build the individual projects in the solution. In my alternative implementation, I prevent the OutDir property from being passed through and I pass through a property called PreferredOutDir instead which individual projects can use if desired.
The first part, substituting the OutDir property for the PreferredOutDir property at the solution level is achieved simply by adding a new file to the directory your solution file resides in. This new file should be named following the pattern “before.<your solution name>.sln.targets”, eg for a solution file called “Foo.sln” then new file would be “before.Foo.sln.targets”. The contents of this new file should look like this. Make sure this new file gets checked-in to source control.
The second part, letting each project control its output folder structure, is simply a matter of adding a line to the project’s *.csproj or *.vbproj file (depending on the language). Locate the first <PropertyGroup> element inside the project file that doesn’t have a Condition attribute specified, and the locate the corresponding </PropertyGroup> closing tag for this element. Immediately above the closing tag add a line something like this:
<OutDir Condition=" '$(PreferredOutDir)' != '' ">$(PreferredOutDir)$(MSBuildProjectName)\</OutDir>
In this example the project will output to the Team Build drop folder under a subfolder named the same as the project file (without the .csproj extension). You might choose a different pattern. Also, Web projects usually create their own output folder under a _PublishedWebSites subfolder of the Team Build drop folder, to maintain this behaviour just set the OutDir property to equal the PreferredOutDir property exactly.
You can verify if your changes have worked on your local machine before checking in simply by running MSBuild from the command-line and specifying the OutDir property just like Team Build does, eg:
msbuild Foo.sln /p:OutDir=c:\TestDropFolder\
Note: This process is based on PowerShell v3 CTP 2 and is subject change.
When you open PowerShell v3′s ISE (Integrated Scripting Environment) you should see a new Commands pane that wasn’t present in version 2.
This is a built-in example of an ISE Add-on Tool but you can also create your own quite easily. At its simplest an ISE Add-on Tool is a WPF Control that implements the IAddOnToolHostObject interface. To get started writing your own add-on follow these simple steps:
- Open Visual Studio and create a new WPF User Control Library project. The new project should contain a new UserControl1.
- Add a new Project Reference to the Microsoft.PowerShell.GPowerShell (version 3.0) assembly located in the GAC.
- Open the UserControl1.xaml.cs code-behind file and change the UserControl1 class to implement the IAddOnToolHostObject interface.
- The only member of this interface is the HostObject property which can be declared a a simple auto-property for now.
- Build the project and find the path of the DLL it produces.
- Open the PowerShell v3 ISE and load the DLL you have just built, eg:
Add-Type -Path ‘c:\users\me\documents\project1\bin\debug\project1.dll’ - Add the UserControl to the current PowerShell tab’s VerticalAddOnTools collection and make it visible (replace “Project1.UserControl1″ below with the full namespace of your control):
$psISE.CurrentPowerShellTab.VerticalAddOnTools.Add(‘MyAddOnTool’, [Project1.UserControl1], $true)
After following these steps you should see a new, blank pane as an extra tab where the Commands pane normally appears and now you can return to Visual Studio and start adding to the appearance and behaviour of your new add-on tool. There are some other things worth knowing about developing ISE Add-On Tools:
- When your UserControl is added to the ISE, the HostObject property is set to an object almost identical to the $psISE variable and your control uses this to interact with the ISE itself. For example you can manipulate text in the editor, or you can register for events that tell your control when a new file is opened.
- While your UserControl is loaded in the ISE, you cannot build in Visual Studio because the DLL is locked and you need to close the ISE to unlock the file. I recommend adding a small PowerShell script to your project that performs steps 6 and 7 above and change your project’s Debug options in Visual Studio so that the ISE is started and your script is opened when you press F5.
- In step 7 above, your control is added to the VerticalAddOnTools collection but there is also a HorizontalAddOnTools collection and the user can move your control between these two at any time via the ISE menus so make sure you design the appearance of your Add-On Tool to work in both orientations.
Alternatively the Show-UI module for PowerShell includes a ConvertTo-ISEAddOn cmdlet to create the necessary WPF objects natively from PowerShell and avoid the need for a Visual Studio project.

