<# .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