Boucles ForEach PowerShell : Types et bonnes pratiques

Lorsque vous commencez à écrire des scripts PowerShell, vous arriverez inévitablement à un moment où vous devrez traiter plusieurs éléments d’une collection. C’est à ce moment que vous devrez vous plonger dans les boucles foreach de PowerShell et apprendre ce qu’elles sont.

Presque tous les langages de programmation ont une structure appelée boucles ; PowerShell ne fait pas exception. L’un des types de boucles les plus populaires dans PowerShell est la boucle foreach. Dans sa forme la plus basique, une boucle foreach lit une collection entière d’éléments et pour chaque élément, exécute un certain type de code.

L’un des aspects les plus déroutants de la boucle foreach de PowerShell pour les débutants est toutes les options dont vous disposez. Il n’y a pas seulement une façon de traiter chaque élément d’une collection ; il y en a trois !

Dans cet article, vous allez apprendre comment chaque type de boucle foreach fonctionne et quand utiliser l’un plutôt que l’autre. À la fin de cet article, vous aurez une bonne compréhension de chaque type de boucle foreach.

Principes de base de la boucle ForEach PowerShell

l’un des types de boucles les plus couramment utilisés dans PowerShell est le type de boucle foreach. Une boucle foreach lit un ensemble d’objets (itére) et se termine lorsqu’elle a terminé avec le dernier. La collection d’objets qui est lue est généralement représentée par un tableau ou une table de hachage.

REMARQUE : Le terme itération est un terme de programmation qui fait référence à chaque exécution d’une boucle. Chaque fois qu’une boucle effectue un cycle, on parle d’une itération. L’action d’exécuter une boucle sur un ensemble d’objets est couramment appelée itération sur l’ensemble.

Peut-être avez-vous besoin de créer un fichier texte réparti dans plusieurs dossiers sur un système de fichiers. Disons que les chemins des dossiers sont C:\Dossier, C:\Program Files\Dossier2 et C:\Dossier3. Sans boucle, nous devrions faire référence à la commande Add-Content trois fois.

Add-Content -Path 'C:\Folder\textfile.txt' -Value 'This is the content of the file'
Add-Content -Path 'C:\Program Files\Folder2\textfile.txt' -Value 'This is the content of the file'
Add-Content -Path 'C:\Folder2\textfile.txt' -Value 'This is the content of the file'

Quelle est la seule différence entre chacune de ces commandes ? C’est la valeur de Path. La valeur de Path est la seule valeur qui change parmi chacune d’elles.

Vous dupliquez beaucoup de code. Vous perdez du temps en tapant et vous vous exposez à des problèmes à l’avenir. Au lieu de cela, vous devriez créer un « ensemble » qui inclut simplement tous les éléments qui changent. Pour cet exemple, nous utiliserons un tableau.

$folders = @('C:\Folder','C:\Program Files\Folder2','C:\Folder3')

Maintenant, vous avez chacun de ces chemins stockés dans un seul « ensemble » ou tableau. Vous êtes maintenant prêt à utiliser une boucle pour itérer sur chacun de ces éléments. Avant de le faire, cependant, c’est le bon moment pour mentionner un sujet qui pose souvent problème aux débutants en PowerShell. Contrairement à d’autres types de boucles, les boucles foreach ne sont pas toutes identiques.

Il existe techniquement trois types de boucles foreach en PowerShell. Bien que chacune soit similaire à utiliser, il est important de comprendre la différence.

L’instruction foreach

Le premier type de boucle foreach est une instruction. foreach est un mot clé interne de PowerShell qui n’est ni une commande ni une fonction. L’instruction foreach est toujours utilisée sous la forme : foreach ($i in $array).

En utilisant l’exemple ci-dessus, la variable $i représente l’itérateur ou la valeur de chaque élément dans $path à mesure qu’il itère sur chaque élément du tableau.

Notez que la variable de l’itérateur n’a pas besoin d’être $i. Le nom de la variable peut être n’importe quoi.

Dans l’exemple ci-dessous, vous pouvez accomplir la même tâche de répétition de la référence Add-Content en faisant ceci :

# Créez un tableau de dossiers
$folders = @('C:\Folder','C:\Program Files\Folder2','C:\Folder3')

# Effectuez une itération pour créer le même fichier dans chaque dossier
foreach ($i in $folders) {
    Add-Content -Path "$i\SampleFile.txt" -Value "This is the content of the file"
}

La déclaration foreach est connue comme une alternative plus rapide à l’utilisation de la cmdlet ForEach-Object.

La cmdlet ForEach-Object

Si foreach est une déclaration et ne peut être utilisée que d’une seule manière, ForEach-Object est une cmdlet avec des paramètres qui peuvent être utilisés de différentes manières. Comme la déclaration foreach, la cmdlet ForEach-Object peut itérer sur un ensemble d’objets. Cette fois-ci, elle passe cet ensemble d’objets et l’action à effectuer sur chaque objet en tant que paramètre, comme indiqué ci-dessous.

$folders = @('C:\Folder','C:\Program Files\Folder2','C:\Folder3')
$folders | ForEach-Object (Add-Content -Path "$_\SampleFile.txt" -Value "This is the content of the file")

Note : Pour rendre les choses confuses, la cmdlet ForEach-Object a un alias appelé foreach. Selon la façon dont le terme « foreach » est appelé, soit la déclaration foreach est exécutée, soit ForEach-Object s’exécute.

A good way to differentiate these situations is to notice if the term “foreach” is followed by ($someVariable in $someSet). Otherwise, in any other context, the code author is probably using the alias for ForEach-Object.

La méthode foreach()

L’un des plus récents boucles foreach a été introduit dans PowerShell v4 et s’appelle la méthode foreach(). Cette méthode existe sur un objet tableau ou collection. La méthode foreach() possède un paramètre de bloc de script standard qui contient les actions à effectuer à chaque itération, tout comme les autres.

$folders = @('C:\Folder','C:\Program Files\Folder2','C:\Folder3')
$folders.ForEach({
	Add-Content -Path "$_\SampleFile.txt" -Value "This is the content of the file"
})

La différence la plus significative avec la méthode foreach() est la façon dont elle fonctionne en interne.

L’utilisation de la méthode foreach() est considérablement plus rapide, et cela se remarque notamment sur de grands ensembles de données. Il est recommandé d’utiliser cette méthode plutôt que les autres si possible.

Mini-Projet : Parcourir un ensemble de noms de serveurs

L’une des utilisations les plus courantes d’une boucle en PowerShell est la lecture d’un ensemble de serveurs à partir d’une source quelconque et d’effectuer une action sur chacun d’entre eux. Pour notre premier mini-projet, construisons un code qui nous permettra de lire des noms de serveurs (un par ligne) à partir d’un fichier texte et de pinger chaque serveur pour déterminer s’ils sont en ligne ou non.

List of server names in a text file

Pour ce flux de travail, quel type de boucle pensez-vous qu’il fonctionnerait le mieux ?

Remarquez que j’ai mentionné le mot « ensemble », comme dans « ensemble de serveurs ». C’est un indice juste là. Vous avez déjà un nombre spécifié de noms de serveurs, et vous souhaitez effectuer une action sur chacun d’entre eux. Cela ressemble à une excellente occasion d’essayer la boucle PowerShell la plus couramment utilisée ; la boucle foreach.

La première tâche consiste à créer un ensemble de noms de serveurs dans le code. La manière la plus courante de le faire est de créer un tableau. Heureusement pour nous, Get-Content retourne par défaut un tableau dont chaque élément est représenté par une seule ligne dans le fichier texte.

Tout d’abord, créez un tableau avec tous les noms de mes serveurs et appelez-le $servers.

$servers = Get-Content .\servers.txt

Maintenant que vous avez créé le tableau, vous devez maintenant vérifier si chaque serveur est en ligne ou non. Une excellente cmdlet pour tester la connexion d’un serveur s’appelle Test-Connection. Cette cmdlet effectue quelques tests de connexion sur un ordinateur pour voir s’il est en ligne ou non.

En exécutant Test-Connection à l’intérieur d’une boucle foreach pour lire chaque ligne du fichier, vous pouvez passer chaque nom de serveur représenté par la variable $server à Test-Connection. C’est ainsi que PowerShell parcourt un fichier texte. Vous pouvez voir un exemple de fonctionnement ci-dessous.

foreach ($server in $servers) {
	try {
		$null = Test-Connection -ComputerName $server -Count 1 -ErrorAction STOP
		Write-Output "$server - OK"
	}
	catch {
		Write-Output "$server - $($_.Exception.Message)"
	}
}

Lorsque la boucle foreach est exécutée, elle ressemblera alors à ceci :

Looping Test-Connection through a list of server names

Vous avez maintenant réussi à tester la connexion d’un fichier texte rempli de noms de serveurs ! À ce stade, vous pouvez ajouter ou supprimer des noms de serveurs dans le fichier texte à volonté sans modifier le code.

Vous avez réussi à mettre en pratique ce que nous prêchons, la méthode DRY.

Exemples de boucle forEach en PowerShell

Jetons un coup d’œil à quelques exemples d’utilisation de la boucle ForEach. Ces exemples sont basés sur des cas d’utilisation réels qui illustrent le concept et que vous pouvez modifier selon vos besoins.

Exemple 1 : Création d’un fichier dans chaque sous-dossier d’un répertoire à l’aide de l’instruction ForEach

Cet exemple illustre l’utilisation courante de la boucle PowerShell foreach pour parcourir les dossiers d’un répertoire.

Supposons qu’il y ait dix sous-dossiers à l’intérieur du dossier C:\ARCHIVE_VOLUMES. Chaque sous-dossier représente un volume d’archive sauvegardé quotidiennement. Après chaque sauvegarde, un fichier nommé BackupState.txt est créé à l’intérieur de chaque dossier, contenant la date de la sauvegarde.

Vous pouvez voir un exemple de ce à quoi cela pourrait ressembler ci-dessous.

Sub-directories under C:\ARCHIVE_VOLUMES

Le script ci-dessous effectue trois actions :

  • obtient la liste de tous les sous-dossiers à l’intérieur de C:\ARCHIVE_VOLUMES
  • parcourt chaque dossier
  • crée un fichier texte nommé BackupState.txt contenant la date et l’heure actuelles comme valeur

L’exemple ci-dessous utilise l’instruction foreach.

# Définir le dossier de niveau supérieur
$TOP_FOLDER = "C:\ARCHIVE_VOLUMES"

# Obtenir tous les sous-dossiers de manière récursive
$Child_Folders = Get-ChildItem -Path $TOP_FOLDER -Recurse | Where-Object { $_.PSIsContainer -eq $true }

# Créer un fichier texte dans chaque sous-dossier et y ajouter la date/heure actuelles en tant que valeur.
foreach ($foldername in $Child_Folders.FullName) {
   (get-date -Format G) | Out-File -FilePath "$($foldername)\BackupState.txt" -Force
}

En utilisant la commande Get-ChildItem, vous pouvez confirmer que les fichiers ont été créés ou mis à jour à l’intérieur de chacun des sous-dossiers.

Get-ChildItem -Recurse -Path C:\ARCHIVE_VOLUMES -Include backupstate.txt | Select-Object Fullname,CreationTime,LastWriteTime,Length

La capture d’écran ci-dessous montre la sortie du script affichant tous les fichiers BackupState.txt trouvés dans chaque sous-répertoire.

A text file is created in each sub-directory

Exemple 2 : Lire le contenu de chaque fichier texte dans les sous-répertoires

Ensuite, pour démontrer l’utilisation de la boucle foreach de PowerShell sur chaque fichier d’un répertoire, le script ci-dessous lira chaque fichier BackupState.txt créé dans l’exemple 1.

  • Rechercher de manière récursive tous les fichiers BackupState.txt à l’intérieur de chaque sous-répertoire.
  • Utiliser l’instruction foreach pour lire chaque fichier texte et obtenir la valeur de « dernière heure de sauvegarde ».
  • Afficher le résultat à l’écran.
## Trouver tous les fichiers BackupState.txt dans C:\ARCHIVE_VOLUMES
$files = Get-ChildItem -Recurse -Path C:\ARCHIVE_VOLUMES -Include 'BackupState.txt' | Select-Object DirectoryName,FullName

## Lire le contenu de chaque fichier
foreach ($file in $files) {
    Write-Output ("$($file.DirectoryName) last backup time - " + (Get-Content $file.FullName))
}

Une fois que le script est exécuté dans votre session PowerShell, vous devriez voir une sortie similaire à la capture d’écran ci-dessous. Cela montre que PowerShell boucle à travers les fichiers, lit le contenu et affiche la sortie.

Using foreach to loop through files and read its contents.

Exemple 3 : Obtenir les services et les démarrer à l’aide de la cmdlet ForEach-Object

Les administrateurs système ont souvent besoin de connaître l’état des services et de déclencher un flux de travail manuel ou automatique pour remédier à tout service en échec. Regardons le script d’exemple utilisant la cmdlet ForEach-Object.

Pour cet exemple, le script ci-dessous fera ce qui suit :

  • Obtenir une liste de services configurés pour un démarrage automatique mais qui ne sont pas actuellement en cours d’exécution.
  • Ensuite, les éléments de la liste sont envoyés à la cmdlet ForEach-Object pour essayer de démarrer chaque service.
  • A message of either success or failed is displayed depending on the result of the Start-Service command.
## Obtenir une liste des services automatiques arrêtés.
$services = Get-Service | Where-Object {$.StartType -eq 'Automatic' -and $.Status -ne 'Running'}

## Passer chaque objet de service dans le pipeline et les traiter avec la cmdlet Foreach-Object
$services | ForEach-Object {
    try {
        Write-Host "Attempting to start '$($.DisplayName)'"
        Start-Service -Name $.Name -ErrorAction STOP
        Write-Host "SUCCESS: '$($.DisplayName)' has been started"
    } catch {
        Write-output "FAILED: $($.exception.message)"
    }
}

Lorsque le script est exécuté, il ressemblera à la capture d’écran de sortie ci-dessous. Comme vous pouvez le voir, chaque service a reçu une commande de démarrage. Certains ont été démarrés avec succès, tandis que d’autres ont échoué à démarrer.

Using the ForEach-Object loop to start services

Exemple 4 : Lecture de données à partir d’un fichier CSV à l’aide de la méthode ForEach()

L’utilisation de données à partir de fichiers CSV est populaire parmi les administrateurs système. Placer des enregistrements à l’intérieur d’un fichier CSV facilite l’exécution d’opérations groupées en utilisant la combinaison Import-CSV  et ForEach. Cette combinaison est couramment utilisée pour créer plusieurs utilisateurs dans Active Directory.

Dans cet exemple suivant, on suppose que vous avez un fichier CSV avec deux colonnes – Prénom et Nom de famille. Ce fichier CSV doit ensuite être rempli avec les noms des nouveaux utilisateurs à créer. Le fichier CSV ressemblerait à quelque chose comme ci-dessous.

"Firstname","Lastname"
"Grimm","Reaper"
"Hell","Boy"
"Rick","Rude"

Maintenant, passons au prochain bloc de script. Tout d’abord, importez le fichier CSV en transmettant le chemin du contenu à la cmdlet Import-CSV. Ensuite, à l’aide de la méthode foreach(), parcourez les noms et créez de nouveaux utilisateurs dans Active Directory.

# Importez la liste des prénoms et noms de famille à partir du fichier CSV
$newUsers = Import-Csv -Path .\Employees.csv

Add-Type -AssemblyName System.Web

# Traitez la liste
$newUsers.foreach(
    {
        # Générez un mot de passe aléatoire
        $password = [System.Web.Security.Membership]::GeneratePassword((Get-Random -Minimum 20 -Maximum 32), 3)
        $secPw = ConvertTo-SecureString -String $password -AsPlainText -Force

        # Formulez un nom d'utilisateur
        $userName = '{0}{1}' -f $_.FirstName.Substring(0, 1), $_.LastName

        # Construisez les nouvelles attributs utilisateur
        $NewUserParameters = @{
            GivenName       = $_.FirstName
            Surname         = $_.LastName
            Name            = $userName
            AccountPassword = $secPw
        }

        try {
            New-AdUser @NewUserParameters -ErrorAction Stop
            Write-Output "User '$($userName)' has been created."
        }
        catch {
            Write-Output $_.Exception.Message
        }
    }
)

Lors de l’exécution, vous devriez avoir créé un utilisateur AD pour chaque ligne du fichier CSV !

Résumé

La logique derrière la boucle foreach en PowerShell n’est pas différente de celle des autres langages de programmation. Elle change uniquement avec la manière dont elle est utilisée et quelle variation de la boucle foreach est choisie pour une tâche spécifique.

Dans cet article, vous avez appris les différents types de boucles foreach disponibles en PowerShell, et ce qu’il faut prendre en compte pour choisir lequel utiliser. Vous avez également vu les trois types de boucles foreach en action à l’aide de différents scénarios d’exemple.

Pour aller plus loin

Source:
https://adamtheautomator.com/powershell-foreach/