Bases du langage

Syntaxe des cmdlet

Les commandes PS (cmdlet) se composent d'un verbe suivi d'un nom d'objet sur lequel il est appliqué : get (verbe), help(nom) devient : get-help (la commande).

Certaines commandes possèdent un alias qui raccourci la frappe.
Exemple : la commande Get-Gelp est identique à son raccourci : help

Les verbes peuvent être : get, add, remove, set et plein d'aurtres encore. utiliser la commande get-command (ou son alias gcm) pour obtenir la liste complète.

Les commandes peuvent être suivies (très souvent) par des paramètres et des "commutateurs" ou "attributs"(je dirais paramètres) qui eux même peuvent avoir un alias (ouh, ça se complique) qu'on n'utilisera que dans de rares cas (ouf!).
Exemple : Get-ChildItem -Path c:/temp qui permet d'afficher le contenu du dossier c:/temp/

Note : les majuscules n'on pas d'importance dans les noms des commandes ou des paramètres mais améliorent la lisibilité des mots-clés utilisés (la connaissance de l'anglais est trrrrès utile)

  • Syntaxe
    : verbe-nom (la plupart du temps), insensible à la casse, disposent souvent d'alias
  • Verbe
    = action effectuée sur le nom : get, add, remove, set, ...
  • Nom
    = objet sur lequel est effectué l'action : item, command, ...
Exemples :
 
	get-help get-command
	help gcm

Les commutateurs précédés d'un - (moins ou tiret 6)

Exemples :
 
	Get-ChildItem -Path . -Filter *.txt # le point signifie "répertoire courant"

 

Quelques commandes courantes

Commandes de base

Powershell [raccourci, alias]

DOS, Bash

exemple

Description

get-help [help]help, manget-help dirObtenir l'aide
get-command [gcm] Obtenir la liste des commandes existantes
get-childItem [ls ou dir]dir, lsGet-ChildItem c:/tempLister les objets (fichiers, liens ou dossiers) enfants du répertoire (courant)
set-location [sl, cd, chdir]cdcd c:/temp/Changer de répertoire
new-itemmkdirnew-item -path c:/temp -name perso -itemType directory
mkdir c:/temp/perso
créer un nouvel objet. Ici, un répertoire, la syntaxe est compliquée volontairement, la seconde ligne présente l'utilisation de l'alias associé à un paramétrage simplifié.
get-service Liste des services actifs
get-ODBCDriver Liste des pilotes ODBC
get-NetIpAddressipconfig, ip Liste des connexions ethernet et des adresses IP
clear-host [clear]cls, clear Effacer l'écran affiché (dans ps)
get-location [gl, pwd]cd Afficher le répertoire courant, idem cd sans paramètre
get-content [gc, cat, type]type, catget-content c:/temp/test.txtAfficher le contenu d'un fichier
get-ADDomain Afficher les données du domaine de l'AD auquel on est rattaché

Certaines commandes possèdent des alias qui permettent de raccourcir leur écriture et de conserver certaines connaissances du DOS et du Shell (compatibilité)

Structures de contrôle

PowershellDOSDescription
if (...) {...} [else   {...}]
IF <test> <cde> [ELSE cde]
Alternative simple
while(...) {...} 
FORBoucle TANT QUE <test> Faire ...
do {...} (while(...))
FORBoucle REPETER ... TANT QUE
for (...;...;...)  {...}
FORBoucle POUR
function ... {
	Param(..., ..., ...)
	...
	Return ...
}
ou
function ([type1] parm1, ...) {
	...
	Return ...
}
:etiquette
	...
goto :eof
Fonction, déclaration des paramètres et retour du résultat.
La déclaration des paramètres peut aussi se faire en ligne (c'est + moderne).

Variables (comme en php) :

Définies par l'utilisateur :
  • Pas déclarées
  • Commencent par $
  • Définies par l'utilisateur
Exemples
  • $nom='Cassin'
  • echo $nom
Prédéfinies, exemples :
  • $Pwd=répertoire en cours,
  • $Args=tableau des arguments passés au script
  • $_ = valeur courante dans le traitement en boucle d'une liste
  • $? : VRAI si cde précédente réussie, FAUX si échec

Les Jokers

  • * = n'importe quel et combien de caractères
  • ? = n'importe quel caractère non vide
Exemples
  • Ls *.txt --> a.txt, a1.txt, a2.txt, az.txt, azt.txt, aaaaa.txt, bw2d.5.txt
  • Ls a?.txt --> a1.txt, a2.txt, az.txt

 

Exemples de base

Note : » signifie que pour des raisons de largeur d'affichage ce qui suit est en continuité de la ligne précédente

Écrire un ` (back tick ou back quote, altgr+7, espace) permet de scinder volontairement une commande en plusieurs lignes

Note : » signifie que pour des raisons de largeur d'affichage ce qui suit est en continuité de la ligne précédente

Écrire un ` (back tick ou back quote, altgr+7, espace) permet de scinder volontairement une commande en plusieurs lignes

Jouer avec les variables, utiliser les méthodes objet

# ===========================================
 
$MaVariable="Une chaîne de caractères" # définition de la variable
$MaVariable # afficher la variable
Une chaîne de caractères
> $MaVariable.GetType() # Afficher ses propriétés (appel de la méthode GetType())
IsPublic IsSerial Name   BaseType 
-------- -------- ----   -------- 
True     True     String System.Object 
 
> $MaVariable.GetType().FullName # Afficher le nom complet du type de la variable
System.String
> $ps=get-process # récup de la liste des processus, on obtient un tableau
> $ps[0] # Afficher le processus 0 (le premier)
 
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s)   Id SI ProcessName
------- ------ ----- ----- ----- ------   -- -- -----------
    121     10  6336  9156    71   0,09 6368  1    ACEngSvr
 
> $ps[0].processname # Afficher l'attribut (objet) processname du processus 0
ACEngSvr
 
# ===========================================

Obtenir la liste des commandes (extrait)

# ===========================================
 
PS> get-command *disk* # Lister des commandes contenant le mot disk
 CommandType  Name                   Version    Source
 -----------  ----                   -------    ------
 Alias        Get-DiskSNV            2.0.0.0    Storage                          # Alias de Get-disk
 Function     Add-PhysicalDisk       2.0.0.0    Storage                          # Ajouter disque physique dans 1 pool
 Function     Disconnect-VirtualDisk 2.0.0.0    Storage                          # Déconnecter un disque virtuel (=fichier .vhd = un disque dur)
 Function     Get-Disk               2.0.0.0    Storage                          # Afficher le/les disques durs (et clés USB)
 Function     Mount-DiskImage        2.0.0.0    Storage                          # Monter une image disque comme disque dur
 Function     New-VirtualDisk        2.0.0.0    Storage                          # Créer un disque virtuel
 Function     Set-Disk               2.0.0.0    Storage                          # Modifie les attributs et id's d'un disque
 Application  diskmgmt.msc           0.0.0.0    C:\WINDOWS\system32\diskmgmt.msc # gestionnaire de disque
 Application  diskpart.exe           10.0.10... C:\WINDOWS\system32\diskpart.exe # gestnre de partitions
 Application  diskperf.exe           10.0.10... C:\WINDOWS\system32\diskperf.exe # start/stop compteurs de perf des disques
 
# Lister tous les fichiers .pst du disque
> Get-ChildItem C:\ -Filter *.pst -Recurse
# Supprimer tout fichier .tmp du répertoire courant et de ses sous répertoires
> Get-ChildItem -Path . -Filter *.tmp -Recurse | Remove-Item
 
# Lister les fichiers de taille > à 1Mo, du répertoire courant et de ses sous-rép.
# Boucle en une ligne.
> Get-ChildItem -Path . -Recurse | Where-Object \{$_.Length -gt 104857600\}
 
# Lister les fichiers dont la date est antérieure à maintenant
> $date = Get-Date -Year (((Get-Date).Year)-1)
> Get-ChildItem -Path . -Recurse | Where-Object \{$_.LastWriteTime -lt $date\}
 
# Faire une copie de sauvegarde du répertoire courant
> Get-ChildItem -Path . -Recurse | Copy-Item -Destination \server01\backup$ -Recurse
 
# Vérifier si l'exécution des scripts est interdite ou non
> Get-ExecutionPolicy Restricted
# Vérifier La liste des modules (on cherche le module ActiveDirectory ) 
> Get-Module -ListAvailable
 
# ===========================================

Script de fermeture des comptes dont l'inactivité dépasse 90 jours (à mettre dans un fichier .ps1)

# ===========================================
# === commandes préliminaires en ligne de commande
# -- Contrôle de restriction de scripting
>Set-ExecutionPolicy Unrestricted
 
# -- Import des fonctions AD
>Import-Module ActiveDirectory
 
 
# === Contenu du fichier script ======
# -- Rechercher les comptes inactifs à bloquer
$comptesInactifs = Search-ADAccount -UsersOnly -AccountInactive `
       -TimeSpan 90.00:00:00 -SearchBase `
       "OU=Users,DC=Domaine,DC=Local" | Where {$_.enabled}
 
# -- Bloquer ces comptes (pas besoin de préciser leur futur statut)
$comptesInactifs | Set-ADUser
# --- Envoi d'un email d'avertissement
# Préparation
$smtpServer = "mail.domaine.local"
$fromTxt    = "Disable-ADAccount <powershell@domaine.local>"
$toTxt      = "Helpdesk <helpdesk@domaine.local>"
$subject    = "[INFO] Comptes AD last logon > 90 jours"
$bodyTxt    = " 
<html><head></head>
<body> <p>Bonjour,</br>
    Les comptes suivants sont désactivés à cause d'une inactivité de plus de 90 jours :</p>
    <p>$comptesInactifs</p> 
</body> </html>\" 
# Envoi 
Send-MailMessage -smtpserver $smtpserver -from $fromTxt -to $toTxt -subject `
       $subject -body $bodyTxt -bodyasHTML -priority High
# ===========================================

Arrêter le serveur pour maintenance & prévenir les utilisateurs

# ===========================================
 
$server = 'myServer'
 
# Récupérer les sessions TS actives sur le serveur.
$connectedSessions = Get-TSSession -ComputerName $server -State Active
 
# Afficher une boite de message dans toutes les sessions TS actives.
$Message = "Important, Le serveur sera arrêté pour maintenance dans 10 minutes.
&raquo;        Veuillez enregistrer votre travail et vous déconnecter."
$connectedSessions | Send-TSMessage -Message $Message
 
# Attendre 10' (=60*10 secondes)
Start-Sleeph-Seconds 600
 
# Déconnecter les sessions TS actives
Get-TSSession -ComputerName $server -State Active | Stop-TSSession
 
# ===========================================

 

Conclusion 1ère partie

Powershell est :
  • On outil puissant
  • Un langage structuré, objet
  • Modulaire
  • Automatisable (gestionnaire de tâches, scripts appelés automatiquement, par GPO...)
  • Incontournable !
  • Existe aussi une version pour Linux
  • Concurrent sérieux du bash (Linux)

 


Exemples de Scripts complets

Exercice script mystère : retrouver à quoi sert ce script

# ===========================================
 
# Connecter des lecteurs réseau en fonction des groupes AD dont l'utilisateur est membre
# Script à lancer à l'ouverture de session utilisateur Sur un poste dans un domaine
#---------------------------------------------------------------------------------#
# Fonction pour vérifier si l'utilisateur courant est dans le groupe donné
   Function inGroup([string] $groupName) {
      $insideGroup = $false
      $ldapQuery    = "(samaccountname="+$env:USERNAME+")"
      $objDomain    = New-Object System.DirectoryServices.DirectoryEntry
      $objSearcher  = New-Object System.DirectoryServices.DirectorySearcher
      -ArgumentList $objDomain,$ldapQuery
 
      $users        = $objSearcher.FindAll()
      $user         = $users[0]
 
      Foreach ($group in $user.properties.memberof) {
         If (($group.ToLower()).IndexOf("cn="+$groupName.ToLower()) -ge 0) {
              $insideGroup = $true
              Break
          }
      }
      return $insideGroup
   }
 
#----------------------------------------------------#
# Déclaration des variables
# nom du Serveur de fichiers
      $server = "WS2012R2.CONTOSO.LOCAL"
# Liste/tableau des groupes avec nom de partage et lettre associés
# "nom_du_groupe"=@("nom_du_partage","lettre_de_lecteur")
      $groups = @{
         "2SISR"=@("2SISR$","W:")
         "SIO"=@("SIO_partage$","W:")
         "Commun"=@("COMM","V:")
      }
 
#------------------------------------------------------------------------------------#
### Corps du script
      $perNetworkShare = "\\"+$server+"\users\"+$env:USERNAME
   If (Test-Path $perNetworkShare) {
      New-PSDrive -name U -psprovider FileSystem -root $perNetworkShare
      }
 
      $groups.GetEnumerator() | Foreach {
      If (inGroup($_.Name)) {
          Write-Host $env:USERNAME " est membre du groupe : " $_.Name
          New-PSDrive -name $_.Value[1] -psprovider FileSystem -root "\\"+$server+"\"+$_.Value[0]
         }
      }
# ===========================================

Ressources documentaires

http://laurent-dardenne.developpez.com/articles/Windows/PowerShell/Introduction/

https://openclassrooms.com/courses/creez-votre-premier-script-avec-powershell

https://goalkicker.com/PowerShellBook/, un vrai document de référence (e-book, en)

 


Envoi d'un email

Envoyer un email au help desk avec un contenu paramétrable.

Pour cela, déclarer les variables : serveur SMTP, expéditeur, destinataire, sujet du mail, corps du mail. Elles sont utilisées dans la cmdlet Send-MailMessage permettant d'envoyer l'email.

# ===========================================
 
$smtpServer = "mail.domaine.local"
$from = "Disable-ADAccount <powershell@domaine.local>"
$to = "Helpdesk <helpdesk@domaine.local>"
$subject = "[INFO] Comptes AD last logon > 90 jours"
$body = "
<html>
  <head></head>
     <body>
        <p>Bonjour,<br />
           Les comptes suivants sont désactivé à cause d'une inactivité de plus de 90 jours<br />:
           $LockedAccount
        </p>
      </body>
</html>"
 
	Send-MailMessage -smtpserver $smtpserver -from $from -to $to -subject $subject -body $body -bodyasHTML -priority High
 
# ===========================================

 


Installation et mise à jour de logiciels avec PowerShell

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 :

  1. 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).
  2. Définir les variable nécessaires.
  3. 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
  4. 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