Posted on 11-05-2015 06:52 PM
Has anyone found a reliable way to do this? What is actually happening when "turn on automatic updates" is clicked?
Posted on 11-05-2015 10:42 PM
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
Posted on 11-06-2015 07:03 AM
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())
Posted on 12-21-2015 08:53 AM
please see this thread here
Posted on 03-03-2017 02:36 PM
@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
Posted on 03-06-2017 11:01 AM
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.
Posted on 04-12-2018 10:10 AM
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
Posted on 03-12-2019 12:40 PM
@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