Skip to content

Instantly share code, notes, and snippets.

@minanagehsalalma
Last active August 6, 2025 02:00
Show Gist options
  • Save minanagehsalalma/01a76c768db6b3c7246e8b7bc26c3e2f to your computer and use it in GitHub Desktop.
Save minanagehsalalma/01a76c768db6b3c7246e8b7bc26c3e2f to your computer and use it in GitHub Desktop.
Enable MANIFEST V2 by pinning chrome version 138
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[string]$PortableDir, # e.g. C:\Users\ASUS\Downloads\Compressed\GoogleChromePortable
[string]$ProfileDir = "C:\Chrome138Profile",
[switch]$DisableUpdater # Optional: stop & disable Google Update services/tasks
)
$ErrorActionPreference = 'Stop'
function Require-Admin {
if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
throw "Run this in an elevated PowerShell window (Run as Administrator)."
}
}
function Stop-Chrome {
Get-Process chrome -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue
Start-Sleep -Seconds 2
}
function Try-UninstallBySetup($basePath) {
$setup = Join-Path $basePath 'Installer\setup.exe'
if (Test-Path $setup) {
try { & $setup --uninstall --multi-install --chrome --force-uninstall | Out-Null } catch {}
}
}
function Uninstall-SystemChrome {
Write-Host "Uninstalling Chrome Stable/Dev/Canary (machine + user) ..."
# Common install roots (machine)
@(
"$env:ProgramFiles\Google\Chrome\Application",
"$env:ProgramFiles(x86)\Google\Chrome\Application",
"$env:ProgramFiles\Google\Chrome Dev\Application",
"$env:ProgramFiles(x86)\Google\Chrome Dev\Application"
) | ForEach-Object {
if (Test-Path $_) { Try-UninstallBySetup (Split-Path -Parent $_) }
}
# User-level installs (Stable/Canary/SxS)
@(
"$env:LocalAppData\Google\Chrome\Application",
"$env:LocalAppData\Google\Chrome SxS\Application"
) | ForEach-Object {
if (Test-Path $_) { Try-UninstallBySetup (Split-Path -Parent $_) }
}
# Fallback: Use Uninstall registry to catch odd cases
$regPaths = @(
'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*',
'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*',
'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*'
)
foreach ($rp in $regPaths) {
Get-ItemProperty $rp -ErrorAction SilentlyContinue |
Where-Object { $_.DisplayName -match 'Google Chrome' -or $_.DisplayName -match 'Chrome Canary' -or $_.DisplayName -match 'Chrome Dev' } |
ForEach-Object {
$cmd = $_.UninstallString
if ($cmd) {
try {
if ($cmd -match 'msiexec') {
# Normalize MSI quiet flags
& cmd.exe /c "$cmd /qn /norestart" | Out-Null
} else {
& cmd.exe /c "$cmd --force-uninstall" | Out-Null
}
} catch {}
}
}
}
# Remove leftovers
@(
"$env:LocalAppData\Google\Chrome SxS",
"$env:LocalAppData\Google\Chrome\Application",
"$env:LocalAppData\Google\Update",
"$env:ProgramData\Google\Update"
) | ForEach-Object {
try { if (Test-Path $_) { Remove-Item $_ -Recurse -Force -ErrorAction SilentlyContinue } } catch {}
}
}
function Toggle-GoogleUpdater([bool]$disable) {
$svc = @('gupdate','gupdatem')
foreach ($s in $svc) {
try {
if ($disable) {
Stop-Service $s -ErrorAction SilentlyContinue
Set-Service $s -StartupType Disabled -ErrorAction SilentlyContinue
} else {
Set-Service $s -StartupType Manual -ErrorAction SilentlyContinue
Start-Service $s -ErrorAction SilentlyContinue
}
} catch {}
}
try {
$tasks = Get-ScheduledTask -TaskPath "\Google\" -ErrorAction SilentlyContinue
if ($tasks) {
if ($disable) { $tasks | Disable-ScheduledTask -ErrorAction SilentlyContinue | Out-Null }
else { $tasks | Enable-ScheduledTask -ErrorAction SilentlyContinue | Out-Null }
}
} catch {}
}
function Enable-MV2Policy {
$chromePol = 'HKLM:\SOFTWARE\Policies\Google\Chrome'
New-Item $chromePol -Force | Out-Null
New-ItemProperty $chromePol -Name 'ExtensionManifestV2Availability' -PropertyType DWord -Value 2 -Force | Out-Null
Write-Host "MV2 policy set (HKLM): ExtensionManifestV2Availability=2"
}
function Find-PortableChromeExe([string]$root) {
$exe = Get-ChildItem -Path $root -Filter chrome.exe -Recurse -ErrorAction SilentlyContinue |
Sort-Object FullName | Select-Object -First 1
if (-not $exe) { throw "chrome.exe not found under $root" }
return $exe.FullName
}
function Ensure-ProfileDir([string]$dir) {
if (-not (Test-Path $dir)) { New-Item -ItemType Directory -Path $dir -Force | Out-Null }
}
function New-DesktopShortcut([string]$exe, [string]$profile) {
$ws = New-Object -ComObject WScript.Shell
$desktop = [Environment]::GetFolderPath('Desktop')
$lnk = Join-Path $desktop 'Chrome 138 (Portable MV2).lnk'
$sc = $ws.CreateShortcut($lnk)
$sc.TargetPath = $exe
$sc.Arguments = "--user-data-dir=`"$profile`" --no-first-run"
$sc.WorkingDirectory = (Split-Path $exe -Parent)
$sc.IconLocation = $exe
$sc.Description = "Portable Chrome 138 with MV2 enabled"
$sc.Save()
Write-Host "Shortcut created: $lnk"
}
function New-LauncherBat([string]$exe, [string]$profile) {
$bat = Join-Path $env:Public 'Desktop\chrome-138-portable-mv2.bat'
$content = "@echo off`r`n""$exe"" --user-data-dir=""$profile"" --no-first-run`r`n"
Set-Content -Path $bat -Value $content -Encoding ASCII
Write-Host "Launcher created: $bat"
}
# ---------------- MAIN ----------------
Require-Admin
Write-Host "Stopping Chrome..."
Stop-Chrome
if ($DisableUpdater) {
Write-Host "Disabling Google Update services & tasks..."
Toggle-GoogleUpdater $true
}
Uninstall-SystemChrome
Write-Host "Enabling MV2 policy..."
Enable-MV2Policy
Write-Host "Configuring portable Chrome 138..."
$chromeExe = Find-PortableChromeExe -root $PortableDir
$ver = (Get-Item $chromeExe).VersionInfo.FileVersion
$major = ($ver -split '\.')[0]
Write-Host "Portable Chrome found: $chromeExe"
Write-Host "Version detected: $ver"
if ([int]$major -ne 138) {
Write-Warning "Portable Chrome is major $major, not 138. MV2 will NOT work on 139+. Replace the portable build with a 138 build."
}
Ensure-ProfileDir -dir $ProfileDir
New-DesktopShortcut -exe $chromeExe -profile $ProfileDir
New-LauncherBat -exe $chromeExe -profile $ProfileDir
if (-not $DisableUpdater) {
Write-Host "Re-enabling Google Update services & tasks (you chose not to disable)."
Toggle-GoogleUpdater $false
}
Write-Host "`nAll set."
Write-Host "Launch from the desktop shortcut **Chrome 138 (Portable MV2)** or the public desktop .bat."
Write-Host "Then in that window:"
Write-Host " - chrome://version → confirm major = 138"
Write-Host " - chrome://policy → Reload → ExtensionManifestV2Availability = 2 (Status: OK)"
Write-Host " - chrome://extensions → Developer mode → Load unpacked your MV2"
@minanagehsalalma
Copy link
Author

minanagehsalalma commented Aug 6, 2025

after the install run command
powershell -ExecutionPolicy Bypass -File .\swap-to-portable-chrome138.ps1 -PortableDir "C:\PATHHHHHH\GoogleChromePortable" -ProfileDir "C:\Chrome138Profile" -DisableUpdater
you can get the installer from here
https://portableapps.com/apps/internet/google_chrome_portable

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