Ma problématique du jour : automatiser au maximum les mises à jours et déploiements de packages sur mon parc de Mac, sans intervention de l’utilisateur. Jusque là c’est simple.
Pour pimenter un peu, l’idée de réaliser les mises à jour au boot de la machine, même si aucune session n’est ouverte. Ici tous les Mac “fixes” sont programmés avec une heure d’allumage et d’extinction auto, via une préférence gérée par le serveur (MCX), ce qui permet de réaliser les mises à jour avant l’arrivée des utilisateurs. Vous pouvez adopter la même stratégie même sans déployer un préférence gérée pour cela, simplement en utilisant le panneau “Economie d’énergie” des préférences système, puis le bouton “Programmer”.
Egalemenent, pour compliquer un peu, avant de lancer les mises à jours, l’idée est de vérifier si le poste est connecté au réseau local dont le serveur fait office de serveur de mise à jour et où est également installé Munki pour le déploiement des applications et autres packages (l’installation de Munki a fait l’objet d’un précédent billet).
Après réflexion, j’ai décidé de scripter ça. Pour lancer ce script, deux alternatives :
- soit en utilisant un script preflight pour munki (le mieux étant d’isoler notre script quelque part et de l’appeler comme sous-script de
/usr/local/munki/preflight
), - soit un script lancé au démarrage de la machine par une entrée LaunchDaemon. L’avantage de cette solution est que si je lance la commande
/usr/local/munki/munkiimport
depuis le terminal ou l’application/Applications/Utilities/Managed Software Update.app
(Mise à jour de logiciels) il ne sera pas fait appel à script. C’est exactement le comportement que je recherche, notamment pour tenir compte des utilisateurs mobiles : ils sont au bureau, au démarrage leur machine va faire ses mises à jours, s’ils ne sont pas au bureau, au démarrage la machine ne fera pas ses mises à jours, par contre, s’ils ont besoin de récupérer un update particulier, rien ne leur empêche de le faire manuellement quand ils ne sont pas au bureau.
Voyons ce script …
Grosso modo, l’idée est de faire dans l’ordre :
- un test de connexion pour vérifier si on accède à internet,
- de récupérer notre IP publique dans un fichier temporaire (en faisant un ‘curl -f ‘ sur http://checkip.dyndns.org par exemple),
- de tester avec ‘grep’ si ce fichier contient la valeur de notre IP (ici je fais le test sur deux IP car nous disposons de deux liens internet avec du load balancing entre les deux).
Le script en lui-même :
#!/bin/bash #nos variables ###### IP_PUBLIQUE="XX.XXX.XX.X" IP_PUBLIQUE2="X.XXX.XXX.XX" LOGSLOCATION="/var/log/" ###### mkdir -p $LOGSLOCATION LOGS="$LOGSLOCATION/munki_autoupdate.log" LOG_OUT="$LOGSLOCATION/munki_autoupdate_out.log" # Redirection des sorties vers nos journaux exec 1>> $LOG_OUT exec 2>> $LOG_OUT DATE_DU_JOUR=$(date) # Fichier unique temporaire pour contenir notre IP TEMP_IP=$(mktemp /tmp/tempip.XXXXX) # Nettoyons les anciens logs temporaires s'il en reste [ -f $LOG_OUT ] && rm $LOG_OUT echo " " >> $LOG_OUT echo "****************************** $DATE_DU_JOUR ******************************" >> $LOG_OUT ## Si le script n'a pas besoin d'être exécuté par le compte root, supprimer ces lignes if [ `whoami` != 'root' ] then echo "Ce script doit être utilisé par le compte root. Utilisez SUDO." >> $LOG_OUT cat $LOG_OUT >> $LOGS [ -f $LOG_OUT ] && rm $LOG_OUT [ -f $TEMP_IP ] && rm $TEMP_IP exit 1 fi # Par sécurité, attendons quelques instants, histoire d'être sûrs que les connexions réseau sont effectives sleep 40 # Testons si une connection internet est ouverte curl -f http://checkip.dyndns.org ERROR=$(echo $?) echo "ERROR checkip.dyndns.org : " $ERROR >> $LOG_OUT if [ $ERROR -ne 0 ] then echo "Non connecté à internet." >> $LOG_OUT cat $LOG_OUT >> $LOGS [ -f $LOG_OUT ] && rm $LOG_OUT [ -f $TEMP_IP ] && rm $TEMP_IP exit 0 else # Récupérons notre IP dans un fichier temporaire TEST_CONNECT=$(curl -f http://checkip.dyndns.org) echo "Votre IP actuelle : " $TEST_CONNECT >> $LOG_OUT echo $TEST_CONNECT >> $TEMP_IP # Testons si nous sommes connectés sur un réseau ayant pour IP Publique l'IP1 TEST_GREP=$(grep -c "$IP_PUBLIQUE" $TEMP_IP) if [ $TEST_GREP -eq 1 ] then echo "Connecté au réseau local, IP publique n°1." >> $LOG_OUT /usr/local/munki/managedsoftwareupdate -v >> $LOG_OUT /usr/local/munki/managedsoftwareupdate --installonly >> $LOG_OUT cat $LOG_OUT >> $LOGS [ -f $LOG_OUT ] && rm $LOG_OUT [ -f $TEMP_IP ] && rm $TEMP_IP exit 0 else # Testons si nous sommes connectés sur un réseau ayant pour IP Publique l'IP2 TEST_GREP2=$(grep -c "$IP_PUBLIQUE2" $TEMP_IP) if [ $TEST_GREP2 -eq 1 ] then echo "Connecté au réseau local, IP publique n°2" >> $LOG_OUT /usr/local/munki/managedsoftwareupdate -v >> $LOG_OUT /usr/local/munki/managedsoftwareupdate --installonly >> $LOG_OUT cat $LOG_OUT >> $LOGS [ -f $LOG_OUT ] && rm $LOG_OUT [ -f $TEMP_IP ] && rm $TEMP_IP exit 0 else echo "Connecté à internet hors du réseau local." >> $LOG_OUT cat $LOG_OUT >> $LOGS [ -f $LOG_OUT ] && rm $LOG_OUT [ -f $TEMP_IP ] && rm $TEMP_IP exit 0 fi fi fi
C’est assez basique, mais fonctionnel.
Pour info, j’ai redirigé toutes les sorties vers les journaux, ce script étant sensé être lancé en tâche de fond. Vous pouvez personnaliser dans les variables l’emplacement.
Dans cet exemple je fais le test sur deux IP publiques, vous pouvez évidemment en ajouter autant que vous voudrez (si c’est assez important, autant écrire une petite fonction pour cela pour garder un script propre) ou n’en garder qu’une.
Vous pouvez évidemment personnaliser les commandes qui sont lancées si le script détecte que vous êtes connecté sur votre réseau local. Ici je recherche et j’installe les mises à jour via Munki (qui lui même distribue les mises à jour Apple) :
/usr/local/munki/managedsoftwareupdate -v /usr/local/munki/managedsoftwareupdate --installonly
Si vous n’avez pas de serveur Munki et que vous souhaitez juste lancer les mises à jour Apple remplacez par un :
sudo softwareupdate -i -a
Comment lancer ce script ?
Pour appeler ce script au démarrage nous allons :
- le déposer à l’abris sur notre machine (ici dans
/usr/local/munki_autoupdate
), - créer une entrée launchdaemon.
Pour déposer votre script :
# créer le dossier sudo mkdir -p /usr/local/munki_autoupdate # rendons le script exécutable chmod 755 /adresse/de/votre/script.sh # puis déplaçons le sudo mv /adresse/de/votre/script.sh /usr/local/munki_autoupdate/
Pour créer le launchdaemon, vous pouvez utiliser Lingon, avec un éditeur ou le faire en ligne de commande :
sudo nano /Library/LaunchDaemons/fr.monserveur.munkiautoupdate.plist
Entrez le contenu suivant, en personnalisant éventuellement les adresses :
<?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>Label</key> <string>fr.monserveur.munkiautoupdate</string> <key>ProgramArguments</key> <array> <string>/bin/sh</string> <string>/usr/local/munki_autoupdate/login_autoupdate.sh</string> </array> <key>RunAtLoad</key> <true/> <key>UserName</key> <string>root</string> </dict> </plist>
Quittez nano avec CTL+X
, validez, puis chargez votre démon :
sudo launchctl load /Library/LaunchDaemons/fr.monserveur.munkiautoupdate.plist
Il ne vous reste plus qu’à redémarrer votre machine et vérifier quelques minutes ensuite le fichier journal /var/log/munki_autoupdate_out.log
pour vérifier que le processus se déroule correctement.
Pour aller plus loin …
J’ai découvert après avoir écrit ce script qu’un script d’exemple était proposé par MunkiWebAdmin pour remplir la même fonction. Il est un peu plus optimisé… à vous de voir !