And now for something completely different: The eTrade API

Right after I created the TFS Build Monitor for Android, I started to dream up new, ingenious ideas that would bring fame and fortune (I know this is why everyone gets into software development :D ). The TFS app didn’t sell a single download when it was 99 cents. When it was free it did a bit better but advertising in an app is like putting a billboard in the middle of the jungle. I would most likely need thousands of downloads to achieve any kind of profit. Even then it probably wouldn’t account for the hours and hours I spent working on the app.

After looking at the app landscape for Android, I came to the conclusion that people with money would most certainly be the ones to pay for an app. What better way than to integrate with a platform that manages money? I have had an eTrade account for a while so I decided this would be a good avenue to explore. Better yet, they didn’t offer an Android app. Gold mine!

Not really…

Exploring the eTrade API

The eTrade API is something you need to apply for. Like many APIs that manage real, physical assets (I guess that could be argued…) a developer key is required. This way they have all your information in case you were to create an app that was malicious in any way. The API was pretty rich. It allows you to look up quotes, return portfolio data, and even buy stocks and options. There were a few things lacking at the time I was working on the API. Mainly, there was no way to return a set of stock data over a period of time. This type of data is necessary for drawing charts. What good is a stock app that can’t draw charts?

The eTrade API uses OAuth to authenticate the application and user. Once the user logs in to the app, they are redirected to the eTrade website within the browser. After accepting some terms and agreements, they can permit the app to access their data. It’s a pretty easy system to get up and running. They even offer a Sandbox URL so you can test your app without acting on any live data.

Here is the code I used to initiate this communication. I’ve removed error handling to make it more succinct.

	public String userAuthorize(Context ctx)
	{
		OAuthAccessor accessor = defaultAccessor();
		OAuthClient client = new OAuthClient(new HttpClient4());
		ArrayList> params = new ArrayList>();
		params.add(new OAuth.Parameter(OAuth.OAUTH_CALLBACK, "oob"));
		client.getRequestToken(accessor, OAuthMessage.GET, params);
		Config.setRequestToken(accessor.requestToken, ctx);
		Config.setRequestTokenSecret(accessor.tokenSecret, ctx);
		return accessor.consumer.serviceProvider.userAuthorizationURL+
		                                  "?key="+accessor.consumer.consumerKey+
		                                  "&token="+accessor.requestToken;
	}

Once the we receive an access token we are free to access the protected resources on the eTrade server. This is done through a RESTful API. I boiled down the actual accessing of the resources into a single method. Again, I’ve removed error handling.

	public  T getProtectedResource(RestRequest request, Boolean isRetry, Context ctx) throws EtradeException
	{
		ArrayList params = new ArrayList();
		OAuthClient oclient = new OAuthClient(new HttpClient4());
		OAuthAccessor accessor = defaultAccessor();
		accessor.tokenSecret = Config.getAccessTokenSecret(ctx).trim();

		OAuth.Parameter param = new OAuth.Parameter("oauth_token", Config.getAccessToken(ctx));

		params.add(param);
		OAuthMessage omessage = null;

		if (request.hasBody())
		{
			omessage = accessor.newRequestMessage(request.getRequestType(), request.getSandboxUrl(), params, new ByteArrayInputStream(request.getBody().getBytes("UTF-8")));
			omessage = oclient.invoke(omessage, ParameterStyle.AUTHORIZATION_HEADER);
		}
		else
		{
			omessage = oclient.invoke(accessor, request.getRequestType(),  request.getSandboxUrl(), params);
		}

		return request.getResponse(omessage.readBodyAsString());
	}

For the generic type parameter in this method, I would specify the object that I would like returned from the REST service  (like a Quote object) . The getReponse method of the RestRequest class would return it for me. The RestRequest class is the base class for all the different types of requests I could make to the eTrade API. I had a class for Quotes, Accounts, AccountPositions, etc.

For example, if I wanted to query the Sandbox for a particular quote, I would access the Sandbox URL of the QuoteRequest class. The QuoteRequest class returns Quote objects.

	public String getSandboxUrl() {
		String url = "https://etwssandbox.etrade.com/market/sandbox/rest/quote/";

		if (strTickers != null)
		{
			url += strTickers;
		}
		else
		{
			for(String ticker : tickers)
			{
				url += ticker + ",";
			}

			url = url.substring(0, url.length() - 2);
		}

		Log.d("QuoteRequest", url);

		return (url + "?detailFlag=ALL");
	}

Responses are returned as XML and can be easily parsed into objects. The QuoteRequest class also has a method for parsing the returned XML into a Quote object. Here is a list of some of the objects that you can access through the API.

  •  Quotes
  • Accounts
  • Account Positions
  • Orders
  • Alerts
The API was pretty easy to work with. The eTrade development team is also really great about responding to questions. My only problem was that eTrade came out with an Android app when I was about half way through development of my own. I should have expected this. They already had Blackberry, iPhone, and iPad apps. It was only a matter of time before they came out with their own Android version. The Android version has all kinds of charts and is actually very well done. It would be cool to see them include more functionality into the API. Who knows? Maybe it’s has more to offer now.

 

Granting Access Rights to SMB Shares in PowerShell 3 and Windows 8

My last post outlined some of the struggles I’ve had when dealing with some of the CIM\WMI based cmdlets in Windows 8. Today I spent some time working with the SmbShare module. I ran into some strange issues while dealing with the access control cmdlets. Here is what I ran into and how to deal with it.

The get cmdlet works just find for share access.

PS C:\Users\Administrator> get-smbshareaccess -Name share
 
Name                    ScopeName               AccountName             AccessControlType       AccessRight
----                    ---------               -----------             -----------------       -----------
Share                   *                       Everyone                Allow                   Read

I started to run into problems when I attempted to revoke permissions to the SMB share. Running the revoke SmbShareAccess with just the same of the share returned an error, INVALID_PARAMETER. Piping to the cmdlet did not help either.

PS C:\Users\Administrator> Revoke-SmbShareAccess -Name Share
Revoke-SmbShareAccess : INVALID_PARAMETER
At line:1 char:1
+ Revoke-SmbShareAccess -Name Share
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (MSFT_SmbShare (...copeName = "*"):ROOT/Microsoft/Windows/SMB/MSFT_SMBS
   hare) [Revoke-SmbShareAccess], CimException
    + FullyQualifiedErrorId : MiClientApiError_InvalidParameter,Revoke-SmbShareAccess
 
PS C:\Users\Administrator> Get-SmbShareAccess -Name Share | Revoke-SmbShareAccess
Revoke-SmbShareAccess : INVALID_PARAMETER
At line:1 char:34
+ Get-SmbShareAccess -Name Share | Revoke-SmbShareAccess
+                                  ~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (MSFT_SmbShare (...copeName = "*"):ROOT/Microsoft/Windows/SMB/MSFT_SMBS
   hare) [Revoke-SmbShareAccess], CimException
    + FullyQualifiedErrorId : MiClientApiError_InvalidParameter,Revoke-SmbShareAccess

I figured the parameters couldn’t be invalid, but rather missing. Specifying the account name fixed the problem but the error message provided nearly no information this was the case.

PS C:\Users\Administrator> Get-SmbShareAccess -Name Share | Revoke-SmbShareAccess  -AccountName Everyone
 
Name                    ScopeName               AccountName             AccessControlType       AccessRight
----                    ---------               -----------             -----------------       -----------
Share                   *                       Everyone                Deny                    Full

Finally, I wanted to grant the privilege back to the Everyone user. I attempted to do the exact opposite of the revoke and was presented with this error message.

PS C:\Users\Administrator> Grant-SmbShareAccess -Name Share -AccountName Everyone
Grant-SmbShareAccess : INVALID_PARAMETER
At line:1 char:1
+ Grant-SmbShareAccess -Name Share -AccountName Everyone
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (MSFT_SmbShare (...copeName = "*"):ROOT/Microsoft/Windows/SMB/MSFT_SMBS
   hare) [Grant-SmbShareAccess], CimException
    + FullyQualifiedErrorId : MiClientApiError_InvalidParameter,Grant-SmbShareAccess
 
PS C:\Users\Administrator> Get-SmbShareAccess -Name Share | Grant-SmbShareAccess -AccountName Everyone
Grant-SmbShareAccess : INVALID_PARAMETER
At line:1 char:34
+ Get-SmbShareAccess -Name Share | Grant-SmbShareAccess -AccountName Everyone
+                                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (MSFT_SmbShare (...copeName = "*"):ROOT/Microsoft/Windows/SMB/MSFT_SMBS
   hare) [Grant-SmbShareAccess], CimException
    + FullyQualifiedErrorId : MiClientApiError_InvalidParameter,Grant-SmbShareAccess

After enumerating some of the parameters on the Grant-SmbShareAccess cmdlet I finally found the one that would allow it to succeed.

PS C:\Users\Administrator> Get-SmbShareAccess -Name Share | Grant-SmbShareAccess -AccountName Everyone -AccessRight Full
 
 
Name                    ScopeName               AccountName             AccessControlType       AccessRight
----                    ---------               -----------             -----------------       -----------
Share                   *                       Everyone                Allow                   Full

After I realized what I was missing it made sense that the errors were being presented. The AccessRight parameter accepts Full, Read, Change, and Custom. The thing I really dislike is the fact that the error messages are just the result of WMI error messages translated to a CimException with almost no information. I really hope the Consumer Preview fixes this!

Block-SmbShareAccess vs Revoke-SmbShareAccess (Unblock\Grant)

The difference between these two sets of cmdlets is that block and unblock require no AccessControlType while Revoke and Grant do. Blocking a share has the effect of revoking Full access to the share by the account name specified. The block\unblock will actually create a new ACL record on the share. I’m not entirely sure on the affect of this.

PS C:\Users\Administrator> Block-SmbShareAccess -Name share -AccountName everyone
 
Name                    ScopeName               AccountName             AccessControlType       AccessRight
----                    ---------               -----------             -----------------       -----------
Share                   *                       Everyone                Deny                    Full
Share                   *                       Everyone                Allow                   Full 
 
PS C:\Users\Administrator> Unblock-SmbShareAccess -Name share -AccountName everyone
 
Name                    ScopeName               AccountName             AccessControlType       AccessRight
----                    ---------               -----------             -----------------       -----------
Share                   *                       Everyone                Allow                   Full

The SMB cmdlets are pretty cool. Mostly because they are so easy to use. The error messages have much to be desired as most cmdlets I have run into in the dev preview that are based off CIM classes.

 

Debugging Poor Error Messages in PowerShell v3

The new CIM IDE is great for creating new cmdlets. It is probably one of the reasons that there are so many new cmdlets in Windows 8. The problem I’m starting to see if that these cmdlets behave very poorly. Take for instance the Set-NetAdapterAdvancedProperty cmdlet. When attempting to do the following (research for my book) I starting running into the following error.

Ouch. That is not a good error message. It looks a lot like the WMI messages you might see when using Invoke-CimMethod or Invoke-WmiMethod. Since a lot of cmdlets have been generated using the CIM IDE it isn’t much of surprise that this type of error is rearing its ugly head in our “native cmdlets” now. After a bit of spelunking I found that the WMI operational log in the event viewer had a bit more information.

In addition to telling me where the error was generated, it also gave me an error code. A quick convert to hex:

1
2
(-2147217407).ToString("X")
80041001

After some Googlin it looks like this is a Generic Failure error. Double ouch… I figured I should try and execute this method outside of PowerShell. There is a nice little utility that comes baked into Windows called wbemtest. It lets you connect to WMI namespaces and do all kinds of things like look up classe, instances, and even execute methods.

Notice that I am connected to the root\standardcimv2 namespace. I used a couple scripts by the Scripting Guys to locate where the offending MSFT_NetAdapterAdvancedPropertySettingData (love the name) lives. Clicking the Open Class button lets me type in the class name and now I’m off! It also lets me peruse the parameter information for these yet-to-be-documented classes.

If I navigate back to the first view I can now execute the method that I want to. Just type in that awesome class name and we should get a drop down of static methods we can execute. I’m going to execute the GetByDisplayName method. Click the Edit In Parameters button, scroll down and double click DisplayName, change Value to “Not Null” and enter the string “Jumbo Packet” (with the quotes). Click save property and then close. Now on the execute method dialog we can click execute. Method Executed Successfully! Woohoo! Now if we click the Edit Out Parameters we can access the returned values. Again scroll down the list to the cmdletOutput property. Double click it and then click the View Embedded button. This will present us with a big list of query results…mine didn’t filter correctly…maybe yours will.

One thing I’ve noticed is that if we open the JumboPacket instance we can scroll down and find a property ValidRegistryValues! Whoa that looks promising.

Then I think to myself, I bet I can find this using a Select *…

Yep, well that was dumb. Although that exercise was nice…I still would really like to actually set this setting. Even when I use valid values I still can’t set the setting. I’ve tried the following.

1
2
3
4
#Trying a string because the property is report as a CIM_STRING|CIM_ARRAY in wbemtest
Get-NetAdapterAdvancedProperty -DisplayName "Jumbo Packet" | Set-NetAdapterAdvancedProperty -RegistryValue "9014"
#Trying a string array 
Get-NetAdapterAdvancedProperty -DisplayName "Jumbo Packet" | Set-NetAdapterAdvancedProperty -RegistryValue @("9014")

I’m at a loss. I’ve made sure to run the command prompt as administrator and nothing seems to set the setting. I’m still seeing the Generic Failure in the operational log and my Jumbo Packet isn’t jumbo.

What’s your take? Poor error handling? A bug? It is a dev preview. I’d love to hear a solution. I’m starting to run into a lot of issues that dead end in a FAILED error message.

Time Zones in PowerShell

Doug Fink wrote an article about getting time zone information in PowerShell awhile back. This helped me out today while I was trying to remember how to construct TimeZoneInfo classes in PowerShell. I was working with a cmdlet (which I had developed…) that accepts a TimeZoneInfo class. Typically I think this is a good idea because we can usually just type in a string to convert to the type we are looking for. This is most beneficial to objects like DateTimes or Version. With these types of objects we can just pass a string down and get all the benefits of a object of that type.

For example, if we created a version we could easily look up the individual parts of that version.

1
2
3
PS C:\> $myversion = [Version]"1.1.20.2"
PS C:\> $myversion.Major
1

I was really surprised to see that TimeZoneInfo did not have such a translation. I was even more surprised to find out that there was not cmdlet to locate time zones. I found Doug’s post but thought it beneficial to add a few more properties to the TimeZoneInfo object returned by my cmdlet. Here is a modified version that returns the current time of the time zone, the time apart from the local time zone and whether the time zone is currently under day light savings.

1
2
3
4
5
6
7
8
9
10
function Get-TimeZone()
{
    param($Name)
 
    $timeZones = [System.TimeZoneInfo]::GetSystemTimeZones() | Where-Object StandardName -Match $Name
 
    $timeZones = $timeZones | Add-Member -MemberType ScriptProperty -Name "CurrentTime" -Value { [TimeZoneInfo]::ConvertTime([DateTIme]::Now, $this) } -PassThru  -Force
    $timeZones = $timeZones | Add-Member -MemberType ScriptProperty -Name "IsDayLightSavingTime" -Value { $this.IsDaylightSavingTime([DateTime]::Now) } -PassThru  -Force
    $timeZones | Add-Member -MemberType ScriptProperty -Name "HoursApart" -Value { $this.CurrentTime - [DateTime]::Now } -PassThru  -Force
}

Now I can use the cmdlet like this.

1
2
3
4
5
6
7
8
9
10
11
PS C:\Users\Adam> Get-TimeZone -Name "Central Standard Time"
 
CurrentTime                : 2/13/2012 8:25:07 PM
IsDayLightSavings          : False
HoursApart                 : 00:00:00
Id                         : Central Standard Time
DisplayName                : (UTC-06:00) Central Time (US & Canada)
StandardName               : Central Standard Time
DaylightName               : Central Daylight Time
BaseUtcOffset              : -06:00:00
SupportsDaylightSavingTime : True

Thanks to Doug for info today!

MadPosh User Group Meeting Tonight!

Tonight is the second meeting of the Madison PowerShell User Group! We are pleased to welcome Steven Murawski of the Milwaukee Script Club. He will be presenting Admin tools for free. This presentation will provide you with some good practical tools that are built right into PowerShell. We will be streaming the presentation to our remote audience using LiveMeeting. Please visit the MadPosh blog for that information.

I have plenty of PowerGUI stickers to give out! Don’t miss this one! I will bring pizza again. If you would like to bring some food or drink for the group, feel free.

Particulars:

Schedule
5:30 – Mingle and food (Adam will bring pizza, feel free to bring something for the group if you’d like!)
5:50 – Announcements
6:00 – Presentation: PowerShell – Admin tools for free by Steven Murawski
7:00 – Discussion, Script Party*
* Please bring in a script that you would like to discuss. You can show it off or we can work through your problems as a group.

When and Where

Tuesday, February 7th

Address info:
2501 W Beltline Hwy
Madison, WI 53713

The conference room is in the East Tower on the 4th Floor in the
“Wipfli” space.

Announcing Windows PowerShell 3.0 Firstlook!

Over the past few months I have been working on a short book on PowerShell 3.0. It’s focus is to quickly get readers up to speed with short and concise chapters on the various features added or changed within the upcoming release. The book has a target release date of May 2012. There will be an eBook available as well as the print copy. The eBook is actually a lot cheaper than the print.

 

It has been an interesting and exciting time. If you have been wondering why bugs are going over looked in some of my OSS projects or I don’t blog quite as much, this is why. To find out more information about the book please visit PACKT’s website.

I will be writing a more in depth blog post as more of the book is finished.

Lazy Sunday: PowerShell and Virtual PC

Sometimes you just starting looking into something for no good reason and waste a whole lot of time. Today I started playing with Virtual PC. I know what you’re thinking: “Arghh why? We have Hyper-V.” Well I have Windows 7 installed on my desktop at home because I play some PC games and wasn’t sure how that would fare on a Server 2008 R2 install. I’m pretty sure it would have been fine and may be rebuilding this machine in the near future. I really miss not having virtual machines though. I decided to start a quest trying to convert an existing VM from a Hyper-V server to Virtual PC. There are some tutorials out there that outline how to do this. It really is only a few steps. Thinking I would enjoy the challenge, I decided to try and write a script to automate the conversion. I didn’t finish it. Instead I decided to write up what I’ve learned about the Virtual PC API. I lost interest. That’s why today is a lazy Sunday.

The Virtual PC API is a COM interface. There are a whole host of classes available for interacting with the system. Ben Armstrong calls this one of the “big under the cover features” of the new version of Virtual PC. The parent interface for the API is the IVMVirtualPC interface. It exposes methods for enumerating, creating and destroying VMs, managing network interfaces and manipulating virtual hardware.

To create  a virtual machine we can use the New-VPCVM function.

function New-VPCVM()
{
	param(
		$Name,
		$Path,
		$Vhd
		)

	if (-not (Test-Path $Path))
	{
		New-Item -Path $Path -Force
	}

	if ($Vhd -ne $null -and -not (Test-Path $Vhd))
	{
		Write-Error "Specified VHD does not exist."
		return
	}

	$VirtualPC = New-Object -ComObject VirtualPC.Application
	$VPCVM = $VirtualPC.CreateVirtualMachine($MachineName, $Path)
	$VPCVM.AddHardDiskConnection($Vhd,0,0) | Out-Null

	$VPCVM
}

The above function creates the Virtual PC VM and has the option to add a VHD. To retrieve the new VM we can use the Get-VPCVM function.

function Get-VPCVM()
{
	param($Name = "*")

	$VirtualPC = New-Object -ComObject VirtualPC.Application
	$VirtualPC.VirtualMachines | Where-Object { $_.Name -like "$Name" }
}

Finally, if we want to remove the VM from Virtual PC we can use the Remove-VPCVM.

function Remove-VPCVM()
{
	[CmdletBinding()]
	param(
		[Parameter(ValueFromPipeline=$true)]
		$vm
		)

	$VirtualPC = New-Object -ComObject VirtualPC.Application

	if ($vm -is [System.String])
	{
		$VM = Get-VPCVM -Name $vm
	}

	$VirtualPC.DeleteVirtualMachine($vm)
}

I spent way to much time on this :D . I know a little bit more about Virtual PC and a whole lot more about why people don’t use it. I finally realized that Virtual PC does not support x64 VMs. This was a deal breaker. Hopefully this post will get you on the right path to automating Virtual PC. I know it may be necessary to some one. Now to go watch the Super Bowl and forget that I spent my afternoon looking into the Virtual PC API. :P

On a side note, there are also steps for converting a VM from Hyper-V to Virtual PC within which we need to adjust what is actually within the VHD. Primarily, we need to replace the HAL.DLL found in the System32 directory. Because of this I have created a couple functions for mount and dismounting a VHD using diskpart. If you want more information about this I would recommend you look at the much more complete example on PoshCode.

PowerShell: Upcoming events

There are a lot of upcoming PowerShell events. I have been trying to keep them straight myself. I wanted to list out some of the ones that I have run into and would like to make sure others know about.

Tuesday, February 7th - Madison PowerShell User Group Meeting – This month we have the pleasure of welcoming Steve Murawski. He will be speaking about Admin tools for free, provided by PowerShell. We are remote-capable for this meeting so please feel free to join us, if you can.

Saturday, March 10th – PowerShell Saturday – The first ever PowerShell Saturday! This event is held in Columbus, Ohio. There will be numerous sessions including one from Ed Wilson. Expect prizes, small script club sessions and much more. This will be a fun event! I hope to be attending. Nothing official yet, though. ;)

Monday, March 19th – International PowerShell User Group Day – Bringing together PowerShell groups around the world, the International PowerShell User Group day is intended to show the power of the community. Don Jones and Ed Wilson will be presenting. There will be give aways and it will be a great excuse to get the group together for a second time in March!

Friday, March 30th - Techstravaganza 2012  - Back again this year the Techstravaganza event is a smaller TechEd-style event with numerous tracks based on Microsoft technologies including PowerShell. It is located in NYC at the Microsoft offices. I have submitted a session abstract and if selected I should be attending this as well.

Sunday, April 29th through Wednesday, May 2nd – The Experts Conference – The Experts Conference is back again this year, featuring 300 and 400 level tracks. The PowerShell Deep Dive is a part of this conference and has become a full track this year. The call for speakers is open until the 15th of February. I encourage you to attend. I am and will be helping to facilitate this event. I hope to see you there! It will be in beautiful San Diego, CA.

Monday June 11th through Thursday, June 14th – TechEd - This is the big Microsoft conference in Orlando this year. It looks like they will have a good offering of PowerShell sessions this year along with a whole host of other interesting technologies. If you’re interested, they have started populating the content catalog. Just give a search for PowerShell. I have submitted a session to this but don’t expect it to be chosen. :D  If not I will probably not be attending.

Friday, June 15th and Saturday, June 16th – CodeStock 2012 – A community based conference that is a forum for sharing knowledge and experience. PowerShell sessions will be included and I think my buddy Jim Christopher will be attending.  I will be trying to make this one but it is still up in the air for me.

If you know of any other up and coming events that you would like listed here let me know. Exciting times to be a PowerShell user or developer!

 

 

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. ;)

Subscribe to RSS Feed Follow me on Twitter!