Friday, 17 June 2016

PowerShell as a web application interface

As a web developer I sometimes use PowerShell to aid development and to automate tasks.

With most applications, especially complex ones, it can be handy to have a PowerShell build script to build the application without having to load Visual Studio. I've written scripts in the past to create deployment packages for release to remote servers. I've also used scripts to unzip the packages, create sites in IIS and deploy the website and database to the remote server.

So PowerShell can be extremely useful as a developer to speed up common development tasks.
Recently I've been looking at expanding my PowerShell knowledge and have looked at how to create own binary PowerShell modules in C#.

The process of creating a C# binary module is relatively simple, and there's a good guide here on how to create them, but I wondered how easy it would be to create PowerShell modules to allow me to administer my own web applications, if that's something that I should do, and if that's something that even makes sense.

I have a sample application which is relatively well designed. The application layer and data layer are separated out, and there is a simple MVC web front end which uses this application layer. The database dependencies are injected using Unity.

The application doesn't really do a lot, it has a couple of different entities which are stored in a LocalDB database and accessed using Entity Framework. The MVC front end lists these entities and allows basic add/update/delete actions on these entities.

Creating the modules

I added a new Class Library project to my application solution, and imported the System.Management.Automation package to allow PowerShell modules to be created.

I wanted to create a couple of modules which would be able to perform simple List and Add operations on the database entities, reusing as much of the existing application code as possible.

To start, I created a base class which my modules would inherit from. This would hold the basic database connection settings, so I wouldn't have to write this for every module.

namespace Solution.PS
{
    using System.Management.Automation;
    using Data;
    using Services;
 
    public class PsModuleBase : PSCmdlet
    { 
        protected EntityService EntityService { getset; }
 
        protected override void BeginProcessing()
        {
            var fac = new ApplicationDbContextFactory(@"Data Source=(LocalDb)\MSSQLLocalDB;AttachDbFilename=C:\Source\Samples\Solution\Solution.Web\App_Data\aspnet-Solution-20160510123834.mdf;Initial Catalog=aspnet-Solution-20160510123834;Integrated Security=True");
            this.EntityService = new EntityService(fac);
        }
    }
}

Then, for my modules, I inherited this base class, and used the already existing Query and Add methods, which are part of my Services project to access the database.

namespace Solution.PS
{
    using System.Linq;
    using System.Management.Automation;
    using Data.Entities;
 
    public class PsModules
    {
        [Cmdlet(VerbsCommon.Get, "AppEntity")]
        public class GetAppEntity : PsModuleBase
        {
            protected override void ProcessRecord()
            {
                this.WriteObject(this.EntityService.Query().ToList());
            }
        }
 
        [Cmdlet(VerbsCommon.Add, "AppEntity")]
        public class AddAppEntity : PsModuleBase
        {
            [Parameter(Mandatory = true)]
            [Alias("EntityName")]
            public string Name { getset; }
 
            protected override async void ProcessRecord()
            {
                var e = new Entity { Name = this.Name };
                await this.EntityService.AddAsync(e);
            }
        }
    }
}

Running the application

Building and importing the module into PowerShell means I can now query the Entity list and Add new entity records using PowerShell modules.

>Add-AppEntity -Name NewEntity
>Get-AppEntity

Id             : 1
Name           : NewEntity
_entityWrapper : System.Data.Entity.Core.Objects.Internal.EntityWrapperWithoutRelationships`1[System.Data.Entity.DynamicProxies.Entity_752509F5469DFFCA653EEAEA813AD30DFAE5B07E9F73172F775CB059AB13ABAB]

I could also see via the web interface that the entities were being correctly created as planned.

So, the code worked. I know it's not perfect code, I'd like to see how easy it would be to use Unity to add the same dependency injection to this. I obviously hard coded the full path to the Database file in the PowerShell base class, so this would be nicer if I was using DI, potentially passing this connection information as a parameter to the modules.

The main thing I wanted to test was if this was possible and how easy it would be. The code here is relatively simple, and I could re-use much of the existing logic and classes I already have.

As for whether this approach is sensible? I'm still not sure.

It seems like a good approach to administering web applications, it could allow people familiar with PowerShell scripting to easily import data into a new deployment of a web application. It would only take a few simple commands to import a CSV file of entities straight into the application using the application's own methods, so any internal business logic can still be used.
It could also allow quick queries of the application to check the status of the data.

So to me, it seems to make a certain amount of sense to create an interface like this for some web applications. Part of the reason for questioning this approach was that I couldn't find any similar examples of this on the web, so it doesn't seem like a common practice.

It would be interesting to see some other examples where people have done something similar, or a best-practice approach to extending web applications in this way.