Freigeben über


Exposing the Power of .NET in a Admin-friendly way

One of our primary goals for Monad was to:   "Expose the power of .NET in an Admin-friendly way"

The challenges of systems administration are large and growing at the same time organizations are under ever more pressure to reduce costs and do more with less.  That was true 10 years ago, it is true now, it will be true 10 years from now.

One of the key strategies for dealing with this is leverage the power of communities by participating in user/news groups where you can give and get help and share scripts for solving problems.  As such, we wanted an environment that provided the widest possible dynamic range so that a large and diverse community of users could come together and help one another.  Monad is designed to meet the needs of admins, beginning scripters, advanced scripters and programmers. The other benefit of this approach is that it allows people to start off as admins and migrate to whatever level of programming they want to.  I fully expect Monad to be the vector that some Admins will use to make a career change into software engineering.  I also expect things to go in the other direction as well as is the case in Unix environments where the line between programmers and admins can get quite fuzzy. 

Here is how you should think about this issue:  Most programs/scripts perform 3 activities:

  1. Get Objects
  2. Process Objects
  3. Generate Output

For each of these areas, Monad provides 3 modes of access along a spectrum of Programmer-Oriented to Admin-Oriented.

  1. Raw .NET access
  2. Enhanced .NET access
  3. Command access

Monad provides a sort of Rubic's cube model where you can choose whatever access mode you want for whatever activity you want.  You can use Raw .NET access to get objects, enhanced .NET access to process those objects and then Command access to generate output.  I won't go through all the combinations but you see what I'm getting at? You can do everything at one mode or mix and match to your hearts content.

Raw .Net Access
Monad allows you to write programs that get/process/output raw .net objects just like any other .NET language.  Here is an example written in C# and then in Monad:
C#
  Process myProcess = new Process();
try
{
string myDocumentsPath =
Environment.GetFolderPath(Environment.SpecialFolder.Personal);

    myProcess.StartInfo.FileName = myDocumentsPath + "\\MyFile.doc";
myProcess.StartInfo.Verb = "Print";
myProcess.StartInfo.CreateNoWindow = true;
myProcess.Start();
}
catch (ComponentModel.Win32Exception e)
{
Console.WriteLine(e.Message + ". Check the path.");
}

Monad
  [System.Diagnostics.Process]$myProcess = new-object System.Diagnostics.Process;
trap [ComponentModel.Win32Exception] {
[Console]::WriteLine("Error: " + $_.Error);
}
$myDocumentsPath = [Environment]::GetFolderPath("Personal");
$myProcess.StartInfo.FileName = $myDocumentsPath + "\\MyFile.Doc";
$myProcess.StartInfo.Verb = "Print";
$myProcess.StartInfo.CreateNoWindow = $true;
$myProcess.Start()

That is a small code fragment but you'll see the same thing if you use a larger program, you can pretty much code C# or VB.NET using Monad (in the same way you can code Fortan using C and C using C++).  That is great but if you just wanted to raw .NET access, you are probably better off just using C# or VB.Net.  Monad provides raw .NET access to establish a rockbed of function that ensures that you'll always be able to get the job done.  If you are coding at a higher level and the function you need is not available, you don't have to throw away your work and start over, you can always access the raw .NET layer and do anything. 

Anyone that has done scripting for very long knows exactly what I mean - there are always times when you need to turn the dial to 11 and when you can't - the situation turns gruesome quickly.

Enhanced .NET access
Monad provides gobs of features that enhance and simplify the access to .NET.  I'm sure that entire books will be dedicated to just this one topic so I can't hope to do much here beyond giving a few examples.

In the area of enhancing getting objects, Monad has a number of accelerators for supporting object types widely used in scripting like [xml], [array], [regex], [scriptblock], [hashtable] (in addition to the obvious ones like [int], [string], [double], etc).  In addition to syntactic support for creating these types of objects, Monad has a number of built in conversion rules which simplify getting the object you need when you need it. 

Monad's synthetic type system also makes it easier to get objects than raw .NET access.  This is used to normalize object naming (e.g. Monad provides the NAME property alias for process which maps to PROCESSNAME and ServiceController which maps to SERVICENAME).  This is also used to increase the granularity/usefulness of raw .NET objects.  For instance on the Process object, we add the properties DESCRIPTION, FILEVERSION, PRODUCT, and PRODUCTVERSION.  All of these properties are available from the process object but to get them you have to navigate through the MainModule.FileVersionInfo path.  i.e.
 $p.PRODUCTVERSION == $p.MainModule.FileVersionInfo.PRODUCTVERSION
Another way of saying this is that .NET objects are often designed for the needs of Programmers not Admins and Monad has capabilities to tweak those objects so they meet the needs of Admins.

Monad enhances .NET objects to make it easier to process those objects as well.  Take the following simple expression which tells you how long until Christmas (My daughter's favorite question).  Let's make it a little more precise since what she really cares about is when she can open presents and we've established a firm rule that the kids have to stay in bed until at least 7am on Christmas (established after the 4am wake up incident).  Here is the code I run whenever she asks that question:

MSH> [DateTime]"12/25/2006 7:00" - [DateTime]::now

Days : 245
Hours : 11
Minutes : 4
Seconds : 11
Milliseconds : 359
Ticks : 212078513593750
TotalDays : 245.461242585359
TotalHours : 5891.06982204861
TotalMinutes : 353464.189322917
TotalSeconds : 21207851.359375
TotalMilliseconds : 21207851359.375

This is possible because many .NET types can manipulate strings to create types.  This is an example of where leveraging .NET makes it dramatically easier for Admins to do hard things.  In this case, DateTime takes that string and makes the following data available:

MSH> [DateTime]"12/25/2006 7:00" |fl *

DateTime : Monday, December 25, 2006 7:00:00 AM
Date : 12/25/2006 12:00:00 AM
Day : 25
DayOfWeek : Monday
DayOfYear : 359
Hour : 7
Kind : Unspecified
Millisecond : 0
Minute : 0
Month : 12
Second : 0
Ticks : 633026268000000000
TimeOfDay : 07:00:00
Year : 2006

Many .NET types can do equally powerful things, saving users TONS of effort by parsing strings and making the intersesting elements available as properties and/or exposing powerful methods.  The problem is that some .NET types do this by taking a string constructor, some do it by having a static PARSE() method on the class, others do it by having a CONVERTER for Strings.  Admins don't want to deal with that so Monad takes care of this mechanical housekeeping for the user.  We will look for the appropriate constructor, parse method, or converter necessary to implement the user intentions.  For instance, [DateTime]"12/25/2006" uses the Parse() method whereas [System.Diagnostics.EventLog]"Application" uses a string constructor.

I mentioned that the .NET type then exposes a set of great methods for the user.  I have relatives in the UK so if we want to ship presents to those kids, they need to get in the mail early.  So let's just say that things need to be shipped 35 days in advanced - when do they need to get in the mail?  I have no clue but luckily .NET is smarter than I am so I can do the following:

MSH> ([datetime]"12/25/2006").AddDays(-35)

Saturday, November 20, 2006 12:00:00 AM

So that is great if you knew that the .NET object had a AddDays method and that you could provide it a negative number to do subtraction.  On the other hand, enhanced .NET access is event smarter than raw .NET access so you can just do the following:
MSH> [datetime]"12/25/2006" - "35.0:00"

Monday, November 20, 2006 12:00:00 AM

Monad takes advantage of the fact that the .NET DateTime type support the Op_Subtraction() method. Monad determines that DateTimes can subtract [System.TimeSpan] types so it automatically converts the string "35.0.00" into this format and calls that method. The result of all this is that admins get to think and type this:
   [datetime]"12/25/2006" - "35.0:00"
Instead of this:
   [DateTime] $date = [DateTime]::Parse("12/25/2006 7:00")
[TimeSpan] $t = [TimeSpan]::Parse("35.0:00")
[DateTime] $newdate = [DateTime]::Op_Subtraction($date, $t)
[Console]::WriteLine($newdate.ToString())

When it comes to outputing objects, Monad allows you to enhance the .NET objects by defining overrides for their ToString() method, grouping the objects properties into named propertysets, and to define which of the objects properties are the default display properties. 

Command Access
Command access is the most admin-friendly way to interact with the system.  Commands exist because someone thought about the needs of the Administrator and coded a good user experience on top of the .NET objects.  Let me give you some examples of the what that means (this is not a complete list):

  • Consistent naming of the commands and its parameters
  • Support for wildcards when specifying objects
  • Support for -Whatif -Confirm for actions that have side effects
  • Produce great errors with all the info necessary to recover from the error
  • Distinguish between terminating and non-terminating errors
  • Emit information on output, verbose, debug, and error streams
  • Issue progress records if an operation takes more than a second or two
  • Design the a set of commands and their outputs so that they can easily pipeline together (object properties with names and types that match command parameters)
  • Specify validation metadata on parameters so that the Monad engine
  • Provide meta data to drive the formating of your output objects
  • Provide great help with detailed descriptions and lots of examples

This sounds like a lot and while there is a lot to get right when it comes to providing the right user experience, the amount of code to do this is very small because this is the design center for Monad's SDK.  Most cmdlets are extremely small making them one of the best bargains in the industry.  In fact, that is the way we define what a Monad Cmdlet is:  the very thin layer on top of .NET necessary to provide a great user experience. 

Getting objects through the command access is pretty straight forward.  It is typically done with a Get-XXX command.  These commands often provide a rich set of functions for getting the right objects.  Many support rich wildcarding (e.g. Get-Process [a-t]*[g-v] ) and provide excellent error messages and error handling if and when something goes wrong.  The other way to get objects is to expose a datastore as a system drive and then Monad provides common set of commands to naviagate the datastore and get objects.  (e.g. CD xxx; get-item; get-itemproperty, get-childitem, get-content, etc).

Manipulating objects through commands is done through a rich set of object-based utility commands like: Where, Group, Measure, Tee, Sort, and Compare.

Lastly, there are a wide range commands for outputing results including: Export-CSV, Export-CliXml, Out-File, Out-Printer, Out-Default, Out-String, Out-Host, Format-List, Format-Table, Format-Wide, Format-custom, Write-Object, Write-Progress, Write-Error, Write-Log, Write-Debug, Write-Warning, Write-Host.

So to sum up, users write scripts/programs that get objects, process objects and generate results.  Monad provides 3 modes of doing each of these steps:  raw .NET access, enhanced .NET access and Command access.  Users can mix and match modes to their hearts content. This architecture:

  • Allows users to pick the programming style that matches their thinking style.
  • Allows the Monad engine to eliminate the programming housekeeping hassles so you can focus on problem you want to solve not HOW you are going to solve it.
  • Ensures full and complete functional coverage so that you aren't stuck if a function you want isn't available at the abstraction you want it.
  • Allows admins, basic scripters, advanced scripters and programmers to form a large common community to share information and scripts.
  • Allows individuals to migrate to whatever level of programming they are interested in or is required by the problem at hand.

Man - I just love this stuff! 

Enjoy!
Jeffrey Snover
Monad Architect

Comments

  • Anonymous
    April 24, 2006
    Check the summary of your post on blogs.msdn.com. It is huge!
  • Anonymous
    April 29, 2006
    I love this stuff too :).  One little annoyance I came accross though while reading this post, using the 'correct' format for the date (ie. [DateTime]"25/12/2006 7:00") doesn't work, although at least the other correct format (ie. [DateTime]"2006/12/25 7:00") does.  Shouldn't it follow the computers regional settings for that?  Using [DateTime]::Parse("25/12/2006 7:00") instead works fine, so I guess the conversion Monad is using doesn't simply use the Parse method.