Hello fellow Stackoverflowers :)
I am looking for ways to assign a fixed Driveletter to USB Drives on Windows Server 2012 (Foundation).
The Scenario:
One of my smaller customers has 2 USB Drives for his serverbackup, which are swapped every day to have an offsite backup. Currently they are manually reassigning the Driveletters if there is a mismatch.
I already have a plan to realize this but wanted to ask for advice before implementing it since i am not sure this is the best possible solution.
I would go about it the following way:
- Enable the DriverFramework-UserMode log if it is not enabled
- Create a scheduled task triggered by EventID 2106
- run a powershell script (via the scheduled task) that assigns the driveletter if the DeviceID matches one of the backup drives
So the question is:
Is it somehow possible to assign a fixed driveletter to a set of usb devices without having to run a script everytime a device is attached?
And if not is there a better way to detect when a device is attached and trigger the task?
Of course i did some research but only found solutions for Windows7 or Server2008. I would prefer to solve this without 3rd party tools.
Thank you in advance for any insight you can give on this topic
Regards Paul
I don't know if this helps much, but I found a nice post at https://social.technet.microsoft.com/Forums/windowsserver/en-US/09c9814a-38fa-4b16-bc8f-01329882a791/powershell-wmi-get-usb-storage-devices-only where the following code helps detect a connected USB device:
$diskdrive = gwmi win32_diskdrive | ?{$_.interfacetype -eq "USB"}
$letters = $diskdrive | %{gwmi -Query "ASSOCIATORS OF {Win32_DiskDrive.DeviceID=`"$($_.DeviceID.replace('\','\\'))`"} WHERE AssocClass = Win32_DiskDriveToDiskPartition"} | %{gwmi -Query "ASSOCIATORS OF {Win32_DiskPartition.DeviceID=`"$($_.DeviceID)`"} WHERE AssocClass = Win32_LogicalDiskToPartition"} | %{$_. deviceid}
$drive = gwmi win32_volume | ? {$letters -contains ($_.name -replace "\\")}
Then you can change the drive letter with the following (must be admin)
$drive.DriveLetter = "O:"
$drive.Put()
EDIT
OK here is another try. The following code (which I found here: http://blogs.technet.com/b/heyscriptingguy/archive/2010/04/13/hey-scripting-guy-april-13-2010.aspx) allows to create a WMI event which will fire when a USB drive is plugged in.
I've added an action to that event so that a script block executes.
The script block checks the letter of the USB drive and if it's not, say, "O:", it will set it.
I didn't use Wait-Job, as the job will continue running for as long as the event exists, and I wanted to see some output for testing.
In anycase, this should be closer to what you want to do:
$scriptblock = {
$driveLetter = "O:"
$diskdrive = gwmi win32_diskdrive | ?{$_.interfacetype -eq "USB"}
$letters = $diskdrive | %{gwmi -Query "ASSOCIATORS OF {Win32_DiskDrive.DeviceID=`"$($_.DeviceID.replace('\','\\'))`"} WHERE AssocClass = Win32_DiskDriveToDiskPartition"} | %{gwmi -Query "ASSOCIATORS OF {Win32_DiskPartition.DeviceID=`"$($_.DeviceID)`"} WHERE AssocClass = Win32_LogicalDiskToPartition"} | %{$_. deviceid}
$drive = gwmi win32_volume | ? {$letters -contains ($_.name -replace "\\")}
if ($drive.DriveLetter -ne $driveLetter)
{
$drive.DriveLetter = $driveLetter
$drive.Put()
}
$drive.DriveLetter
}
$job = Register-WmiEvent -Query "Select * from __InstanceCreationEvent within 5 where targetinstance isa 'win32_logicaldisk'" -SourceIdentifier usb -Timeout 1000 -Action $scriptblock
while ($job.State -ne 'Stopped')
{
$job | Receive-Job
sleep 5
}
Unregister-Event -SourceIdentifier usb -Force | Out-Null
$job | Remove-Job -force
Worth noting:
The script block will receive $args as well as $event. I didn't use them but they will probably help optimize the script further.
Here is the code i will probably end up using:
To create a permanent wmi consumer that runs a script:
$computer = "xxx"
$filterNS = "root\cimv2"
$wmiNS = "root\subscription"
$query = "Select * from __InstanceCreationEvent within 5 where targetinstance isa 'win32_logicaldisk'"
$filterName = "TestFilter"
$filterPath = Set-WmiInstance -Class __EventFilter `
-ComputerName $computer -Namespace $wmiNS -Arguments `
@{name=$filterName; EventNameSpace=$filterNS; QueryLanguage="WQL";
Query=$query}
$consumerPath = Set-WmiInstance -Class CommandLineEventConsumer `
-ComputerName $computer -Namespace $wmiNS `
-Arguments @{
name="TestConsumer";
ExecutablePath= "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe";
CommandLineTemplate = "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe -executionpolicy bypass -file D:\\reassignDriveletter.ps1"
}
Set-WmiInstance -Class __FilterToConsumerBinding -ComputerName $computer `
-Namespace $wmiNS -arguments @{Filter=$filterPath; Consumer=$consumerPath} |
out-null
And here is the script that changes the driveletter if the Serialnumber of the drive matches one of the Backupdrives
$driveLetter = "Z:"
$diskdrive = gwmi win32_diskdrive | ?{($_.interfacetype -eq "USB") -and $_.serialnumber.trim() -eq "761203FA9J813S" -or $_.serialnumber.trim() -eq "761239FA9J813S"}
$letters = $diskdrive | %{gwmi -Query "ASSOCIATORS OF {Win32_DiskDrive.DeviceID=`"$($_.DeviceID.replace('\','\\'))`"} WHERE AssocClass = Win32_DiskDriveToDiskPartition"} | %{gwmi -Query "ASSOCIATORS OF {Win32_DiskPartition.DeviceID=`"$($_.DeviceID)`"} WHERE AssocClass = Win32_LogicalDiskToPartition"} | %{$_. deviceid}
$drive = gwmi win32_volume | ? {$letters -contains ($_.name -replace "\\")}
if ($drive.DriveLetter -ne $driveLetter)
{
$drive.DriveLetter = $driveLetter
$drive.Put()
}