Source : http://www.openbrain.fr/index.php?article15/deploiement-powershell
Nous n'avons pas toujours les packages MSI des différents logiciels que l'on a besoin de déployer via GPO.
Pour ma part, mon parc est uniquement composé de postes sous Windows, donc PowerShell m'a semblé la meilleure solution pour déployer ces logiciels.
Je vais présenter la démarche pour l'installation et la mise à jour d'un logiciel ainsi que la mise en place d'un fichier de log unique pour chaque poste.
Ici, l'exemple pris est CCleaner, qui n'est pas installable/désinstallable avec les objets WMI, ce qui rajoute un peu de difficulté. Mais ce script reste adaptable aux autres logiciels, je vous expliquerai quelles sont les variables à modifier.
Allez, c'est parti!
L'algorithme :
- Initialisations et récupérer les données qui nous intéressent (nom de machine, IP, OS 32 ou 64b et chemin du fichier de log).
- Définir les variable nécessaires.
- Définir les fonctions de traitement.
- Pour chaque clé de registre qui concerne le logiciel,
- récupérer les info sur l'installation courante
- Vérifier les clés de registe
-
- code principal du script :
- appel à la fonction qui détermine l'action
- (0) si le logiciel n'est pas encore installé
- installer le logiciel,
- faire un rapport dans le log
- (1) si le logiciel n'est pas à la bonne version
- désinstaller l'ancien,
- faire un rapport dans le log
- installer le nouveau logiciel,
- faire un rapport dans le log
- (2) si le logiciel est bon
- faire un rapport dans le log
1 Initialisations
Ce chapitre est souvent défini en fin de construction du script mais doit être exécuté au début
# ===========================================
# Nom de la machine
$Poste = $env:COMPUTERNAME
# Récupération de l'adresse ip de la machine
$IpLocale = ((ipconfig | findstr [0-9].\.)[0]).Split()[-1]
# Récupération de l'architecture de l'OS, renvoie 4 pour du 32bits et 8 pour du 64bits
$ArchiOS = [IntPtr]::Size
# Définition du chemin d'accès au fichier de log du poste
$LogFile="\\serveur\chemin\du\fichier\log $poste.txt"
# ===========================================
Rien de particulier à expliquer ici, si ce n'est que l'on peut directement passer la valeur d'une variable dans une chaîne de caractère, ce qui évite les concaténations et autres opérations sur les chaines.
Remarquer les instructions pour récupérer l'adresse IP à partir de ipconfig. Note : elle ne récupère l'adresse que de la première connexion. Chez moi, c'est une adresse virtuelle pour VBox :'(
Maintenant que les variables utiles aux fichiers de logs sont initialisées, passons aux variables concernant le logiciel.
2 Définition des variables
Attention, les paramètres d'installation ne sont pas standards, elles correspondent ici à celles que je désire utiliser pour CCleaner. /S correspond à une installation silencieuse, et /L=1036 permet de définir la langue d'installation, ici le français.
# ===========================================
# Nom du logiciel
$Logiciel = "CCleaner"
# Variable de versions des différents logiciels
$Version = "5.02"
# Définition du chemin des fichiers exécutables
$FichierInstallation = "\serveur\chemin\de\l_executable\ccsetup502.exe"
$FichierDesinstallation = ""
# Définition des paramètres d'installation pour CCleaner : silencieuse et en français
$ParametreInstallation = "/S /L=1036"
# ===========================================
La variable $Logiciel permet de définir la présence du logiciel par matching. Si le nom d'un programme installé contient la chaine définie dans cette variable, alors le logiciel sera considéré comme installé. Attention donc à ce que vous lui donnez comme valeur.
De la même manière, la variable $Version doit avoir une valeur exacte, sinon ce sera désinstallation et réinstallation à chaque exécution!
Les dernières variables que nous allons initialiser sont celles qui seront utilisées pour parcourir le registre.
# ===========================================
# Accès à la base de registre de la machine
$Type = [Microsoft.Win32.RegistryHive]::LocalMachine
$RemoteRegistry = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($Type, $Poste)
# Définition des clés de registre comportant les programmes
$Clef = "SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
$ClefBis = "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"
# Stockage du chemin de registre pour parcours ultérieur
$RegKey = $RemoteRegistry.OpenSubKey($Clef)
$RegKeyBis = $RemoteRegistry.OpenSubKey($ClefBis)
# ===========================================
Ici, c'est tout bête, il suffit de faire un copier/coller, ce seront les mêmes partout!
Voilà que tout est prêt pour commencer! Allez, une petite initialisation du fichier de log au format que l'on veut, et c'est parti.
# ===========================================
# Ajout d'une ligne de séparation pour chaque pc dans le fichier de log
ADD-content -path $LogFile -value "====================================================="
Add-Content -Path $LogFile -Value "$(Get-Date) : DEBUT D'EXECUTION DU SCRIPT"
# Ajout du nom de l'ordinateur et de son ip
ADD-content -path $LogFile -value "$(Get-Date) : Nom de la machine : $Poste"
# Ajout de l'architecture de l'OS
if ($ArchiOS -eq "4") { ADD-content -path $LogFile -value "$(Get-Date) : Architecture : 32 bits" }
if ($ArchiOS -eq "8") { ADD-content -path $LogFile -value "$(Get-Date) : Architecture : 64 bits" }
# Ajout de l'adresse ip
Add-Content -Path $LogFile -Value "$(Get-DAte) : Adresse IP : $IpLocale"
# ===========================================
3 Définition des fonctions à utiliser
C'est maintenant que les choses sérieuses commencent.
Nous allons écrire la fonction presence_registre qui vérifie la présence et la version du logiciel, et peut renvoyer trois valeurs différentes :
- 0 si le logiciel est absent,
- 1 s'il est présent et dans la mauvaise version et
- 2 si tout va pour le mieux dans le meilleur des mondes.
Allons-y progressivement. Tout d'abord, la fonction, ses arguments et sa variable à retourner.
On passe deux paramètres à l'appel de la fonction : le nom du logiciel et sa version, définis plus haut.
Ensuite la variable $etat est initialisée à 0. Elle prendra la valeur 0, 1 ou 2 en fonction de la présence ou non du logiciel et de sa version.
# ===========================================
function presence_registre {
# Définition des paramètres nécessaires à la fonction
param([String]$logiciel,[String]$version)
# On initialise la variable de retour à 0
$etat = 0
# ...
Code alternatif :
# ===========================================
# La définition des paramètres nécessaires à la fonction est faite au début de la fonction
function presence_registre ([String]$logiciel,[String]$version){
# On initialise la variable de retour à 0
$etat = 0
# ...
Maintenant, vérifier la présence du logiciel, en parcourant les clés de registre.
Ensuite, pour chaque clé trouvée,
a) Récupérer les informations sur l'installation courante vers une variable structurée (monObjet):
b) Les comparer avec ce que l'on cherche (nom et version) et donner une valeur à notre variable de retour en fonction.
Noter que la variable $global est globale et utilisable dans le script qui appelle la fonction.
# ...
# test de la première clé ---------------
if (Test-Path "HKLM:\$Clef") # Vérifier que le chemin existe bien :
# Si c'est ok, parcourir l'ensemble des sous-clés :
foreach($keyName in $RegKey.GetSubKeyNames()) # Pour chaque sous-clé :
# Pour chaque sous-clé :
# a) Récupère les informations qui nous intéressent, à savoir :
# - Le nom du logiciel
# - sa version
# - et le chemin de l'outil de désinstallation
$SubKey = $RegKey.OpenSubKey($keyName)
$MonObjet = "" | Select DisplayName,Version,UninstallString
$MonObjet.DisplayName = $SubKey.GetValue("DisplayName")
$MonObjet.Version = $SubKey.GetValue("DisplayVersion")
$MonObjet.UninstallString = $Subkey.GetValue("UninstallString")
# b) Comparer la clé avec le nom de notre logiciel et donner une valeur à l'état
if($logiciel -match $MonObjet.DisplayName) { #Si le logiciel est installé, etat=1
$etat = 1 # On a le logiciel. Mais est-ce la bonne version ?
# Récupération du chemin de l'exécutable de désinstallation
$global:FichierDesinstallation = $MonObjet.UninstallString
if($MonObjet.Version -eq $version) { $etat = 2 } #Si la version est bonne, etat=2
if($MonObjet.Version -eq $version) { #Si la version est bonne, 2
$etat = 2 # le logiciel est installé ET c'est la bonne version. Bien !
} #fi test de version
#Le programme a été trouvé, on sort de la fonction et on renvoie son résultat
return $etat
} #fi test si le nom correspond.
# Si le test a échoué, etat reste à 0
} # done : fin du foreach
} # fi : fin du test de la 1ère clé
# ...
On fait la même démarche avec la seconde clé ($cleBis) et la seconde liste de noms ($RegKeyBis). Le code est identique, il suffit de remplacer le nom de ces deux variables.
Voir le conde complet plus bas
Enfin, on termine le retour de la variable $etat au script appelant la fonction, si ça n'a pas déjà été fait plus haut
# ...
return $etat
} #End function presence
# ==================================================
Voici le code complet
# ===========================================
function presence_registre {
param([String]$logiciel,[String]$version) # Définir les paramètres de la fonction
$etat = 0 # Initialiser la variable de retour à 0
# test de la première clé ---------------
if (Test-Path "HKLM:\$Clef") # Tester le chemin
# Si ok, parcourir les sous-clés :
foreach($keyName in $RegKey.GetSubKeyNames()) { # Pour chaque sous-clé :
# a) Récupérer le nom du logiciel, sa version, le chemin du désinstalleur
$SubKey = $RegKey.OpenSubKey($keyName)
$MonObjet = "" | Select DisplayName,Version,UninstallString
$MonObjet.DisplayName = $SubKey.GetValue("DisplayName")
$MonObjet.Version = $SubKey.GetValue("DisplayVersion")
$MonObjet.UninstallString = $Subkey.GetValue("UninstallString")
# b) Comparer la clé avec le nom de notre logiciel et donner une valeur à l'état
if($logiciel -match $MonObjet.DisplayName) { #Si le nom du logiciel correspond, alors il est installé, etat=1
$etat = 1 # on a le logiciel.
$global:FichierDesinstallation = $MonObjet.UninstallString # Récupérer le chemin du désinstalleur
if($MonObjet.Version -eq $version) { $etat = 2 } #Si la version est bonne, etat=2
return $etat # fin de la fonction
} #fi test si le nom correspond.
} # done : fin du foreach
} # fi : fin du test de la 1ère clé
# test de la seconde clé (traitement presque identique)
if (Test-Path "HKLM:\$ClefBis") {
# Si ok, parcourir les sous-clés :
foreach($keyName in $RegKeyBis.GetSubKeyNames()) { # Pour chaque sous-clé
# a) Récupérer le nom du programmes, sa version, et son exécutable de désinstallation
$SubKey = $RegKeyBis.OpenSubKey($keyName)
$MonObjet = "" | Select DisplayName,Version,UninstallString
$MonObjet.DisplayName = $SubKey.GetValue("DisplayName")
$MonObjet.Version = $SubKey.GetValue("DisplayVersion")
$MonObjet.UninstallString = $Subkey.GetValue("UninstallString")
# b) Vérifier la présence des programmes et de la bonne version
if($logiciel -match $MonObjet.DisplayName) { #Si le nom correspond, alors le logiciel est installé, etat=1
$etat = 1 # on a le logiciel.
$global:FichierDesinstallation = $MonObjet.UninstallString # Récupérer le chemin du désinstalleur
if($MonObjet.Version -eq $version) { $etat = 2 } #Si la version est bonne, etat=2
return $etat # fin de la fonction
} #fi test : si le nom est bon.
} # done : fin du foreach
} # fi : fin du test de la clé
return $etat
} #End function presence
# ===========================================
code principal
Le plus gros du travail est maintenant fait! Ouf!
Il ne reste plus qu'à exécuter les actions selon ce que nous retourne la fonction (qu'on oublie pas d'appeler)!
Ici, nous détaillons également chaque étape dans le fichier de logs, ça peut toujours servir pour savoir où bloque le script.
Selon l'action déterminé par la fonction :
- A) On consigne le début dans le log,
- B) on effectue le travail
- C) on écrit le résultat dans le log
# debut ===========================================
## ... code de la fonction ici ...##
# ==============================================
# Code principal
# ==============================================
# 1. Appel à la fonction pour savoir quelle action effectuer : 0, 1 ou 2
$action = presence_registre -logiciel $Logiciel -version $Version #### Appel de la fonction préparée
# 2. Si le programme n'est pas présent sur la machine, $action = 0, on installe le logiciel avec écriture dans le fichier de logs
if ($action -eq 0) {
# A) Inscription dans le fichier log
Add-Content -Path $LogFile -Value "$(Get-Date) : CCleaner absent, début de l'installation"
# B) Installation de l'application à jour avec les paramètres définis au début du script
Start-Process $FichierInstallation $ParametreInstallation
Start-Sleep -Seconds 60
# Vérification de l'installation de CCleaner
$ActionRealisee = presence_registre -logiciel $Logiciel -version $version
# C) Ecriture dans le fichier de log du succès ou de l'échec de l'installation
if ($ActionRealisee -eq 2) {
Add-Content -Path $LogFile -Value "$(Get-Date) : Installation de la version à jour de CCleaner réussie"
}
else {
Add-Content -Path $LogFile -Value "$(Get-Date) : Echec de l'installation de CCleaner"
}
} # fi action=0
# 3. Si le programme est présent dans la mauvaise version, $action = 1,
# on désinstalle la version obsolète et on installe la bonne avec écriture dans le fichier de logs
if ($action -eq 1) {
# A1) Inscription dans le log
Add-Content -Path $LogFile -Value "$(Get-Date) : Version obsolète de CCLeaner, désinstallation"
# B1) Désinstallation silencieuse (/S)
Start-Process $FichierDesinstallation "/S"
Start-Sleep -Seconds 40
# Test de la désinstallation
$DesinstallationOK = presence_registre -logiciel $Logiciel -Version $Version
# C1) Ecriture dans le fichier de log de l'échec ou du succès de la désinstallation
if($DesinstallationOK -eq 0){
Add-Content -Path $LogFile -Value "$(Get-Date) : Désinstallation de la version obsolète de CCleaner réussie"
}
else {
Add-Content -Path $LogFile -Value "$(Get-Date) : Echec de la désinstallation de la version obsolète de CCleaner"
}
# A2) Inscription dans le log
Add-Content -Path $LogFile -Value "$(Get-Date) : Début de l'installation de la version à jour de CCleaner"
# B2) Installation de l'application à jour avec les paramètres définis au début du script
Start-Process $FichierInstallation $ParametreInstallation
Start-Sleep -Seconds 60
# Vérification de l'installation de CCleaner
$actionRealisee = presence_registre -logiciel $Logiciel -Version $Version
# C2) Ecriture dans le fichier de log de l'échec ou du succès de l'installation
if ($actionRealisee -eq 2) {
Add-Content -Path $LogFile -Value "$(Get-Date) : Installation de la version à jour de CCleaner réussie"
}
else {
Add-Content -Path $LogFile -Value "$(Get-Date) : Echec de l'installation de la version à jour de CCleaner"
}
} # fi action=1
# 4. Si le programme est présent sur la machine, $action = 2, on écrit seulement dans le fichier de log
if ($action -eq 2) {
Add-Content -Path $LogFile -Value "$(Get-Date) : La version installée de CCleaner est à jour"
}
# 5. Pour finir, on précise dans le fichier de log que l'exécution du script est terminée :
Content -Path $LogFile -Value "$(Get-Date) : FIN D'EXECUTION DU SCRIPT"</td>
# ===========================================
Et voilà, tout est fait!
Cependant, le code peut être largement optimisé, amélioré : Il faudrait arrêter le script si l'ancienne version ne peut pas être désinstallée ; il faudrait comparer les versions pour ne pas désinstaller une version supérieure à celle installée.
De même, le fichier log pourrait être complété par des séparateurs de pc, le script pourrait être lancé sur une liste de PC ciblée ou tous, du domaine, etc. etc.
Liste des utilisateurs par groupe
Script de production de la liste des utilisateurs par groupe, en indiquant le statut (enabled/disabled) pour chacun.
Ce script produit un affichage (write-host) et un fichier log (write-output).
# ===========================================
<#
.SYNOPSIS
Get all AD objects stored in AD Group
.DESCRIPTION
Get a list of all AD objects stored in AD Group
.PARAMETER LogFilePath
Specify your log file path. This path must end with a "\"
.EXAMPLE
.\AD_Get-ADUserByGroup.ps1 -LogFilePath "C:\Logs\"
This will create a new log file containing all AD Objects by AD Groups
.NOTES
Author : Sylver SCHORGEN
Blog : http://microsofttouch.fr/default/b/sylver
Created : 25 feb. 2015
@sylver_schorgen
#>
Param(
[Parameter(Mandatory=$true)]
[string]$LogFilePath
)
Write-Host "============ Starting script ================"
Write-Host "Chercher tous les membres de l'AD par groupe"
# Getting all AD Groups and writing them in a file and on the console
# Setting up log file and Groups variables
$heureLog = Get-Date -Format "yyyyMMdd_hhmmss - "
$nomFichierLog = "AD Users by Group.log"
$fichierLog = $LogFilePath + $heureLog + $nomFichierLog
# chercher la liste des groupes
$lesGroupesAD = Get-ADGroup -Filter "*"
# pour chaque groupe de la liste des groupes
foreach ($unGroupe in $lesGroupesAD) {
# Afficher le groupe
Write-Host
Write-Host " Groupe : $($unGroupe.Name)"
Write-Output "Groupe : $($unGroupe.Name)" | Out-File $fichierLog -Append
# Chercher tous les membres d'un groupe AD et les écrire dans un fichier et les afficher
$lesMembres = Get-ADGroupMember $($unGroupe.Name)
# Pour chaque membre de la liste des membres du groupe
foreach($unMembre in $lesMembres) {
if($unMembre.ObjectClass -eq "user") { // si le membre est un utilisateur (pour éliminer les ordinateurs et autres objets)
# Chercher le satut du compte de l'utilisateur (Enable ou Disable)
$user = Get-ADUser $unMembre -Properties enabled
# Traiter les données de l'utilisateur
if ($user.enabled -eq $True) {
Write-Host " * User : $($user.Name) --> Enable"
Write-Output " * User : $($user.Name) --> Enable" | Out-File $fichierLog -Append
}
else {
Write-Host " * User : $($user.Name) --> Disable"
Write-Output " * User : $($user.Name) --> Disable" | Out-File $fichierLog -Append
}
} # /if membre utilisateur
} # /foreach lesMembres : fin pour chaque membre du groupe
Write-Host # saut de ligne vide
Write-Output "" | Out-File $fichierLog -Append
} # /foreach lesGroupesAD : fin pour chaque groupe de la liste des groupes
Write-Host
Write-Host "================= Travail terminé =====================" -ForeGroundColor Green
# ===========================================
Voila, avec ça on a l'essentiel