Skip to content

Instantly share code, notes, and snippets.

@DJStompZone
Created October 3, 2025 14:47
Show Gist options
  • Select an option

  • Save DJStompZone/9258d0b6d00ac7e864e4fa9d487e9df8 to your computer and use it in GitHub Desktop.

Select an option

Save DJStompZone/9258d0b6d00ac7e864e4fa9d487e9df8 to your computer and use it in GitHub Desktop.
<#
.SYNOPSIS
Purges audio devices and driver packages for a clean OEM reinstall.
.DESCRIPTION
This script removes present audio-related PnP devices (classes MEDIA, AudioEndpoint, and SoftwareComponent entries that look audio-related),
then deletes their third-party driver packages using pnputil. It uses Write-Progress throughout, supports WhatIf, can optionally create a System Restore Point,
and offers a switch to include Microsoft-signed packages if you truly want scorched earth.
By default it:
• Removes devices in classes: MEDIA, AudioEndpoint, and audio-related SoftwareComponent
• Deletes third-party driver packages with Class MEDIA or AudioEndpoint (i.e., non-Microsoft providers)
• Leaves Microsoft providers intact unless -IncludeMicrosoft is supplied
• Prompts for reboot at the end
.PARAMETER IncludeMicrosoft
Include Microsoft-provided driver packages in deletion. Use only if you want to remove generic MS audio stacks too. They will come back after reboot/scan.
.PARAMETER CreateRestorePoint
Attempt to create a system restore point named "AudioPurge (timestamp)" before changes. Requires System Restore enabled on the OS drive.
.PARAMETER LogPath
Path to write a transcript log (e.g., C:\Users\Public\AudioPurge.log). Defaults to %TEMP%\AudioPurge_<timestamp>.log
.PARAMETER ForceReboot
Automatically reboot without prompting when finished.
.PARAMETER WhatIf
Show what would happen without making changes.
.EXAMPLE
.\Purge-AudioDrivers.ps1
Runs with defaults: removes devices, deletes third-party audio driver packages, prompts to reboot.
.EXAMPLE
.\Purge-AudioDrivers.ps1 -IncludeMicrosoft -CreateRestorePoint -ForceReboot
Full wipe including Microsoft packages, makes a restore point first, then reboots automatically.
.NOTES
Author: DJ Stomp <85457381+DJStompZone@users.noreply.github.com>
License: MIT
Repo: https://github.com/djstompzone/placeholder
Requires: Windows 10/11 with pnputil, PowerShell 5.1+; Admin rights.
#>
[CmdletBinding(SupportsShouldProcess=$true)]
param(
[switch]$IncludeMicrosoft,
[switch]$CreateRestorePoint,
[string]$LogPath = $(Join-Path $env:TEMP ("AudioPurge_{0:yyyyMMdd_HHmmss}.log" -f (Get-Date))),
[switch]$ForceReboot
)
function Assert-Elevation {
$id = [Security.Principal.WindowsIdentity]::GetCurrent()
$p = New-Object Security.Principal.WindowsPrincipal($id)
if (-not $p.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
Write-Host "[*] Elevation required. Relaunching as Administrator..." -ForegroundColor Yellow
$psi = @{
FilePath = "powershell.exe"
ArgumentList = @("-NoProfile","-ExecutionPolicy","Bypass","-File",("`"{0}`"" -f $PSCommandPath))
Verb = "RunAs"
}
if ($PSBoundParameters.ContainsKey('IncludeMicrosoft')) { $psi.ArgumentList += "-IncludeMicrosoft" }
if ($PSBoundParameters.ContainsKey('CreateRestorePoint')) { $psi.ArgumentList += "-CreateRestorePoint" }
if ($PSBoundParameters.ContainsKey('LogPath')) { $psi.ArgumentList += @("-LogPath","`"$LogPath`"") }
if ($PSBoundParameters.ContainsKey('ForceReboot')) { $psi.ArgumentList += "-ForceReboot" }
if ($WhatIfPreference) { $psi.ArgumentList += "-WhatIf" }
Start-Process @psi
exit
}
}
function Start-TranscriptSafe {
param([string]$Path)
try {
Start-Transcript -Path $Path -Force -ErrorAction Stop | Out-Null
Write-Host ("[+] Logging to: {0}" -f $Path)
} catch {
Write-Warning ("[!] Could not start transcript: {0}" -f $_.Exception.Message)
}
}
function Stop-TranscriptSafe {
try { Stop-Transcript | Out-Null } catch {}
}
function New-RestorePointIfRequested {
param([switch]$Enable)
if (-not $Enable) { return }
Write-Host "[*] Creating System Restore Point (this can take a minute)..." -ForegroundColor Yellow
try {
# Checkpoint-Computer works only if System Restore is enabled
Checkpoint-Computer -Description ("AudioPurge {0:yyyy-MM-dd HH:mm:ss}" -f (Get-Date)) -RestorePointType "MODIFY_SETTINGS" -ErrorAction Stop
Write-Host "[+] Restore point created."
} catch {
Write-Warning ("[!] Failed to create restore point: {0}" -f $_.Exception.Message)
}
}
function Invoke-PnpUtil {
param([string[]]$Arguments, [int]$ExpectedExit = 0)
$psi = New-Object System.Diagnostics.ProcessStartInfo
$psi.FileName = "$env:SystemRoot\System32\pnputil.exe"
$psi.Arguments = ($Arguments -join " ")
$psi.RedirectStandardOutput = $true
$psi.RedirectStandardError = $true
$psi.UseShellExecute = $false
$psi.CreateNoWindow = $true
$p = New-Object System.Diagnostics.Process
$p.StartInfo = $psi
[void]$p.Start()
$stdout = $p.StandardOutput.ReadToEnd()
$stderr = $p.StandardError.ReadToEnd()
$p.WaitForExit()
[pscustomobject]@{
ExitCode = $p.ExitCode
StdOut = $stdout
StdErr = $stderr
Ok = ($p.ExitCode -eq $ExpectedExit)
CmdLine = "pnputil $($psi.Arguments)"
}
}
function Get-AudioDevices {
# MEDIA and AudioEndpoint are definitive. SoftwareComponent holds APO/FX/etc. We filter only those that smell like audio.
$classes = @("MEDIA","AudioEndpoint","SoftwareComponent")
$devices = @()
foreach ($cls in $classes) {
try {
$chunk = Get-PnpDevice -Class $cls -ErrorAction SilentlyContinue
if ($null -ne $chunk) { $devices += $chunk }
} catch {}
}
# Filter SoftwareComponent to audio-related ones (friendly name or instance id hints)
$devices | Where-Object {
if ($_.Class -ne 'SoftwareComponent') { return $true }
($_.FriendlyName -match '(?i)audio|sound|realtek|nahimic|dts|dolby|ap( o)?|maxx') -or ($_.InstanceId -match '(?i)audio|apoaudio|realtek|nahimic|dts|dolby')
}
}
function Remove-Devices {
param([array]$Devices)
if (-not $Devices -or $Devices.Count -eq 0) { return @() }
$total = $Devices.Count
$i = 0
$removed = @()
foreach ($dev in $Devices) {
$i++
$pct = [int](($i / $total) * 100)
$title = "Removing audio devices ($i of $total)"
$status = "{0} — {1}" -f $dev.Class, ($dev.FriendlyName ?? $dev.InstanceId)
Write-Progress -Activity $title -Status $status -PercentComplete $pct
if ($PSCmdlet.ShouldProcess($dev.InstanceId, "Remove device")) {
$res = Invoke-PnpUtil -Arguments @("/remove-device","`"$($dev.InstanceId)`"")
if (-not $res.Ok) {
Write-Warning ("[!] Failed to remove device: {0}`n {1}" -f $dev.InstanceId, ($res.StdErr.Trim() + " " + $res.StdOut.Trim()))
} else {
$removed += $dev
Write-Verbose ("Removed device: {0}" -f $dev.InstanceId)
}
}
}
Write-Progress -Activity "Removing audio devices" -Completed
return $removed
}
function Parse-PnpDriverBlocks {
param([string]$Text)
# pnputil /enum-drivers output comes in blank-line separated blocks
$blocks = ($Text -split "(\r?\n){2,}") | Where-Object { $_ -match 'Published Name\s*:' }
foreach ($b in $blocks) {
$obj = [pscustomobject]@{
PublishedName = (($b -split "`n" | Where-Object { $_ -match 'Published Name' } | Select-Object -First 1) -replace '.*:\s*','').Trim()
Provider = (($b -split "`n" | Where-Object { $_ -match 'Driver Package Provider' } | Select-Object -First 1) -replace '.*:\s*','').Trim()
Class = (($b -split "`n" | Where-Object { $_ -match '^Class\s*:' } | Select-Object -First 1) -replace '.*:\s*','').Trim()
ClassGuid = (($b -split "`n" | Where-Object { $_ -match 'Class GUID' } | Select-Object -First 1) -replace '.*:\s*','').Trim()
DateVersion = (($b -split "`n" | Where-Object { $_ -match 'Driver Date and Version' } | Select-Object -First 1) -replace '.*:\s*','').Trim()
Signer = (($b -split "`n" | Where-Object { $_ -match 'Signer Name' } | Select-Object -First 1) -replace '.*:\s*','').Trim()
Raw = $b
}
if ($obj.PublishedName) { $obj }
}
}
function Get-AudioDriverPackages {
param([switch]$IncludeMicrosoft)
$targets = @()
foreach ($cls in @("MEDIA","AudioEndpoint")) {
$enum = Invoke-PnpUtil -Arguments @("/enum-drivers","/class",$cls)
if ($enum.StdOut) { $targets += Parse-PnpDriverBlocks -Text $enum.StdOut }
}
# Dedup
$targets = $targets | Sort-Object PublishedName -Unique
if (-not $IncludeMicrosoft) {
$targets = $targets | Where-Object { $_.Provider -notmatch '^(?i)Microsoft' }
}
$targets
}
function Delete-DriverPackages {
param([array]$Packages)
if (-not $Packages -or $Packages.Count -eq 0) { return @() }
$total = $Packages.Count
$i = 0
$deleted = @()
foreach ($pkg in $Packages) {
$i++
$pct = [int](($i / $total) * 100)
$title = "Deleting driver packages ($i of $total)"
$status = "{0} — {1} [{2}]" -f $pkg.PublishedName, $pkg.Provider, $pkg.Class
Write-Progress -Activity $title -Status $status -PercentComplete $pct
if ($PSCmdlet.ShouldProcess($pkg.PublishedName, "pnputil /delete-driver /uninstall /force")) {
$res = Invoke-PnpUtil -Arguments @("/delete-driver",$pkg.PublishedName,"/uninstall","/force")
if (-not $res.Ok) {
# If in use, device removal may lag; attempt a non-uninstall delete as a fallback
$fallback = $false
if ($res.StdErr -match '(in use|currently in use|failed to delete)') {
$fallback = $true
$res2 = Invoke-PnpUtil -Arguments @("/delete-driver",$pkg.PublishedName,"/force")
if ($res2.Ok) { $deleted += $pkg }
else {
Write-Warning ("[!] Failed to delete package {0}: {1}" -f $pkg.PublishedName, ($res2.StdErr.Trim() + " " + $res2.StdOut.Trim()))
}
}
if (-not $fallback) {
Write-Warning ("[!] Failed to delete package {0}: {1}" -f $pkg.PublishedName, ($res.StdErr.Trim() + " " + $res.StdOut.Trim()))
}
} else {
$deleted += $pkg
Write-Verbose ("Deleted package: {0}" -f $pkg.PublishedName)
}
}
}
Write-Progress -Activity "Deleting driver packages" -Completed
return $deleted
}
try {
Assert-Elevation
Start-TranscriptSafe -Path $LogPath
if ($PSCmdlet.ShouldProcess("System","Audio driver purge starting")) {
New-RestorePointIfRequested -Enable:$CreateRestorePoint
Write-Host "`n[*] Enumerating audio devices..." -ForegroundColor Cyan
$devices = Get-AudioDevices
if ($devices.Count -gt 0) {
Write-Host ("[+] Found {0} audio-related devices to remove." -f $devices.Count)
$removed = Remove-Devices -Devices $devices
Write-Host ("[+] Removed {0} devices (requested)." -f $removed.Count)
} else {
Write-Host "[=] No removable audio devices found."
}
Write-Host "`n[*] Enumerating driver packages..." -ForegroundColor Cyan
$pkgs = Get-AudioDriverPackages -IncludeMicrosoft:$IncludeMicrosoft
if ($pkgs.Count -gt 0) {
Write-Host ("[+] Found {0} driver packages to delete{1}." -f $pkgs.Count, $(if (-not $IncludeMicrosoft) { " (third-party only)" } else { "" }))
$deleted = Delete-DriverPackages -Packages $pkgs
Write-Host ("[+] Deleted {0} packages (requested)." -f $deleted.Count)
} else {
Write-Host "[=] No matching driver packages found."
}
Write-Host "`n[*] Finalizing..." -ForegroundColor Cyan
Write-Host " • Recommend reboot now. After reboot: install OEM audio package, or run 'pnputil /scan-devices' to rediscover devices."
if ($ForceReboot -and -not $WhatIfPreference) {
Write-Host "[*] Rebooting in 5 seconds..."
Start-Sleep -Seconds 5
Restart-Computer -Force
} else {
$ans = Read-Host "Reboot now? (Y/N)"
if ($ans -match '^(?i)Y') { Restart-Computer -Force }
}
}
}
finally {
Stop-TranscriptSafe
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment