Assigning fixed drive letters to USB Devices on Wi

2019-09-02 06:17发布

问题:

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:

  1. Enable the DriverFramework-UserMode log if it is not enabled
  2. Create a scheduled task triggered by EventID 2106
  3. 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

回答1:

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.



回答2:

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()
    }