Hey everyone...
I wanted to make Google Chrome a part of my automated deployment process and also be available within Self-Service.
Why? Users can just download from Google on their own. True dat. But hey, one-stop shopping in Self-Service, right?
As we all know, Google deploys Chrome via a DMG file. The Google Chrome.app file must be dragged to the /Applications folder.
You could effectively re-package everything using Composer, but then things get REAL STALE, REAL FAST - Google updates Chrome frequently. So stale software is bad software.
So how can we deploy Google Chrome with the freshest of the fresh so we don't get that not-so-fresh feeling?
Well, lemme share my story, morning glory.
.
THE SCRIPT
I wrote a simple script that does the heavy lifting...
I tossed this script into System Settings > Computer Management > Scripts within the JSS...
NOTE: Yes, I am aware that I don't have any error checking taking place. This is v1.1 - quick and dirty. Feel free to add some if you feel the need.
#!/bin/sh
####################################################################################################
#
# Google Chrome Installation Script
#
####################################################################################################
#
# DESCRIPTION
#
# Automatically download and install Google Chrome
#
####################################################################################################
#
# HISTORY
#
# Created by Caine Hörr on 2016-07-25
#
# v1.1 - 2016-10-11 - Caine Hörr
# Added -nobrowse flag to hdiutil attach /tmp/$VendorDMG command line arguments
# Shout out to Chad Brewer (cbrewer) on JAMFNation for this fix/update
# https://jamfnation.jamfsoftware.com/viewProfile.html?userID=1685
#
# v1.0 - 2016-07-25 - Caine Hörr
# Google Chrome Installation script
# Vendor supplied DMG file
VendorDMG="googlechrome.dmg"
# Download vendor supplied DMG file into /tmp/
curl https://dl.google.com/chrome/mac/stable/GGRO/$VendorDMG -o /tmp/$VendorDMG
# Mount vendor supplied DMG File
hdiutil attach /tmp/$VendorDMG -nobrowse
# Copy contents of vendor supplied DMG file to /Applications/
# Preserve all file attributes and ACLs
cp -pPR /Volumes/Google Chrome/Google Chrome.app /Applications/
# Identify the correct mount point for the vendor supplied DMG file
GoogleChromeDMG="$(hdiutil info | grep "/Volumes/Google Chrome" | awk '{ print $1 }')"
# Unmount the vendor supplied DMG file
hdiutil detach $GoogleChromeDMG
# Remove the downloaded vendor supplied DMG file
rm -f /tmp/$VendorDMG
.
SMART COMPUTER GROUP
We need a Smart Computer Group so Policy #1 has something to work from...
Computer Group
Display Name = "Google Chrome - Not Installed"
Criteria
Application Title] eis not] tGoogle Chrome.app]
.
POLICY #1
This policy makes things happen auto-magically based on the aforementioned Smart Computer Group.
Policy: Options
General
Display Name: "Download & Install Google Chrome"
Enabled = Checked
Triggers = Login, Recurring Check-In, Make Available Offline
Execution Frequency = Ongoing
Make Available Offline = Checked
Scripts
Points to the script in System Settings > Computer Management > Scripts
Priority: After
Maintenance
Update Inventory = Checked
Policy: Scope
Target Computers = Specific Computers
Target Users = Specific Users
Target/Type = "Google Chrome - Not Installed" Smart Computer Group
.
POLICY #2
I wanted a second policy for the sole purpose of Self-Service. I did not want the user's ability to download/install Google Chrome to be hindered within Self-Service by them being out of scope.
Why?
Perhaps the user's version of Chrome isn't updating properly... they can go to Self-Service and download/install at their leisure. There may be other reasons.
Policy: Options
General
Display Name: "Google Chrome (Latest Version)"
Enabled = Checked
Execution Frequency = Ongoing
Make Available Offline = Checked
Scripts
Points to the script in System Settings > Computer Management > Scripts
Priority: After
Maintenance
Update Inventory = Checked
Policy: Scope
Target Computers = All Computers
Target Users = All Users
Policy: Self Service
Make the policy available in Self Service = Checked
Description: Download and install the latest version of Google Chrome
Icon: I ripped the Google Chrome 128x128 icon from the icon file found within the Google Chrome.app
Feature the policy on the main page = Checked
Anyway - That's about it. It's a simple workflow.
- Chrome will auto-install on machines without Chrome
- Chrome can be manually installed via Self-Service
- Chrome will always be fresh when installed
.
Feel free to salt-to-taste - even better if you share your changes.
Cheers!
I use something @daz_wallace made that uses an EA to scope the update policy, but I can't find the blog.
#!/bin/bash
readData() {
installedApplicationVersion=$(defaults read /Applications/Google Chrome.app/Contents/Info.plist CFBundleShortVersionString)
latestVersion=$(curl -s https://omahaproxy.appspot.com/history | awk -F',' '/mac,stable/{print $3; exit}')
}
readData
if [ "$installedApplicationVersion" != "$latestVersion" ]; then
state="Old"
else
state="Latest"
fi
echo "<result>$state</result>"
Any using this python script to enable auto updates for Chrome? Once you run it, no need to keep packaging/updating Chrome, since it does it automatically on the client:
https://github.com/hjuutilainen/adminscripts/blob/master/chrome-enable-autoupdates.py
#!/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:
2017-09-01, Hannes Juutilainen
- Ignore errors when installing keystone
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 keystone install 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 subprocess
import plistlib
chrome_path = "/Applications/Google Chrome.app"
info_plist_path = os.path.realpath(os.path.join(chrome_path, 'Contents/Info.plist'))
brand_path = "/Library/Google/Google Chrome Brand.plist"
brand_key = "KSBrandID"
tag_path = info_plist_path
tag_key = "KSChannelID"
version_path = info_plist_path
version_key = "KSVersion"
class Usage(Exception):
def __init__(self, msg):
self.msg = msg
def chrome_installed():
"""Check if Chrome is installed"""
if os.path.exists(chrome_path):
return True
else:
return False
def chrome_version():
"""Returns Chrome version"""
info_plist = plistlib.readPlist(info_plist_path)
bundle_short_version = info_plist["CFBundleShortVersionString"]
return bundle_short_version
def chrome_update_url():
"""Returns KSUpdateURL from Chrome Info.plist"""
info_plist = plistlib.readPlist(info_plist_path)
update_url = info_plist["KSUpdateURL"]
return update_url
def chrome_product_id():
"""Returns KSProductID from Chrome Info.plist"""
info_plist = plistlib.readPlist(info_plist_path)
product_id = info_plist["KSProductID"]
return product_id
def keystone_registration_framework_path():
"""Returns KeystoneRegistration.framework path"""
keystone_registration = os.path.join(chrome_path, 'Contents/Versions')
keystone_registration = os.path.join(keystone_registration, chrome_version())
keystone_registration = os.path.join(keystone_registration, 'Google Chrome Framework.framework')
keystone_registration = os.path.join(keystone_registration, 'Frameworks/KeystoneRegistration.framework')
return keystone_registration
def keystone_install():
"""Install the current Keystone"""
install_script = os.path.join(keystone_registration_framework_path(), 'Resources/ksinstall')
if not os.path.exists(install_script):
install_script = os.path.join(keystone_registration_framework_path(), 'Resources/install.py')
keystone_payload = os.path.join(keystone_registration_framework_path(), 'Resources/Keystone.tbz')
if os.path.exists(install_script) and os.path.exists(keystone_payload):
ksinstall_process = [
install_script,
'--install', keystone_payload,
'--force'
]
p = subprocess.Popen(ksinstall_process, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(results, error) = p.communicate()
if results:
print results
if p.returncode != 0:
if error:
print >> sys.stderr, "%s" % error
print >> sys.stderr, "Keystone install exited with code %i" % p.returncode
# Since we used --force argument, succeed no matter what the exit code was.
return True
else:
print >> sys.stderr, "Error: KeystoneRegistration.framework not found"
return False
def register_chrome_with_keystone():
"""Registers Chrome with Keystone"""
ksadmin = "/Library/Google/GoogleSoftwareUpdate/GoogleSoftwareUpdate.bundle/Contents/MacOS/ksadmin"
if os.path.exists(ksadmin):
ksadmin_process = [
ksadmin,
'--register',
'--productid', chrome_product_id(),
'--version', chrome_version(),
'--xcpath', chrome_path,
'--url', chrome_update_url(),
'--tag-path', tag_path,
'--tag-key', tag_key,
'--brand-path', brand_path,
'--brand-key', brand_key,
'--version-path', version_path,
'--version-key', version_key
]
p = subprocess.Popen(ksadmin_process, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(results, error) = p.communicate()
if error:
print >> sys.stderr, "%s" % error
if results:
print results
if p.returncode == 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 chrome_installed():
print >> sys.stderr, "Error: Chrome is not installed on this computer"
return 1
if keystone_install():
print "Keystone installed"
else:
print >> sys.stderr, "Error: Keystone install failed"
return 1
if register_chrome_with_keystone():
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())
i want to do something similar for Google Chat. The one I modified from a chrome download pulled down the chat dmg from google but when I launch the app, it says to check with the developer to make sure it matches the OS. Delete that version then manually download from Google, then drag into the applications folder, then launch and it is happy. After it is happy, I deleted the app and ran my script again. This time it says it cannot verify the developer.
#!/bin/sh
# original script from https://lew.im/2017/03/auto-update-chrome/
# below are my modifications and edits
#this will download the app and move into applications, but when launching the app, gets an error
dmgfile="InstallHangoutsChat.dmg"
volname="Install Hangouts Chat"
logfile="/Library/Logs/GoogleChatInstallScript.log"
# url='https://dl.google.com/chrome/mac/stable/GGRO/googlechrome.dmg'
url='https://dl.google.com/chat/latest/InstallHangoutsChat.dmg'
/bin/echo "--" >> ${logfile}
/bin/echo "`date`: Downloading latest version." >> ${logfile}
/usr/bin/curl -s -o /tmp/${dmgfile} ${url}
/bin/echo "`date`: Mounting installer disk image." >> ${logfile}
/usr/bin/hdiutil attach /tmp/${dmgfile} -nobrowse -quiet
/bin/echo "`date`: Installing..." >> ${logfile}
ditto -rsrc "/Volumes/${volname}/Chat.app" "/Applications/Chat.app"
/bin/sleep 10
/bin/echo "`date`: Unmounting installer disk image." >> ${logfile}
/usr/bin/hdiutil detach $(/bin/df | /usr/bin/grep "${volname}" | awk '{print $1}') -quiet
/bin/sleep 10
/bin/echo "`date`: Deleting disk image." >> ${logfile}
/bin/rm /tmp/"${dmgfile}"
exit 0
@gldc on your set up what Package and other scripts are you running?
Thanks, @ellavader . I used your script (with the DownloadURL="$4"
modification).
When I first tried it, I got an error about curl not supporting https. I tried it with the Chrome URL you included as parameter 4. Tried it both with and without quotes (in the parameter field), and neither worked.
What did work was using single quotes, like so: 'https://dl.google.com/chrome/mac/stable/googlechrome.dmg'
I mention this in case anyone else runs into that problem.
As a small addition, I had to modify a line in ellie's script for OS X 10.14 (not sure if it affects other versions as well):
cp -pPR /tmp/jamf/mount/*.app /Applications || :
needs to be
cp -a /tmp/jamf/mount/*.app /Applications || :
Otherwise it messes up the permissions of existing Chrome installations.
Thanks for the awesome contribution!
@swhps I was running into similar issues as you, but finally determined the issue was how the Chat app was being copied to the Applications folder. I wasn't having any luck with ditto, but was finally able to open Chat when using cp. Additionally, I modified the curl request to handle the URL redirect.
#!/bin/sh
# original script from https://lew.im/2017/03/auto-update-chrome/
# below are my modifications and edits
#this will download the app and move into applications, but when launching the app, gets an error
dmgfile="InstallHangoutsChat.dmg"
volname="Install Hangouts Chat"
logfile="/Library/Logs/GoogleChatInstallScript.log"
# url='https://dl.google.com/chrome/mac/stable/GGRO/googlechrome.dmg'
url='https://dl.google.com/chat/latest/InstallHangoutsChat.dmg'
/bin/echo "--" >> ${logfile}
/bin/echo "`date`: Downloading latest version." >> ${logfile}
/usr/bin/curl -L ${url} > /tmp/${dmgfile}
/bin/echo "`date`: Mounting installer disk image." >> ${logfile}
/usr/bin/hdiutil attach /tmp/${dmgfile} -nobrowse -quiet
/bin/echo "`date`: Installing..." >> ${logfile}
cp -r "/Volumes/${volname}/Chat.app" "/Applications"
/bin/sleep 10
/bin/echo "`date`: Unmounting installer disk image." >> ${logfile}
/usr/bin/hdiutil detach $(/bin/df | /usr/bin/grep "${volname}" | awk '{print $1}') -quiet
/bin/sleep 10
/bin/echo "`date`: Deleting disk image." >> ${logfile}
/bin/rm /tmp/"${dmgfile}"
exit 0
Thanks for sharing this script! Has anyone run into an issue where Chrome can't update when deployed this way? What is a proposed workaround? I assume it might have something to do with user permissions / rights from when it was installed. Thanks!
@cornwella : Do the permissions problem you fixed prevent Chrome from updating itself?
If so, then @pds.jamfadmin needs to make the change you recommended.
you can deploy Chrome once and use a configuration profile to force auto updates. Also, curl
scripts as root on all your endpoints is a security risk, FYI
@tlarkin As long as curl is using https, then a MITM attack becomes less of a concern. However, if Google were hacked and a bad copy of Chrome were sneaked in, then there would be a problem - a BIGGER problem. Anyway, just a point to consider. YMMV... ¯_(ツ)_/¯
@caine.horr only if the cert is pinned, which isn't every CDN or host is. That is the only the time MITM is not gonna work. Your results will vary.
first off, there is some great info in this post, thanks to everyone who has contributed. has anyone tried using this script to deploy chrome update on a machine where an older version of chrome is currently open/running? when i try to do so, the update goes through successfully, but then when i exit out of the running instance of chrome, i can't seem to reopen chrome at all until a reboot. if i try to run the app before rebooting, the chrome icon just bounces up and down on the dock but nothing launches. once i reboot, i am able to open chrome and it gives message that it closed unexpectedly last time, but it is on the new version and works fine. wondering if this is possible at all to deploy while chrome is running or if we will need to ensure users close out of the app first before deploying.
@dancunn typically you can install apps over running apps and it will mostly work, but there is always a risk of failure. So, to be safe, you should attempt to quit the app first. I have a script that will quit and run a policy to patch an app using NSRunningApplication
and bundle IDs, found here.
Hopefully this helps
Today I'm revisiting @ellavader 's excellent script. I'm still a huge fan, but…
Today I was troubleshooting some Jamf stuff, and so I was manually invoking a policy that included this script. (using a custom trigger)
And in so doing, was able to "watch" it execute, and noticed this:
installer: Error - the package path specified was invalid: '/tmp/jamf/mount/*.pkg'.
Okay, so the attempt to install as a pkg failed because it wasn't a pkg. Should we care?
We could edit the script to check if the DownloadURL ends with .pkg, or .dmg, or .zip, and only attempt to execute the relevant code.
I'm not a pedant, and I don't want to waste time hiding error messages that no one will see, if it doesn't affect the results.
So, does it matter? Or is worrying about it a waste of time?
Hello all. I have been happily using the script by @cainehorr It has been working great. However it seems the link for the download no longer works. Does anyone have an updated link to continue using this scipt?
You're using https://dl.google.com/chrome/mac/stable/GGRO/googlechrome.dmg? It works fine for me. What happens if you access it in a browser? Are you getting a valid cert?
I had an older link which no longer exists. I updated the link and all is well... Thank you! @cbrewer
We have this script that goes out once a month and checks the current version to the latest google version. It just went out and started the update to version 78.xxxx. Worked great for us.
!/bin/sh
dmgfile="googlechrome.dmg"
volname="Google Chrome"
logfile="/Library/Logs/GoogleChromeInstallScript.log"
url='https://dl.google.com/chrome/mac/stable/GGRO/googlechrome.dmg'
/bin/echo "--" >> ${logfile}
/bin/echo "date
: Downloading latest version." >> ${logfile}
/usr/bin/curl -s -o /tmp/${dmgfile} ${url}
/bin/echo "date
: Mounting installer disk image." >> ${logfile}
/usr/bin/hdiutil attach /tmp/${dmgfile} -nobrowse -quiet
/bin/echo "date
: Installing..." >> ${logfile}
ditto -rsrc "/Volumes/${volname}/Google Chrome.app" "/Applications/Google Chrome.app"
/bin/sleep 10
/bin/echo "date
: Unmounting installer disk image." >> ${logfile}
/usr/bin/hdiutil detach $(/bin/df | /usr/bin/grep "${volname}" | awk '{print $1}') -quiet
/bin/sleep 10
/bin/echo "date
: Deleting disk image." >> ${logfile}
/bin/rm /tmp/"${dmgfile}"
exit 0
Hello. My organization is using this script too (v1.1 at the top of the thread.) But I'm having to reevaluate this because it installs on our MacBooks with auto updates broken. Is anyone having this issue too?
My preference is finding a solution where Chrome is installed with auto updates turned on. Can anyone assist with this?
We are using a single policy with the following 2 scripts to install the latest version of Chrome, and configure automatic updates...
Install:
#!/bin/sh
dmgfile="googlechrome.dmg"
volname="Google Chrome"
logfile="/Library/Logs/GoogleChromeInstallScript.log"
url='https://dl.google.com/chrome/mac/stable/GGRO/googlechrome.dmg'
/bin/echo "--" >> ${logfile}
/bin/echo "`date`: Downloading latest version." >> ${logfile}
/usr/bin/curl -s -o /tmp/${dmgfile} ${url}
/bin/echo "`date`: Mounting installer disk image." >> ${logfile}
/usr/bin/hdiutil attach /tmp/${dmgfile} -nobrowse -quiet
/bin/echo "`date`: Installing..." >> ${logfile}
ditto -rsrc "/Volumes/${volname}/Google Chrome.app" "/Applications/Google Chrome.app"
/bin/sleep 10
/bin/echo "`date`: Unmounting installer disk image." >> ${logfile}
/usr/bin/hdiutil detach $(/bin/df | /usr/bin/grep "${volname}" | awk '{print $1}') -quiet
/bin/sleep 10
/bin/echo "`date`: Deleting disk image." >> ${logfile}
/bin/rm /tmp/"${dmgfile}"
exit 0
Updates:
https://github.com/ryangball/chrome-enable-autoupdates/blob/master/chrome-enable-autoupdates.sh
MrRoboto, your script on Github totally worked for me. Automatic updates in Chrome are working. Thanks a million.
When i run the Github script i get the following, am i doing something wrong?
Keystone installed
AutoUpdateChrome.sh: line 51: /Library/Google/GoogleSoftwareUpdate/GoogleSoftwareUpdate.bundle/Contents/MacOS/ksadmin: No such file or directory
Error: Failed to register Chrome with Keystone - code 0
@ralvarezOES That's good to hear. It's not my script, created by @ryan.ball
@Dylan_YYC What version of Chrome are you testing? The script only works for 75+