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

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

Rate This
  • Comments 13

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

[Edit: Monad has now been renamed to Windows PowerShell. This script or discussion may require slight adjustments before it applies directly to newer builds.]

PSMDTAG:DOTNET: raw .net Access, Enhanced .NET Access, Environment, System.Diagnostics.Process
PSMDTAG:PHILOSOPHY: Cast strings to .NET objects using PARSE(), Constructors, and/or Converters.
PSMDTAG:PHILOSOPHY: Expose the power of .NET in an Admin-Friendly way.
PSMDTAG:PHILOSOPHY: Leverage a synthetic type system to project an admin-friendly view of objects.
PSMDTAG:PHILOSOPHY:Command provide the best Admin experience.
PSMDTAG:PHILOSOPHY:Allow the mix-and-match of raw .net access, enhanced .net access and command access.

Leave a Comment
  • Please add 2 and 1 and type the answer here:
  • Post
  • Coming from a C# background, I just love writing scripts in PowerShell.

    Just today, I've been able to write tons of useful deployment scripts using my .NET knowledge, that I could not have done so quickly in vbscript.

    Can you easily create a hash of hashes in vbscript ? Dunno, but I just did in PowerShell. Got the job done and the Admin guys can look at what I did, understand it, modify it to their hearts content.




  • Okay, this is a rather impossible request (obviously it's way too late), but is there a chance you'll ever redo PowerShell's syntax? Please, please consider it in the future, especially if it's ever going to be shipped with Vista.

    I love PowerShell and I use it all the time, but the syntax is a nightmare to learn with all of its crazy quirks. It really isn't admin-friendly or coder-friendly at all. I wanted to use it for an application and the syntax was just way too cryptic and unintuitive. Often it's even dangerous. Like this:

    #PowerShell's syntax causes dangerous problems generating incorrect results and no error for seemingly innocent expressions:
    function Pow($var, $exp) { [Math]::Pow($var, $exp) }
    Pow(2, 8) + 3 #invalid (you'd expect it to work)
    Pow 2 8 + 3 #valid, BUT INCORRECT RESULT (256)
    (Pow 2 8) + 3 #vald (259)
    #The solution would be to ditch the 3, 5 syntax for instantiating arrays to resolve ambiguity. Really, {3, 5} and 3; 5 are good enough.

    #PowerShell requires you to know whether a function is a cmdlet/script or a .NET method. Even though this method does the exact same thing as the method above, the syntax differs:
    [Math]::Pow 2 8 + 3 #invalid
    [Math]::Pow(2, 8) + 3 #valid

    #Certain universally recognized aliases for common tasks don't exist:
    ls #aliased
    dir #aliased
    new #not aliased (new-object)

    #Calling a default constructor requires its own special syntax:
    new-object collections.arraylist(5) #valid
    new-object collections.arraylist() #invalid
    new-object collections.arraylist #valid

    #Since quotes are not required for most cases, we have to MEMORIZE all of the cases where they are (string literals should just always be quoted; this would also relieve the need for special tokens to indicate variables, which is way too 1980s):
    echo hi #valid
    echo #hi #invalid, of course
    echo "#hi" #valid
    echo @hi #invalid
    echo %hi #valid
    echo ^hi #valid
    echo &hi #invalid

    Also, the no plurals thing: it doesn't work. The convention in .NET is to signify collections as plurals, and PowerShell's no-plurals policy is a real lump in the pudding. get-member would make a lot more sense if it were called get-members.


    Yeah, I know, this isn't the right place to be rambling about this. But really, this is bugging me a lot. Please, let the next version have improved syntax. Otherwise, I guarantee you, PowerShell will be immediately obsoleted by other new languages, perhaps on other platforms.
  • Okay, this is a rather impossible request (obviously it's way too late), but is there a chance you'll ever redo PowerShell's syntax? Please, please consider it in the future, especially if it's ever going to be shipped with Vista.

    I love PowerShell and I use it all the time, but the syntax is a nightmare to learn with all of its crazy quirks. It really isn't admin-friendly or coder-friendly at all. I wanted to use it for an application and the syntax was just way too cryptic and unintuitive. Often it's even dangerous. Like this:

    #PowerShell's syntax causes dangerous problems generating incorrect results and no error for seemingly innocent expressions:
    function Pow($var, $exp) { [Math]::Pow($var, $exp) }
    Pow(2, 8) + 3 #invalid (you'd expect it to work)
    Pow 2 8 + 3 #valid, BUT INCORRECT RESULT (256)
    (Pow 2 8) + 3 #vald (259)
    #The solution would be to ditch the 3, 5 syntax for instantiating arrays to resolve ambiguity. Really, {3, 5} and 3; 5 are good enough.

    #PowerShell requires you to know whether a function is a cmdlet/script or a .NET method. Even though this method does the exact same thing as the method above, the syntax differs:
    [Math]::Pow 2 8 + 3 #invalid
    [Math]::Pow(2, 8) + 3 #valid

    #Certain universally recognized aliases for common tasks don't exist:
    ls #aliased
    dir #aliased
    new #not aliased (new-object)

    #Calling a default constructor requires its own special syntax:
    new-object collections.arraylist(5) #valid
    new-object collections.arraylist() #invalid
    new-object collections.arraylist #valid

    #Since quotes are not required for most cases, we have to MEMORIZE all of the cases where they are (string literals should just always be quoted; this would also relieve the need for special tokens to indicate variables, which is way too 1980s):
    echo hi #valid
    echo #hi #invalid, of course
    echo "#hi" #valid
    echo @hi #invalid
    echo %hi #valid
    echo ^hi #valid
    echo &hi #invalid

    Also, the no plurals thing: it doesn't work. The convention in .NET is to signify collections as plurals, and PowerShell's no-plurals policy is a real lump in the pudding. get-member would make a lot more sense if it were called get-members.


    Yeah, I know, this isn't the right place to be rambling about this. But really, this is bugging me a lot. Please, let the next version have improved syntax. Otherwise, I guarantee you, PowerShell will be immediately obsoleted by other new languages, perhaps on other platforms.
  • PingBack from http://blogs.msdn.com/powershell/archive/2006/07/23/Issues_with_Windows_PowerShell_syntax.aspx
  • I am trying to get a remoting script working using powershell. Essentially, I want to connect to a server using GetObject but I keep getting an exception. Here is the snippet of the script I am using:

    [System.Reflection.Assembly]::LoadWithPartialName("mscorlib")
    [System.Reflection.Assembly]::LoadFrom("c:\windows\system32\myremoting.dll")
    [System.Reflection.Assembly]::LoadWithPartialName("System.Runtime.Remoting")

    $channel = New-Object System.Runtime.Remoting.Channels.Ipc.IpcChannel;
    [System.Runtime.Remoting.Channels.ChannelServices]::RegisterChannel($channel, 0);

    $svr = [System.Activator]::GetObject([MyRemotingNamespace.ISvrObject],
    "ipc://ServerApplication/SvrObject");
    [string]$str = $serverApp::GetMyMethod();

    [System.Runtime.Remoting.Channels.ChannelServices]::UnregisterChannel($channel);

    The exception is at calling the GetObject method. Any pointers would be appreciated.

    Thanks,
    Al.
  • I guess it needs time to soak in.  I went crazy with VBScript, then Visual Basic.  But I finally DID get it, after a few months.  Everything takes time, and this being a new concept, we'll just have to work with it a while to see the benefits that are alrady obvious to others.
  • You'd think that an admin-friendly language would be one that's easy to learn and hard to make crazy mistakes in.

    They really should have designed it with the assumption that there'd be some kind of Intellisense-ish features to go with it.

    I'm not quite convinced that the cmd.exe UI won't be improved upon given PowerShell's inevitable popularity.
  • > They really should have designed it with the assumption that there'd be some kind of Intellisense-ish features to go with it.

    PowerShell is designed to support Intellisense.  It is just that the actual support for it did nto get into V1.  We are all sad about that.  We made Tab-Completion user-expandable so that the community could address this in V1.0

    Jeffrey Snover [MSFT]
    Windows PowerShell/Aspen Architect
    Visit the Windows PowerShell Team blog at:    http://blogs.msdn.com/PowerShell
    Visit the Windows PowerShell ScriptCenter at:  http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx
  • What I meant to say was that the language itself should have been made to make full use of Intellisense, the same way Hungarian Notation was thrown out for the rest of .NET because Intellisense makes it unnecessary.

    You justified the no-plurals thing by the lack of Intellisense, so I was somewhat under the impression that you guys weren't considering it at all. (Actually, tab completion is enough to make the no-plurals thing unnecessary to be honest.)

    Sorry to beat a dead horse but it really bothers me :(

    >> Even though most Japanese people have a hard time knowing _how_ to use plurals, most of them know one when they see one.
    >Therein lies the problem.  With envrionments like Visual Studio, you will SEE things (via intellisense) and be able to RECOGNIZE them.  With a command line environment, you have to RECALL what to type.  RECOGNITION VS RECALL is the central theoretical split between GUI and CLI environments.
  • By the way, out of curiosity, once Vn is out, will there be many cases where PowerShell is used in environments that Intellisense won't be available? For instance, would PowerShell be used much outside the normal Windows GUI?
  • > tab completion is enough to make the no-plurals thing unnecessary to be honest.

    You make a really good point.  That said, intellisense is a feature of the host and we are providing merely one of the hosts.  I hope that hosts all provide intellisense but who knows.

    Jeffrey Snover [MSFT]
    Windows PowerShell/Aspen Architect
    Visit the Windows PowerShell Team blog at:    http://blogs.msdn.com/PowerShell
    Visit the Windows PowerShell ScriptCenter at:  http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx

  • PingBack from http://www.ditii.com/2007/07/03/windows-powershell/

  • PingBack from http://greatfornews.com/addandsubtractwmidatetime.html

Page 1 of 1 (13 items)