> pshdo

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.

Enhancements

General

  • 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.

Certificates

  • 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.

Computer

  • 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.

IIS

  • 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).

Privileges

Bug Fixes

Computer

  • 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
try
{
    do
    {
        # 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
            break
        }
    }
    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
    do
    {
        Start-Sleep -Milliseconds 100
        try
        {
            $errors = [IO.File]::ReadAllText($stdErrFile)
            $readFile = $true
        }
        catch
        {
        }
    }
    while( -not $readFile )

    if( $errors )
    {
        Write-Error -Message $errors
    }

}
finally
{
    # 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.

Ugh.

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