MondeIT Systèmes, réseaux & infrastructure
PowerShell Avancé

Guide des commandes PowerShell — Deploy-GLPIAgent.ps1

Chaque commande est présentée avec son rôle, sa syntaxe de principe et les pièges à éviter. L'ordre suit la logique du script.

Ce guide explique chaque commande dont tu as besoin, comme si tu n'avais jamais fait de PowerShell. Pour chacune : à quoi elle sert, comment sa syntaxe se lit mot par mot, comment la tester seule dans une console avant de la mettre dans le script, et les pièges classiques.

Les exemples de syntaxe sont génériques (c'est du PowerShell de base, documenté partout) : c'est à toi de les adapter et de les assembler pour construire ton script.

0. Les bases à connaître avant de commencer#

Cinq notions qui reviennent partout dans ce guide :

Une cmdlet : c'est le nom des commandes PowerShell. Elles suivent toujours le format Verbe-Nom : Get-Service (« obtenir - service »), New-Item (« créer - élément »), Remove-Item (« supprimer - élément »). Le verbe te dit ce que la commande fait.

Un paramètre : une option qu'on donne à une cmdlet, toujours précédée d'un tiret : Get-Service -Name glpi-agent. Ici -Name est le paramètre, et glpi-agent la valeur qu'on lui donne.

Une variable : une boîte pour ranger une valeur, toujours préfixée par $ :

$monDossier = "C:\Temp\GLPI"

Ensuite, écrire $monDossier n'importe où équivaut à écrire son contenu. Important : dans une chaîne entre guillemets doubles "...", les variables sont remplacées par leur valeur ; entre guillemets simples '...', non.

Un objet : en PowerShell, les commandes ne renvoient pas du texte mais des objets avec des propriétés. Exemple : Get-Service renvoie un objet service, qui a une propriété Status, une propriété Name, etc. On lit une propriété avec un point :

$service = Get-Service -Name glpi-agent
$service.Status

Pour découvrir toutes les propriétés d'un objet : $service | Get-Member.

Le pipeline | : la barre verticale envoie le résultat d'une commande à la commande suivante, comme une chaîne de montage :

commande1 | commande2 | commande3

Chaque commande travaille sur ce que la précédente lui a passé.


1. param(...) — recevoir les paramètres du script#

Rôle : permettre à celui qui lance le script de lui donner des informations (l'URL GLPI, le tag) sans modifier le code.

Le bloc param(...) se place tout en haut du fichier, avant toute autre instruction (seuls les commentaires peuvent le précéder). Comme c'est une syntaxe générique de PowerShell et pas la solution de l'exercice, voici le principe complet :

param(
    [Parameter(Mandatory = $true)]
    [string]$ServerUrl,

    [string]$Tag,

    [switch]$ForceReinstall
)

Ce qu'il faut comprendre, ligne par ligne :

  • [Parameter(Mandatory = $true)] rend le paramètre obligatoire. Si l'utilisateur lance le script sans -ServerUrl, PowerShell le lui demande interactivement au lieu de planter. L'attribut s'applique uniquement au paramètre qui le suit immédiatement.
  • Le type entre crochets ([string], [switch]) force le type de la valeur reçue. [string] = une chaîne de caractères.
  • Tag n'a pas d'attribut Mandatory : il est optionnel, et vaudra une chaîne vide si absent.
  • [switch] est un type spécial pour les options oui/non : on ne lui passe pas de valeur. Présent sur la ligne de commande = $true, absent = $false. Dans le script, tu le testes avec if ($ForceReinstall) { ... }.
  • La virgule sépare chaque paramètre — c'est l'oubli classique qui fait planter le bloc.

À l'exécution, ça donne des appels comme :

.\Deploy-GLPIAgent.ps1 -ServerUrl "https://helpdesk.example.test/front/inventory.php" -Tag "TSSR-J7"

ou avec forçage :

.\Deploy-GLPIAgent.ps1 -ServerUrl "..." -Tag "TSSR-J7" -ForceReinstall

Ensuite, dans le corps du script, les variables $ServerUrl, $Tag et $ForceReinstall sont directement utilisables partout — pas besoin de les redéclarer.

Améliorations pour plus tard (quand la base fonctionne) : [ValidateNotNullOrEmpty()] sur ServerUrl pour refuser une chaîne vide, ou HelpMessage = "..." dans l'attribut Parameter. Le bloc simple suffit pour le minimum attendu.


2. Vérifier les droits administrateur#

Rôle : refuser de continuer si le script n'a pas les privilèges admin (sinon msiexec échouera plus loin, de façon moins claire).

Comprendre d'abord les trois notions#

Windows sépare trois choses :

  • L'identité (identity) : QUI est connecté. Ton compte, son nom, son jeton de sécurité.
  • Le principal (principal) : l'identité plus ses appartenances aux groupes. C'est l'objet capable de répondre à « cet utilisateur fait-il partie de tel groupe ? »
  • Le rôle (role) : un groupe prédéfini de Windows (Administrators, Users, Guests...), listé dans l'énumération WindowsBuiltInRole.

Le test = récupérer l'identité → l'envelopper dans un principal → demander au principal si l'identité appartient au rôle Administrator.

Piège UAC à connaître : être membre du groupe Administrateurs ne suffit pas. Avec l'UAC, un admin qui ouvre une console normale travaille avec un jeton filtré, sans les privilèges. Le test renverra $false tant que la console n'est pas ouverte via « Exécuter en tant qu'administrateur ». C'est exactement ce qu'on veut détecter.

Étape A — récupérer l'identité courante#

Syntaxe générique .NET/PowerShell :

$identite = [Security.Principal.WindowsIdentity]::GetCurrent()

Mot par mot :

  • Les crochets autour du nom disent à PowerShell « ceci est un type (une classe .NET), pas une commande ».
  • :: (deux-points doublés) : la syntaxe pour appeler une méthode statique — une méthode qu'on invoque directement sur la classe, sans créer d'objet avant.
  • GetCurrent() : la méthode en question. Parenthèses vides = aucun argument. Elle renvoie un objet représentant l'utilisateur qui exécute le script.
  • $identite = ... : on range le résultat pour l'étape suivante.

Mini-test : tape la ligne seule dans une console, puis $identite.Name. Tu dois voir ton MACHINE\utilisateur.

Étape B — construire le principal avec New-Object#

WindowsPrincipal n'a pas de méthode statique : il faut fabriquer un objet, et sa recette de fabrication (son constructeur) exige une identité. Forme générale de New-Object :

$objet = New-Object NomComplet.De.LaClasse($cequonluidonne)
  • La classe à utiliser ici : Security.Principal.WindowsPrincipal
  • L'argument attendu entre parenthèses : ta variable de l'étape A.
  • Range le résultat dans une variable, par exemple $principal.

À toi d'assembler cette ligne.

Étape C — poser la question avec .IsInRole()#

Le principal possède une méthode d'instance .IsInRole() (« est dans le rôle ? »). Contrairement à l'étape A, elle s'appelle avec un simple point sur la variable :

$variable.NomDeLaMethode(argument)
  • L'argument à mettre entre parenthèses est le rôle à vérifier. Le rôle Administrateur est une valeur d'énumération (une liste fermée de valeurs nommées), et on y accède avec la même syntaxe :: :
[Security.Principal.WindowsBuiltInRole]::Administrator

Ici le :: désigne une valeur de la liste, pas une méthode. - Le retour est un booléen : $true si les privilèges admin sont actifs, $false sinon. Range-le dans une variable du genre $estAdmin.

Étape D — réagir si le test échoue#

Un booléen se teste directement dans un if, sans le comparer à quoi que ce soit :

if (-not $estAdmin) {
    # erreur claire + sortie (voir section 12)
}
  • -not inverse la condition : « si PAS admin, alors... ».
  • Dans le bloc : Write-Error avec un message qui dit quoi faire (relancer PowerShell en tant qu'administrateur), puis exit 1.

Ce test doit être la première action après le bloc param.

Validation : console normale → refus avec ton message. Console « Exécuter en tant qu'administrateur » → le script continue. Les deux comportements bons = étape validée.


3. Join-Path — construire un chemin proprement#

Rôle : coller deux morceaux de chemin sans se soucier des \.

Syntaxe générique :

$chemin = Join-Path <cheminParent> <nomEnfant>

Ce qu'il faut comprendre :

  • Le premier argument est le dossier parent, le second ce qu'on ajoute dedans.
  • La cmdlet gère elle-même le séparateur \ : pas de risque de double \\ ou de \ oublié comme avec une concaténation $a + "\" + $b.
  • Pour le dossier temporaire, le parent tout trouvé est la variable d'environnement $env:TEMP : c'est le dossier temporaire de Windows, déjà connu du système. Les variables $env:... donnent accès aux variables d'environnement Windows ($env:TEMP, $env:COMPUTERNAME, etc.).

Mini-test : Join-Path $env:TEMP "Essai" dans une console → affiche un chemin du genre C:\Users\toi\AppData\Local\Temp\Essai. Rien n'est créé : Join-Path fabrique juste la chaîne de texte du chemin.


4. Test-Path — vérifier qu'un chemin existe#

Rôle : savoir si un dossier ou fichier existe déjà, avant d'agir.

Syntaxe générique :

Test-Path <chemin>
  • Renvoie un booléen : $true si le chemin existe, $false sinon.
  • Comme c'est un booléen, ça se met directement dans un if :
if (Test-Path $monChemin) { ... }
if (-not (Test-Path $monChemin)) { ... }

Note les parenthèses supplémentaires autour de Test-Path ... quand on combine avec -not : sans elles, PowerShell croit que -not est un argument de Test-Path.

Mini-test : Test-Path C:\WindowsTrue. Test-Path C:\NExistePasFalse.


5. New-Item — créer le dossier temporaire#

Rôle : créer le dossier de travail où le MSI sera téléchargé.

Syntaxe générique :

New-Item -ItemType Directory -Path <chemin> -Force | Out-Null

Mot par mot :

  • -ItemType Directory : on précise qu'on crée un dossier (la même cmdlet sait aussi créer des fichiers, d'où la précision).
  • -Path : où le créer — utilise la variable construite avec Join-Path.
  • -Force : ne pas planter si le dossier existe déjà.
  • | Out-Null : New-Item renvoie un objet décrivant le dossier créé, et PowerShell l'afficherait à l'écran. Out-Null jette cet affichage à la poubelle pour garder une sortie propre.

Mini-test : crée un dossier d'essai dans $env:TEMP, vérifie avec Test-Path, puis supprime-le (section 13).


6. Invoke-WebRequest — télécharger le MSI#

Rôle : télécharger un fichier depuis une URL vers le disque.

Syntaxe générique :

Invoke-WebRequest -Uri <URL> -OutFile <cheminDestination> -UseBasicParsing -ErrorAction Stop

Mot par mot :

  • -Uri : l'adresse à télécharger (l'URL du MSI x64 — celle trouvée via l'API GitHub dans la version bonus, ou l'URL de la release fournie sinon).
  • -OutFile : le chemin complet du fichier de destination, dans ton dossier temporaire (pas dans Downloads — c'est une contrainte de l'énoncé). Construis-le avec Join-Path : ton dossier temp + le nom du fichier MSI.
  • -UseBasicParsing : option de compatibilité (évite une dépendance à Internet Explorer sur les vieux PowerShell). Ne coûte rien, mets-la.
  • -ErrorAction Stop : voir section 11 — transforme un échec de téléchargement en erreur bloquante, pour que ton try/catch la voie.

Pièges :

  • Sans -OutFile, la cmdlet ne sauvegarde rien sur le disque : elle renvoie la réponse en mémoire.
  • Un téléchargement qui « réussit » mais renvoie une page d'erreur HTML au lieu d'un MSI, ça arrive : c'est justement le contrôle SHA256 (section suivante) qui te protège de ça.

Mini-test : télécharge n'importe quel petit fichier public vers $env:TEMP, puis vérifie son existence avec Test-Path.


7. Get-FileHash — contrôler le SHA256#

Rôle : calculer l'empreinte du fichier téléchargé et la comparer à l'empreinte officielle, pour garantir que le MSI est intact et authentique.

Ce qu'est un hash, en deux mots#

Un hash SHA256 est une empreinte digitale du fichier : une longue chaîne hexadécimale calculée à partir de son contenu. Si un seul octet du fichier change, l'empreinte change du tout au tout. Comparer ton empreinte calculée à l'empreinte publiée par l'éditeur prouve que le fichier est exactement celui attendu.

Calculer le hash#

Syntaxe générique :

$resultat = Get-FileHash <cheminDuFichier> -Algorithm SHA256
  • -Algorithm SHA256 : l'algorithme demandé par l'énoncé.
  • Attention : $resultat n'est PAS le hash. C'est un objet avec plusieurs propriétés (Algorithm, Hash, Path). Le hash lui-même est dans la propriété .Hash :
$resultat.Hash

C'est cette propriété (une chaîne en MAJUSCULES) qu'il faut comparer.

Comparer avec le hash attendu#

L'opérateur de comparaison d'égalité en PowerShell est -eq (pas == comme dans d'autres langages) :

if ($valeurA -eq $valeurB) { ... }
  • Sur des chaînes, -eq est insensible à la casse par défaut : pratique, car les hash publiés sur GitHub sont souvent en minuscules et ceux de Get-FileHash en majuscules. La comparaison directe fonctionne.
  • Le hash attendu vient soit du champ digest de l'asset GitHub (bonus, voir section 15 — attention, il peut être préfixé par sha256:, à retirer avant de comparer), soit du fichier .sha256 publié avec la release.
  • Si les deux hash diffèrent : erreur claire et ARRÊT du script, sans installer. C'est tout l'intérêt du contrôle.

Mini-test : calcule le hash d'un fichier quelconque deux fois → même résultat. Modifie une lettre dans un fichier texte, recalcule → résultat totalement différent.


8. Start-Process + msiexec — installer en silencieux#

Rôle : lancer l'installateur Windows (msiexec.exe) sans interface, avec les options GLPI, et savoir si l'installation a réussi.

Pourquoi Start-Process et pas juste taper la commande ?#

On pourrait écrire msiexec /i ... directement, mais PowerShell rendrait la main immédiatement, sans attendre la fin de l'installation, et sans te donner le résultat. Start-Process règle les deux problèmes avec -Wait et -PassThru.

Syntaxe générique :

$processus = Start-Process -FilePath <programme> -ArgumentList <tableauDArguments> -Wait -PassThru

Mot par mot :

  • -FilePath : le programme à lancer, ici "msiexec.exe".
  • -ArgumentList : les arguments à lui passer, sous forme de tableau de chaînes séparées par des virgules :
-ArgumentList "/i", $cheminMsi, "/quiet", "/norestart", "OPTION1=valeur", "OPTION2=valeur"
  • -Wait : attendre que msiexec ait terminé avant de continuer le script.
  • -PassThru : renvoyer un objet représentant le processus. Sans lui, Start-Process ne renvoie rien.
  • $processus.ExitCode : le code de sortie de msiexec, lisible après coup grâce à -PassThru + -Wait.

Les arguments msiexec à connaître#

  • /i suivi du chemin du MSI : « installer ce paquet ».
  • /quiet : aucune interface, aucune question.
  • /norestart : ne pas redémarrer le poste tout seul.
  • Les options GLPI Agent au format OPTION=valeursans espace autour du = (contrainte de l'énoncé). Celles qui t'intéressent : SERVER= (avec ta variable $ServerUrl), TAG= (avec $Tag), et l'option qui déclenche un inventaire immédiat — cherche RUNNOW dans la doc officielle « Windows installer » de GLPI Agent. Le mode service Windows est le comportement par défaut de l'installateur.

Interpréter le code de sortie#

Ton script doit tester ExitCode et considérer 0 et 3010 comme des succès.

Pièges :

  • Si le chemin du MSI contient des espaces, entoure-le de guillemets supplémentaires dans l'argument (guillemets dans la chaîne).
  • Grâce aux guillemets doubles, "SERVER=$ServerUrl" insère bien la valeur de la variable. Avec des guillemets simples, non.
  • La piste 5 de l'énoncé propose une variante : préparer la ligne complète puis l'exécuter via cmd.exe /c msiexec.exe .... Les deux approches sont acceptables ; Start-Process te donne le code de sortie plus proprement.

9. Get-Service — vérifier l'état du service#

Rôle : contrôler qu'après installation, le service glpi-agent existe et tourne.

Syntaxe générique :

$service = Get-Service -Name <nomDuService>
  • Le nom du service à vérifier ici : glpi-agent.
  • Le résultat est un objet avec notamment la propriété .Status : valeurs possibles Running (démarré), Stopped (arrêté), etc.
  • Comportement en cas d'absence : si le service n'existe pas, la cmdlet lève une erreur. Deux stratégies au choix :
  • -ErrorAction Stop → l'absence part dans ton catch (service manquant = installation ratée = échec clair) ;
  • -ErrorAction SilentlyContinue → la variable reste vide, et tu testes if ($null -eq $service) { ... } pour gérer le cas toi-même.

Pour l'affichage final demandé par l'énoncé, montrer .Status (et éventuellement .Name) suffit.

Mini-test : Get-Service -Name Spooler (le spouleur d'impression, présent sur tout Windows) → observe l'objet renvoyé et sa propriété Status.


10. Start-Service — démarrer le service si besoin#

Rôle : si le service est installé mais arrêté, le démarrer.

Syntaxe générique :

Start-Service -Name <nomDuService>
  • À placer dans un if : seulement si .Status n'est pas Running.
  • La comparaison « différent de » en PowerShell : -ne (not equal), le cousin de -eq.
  • Après un Start-Service, relis l'état avec Get-Service pour l'afficher dans ton résumé.

11. try / catch / finally + -ErrorAction — gérer les erreurs#

Rôle : intercepter les échecs (téléchargement, service absent...) pour afficher un message clair au lieu d'un pavé rouge illisible, et garantir le nettoyage final.

La structure#

try {
    # les étapes qui peuvent échouer
}
catch {
    # ce qu'on fait si une erreur bloquante survient dans le try
}
finally {
    # exécuté DANS TOUS LES CAS, succès ou échec
}

Ce qu'il faut comprendre :

  • Dès qu'une erreur bloquante survient dans le try, PowerShell saute immédiatement dans le catch (le reste du try n'est pas exécuté).
  • Dans le catch, la variable automatique $_ contient l'erreur. $_.Exception.Message en donne le message lisible, à intégrer dans ton propre message d'erreur.
  • Le finally est optionnel mais précieux : c'est l'endroit idéal pour le nettoyage du dossier temporaire (section 13), car il s'exécute même si tout a explosé avant.

Le piège n°1 du débutant : -ErrorAction Stop#

Beaucoup de cmdlets lèvent par défaut des erreurs non bloquantes : elles affichent du rouge mais le script continue, et le catch ne se déclenche pas. Pour forcer une cmdlet à produire une erreur bloquante (donc « catchable ») :

UneCmdlet -SesParametres -ErrorAction Stop

Mets -ErrorAction Stop sur toutes les étapes critiques : téléchargement, calcul de hash, vérification du service. Sans ça, ton try/catch semble ne « pas marcher ».

Mini-test : dans une console, essaie Get-Service -Name nexistepas seul, puis la même chose dans un try/catch sans -ErrorAction Stop (le catch ne se déclenche pas), puis avec (le catch se déclenche). La différence saute aux yeux.


12. Write-Host, Write-Error, exit — afficher et signaler#

Rôle : informer l'utilisateur de l'avancement, et signaler un échec de façon exploitable.

Write-Host — messages d'avancement#

Write-Host "Etape X : ce que je suis en train de faire..."
Write-Host "OK" -ForegroundColor Green
Write-Host "ECHEC" -ForegroundColor Red
  • Affiche du texte à l'écran, c'est tout.
  • -ForegroundColor colore le texte : vert pour les succès, rouge pour les échecs — très lisible pour ton compte rendu.
  • Bonne pratique : un message avant chaque grande étape, et un message de résultat après. C'est ce qui rend le script « relisible » et son exécution compréhensible.

Write-Error — signaler une erreur#

Write-Error "Description claire du probleme et quoi faire"
  • Affiche le message sur le flux d'erreur (en rouge), le canal prévu pour ça — contrairement à un simple Write-Host rouge.
  • À utiliser dans tes catch et tes tests qui échouent (hash invalide, pas admin...).

exit — sortir avec un code#

exit 1
  • Termine le script immédiatement, avec un code de sortie : 0 = tout s'est bien passé (c'est le code par défaut si le script va au bout), toute autre valeur = échec.
  • Ce code est le même mécanisme que l'ExitCode de msiexec : il permet à un autre outil (ou au formateur) de savoir si ton script a réussi sans lire les messages.
  • Le duo classique en cas de pépin : Write-Error "..." puis exit 1.

13. Remove-Item — nettoyer à la fin#

Rôle : supprimer le dossier temporaire et le MSI téléchargé une fois l'installation terminée.

Syntaxe générique :

Remove-Item -Path <chemin> -Recurse -Force
  • -Recurse : supprime aussi tout le contenu du dossier (sans lui, un dossier non vide fait échouer la suppression, avec une question interactive).
  • -Force : supprime aussi les fichiers cachés ou en lecture seule.
  • Où le placer : dans le bloc finally (section 11), pour que le nettoyage ait lieu même si le script a échoué en route.
  • Prudence de débutant : vérifie que la variable de chemin n'est pas vide avant de supprimer (un Test-Path dans un if fait l'affaire). Remove-Item -Recurse -Force sur un mauvais chemin ne pardonne pas.

PARTIE BONUS

À n'attaquer que quand le minimum fonctionne de bout en bout.


14. Invoke-RestMethod — interroger l'API GitHub#

Rôle : demander à GitHub la liste des releases GLPI Agent pour trouver automatiquement la dernière version et son MSI x64.

API et JSON, en deux mots#

Une API web est une URL qui, au lieu de renvoyer une page à regarder, renvoie des données structurées au format JSON (du texte organisé en champs et listes). L'URL à utiliser est donnée dans la piste 3 de l'énoncé.

Syntaxe générique :

$reponse = Invoke-RestMethod -Uri <urlDeLApi>

Ce qu'il faut comprendre :

  • Cousine d'Invoke-WebRequest, mais elle convertit automatiquement le JSON en objets PowerShell. Pas de texte à découper : tu obtiens directement des objets avec des propriétés.
  • Ici, $reponse est un tableau de releases. Chaque release a notamment :
  • .tag_name : le nom de version (ex. un tag contenant « 1.18 ») ;
  • .prerelease : $true si c'est une préversion (à écarter) ;
  • .draft : $true si c'est un brouillon (à écarter) ;
  • .assets : la liste des fichiers publiés avec la release. Chaque asset a un .name (nom du fichier), une .browser_download_url (l'URL de téléchargement à donner ensuite à Invoke-WebRequest), et éventuellement un .digest (le hash, voir section 7).

Mini-test : appelle l'API dans une console, puis explore : $reponse.Count (combien de releases ?), $reponse[0].tag_name (la plus récente — les crochets [0] prennent le premier élément d'un tableau), $reponse[0].assets.name (les fichiers de cette release).


15. Where-Object — filtrer les releases et les assets#

Rôle : garder uniquement les releases stables, puis trouver l'asset GLPI-Agent-*-x64.msi.

Syntaxe générique :

$resultat = $collection | Where-Object { <condition sur $_> }

Ce qu'il faut comprendre :

  • Where-Object reçoit une collection par le pipeline | et ne laisse passer que les éléments pour lesquels la condition entre { } est vraie.
  • Dans la condition, $_ désigne l'élément en cours d'examen (chaque release, ou chaque asset, tour à tour).
  • Opérateurs utiles dans la condition :
  • -not : inversion. Pour garder les stables : -not $_.prerelease ;
  • -and : combiner deux conditions (pas prerelease et pas draft) ;
  • -like : comparaison avec joker * (le * remplace n'importe quoi). Pour l'asset : condition sur $_.name avec le motif "GLPI-Agent-*-x64.msi".

Tu auras donc deux filtrages : un sur $reponse (les releases), puis un sur la propriété .assets de la release retenue (les fichiers).

Mini-test : Get-Service | Where-Object { $_.Status -eq "Running" } → uniquement les services démarrés. Le principe est identique.


16. Select-Object -First 1 — garder la plus récente#

Rôle : après filtrage, ne retenir que la première release de la liste.

Syntaxe générique :

$premier = $collection | Select-Object -First 1
  • L'API GitHub renvoie les releases triées de la plus récente à la plus ancienne : le premier élément après filtrage est donc la dernière version stable.
  • S'enchaîne naturellement après Where-Object dans le même pipeline : filtre, puis prends le premier.

17. Get-ItemProperty sur le registre — version installée#

Rôle : détecter si GLPI Agent est déjà présent sur le poste, et en quelle version.

Le registre, en deux mots#

Le registre Windows est la base de configuration du système. PowerShell y navigue comme dans un disque : HKLM: (HKEY_LOCAL_MACHINE, la config machine) s'utilise comme C:. Chaque programme installé laisse une clé de désinstallation contenant son nom et sa version.

Syntaxe générique :

$programmes = Get-ItemProperty -Path <cheminRegistre>

Ce qu'il faut comprendre :

  • Le chemin des programmes installés : HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\* — le * final signifie « toutes les sous-clés », donc tous les programmes.
  • Par acquit de conscience, il existe une seconde branche pour les programmes 32 bits : même chemin avec WOW6432Node intercalé après SOFTWARE.
  • Chaque objet renvoyé a (entre autres) :
  • .DisplayName : le nom du programme tel qu'affiché ;
  • .DisplayVersion : sa version, en chaîne de caractères.
  • Pour isoler GLPI Agent : un Where-Object (section 15) avec une condition -like sur .DisplayName et le motif "GLPI Agent*".
  • Si le filtrage ne renvoie rien : l'agent n'est pas installé — c'est une information utile, pas une erreur (le script doit alors installer).

Mini-test : liste tout avec ce chemin, affiche les DisplayName, et retrouve à l'œil des programmes que tu connais.


18. Le cast [version] — comparer deux versions#

Rôle : comparer correctement la version locale et la version en ligne pour décider s'il faut installer.

Pourquoi ne pas comparer les chaînes directement#

En comparaison de chaînes, "1.9" est supérieur à "1.18" (comparaison caractère par caractère : « 9 » > « 1 »). C'est faux au sens des versions. Le type [version] de .NET compare nombre par nombre : 1.18 > 1.9, comme attendu.

Syntaxe générique du cast (conversion de type) :

[version]$maChaine
  • Placer [version] devant une chaîne la convertit en objet version comparable.
  • Les opérateurs de comparaison PowerShell : -lt (less than, inférieur), -gt (greater than, supérieur), -eq (égal), -le / -ge (inférieur ou égal / supérieur ou égal). Exemple de principe :
if ([version]$versionLocale -lt [version]$versionEnLigne) { ... }

Pièges :

  • La chaîne doit être purement numérique à points (« 1.18 », « 1.18.0 »). Le .tag_name GitHub peut contenir un préfixe non numérique : nettoie la chaîne avant le cast (regarde du côté de la méthode .TrimStart() ou de l'opérateur -replace).
  • Prévois le cas « rien d'installé » (section 17) : pas de version locale à comparer → on installe, tout simplement.
  • C'est ici que se branche le paramètre ForceReinstall : même à jour, si le switch est présent, on installe quand même.

Mini-test : dans une console, compare "1.9" -lt "1.18" (résultat surprenant) puis [version]"1.9" -lt [version]"1.18" (résultat correct). Tu verras immédiatement pourquoi le cast est indispensable.


Ordre d'assemblage du script

  1. param — paramètres (section 1)
  2. Test admin (section 2)
  3. Variables importantes visibles en début de script (chemins, noms...)
  4. Dossier temporaire (sections 3, 4, 5)
  5. (Bonus) API GitHub → release stable → asset x64 (sections 14, 15, 16)
  6. (Bonus) Version locale + comparaison + ForceReinstall (sections 17, 18)
  7. Téléchargement (section 6)
  8. Contrôle SHA256 — arrêt si différent (section 7)
  9. Installation msiexec + code de sortie (section 8)
  10. Vérification / démarrage du service (sections 9, 10)
  11. Résumé final pour le compte rendu (section 12)
  12. Nettoyage dans le finally (sections 11, 13)

Le tout enveloppé dans try / catch / finally, avec -ErrorAction Stop sur les étapes critiques.


Méthode de travail conseillée

  1. Teste chaque commande seule dans une console avec les mini-tests de ce guide, avant de l'écrire dans le script.
  2. Construis le script étape par étape : fais fonctionner le test admin seul, puis ajoute le dossier temporaire, puis le téléchargement, etc. Relance le script à chaque ajout.
  3. Garde les bonus pour la fin, un par un, sans casser ce qui marche.
  4. Note au fur et à mesure les infos demandées pour le compte rendu (version détectée, URL du MSI, résultat du hash, code de sortie, état du service).

Vincent

Ces articles devraient vous plaire

Commentaires (0)

Aucun commentaire pour l'instant. Ouvrez le bal.

Laisser un commentaire

Jamais publié.

Connectez-vous pour commenter sous votre nom, sans ressaisie et sans attendre la modération.