PowerShell v3 added some very useful web cmdlets that make working with web services and pages much easier. In previous versions, it was common practice to use the WebClient class but this wasn’t straight forward in the least bit. Recently, I’ve been working with ASP.NET MVC4 Web API and found that the new additions to PowerShell to be extremely handy. Here are a couple techniques to make working with the cmdlets against Web API a pleasant experience.
The Invoke-RestMethod Cmdlet
The Invoke-RestMethod cmdlet is very easy to use. That’s my favorite part. It’s much easier than using Fiddler or Curl and it’s installed right out of the box. I did another blog post about how to use the cmdlet to access Reddit APIs and pull down the hot posts in a Subreddit. Aside from invoking the GET verb, the Invoke-RestMethod cmdlet has a Method parameter that accepts any of the HTTP verbs as fodder. This enables for PUTs, POSTs, DELETEs oh my!.
A Simple Web API
Assume that I have a simple CodeMonkey class with a couple basic properties.
My Web API is also very simple. It exposes a GET, PUT, POST and DELETE.
Accessing Code Monkeys
By default, the Invoke-RestMethod uses the GET verb and will retrieve entities from the API. Invoke-RestMethod also converts JSON directly into PowerShell objects that can be piped to all your favorite formatting and processing cmdlets.
Just as easily, we can grab a single monkey by tacking on the Id in a very RESTful manner.
Since the objects coming back to the pipeline are PSCustomObjects, we have the ability to use cmdlets like Select-Object to process the returned data. There is a caveat when piping collections of objects returned by this cmdlet. You may notice that using cmdlets like Select-Object and Where-Object don’t behave as expected. You’ll have to use the GetEnumerator method of the list that is returned in order to actually access the inner objects and process them as expected.
Posting New Code Monkeys
In addition to getting entities, the Invoke-RestMethod cmdlet can easily modify them as well. Since the Invoke-RestMethod cmdlet can invoke PUT and POST requests, it is necessary to serialize data in a way that the Web API will find meaningful. To accomplish this, we can use a combination of PowerShell hashtables and the new ConvertTo-Json cmdlet. It’s as simple as creating a hashtable that matches your data transfer object, converting it to a JSON string and passing it to the Body parameter of Invoke-RestMethod.
While using Web API, it’s important to use the correct content type. I struggled quite a bit with this in the beginning. If you don’t set the content type to application/json, the request will have an application/x-www-form-urlencoded content type and the Web API won’t know what to do with it. I saw some very strange behavior. Null CodeMonkeys or CodeMonkeys without properties set!
Updating Existing Code Monkeys
Just like when POSTing new code monkeys to the API, we can also PUT, or update, existing code monkeys. This is accomplish almost exactly the same way as a POST with only a change to the Method and the URI that we specify.
Removing Code Monkeys
Equally as easy is deleting existing monkeys. No need for a body here. We only need to specify the Delete method and the Id.
Nested Object Support
Web API, Invoke-RestMethod and ConvertTo-Json support nested objects. Keep in mind that the ConvertTo-Json and ConvertFrom-Json cmdlets have a Depth property. By default, these cmdlets will serialize 3 levels deep. To create nested objects, just create a hash table that matches the format of your data transfer object and convert it to JSON. Web API will happily deserialize it if it matches the new DTO. Also notice the support for arrays of objects.
Why should I use this to test my Code Monkeys?
This is just another tool in the toolbox for developers or admins alike to easily access web services. What I find especially appealing is the close similarities in the JSON and PowerShell hashtable syntax. It really helped me to visualize how my data would be sent across the wire. Being in PowerShell, I found it extremely flexible to quickly modify the data or request. I haven’t found an easier way to consume a web service!
So what do you think? Could you use this technique to conquer your Code Monkeys?
Show-Command is a really handy PowerShell cmdlet. Jeff Hicks gave a great presentation at the PowerShell Deep Dive about how to create simple GUIs without having to resort to WinForms or WPF. In his talk he showed off Show-Command and other cmdlets like Out-GridView. Recently, we have been providing some simple solutions to customers using Show-Command to provide a simple interfaces that even non-techies can use. One of the caveats with Show-Command against the stock cmdlets is that there are a large number of possible parameters that will be exposed in the UI. This can be just as intimidating and hard to use as the command line.
Instead of providing the user with a raw cmdlet, it’s possible to use Show-Command and a function. Additionally, we can use the ValidateSet attribute to dynamically provide a predefined list of possible selections that they can make for any of the parameters. Using Invoke-Expression to create a function from a here-string, we can generate a function on the fly.
We can also remove the common parameters with the NoCommonParameters parameter on Show-Command. Due to a caveat with Show-Command, we need to use the PassThru parameter and use Invoke-Expression to actually execute the command after the user selects the input. My theory, is that since Show-Command has to have to a separate UI thread, executing a script containing Show-Command will cause strange results. This particular format causes Show-Command to return to the current thread and then process the command. Without the PassThru and Invoke-Expression, it seems to work in the ISE but not in a script!
The final result is a very simple UI.
It’s a simple little trick but it can be useful to easily wrap complex scripts for anyone to use.
Today is my first day working in my new role as Technical Product Manager at Provance Technologies. A little while back Provance approached me to talk about this position, and it seemed like a very natural fit. Rarely have I felt such a positive vibe from a company through the interview process, so I was really happy to accept the position of Technical Product Manager with them and I have been looking forward to starting work with them.
Now that I am working full-time with Provance, I’ll be getting up to speed as quickly as I can on the IT Asset Management Pack and the Data Management Pack products that we offer to help companies properly manage the assets they have inside their organization. I know there is already some PowerShell support in one of the products, although I haven’t personally taken a look at it yet (but you can bet I will be soon).
For those of you who regularly follow my blog, if you happen to use either of these products in your organization, I’d love to hear about it.
Kirk out.
This week at the PowerShell Summit in Redmond, I gave a talk about using .NET reflection in PowerShell. We took a quick look at ILSpy and how to read the decompiled C# code that makes up compiled .NET assemblies. After a comment by Karl Prosser, I figured it would be cool to take it a step further and actually marry the two. The result was a Get-MemberBody advanced function that outputs the actual C# code for members piped from Get-Member.
I used ILSpy to take apart ILSpy and find the classes it uses to actually disassemble .NET assemblies. The core piece here is the CSharpLanguage class and the AssemblyDefinition’s ReadAssembly method.
Once all the pieces are in place you can easily call the CSharpLanguage’s many Decompile methods to turn a particular member into a string representation of the decompiled C# code.
Finally, I wrapped everything into an advanced function that accepts pipeline input from the Get-Member cmdlet. This allows for simple pipes like the following.
The output from a pipeline like this is the decompiled source code for AddTicks. It’s a simple string and even includes the XML help!
I’ve posted the advanced function to PoshCode.org. You’ll need an install of ILSpy. I tested on the latest version (2.1.0.1603).
PSExec is a SysInternals tool that enables remote execution of processes. One of the really neat aspects of the tool is that it can execute processes interactively in user sessions and on systems that may not expose another way of remotely executing processes. PSExec works much differently than PowerShell remoting or WMI. PSExec manages all the remote management by actually installing a service on the remote machine and communicating via remote named pipes. The process is as follows.
Obviously, PSExec requires administrator access but it doesn’t rely on additional configuration like WinRM and PowerShell remoting do.This makes it unique in this sense.
Introducing PoshExec
PoshExec is currently a single cmdlet; Start-RemoteProcess. It uses the same set of steps that PSExec uses to install a service on the remote machine and communicate via named pipes. What’s cool about PoshExec is the entire tool is wrapped in a single PS1 file. The service code is written in C#. It’s a simple service that inherits from ServiceBase that runs a message loop, waiting for communications via the PoshExecSvc named pipe.
The message sent across the wire is a simple XML serialized object. It contains all the options exposed by Start-RemoteProcess. Once the service receives the message it deserializes and begins starting the process.
At the moment, interactive execution is not working. PoshExec also requires the process exist on the remote machine. It can run the process as a separate user from the one executing Start-RemoteProcess. The framework is in place to begin working out feature parity between the two tools.
The Start-RemoteProcess cmdlet has to set the whole process in motion. It follows the same process as PSExec. First, it maps a drive and copies the service over to the machine. Next, it uses the sc command line tool to create the remote service.
After the service is up and running, Start-RemoteProcess, calls the new Send-NamedPipeMessage cmdlet to send a message via the PoshExecSvc named pipe to the remote machine. The contents of the message is the XML serialized start info that was mentioned earlier in the post.
Finally, the cmdlet cleans up by stopping and removing the service and removing the mapped drive.
Although a very rough first cut, it was an interesting experiment to look into how PSExec worked. Feel free to star or fork my repo on Github. The script for PoshExec is currently in the dev branch.
Here’s Sean’s newest crazy PowerShell video in which he managed to feature your’s truly doing moonwalk (or trying to
)
As you can see from the video – Microsoft MVP Summits are a lot of fun with amazingly smart and crazy people around.
No more one off IT management. Just Script It!
Here’s a script I wrote to help setup a vWorkspace farm. I’m constantly rolling out new farms and having a script like this will probably save me a week of my live this year. I don’t have to click through the same wizards to create the same apps and assign them to the same users. The script adds my Hyper-V host and creates computer groups and syspreps for each operating system. Then it goes a head, adds the domain users group to my targets and publishes a set of three apps each computer group and target. This kind of setup would probably take me about an hour (give or take depending on my level of distraction). This gets it done in under 10 seconds.
function Create-Farm { [CmdletBinding()] param( [Parameter(Mandatory=$true)] $HyperVHost, [Parameter()] $Credentials=(Get-Credential), [Parameter()] $LocalSysPrepCredentials=(Get-Credential), [Parameter()] $ComputerPrefix = "MDN", [Parameter()] $Windows7x64Template=$null, [Parameter()] $Windows7x86Template=$null, [Parameter()] $WindowsXPx64Template=$null, [Parameter()] $WindowsXPx86Template=$null, [Parameter()] $Windows2008R2Template=$null ) Set-Location "AdamsFarm:" Connect-QVWFarm Add-QVWHyperVHost -Name $HyperVHost -Location Location1 -Credential $Credentials #Create the computer groups New-QVWComputerGroup -Name "Windows 7 x86" -AdminCredential $Credentials -SystemType HypervHost -GroupMode Traditional -Location Location1 New-QVWComputerGroup -Name "Windows XP x86" -AdminCredential $Credentials -SystemType HypervHost -GroupMode Traditional -Location Location1 New-QVWComputerGroup -Name "Windows 7 x64" -AdminCredential $Credentials -SystemType HypervHost -GroupMode Traditional -Location Location1 New-QVWComputerGroup -Name "Windows 2008R2" -AdminCredential $Credentials -SystemType HypervHost -GroupMode Traditional -Location Location1 New-QVWComputerGroup -Name "Windows XP x64" -AdminCredential $Credentials -SystemType HypervHost -GroupMode Traditional -Location Location1 #Import all Hyper-V templates Import-QVWComputerTemplate -HyperVHost $HyperVHost | Add-QVWComputerTemplate -Location Location1 -ComputerGroup "Windows 7 x86" #Create all the syspreps New-QVWSysPrep -Name "Windows 2008R2" -AdminCredential $LocalSysPrepCredentials -Domain "mdnvdi.local" -DomainCredential $Credentials ` -ExecutionMode Instant -OperatingSystem Server2008 -ProcessorArchitecture x86 -ProductKey "Get your own! :D" New-QVWSysPrep -Name "Windows 7 x86" -LocalAccountCredential $LocalSysPrepCredentials -Domain "mdnvdi.local" -DomainCredential $Credentials ` -ExecutionMode Instant -OperatingSystem VistaWin7 -ProcessorArchitecture x86 -ProductKey "Get your own! :D" New-QVWSysPrep -Name "Windows XP x86" -AdminCredential $LocalSysPrepCredentials -Domain "mdnvdi.local" -DomainCredential $Credentials ` -ExecutionMode Instant -OperatingSystem XPPro -ProductKey "Get your own! :D" -ConcurrentConnections 5 New-QVWSysPrep -Name "Windows 7 x64" -LocalAccountCredential $LocalSysPrepCredentials -Domain "mdnvdi.local" -DomainCredential $Credentials ` -ExecutionMode Instant -OperatingSystem VistaWin7 -ProcessorArchitecture x64 -ProductKey "Get your own! :D" New-QVWSysPrep -Name "Windows XP x64" -AdminCredential $LocalSysPrepCredentials -Domain "mdnvdi.local" -DomainCredential $Credentials ` -ExecutionMode Instant -OperatingSystem XPPro -ProductKey "Get your own! :D" -ConcurrentConnections 5 #Create a new client Add-QVWTarget -Group "mdnvdi\Domain Users" #Create a desktop app foreach($cg in (Get-QVWComputerGroup)) { New-QVWManagedApplication -ComputerGroup $cg -Name "$($cg.Name) Desktop" } #Create a notepad app foreach($cg in (Get-QVWComputerGroup)) { New-QVWManagedApplication -ComputerGroup $cg -Name "$($cg.Name) Notepad" -Path "C:\windows\system32\notepad.exe" ` -SeamlessWindowMode vWorkspace } #Create a ie app foreach($cg in (Get-QVWComputerGroup)) { New-QVWManagedApplication -ComputerGroup $cg -Name "$($cg.Name) IE" -Path 'C:\program files (x86)\Internet Explorer\iexplore.exe' ` -SeamlessWindowMode vWorkspace } foreach($app in (Get-QVWManagedApplication)) { foreach($user in (Get-QVWtarget)) { Publish-QVWResource -Resource $app -Folder "My Applications" -Client $user } } $NC = New-QVWNamingConventions -BaseName ($ComputerPrefix + "764??") -StartValue 0 -Increment 1 Set-QVWProvisioningSettings -ComputerGroup "Windows 7 x64" -CloneMethod HyperVDifferencingDisk ` -SysPrepCustomizations "Windows 7 x64" -NamingConventions $NC $NC = New-QVWNamingConventions -BaseName ($ComputerPrefix + "XP64??") -StartValue 0 -Increment 1 Set-QVWProvisioningSettings -ComputerGroup "Windows XP x64" -CloneMethod HyperVDifferencingDisk ` -SysPrepCustomizations "Windows XP x64" -NamingConventions $NC $NC = New-QVWNamingConventions -BaseName ($ComputerPrefix + "2K8R2??") -StartValue 0 -Increment 1 Set-QVWProvisioningSettings -ComputerGroup "Windows 2008R2" -CloneMethod HyperVDifferencingDisk ` -SysPrepCustomizations "Windows 2008R2" -NamingConventions $NC $NC = New-QVWNamingConventions -BaseName ($ComputerPrefix + "786??") -StartValue 0 -Increment 1 Set-QVWProvisioningSettings -ComputerGroup "Windows 7 x86" -CloneMethod HyperVDifferencingDisk ` -SysPrepCustomizations "Windows 7 x86" -NamingConventions $NC $NC = New-QVWNamingConventions -BaseName ($ComputerPrefix + "XP86??") -StartValue 0 -Increment 1 Set-QVWProvisioningSettings -ComputerGroup "Windows XP x86" -CloneMethod HyperVDifferencingDisk ` -SysPrepCustomizations "Windows XP x86" -NamingConventions $NC } |
Easy as that!
Trying something new! Today I have a guest blogger. Joel Webster works at Dell with me and works quite a bit on the PowerShell support for vWorkspace. He has been an intern for quite a while and really knows his stuff.
Adam
A few weeks ago, I came in to work and after I unlocked my machine, I noticed something strange: the clock on my development VM was different than the clock on my host machine. As I gave the dev VM mouse focus, it went black and I got the “lost connection to virtual machine” message, and all of the VMs in my Hyper-V Manager went from Running to Critical.
This was not good.
I started checking into it, and when I opened up windows explorer, I noticed something curious: my workhorse drive was gone. The one with all of my VMs stored on it. It turns out the drive died in the middle of the night.
Thankfully, our in-house IT guy was able to recover my most important VMs, so I didn’t have to rebuild my environment completely from scratch. But I did learn an important lesson: ALWAYS BACKUP YOUR STUFF
I looked in to the built-in Hyper-V Export functions, but these took a long time to execute. I figured there had to be a better way to backup. So I made one, with help from some other folks who had a similar problem.
First, I looked into the PowerShell Management Library for Hyper-V and its cmdlets. I found that, through this module, I could discover VMs on a host, save their state, export them, and resume them. I also discovered that Stan Czerno had already done this part for me, which saved me a good deal of time. All I had to do was modify his code a little.
Once I had all of these exported VMs lying around, I noticed that they took up a great deal of space on my backup drive. I started looking for a good way to compress these archives. In Windows Explorer I can just right-click a folder and compress it. Through PowerShell this is much more tricky. Thankfully, Jeremy Jameson already did much of the heavy lifting here, and again, all I needed to do was modify his scripts to suit very large files.
With these two core components in place, all I needed to do was write a script that tied them together and did some happy bookending and logging.
The end product of my efforts is the module linked below. Point it at a drive to backup onto, tell it which host to look at, tell it which VMs to back up, and then let it fly. You can even tell it to shut down the host when it’s finished. You can make a script out of it and back up an entire farm of VMs on multiple hosts. I use it to protect myself for if/when my drive dies again.
You could even set it to run on a schedule so you don’t even have to think about it.
In the weeks since I first made this module, I’ve already used it when I accidentally deleted a terminal server VM out of my farm. I just grabbed the previous night’s backup and imported the VM out of it – complete recovery in less than 15 minutes. I dare you to rebuild a terminal server from scratch in 15 minutes.
Because of its size (566 lines) I’ve decided to simply link to it instead of posting the whole module. The module is available on PoshCode.org: Backup Hyper-V VMs
Use it, so you don’t have to suffer the loss of your drives and VMs!
The ListDlls SysInternals tool is used to list the DLLs that are loaded into processes on the system. It can either return DLLs for all processes, a single process or return processes that contain a particular DLL. It also has the ability to flag DLLs that are rebased and unsigned. Most of this functionality is relatively easy to implement in PowerShell. The Get-Process cmdlet returns System.Diagnostics.ProcessInfo classes that contain a Modules property. The property returns the DLLs that are currently loaded into the process. It also returns module base information. One property it does not contain is signing information. This can be accomplished with the WinVerifyTrust Win32 function. This Stackoverflow post contains a C# implementation for accessing this particular API call.
The following advanced function accepts a process name or IDs from the pipe, a module name and whether to filter by unsigned binaries. The P\Invoke for verifying signed files can be found in the PInvoke.ps1 file in the PoshInternals module.
function Get-Dll { [CmdletBinding()] param( [Parameter(ValueFromPipeline=$true)] [String]$ProcessName = "", [Parameter(ValueFromPipeline=$true)] [Int]$ProcessId = 0, [Parameter()] [String]$ModuleName, [Parameter()] [Switch]$Unsigned ) Begin{ $script:Modules = @() $script:Processes = @() } Process { if (-not [String]::IsNullOrEmpty($ProcessName)) { $Modules += Get-Process -Name $ProcessName | Select-Object -ExpandProperty Modules } elseif ($ProcessId -ne 0) { $Modules += Get-Process -Id $ProcessId | Select-Object -ExpandProperty Modules } elseif(-not [String]::IsNullOrEmpty($ModuleName)) { $Processes = Get-Process | Where-Object { ($_.Modules).ModuleName -Contains $ModuleName } } else { $Modules += Get-Process | Select-Object -ExpandProperty Modules } } End { if ($Processes.Length -gt 0) { $Processes return } if (-not [String]::IsNullOrEmpty($ModuleName)) { $Modules = $Modules | Where-Object { $_.ModuleName -eq $ModuleName } } if ($Unsigned) { $Modules = $Modules | Where { -not [PoshInternals.AuthenticodeTools]::IsTrusted($_.FileName) } } $Modules } } |
Bluescreen is a screen saver that is part of the SysInternals suite that mimics a Windows blue screen of death. It can also mimic chkdsk and the boot process. Screen savers themsevles are actually just executable files renamed with an extension “scr”. Once placed in the System32 or SysWow64 directory, they can be accessed via the personalization control panel applet. In order to get this to work with PowerShell, it was necessary to create an executable that could be used to bootstrap the PowerShell script that would display the blue screen. This is accomplished by calling the C# compiler directly.
Once we have an exe, we can just rename to scr and copy over to the system directory. Next, we need to create the ScreenSaver.ps1 file to actually display the screen saver. It will also need to process the special arguments that a screen saver is passed. There are three arguments: configuration, preview and full screen. In this example I only implemented the full screen mode. You can find out more information here.
The Show-ScreenSaver function is responsible for drawing the form that will save our screen. We need to create a window that spans the entire screen and that has no border. Then we add some event handlers to let us exit the screen saver.
Next, we create all the text, format it and position it correctly on the form. Once this is complete, we can just display our form using ShowDialog.
Once the proper files are copied to the system directory. We can see that the screen saver shows up in the screen saver options window.
Clicking preview will start the bootstrapper process that will then launch the ScreenSaver.ps1 script and we get to see the pretty new Windows 8 blue screen! Clicking or pressing any key will close the screen saver.
The screen saver scripts have been added to the PoshInternals module on CodePlex. PoshInternals module on GitHub.
Other posts found in this series
Another cool tool in the SysInternals suite is MoveFile and PendMoves. Rather than moving the file immediately the tool is used to move, rename or delete a file on restart. The System Manager looks at a special registry key to determine which files to are candidates to be operated on. I’ve created two cmdlets that mimic the function of the MoveFile tool. They both call the Win32 function MoveFileEx.
Move-FileOnReboot will (amazingly!) register a file to be moved on reboot.
Remove-FileOnReboot will do the same, except it will delete the file.
Get-PendingFileRenameOperation will return the files that are registered to be moved, renamed or delete.
I’ve also wrapped up the last two blog posts in this series in a module hosted on CodePlex. a module hosted on GitHub.
Other posts found in this series
The SysInternals suite is a collection of tools authored by Mark Russinovich. The tools offer all kinds of deep system analysis for Windows. Some of the most commonly used tools include Process Explorer, Process Monitor and psexec. I use them all the time. After much use, I decided it would be pretty neat to try and replicate what some of the tools accomplish in PowerShell. My first target was Handle. Handle.exe returns different types of system handles that processes own. Handles can be files, directories, registry keys and all other kinds of system resources. Most frequently this is used to determine which process is locking a file. Retrieving information about systems handles currently allocated require several Win32 API calls. Using the examples found in this Stackoverflow post I was able to create an advanced function capable of returning the handles associated with processes throughout the system.
Most of the code required for this cmdlet is C# based. Due to the level of P\Invoke used, it was easier to author it this way. The first step is to grab basic information for all the handles on the system. NtQuerySystemInformation returns a collection of handle information structs. Interestingly, the struct and enumeration value are absent in the MSDN documentation for this function. Once the basic handle information is return we can begin to construct HandleInfo classes.
HandleInfo offers a couple basic properties that will be helpful once the objects are returned to the command line.
The HandleInfo class caches the Name and Type values so that it doesn’t have to look them up twice. It uses the NtQueryObject function to retrieve these values.
Once the objects have been put together in C# and Add-Type, we can create a simple advanced function to offer some better command line support. The cmdlet can filter handles by Process and handle Name. I hope to improve this further. The Sysinternals tool exposes the ability to close handles and provide a bit more information. I will also be adding the ability to filter based on handle type.
The output from the function is pretty handly
. It contains the process, handle type and name.
I hope to examine a few more of the SysInternals tools and create some more interesting PowerShell solutions. Get Get-Handle here.