Force Enable Google Chrome Auto Updates

kitzy
Contributor III

Has anyone found a reliable way to do this? What is actually happening when "turn on automatic updates" is clicked?

7 REPLIES 7

timdambrosio
New Contributor III

This script installs the latest version straight from Google. It's the same as the update Flash from the internet script, you can tell by some of the comments that haven't been updated. Add config profile or similar to set it the app itself to never update and let the script do the work.

Put it in self service or run it once per week or whatever.

@brunerd's work, not mine.

#!/bin/sh

[ -f /tmp/debug ] && set -x
# This script downloads the latest Google Chrome

URL='https://dl.google.com/chrome/mac/stable/GGRO/googlechrome.dmg'
#just the file name
dmgfile="${URL##*/}"
dmgFilePath="/tmp/${dmgfile}"
# Use mktemp to specify a mountpoint for the disk image (XXXX generates numbers)
TMPMOUNT=$(/usr/bin/mktemp -d /tmp/chrome.XXXX)

/bin/echo "Downloading Chrome"
/usr/bin/curl -s -o "$dmgFilePath" ${URL}
echo "Mounting $dmgFilePath to $TMPMOUNT"
# Mount the latest Flash Player disk image to /tmp/flashplayer.XXXX mountpoint
hdiutil attach "$dmgFilePath" -mountpoint "$TMPMOUNT" -nobrowse -noverify -noautoopen -quiet

#verify it is in the dmg first
if [ -e "$TMPMOUNT/Google Chrome.app" ]; then
/bin/echo "Removing old copy..."
#Finder did not pick up on the new version this should force launchservices to notice
rm -rf "/Applications/Google Chrome.app"
/bin/echo "Installing..."
ditto -rsrc "$TMPMOUNT/Google Chrome.app" "/Applications/Google Chrome.app"
fi

# Clean-up
echo "Cleaning Up..."
# Unmount the Flash Player disk image from /tmp/flashplayer.XXXX
/usr/bin/hdiutil detach "$TMPMOUNT" -quiet

# Remove the /tmp/flashplayer.XXXX mountpoint
/bin/rm -rf "$TMPMOUNT"

# Remove the downloaded disk image
/bin/rm -rf "$dmgFilePath"

exit 0

ACousino
New Contributor II

This is a python script that enables automatic update for all users. It seems to work well for us.

#!/usr/bin/env python
# encoding: utf-8
"""
chrome-enable-autoupdates.py

This script enables system wide automatic updates for Google Chrome.
It should work for Chrome versions 18 and later. No configuration needed
as this is originally intended as a munki postinstall script.

Created by Hannes Juutilainen, hjuutilainen@mac.com

History:

2015-09-25, Niklas Blomdalen - Modifications to include old KeystoneRegistration installation (python version) 2014-11-20, Hannes Juutilainen - Modifications for Chrome 39 2012-08-31, Hannes Juutilainen - Added --force flag to keystoneInstall as suggested by Riley Shott 2012-05-29, Hannes Juutilainen - Added more error checking 2012-05-25, Hannes Juutilainen - Added some error checking in main 2012-05-24, Hannes Juutilainen - First version """ import sys import os import getopt import subprocess import plistlib chromePath = "/Applications/Google Chrome.app" infoPlistPath = os.path.realpath(os.path.join(chromePath, 'Contents/Info.plist')) brandPath = "/Library/Google/Google Chrome Brand.plist" brandKey = "KSBrandID" tagPath = infoPlistPath tagKey = "KSChannelID" versionPath = infoPlistPath versionKey = "KSVersion" class Usage(Exception): def __init__(self, msg): self.msg = msg def chromeIsInstalled(): """Check if Chrome is installed""" if os.path.exists(chromePath): return True else: return False def chromeVersion(): """Returns Chrome version""" infoPlist = plistlib.readPlist(infoPlistPath) bundleShortVersion = infoPlist["CFBundleShortVersionString"] return bundleShortVersion def chromeKSUpdateURL(): """Returns KSUpdateURL from Chrome Info.plist""" infoPlist = plistlib.readPlist(infoPlistPath) KSUpdateURL = infoPlist["KSUpdateURL"] return KSUpdateURL def chromeKSProductID(): """Returns KSProductID from Chrome Info.plist""" infoPlist = plistlib.readPlist(infoPlistPath) KSProductID = infoPlist["KSProductID"] return KSProductID def keystoneRegistrationFrameworkPath(): """Returns KeystoneRegistration.framework path""" keystoneRegistration = os.path.join(chromePath, 'Contents/Versions') keystoneRegistration = os.path.join(keystoneRegistration, chromeVersion()) keystoneRegistration = os.path.join(keystoneRegistration, 'Google Chrome Framework.framework') keystoneRegistration = os.path.join(keystoneRegistration, 'Frameworks/KeystoneRegistration.framework') return keystoneRegistration def keystoneInstall(): """Install the current Keystone""" installScript = os.path.join(keystoneRegistrationFrameworkPath(), 'Resources/ksinstall') if not os.path.exists(installScript): installScript = os.path.join(keystoneRegistrationFrameworkPath(), 'Resources/install.py') keystonePayload = os.path.join(keystoneRegistrationFrameworkPath(), 'Resources/Keystone.tbz') if os.path.exists(installScript) and os.path.exists(keystonePayload): retcode = subprocess.call([installScript, '--install', keystonePayload, '--force']) if retcode == 0: return True else: return False else: print >> sys.stderr, "Error: KeystoneRegistration.framework not found" return False def removeChromeFromKeystone(): """Removes Chrome from Keystone""" ksadmin = "/Library/Google/GoogleSoftwareUpdate/GoogleSoftwareUpdate.bundle/Contents/MacOS/ksadmin" ksadminProcess = [ ksadmin, '--delete', '--productid', chromeKSProductID()] retcode = subprocess.call(ksadminProcess) if retcode == 0: return True else: return False def registerChromeWithKeystone(): """Registers Chrome with Keystone""" ksadmin = "/Library/Google/GoogleSoftwareUpdate/GoogleSoftwareUpdate.bundle/Contents/MacOS/ksadmin" if os.path.exists(ksadmin): ksadminProcess = [ksadmin, '--register', '--preserve-tttoken', '--productid', chromeKSProductID(), '--version', chromeVersion(), '--xcpath', chromePath, '--url', chromeKSUpdateURL(), '--tag-path', tagPath, '--tag-key', tagKey, '--brand-path', brandPath, '--brand-key', brandKey, '--version-path', versionPath, '--version-key', versionKey] retcode = subprocess.call(ksadminProcess) if retcode == 0: return True else: return False else: print >> sys.stderr, "Error: %s doesn't exist" % ksadmin return False def main(argv=None): if argv is None: argv = sys.argv try: # Check for root if os.geteuid() != 0: print >> sys.stderr, "This script must be run as root" return 1 if not chromeIsInstalled(): print >> sys.stderr, "Error: Chrome is not installed on this computer" return 1 if keystoneInstall(): print "Keystone installed" else: print >> sys.stderr, "Error: Keystone install failed" return 1 if registerChromeWithKeystone(): print "Registered Chrome with Keystone" return 0 else: print >> sys.stderr, "Error: Failed to register Chrome with Keystone" return 1 except Usage, err: print >>sys.stderr, err.msg print >>sys.stderr, "for help use --help" return 2 if __name__ == "__main__": sys.exit(main())

luke_jaeger
New Contributor III

please see this thread here

chuck3000
Contributor

@brunerd is there a way to set the default home page in your script above? I've mussed with the post at https://www.jamf.com/jamf-nation/discussions/9868/chrome-homepage but no success. Thanks

rscherrer
New Contributor

Does anyone have solution for this?

We are using Composer to take a snapshot of Google Chrome and it's working well for creating Chrome .dmg's. However, after the .dmg deployed to a mac, it displays "Google Chrome may not be able to keep itself updated." on first run. We would like for automatic updates to be enabled for Chrome without the end-user having to respond.

Any help is greatly appreciated.

UPDATE:
Got it working with Composer. Didn't realize that chrome autoupdate is configured from the About Google Chrome menu. Composer correctly captures it.

kierank
New Contributor

Unable to get this method to work so far...

Found here: https://support.google.com/chrome/a/answer/7591084

Basically the instructions tell you to apply this plist file either via script of Configuration Profile and the Chrome (and all other Google apps, for app-specific preferences read article) preference is set globally.

Name: com.google.Keystone.plist
Preference Domain: com.google.Keystone

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<key>updatePolicies</key>
<dict>
    <key>global</key>
    <dict>
        <key>UpdateDefault</key>
        <integer>0</integer>
        <key>DownloadPreference</key>
        <string>cacheable</string>
    </dict>
</dict>
</plist>

Create plist file:

cat <<EOF >> /Library/Preferences/com.google.Keystone.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<key>updatePolicies</key>
<dict>
    <key>global</key>
    <dict>
        <key>UpdateDefault</key>
        <integer>0</integer>
        <key>DownloadPreference</key>
        <string>cacheable</string>
    </dict>
</dict>
</plist>
EOF

brunerd
Contributor

@kierank Close ... it all needs to be in a dictionary to be a valid plist...

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>updatePolicies</key>
    <dict>
      <key>global</key>
      <dict>
        <key>UpdateDefault</key>
        <integer>0</integer>
      </dict>
    </dict>
</dict>
</plist>

Still, I don't see it doing much good, and maybe you didn't find it to be effective either

I took that Python script and converted it to BASH and streamlined a lot of stuff (key name variables, etc...) and added a couple scripts that get run when you manually click the "Enable Auto Updates" button (basically it owns Chrome as root)

#!/bin/sh
#Joel Bruner

#specify a custom path in parameter $4 or just take the default
appPath="${4:-/Applications/Google Chrome.app}"

#no exist? exit.
[ ! -e "${appPath}/Contents/Info.plist" ] && exit

appVersion=$(defaults read "${appPath}/Contents/Info.plist" CFBundleShortVersionString)
updateURL=$(defaults read "${appPath}/Contents/Info.plist" KSUpdateURL)
productID=$(defaults read "${appPath}/Contents/Info.plist" KSProductID)

"${appPath}/Contents/Versions/${appVersion}/Google Chrome Framework.framework/Versions/A/Resources/keystone_promote_preflight.sh" 2>/dev/null 1>&2
"${appPath}/Contents/Versions/${appVersion}/Google Chrome Framework.framework/Frameworks/KeystoneRegistration.framework/Resources/ksinstall" --install "${appPath}/Contents/Versions/${appVersion}/Google Chrome Framework.framework/Frameworks/KeystoneRegistration.framework/Resources/Keystone.tbz" --force
/Library/Google/GoogleSoftwareUpdate/GoogleSoftwareUpdate.bundle/Contents/MacOS/ksadmin --register --productid "${productID}" --version "${appVersion}" --xcpath "${appPath}" --url "${updateURL}" --tag-path "${appPath}/Contents/Info.plist" --tag-key "KSChannelID" --brand-path "/Library/Google/Google Chrome Brand.plist" --brand-key "KSBrandID" --version-path "${appPath}/Contents/Info.plist" --version-key "KSVersion"
"${appPath}/Contents/Versions/${appVersion}/Google Chrome Framework.framework/Versions/A/Resources/keystone_promote_postflight.sh" "${appPath}" 2>/dev/null 1>&2

This does the trick, although I've noticed that until Chrome is quit and relaunched (why do they make you long press Command-Q?!) if you check in About... it will not report correctly, it must be quit and relaunched.

Also ksadmin is a miserably documented tool...
To list updates:

/Library/Google/GoogleSoftwareUpdate/GoogleSoftwareUpdate.bundle/Contents/MacOS/ksadmin -l

To print tickets:

/Library/Google/GoogleSoftwareUpdate/GoogleSoftwareUpdate.bundle/Contents/MacOS/ksadmin

The ticket thing is mysterious, I've seen them and they are pretty opaque what they are for, it doesn't really identify the software it is updating, and now I have no tickets... so my point is that ksadmin is not the EA data source you are looking for if you are trying to manage AutoUpdates for Chrome or report on it's status