Activité Création automatique de comptes d'utilisateur·rice·s en PowerShell
Consigne
Suivez les instructions ci-après et effectuez les tâches demandées.
Situation
Dans le cadre de l’automatisation d’une partie du processus d’onboarding dans votre entreprise, on vous demande de réaliser un script capable de créer des comptes d’utilisateur·rice·s à partir d’un fichier CSV.
Objectifs
À la fin de ce travail, vous devez :
- Connaître les cmdlet
Get-Help
,Get-Command
etGet-Member
- Connaître les cmdlet
ForEach-Object
etWhere-Object
- Connaître la cmdlet
Import-Csv
- Être capable de lire et d’utiliser les données d’un fichier CSV.
- Connaître les cmdlet
New-LocalUser
,Get-LocalUser
,Set-LocalUser
- Connaître les cmdlet
New-LocalGroup
,Get-LocalGroup
,Set-LocalGroup
- Connaître les cmdlet
Add-LocalGroupMember
,Get-LocalGroupMember
- Connaître la cmdlet
ConvertTo-SecureString
Résultat attendu
- Un bref rapport qui décrit votre travail et votre démarche.
- Votre script PowerShell.
Ressources
Documents :
Logiciel :
- Visual Studio Code
Matériel :
- Une machine Windows à l’adresse IP 172.20.##.111.
Mise en route
Administrer Windows avec SSH
Pour administrer à distance une machine Windows, on utilise traditionnellement l’interface graphique à l’aide du Remote Desktop protrocol (RDP). Mais il est également possible de le faire en ligne de commande à l’aide du PowerShell Remoting, ou à travers une connexion SSH.
Le PowerShell Remoting est puissant, mais il est conçu pour être utilisé à l’intérieur d’un domaine ActiveDirectory, et est globalement assez difficile à mettre en œuvre.
Dans cette activité, nous allons donc utiliser directement SSH. Pour cela, ouvrez une fenêtre de terminal sans privilège et tapez la commande ci-après, en remplaçant ## par le numéro de votre réseau, et saisissez votre mode passe lorsque vous y êtes invité.
1
ssh Administrateur@172.20.##.111
Activer l’authentification par clé publique
Sous Windows, l’authentification par clé publique fonctionne de la même manière que sous Linux. Pour un compte utilisateur·rice normal (non-administrateur), il suffit d’ajouter votre clé publique dans le fichier $home.ssh\authorized_keys. Toutefois, si votre compte est membre du groupe Administrateurs (comme c’est le cas du compte Administrateur), vous devez ajouter votre clé dans le fichier ‘C:\ProgramData\ssh\administrators_authorized_keys’.
Lorsque c’est fait, quittez votre session sur le serveur et relancez la commande pour vérifier que vous pouvez vous connecter sans mot de passe.
Utiliser le fichier de configuration de SSH
Comme il n’y a pas d’entrée dans le DNS pour ces machines, nous pouvons utiliser la configuration de ssh associer un nom et une configuration à l’adresse IP. Par défaut, la configuration du ssh pour un·e utilisateur·rice se trouver dans le fichier .ssh/config. Sur le système de votre ordinateur personnel, ouvrez le fichier .ssh/config dans un éditeur de texte et ajoutez y les lignes ci-dessous ; créez le fichier s’il n’existe pas déjà.
1
2
3
Host wks-m123
HostName 172.20.32.111
User Administrateur
De manière générale le format du fichier de configuration ressemble au contenu de la figure ci-dessous. Les noms hostname1 et hostname2 sont des noms définis par l’utilisateur·rice et ne doivent pas nécessairement correspondre au nom de l’hôte cible. Le FQDN ou l’adresse IP de l’hôte cible est spécifié à l’aide de l’option SSH “HostName”. L’option “User” permet de spécifier le nom du compte d’utilisateur·rice par défaut.
On peut trouver une liste de toutes options dans le manuel de ssh. Enfin, l’entrée “Host *” contient les options par défaut, applicable à tous les hôtes.
1
2
3
4
5
6
7
8
9
Host *
SSH_OPTION value
Host hostname1
SSH_OPTION value
SSH_OPTION value
Host hostname2
SSH_OPTION value
Vous pouvez maintenant vous connecter à votre serveur avec la commande ci-dessous. Si vous ne spécifiez pas le nom d’utilisateur·rice, la connexion est effectuée avec le compte Administrateur.
1
ssh wks-m123
Modifier le shell par défaut du service sshd
Par défaut, le shell par défaut du service sshd est cmd.exe. Lorsqu’on se connecte en SSH sur une machine Windows et que l’on souhaite utiliser PowerShell, on doit lancer manuellement la commande PowerShell.
Une autre option est de modifier le shell par défaut dans la configuration du service sshd avec la commande suivante :
1
New-ItemProperty -Path "HKLM:\SOFTWARE\OpenSSH" -Name DefaultShell -Value "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -PropertyType String -Force
Éditer du texte
Éditer du texte en ligne de commande
Par défaut, Windows n’a pas d’éditeur de texte en ligne de commande. L’éditeur de texte standard est “notepade.exe”, dont les fonctionnalités sont particulièrement limitées, nécessite l’environnement graphique de Windows.
Toutefois, nous pouvons également utiliser vim sous Windows à condition de l’installer. Pour cela, connectez-vous en SSH à votre machine Windows avec le compte Administrateur, et lancez la commande suivante :
1
choco install vim
Vous pouvez maintenant utiliser l’éditeur vi comme vous le faites sous Linux.
Éditer du texte à distance avec Visual Studio Code
Une autre option est d’utiliser l’extension Remote SSH de Visual Studio Code, qui permet d’éditer des fichiers qui se trouvent sur une machine distante.
Pour cela, ouvrez une nouvelle fenêtre de VSCode et installez le Remote Development Extension Pack, si ce n’est pas déjà fait. Ce pack d’extension permet, entre autres, de connecter VSCode à des machines distantes via le protocole SSH et de développer à l’aide de conteneurs (container).
Pour vous connecter à une machine distante, pressez les touches Ctrl+Maj+P ou Maj+Commande+P pour afficher la palette des commandes. Tapez “ssh connect” et sélectionnez “Remote-SSH: Connect To Host”. En principe, vous devriez voir le nom que vous avez donné à votre machine Windows dans le fichier *.ssh/config. Si c’est le cas, sélectionnez l’entrée pour ouvrir une connexion. Si ce n’est pas le cas, demandez de l’aide à votre enseignant.
Si la connexion a été effectuée avec succès, vous devriez voir le nom de votre machine dans la zone verte en bas à gauche comme sur la figure ci-dessus. Si c’est le cas, VSCode est connecté à votre machine et si vous cliquez “Open Folder…”, vous pouvez parcourir le système de fichier de cette machine. Vous pouvez également ouvrir une fenêtre de terminal intégré à partir du menu ou en utilisant la palette de commande (voir plus haut). Avec la palette de commande, commencez à taper “Create New Terminal” et sélectionnez l’entrée “Terminal: Create New Terminal” lorsqu’elle apparaît. Vous pouvez maintenant manipuler votre système de fichier avec PowerShell.
On peut, par exemple, utiliser cette fenêtre de terminal pour créer une structure de répertoire $home\test\m122\powershell, avec la commande ci-dessous. Cette commande est l’équivalent PowerShell de la commande Bash mkdir -p ~/test/m122/powershell
. Observez le nom des commandes et les résultats affichés, nous y reviendrons.
1
New-Item -ItemType "directory" -Path "$home\test\m122\powershell"
Pour afficher le contenu d’un répertoire, on utilise la commande Get-ChildItem
. Par exemple, on peut afficher le contenu du répertoire courant, avec la commande suivante :
1
Get-ChildItem -Path "./"
Pour supprimer un répertoire ou un fichier, on utilise la commande Remove-Item
, et la commande Move-Item
pour déplacer un fichier ou un répertoire. Par exemple, pour supprimer le répertoire test, vous pouvez lancer la commande ci-après. L’option -Recurse
indique à la commande qu’elle doit supprimer récursivement le contenu du répertoire.
1
Remove-Item -Recurse -Path "$home\test"
Introduction à PowerShell
Qu’est-ce que PowerShell ?
Sous Windows, le shell en ligne de commande historique est le programme CMD.EXE. Ce shell est un descendant direct du programme COMMAND.COM de MSDOS.Même si ce shell est toujours utilisé, on lui préfère aujourd’hui un shell plus moderne apparu plus ou moins en même temps que Windows Vista au milieu des années 2000.
Comme nous l’avons vu, un shell est une interface utilisateur pour les fonctionnalités du système d’exploitation. Comme il s’agit d’un programme qui s’exécute dans l’espace utilisateur (par opposition à l’espace noyau), il dépend de l’interface de programmation d’application (API) du système.
Dans le cas du Bash et des autres shells des systèmes POSIX, l’API est essentiellement la bibliothèque standard du C (C standard library). Dans le cas, du CMD.EXE, l’API est Windows API, également connu sous le nom Win32 API. Comme pour l’API des systèmes POSIX, cette API a été conçue pour le langage C.
L’API sur laquelle repose PowerShell est le .NET Framewwork pour les versions du shell intégré à Winwows, et le .NET Core pour les versions multiplateformes. Ces API ne sont pas directement des API de Windows, mais d’un environnement d’exécution comparable à la JVM (Java Virtual Machine), appelé CLR (Common Langage Runtime).
Flux de caractères et flux d’objets
Les shells POSIX et le cmd.exe sont essentiellement basés sur des flux de caractères (stdio) et des variables d’environnements dont le contenu est du texte.
En PowerShell en revanche, même si les commandes et les arguments sont toujours saisis sous forme de texte, les flux d’entrée et de sortie des commandes (appelées cmdlet) ne véhiculent pas du texte, mais des objets, et les variables sont typées. Par exemple, lorsque vous avez créé votre répertoire test, la commande a affiché le résultat suivant :
1
2
3
4
5
Directory: C:\Users\Administrateur\test\m122
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 18.06.2024 10:28 powershell
Il s’agit de la représentation par défaut de l’objet de type System.IO.DirectoryInfo
renvoyé par la commande New-Item
. Voyons cela de plus près.
Si vous ne l’avez pas déjà fait, commencez par supprimer le répertoire test avec la commande Remove-Item
. Puis lancez la commande suivante :
1
$myTestDirectory = New-Item -ItemType "directory" -Path "$home\test\m122\powershell"
Cette fois la commande n’affiche rien, car nous avons affecté le résultat à la variable $myTestDirectory
. Remarquez au passage, de quelle manière sont définies les variables en PowerShell. Le nom d’une variable commence toujours par un $
; contrairement au Bash, le signe $
fait partie du nom, et il peut y avoir des caractères blancs avant et après le signe égal.
Essayez maintenant, tapez la ligne ci-dessous, pressez la touche Entrée, et observez ce qu’il se passe.
1
$myTestDirectory
Le shell affiche de nouveau une représentation par défaut de l’objet. Le principe est le suivant : le shell interprète la ligne de commande. Si la ligne de commande contient une expression (une commande qui renvoie un objet, une variable, une opération, ou simplement une constante), le shell écrit la valeur de l’expression dans la sortie standard. Si la sortie standard ne supporte pas un flux d’objets (p. ex. le terminal), le shell produit une représentation par défaut de la valeur de l’expression, sous forme de texte.
Voyons ce qu’il se passe avec la commande suivante :
1
$myTestDirectory.GetType()
1
2
3
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True False DirectoryInfo System.IO.FileSystemInfo
La méthode GetType()
est une fonction qui renvoie le type de la variable. Le shell affiche donc une représentation par défaut de l’objet de type DirectoryInfo
qui représente ce type (en PowerShell, le type d’une variable correspond au type de sa valeur).
Essayons encore la commande suivante :
1
$myTestDirectory.FullName
1
C:\Users\Administrateur\test\m122\powershell
Cette fois, la commande utilise la propriété FullName
de l’objet qui représente le répertoire pour obtenir une chaine de caractère qui contient le nom complet du type.
Utilisation des tubes
Puisque PowerShell écrit des objets dans la sortie standard, on peut utiliser un tube (pipe |
) pour passer un objet à une autre commande. Par exemple, lancez la commande suivante :
1
$myTestDirectory | Get-Member -Type Properties
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
TypeName: System.IO.DirectoryInfo
Name MemberType Definition
---- ---------- ----------
Target AliasProperty Target = LinkTarget
LinkType CodeProperty System.String LinkType{get=GetLinkType;}
Mode CodeProperty System.String Mode{get=Mode;}
ModeWithoutHardLink CodeProperty System.String ModeWithoutHardLink{get=ModeWithoutHardLink;}
ResolvedTarget CodeProperty System.String ResolvedTarget{get=ResolvedTarget;}
PSChildName NoteProperty string PSChildName=powershell
PSDrive NoteProperty PSDriveInfo PSDrive=C
PSIsContainer NoteProperty bool PSIsContainer=True
PSParentPath NoteProperty string PSParentPath=Microsoft.PowerShell.Core\FileSystem::C:\Users\Administrateur\test\m122
PSPath NoteProperty string PSPath=Microsoft.PowerShell.Core\FileSystem::C:\Users\Administrateur\test\m122\powershell
PSProvider NoteProperty ProviderInfo PSProvider=Microsoft.PowerShell.Core\FileSystem
Attributes Property System.IO.FileAttributes Attributes {get;set;}
CreationTime Property datetime CreationTime {get;set;}
CreationTimeUtc Property datetime CreationTimeUtc {get;set;}
Exists Property bool Exists {get;}
Extension Property string Extension {get;}
FullName Property string FullName {get;}
LastAccessTime Property datetime LastAccessTime {get;set;}
LastAccessTimeUtc Property datetime LastAccessTimeUtc {get;set;}
LastWriteTime Property datetime LastWriteTime {get;set;}
LastWriteTimeUtc Property datetime LastWriteTimeUtc {get;set;}
LinkTarget Property string LinkTarget {get;}
Name Property string Name {get;}
Parent Property System.IO.DirectoryInfo Parent {get;}
Root Property System.IO.DirectoryInfo Root {get;}
UnixFileMode Property System.IO.UnixFileMode UnixFileMode {get;set;}
BaseName ScriptProperty System.Object BaseName {get=$this.Name;}
La commande Get-Member
renvoie tous les membres (méthodes, propriété, etc.) d’un objet. L’option -Type
permet de limiter l’affichage à un type de membre particulier (ici, uniquement les propriétés).
Liste et itérateur
Pour créer un tableau de valeur en PowerShell, il suffit d’écrire une série de valeurs séparées par des virgules. Par exemple, la ligne suivante écrit une List d’objet dans la sortie standard.
1
3, 10, 22, 11
Contrairement à Java, les éléments de la liste peuvent être de différents types. Par exemple :
1
145, "Une chaîne.", 3.14, "Une autre chaîne.", $true
En PowerShell, pour itérer les éléments d’un tableau, on utilise rarement une structure de boucle « for » comme en Java ou en Bash. On préfère utiliser la cmdlet ForEach-Object
. Par exemple, lancez la commande ci-après pour afficher le type de chaque élément de cette liste. La commande qui se trouve dans l’accolade est exécutée une fois pour chaque élément de la liste. La variable $_
représente l’élément courant.
1
145, "Une chaîne.", 3.14, "Une autre chaîne.", $true | ForEach-Object { $_.GetType().FullName }
1
2
3
4
5
System.Int32
System.String
System.Double
System.String
System.Boolean
Pour créer une liste de valeurs contiguës, par exemple, une liste d’entiers de 5 à 20, on peut utiliser la syntaxe suivante :
1
5..20
La cmdlet Where-Object
de filter les éléments d’une liste. Par exemple, lancez la commande suivante pour produire une liste de tous les multiples de 10 entre 1 et 100.
1
1..100 | Where-Object { $_ % 10 -eq 0 }
Entre les accolades de la cmdlet Where-Object
, vous reconnaissez une expression booléenne, composée d’une opération avec l’opérateur %
(modulo) et d’une comparaison avec l’opérateur -eq
pour tester l’égalité. La commande renvoie dans la sortie standard toutes les valeurs divisibles par 10.
Comme toutes les commandes précédentes sont des expressions, vous pouvez affecter le résultat de ces commandes à des variables. Par exemple :
1
2
$a1 = 145, "Une chaîne.", 3.14, "Une autre chaîne.", $true
$a2 = 1..100 | Where-Object { $_ % 10 -eq 0 }
Utiliser une collection renvoyée par une commande
Bon nombre de cmdlet renvoient des collections d’objets. C’est le cas par exemple de la valeur renvoyée par la commande Get-ChildItem
:
1
2
$test = Get-ChildItem -Path "$home"
$test.GetType()
1
2
3
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
La méthode GetType()
nous informe qu’il s’agit d’un tableau d’objet.
Lorsque l’on doit traiter un tableau d’objet, on peut passer ce tableau la cmdlet ForEach-Objet
pour effectuer une action avec chaque élément. Par exemple, lancez la commande ci-après pour afficher le nom complet de chaque élément du répertoire $home dans la fenêtre du terminal avec la commande Write-Host
.
1
Get-ChildItem $home | ForEach-Object { Write-Host $_.FullName }
On peut également utiliser la commande Where-Object
pour filtrer le contenu d’une collection avant de l’itérer. Par exemple :
1
Get-ChildItem $home | Where-Object { $_.Name.StartsWith("D") } | ForEach-Object { Write-Host $_.FullName }
Aggrégation de valeurs
Pour finir, voyons encore une fonctionnalité de la commande ForEach-Objet
. Avec trois blocs cette commande permet d’aggréger les objets d’une façon ou d’une autre. Par exemple:
1
1..10 | ForEach-Object { $sum = 0 } { $sum += $_ } { Write-Output $sum }
Le premier bloc est exécuté une seule fois avant de commencer l’itération, le second bloc est exécuté une fois pour chaque élément de la collection, et le troisième bloc est exéctuté une fois, à la fin de l’itération.
En principe, une commande Powershell s’écrit sur une seule ligne. Pour écrire une commande sur plusieurs ligne on peut utiliser le caractère backtick (`) pour indiquer que la commande se poursuit sur la ligne suivante. Par exemple:
1
2
3
4
5
6
Get-ChildItem $home `
| Where-Object { $_.Name -like "D*" } `
| ForEach-Object { Write-Output $_.Name.ToUpper() } `
| ForEach-Object { $fileNames = "" } `
{ $fileNames += " " + $_ } `
{ "Fichier: $($filenames.Trim())" }
Informations complémentaires
Après avoir discuté avec votre supérieur, vous avez obtenu les informations ci-après.
- La première ligne du fichier contient les en-têtes
- Le caractère de séparation est le point-virgule (
;
). - Les champs Group1, Group2, Group3 et Group4 sont les noms des groupes supplémentaires de l’utilisateur·rice. Ces champs sont optionnels et peuvent donc être vides.
- Si un groupe n’existe pas, il doit être créé.
- Si un compte existe déjà, le script ne doit pas échouer et le compte ne doit pas être modifié.
- Le script doit écrire les informations ci-après dans stderr :
- la liste des comptes créés,
- la liste des comptes qui n’ont pas été modifiés (s’il y en a),
- la liste des groupes crée (s’il y en a).
Votre supérieur vous demande encore de regarder les cmdlet suivantes :
Import-Csv
: lit un fichier CSV et renvoie une collection d’objets. Chaque objet représente une ligne du fichier. Si le fichier a des entêtes, les noms des entêtes sont utilisés pour les noms des propriétés.ConvertTo-SecureString
: convertir une chaîne de caractère en une chaîne sécurisée requise par certaines cmdlets.
Tâches
Réaliser le script
On vous demande de réaliser un script en bas nommé Add-Users qui s’utilise de la manière suivante : addusers <chemin du fichier csv>
Si le script est exécuté sans paramètre ou si le premier paramètre est -Help, il doit afficher la syntaxe de la commande et le format du fichier CSV, dans la fenêtre du terminal.
Si le fichier passé en paramètre n’existe pas, le script doit afficher un message d’information dans la sortie d’erreurs.
Si le script est lancé sans privilège, il doit se terminer immédiatement avec un message d’information dans la sortie d’erreurs.
N’hésitez pas à interroger un chatbot pour vous aider, mais il est important de comprendre ce qu’il vous propose et d’expérimenter par vous-même pour bien comprendre ce que fait chaque partie du script.