<# .SYNOPSIS Reports the installed Google Chrome version and update policy, and can tell Google's own updater to check for an update right now. .DESCRIPTION Finds system-wide and per-user Chrome installs, reads their versions, notices a staged update that's waiting on a browser restart, and reads the Google Update policy keys so you can see if updates have been switched off. With -TriggerUpdate it wakes whichever updater is present: the newer GoogleUpdater (updater.exe --wake) or the legacy GoogleUpdate.exe (/ua /installsource scheduler). Supports -WhatIf. .PARAMETER TriggerUpdate Ask the updater to check for and install updates now, instead of waiting for its schedule. .PARAMETER CompareToLatest Look up the current Stable version for Windows from Google's VersionHistory API and flag whether this machine is behind. Needs internet access. .EXAMPLE .\Invoke-ChromeUpdateCheck.ps1 -CompareToLatest .EXAMPLE .\Invoke-ChromeUpdateCheck.ps1 -TriggerUpdate -Verbose #> [CmdletBinding(SupportsShouldProcess)] param( [switch]$TriggerUpdate, [switch]$CompareToLatest ) $programDirs = @($env:ProgramFiles, ${env:ProgramFiles(x86)}) | Where-Object { $_ } | Select-Object -Unique $chromeGuid = '{8A69D345-D564-463C-AFF1-A69D9E530F96}' # What does policy say? 0 = disabled, 1 = always allow, 2 = manual only, 3 = automatic only. $policy = Get-ItemProperty -Path 'HKLM:\SOFTWARE\Policies\Google\Update' -ErrorAction SilentlyContinue $policyValue = if ($policy -and $null -ne $policy."Update$chromeGuid") { $policy."Update$chromeGuid" } elseif ($policy -and $null -ne $policy.UpdateDefault) { $policy.UpdateDefault } $policyNames = @{ 0 = 'Updates disabled by policy' 1 = 'Always allow (policy)' 2 = 'Manual updates only (policy)' 3 = 'Automatic updates only (policy)' } $policyText = if ($null -ne $policyValue -and $policyNames.ContainsKey([int]$policyValue)) { $policyNames[[int]$policyValue] } else { 'Not configured' } $latest = $null if ($CompareToLatest) { try { $uri = 'https://versionhistory.googleapis.com/v1/chrome/platforms/win64/channels/stable/versions' $latest = [version](Invoke-RestMethod -Uri $uri -ErrorAction Stop).versions[0].version Write-Verbose "Current Stable for win64: $latest" } catch { Write-Warning "Couldn't reach the VersionHistory API: $($_.Exception.Message)" } } # System-wide installs first, then the per-user one for whoever is running this. $candidates = @($programDirs | ForEach-Object { Join-Path $_ 'Google\Chrome\Application' }) if ($env:LOCALAPPDATA) { $candidates += Join-Path $env:LOCALAPPDATA 'Google\Chrome\Application' } foreach ($appDir in $candidates) { $exe = Join-Path $appDir 'chrome.exe' if (-not (Test-Path -LiteralPath $exe)) { continue } $installed = [version](Get-Item -LiteralPath $exe).VersionInfo.ProductVersion # When Chrome is running during an update, the new build is parked as new_chrome.exe # and swapped in on the next launch. $staged = Join-Path $appDir 'new_chrome.exe' $pending = if (Test-Path -LiteralPath $staged) { [version](Get-Item -LiteralPath $staged).VersionInfo.ProductVersion } $effective = if ($pending) { $pending } else { $installed } [pscustomobject]@{ Scope = if ($env:LOCALAPPDATA -and $appDir -like "$env:LOCALAPPDATA*") { 'PerUser' } else { 'System' } InstalledVersion = $installed PendingRestart = $pending LatestStable = $latest UpToDate = if ($latest) { $effective -ge $latest } else { $null } UpdatePolicy = $policyText Path = $exe } } if (-not $TriggerUpdate) { return } if ($policyValue -eq 0 -or $policyValue -eq 2) { Write-Warning "Policy blocks automatic updates ($policyText). The updater will likely do nothing." } # Newer machines: GoogleUpdater, one folder per updater version. Use the newest. $updater = $programDirs | ForEach-Object { Get-ChildItem -Path (Join-Path $_ 'Google\GoogleUpdater\*\updater.exe') -ErrorAction SilentlyContinue } | Sort-Object { try { [version]$_.Directory.Name } catch { [version]'0.0' } } -Descending | Select-Object -First 1 if ($updater) { if ($PSCmdlet.ShouldProcess($updater.FullName, 'Wake GoogleUpdater (--wake --system)')) { Start-Process -FilePath $updater.FullName -ArgumentList '--wake', '--system' -WindowStyle Hidden Write-Verbose 'GoogleUpdater woken. It works in the background; check again in a few minutes.' } return } # Older machines: the legacy Omaha updater. $legacy = $programDirs | ForEach-Object { Join-Path $_ 'Google\Update\GoogleUpdate.exe' } | Where-Object { Test-Path -LiteralPath $_ } | Select-Object -First 1 if ($legacy) { if ($PSCmdlet.ShouldProcess($legacy, 'Run GoogleUpdate.exe /ua /installsource scheduler')) { Start-Process -FilePath $legacy -ArgumentList '/ua', '/installsource', 'scheduler' -WindowStyle Hidden Write-Verbose 'GoogleUpdate.exe started. It works in the background; check again in a few minutes.' } return } Write-Warning 'No Google updater found. If Chrome came from an MSI with updates stripped out, update it the way it was deployed.'