Auto mount/unmount new PSDrives for removable drives and network shares in PowerShell v2

The new eventing infrastructure in PowerShell 2.0 is pretty delicious. You couldn’t do the following in 1.0 without a 3rd party snap-in (like my PSEventing snapin), but now it’s all there at the touch of your fingers. Well, it demands a bit of a sniff around WMI too, but hey, it works well.  With this module, anytime you add or remove a removable device like an external harddrive or USB key, or map a new network drive in explorer, PowerShell will now automatically add or remove a corresponding PSDrive for you.

  1. # AutoMount.psm1 v1.0  
  2. # Oisin "x0n" Grehan (MVP)  
  3.  
  4. $query = new-object System.Management.WqlEventQuery  
  5. $query.EventClassName = "__InstanceOperationEvent" 
  6.  
  7. # default to every 2 seconds  
  8. $query.WithinInterval = new-object System.TimeSpan 0,0,2  
  9.  
  10. # this WMI is only available with Windows 2003 and Vista (not XP it appears).  
  11. # this could be rewritten to use different WMI queries to support 2000/NT/XP also.  
  12. $query.QueryString = "Select * from Win32_VolumeChangeEvent" 
  13.  
  14. # attach a watcher  
  15. $watcher = new-object System.Management.ManagementEventWatcher $query 
  16.  
  17. # here we use -SupportEvent instead of -SourceIdentifier  
  18. # this prevents this event from being generally visible  
  19. # also note the use of the call operator to invoke a   
  20. # function in the scope of the module since this action  
  21. # occurs outside of module scope.  
  22. Register-ObjectEvent $watcher -EventName "EventArrived" `  
  23.     -SupportEvent "WMI.VolumeChange" -Action {  
  24.         & (get-module automount) VolumeChangeCallback @args 
  25.     }  
  26.  
  27. # New PSEvents:  
  28. #  
  29. #     PowerShell.DeviceConfigurationChanged  
  30. #     PowerShell.DeviceArrived  
  31. #     PowerShell.DeviceRemoved  
  32. #     PowerShell.DeviceDocking  
  33.  
  34. # win32_volumechangeevent event types  
  35. $eventTypes = @{  
  36.     1 = "ConfigurationChanged";  
  37.     2 = "Arrived";  
  38.     3 = "Removed";  
  39.     4 = "Docking";  
  40. }  
  41.  
  42. # private module level callback function  
  43. function VolumeChangeCallback ($sender, $eventargs) {  
  44.     trap { write-warning $_ }  
  45.  
  46.     $driveName = $eventArgs.NewEvent.DriveName.TrimEnd(":")  
  47.     $eventType = [int]$eventArgs.NewEvent.EventType # was uint16  
  48.  
  49.     $forwardedEvent = "Device$($eventTypes[$eventType])" 
  50.       
  51.     # forward a new simpler event specific to device event type  
  52.     [void]( New-PSEvent "PowerShell.$forwardedEvent" -Sender $driveName `  
  53.         -EventArguments $eventargs )  
  54. }  
  55.  
  56. # hook up our psdrive mount / unmount events  
  57. # and start the WMI watcher  
  58. function Enable-AutoMount {  
  59.  
  60.     Register-PSEvent -SourceIdentifier "PowerShell.DeviceArrived" `  
  61.         -Action {              
  62.             new-psdrive -name $args[0] -psprovider `  
  63.                 filesystem -root "$args[0]:";  
  64.          }  
  65.  
  66.     Register-PSEvent -SourceIdentifier "PowerShell.DeviceRemoved" `  
  67.         -Action {  
  68.             remove-psdrive -name $args[0] -ea 0; # may not exist  
  69.         }  
  70.       
  71.     $watcher.Start()  
  72. }  
  73.  
  74. # tear down our psdrive mount / unmount events  
  75. # and stop the WMI watcher  
  76. function Disable-AutoMount {  
  77.  
  78.     Unregister-PSEvent -SourceIdentifier "PowerShell.DeviceArrived" 
  79.     Unregister-PSEvent -SourceIdentifier "PowerShell.DeviceRemoved" 
  80.       
  81.     $watcher.Stop()  
  82. }  
  83.  
  84. # export functions to control automount  
  85. Export-ModuleMember Enable-AutoMount, Disable-AutoMount  
  86.  
  87. # start watching and (un)mounting  
  88. Enable-AutoMount 

This only works PowerShell v2.0 CTP2, and you’ll need to save it as AutoMount.psm1 in a directory under your documents folder like so (vista example):

%userprofile%\documents\windowspowershell\packages\automount\automount.psm1

You can then load it with the command:

ps> add-module automount

I have this in my profile.  You can temporarily disable automount with the function Disable-AutoMount and reenable it at anytime with Enable-AutoMount. The module also exposes four new events for you to consume yourself. You could, for example, hook your own script to run anytime a device is added and/or removed. This is what I do myself in the module. I hook a WMI event once then forward 1 of 4 possible new events depending on the type of WMI event that was raised.

NOTE: this particular flavour of WMI query only works in Vista and Windows 2003 it appears. I’m looking into getting it working with 2000/XP also.

Have fun!

Time For A Change

It’s getting harder to post useful scripting tips for PowerShell these days as there are so many talented hardcore scripting bloggers around. My day to day is job is not system/network/server administrator; I’m a .NET developer, having started the C# habit about eight or nine years ago with the early CLR 1.0 beta. So, from here on in, I’ve decided that a better direction for me to take from now on is that of a PowerShell developer,  as opposed to a scripter. There are very few (if any?) dedicated PowerShell developer blogs around, and so I figured I should try to fill that gap as best I can. I have a not insignificant amount of experience writing providers, cmdlets and other widgety bits, so it’s a good time to share my experiences. Of course, my way is not “the” way, so please reply with your own experiences/advice/mocking/whatever. ;-)

That said, I am not eschewing scripting altogether – I have some stuff in the pipeline (har-har) concerning areas I’m interested in, like eventing and jobs/remoting.

Tips for using Enumerated Types in PowerShell

Just a handy tip - you don't have to cast enum types in powershell usually. It will do that for you if that's the only constructor (or method) overload that makes sense:

$accessrule = New-Object system.security.AccessControl.FileSystemAccessRule $userName, `
     "Modify",  "ContainerInherit,ObjectInherit", "None", "Allow"

So when I say "only overload that makes sense," what do I mean by that? Take this method for example which has two overloads (meaning it has two different sets of parameters you could use):

void MyMethod(string a, string b, string c)

void MyMethod(string a, enum b, string c)

If you try to call that method with $o.MyMethod("foo", "foo", "foo"), it will pick the first version that takes three strings. In that case, you would have to cast/convert the enum to its native type so that powershell will pick the right method.

PowerShell v2 one-liner to get clipboard text

With PowerShell’s new –STA startup switch, interacting with the Windows Forms object model was never so easy:

PS> $text = & {powershell –sta {add-type –a system.windows.forms; [windows.forms.clipboard]::GetText()}}

Easy, eh?

Batch remove Zone.Identity information from downloaded files and scripts

blocked file properties

As we all [should] know, running scripts downloaded from the Internet is a risky business. But sometimes you know exactly where they came from, and you trust the source. The problem arrives when you’re on a server without any of your familiar utilities and you’ve just downloaded a zip of several ps1 scripts. Unzipping the zip via the windows built-in zip handler in explorer will preserve the Zone.Identifier information for the extracted files. This means that even if you have your Execution Policy set to RemoteSigned (which most people seem to have – it’s a sensible balance), the now “local” scripts are treated as remote and they will not run. Ideally you should “unblock” the zip file before extracting the files; all extracted files are then also “unblocked.” Unblocking a file is as simple as right-clicking it in Explorer and choosing “Properties.” (see figure 1).

Now, sometimes you don’t have this luxury. Either someone else downloaded/extracted the files or you are logged in remotely via PowerShell Remoting/WINRM for example. Thankfully, the annoyingly talented Mark Russinovich has written a great little tool for stripping NTFS ADS (alternate data streams – where the zone indentifier information is attached to a regular file) called streams.exe. He’s also made the tool easily available via a UNC path: \\live.sysinternals.com\tools\streams.exe Usage is simple: just start in the root directory of the extracted scripts and run: streams –s –d *.ps1 ; the –s means traverse subdirectories and –d instructs it to delete any alternate data streams from the files.

About the author

Irish, PowerShell MVP, .NET/ASP.NET/SharePoint Developer, Budding Architect. Developer. Montrealer. Opinionated. Montreal, Quebec.

Month List

Page List