An Administrator's guide to PowerShell

Saturday, January 6, 2007

A Powershell Adventure: Chpt2 ‘Using .NET in Powershell'

Using .NET in Powershell (admins view)
------------------------------------------------------------
One of the best features of POSH is the direct access to the .NET Object model. Unfortunately... this was one of my biggest obstacles. I’m not a developer and most of the concepts of programming are somewhat foreign to me.

When I started learning POSH I didn’t know the difference between Classes, Interfaces, constructs, or members and I didn’t truly understand the concept of objects (from a programmers point of view.) I think these concepts are critical to learning and using .NET with POSH. I also found that knowing a little c# (at least be able to read it) has become incredible helpful.

I would like to try to explain these concepts without getting to developery. As in all the things I post.. I like to explain by examples so I will be providing some code to help me through the process. I will also try (with my limited ability) to give some basic guidance in C# > POSH translation (this is critically helpful as most .NET examples are written in c#.)

Ok... Lets start with Definitions. Again.. if you’re a Developer... Please feel free to comment on anything I may not be exactly right on. These definitions are my opinion on what these concepts entail.

Definitions
===========
.NET: Blackboxed code that accepts specific input and returns either a value or object. Basically, Microsoft did all the coding for you. You just have to call on it correctly... MSDN is invaluable resource for this.

Wiki - http://en.wikipedia.org/wiki/Microsoft_.NET_Framework

Class: Almost everything I reference in .NET is a class. I like to think of a class as a template for an Object. A class is definition of what an object should look like. What properties/methods it should have. For Example... a Microsoft.Win32.RegistryKey object should have Name Property and a GetValue method.

Wiki - http://en.wikipedia.org/wiki/Class_%28computer_science%29

Members: Every class has members. Members are the properties and methods combined. It is a good place to look if you just want to see and overview of what a class has to offer. Check this out.
http://msdn2.microsoft.com/en-us/library/microsoft.win32.registrykey_members.aspx

Properties: Properties are define by a class as attributes of an object. Microsoft.Win32.RegistryKey class has properties of Name, SubkeyCount, and Value Count. So every Microsoft.Win32.RegistryKey object can have those properties.

Wiki- http://en.wikipedia.org/wiki/Property_%28programming%29

Methods: Methods are also defined by the class but instead of an attribute its more like a function of the class. Microsoft.Win32.RegistryKey class has CreateSubKey, DeleteSubkey, SetValue, and so on. So... just like properties every Microsoft.Win32.RegistryKey object can have those methods.

Wiki - http://en.wikipedia.org/wiki/Method_%28computer_science%29

Constructor: I define Contructs as the information or objects required to create an instance of a class. Lets use System.Data.SqlClient.SqlCommand as an example. It has 4 different ways you can create an object from the class. Each way creates and object with slightly different data.
Ref: http://msdn2.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.sqlcommand.aspx

Wiki - http://en.wikipedia.org/wiki/Constructor_%28computer_science%29

Static Methods: These are just like methods, but are availible without having to create an instance of the object. The reason I single these out is because in Powershell... accessing static functions is really simple. All you have to do is to [.NET Class]::StaticMethods("arguments"). Here is an example [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey.

Now that you know that...

Lets look at some examples of .NET use in POSH.
Lets look at Eventlog access using .NET
PS C:\> $evtLog = new-object system.diagnostics.eventlog
PS C:\> $evtLog.Log = "Application"
PS C:\> $evtLog.Entries
Index Time Type Source EventID Message
----- ---- ---- ------ ------- -------
1350 Nov 19 15:54 Warn Alert Manager Eve... 257 VirusScan Enterprise: Would be blocked by behaviour blocking
1351 Nov 19 15:54 Warn Alert Manager Eve... 257 VirusScan Enterprise: Would be blocked by behaviour blocking


Lets look at a couple of .NET static method Calls

Lets call Now method of System.DataTime
PS C:\> [system.datetime]::now
Monday, January 08, 2007 7:31:31 PM

Lets call GetLogicalDrives method of System.Environment
PS C:\> [System.Environment]::GetLogicalDrives()
A:\
C:\
D:\
E:\
F:\


One last one... System.Math. We will use the static method pow. What is 83 to the second power anyway?
PS C:\> [system.math]::pow(83,2)
6889


C# to Powershell translation
C# Code taking from:
http://www.csharp-station.com:80/Tutorials/AdoDotNet/Lesson07.aspx
using System;
using System.Data;
using System.Data.SqlClient;

// create and open a connection object
conn = new SqlConnection("Server=(local);DataBase=Northwind;Integrated Security=SSPI");
conn.Open();

// 1. create a command object identifying
// the stored procedure
SqlCommand cmd = new SqlCommand("CustOrderHist", conn);

// 2. set the command object so it knows
// to execute a stored procedure
cmd.CommandType = CommandType.StoredProcedure;

// 3. add parameter to command, which
// will be passed to the stored procedure
cmd.Parameters.Add(new SqlParameter("@CustomerID", custId));

// execute the command
rdr = cmd.ExecuteReader();

// iterate through results, printing each to console
while (rdr.Read())
{
Console.WriteLine(
"Product: {0,-35} Total: {1,2}",
rdr["ProductName"],
rdr["Total"]);
}

PowerShell:
$srv = "srv1"
$db = "Northwind"
$conn = new-Object
System.Data.SqlClient.SqlConnection("Server=$srv1;DataBase=$db;Integrated
Security=SSPI")
$conn.Open() out-null

$cmd = new-Object System.Data.SqlClient.SqlCommand("CustOrderHist", $conn)
$cmd.CommandType = [System.Data.CommandType]'StoredProcedure'

# This Parameter Line This will error if Parameters
# are not accepted by Stored Procedure.

$cmd.Parameters.Add("@CustomerID","ANATR") out-Null

$rdr = $cmd.ExecuteReader()

While($rdr.Read()){
Write-Host "Product Name: " $rdr['ProductName']
}

$conn.Close()
$rdr.Close()

Ok... Now to explain a couple of differences.

I think the key part of C# to Powershell translation is understanding the Namespace. In Powershell (at least as far as I know) you are unable to include namespaces. Therefore you have Fully Qualify any .Net classes you want to us. This is not the case in C#. In C# you are able to include namespaces. i.e. System.Data.SqlClient. This makes the translation a little more complicated because you have to figure out what namespace the class is from. Lets look at This example.
conn = new SqlConnection("Server=(local);DataBase=Northwind;Integrated Security=SSPI");
How do I know what SqlConnection is? You best option is to google/MSDN it. If that is unavailble you can look that the using statements and see which make sense. In this case it is using System.Data.SqlClient.
So that in PowerShell is
$conn = new-Object
System.Data.SqlClient.SqlConnection("Server=$srv1;DataBase=$db;Integrated
Security=SSPI")


Another important issue is strongly typing. While you can strongly type in Powershell you don’t have to. In my understanding of C# you have to strongly type variables. Not only that but you have instantiate them as well. This illustrated in this line
SqlCommand cmd = new SqlCommand("CustOrderHist", conn);
This line in Powershell is (notice the fully qualified class
$cmd = new-Object System.Data.SqlClient.SqlCommand("CustOrderHist", $conn)


Summary: As I hope you can see. Using .NET in Powershell is pretty strait forward and actually if you use PowerShell at all... you use .NET regularly, but maybe not as directly. Please feel free to leave comments. Also... please let me know if anything is unclear.

1 comment:

Anonymous said...

On namespaces, the System namespace is always included, so you can do:

[Environment]::GetLogicalDrives()

instead of the full [System.Environment]

In addition, if you use a longish namespace a lot, you can assign it to a variable, eg:

$m=[Math]
$z=$m::Max(3,4)


When using new-object you can of course do this:

$coordcls="Management.Automation.Host.Coordinates"
$p=new-object $coordcls (4,9)


It would be nice if PS1 had a using/import mechanism though

About Me

Montclair, NJ, United States

technorati

Add to Technorati Favorites