Présentation
Opsi (Open PC Server Integration) est un outil de gestion de clients Windows basé sur un serveur Linux et sous licence AGPLv3 (hors code des modules cofinancés), version modifiée et orientée serveurs de la licence GPLv3. Son code source est la propriété de la société uib gmgh et son représentant en France/Belgique est la société Opensides.
Ses fonctionnalités principales sont :
- Installation automatisée via PXE de systèmes d’exploitation clients Windows par le biais d’une image
- Distribution automatique des logiciels via une connexion agent/serveur
- Intégration automatique des drivers en se basant sur l’ID de ceux-ci
- Inventaires matériels et logiciels
- Dépôts logiciels multiples et décentralisés
Objectif
L’objectif de cet article est la création pas-à-pas d’un nouveau produit Opsi à déployer sur un parc informatique.
Nous prendrons ici l’exemple de Mozilla Firefox en version 45.7 ESR 32 bits.
Son exécutable portera pour nom Firefox Setup 45.7.0esr.exe.
Réalisation
1. Adaptation des scripts Opsi Winst
Il faut, dans un premier temps, créer les 3 scripts Opsi Winst setup32.ins, uninstall32.ins et delsub32.ins à partir des templates et les adapter au produit à générer, ici Mozilla Firefox 45.7.0 ESR 32 bits. Ils seront nécessaires à l’étape 3.
1.1. Fichier setup32.ins adapté
[Actions] requiredWinstVersion >= "4.11.4.6" ScriptErrorMessages=off DefVar $MsiId$ DefVar $UninstallProgram$ DefVar $LogDir$ DefVar $ProductId$ DefVar $MinimumSpace$ DefVar $InstallDir$ DefVar $ExitCode$ DefVar $LicenseRequired$ DefVar $LicenseKey$ DefVar $LicensePool$ DefVar $myProperty$ Set $LogDir$ = "%opsiLogDir%" Set $ProductId$ = "firefox" Set $MinimumSpace$ = "100 MB" Set $InstallDir$ = "%ProgramFiles32Dir%\Mozilla Firefox" Set $LicenseRequired$ = "false" Set $LicensePool$ = "p_" + $ProductId$ if not(HasMinimumSpace ("%SystemDrive%", $MinimumSpace$)) LogError "Not enough space on %SystemDrive%, " + $MinimumSpace$ + " on drive %SystemDrive% needed for " + $ProductId$ isFatalError "No Space" else comment "Show product picture" ShowBitmap "%ScriptPath%\" + $ProductId$ + ".png" $ProductId$ comment "get property value ....." set $myProperty$ = getProductProperty("dummy_prop", "yes") if $myProperty$ = "yes" comment "myproperty value is yes" else if $myProperty$ = "no" comment "myproperty value is no" else comment "myproperty value must be may_be" endif endif if FileExists("%ScriptPath%\delsub32.ins") comment "Start uninstall sub section" Sub "%ScriptPath%\delsub32.ins" endif Message "Installing " + $ProductId$ + " ..." if $LicenseRequired$ = "true" comment "Licensing required, reserve license and get license key" Sub_get_licensekey endif comment "Start setup program" ChangeDirectory "%SCRIPTPATH%" Winbatch_install Sub_check_exitcode comment "Copy files" Files_install /32Bit comment "Patch Registry" Registry_install /32Bit comment "Create shortcuts" LinkFolder_install endif [Winbatch_install] "%ScriptPath%\Firefox Setup 45.7.0esr.exe" -ms -ira [Files_install] [Registry_install] [LinkFolder_install] [Sub_get_licensekey] if opsiLicenseManagementEnabled comment "License management is enabled and will be used" comment "Trying to get a license key" Set $LicenseKey$ = demandLicenseKey ($LicensePool$) DefVar $ServiceErrorClass$ set $ServiceErrorClass$ = getLastServiceErrorClass comment "Error class: " + $ServiceErrorClass$ if $ServiceErrorClass$ = "None" comment "Everything fine, we got the license key '" + $LicenseKey$ + "'" else if $ServiceErrorClass$ = "LicenseConfigurationError" LogError "Fatal: license configuration must be corrected" LogError getLastServiceErrorMessage isFatalError else if $ServiceErrorClass$ = "LicenseMissingError" LogError "Fatal: required license is not supplied" isFatalError endif endif endif else LogError "Fatal: license required, but license management not enabled" isFatalError endif [Sub_check_exitcode] comment "Test for installation success via exit code" set $ExitCode$ = getLastExitCode if ($ExitCode$ = "0") comment "Looks good: setup program gives exitcode zero" else comment "Setup program gives a exitcode unequal zero: " + $ExitCode$ if ($ExitCode$ = "1605") comment "ERROR_UNKNOWN_PRODUCT 1605 This action is only valid for products that are currently installed." comment "Uninstall of a not installed product failed - no problem" else if ($ExitCode$ = "1641") comment "looks good: setup program gives exitcode 1641" comment "ERROR_SUCCESS_REBOOT_INITIATED 1641 The installer has initiated a restart. This message is indicative of a success." else if ($ExitCode$ = "3010") comment "looks good: setup program gives exitcode 3010" comment "ERROR_SUCCESS_REBOOT_REQUIRED 3010 A restart is required to complete the install. This message is indicative of a success." else logError "Fatal: Setup program gives an unknown exitcode unequal zero: " + $ExitCode$ isFatalError endif endif endif endif
1.2. Fichier uninstall32.ins adapté
[Actions] requiredWinstVersion >= "4.11.4.6" ScriptErrorMessages=off DefVar $MsiId$ DefVar $UninstallProgram$ DefVar $LogDir$ DefVar $ExitCode$ DefVar $ProductId$ DefVar $InstallDir$ DefVar $LicenseRequired$ DefVar $LicensePool$ Set $LogDir$ = "%opsiLogDir%" Set $ProductId$ = "firefox" Set $InstallDir$ = "%ProgramFiles32Dir%\Mozilla Firefox" Set $LicenseRequired$ = "false" Set $LicensePool$ = "p_" + $ProductId$ comment "Show product picture" ShowBitmap "%ScriptPath%\" + $ProductId$ + ".png" $ProductId$ Message "Uninstalling " + $ProductId$ + " ..." if FileExists("%ScriptPath%\delsub32.ins") comment "Start uninstall sub section" Sub "%ScriptPath%\delsub32.ins" endif if $LicenseRequired$ = "true" comment "Licensing required, free license used" Sub_free_license endif [Sub_free_license] comment "License management is enabled and will be used" comment "Trying to free license used for the product" DefVar $result$ Set $result$ = FreeLicense($LicensePool$)
1.3. Fichier delsub32.ins adapté
Set $MsiId$ = '{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}' Set $UninstallProgram$ = $InstallDir$ + "\uninstall\helper.exe" Message "Uninstalling " + $ProductId$ + " ..." if FileExists($UninstallProgram$) comment "Uninstall program found, starting uninstall" Winbatch_uninstall sub_check_exitcode endif if not (GetRegistryStringValue32("[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\" + $MsiId$ + "] DisplayName") = "") comment "MSI id " + $MsiId$ + " found in registry, starting msiexec to uninstall" Winbatch_uninstall_msi sub_check_exitcode endif comment "Delete files" Files_uninstall /32Bit comment "Cleanup registry" Registry_uninstall /32Bit comment "Delete program shortcuts" LinkFolder_uninstall [Winbatch_uninstall] "$UninstallProgram$" /S [Winbatch_uninstall_msi] msiexec /x $MsiId$ /qb-! REBOOT=ReallySuppress [Files_uninstall] [Registry_uninstall] [LinkFolder_uninstall] [Sub_check_exitcode] comment "Test for installation success via exit code" set $ExitCode$ = getLastExitCode if ($ExitCode$ = "0") comment "Looks good: setup program gives exitcode zero" else comment "Setup program gives a exitcode unequal zero: " + $ExitCode$ if ($ExitCode$ = "1605") comment "ERROR_UNKNOWN_PRODUCT 1605 This action is only valid for products that are currently installed." comment "Uninstall of a not installed product failed - no problem" else if ($ExitCode$ = "1641") comment "looks good: setup program gives exitcode 1641" comment "ERROR_SUCCESS_REBOOT_INITIATED 1641 The installer has initiated a restart. This message is indicative of a success." else if ($ExitCode$ = "3010") comment "looks good: setup program gives exitcode 3010" comment "ERROR_SUCCESS_REBOOT_REQUIRED 3010 A restart is required to complete the install. This message is indicative of a success." else logError "Fatal: Setup program gives an unknown exitcode unequal zero: " + $ExitCode$ isFatalError endif endif endif endif
2. Génération du nouveau produit vierge
Sur le serveur Opsi, suivre la procédure suivante :
# cd /home/opsiproducts/ # opsi-newprod
Sélectionner le type de produit localboot pour spécifier qu’il s’agit d’un logiciel à déployer (un produit netboot correspond à un système d’exploitation à déployer).
Renseigner les champs indispensables comme l’ID du produit (caractères alphanumériques minuscules et tirets éventuels, mais sans espaces), le Nom du produit (nom complet, espaces acceptés), la version du produit et la version du paquet (1 pour un nouveau paquet). On peut laisser les autres champs avec leurs valeurs par défaut et modifier éventuellement la description.
Spécifier le nom du futur Script de configuration, c’est-à-dire le script d’installation, et le nom du futur Script de désinstallation.
Notre script ne dépendra d’aucun autre produit.
Aucune propriété spécifique au produit ne sera définie.
Renseigner le nom du créateur du produit et son adresse e-mail.
Notre produit a été créé et ajouté dans /home/opsiproducts/, son répertoire source porte le nom de l’ID du produit spécifié plus haut.
Le nouveau produit généré comporte 3 dossiers et 3 fichiers :
# tree firefox/
firefox/
├── CLIENT_DATA/
├── OPSI/
│ ├── control
│ ├── postinst
│ └── preinst
└── SERVER_DATA/
3 directories, 3 files
Tous sont vides sauf le fichier firefox/OPSI/control qui contient les informations spécifiées plus haut lors de la génération du produit.
[Package] version: 1 depends: incremental: False [Product] type: localboot id: firefox name: Firefox ESR description: Mozilla Firefox Extended Support Release advice: version: 45.7.0 priority: 0 licenseRequired: False productClasses: setupScript: setup32.ins uninstallScript: uninstall32.ins updateScript: alwaysScript: onceScript: customScript: userLoginScript: [Changelog] firefox (45.7.0-1) testing; urgency=low * Initial package -- adminFri, 24 Feb 2017 15:46:20 +0000
Il faut ensuite corriger d’éventuels problèmes de propriétaires et de droits sur l’arborescence du produit généré.
# opsi-setup --set-rights firefox/ [5] [Feb 24 15:50:26] Setting rights on directory u'/home/opsiproducts/firefox' (Rights.py|121)
Notre paquet n’apparait pas encore dans la liste des paquets déployables via l’interface de gestion Opsi Configed :
# opsi-package-manager -l firefox
------------------------------
- opsi-test.opensharing.priv -
------------------------------
Product ID Version Name
===========================================================================
De même, il n’apparait pas encore dans le répertoire /var/lib/opsi/depot/ sur lequel pointent les partages [opsi_depot] (RO) et [opsi_depot_rw] (RW).
3. Création des fichiers constituant le produit
Tout d’abord, il faut déposer les scripts Opsi Winst setup32.ins, uninstall32.ins et delsub32.ins, vus plus haut, dans le répertoire CLIENT_DATA relatif au produit firefox.
Déposer également le fichier image firefox.png (qui s’affichera lors de l’installation du produit) et l’exécutable Firefox Setup 45.7.0esr.exe.
Pour cela, il y a deux possibilités :
- Déposer les fichiers directement dans /home/opsiproducts/firefox/CLIENT_DATA/
- Avec l’utilisateur adminuser, membre du groupe pcpatch, monter un lecteur réseau temporaire pointant sur le partage [opsi_workbench] et accéder à firefox/CLIENT_DATA/
Une fois ceci fait, on peut générer le paquet qui sera déployé sur le client.
La commande opsi-makeproductfile crée un fichier respectant la nomenclature IDproduit_versionproduit_versionpaquet.opsi
Il s’agit d’une archive cpio contenant deux autres archives cpio compressées au format gzip : CLIENT_DATA.cpio.gz et OPSI.cpio.gz créées à partir des fichiers inclus dans les répertoires de même nom.
# cd /home/opsiproducts/firefox/ # opsi-makeproductfile Verrouillage du paquet Information sur le paquet ---------------------------------------------------------------------------- version : 1 custom package name : incremental package : False package dependencies : Information sur le produit ---------------------------------------------------------------------------- product id : firefox product type : localboot version : 45.7.0 name : Firefox ESR description : Mozilla Firefox Extended Support Release advice : priority : 0 licenseRequired : False product classes : windows software ids : Scripts du produit ---------------------------------------------------------------------------- setup : setup32.ins uninstall : uninstall32.ins update : always : once : custom : user login : Création du fichier pour le paquet '/home/opsiproducts/firefox/firefox_45.7.0-1.opsi' Creating archive CLIENT_DATA.cpio.gz 100.00% [==================================================] Creating archive OPSI.cpio.gz 100.00% [==================================================] Creating archive firefox_45.7.0-1.opsi 100.00% [==================================================] Nettoyage Déverrouillage du paquet
L’arborescence finale du répertoire CLIENT_DATA est la suivante :
- 3 fichiers de script
- 1 fichier image
- 1 exécutable
- 1 archive cpio à déployer
# tree /home/opsiproducts/firefox/
/home/opsiproducts/firefox/
├── CLIENT_DATA/
│ ├── delsub32.ins
│ ├── firefox.png
│ ├── Firefox Setup 45.7.0esr.exe
│ ├── setup32.ins
│ └── uninstall32.ins
├── firefox_45.7.0-1.opsi
├── OPSI/
│ ├── control
│ ├── postinst
│ └── preinst
└── SERVER_DATA/
3 directories, 9 files
4. Intégration du produit à l’interface Opsi Configed
# opsi-package-manager -i firefox_45.7.0-1.opsi

Ceci a pour effet de générer le fichier /var/lib/opsi/config/products/firefox_45.7.0-1.localboot, copie du fichier control du paquet.
Notre produit est bien listé parmi les produits déployables :
# opsi-package-manager -l firefox
------------------------------
- opsi-test.opensharing.priv -
------------------------------
Product ID Version Name
===========================================================================
firefox 45.7.0-1 Firefox ESR
Il apparait sur l’interface Opsi Configed avec les informations renseignées lors de la génération du produit.
Et c’est lors de cette intégration à Opsi Configed que notre paquet apparait désarchivé et décompressé dans /var/lib/opsi/depot/. Les partages [opsi_depot] (RO) et [opsi_depot_rw] (RW) pointent sur ce dossier et le partage [opsi_depot], donc en lecture seule, sera monté sur le client avec l’utilisateur adminuser (membre du groupe pcpatch) et un montage temporaire sur la lettre P: afin de procéder à l’installation ou la désinstallation du produit.
# ls -lA /var/lib/opsi/depot/firefox/
total 43148
-rw-rw---- 1 opsiconfd pcpatch 2051 févr. 25 14:27 delsub32.ins
-rw-rw---- 1 opsiconfd pcpatch 296 févr. 25 14:42 firefox.files
-rw-rw---- 1 opsiconfd pcpatch 40428 mai 23 2013 firefox.png
-rw-rw---- 1 opsiconfd pcpatch 44124744 févr. 25 14:29 Firefox Setup 45.7.0esr.exe
-rw-rw---- 1 opsiconfd pcpatch 3890 févr. 25 14:26 setup32.ins
-rw-rw---- 1 opsiconfd pcpatch 1034 févr. 25 14:27 uninstall32.ins
On y retrouve le contenu du dossier /home/opsiproducts/firefox/CLIENT_DATA/ mais aussi un fichier portant le nom de l’ID du produit avec l’extension .files, firefox.files, contenant la liste des fichiers requis pour le paquet :
f 'Firefox Setup 45.7.0esr.exe' 44124744 ef87a5ac0012a13e223481507fd75e6c f 'delsub32.ins' 2051 cf14f019f077e57e118bc16ea2eb9dbe f 'firefox.png' 40428 c638484624813ccb10e141e98a5a50db f 'setup32.ins' 3890 fc4fe9fb5eaa3d22ff655067912d5127 f 'uninstall32.ins' 1034 f21b6cc8255192b338683f0a6d3a26d8
5. Déploiement du nouveau produit sur un client
Pour installer le produit Firefox sur notre client, il faut tout d’abord définir le champs Action sur setup (clic gauche).
Puis enregistrer la configuration modifiée avec un clic droit et Sauver.
- Si l’on souhaiter déployer immédiatement le produit, il suffit de faire un nouveau clic droit et sélectionner Exécuter maintenant ("on_demand").
- Si l’on souhaite que l’installation se fasse au prochain redémarrage/démarrage du client, il n’y a rien d’autre à faire.

Comme il s’agit d’une première installation, la désinstallation préalable n’a pas lieu car le fichier C:\Program Files\Mozilla Firefox\uninstall\helper.exe n’existe pas encore, comme nous pouvons le voir dans les logs (côté client et côté serveur).
(20819) [6] [févr. 24 16:20:09:379] [firefox] If (20820) [5] [févr. 24 16:20:09:379] [firefox] Starting query if file exist ... (20821) [5] [févr. 24 16:20:09:379] [firefox] "C:\Program Files\Mozilla Firefox\uninstall\helper.exe": (20822) [5] [févr. 24 16:20:09:379] [firefox] FileExists($UninstallProgram$) <<< result false (20823) [6] [févr. 24 16:20:09:379] [firefox] Then (20824) [6] [févr. 24 16:20:09:379] [firefox] EndIf
La procédure de désinstallation est identique, il suffit de programmer un uninstall sur Opsi Configed, de sauver, puis d'exécuter à la demande ou attendre le prochain redémarrage.

Aller plus loin