SCRIPT LIBRARY · POWERSHELL
Clear a Dell Hard Drive Password with PowerShell and ConfigMgr
Remove the internal drive password from Dell PCs with Dell's PowerShell provider, without ever writing the password into a script or package.
- What it does
- Checks whether a Dell PC has an internal drive (HDD) password set and clears it. The current password comes in as a SecureString or from a hidden task sequence variable at runtime, never from the script itself.
- Requires
- Windows PowerShell 5.1
- DellBIOSProvider module (Dell Command | PowerShell Provider), installed or shipped in the package
- Microsoft Visual C++ Redistributable (the provider needs it)
- Permissions
- Local administrator or SYSTEM on the target PC, plus the current drive password
- Runs on
- Dell business PCs (OptiPlex, Latitude, Precision) on Windows 10/11
- Tested
- Parse-checked and dry-run with mocked Dell provider cmdlets in PowerShell 7.4
Drive passwords are one of those settings that seem like a great idea when somebody turns them on, and a real headache when you need to reimage, repurpose, or hand a machine to someone else. Every reboot stops at a prompt, and nobody remembers who knows the password.
Dell's PowerShell provider (Dell Command | PowerShell Provider, module name DellBIOSProvider) exposes the BIOS as a drive, so clearing the drive password is one Set-Item. The hard part isn't the command. It's getting the current password to the machine without leaving it lying around.
The first version of this post wrote the password straight into the script inside the package. Don't do that. This version takes the password as a SecureString when you run it by hand, and in a task sequence it reads it at runtime from a hidden task sequence variable. It also checks whether a drive password is set at all, skips anything that isn't a Dell, and returns exit codes ConfigMgr can report on.
<#
.SYNOPSIS
Clears the internal drive (HDD) password on a Dell PC with the Dell Command | PowerShell Provider.
.DESCRIPTION
Loads the DellBIOSProvider module (installed, or shipped in a folder next to this script), checks
whether a drive password is actually set, and clears it. The current drive password comes in as a
SecureString, or is read at runtime from a Configuration Manager task sequence variable, so it
never has to be written into the script or the package.
Exit codes: 0 = cleared, not set, or not a Dell; 1 = the BIOS refused the change;
2 = provider module missing; 3 = no password supplied.
.PARAMETER CurrentPassword
The drive password that's set today.
.PARAMETER PasswordVariable
Task sequence variable to read the password from when -CurrentPassword isn't given.
.EXAMPLE
.\Clear-DellHddPassword.ps1 -CurrentPassword (Read-Host -AsSecureString 'Current HDD password')
.EXAMPLE
.\Clear-DellHddPassword.ps1 -PasswordVariable BIOSHddPassword -WhatIf
#>
[CmdletBinding(SupportsShouldProcess)]
param(
[securestring]$CurrentPassword,
[ValidateNotNullOrEmpty()][string]$PasswordVariable = 'BIOSHddPassword'
)
function Get-TSSecret {
param([string]$Name)
# Only exists inside a running task sequence. Anywhere else, quietly return nothing.
try { $ts = New-Object -ComObject Microsoft.SMS.TSEnvironment -ErrorAction Stop } catch { return $null }
$value = $ts.Value($Name)
if ([string]::IsNullOrEmpty($value)) { return $null }
ConvertTo-SecureString -String $value -AsPlainText -Force
}
function Import-DellProvider {
if (Get-Module -ListAvailable -Name DellBIOSProvider) { Import-Module DellBIOSProvider -ErrorAction Stop; return }
# Fall back to a copy shipped in the package (Save-Module puts it in a version subfolder).
$bundled = Get-ChildItem -Path (Join-Path $PSScriptRoot 'DellBIOSProvider') -Filter 'DellBIOSProvider.psd1' -Recurse -ErrorAction SilentlyContinue | Select-Object -First 1
if ($bundled) { Import-Module $bundled.FullName -ErrorAction Stop; return }
throw 'DellBIOSProvider not found. Install it, or copy the module folder into the package next to this script.'
}
$result = [ordered]@{ ComputerName = $env:COMPUTERNAME; Setting = 'HDDPassword'; Status = ''; Detail = '' }
$exitCode = 0
$manufacturer = (Get-CimInstance -ClassName Win32_ComputerSystem).Manufacturer
if ($manufacturer -notlike 'Dell*') {
$result.Status = 'NotApplicable'; $result.Detail = "Manufacturer is '$manufacturer'."
[pscustomobject]$result; exit 0
}
try { Import-DellProvider }
catch { $result.Status = 'Failed'; $result.Detail = $_.Exception.Message; [pscustomobject]$result; exit 2 }
$isSet = (Get-Item -Path 'DellSmbios:\Security\IsHDDPasswordSet' -ErrorAction SilentlyContinue).CurrentValue
Write-Verbose "IsHDDPasswordSet reports '$isSet'"
if ("$isSet" -eq 'False') {
$result.Status = 'NotSet'; $result.Detail = 'No drive password to clear.'
[pscustomobject]$result; exit 0
}
if (-not $CurrentPassword) { $CurrentPassword = Get-TSSecret -Name $PasswordVariable }
if (-not $CurrentPassword) {
$result.Status = 'Failed'; $result.Detail = "No password given and task sequence variable '$PasswordVariable' is empty or unavailable."
[pscustomobject]$result; exit 3
}
if ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, 'Clear the internal drive (HDD) password')) {
# The provider wants a plain string. Decrypt at the last possible moment and drop it right after.
$plain = [System.Net.NetworkCredential]::new('', $CurrentPassword).Password
try {
Set-Item -Path 'DellSmbios:\Security\HDDPassword' -Value '' -Password $plain -ErrorAction Stop
$result.Status = 'Cleared'
}
catch {
$result.Status = 'Failed'; $result.Detail = $_.Exception.Message; $exitCode = 1
}
finally {
$plain = $null
}
}
else {
$result.Status = 'WhatIf'
}
[pscustomobject]$result
exit $exitCode
Parameters
| Parameter | Type | Default | What it's for |
|---|---|---|---|
-CurrentPassword | securestring | — | The drive password that's set today. Leave it off inside a task sequence and the script reads the variable below instead. |
-PasswordVariable | string | BIOSHddPassword | Name of the task sequence variable holding the current password. Mark it "Do not display this value" wherever you set it. |
-WhatIf | switch | — | Shows what it would do, after checking the model and password state, without touching the BIOS. |
Run it
On one machine, typing the password in when asked.
.\Clear-DellHddPassword.ps1 -CurrentPassword (Read-Host -AsSecureString 'Current HDD password')As a "Run PowerShell Script" step in a task sequence. No parameters needed; it reads the hidden BIOSHddPassword variable.
powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\Clear-DellHddPassword.ps1Your task sequence uses a different variable name.
.\Clear-DellHddPassword.ps1 -PasswordVariable OSDDrivePasswordCheck what would happen first.
.\Clear-DellHddPassword.ps1 -CurrentPassword (Read-Host -AsSecureString 'Current HDD password') -WhatIfWhat you'll see
ComputerName : PC-0142
Setting : HDDPassword
Status : Cleared
Detail :
ComputerName : PC-0187
Setting : HDDPassword
Status : NotSet
Detail : No drive password to clear.
How it works
- Skip anything that isn't a Dell. It reads the manufacturer from
Win32_ComputerSystemand exits 0 on anything else, so a mixed collection doesn't light up red. - Load the provider. It uses an installed copy of
DellBIOSProviderif there is one, otherwise it looks for a copy shipped in the package next to the script. That means you don't need the PowerShell Gallery during a build. - Check whether there's anything to do.
DellSmbios:\Security\IsHDDPasswordSetsays whether a drive password exists. If it doesn't, the script stops there. - Get the password, safely. A SecureString parameter if you passed one, otherwise the task sequence variable. It's turned into plain text only for the
Set-Itemcall, because that's what the provider accepts, and dropped straight after. - Clear it and report.
Set-Item -Path DellSmbios:\Security\HDDPassword -Value '' -Password <current>does the actual work. You get an object back and an exit code: 0 for cleared or nothing to do, 1 if the BIOS said no, 2 if the provider is missing, 3 if there was no password to use.
Packaging it for a task sequence
This one belongs in a task sequence, because that's where you get hidden variables. The package just holds the script and a copy of the provider:
$SiteCode = 'ABC'
$SourceShare = '\\sccm01\Sources\Scripts'
$DPGroup = 'All DPs'
$Name = 'Dell - Clear HDD Password'
# Do the file work first, from a normal path. The CM site drive can't copy to UNC paths.
$source = Join-Path $SourceShare 'Clear-DellHddPassword'
New-Item -ItemType Directory -Path $source -Force | Out-Null
Copy-Item .\Clear-DellHddPassword.ps1 -Destination $source
Save-Module -Name DellBIOSProvider -Path $source
Import-Module (Join-Path $env:SMS_ADMIN_UI_PATH '..\ConfigurationManager.psd1')
Push-Location "$($SiteCode):\"
New-CMPackage -Name $Name -Path $source | Out-Null
Start-CMContentDistribution -PackageName $Name -DistributionPointGroupName $DPGroup
Pop-Location
In the task sequence, add a Set Task Sequence Variable step for BIOSHddPassword with "Do not display this value" ticked, then a Run PowerShell Script step pointing at the package and Clear-DellHddPassword.ps1. If you build a lot of these, there's a reusable version of the packaging part in Automating MECM Package Creation with PowerShell.
Take it further
- Use a collection variable instead. Set
BIOSHddPasswordon the collection and tick "Do not display this value in the Configuration Manager console", so the password isn't sitting in the task sequence itself. - Pair it with the admin password posts. Clear the drive password, change the admin password, and you've got a clean handoff sequence for repurposed machines.
- Report on it. The returned object drops straight into
Export-Csvif you run it throughInvoke-Commandacross a handful of machines.
Things that'll trip you up
- Never bake the password into the package. A password inside a .ps1 ends up on your source share, on every distribution point, and in ccmcache on every client that runs it. Use a hidden task sequence variable (set with "Do not display this value"), pull it from a secrets vault at runtime, or let Dell Command | Configure build the change for you as a package with the password encrypted inside it.
- A drive password is not BitLocker. It's ATA security on the drive itself. If the password is lost, there's no recovery key to fall back on, and the drive is about as useful as a doorstop. Try the script on one machine before a collection of two hundred.
- Not every model reports the state. If the model doesn't expose IsHDDPasswordSet, the script can't tell whether a password is set, so it goes ahead and tries. A wrong-password failure on those shows up as exit code 1.
- The provider needs the Visual C++ runtime. Without the Visual C++ Redistributable, importing DellBIOSProvider fails with an error that says nothing useful. Install the runtime first, or add it as an earlier step in the task sequence.