Fun with CIM Associations

I have been playing with the new CIM cmdlets in PowerShell 3.0. They are really handy and I am excited to see what Microsoft does with NanoWBEM. It will be really cool to use this technology outside of the Windows stack. I have know how cool WMI and CIM have been for awhile but seeing how easy the new Get-CimAssociatedInstance has got me all excited about WMI associations. Before it took a lot of WQL to query associations. No longer!

Now we can just do something like this:

Get DLLs in a process

Get-CimInstance -ClassName Win32_Process -Filter "Name=PowerShell.exe" | Get-CimAssociatedInstance -Associator Cim_ProcessExecutable
Name
----
c:\windows\system32\windowspowershell\v1.0\powershell.exe
c:\windows\system32\ntdll.dll
c:\windows\system32\kernel32.dll
c:\windows\system32\kernelbase.dll
c:\windows\system32\advapi32.dll
c:\windows\system32\msvcrt.dll
...

This got me excited. I started to dig around for other cool associated classes. Here are a few I found interesting.

Get the COM classes in a COM application

PS C:\Windows\system32> Get-CimInstance -ClassName Win32_ComApplication -Filter "Name='IMAPI2'" | Get-CimAssociatedInstance
Caption : IMAPI2
Description : IMAPI2
SettingID :
AppID : {273541FF-7F64-5B0F-8F00-5D77AFBE261E}
AuthenticationLevel :
CustomSurrogate :
EnableAtStorageActivation : False
LocalService :
RemoteServerName :
RunAsUser :
ServiceParameters :
UseSurrogate : True
Caption : Microsoft IMAPI v2, Raw CD Image Creator
Description : Microsoft IMAPI v2, Raw CD Image Creator
InstallDate :
Name : Microsoft IMAPI v2, Raw CD Image Creator
Status :
ComponentId : {25983561-9D65-49CE-B335-40630D901227}
Caption : Microsoft IMAPI v2, Stream interleave utility
Description : Microsoft IMAPI v2, Stream interleave utility
InstallDate :
Name : Microsoft IMAPI v2, Stream interleave utility
Status :
ComponentId : {27354124-7F64-5B0F-8F00-5D77AFBE261E}
Caption : Microsoft IMAPI v2, Stream concatenation utility
Description : Microsoft IMAPI v2, Stream concatenation utility
InstallDate :
Name : Microsoft IMAPI v2, Stream concatenation utility
Status :
ComponentId : {27354125-7F64-5B0F-8F00-5D77AFBE261E}

….
Get the user logged into a session

PS C:\Windows\system32> Get-CimInstance -ClassName Win32_LogonSession -Filter "LogonId=995" | Get-CimAssociatedInstance -Association "Win32_LoggedOnUser"
Caption Domain Name SID
------- ------ ---- ---
DRISCOLL-DESK\IUSR DRISCOLL-DESK IUSR S-1-5-17

Get network adapters running IPv6

PS C:\Windows\system32> Get-CimInstance -ClassName Win32_NetworkProtocol -Filter "Name='MSAFD Tcpip [TCP/IPv6]'" | Get-CimAssociatedInstance -Association Win32_ProtocolBinding
DisplayName : NVIDIA nForce Networking Controller Driver
Name : NVENETFD
State : Running
Status : OK
Started : True
ServiceName : NVENETFD
MACAddress :�
AdapterType : Ethernet 802.3
DeviceID : 7
Name : NVIDIA nForce Networking Controller
NetworkAddresses :
Speed : 100000000

Get files in a directory share

PS C:\Windows\system32> Get-CimInstance -ClassName win32_share -Filter "Name='Backup'" | Get-CimAssociatedInstance -Association Win32_ShareToDirectory | Get-CimAssociatedInstance -Association CIM_DirectoryCOntainsFile
Compressed : False
Encrypted : False
Size :
Hidden : False
Name : c:\backup\backup.exe
Readable : True
System : False
Version : 1.0.0.9
Writeable : True

Get partitions on a drive

PS C:\Windows\system32> Get-CimInstance -ClassName Win32_DiskDrive -Filter "Caption LIKE 'WDC%'" | Get-CimAssociatedInstance -Association Win32_DiskDriveToDiskPartition
Name NumberOfBlocks BootPartition PrimaryPartition Size Index
---- -------------- ------------- ---------------- ---- -----
Disk #0, Part... 204800 True True 104857600 0
Disk #0, Part... 1250054144 False True 640027721728 1

Generating Workflow Activities from Cmdlets

One thing I noticed about the new Workflow integration into PowerShell was that there actually had to be an implementation of an activity created in order to use a cmdlet as part of a workflow. It is possible to simply call a cmdlet within an inline script block but that seems like a bit of a hack in order to use workflows. In this post I’ll show how to use Add-Type to create a simple .NET wrapper that you can use to expose a cmdlet as an activity.

After studying some of the new PowerShell activities using Reflector, I’ve found that activity class that is capable of being run remotely extends from the base class PSRemotingActivity. This class is part of the Microsoft.PowerShell.Workflow.ServiceCore assembly. This assembly will be in the GAC after you install PowerShell 3.o.

Each of the parameters for the cmdlet is exposed as a InArgument property on the activity class. The InActivity class is an open generic type that should then be the type of the parameter.

There is an abstract method that needs to be implemented as well. The GetPowerShell method checks which parameters are specified and then adds them to a PowerShell instance as a parameter.

Since each one of these activities is very cookie cutter, it was easy to create a PowerShell script that iterates of the parameters of the cmdlet and outputs them as the activity’s code.

I’ve published the script to PoshCode.  It takes a command name and assembly output path. It will create a DLL that can be added to the Visual Studio workflow designer. As an example, I took two of the vWorkspace cmdlets, Connect-QVWFarm and New-QVWComputer, and created activity wrappers for them. I then added their DLLs to the Workflow Toolbox window and created a workflow using the two. On the right hand side you can see the different arguments are the same as the parameters on the cmdlets.

The New-Activity function is lacking on several fronts. It generates an assembly for each one of the types and doesn’t take pipeline input. This could be extended to support multiple types for a single assembly and would be a much better user experience. It also doesn’t take into account cmdlets that do not support remoting. Microsoft has some cmdlets like this and they extend directly from the CodeActivity class rather than the PSRemotingActivity class. Additionally, it does not sign the DLL that is created. Sometimes WF activites need to be placed in the GAC and this requires signing.

Please me know if you find this useful or find something cool after reading this! I think the PowerShell team is onto something with this WF business. ;)

vWorkspace PowerShell Wallpaper

I thought it would be cool to have a vWorkspace PowerShell Wallpaper. I used the existing vWorkspace Wallpaper and added some PowerShell coolness to it. Available here in 1920×1200.

Invoke-DesktopVirtualization

Get-QuestvWorkspace

Building Binary PowerShell Modules – Part 2 – Design Principles and Other Guidelines

I had anticipated that I would be writing this second part about providers rather than design principles, but after listening to a recent Powerscripting Podcast I thought it would be helpful for me to provide a few things I’ve learned from developing the vWorkspace PowerShell module. Many of the points brought up during the podcast were that developers often have no experience with PowerShell so they do not fully understand how a cmdlet or module should be developed. I hope this post can provide some insight into my endeavor.

Develop a Module

Snap-ins were a 1.0 concept that was a step in the wrong direction. A lot of companies developed snap-ins to get on the PowerShell band wagon and now are paying the price because snap-ins are becoming a second class citizen. In PowerShell 3.0, Microsoft didn’t even release any snap-ins, everything is a module. A module is much easier to work with and can easily xcopy deploy. The ability to use a module manifest makes it even easier to customize the user experience or brand it.

Use the pipeline correctly

Cmdlets that can process a collection of items should always accept pipeline input. The user should not have to use a ForEach-Object to use your cmdlet with a collection. Take for instance the Get-Process and Stop-Process cmdlets. Notice that they work together to provide a seamless pipeline experience.

Get-Process VMM* | Stop-Process

If Stop-Process would have been poorly designed the command would look like this.

Get-Process VMM* | ForEach-Object { Stop-Process $_ }

This is a horrible user experience. This is also not what a PowerShell user expects from a cmdlet. Whenever I see a pair of cmdlets with the same noun, such as a Get and then a Set,Invoke,Stop, etc, I expect the cmdlet taking action (the latter), to accept pipeline input from cmdlet writing to the pipeline (the former). There are two ways (of three total) that users will typically use pipeline input. The most common is to simply accept pipeline input of the object as a whole. In the example of Get-Process, the ProccessInfo object is written to the pipeline and the Set-Process cmdlet consumes the object as a whole.

Get-Process -> ProcessInfo -> Set-Process

The second type of pipeline input that is commonly, although less than the first, is pipeline input via property name. In this circumstance, a single property of the pipeline object will be provided to the consuming cmdlet. For example, let’s create a new related cmdlet called Out-Id. Since an Id is not process specific, we want the cmdlet to accept an Id property from any type of object.

[Cmdlet(VerbsCommon.Out, "Id")]
public class OutIdCommand : Cmdlet
{
    [Parameter(ValueFromPipelineByPropertyName=true)]
    public object Id {get;set;}

    public override void ProcessRecord()
    {
        WriteObject(Id);
    }
}

Whenever an object is piped to the Out-Id cmdlet, it will read the Id property from the object and set the Id parameter of the Out-Id cmdlet. It them simply writes that Id to the pipeline.

Get-Process -> ProcessInfo.Id -> Get-Id -> Id

I think this type of pipeline input it undervalued and underused. I would love to seem some cmdlets developed that really harnessed this. I, for one, have never successfully created a cmdlet that could.

The final type of pipeline acceptance is value by remaining arguments. I think it gets its start from the params keyword in C#. It will consume the remaining parameters and pass them to the specified pipeline parameter. Again, I have never used this but in this circumstance I can’t say I’ve ever seen it used.

Write output to the pipeline correctly

The pipeline is intended to be serial. Rather than outputing collections to the pipeline, a single object should be written at a time. This means that if our cmdlet is to output a collection of objects, we need to loop through each one and write each one using WriteObject.

public override void ProcessRecord()
{
     MyObject myObjects[] = GetMyObjects();
     foreach(var obj in myObjects)
     {
        WriteObject(obj);
     }
}

To clean up my cmdlets, in my base class, I created a simple method called WriteObjects.

protected void WriteObjects(IEnumerable objects)
{
    foreach(var obj in objects)
    {
       WriteObject(obj);
    }
}

Now I can change the above code to this.

public override void ProcessRecord()
{
     MyObject myObjects[] = GetMyObjects();
     WriteObjects(myObjects);
}

Consider using Type Formatting XML for objects

Files with the PS1XML extension can be used to format your custom types into a more user friendly experience. Rather than dealing with 30-40 properties on an object, a user will see the ones that are most important. If they wish to get more information, they can simply use the Format-List * command to get all the properties. Depending on your situation, it also gives you the opportunity to add methods or properties that will only make sense on the command line. The System.Management.ManagementObject formatting is a great example of this. The date and time used by WMI is impossible to understand without conversion. The type formmatting adds a simple method to the ManagementObject class to provide a human readable interpretation.

<Name>System.Management.ManagementObject</Name>
<Members>
<ScriptMethod>
<Name>ConvertToDateTime</Name>
<Script>
[System.Management.ManagementDateTimeConverter]::ToDateTime($args[0])
</Script>
</ScriptMethod>

For examples of type formatting look in the PowerShell installation directory. There are all kinds of examples of what Microsoft has done to make particular .NET objects work better on the command line.

C:\windows\system32\windowspowershell\v1.0\

Consider using built in Aliases for cmdlets

Microsoft has many built in aliases for their important cmdlets. For instance Get-Command is gcm or Get-Process is gps. The vWorkspace team decided to create aliases for every cmdlet. I am not entirely convinced this was the best idea but I still think it is helpful. There is some discussion as to whether this is best practice. I recommend reading Kirk Munro’s post on the topic. Always make sure that another alias does not conflict with the one you are creating.

Accept multiple types for input

PowerShell is very forgiving when it comes to actual .NET types. Types can be cast to other types. Consider reading this blog post about PowerShell casting to see how it performs that conversion. When exposing parameters take these kind of conversions into consideration. Being able to type a date as a string rather than using some sort of .NET class or cmdlet is very time saving. If you wish to accept a complex type, consider creating a ArgumentTransformationAttribute on the parameter. This will allow you to write some custom code to convert the user input into the type you are expecting. This makes working with your cmdlets much, much easier.

Validate Early

Consider using ValidationAttributes on your parameters so that input doesn’t even make it past the PowerShell runtime. Using a generalized validation attribute can make the user’s workflow much more predictable. It also makes learning your cmdlet easier. Instead of “now, what does this error message mean…” it will be “oh, I’ve seen this error message before, I have to do this.” Check the list of built in validation attributes before making your own.

Don’t put parameters on base classes

I think this rule may be not always be true but I have found that putting a parameter on the base class, even when you KNOW that every cmdlet will need it, still has some negative side effects. The biggest one for me was the fact that there is no good way to set the positional argument of the ParameterAttribute.

Use Dynamic Parameters Sparingly

Dynamic parameters are cool but mostly for developers. As a developer I feel like there is always a mentality to create a super generic, reusable component and I see this taken too far. Dynamic parameters are kind of like this. It would be cool to have a single class that defines the parameters for both your New and Set but it makes it way harder to understand how to use your cmdlet. You don’t get any syntax help from Get-Help. That’s a deal killer for me.

The one place were I would use dynamic parameters would be within a provider to add additional parameters to cmdlets like Get-Item. The parameters still need to be documented in the provider documentation, but users expect this because that is how some of the pre-existing providers work. I still don’t completely agree with these “hidden” parameters but they are useful.

Make your cmdlets testable

Test driven development is all the hype now! While I’m not a loyal follower of the ideology, I still think that unit testing is required to create good software. If you develop your cmdlets directly on top of the PowerShell subsystem, it may be hard to mock, inject and test later on. Consider wrapping the SessionState, WriteObject, ShouldProcess and other underlying Cmdlet and PSCmdlet members with a layer of interfaces to allow for easy testing later on. Most of the time there should not be that much code in your cmdlet, and more in your business object, but there will always be more logic than you realize and testing it will be necessary. Put a little time in now to save yourself later.

Sign Your Scripts

If you are producing a module, make sure you sign your scripts. It really wraps up everything and gives it that professional seal. It’s a pain in the but so I use the Script Signing add-on for PowerGUI. It makes it way easier. Once it’s configured, I can just open up a script in the editor, click one button to sign the file.

Document Everything

By far the most important documentation you can provide is the built in cmdlet documentation. Get-Help is the crutch of every PowerShell user. I use it for cmdlets that I have used 100 times. Make sure to get the Cmdlet Help Editor to make generating the XAML file. It can be finicky so save often. In addition to cmdlet help the vWorkspace module objects are also documented on a Wiki. This help is pulled right from the XML help found in the source code and put into a WikiMedia import XML file so it can just be imported right into the Wiki. Every build we have a new wiki generated on the file that has all the object and cmdlet help. If it pertains to your module, provide about_*.txt files to document core concepts that can span pages.

I do not believe this encompasses every best practices or that all these are even best practices. This is what I have found has worked for our development process. I would love to hear some feedback about what you have done during the development of cmdlets and modules. I hope to extend this list as I discover new things or think of something to add to the list. I still plan on doing a part on providers. ;)

Parallel Foreach in PowerShell 3.0

I have the CTP 2 of PowerShell 3.0 installed on my Windows 7 machine. I have been digging through the PowerShell team’s getting started guide to Windows Workflow in PowerShell. It is mind blogging to say the least. If you are unfamiliar with WF at all it will be a pretty steep learning curve. I have some experience from developing TFS build processes but still do not feel quite adequete enough to completely wrap my head around the new functionality. One interesting note is that the new Workflow feature supports parallel execution of Foreach blocks. I blogged about this awhile back but came to the conclusion that directly using the .NET classes would not actually be faster and the Foreach was still the way to go.

To use a parallel foreach in CTP 2 workflows you can do the following.

workflow Invoke-ForEachParallel { param([string[]]$computerName)
# The contents of the foreach block will be executed in parallel
foreach -parallel($computer in $computerName) { "Executing on $computer" } }

This got me to thinking, what if we were to execute this same command outside of a workflow.

Notice that the error states that the parameter is reserved for future use. I really hope the future is CTP3!

PowerShell Script for Selecting a Winner

Tonight, at the first Madison PowerShell User Group, we gave away a couple books by Ed Wilson and Jeff Hicks along with some T-shirts. I thought that it would be cool to use PowerShell to select the winner. I wrote a simple little script that would do it in a cool way. The script will loop through the available names and then output the winner with a little bit of animation and some nifty colors in between. Check of this video to see it in action.

You can download the contents of the script here.

Madison PowerShell User Group Meeting Tonight!

Please join us for the first Madison PowerShell User Group Meeting tonight! We have Ed Wilson presenting Windows PowerShell Best Practices. This will be a whole lot of fun and really educational! He is presenting via LiveMeeting so you are welcome to join us remotely. For all that can make the meeting physically, we will be giving away a bunch of cool stuff after the presentation.

For more information head over to the Madison PowerShell User Group blog page.

Hope to see you there!

Perspectives 2.0

I’d like to announce the second version of Perspectives, a Visual Studio 2010 extension for managing windows configurations.  This is what version 2.0 adds.

  • Added a toolbar for quick access to window configurations
  • Added support for favorite configurations that show up on the quick access bar
  • Added a refresh button to perspectives manager so that you could refresh the view of the configurations were missing
  • Reworked the entire perspectives manager window so that it looks much better

To see it in action, check out this video.

You can download from the Visual Studio Gallery or through the extension manager.

If you have any issues or want to leave feedback please head over to the CodePlex site.

vWorkspace PowerShell Prompt

PowerCLI has a very cool little shortcut setup when you install the product. I thought it would be cool to mimic something like that using the vWorkspace PowerShell module. I created a script and shortcut that can be run that configures the PowerShell environment to output the farms that are currently defined, give a few hints as were to start and provides a new cmdlet to jump to the vWorkspace community site.

All you need to do is download the script at the end of this post. Then create a shortcut to PowerShell.exe. It can be found in C:\windows\system32\windowspowershell\v1.0\powershell.exe. I named the shortcut Quest vWorkspace PowerShell Environment. After saving the script off to somewhere I can find it, I set the command line for the shortcut to the following value:

C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -noexit -c “C:\users\adriscoll\documents\windowspowershell\initialize-qvwenvironment.ps1″

Now I have a shortcut on my desktop.

When I open PowerShell from the shortcut I nowhave a vWorkspace-customized PowerShell console. Looks pretty cool!

You can download the script here.

 

PowerGUI QuickConsole 1.1

As per Hal Rottenberg‘s request, I’ve updated the PowerGUI QuickConsole to support PowerGUI Pro! I also fixed a bug where the console would still be running even after you attempted to close out of it. Additionally, because this fix was part of the Add-On API, PowerGUI VSX should now work with PowerGUI Pro as well. I will be pushing that change up to Codeplex before the end of the night. I have pushed this change to Codeplex. The next version of PowerGUI VSX will work with both PowerGUI and PowerGUI Pro. If they are both installed it will use the PowerGUI components.

Download Link: PowerGUI QuickConsole 1.1

If you have problems with this version, you can turn on logging by placing the following XML file in your Documents folder. It will log to your Documents folder into the QuickConsole.txt file.

Download Link for Logging XML file: Logging Config

Subscribe to RSS Feed Follow me on Twitter!