Skip to content

How to Check if a Drive is a Removable Drive in Batch Script

Learn how to programmatically identify removable media (USB flash drives, SD cards) in Windows Batch scripts using PowerShell and WMI to prevent accidental formatting or data loss.

When your script is about to format a drive, copy sensitive data, or run a cleanup routine, you need to ensure it targets the correct type of storage. Accidentally formatting a USB flash drive thinking it was a local partition, or vice versa, can be catastrophic. Windows classifies drives by their type: Fixed (internal hard drives), Removable (USB sticks, SD cards), CD-ROM, Network, and more. A Batch script can check this classification before performing any destructive action, providing a critical safety gate.

This guide will explain how to identify removable media programmatically.

Drive Type Classifications

Type Code Name Description Examples
0 Unknown Drive type cannot be determined Rare, damaged or unusual hardware
1 No Root Directory Root path is invalid Very rare
2 Removable Removable media USB flash drives, SD cards, floppy drives
3 Fixed Non-removable local disk Internal HDDs, SSDs, and most external USB hard drives
4 Network Network-mapped drive \\Server\Share mapped to a letter
5 CD-ROM Optical disc drive CD, DVD, Blu-ray
6 RAM Disk RAM-based virtual disk RAM drives

Method 1: Check a Specific Drive

This method queries the drive type of a specific drive and provides a clear classification with human-readable output.

@echo off
setlocal EnableDelayedExpansion

set "Drive=%~1"

if "%Drive%"=="" (
    echo Usage: %~nx0 ^<drive_letter^>
    echo.
    echo Examples:
    echo   %~nx0 C
    echo   %~nx0 E:
    echo.
    echo Available drives:
    wmic logicaldisk get deviceid,volumename,drivetype 2>nul
    endlocal
    exit /b 1
)

:: Normalize drive letter (handle both "C" and "C:")
set "DriveLetter=%Drive:~0,1%"
set "DriveColon=%DriveLetter%:"

if not exist %DriveColon%\ (
    echo [ERROR] Drive %DriveColon% does not exist or is not accessible. >&2
    endlocal
    exit /b 1
)

echo ============================================================
echo   Drive Information Query
echo ============================================================
echo.
echo Querying: %DriveColon%
echo.

:: Build PowerShell command with proper escaping
set "PSCmd=$vol = Get-CimInstance Win32_LogicalDisk -Filter \"DeviceID='%DriveColon%'\" -ErrorAction SilentlyContinue;"
set "PSCmd=!PSCmd! if (-not $vol) { Write-Output 'NOT_FOUND~~~'; exit 1 };"
set "PSCmd=!PSCmd! $typeName = switch ($vol.DriveType) {"
set "PSCmd=!PSCmd!     0 { 'Unknown' }"
set "PSCmd=!PSCmd!     1 { 'No Root Directory' }"
set "PSCmd=!PSCmd!     2 { 'Removable' }"
set "PSCmd=!PSCmd!     3 { 'Fixed' }"
set "PSCmd=!PSCmd!     4 { 'Network' }"
set "PSCmd=!PSCmd!     5 { 'CD-ROM' }"
set "PSCmd=!PSCmd!     6 { 'RAM Disk' }"
set "PSCmd=!PSCmd!     default { 'Unknown' }"
set "PSCmd=!PSCmd! };"
set "PSCmd=!PSCmd! $label = if ($vol.VolumeName) { $vol.VolumeName } else { '(No Label)' };"
set "PSCmd=!PSCmd! $sizeGB = if ($vol.Size -gt 0) { [math]::Round($vol.Size / 1GB, 2) } else { 'N/A' };"
set "PSCmd=!PSCmd! $freeGB = if ($vol.FreeSpace -gt 0) { [math]::Round($vol.FreeSpace / 1GB, 2) } else { 'N/A' };"
set "PSCmd=!PSCmd! $fs = if ($vol.FileSystem) { $vol.FileSystem } else { 'N/A' };"
set "PSCmd=!PSCmd! Write-Output ('{0}~{1}~{2}~{3}~{4}~{5}' -f $vol.DriveType, $typeName, $label, $sizeGB, $freeGB, $fs)"

:: Execute PowerShell and parse output (using ~ as delimiter)
set "TypeCode="
set "TypeName="
set "Label="
set "SizeGB="
set "FreeGB="
set "FileSystem="

for /f "tokens=1-6 delims=~" %%a in (
    'powershell -NoProfile -ExecutionPolicy Bypass -Command "!PSCmd!" 2^>nul'
) do (
    set "TypeCode=%%a"
    set "TypeName=%%b"
    set "Label=%%c"
    set "SizeGB=%%d"
    set "FreeGB=%%e"
    set "FileSystem=%%f"
)

:: Validate output
if "!TypeCode!"=="NOT_FOUND" (
    echo [ERROR] Could not query drive %DriveColon%. >&2
    endlocal
    exit /b 1
)

if not defined TypeCode (
    echo [ERROR] PowerShell query failed. >&2
    endlocal
    exit /b 1
)

:: Display results
echo ============================================================
echo   Drive Details
echo ============================================================
echo.
echo   Drive:        %DriveColon%
echo   Label:        !Label!
echo   Type:         !TypeName! (Code: !TypeCode!)
echo   File System:  !FileSystem!

if "!SizeGB!" neq "N/A" (
    echo   Size:         !SizeGB! GB
    echo   Free:         !FreeGB! GB
    
    :: Calculate used percentage
    for /f %%p in ('powershell -NoProfile -Command "if (!SizeGB! -gt 0) { [math]::Round(((!SizeGB! - !FreeGB!) / !SizeGB!) * 100, 1) } else { 0 }"') do set "UsedPct=%%p"
    echo   Used:         !UsedPct!%%
)

echo ============================================================
echo.

:: Interpret drive type
if "!TypeCode!"=="2" (
    echo [REMOVABLE MEDIA]
    echo.
    echo Characteristics:
    echo   - USB flash drives, SD cards, external SSDs
    echo   - Can be safely ejected
    echo   - Hot-pluggable
    echo.
    echo Recommendations:
    echo   - Always use "Safely Remove Hardware"
    echo   - Verify backups before formatting
    
) else if "!TypeCode!"=="3" (
    echo [FIXED DISK]
    echo.
    echo Characteristics:
    echo   - Internal hard drives, SSDs
    echo   - External USB hard drives also show as Fixed
    echo   - Permanent storage
    echo.
    
    :: Check if disk is getting full
    if defined UsedPct (
        for /f %%n in ("!UsedPct!") do set /a "UsedInt=%%n"
        if !UsedInt! GEQ 90 (
            echo [WARNING] Disk is !UsedPct!%% full - consider cleanup
        ) else if !UsedInt! GEQ 80 (
            echo [INFO] Disk is !UsedPct!%% full - monitor space usage
        )
    )
    
) else if "!TypeCode!"=="4" (
    echo [NETWORK DRIVE]
    echo.
    echo Characteristics:
    echo   - Mapped network share
    echo   - Requires network connectivity
    echo   - May have access restrictions
    echo.
    echo Note: Performance depends on network speed
    
) else if "!TypeCode!"=="5" (
    echo [OPTICAL DRIVE]
    echo.
    echo Characteristics:
    echo   - CD-ROM, DVD, or Blu-ray drive
    echo   - Read-only or writable depending on media
    echo   - May be empty (no disc inserted^)
    
) else if "!TypeCode!"=="6" (
    echo [RAM DISK]
    echo.
    echo Characteristics:
    echo   - Virtual drive in system memory
    echo   - Extremely fast access
    echo   - Data lost on shutdown/reboot
    echo.
    echo Warning: All data is volatile
    
) else (
    echo [UNKNOWN TYPE]
    echo.
    echo Drive type code !TypeCode! is not recognized.
    echo This may be a specialized or virtual drive.
)

echo ============================================================
echo.

endlocal
exit /b 0

Why Get-CimInstance instead of wmic:

  • wmic is deprecated since Windows 10 21H1 and produces \r-corrupted output. When parsing wmic logicaldisk get drivetype /value, the \r character becomes part of the variable, causing if "%Type%"=="2" to fail because the variable actually contains 2\r.
  • Get-CimInstance Win32_LogicalDisk returns the same data as clean typed values.

Method 2: Complete Drive Inventory by Type

Shows every connected drive organized by type, useful for understanding the full storage landscape before running any automated operation.

@echo off
setlocal

echo [INFO] Drive inventory by type:
echo --------------------------------------------------

powershell -NoProfile -Command ^
    "$drives = Get-CimInstance Win32_LogicalDisk;" ^
    "if (-not $drives) { Write-Host 'No drives found.'; exit 0 };" ^
    "$drives | ForEach-Object {" ^
    "    $typeName = switch ($_.DriveType) {" ^
    "        0 { 'Unknown' } 1 { 'No Root' } 2 { 'Removable' }" ^
    "        3 { 'Fixed' } 4 { 'Network' } 5 { 'CD-ROM' } 6 { 'RAM Disk' }" ^
    "        default { 'Unknown' }" ^
    "    };" ^
    "    $sizeGB = if ($_.Size) { [math]::Round($_.Size / 1GB, 1) } else { 'N/A' };" ^
    "    $freeGB = if ($_.FreeSpace) { [math]::Round($_.FreeSpace / 1GB, 1) } else { 'N/A' };" ^
    "    [PSCustomObject]@{" ^
    "        Drive = $_.DeviceID;" ^
    "        Type = $typeName;" ^
    "        Label = if ($_.VolumeName) { $_.VolumeName } else { '(none)' };" ^
    "        FileSystem = if ($_.FileSystem) { $_.FileSystem } else { 'N/A' };" ^
    "        'Size GB' = $sizeGB;" ^
    "        'Free GB' = $freeGB" ^
    "    }" ^
    "} | Sort-Object Type, Drive | Format-Table -AutoSize"

echo --------------------------------------------------

endlocal
exit /b 0

Sample output:

Drive Type      Label       FileSystem Size GB Free GB
----- ----      -----       ---------- ------- -------
C:    Fixed     Windows     NTFS         476.9   234.1
D:    Fixed     Data        NTFS         931.5   612.3
E:    Network   SharedDocs  NTFS         N/A     N/A
F:    Removable KINGSTON    FAT32         28.9    15.2
G:    CD-ROM    (none)      N/A          N/A     N/A

Method 3: Deep Drive Classification (Bus Type + Media Type)

For the most accurate classification, distinguishing internal drives from external USB hard drives, NVMe from SATA, and SSDs from HDDs, query the physical disk properties.

@echo off
setlocal

echo [INFO] Detailed drive classification:
echo --------------------------------------------------

powershell -NoProfile -Command ^
    "Get-CimInstance Win32_LogicalDisk | Where-Object { $_.DriveType -in 2,3 } | ForEach-Object {" ^
    "    $logicalDisk = $_;" ^
    "    $partition = Get-CimAssociatedInstance $logicalDisk -ResultClassName Win32_DiskPartition -ErrorAction SilentlyContinue;" ^
    "    $physDisk = if ($partition) {" ^
    "        Get-CimAssociatedInstance $partition -ResultClassName Win32_DiskDrive -ErrorAction SilentlyContinue" ^
    "    };" ^
    "    $psDisk = if ($physDisk) {" ^
    "        Get-PhysicalDisk | Where-Object { $_.DeviceId -eq $physDisk.Index } | Select-Object -First 1" ^
    "    };" ^
    "    $busType = if ($psDisk) { [string]$psDisk.BusType } elseif ($physDisk) { $physDisk.InterfaceType } else { 'Unknown' };" ^
    "    $mediaType = if ($psDisk.MediaType) { [string]$psDisk.MediaType } else { 'Unknown' };" ^
    "    $classification = switch ($true) {" ^
    "        ($logicalDisk.DriveType -eq 2) { 'Removable Media' }" ^
    "        ($busType -eq 'USB') { 'External (USB)' }" ^
    "        ($busType -eq 'NVMe') { 'Internal (NVMe)' }" ^
    "        ($busType -match 'SATA|ATA') { 'Internal (SATA)' }" ^
    "        ($busType -eq 'SAS') { 'Internal (SAS)' }" ^
    "        default { 'Local' }" ^
    "    };" ^
    "    [PSCustomObject]@{" ^
    "        Drive = $logicalDisk.DeviceID;" ^
    "        Classification = $classification;" ^
    "        Bus = $busType;" ^
    "        Media = $mediaType;" ^
    "        Label = if ($logicalDisk.VolumeName) { $logicalDisk.VolumeName } else { '-' };" ^
    "        'Size GB' = [math]::Round($logicalDisk.Size / 1GB, 1)" ^
    "    }" ^
    "} | Format-Table -AutoSize"

echo --------------------------------------------------
echo.
echo Key: Removable Media = USB flash/SD card
echo      External (USB)  = USB hard drive (reports as Fixed/type 3)
echo      Internal (NVMe) = NVMe SSD
echo      Internal (SATA) = SATA HDD or SSD

endlocal
exit /b 0

Sample output:

Drive Classification    Bus  Media       Label       Size GB
----- --------------    ---  -----       -----       -------
C:    Internal (NVMe)   NVMe SSD         Windows       476.9
D:    Internal (SATA)   SATA HDD         Data          931.5
E:    External (USB)    USB  Unspecified  Backup        465.8
F:    Removable Media   USB  Unspecified  KINGSTON       28.9

Why bus type matters more than drive type code:

DriveType 3 (Fixed) includes both internal SATA/NVMe drives AND external USB hard drives. If your safety gate blocks only DriveType 2 (Removable), an external USB HDD would pass the check and could be accidentally formatted. Method 3's classification distinguishes Internal (SATA) from External (USB) by checking the bus type, providing a more accurate safety check.

Method 4: Safety Gate for Destructive Operations

Use this before any format, wipe, or destructive operation to prevent accidentally targeting removable or external media.

@echo off
setlocal EnableDelayedExpansion

set "TargetDrive=%~1"

if "%TargetDrive%"=="" (
    echo ============================================================
    echo   Drive Safety Gate - Usage
    echo ============================================================
    echo.
    echo Usage: %~nx0 ^<drive_letter^>
    echo.
    echo Purpose:
    echo   Verifies a drive is a fixed internal drive before allowing
    echo   destructive operations (format, wipe, partition, cleanup^)
    echo.
    echo Examples:
    echo   %~nx0 D
    echo   %~nx0 E:
    echo.
    endlocal
    exit /b 1
)

:: Normalize drive letter
set "DriveLetter=%TargetDrive:~0,1%"
set "DriveColon=%DriveLetter%:"

if not exist %DriveColon%\ (
    echo [ERROR] Drive %DriveColon% not found or not accessible. >&2
    endlocal
    exit /b 1
)

echo ============================================================
echo   Safety Gate: Destructive Operation Check
echo ============================================================
echo.
echo Target Drive: %DriveColon%
echo.
echo [SAFETY] Verifying drive type...
echo.

:: Build PowerShell command with proper escaping
set "PSCmd=$ErrorActionPreference = 'Stop';"
set "PSCmd=!PSCmd! try {"
set "PSCmd=!PSCmd!   $ld = Get-CimInstance Win32_LogicalDisk -Filter \"DeviceID='%DriveColon%'\";"
set "PSCmd=!PSCmd!   if (-not $ld) { Write-Output 'ERROR~Drive not found'; exit 2 };"
set "PSCmd=!PSCmd!   "
set "PSCmd=!PSCmd!   # Check basic drive type"
set "PSCmd=!PSCmd!   if ($ld.DriveType -eq 2) { Write-Output 'REMOVABLE~USB flash or SD card'; exit 1 };"
set "PSCmd=!PSCmd!   if ($ld.DriveType -eq 4) { Write-Output 'NETWORK~Network mapped drive'; exit 1 };"
set "PSCmd=!PSCmd!   if ($ld.DriveType -eq 5) { Write-Output 'OPTICAL~CD/DVD drive'; exit 1 };"
set "PSCmd=!PSCmd!   if ($ld.DriveType -eq 6) { Write-Output 'RAMDISK~RAM Disk'; exit 1 };"
set "PSCmd=!PSCmd!   if ($ld.DriveType -ne 3) { Write-Output \"OTHER~Unknown type ($($ld.DriveType))\"; exit 1 };"
set "PSCmd=!PSCmd!   "
set "PSCmd=!PSCmd!   # DriveType is 3 (Fixed), but need to check if it's external USB"
set "PSCmd=!PSCmd!   $part = Get-CimAssociatedInstance $ld -ResultClassName Win32_DiskPartition -ErrorAction SilentlyContinue;"
set "PSCmd=!PSCmd!   $disk = if ($part) { Get-CimAssociatedInstance $part -ResultClassName Win32_DiskDrive -ErrorAction SilentlyContinue };"
set "PSCmd=!PSCmd!   "
set "PSCmd=!PSCmd!   if ($disk) {"
set "PSCmd=!PSCmd!     $interface = $disk.InterfaceType;"
set "PSCmd=!PSCmd!     $model = $disk.Model;"
set "PSCmd=!PSCmd!     $size = [math]::Round($disk.Size / 1GB, 1);"
set "PSCmd=!PSCmd!     "
set "PSCmd=!PSCmd!     # Check for USB interface"
set "PSCmd=!PSCmd!     if ($interface -eq 'USB') {"
set "PSCmd=!PSCmd!       Write-Output \"EXTERNAL_USB~External USB HDD ($model, ${size}GB)\"; exit 1;"
set "PSCmd=!PSCmd!     }"
set "PSCmd=!PSCmd!     "
set "PSCmd=!PSCmd!     # Additional check: MediaType for removable"
set "PSCmd=!PSCmd!     if ($disk.MediaType -match 'Removable') {"
set "PSCmd=!PSCmd!       Write-Output \"REMOVABLE_DISK~Removable disk ($model)\"; exit 1;"
set "PSCmd=!PSCmd!     }"
set "PSCmd=!PSCmd!     "
set "PSCmd=!PSCmd!     Write-Output \"INTERNAL~Internal fixed drive ($interface, $model, ${size}GB)\"; exit 0;"
set "PSCmd=!PSCmd!   } else {"
set "PSCmd=!PSCmd!     # Could not get physical disk info, assume internal but warn"
set "PSCmd=!PSCmd!     Write-Output 'INTERNAL_UNKNOWN~Fixed drive (physical disk info unavailable)'; exit 0;"
set "PSCmd=!PSCmd!   }"
set "PSCmd=!PSCmd! } catch {"
set "PSCmd=!PSCmd!   Write-Output \"ERROR~$($_.Exception.Message)\"; exit 2;"
set "PSCmd=!PSCmd! }"

:: Execute PowerShell command
set "DriveClass="
set "DriveDesc="

for /f "tokens=1,2 delims=~" %%a in (
    'powershell -NoProfile -ExecutionPolicy Bypass -Command "!PSCmd!" 2^>nul'
) do (
    set "DriveClass=%%a"
    set "DriveDesc=%%b"
)

set "GateResult=!errorlevel!"

echo ============================================================
echo   Analysis Results
echo ============================================================
echo.

:: Evaluate result
if !GateResult! equ 0 (
    echo Classification: !DriveClass!
    echo Details:        !DriveDesc!
    echo.
    echo ============================================================
    echo   SAFETY CHECK PASSED
    echo ============================================================
    echo.
    echo [CLEAR] Drive %DriveColon% is verified as an internal drive.
    echo [CLEAR] Destructive operations are PERMITTED.
    echo.
    echo WARNING: Ensure you have backups before proceeding!
    echo.
    endlocal
    exit /b 0
)

if !GateResult! equ 2 (
    echo [ERROR] Could not determine drive type for %DriveColon%. >&2
    echo [ERROR] Reason: !DriveDesc! >&2
    echo.
    echo ============================================================
    echo   OPERATION BLOCKED (Error)
    echo ============================================================
    echo.
    echo Destructive operation blocked for safety.
    echo.
    endlocal
    exit /b 1
)

:: Drive is not internal - BLOCK
echo Classification: !DriveClass!
echo Details:        !DriveDesc!
echo.
echo ============================================================
echo   OPERATION BLOCKED (Safety Gate)
echo ============================================================
echo.
echo [BLOCKED] Drive %DriveColon% is NOT an internal fixed drive.
echo [BLOCKED] Destructive operations are PROHIBITED.
echo.

:: Provide specific guidance based on drive type
if "!DriveClass!"=="REMOVABLE" (
    echo Drive Type: Removable media
    echo.
    echo This includes USB flash drives, SD cards, and similar devices.
    echo These should never be formatted without explicit user confirmation.
    
) else if "!DriveClass!"=="EXTERNAL_USB" (
    echo Drive Type: External USB hard drive
    echo.
    echo External USB drives are blocked by default because they:
    echo   - Are physically removable
    echo   - May contain user backup data
    echo   - Can be easily disconnected during operations
    echo.
    echo If you INTENTIONALLY want to format this drive:
    echo   1. Verify this is the correct drive
    echo   2. Ensure you have backups of any important data
    echo   3. Modify the safety gate script to allow USB drives
    echo      (search for "EXTERNAL_USB" in the script^)
    
) else if "!DriveClass!"=="NETWORK" (
    echo Drive Type: Network drive
    echo.
    echo Network drives cannot be formatted locally.
    echo Manage storage on the network share server.
    
) else if "!DriveClass!"=="OPTICAL" (
    echo Drive Type: Optical drive (CD/DVD^)
    echo.
    echo Optical drives cannot be formatted like hard drives.
    echo Use disc burning software instead.
    
) else (
    echo Drive Type: !DriveClass!
    echo.
    echo This drive type is not permitted for automated destructive operations.
)

echo.
echo ============================================================
echo.

endlocal
exit /b 1

Integration into destructive scripts:

:: Before any format, wipe, or cleanup
call check_drive_type.bat D
if errorlevel 1 (
    echo [ABORT] Target drive is not a fixed internal drive.
    exit /b 1
)

:: Only reaches here if the drive is internal and fixed
echo [PROCEED] Formatting D: ...
format D: /fs:ntfs /q /v:DataDrive

Method 5: Fleet-Wide Removable Drive Audit

For security monitoring, track which machines have removable drives attached. This is useful for data loss prevention (DLP) policies.

@echo off
setlocal

set "CSVFile=\\Server\Audit\removable_drives.csv"

if not exist "%CSVFile%" (
    echo "Timestamp","Computer","Drive","Type","Label","FileSystem","SizeGB","BusType" > "%CSVFile%" 2>nul
)

powershell -NoProfile -Command ^
    "$ts = Get-Date -Format 'yyyy-MM-dd HH:mm:ss';" ^
    "$removable = Get-CimInstance Win32_LogicalDisk | Where-Object { $_.DriveType -eq 2 };" ^
    "foreach ($r in $removable) {" ^
    "    $label = if ($r.VolumeName) { $r.VolumeName } else { 'none' };" ^
    "    $fs = if ($r.FileSystem) { $r.FileSystem } else { 'N/A' };" ^
    "    $sizeGB = if ($r.Size) { [math]::Round($r.Size / 1GB, 1) } else { 0 };" ^
    "    Write-Output ('\"' + $ts + '\",\"' + $env:COMPUTERNAME + '\",\"' + $r.DeviceID + '\",\"Removable\",\"' + $label + '\",\"' + $fs + '\",\"' + $sizeGB + '\",\"USB\"')" ^
    "}" >> "%CSVFile%" 2>nul

echo [OK] Removable drive data exported for %COMPUTERNAME%.

endlocal
exit /b 0

What to look for:

  • Removable drives on servers: Servers typically should not have USB flash drives attached. This may indicate unauthorized data transfer.
  • Large removable drives: A 256 GB USB drive can hold significant amounts of data. DLP policies may restrict removable media above certain sizes.
  • Unrecognized labels: A drive labeled "BACKUP" on a workstation that doesn't have an approved backup policy may be an unauthorized personal device.

How to Avoid Common Errors

Wrong Way: Guessing Drive Type by Letter

:: WRONG - drive letters are assigned dynamically
if "%TargetDrive%"=="C:" echo Fixed
if "%TargetDrive%"=="E:" echo Removable

Drive letters change based on plug order, existing mappings, and user configuration. C: is almost always the OS drive, but D:, E:, F: can be anything.

Correct Way: Always query the DriveType property (Methods 1–4). Hardware classification comes from the operating system, not the letter.

Problem: External USB HDD Reports as Fixed (Type 3)

External USB hard drives report DriveType 3 (Fixed) because the USB-SATA bridge controller presents them to Windows as fixed disks. A safety check that only blocks DriveType 2 will allow formatting an external backup drive.

Solution: Method 3 checks the physical bus type (USB, SATA, NVMe) in addition to the logical drive type. Method 4's safety gate blocks both DriveType 2 (removable) and USB bus type (external HDD).

Problem: wmic Drive Type Parsing Fails

:: BROKEN: \r character from wmic corrupts the variable
for /f "tokens=2 delims==" %%t in ('wmic logicaldisk where "DeviceID='E:'" get drivetype /value') do set "Type=%%t"
if "%Type%"=="2" echo Removable
:: This comparison fails because Type actually contains "2\r"

Correct Way: Use Get-CimInstance in PowerShell, which returns clean typed values.

Best Practices and Rules

1. Check Drive Type Before ANY Destructive Operation

Format, wipe, delete-all, and partition operations should always include a drive type check as a pre-condition. This is the simplest protection against accidentally destroying data on the wrong drive.

2. Check Bus Type, Not Just Drive Type

DriveType alone misclassifies external USB hard drives as "Fixed". For accurate protection, check the bus type (USB, SATA, NVMe) using Method 3 or 4.

3. Use as a Pre-Condition, Not a Suggestion

The safety gate (Method 4) should return a non-zero exit code that the calling script checks. If the gate fails, the destructive operation must not proceed, not just display a warning.

4. Log Removable Drive Activity for Security

For data loss prevention, track when removable drives appear on workstations and servers. Method 5 provides this as a fleet-wide CSV audit.

5. Never Assume by Drive Letter

Drive letters are dynamic. A script that assumes "E: is always the USB drive" will eventually format the wrong drive when the letter assignment changes.

Conclusions

Checking whether a drive is removable is a critical safety measure for any automated storage operation.

By combining logical drive type classification with physical bus type detection, you accurately distinguish internal fixed drives from USB flash drives AND external USB hard drives, preventing accidental data loss on the wrong type of media.

Integrating this check as a mandatory pre-condition in destructive scripts ensures that human error in drive identification never results in catastrophic data loss.

Navigation