Learning how to check USB storage status using a PowerShell script is a vital part of endpoint security for IT professionals and managed service providers (MSPs). Unauthorized use of USB drives can lead to data leakage, malware intrusion, or compliance violations. Understanding whether USB storage is enabled or disabled across devices gives administrators critical visibility and control. One way to automate this visibility is with a PowerShell script purpose-built for auditing USB storage status. This post explores a professional-grade PowerShell script that does exactly that—and integrates seamlessly with NinjaOne via custom fields.
Background
USB device usage control is often handled via Group Policy, registry edits, or service configuration changes. However, manually auditing these settings on hundreds or thousands of machines is impractical. The PowerShell script covered here was designed to automate the assessment of USB storage access, with the added functionality of updating NinjaOne custom fields with the result.
Whether you’re rolling out new security policies, responding to a compliance audit, or simply ensuring baseline configuration, this script helps ensure USB restrictions are accurately enforced. For MSPs, the ability to write this information back to NinjaOne’s device inventory enables scalable reporting and alerting.
The Script
#Requires -Version 5.1 <# .SYNOPSIS Checks if the workstation currently allows access to USB storage devices and optionally saves the results to a custom field. .DESCRIPTION Checks if the workstation currently allows access to USB storage devices and optionally saves the results to a custom field. By using this script, you indicate your acceptance of the following legal terms as well as our Terms of Use at https://www.ninjaone.com/terms-of-use. Ownership Rights: NinjaOne owns and will continue to own all right, title, and interest in and to the script (including the copyright). NinjaOne is giving you a limited license to use the script in accordance with these legal terms. Use Limitation: You may only use the script for your legitimate personal or internal business purposes, and you may not share the script with another party. Republication Prohibition: Under no circumstances are you permitted to re-publish the script in any script library or website belonging to or under the control of any other software provider. Warranty Disclaimer: The script is provided “as is” and “as available”, without warranty of any kind. NinjaOne makes no promise or guarantee that the script will be free from defects or that it will meet your specific needs or expectations. Assumption of Risk: Your use of the script is at your own risk. You acknowledge that there are certain inherent risks in using the script, and you understand and assume each of those risks. Waiver and Release: You will not hold NinjaOne responsible for any adverse or unintended consequences resulting from your use of the script, and you waive any legal or equitable rights or remedies you may have against NinjaOne relating to your use of the script. EULA: If you are a NinjaOne customer, your use of the script is subject to the End User License Agreement applicable to you (EULA). .EXAMPLE -CustomField "usbStorageStatus" Checking if the USB Mass Storage Driver service is enabled. Checking if the USB Mass Storage Driver is disabled via the registry. Checking if USB is disabled via group policy. USB is currently disabled. Attempting to set Custom Field 'usbStorageStatus'. Successfully set Custom Field 'usbStorageStatus'! .NOTES Minimum OS Architecture Supported: Windows 10 Release Notes: Initial Release #> [CmdletBinding()] param ( [Parameter()] [String]$CustomField ) begin { if ($env:customFieldName -and $env:customFieldName -notlike "null") { $CustomField = $env:customFieldName } if ($CustomField) { $CustomField = $CustomField.Trim() } function Set-NinjaProperty { [CmdletBinding()] Param( [Parameter(Mandatory = $True)] [String]$Name, [Parameter()] [String]$Type, [Parameter(Mandatory = $True, ValueFromPipeline = $True)] $Value, [Parameter()] [String]$DocumentName, [Parameter()] [Switch]$Piped ) # Remove the non-breaking space character if ($Type -eq "WYSIWYG") { $Value = $Value -replace 'Â ', ' ' } # Measure the number of characters in the provided value $Characters = $Value | ConvertTo-Json | Measure-Object -Character | Select-Object -ExpandProperty Characters # Throw an error if the value exceeds the character limit of 200,000 characters if ($Piped -and $Characters -ge 200000) { throw [System.ArgumentOutOfRangeException]::New("Character limit exceeded: the value is greater than or equal to 200,000 characters.") } if (!$Piped -and $Characters -ge 45000) { throw [System.ArgumentOutOfRangeException]::New("Character limit exceeded: the value is greater than or equal to 45,000 characters.") } # Initialize a hashtable for additional documentation parameters $DocumentationParams = @{} # If a document name is provided, add it to the documentation parameters if ($DocumentName) { $DocumentationParams["DocumentName"] = $DocumentName } # Define a list of valid field types $ValidFields = "Attachment", "Checkbox", "Date", "Date or Date Time", "Decimal", "Dropdown", "Email", "Integer", "IP Address", "MultiLine", "MultiSelect", "Phone", "Secure", "Text", "Time", "URL", "WYSIWYG" # Warn the user if the provided type is not valid if ($Type -and $ValidFields -notcontains $Type) { Write-Warning "$Type is an invalid type. Please check here for valid types: https://ninjarmm.zendesk.com/hc/en-us/articles/16973443979789-Command-Line-Interface-CLI-Supported-Fields-and-Functionality" } # Define types that require options to be retrieved $NeedsOptions = "Dropdown" # If the property is being set in a document or field and the type needs options, retrieve them if ($DocumentName) { if ($NeedsOptions -contains $Type) { $NinjaPropertyOptions = Ninja-Property-Docs-Options -AttributeName $Name @DocumentationParams 2>&1 } } else { if ($NeedsOptions -contains $Type) { $NinjaPropertyOptions = Ninja-Property-Options -Name $Name 2>&1 } } # Throw an error if there was an issue retrieving the property options if ($NinjaPropertyOptions.Exception) { throw $NinjaPropertyOptions } # Process the property value based on its type switch ($Type) { "Checkbox" { # Convert the value to a boolean for Checkbox type $NinjaValue = [System.Convert]::ToBoolean($Value) } "Date or Date Time" { # Convert the value to a Unix timestamp for Date or Date Time type $Date = (Get-Date $Value).ToUniversalTime() $TimeSpan = New-TimeSpan (Get-Date "1970-01-01 00:00:00") $Date $NinjaValue = $TimeSpan.TotalSeconds } "Dropdown" { # Convert the dropdown value to its corresponding GUID $Options = $NinjaPropertyOptions -replace '=', ',' | ConvertFrom-Csv -Header "GUID", "Name" $Selection = $Options | Where-Object { $_.Name -eq $Value } | Select-Object -ExpandProperty GUID # Throw an error if the value is not present in the dropdown options if (!($Selection)) { throw [System.ArgumentOutOfRangeException]::New("Value is not present in dropdown options.") } $NinjaValue = $Selection } default { # For other types, use the value as is $NinjaValue = $Value } } # Set the property value in the document if a document name is provided if ($DocumentName) { $CustomField = Ninja-Property-Docs-Set -AttributeName $Name -AttributeValue $NinjaValue @DocumentationParams 2>&1 } else { try { # Otherwise, set the standard property value if ($Piped) { $CustomField = $NinjaValue | Ninja-Property-Set-Piped -Name $Name 2>&1 } else { $CustomField = Ninja-Property-Set -Name $Name -Value $NinjaValue 2>&1 } } catch { Write-Host -Object "[Error] Failed to set custom field." throw $_.Exception.Message } } # Throw an error if setting the property failed if ($CustomField.Exception) { throw $CustomField } } function Test-IsElevated { $id = [System.Security.Principal.WindowsIdentity]::GetCurrent() $p = New-Object System.Security.Principal.WindowsPrincipal($id) $p.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator) } if (!$ExitCode) { $ExitCode = 0 } } process { # Check if the script is running with elevated (Administrator) privileges. if (!(Test-IsElevated)) { Write-Host -Object "[Error] Access Denied. Please run with Administrator privileges." exit 1 } # Notify that the script is checking if the USB Mass Storage Driver service is enabled. Write-Host -Object "Checking if the USB Mass Storage Driver service is enabled." try { $USBService = Get-Service -Name "USBStor" -ErrorAction Stop } catch { Write-Host -Object "[Error] $($_.Exception.Message)" $ExitCode = 1 } # If the USB Mass Storage Driver service was not found, print an error message and exit. if (!$USBService) { Write-Host -Object "[Error] Accessing the 'USB Mass Storage Driver' service status." if ($CustomField) { try { Write-Host -Object "Attempting to set Custom Field '$CustomField'." Set-NinjaProperty -Name $CustomField -Value "Unable to Determine" Write-Host -Object "Successfully set Custom Field '$CustomField'!" } catch { Write-Host "[Error] $($_.Exception.Message)" } } exit 1 } # Notify that the script is checking if the USB Mass Storage Driver is disabled via the registry. Write-Host -Object "Checking if the USB Mass Storage Driver is disabled via the registry." # Check if the registry path for the USB Mass Storage Driver exists. if (!(Test-Path -Path "Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\USBSTOR")) { Write-Host -Object "[Error] The 'USB Mass Storage Driver' Service is missing it's registry key." if ($CustomField) { try { Write-Host -Object "Attempting to set Custom Field '$CustomField'." Set-NinjaProperty -Name $CustomField -Value "Unable to Determine" Write-Host -Object "Successfully set Custom Field '$CustomField'!" } catch { Write-Host "[Error] $($_.Exception.Message)" } } exit 1 } # Get the registry value for the USB Mass Storage Driver service start type. $USBRegKey = Get-ItemProperty -Path "Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\USBSTOR" -Name "Start" | Select-Object -ExpandProperty Start # Notify that the script is checking if USB is disabled via group policy. Write-Host -Object "Checking if USB is disabled via group policy." # Check if the group policy registry path for removable usb devices exists. if (Test-Path -Path "Registry::HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\RemovableStorageDevices\{53f5630d-b6bf-11d0-94f2-00a0c91efb8b}" -ErrorAction SilentlyContinue) { $USBPolicyRegKey = Get-ItemProperty -Path "Registry::HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\RemovableStorageDevices\{53f5630d-b6bf-11d0-94f2-00a0c91efb8b}" # If all group policy settings deny read, write, and execute access, set the USB policy status to "Disabled". if ($USBPolicyRegKey.Deny_Read -eq 1 -and $USBPolicyRegKey.Deny_Write -eq 1 -and $USBPolicyRegKey.Deny_Execute -eq 1) { $USBPolicy = "Disabled" } } # Check if the group policy registry path for all removable storage devices exists. if (Test-Path -Path "Registry::HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\RemovableStorageDevices" -ErrorAction SilentlyContinue) { $USBPolicyRegKey = Get-ItemProperty -Path "Registry::HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\RemovableStorageDevices" # If group olicy is set to deny all removable devices, set the USB policy status to "Disabled". if ($USBPolicyRegKey.Deny_All -eq 1) { $USBPolicy = "Disabled" } } try { # If the USB service is disabled, the registry value indicates it is disabled, or the group policy disables it, set the custom field to "Disabled". if ($USBService.StartType -eq "Disabled" -or $USBRegKey -eq 4 -or $USBPolicy -eq "Disabled") { Write-Host -Object "`nUSB is currently disabled.`n" if ($CustomField) { Write-Host -Object "Attempting to set Custom Field '$CustomField'." Set-NinjaProperty -Name $CustomField -Value "Disabled" Write-Host -Object "Successfully set Custom Field '$CustomField'!" } } else { # If none of the conditions indicate the USB is disabled, set the custom field to "Enabled". Write-Host -Object "`nUSB is currently enabled.`n" if ($CustomField) { Write-Host -Object "Attempting to set Custom Field '$CustomField'." Set-NinjaProperty -Name $CustomField -Value "Enabled" Write-Host -Object "Successfully set Custom Field '$CustomField'!" } } } catch { # Catch any exceptions that occur and print an error message. Write-Host "[Error] $($_.Exception.Message)" $ExitCode = 1 } # Exit the script with the appropriate exit code. exit $ExitCode } end { }
Detailed Breakdown
This script performs three primary checks and optionally sets a NinjaOne custom field to reflect the USB status:
Check USB Service Status
The script begins by verifying if the USBStor (USB Mass Storage Driver) service is enabled:
powershell
CopyEdit
$USBService = Get-Service -Name “USBStor”
- If the service is missing or disabled, USB storage is likely blocked.
Check Registry Settings
It then checks the registry value under:
sql
CopyEdit
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\USBSTOR
- A Start value of 4 means the service is disabled.
Check Group Policy Settings
Lastly, it examines Group Policy-based registry keys under:
CopyEdit
HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\RemovableStorageDevices
- Specific GUIDs and keys like Deny_All, Deny_Read, Deny_Write, and Deny_Execute are checked to determine if policy-based restrictions are in place.
- Custom Field Integration
If the -CustomField parameter is provided, the script uses the Set-NinjaProperty function to set the device’s NinjaOne custom field with one of the following values:- Enabled
- Disabled
- Unable to Determine
Potential Use Cases
Case Study: USB Security Compliance Audit
An MSP is onboarding a new client in the healthcare industry. HIPAA compliance requires that USB storage be disabled on all endpoints. The technician uses this script to audit all Windows 10 machines via NinjaOne. Each device is automatically tagged with the USB status in a custom field called usbStorageStatus. A report is generated within NinjaOne showing which devices are non-compliant, enabling quick remediation.
Comparisons
Alternative Methods:
- Group Policy Audit: Manually inspect settings via gpedit.msc. Time-consuming and non-scalable.
- Endpoint Protection Software: Tools like Symantec or CrowdStrike often monitor USB, but lack simple integration with RMM tools for visibility.
- Manual Registry Checks: Reliable but requires deep knowledge and scripting ability to scale.
Compared to these, the PowerShell script provides a fast, consistent, and automatable approach—especially valuable when integrated with NinjaOne’s custom fields for centralized tracking.
FAQs
Q: Does the script require administrator privileges?
Yes, the script verifies elevation using Test-IsElevated and exits with an error if not run as Administrator.
Q: What OS versions are supported?
Minimum supported OS is Windows 10, as defined in the script notes.
Q: What happens if the USBStor service or registry keys are missing?
The script marks the result as Unable to Determine and safely exits.
Q: Is this compatible with other RMM tools?
The script is built with NinjaOne integration in mind. However, the USB checking logic can be repurposed for use with other systems.
Implications
Improperly configured USB policies are a common security blind spot. If the script finds USB enabled on sensitive devices, this could signify a compliance failure or a potential attack vector. Conversely, consistent reporting of Disabledacross a fleet confirms that security policies are effective.
From a business continuity standpoint, identifying endpoints with unknown or improperly configured USB settings helps prevent data exfiltration and enhances incident response preparedness.
Recommendations
- Use as Scheduled Task: Run this script weekly as part of your standard device audit workflow.
- Integrate with NinjaOne Reports: Build reports around the usbStorageStatus field to monitor and alert on changes.
- Pair with Remediation: Create companion scripts to disable USB automatically if compliance is not met.
- Version Control: Document and version your script for audit trails and maintenance.
Final Thoughts
For MSPs and IT departments, the ability to get USB storage status with PowerShell is a powerful asset. This script not only determines USB enablement across multiple methods (service, registry, and group policy) but also writes results directly into NinjaOne for scalable tracking. By integrating security policy validation into your RMM tool, you strengthen compliance, reduce manual effort, and gain actionable insights.