> pshdo

Carbon v1.2 Released

Carbon v1.2 is out, is backwards compatible with v1.0 and v1.1 and supports PowerShell 3. (Future releases of Carbon will most likely only support PowerShell 3.) This release features mostly new functionality, including new functions for managing NTFS compression and performing XDT transformations. Thanks to Mark Sargent and Philip Teilmeier for contributing enhancements.

Get the bits at BitBucket.



  • Carbon now works under PowerShell v3.0!



File System



  • Created Remove-IniEntry function for removing entries/settings from an INI file.

Performance Counters


  • Invoke-PowerShell now defaults to running under the current CLR, instead of defaulting to a v2.0 CLR. This makes upgrading to PowerShell v3.0 easier.
  • Invoke-PowerShell now writes an error and returns if running PowerShell v3.0 and you want to run under a v2.0 CLR. Unfortunately, PowerShell v3.0 requires .NET 4.0, so you can’t run anything on an earlier CLR.


  • Revoke-Privilege now supports case-insensitive privilege names.
  • Updated Grant-Privilege to better handle when passing a privilege name with the wrong case.
  • Updated Grant-Privilege documentation to make it clear privilege names are case-sensitive.


  • New Convert-XmlFile, for transforming an XML file with Microsoft’s XDT (XML Data Transformation) technology. Thanks to Mark Sargent for the contribution.

Bug Fixes


  • Deleted the obsolete variable $CarbonImported. Carbon no longer exports any of its variables.


Performance Counters

  • Install-PerformanceCounter couldn’t be used to create counters that used/required a base counter. Thanks to Philip Teilmeier for the contribution.

Desired State Configuration With PowerShell

I recommend finding time to watch Jeffrey Snover and Kenneth Hansen’s Desired State Configuration in Windows Server 2012 R2 PowerShell talk from TechEd 2013. Desired State Configuration is a way of specifying, via new PowerShell language features, how you want a server to get setup and configured. PowerShell then takes that configuration data, and applies it on the server or servers of your choice. It looks like a great piece of technology.

Remember, keep your configuration and your scripts separate.


I like the idea behind the new Serilog .NET logging framework: instead of logging strings, you actually log objects. Cool idea. The code is hosted on Github.

Carbon v1.1 Released

Carbon v1.1 is out. It is backwards-compatible with v1.0. Get the bits at BitBucket.

Upgrade Instructions

  • On Windows 2008 R2, custom identies that run IIS app pools need the SeBatchLogonRight. Install-IisAppPool now grants this privilege on all operating systems. If this won’t work in your environment, you can remove these privileges with Revoke-Privilege.



  • Fixed some typos and ommissions in the v0.5.0.1 and v0.5.0.0 sections of the release notes.
  • Updated Import-Carbon.ps1 script to import Carbon regardless of the name of the directory Carbon is installed in.


  • Added IssuedTo and IssuedBy properties to X509Certificate2 objects. The values match what the Certificates MMC snap-in displays.
  • Added DisplayName property to X509Store objects, to show the names of the stores as they are displayed in the Certificates MMC snap-in.


  • Created Resolve-NetPath for getting the path to the Windows net.exe command/application. Updated all functions that call net.exe to use this function to resolve its path. Thanks to Paul Aage Aasheim for discovering that when running login scripts, net.exe isn’t in the path.


  • Created Get-IisHttpHeader for getting the custom HTTP headers for a website or one of its sub-directories.
  • Created Set-IisHttpHeader for creating/setting a custom HTTP header for a website or one of its sub-directories.
  • Created Get-IisMimeMap for getting the file extension to MIME type mappings for the IIS web server.
  • Created Remove-IisMimeMap for removing a file extension to MIME type mapping for the IIS web server.
  • Created Set-IisMimeMap for creating/setting a file extension to MIME type mapping for the IIS web server.
  • When creating an app pool that runs under a custom, non-service account, Install-IisAppPool grants that user the SeBatchLogonRight.
  • Install-IisAppPool writes an error if its user account doesn’t exist (i.e. if the value of the Username parameter doesn’t exist).


Bug Fixes


  • Install-SmbShare can’t find net.exe when running as part of a Windows logon script. Thanks to Paul Aage Aasheim for identifying and reporting this bug. All usages of the net.exe application were updated to use the new Resolve-NetPath function, which get the path to net.exe without assuming it is in the user’s PATH environment variable.

Users and Groups

  • Test-Identity no longer writes an error if it can’t find a fully-qualified local user, e.g. $env:COMPUTERNAME\Username.

Stupid Feed Tricks

Brent Simmons, who goes on to quote Brian Reischl:

At NewsGator and Sepia Labs I worked with Brian Reischl, one of the server-side guys. Among other things, he worked on NewsGator’s RSS content service, which reads n million feeds once an hour.

(I don’t know if I can say what n is. It surprised me when I heard it. The system is still running, by the way.)

Brian is intimately acquainted with the different ways feeds can be screwed up. So he posted Stupid Feed Tricks on Google Docs.

I quote the entire thing below for people like me who don’t have Google accounts. The below is all by Brian:

He then goes on to list several dozen edge cases the feed reader had to handle.

Writing software is hard.

PowerShell: Check if Processes Are Waiting and Prompting for User Input

I’m currently writing a PowerShell script that will commit changes to a Mercurial repository, then push those changes out to our central Mercurial server. If there are new changes on the server, the script will need to pull those changes down before it pushes, merge them, commit the merge, then re-push to the server. Rinse and repeat until the push succeeds. Because it will run under our continuous integration build server, it needs to be able to run headless (i.e., non-interactively).

During a merge, Mercurial will sometimes prompt the user for input (e.g. if a file was deleted by someone, but modified by someone else, Mercurial asks if you want to keep the change or keep the deletion). Prompting for input won’t work under a continuous integration server because no one’s around to answer. So, my script needs to detect when Mercurial is prompting for input, kill it, then notify those responsible that manual intervention is necessary.

Google turned up Is My Process Waiting for Input? on StackOverflow. The .NET System.Net.Diagnostics.Process class has a Threads property, which is a collection of ProcessThread objects. If a process is waiting for input, one of its threads will be in a wait state (i.e. its ThreadState property will be Wait), waiting for user input (i.e., its WaitReason property will be LpcReply).

So, to get all process in PowerShell that are waiting for user input, you can do something like this:

Get-Process | Where-Object { 
    Select-Object -ExpandProperty Threads | 
        Where-Object { $_.ThreadState -eq 'Wait' -and $_.WaitReason -eq 'LpcReply' }

Of course, this is the easy part. Here’s my final script. In addition to using Start-Process to start the merge so we can monitor the process while its running, it redirects STDOUT and STDERR to files, so we can see it in our build log.

# Create some files for storing STDOUT and STDERR streams
$randomFileName = [IO.Path]::GetRandomFileName()

$stdOutFile = 'hg.merge.StdOut.{0}' -f $randomFileName
$stdOutFile = Join-Path $env:Temp $stdOutFile

$stdErrFile = 'hg.merge.StdErr.{0}' -f $randomFileName
$stdErrFile = Join-Path $env:Temp $stdErrFile

# Start the merge.
$mergeProcess = Start-Process -FilePath hg.exe `
                              -ArgumentList 'merge','-r',$Revision `
                              -PassThru `
                              -NoNewWindow `
                              -RedirectStandardOutput $stdOutFile `
                              -RedirectStandardError $stdErrFile
        # If the merge every prompts the user for input, kill it.
        Start-Sleep -Milliseconds 100
        $waitingForInput = $mergeProcess.Threads | 
                                Where-Object { $_.ThreadState -eq 'Wait' -and $_.WaitReason -eq 'LpcReply' }
        if( $waitingForInput )
            Write-Error -Message ('Stopping non-interactive merge: Mercurial is prompting for input.') `
                        -RecommendedAction 'Please merge manually.'
            $null = $mergeProcess | Stop-Process -PassThru | Wait-Process
    while( -not $mergeProcess.HasExited )
    # Display STDOUT.
    Get-Content -Path $stdOutFile | Write-Host
    # Display STDERR
    # For some reason, it takes a little time to read the Standard Error file.
    $errors = $null
    $readFile = $false
        Start-Sleep -Milliseconds 100
            $errors = [IO.File]::ReadAllText($stdErrFile)
            $readFile = $true
    while( -not $readFile )
    if( $errors )
        Write-Error -Message $errors
    # Clean up the temporary files used to capture STDOUT and STDERR
    ($stdOutFile,$stdErrFile) | Where-Object { Test-Path -Path $_ -PathType Leaf } | Remove-Item

Monitoring Windows Process Start and Exit Events

Part of my build server management responsibilities include trying to figure out why things are going wrong. I don’t know about other environments, but our builds run a lot of processes. For example, we use Robocopy to individually deploy a website’s sub-directories. When things go wrong, it’s hard to debug because these robocopy processes start and stop so quickly, I don’t have time to double-click them in Process Explorer. I usually resorted to adding lines in our deployment script to output the Robocopy command line and exit code, then running the build, waiting for it to finish, then inspecting the build logs.


Last week, I discovered that Process Monitor captures process start and exit events. Just the information I need in these situations.

Open Process Monitor, and click the “Filter” button.

Create a new condition that matches when the Operation column contains the word process (don’t forget to click the Add button).

Click OK and Process Monitor will start to show process start and exit events. Start events will show the parent process ID and command line used to start the program. Exit events will show the exit/return code and CPU/memory statistics.

If you double-click the start event, you’ll be able to see the entire command line and all the process’s environment variables.

Thanks to the Windows Sysinternals team for the great tools.

Getting X509Certificate2’s “Issued From” and “Issued to” Properties in PowerShell

One of my problems with the X509Certificate2 object is that it doesn’t expose properties for the “Issued From” and “Issued To” data, properties which show by default when viewing certificates in the Windows Certificate MMC snap-in. This makes it difficult to match certificates you see the Certificates MMC with certificates you see in PowerShell.

Today I discovered the GetNameInfo method, which can return the “Issued To/By” text seen in the Certificates MMC snap-in:

Get-ChildItem cert:\CurrentUser\My |
    Format-Table @{ Label = 'IssuedTo'; Expression = { $_.GetNameInfo( 'SimpleName', $false ) } },@{ Label = 'IssuedBy' ;Expression = { $_.GetNameInfo( 'SimpleName', $true ) } }

Here it is a little clearer:

$cert.GetNameInfo( 'SimpleName', $false ) # This is IssuedTo.
$cert.GetNameInfo( 'SimpleName', $true )  # This is IssuedBy.

You could also pipe your certificates to Add-Member:

Get-ChildItem cert:\CurrentUser\My |
    Add-Member ScriptProperty -Name IssuedTo -Value { $this.GetNameInfo( 'SimpleName', $false) } -PassThru |
    Add-Member ScriptProperty -Name IssuedBy -Value { $this.GetNameInfo( 'SimpleName', $true ) } -PassThru |
    Format-Table -Property IssuedTo,IssuedBy -AutoSize

The next version of Carbon will add IssuedBy and IssuedTo properties to X509Certificate2 objects, so you can do this instead:

Get-ChildItem cert:\CurrentUser\My |
    Format-Table IssuedTo,IssuedBy -AutoSize

Carbon v1.0.0 Released

I’m extremely happy to announce the release of Carbon v1.0.0. Thanks to everyone who’s already started using Carbon. Please continue to provide feedback. I love to hear from everyone that’s using it, good or bad. Also, now that we’re at v1.0, don’t hesitate to spread the word!

If you’re on v0.5.0.1, you should be able to upgrade with little or no work as this release is includes mostly fixes for bugs found in that release.

Get the bits here!

Upgrade Instructions

  • Remove the Quiet parameter from the Import-Carbon.ps1 script.
  • If you’re nesting Carbon as a sub-module of another module, STOP. This causes havoc. Create an Import-*.ps1 script for your module which imports Carbon before importing your own module. Update your scripts to import your module with your fancy new Import-*.ps1 script. See Best Practices for Importing PowerShell Modules for details.



  • The Import-Carbon.ps1 script no longer checks if Carbon is a sub-module of another module, so the Quiet parameter was removed. Please don’t nest Carbon in your modules! It will cause havoc.
  • Import-Carbon.ps1 will no longer stop execution if an error occurs during an import (i.e. the $ErrorActionPreference = 'Stop' line was removed).


  • Renamed Install-Service’s Dependencies parameter to Dependency (with backwards-compatible alias), to follow PowerShell naming standards.

Users and Groups

  • Install-User: you can now set a user’s full name with the optional FullName parameter.

Bug Fixes


  • Added Test-IisWebsiteExists alias for Test-IisWebsite, for backwards-compatibility with earlier releases.


  • Grant-Permission returns boolean values to the pipeline when clearing access rules.
  • Added Unprotect-AclAccessRules alias for Protect-Acl, for backwards-compatibility with earlier releases.
  • Updated v0.5.0.0 section of release notes to include a note that Unprotect-AclAccessRules was renamed to Protect-Acl.


  • Install-Service fails if Dependency parameter doesn’t have a value. Sometimes.


Users and Groups

  • Add-GroupMember doesn’t handle when the .NET Active Directory throws an exception when adding members to a group, causing script termination.