<# .SYNOPSIS Sets or changes the BIOS admin (setup) password on a Dell PC with the Dell Command | PowerShell Provider. .DESCRIPTION If no admin password is set, it sets one. If one is set, it changes it, which needs the current password. Both passwords come in as SecureStrings, or are read at runtime from Configuration Manager task sequence variables, so neither one is ever written into the script or the package. Exit codes: 0 = set or changed (or not a Dell); 1 = the BIOS refused the change; 2 = provider module missing; 3 = a needed password wasn't supplied; 4 = new password fails basic checks. .PARAMETER NewPassword The admin password you want the machine to end up with. .PARAMETER CurrentPassword The admin password that's set today. Not needed if none is set. .PARAMETER NewPasswordVariable Task sequence variable holding the new password, used when -NewPassword isn't given. .PARAMETER CurrentPasswordVariable Task sequence variable holding the current password, used when -CurrentPassword isn't given. .EXAMPLE .\Set-DellAdminPassword.ps1 -NewPassword (Read-Host -AsSecureString 'New') -CurrentPassword (Read-Host -AsSecureString 'Current') .EXAMPLE .\Set-DellAdminPassword.ps1 -NewPasswordVariable BIOSAdminPasswordNew -CurrentPasswordVariable BIOSAdminPassword #> [CmdletBinding(SupportsShouldProcess)] param( [securestring]$NewPassword, [securestring]$CurrentPassword, [ValidateNotNullOrEmpty()][string]$NewPasswordVariable = 'BIOSAdminPasswordNew', [ValidateNotNullOrEmpty()][string]$CurrentPasswordVariable = 'BIOSAdminPassword' ) function Get-TSSecret { param([string]$Name) 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 = 'AdminPassword'; Action = ''; Status = ''; Detail = '' } function Complete-Run { param([int]$Code) [pscustomobject]$result; exit $Code } $manufacturer = (Get-CimInstance -ClassName Win32_ComputerSystem).Manufacturer if ($manufacturer -notlike 'Dell*') { $result.Status = 'NotApplicable'; $result.Detail = "Manufacturer is '$manufacturer'."; Complete-Run 0 } try { Import-DellProvider } catch { $result.Status = 'Failed'; $result.Detail = $_.Exception.Message; Complete-Run 2 } if (-not $NewPassword) { $NewPassword = Get-TSSecret -Name $NewPasswordVariable } if (-not $NewPassword) { $result.Status = 'Failed'; $result.Detail = 'No new password supplied.'; Complete-Run 3 } # Dell's limits vary by model, but 4 to 32 characters is safe almost everywhere. if ($NewPassword.Length -lt 4 -or $NewPassword.Length -gt 32) { $result.Status = 'Failed'; $result.Detail = "New password is $($NewPassword.Length) characters; keep it between 4 and 32." Complete-Run 4 } $isSet = "$((Get-Item -Path 'DellSmbios:\Security\IsAdminPasswordSet' -ErrorAction SilentlyContinue).CurrentValue)" $result.Action = if ($isSet -eq 'False') { 'Set' } else { 'Change' } if ($result.Action -eq 'Change') { if (-not $CurrentPassword) { $CurrentPassword = Get-TSSecret -Name $CurrentPasswordVariable } if (-not $CurrentPassword) { $result.Status = 'Failed'; $result.Detail = 'An admin password is already set; supply the current one.'; Complete-Run 3 } } $exitCode = 0 if ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, "$($result.Action) the BIOS admin password")) { $setArgs = @{ Path = 'DellSmbios:\Security\AdminPassword' Value = [System.Net.NetworkCredential]::new('', $NewPassword).Password ErrorAction = 'Stop' } if ($result.Action -eq 'Change') { $setArgs.Password = [System.Net.NetworkCredential]::new('', $CurrentPassword).Password } try { Set-Item @setArgs $result.Status = 'Success' } catch { $result.Status = 'Failed'; $result.Detail = $_.Exception.Message; $exitCode = 1 } finally { $setArgs.Clear() } } else { $result.Status = 'WhatIf' } Complete-Run $exitCode