19 Apr 2011

PowerShell Web-Based Logging Handler

by Dave

We have recently been doing a small bit of work with PowerShell, and an ASP.Net front-end.  This was provided to help an IT department manage their users in both AD and unix, whilst creating/disabling Exchange mailboxes and making calls to a number of web services.  So, we created a web front end that called some PowerShell scripts to do the relevant things.  However, we needed to report back to the user on the progress of these scripts, so I created a really simple logging system to do this.

It works by using a generic handler (.ashx) on the web site, which stores the provided string in the application cache (we couldn't use the session state, since the scripts were running under a different context).  A bit of JQuery on the web site performed callbacks to the handler (with different arguments), which returned the contents of the log.  This was then output to a textbox.

It proved monumentally useful, as we were then able to log every bit of feedback from the scripts that would have been otherwise hidden.  We used the following PoerShell functions to call the log handler:

#Log a message back to the admin console
function fnLog($strMessage) 
{
	$wc = New-Object System.Net.WebClient
	$wc.Headers.Add("user-agent", "PowerShell")
	$wc.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials
	$resultString = "http://<url of site>/Log.ashx?Message=" + $strMessage + "&Key=" + $global:LoggingKey
	$result = $wc.DownloadData($resultString)
}


#Tell the admin console that all scripts are complete
function fnComplete($blComplete)
{
	$wc = New-Object System.Net.WebClient
	$wc.Headers.Add("user-agent", "PowerShell")
	$wc.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials
	$resultString = "http://<url of site>/Log.ashx?Complete=" + $blComplete + "&Key=" + $global:LoggingKey
	$result = $wc.DownloadString($resultString)
}

So, the gist of these are that they call the handler with the following arguments:

  • Message; the message to log
  • Complete; true or false, depending on whether or not the scripts are complete
  • Key; a unique key that is set from the web site, and distinguishes between any different sessions

This then adds the relevant messages to the application cache:

Context.Cache[Key] = Context.Cache[Key] + "\\n" + Message;
Context.Cache[Key + "Complete"] = Complete ?? false;

Then, on the web site, we have some JQuery to poll the log handler and retrieve the log messages:

function Log() {
    $.ajax({
        url: 'Log.ashx',
        data: { Key: '<%= this.UserKey %>' },
        dataType: 'json',
        cache: false,
        success: function (data) {
            $('.outputLog').val(data.Message.toString());
            window["LoggingComplete"] = data.Complete;
            $('.outputLog')[0].scrollTop = $('.outputLog')[0].scrollHeight;
        },
        error: function (err) {
        }
    });

    if (window["LoggingComplete"] != true) {
        setTimeout('Log()', 500);
    }
}

When the log handler is presented with no Message or Complete arguments, it just returns the contents of the log:

Context.Response.ContentType = "text/json";
Context.Response.Write(string.Format(@"{{ ""Message"": ""{0}"", ""Complete"": {1} }}", Context.Cache[Key], ((Context.Cache[Key + "Complete"] as bool?) ?? false).ToString().ToLowerInvariant()));

 When the scripts are run asynchronously, this provides a really good way of getting information back to the user.

About me

I am a software developer in the UK, and specialise in Microsoft technologies.

Starting as a web developer working on the British Airways website at Leighton, I progressed to .net development at Aspire Technology Solutions.  A lot of my work has involved SharePoint, and I have implemented numerous solutions using the platform.  Recently I have been involved in a large-scale enterprise application utilising WCF services, and also a large BI databse using SQL Server technologies.

Please download my CV.

MCTS

Month List