Befriending PowerShell with ADAM

Windows PowerShell is an extensible automation engine consisting of a command-line interpreter and a scripting language. It is completely written in .NET and provides system administrators a powerful tool for automating administration tasks.

Work is performed through so-called cmdlets (pronounced commandlets), which are actually no more than registered implementations of PowerShell API base classes which can be combined at will in PowerShell scripts.

In this post series we'll explore the possibilities of connecting to and querying, as well as performing some administrative tasks in ADAM through the PowerShell interface. We will do this by developing a custom, ADAM-enabled PowerShell snap-in.

Getting PowerShell

PowerShell 2.0 (which is the version we're going to use) is shipped with Windows 7, for other versions of Windows, please refer to http://support.microsoft.com/kb/968929 to download the correct version for your system.

Setting up the project

Using Visual Studio 2010, create a new library project named Adam.PowerShell.Core and add references to the following assemblies:

  • Adam.Core.dll
  • Adam.Tools.dll
  • System.Management.Automation.dll
  • System.Configuration.Install.dll

Also, add a new class named AdamSnapIn and deriving from the PSSnapIn class. The code listing for this class is shown below.

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[RunInstaller(true)]
public sealed class AdamSnapIn : PSSnapIn
{
  public override string Description
  {
    get { return "This is a PowerShell snap-in that provides several ADAM cmdlets."; }
  }

  public override string Name
  {
    get { return "AdamProviderSnapin100"; }
  }

  public override string Vendor
  {
    get { return "ADAM Software"; }
  }
}

This class enables our PowerShell snap-in to register itself within PowerShell and provides descriptive information about the snap-in. All PowerShell providers and cmdlets within the project will automagically be discovered and registered when the snap-in is added to the PowerShell context.

Registering our PowerShell snap-in

In order for our PowerShell provider to work its magic, we need to register it in the PowerShell environment first. This is done by running the InstallUtil.exe utility on the output assembly of the project. The snap-in will register itself using the information provided in the AdamSnapIn class (and its base class).

ASP.NET
1
2
Set-Alias InstallUtil $env:windir\Microsoft.NET\Framework\v2.0.50727\installutil
InstallUtil /i C:\ADAM\Samples\PowerShell\bin\Debug\Adam.PowerShell.Core.dll

You can verify that the snap-in registered correctly by running the Get-PSSnapIn cmdlet with the Registered parameter, as shown below. The output is the list of registered snap-ins and should contain our provider as well.

ASP.NET
1
Get-PSSnapin -Registered

Now that we have registered our snap-in, it still isn't loaded into the current console context. Therefore, we need to execute the Add-PSSnapIn cmdlet, as shown below. If the operation is successful, we will be able to run our own cmdlets.

ASP.NET
1
Add-PSSnapin AdamProviderSnapin100

Adding our first cmdlet

Now we have the basic infrastructure set up, let's create a PowerShell cmdlet that enables us to start the ADAM maintenance manager. Basically, we will create a Adam.Core.CommandLine RunMaintenanceJobs command on steroids.

First off, we start by creating a RunMaintenanceJobsCmdlet class deriving from the Cmdlet class, and adding all available arguments as instance properties in this class. Note that we can use the actual type of the properties, PowerShell will take the burden of trying to convert them to what our cmdlet wants (and report errors accordingly).

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
[Parameter]
public string Registration
{
    get
    {
        return _Registration;
    }
    set
    {
        _Registration = value;
    }
}

[Parameter]
public PSCredential Credential
{
    get
    {
        return _Credential;
    }
    set
    {
        _Credential = value;
    }
}

[Parameter]
public SwitchParameter TakeOver
{
    get
    {
        return _TakeOver;
    }
    set
    {
        _TakeOver = value;
    }
}
        
// Various other parameters go here...

Most notable in the code above is the PSCredential class used as the type for the Credential property. This class is part of the PowerShell API and contains a set of security credentials. As we'll see later, we can use the built-in Get-Credential cmdlet to pop up a credentials dialog and pass this into our parameter.

Also worth mentioning is the SwitchParameter class used by the TakeOver property. This parameter is a switch and will only be set to true if the parameter is actually defined on the command line. The SwitchParameter type automatically takes care of this.

Now let's get some processing logic up in this cmdlet. To implement processing logic, you can override four methods from the Cmdlet base class: BeginProcessing, ProcessRecord, EndProcessing and StopProcessing.

The BeginProcessing method is executed after the command line arguments are parsed and is therefore suitable for initializing resources like streams and database connections. Here we use it to initialize the ADAM maintenance manager and logging system.

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
protected override void BeginProcessing()
{
    // Make sure we can sucessfully log on and execute maintenance jobs.
    App.DemandRole(RoleType.ExecuteMaintenanceJobs);

    // Initialize the log listeners.)
    if (LogManager.Listeners.Count == 0)
    {
        EventLogListener.Source = "ADAM PowerShell Provider";

        LogManager.Listeners.Add(new ConsoleLogListener(LogSeverity.Normal));
        LogManager.Listeners.Add(new EventLogListener(LogSeverity.Normal));
    }

    MaintenanceManager.IsRunningUnattended = true;
}

The ProcessRecord method is invoked for every record passed to the cmdlet. A record here is not an ADAM record, but any object passed to the cmdlet through the pipeline. Our first version will not provide any pipelining support but we're going to override this method to start the maintenance manager threads.

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
protected override void ProcessRecord()
{
    // Creating all the necessary threads...
    _WorkerThreads = new List<Thread>();
    for (int i = 0; i < Threads; i++)
    {
        Thread thread = new Thread(RunMaintenanceJobsThreadStart);
        thread.Start(this);
        _WorkerThreads.Add(thread);
    }

    // Waiting until they're all done...
    for (int index = 0; index < Threads; index++)
    {
        _WorkerThreads[index].Join();
    }
}

private void RunMaintenanceJobsThreadStart(object obj)
{
    // Initialize and run the ADAM maintenance manager.
}

The EndProcessing method is executed when the cmdlet is finishing and the StopProcessing method is called when the user aborts the cmdlet execution using the CTRL+C keystroke. In here we will just log off from ADAM in order to clean up after ourselves.

Running the cmdlet

Let's try and run our cmdlet in PowerShell. First off, remember you need to register the provider and add the snap-in to the console context (scroll up, man!). After this, our cmdlet will be available from the shell:

ASP.NET
1
2
$Administrator = Get-Credential Administrator
Run-AdamMaintenanceJobs -Registration AdamUnitTests -Credential $Administrator

Notice how we use the existing Get-Credential cmdlet to provide a value for our credential parameter. This illustrates how any parameter value can come from variables in PowerShell. You can also inline the Get-Credential cmdlet if you don't want to use variables:

ASP.NET
1
Run-AdamMaintenanceJobs -Registration AdamUnitTests -Credential (Get-Credential Administrator)

Another cool feature of PowerShell worth mentioning at this point is command completion. When you're writing your command, you can use the TAB-key to cycle through commands, parameter names and such.

Wrapping it up

This concludes our little introduction to PowerShell and cmdlet development in combination with ADAM (for now). Given this example, you should be able to develop more potent cmdlets. Also, stay tuned on this series as I will expand this provider further and elaborate more on the intricacies of PowerShell development in future articles.

As always, feel free to share your experiences in the comments. Live long and prosper!

Sample Code

The article contains sample code project(s).
You must be logged in to view or download sample code.
Sign in now

Comments

Leave a comment
You must be logged in to post comments.
Sign in now
 
 
Technical
Business
rss feed