Skip to content

Instantly share code, notes, and snippets.

@nohwnd
Last active April 24, 2026 13:11
Show Gist options
  • Select an option

  • Save nohwnd/5c07fe62c861ee563f69c9ee1f7c9688 to your computer and use it in GitHub Desktop.

Select an option

Save nohwnd/5c07fe62c861ee563f69c9ee1f7c9688 to your computer and use it in GitHub Desktop.
Remove built-in version of Pester 3 (or -All) from Windows 10 Program Files and Program Files (x86).
#Requires -RunAsAdministrator
function Uninstall-Pester ([switch]$All) {
if ([IntPtr]::Size * 8 -ne 64) { throw "Run this script from 64bit PowerShell." }
#Requires -RunAsAdministrator
$pesterPaths = foreach ($programFiles in ($env:ProgramFiles, ${env:ProgramFiles(x86)})) {
$path = "$programFiles\WindowsPowerShell\Modules\Pester"
if ($null -ne $programFiles -and (Test-Path $path)) {
if ($All) {
Get-Item $path
}
else {
Get-ChildItem "$path\3.*"
}
}
}
if (-not $pesterPaths) {
"There are no Pester$(if (-not $all) {" 3"}) installations in Program Files and Program Files (x86) doing nothing."
return
}
foreach ($pesterPath in $pesterPaths) {
takeown /F $pesterPath /A /R
icacls $pesterPath /reset
# grant permissions to Administrators group, but use SID to do
# it because it is localized on non-us installations of Windows
icacls $pesterPath /grant "*S-1-5-32-544:F" /inheritance:d /T
Remove-Item -Path $pesterPath -Recurse -Force -Confirm:$false
}
}
Uninstall-Pester
@Hot12345
Copy link
Copy Markdown

@jasonrush good point. I spent some time and refactored into a true cmdlet using advanced function syntax. The module name is Utils and must be installed in a location located within $env:PSModulePath. The file structure is:

  • Utils

    • Utils.psd1

    • Utils.psm1

    • /Public

      • Uninstall-Pester.ps1
    • /Private

The Utils.psd1

@{

ModuleVersion = "1.0.0"

GUID = "e38b14a6-0d85-4be5-869e-38ed7e270126"

Author = "Your Name Here"

CompanyName = "My Company"

Copyright = "(c) 2021 My Company All rights reserved."

RootModule = "Utils.psm1"

}

Utils.ps1 file

Get-ChildItem -Filter *.ps1 -Path "$PSScriptRoot\public","$PSScriptRoot\private" -Recurse | ForEach-Object { . $_.FullName; Write-Verbose "Loaded $_.BaseName" }
Get-ChildItem -Filter *.ps1 -Path "$PSScriptRoot\public" -Recurse | ForEach-Object { Export-ModuleMember $_.BaseName; Write-Verbose "Exported $_.BaseName" }

Uninstall-Pester.ps1 file

function Uninstall-Pester {
    [CmdletBinding()]
    [OutputType([ bool ])]
    param(
        [Parameter()]
        [switch]
        $All = $false
    )
    
    begin {
        try {
            Write-Verbose -Message "[$(( Get-Date ).TimeOfDay )][$( $MyInvocation.MyCommand )]-Begin Enter"
            $result = $true
            $pesterPath = 'WindowsPowerShell\Modules\Pester'

            if ( -not $All ) {
                $pesterPath += '\3.*'
            }

            # See if there is anything to do
            $paths = @()
            foreach ( $path in @(${env:ProgramFiles}, ${env:ProgramFiles(x86)} ) ) {
                if ( $path -and ( Test-Path -Path "$path\$pesterPath" ) ) {
                    $paths += ( Get-ChildItem -Path "$path\$pesterPath" -Directory -ErrorAction SilentlyContinue ).FullName
                }
            }

            if ( -not $paths.Count ) {
                Write-Warning "There are no Pester$( if ( -not $All ) { " 3" } ) installations in Program Files and Program Files (x86)"
                $result = $false
                return
            }
        
            $isAdmin = (New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
            $is32BitProcess = $pshome -like "*syswow64*"
            $is64BitOS = ( Get-WmiObject Win32_OperatingSystem ).OSArchitecture -like "64*"
            $filePath = $null
            
            switch ( $true ) {
                { ( $is32BitProcess -and -not $is64BitOS ) -or ( -not $isAdmin -and $is64BitOS ) } {
                    Write-Warning "Restarting script under 64 bit powershell as admin"
                 
                    # relaunch this script under 64 bit shell as adminstrator
                    $filePath = "$( $pshome -replace "syswow64", "System32" )\powershell.exe"
                    break
                 }
                 { -not $isAdmin } {
                     Write-Warning "Restarting script using admin privs"
                    $filePath = 'powershell.exe'
                 }
            }
            
            if ( $filePath ) {
                Start-Process -FilePath $filePath -Verb RunAs -ArgumentList "-NoLogo -NoExit -Command `" Import-Module -name Utils; Uninstall-Pester $( if( $All ) { "-All" } )  `""
                $result = $false
                return
            }
        }
        catch {
            $result = $false
            Write-Verbose -Message  "[$(( Get-Date ).TimeOfDay )][$( $MyInvocation.MyCommand )] Exception:`r`n$_"
            throw $_
        }
        finally {
            Write-Verbose -Message "[$(( Get-Date ).TimeOfDay )][$( $MyInvocation.MyCommand )]-Begin Exit"
        }
    }

    process {
        try {
            Write-Verbose -Message "[$(( Get-Date ).TimeOfDay )][$( $MyInvocation.MyCommand )]-Process Enter"
            if ( -not $result ) {
                return
            }

            foreach ($path in $paths) {
                takeown /F $path /A /R
                icacls $path /reset
                # grant permissions to Administrators group, but use SID to do
                # it because it is localized on non-us installations of Windows
                icacls $path /grant "*S-1-5-32-544:F" /inheritance:d /T
                Remove-Item -Path $path -Recurse -Force -Confirm:$false -ErrorAction SilentlyContinue
            }
        
        }
        catch {
            $result = $false
            Write-Verbose -Message  "[$(( Get-Date ).TimeOfDay )][$( $MyInvocation.MyCommand )] Exception:`r`n$_"
            throw $_
        }
        finally {
            Write-Verbose -Message "[$(( Get-Date ).TimeOfDay )][$( $MyInvocation.MyCommand )]-Process Exit"
        }
    }
    
    end {
        try {
            Write-Verbose -Message "[$(( Get-Date ).TimeOfDay )][$( $MyInvocation.MyCommand )]-End Enter"
            return $result
        }
        catch {
            Write-Verbose -Message  "[$(( Get-Date ).TimeOfDay )][$( $MyInvocation.MyCommand )] Exception:`r`n$_"
            throw $_
        }
        finally {
            Write-Verbose -Message "[$(( Get-Date ).TimeOfDay )][$( $MyInvocation.MyCommand )]-End Exit"
        }
    }
}

Thanks your script worked out for to uninstall the module. And installing manually the newer version.

@sidey79
Copy link
Copy Markdown

sidey79 commented Jan 26, 2025

Thanks, helped me with this problem.

@t3hcr
Copy link
Copy Markdown

t3hcr commented Apr 24, 2026

It's the year 2026 and this is still needed. Thanks for figuring this out and sharing. ๐Ÿ˜Š

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment