Faciliter sa migration de fileserver DFS avec Powershell

Ayant éprouvé ces scripts lors d'une migration d'un serveur de fichiers, je vais les partager dans ce billet. Le but n'est pas d'automatiser complètement la migration mais de faciliter le travail et de limiter les risques d'erreurs humaines.

A noter qu'il est important de vérifier le bon fonctionnement des scripts sur votre environnement : en effet, il est possible que les espaces dans les noms de chemin pour les scripts Robocopy puissent causer des dysfonctionnements. Il est également important que les noms de répertoires sur le serveur correspondent au nom des répertoires DFS. Il peut donc être nécessaire d'adapter le code pour qu'il fonctionne correctement.

Ce snippet appelle Robocopy pour faire le transfert des données.

$servername = "fs001.dundermifflin.inc"
cd c:\mig\robocopy
$FoldersToDo = Get-Item \\$ServerName\C$\shares\* | select name
foreach($Folder in $FoldersToDo){
    $FolderName = $Folder.Name
	start-process "robocopy.exe" -ArgumentList "/MIR /COPYALL /ZB /W:0 /R:0 /MT:16 \\$ServerName\C$\shares\$FolderName C:\shares\$FolderName /log:robocopy_$ServerName_$FolderName.log /v" -wait
}

Ce script va s'occuper de la partie DFS ; il va ajouter le nouveau serveur de fichier en tant que répertoire cible pour chaque répertoire DFS qui contient un lien vers l'ancien serveur. Ce nouveau lien sera en désactivé, ainsi la migration peut être préparée en amont.

Param([Parameter(Mandatory=$true)][string] $OldServer, [Parameter(Mandatory=$true)][string] $NewServer, [Parameter(Mandatory=$true)][string] $Namespace)
$fNameSpace = "$Namespace\*"
$folderlist = Get-DfsnFolder $fNameSpace | foreach { (Get-DfsnFolderTarget $_.Path).TargetPath } | Where-Object { $_ -like "\\$OldServer*" }
foreach($folder in $folderlist){
	$folderName = $folder.SubString($OldServer.Length+3,$folder.Length-$OldServer.Length-3)
    echo $folderName
	New-DfsnFolderTarget -Path "$Namespace\$folderName" -TargetPath "\\$NewServer\$folderName" -State Offline
}

Par exemple, si l'on souhaite migrer de fs001 vers fs002 tous les liens DFS du namespace Documents, le script pourra être appelé de cette manière :

.\ImportationDFS-NewServer.ps1 -OldServer fs001 -NewServer fs002 -Namespace \\dundermifflin.inc\Documents

Ainsi, le jour de la migration, en jouant ce script on réalise la bascule vers le nouveau serveur fs002 puisque celui-ci va activer les liens DFS vers le nouveau serveur en désactivant ceux de l'ancien serveur.

Param([Parameter(Mandatory=$true)][string] $OldServer, [Parameter(Mandatory=$true)][string] $NewServer, [Parameter(Mandatory=$true)][string] $Namespace)
$fNameSpace = "$Namespace\*"
$folderlist = Get-DfsnFolder $fNameSpace | foreach { (Get-DfsnFolderTarget $_.Path).TargetPath } | Where-Object { $_ -like "\\$OldServer*" }
foreach($folder in $folderlist){
	$folderName = $folder.SubString($OldServer.Length+3,$folder.Length-$OldServer.Length-3)
    echo $folderName
	Set-DfsnFolderTarget -Path "$Namespace\$folderName" -TargetPath "\\$NewServer\$folderName" -State Online
    Set-DfsnFolderTarget -Path "$Namespace\$folderName" -TargetPath "\\$OldServer\$folderName" -State Offline
}

Ce script prend les mêmes paramètres que le précédent. En fonction du type de migration effectuée, il peut-être nécessaire de jouer le script plusieurs fois : si le nouveau serveur remplace et conserve le nom de l'ancien, il y a de grandes chances qu'à un moment le nouveau serveur doive être renommé. Dans ce cas, Microsoft conseille de supprimer le répertoire de destination dans DFS avant de renommer le serveur ; cependant si un répertoire DFS n'a plus de répertoire de destination, alors ce répertoire DFS est supprimé (en conservant bien sûr, les données sur le disque du serveur en question). Il sera donc nécessaire de procéder en plusieurs étapes et en jouant le script plusieurs fois afin de toujours conserver un lien DFS, qu'il soit actif ou non.

Par exemple, si l'on considère le serveur fs001 et le serveur fs001new, en admettant que l'on renomme le serveur fs001 en fs001old une fois la migration effectuée, cela nous donne ceci :

Etape #Serveurs contenus dans les liens DFSDétails
1fs001Etat pré-migration
2fs001 : actif - fs001new : inactifAjout de fs001new
3fs001 : inactif - fs001new : actifActivation de fs001new
Désactivation de fs001
4fs001new : actifRetrait de fs001
Renommage en fs001old
5fs001new : actif - fs001old : inactifAjout de fs001old
6fs001old : inactifRetrait de fs001new
Renommage en fs001
7fs001 : actif - fs001old : inactifAjout de fs001
8fs001Etat post-migration

Il n'y a pas de dysfonctionnement grave à ne pas retirer le serveur des répertoires cibles avant qu'il soit renommé. Il faudra cependant attendre une petite trentaine de minutes que côté AD tous les liens DFS soit remis en état par le système car bien qu'il s'agisse toujours de fs001, l'objet dans l'annuaire n'est pas le même. Il ne reste ensuite plus qu'à valider le bon fonctionnement.

Installation de PowerShell V7 et OpenSSH sous Windows Server 2016

Si Microsoft s'ouvre de plus en plus vers le monde GNU/Linux, cet effort se retrouve principalement sur Windows Server 2019. Cependant, il est tout à fait possible d'installer OpenSSH sur Windows Server 2016 et ainsi d'administrer son serveur à distance en utilisant la dernière version de Powershell, ou encore de pouvoir faire du transfert de fichier via SSH comme sur un hôte GNU/Linux.

Bien que Microsoft indique que les binaires OpenSSH ne soient pas suffisamment stables pour un usage en production, je n'ai rencontré aucun dysfonctionnement pénalisant avec dans mon environnement de test et j'ai intégré OpenSSH à ma dernière révision du master. Chaque environnement étant différent, il reste important de tester avant une intégration en production.

Les sources de Powershell V7 se trouvent sur le GitHub de Powershell accessible à ce lien. Les sources d'installation d'OpenSSH pour Windows se trouvent sur ce GitHub.

L'installation de Powershell V7 se fait simplement en exécutant le setup ; cependant, l'implémentation d'OpenSSH sous Windows semble rencontrer des dysfonctionnements lorsqu'elle doit gérer des répertoires avec des espaces. Je vous conseille donc d'installer PS dans un autre répertoire que celui proposé par défaut ; dans cet exemple je l'ai installé directement à la racine du C:\.

L'installation d'OpenSSH se fait également en dézippant l'archive. Pour les mêmes raisons que précédemment, je vous conseille de déployer les binaires dans un répertoire sans espace (et de manière générale, de bannir les espaces des noms de fichiers !).

Vous pouvez désormais ajouter les deux répertoires à la variable système PATH, comme ceci :

L'installation d'OpenSSH se fait en Powershell, avec une élévation en administrateur :

cd C:\OpenSSH-Win64
.\install-sshd.ps1

Ce script va créer deux services que l'on peut retrouver dans la console. Par défaut, les services ne sont pas démarrés (c'est pourquoi je les ai intégrés à mon master, ainsi je peux les activer si il y a besoin, sans cela ils restent "dormants" sans avoir d'impact sur le système). Il faut donc les démarrer, et en fonction du besoin, activer le démarrage automatique.

Ensuite, il convient de générer une clef SSH. Toujours dans le répertoire d'OpenSSH :

ssh-keygen.exe -A

Le fichier de configuration est identique à celui que l'on peut retrouver sur un système GNU/Linux utilisant OpenSSH. Il suffit de copier le fichier sshd_config_default vers un nouveau fichier nommé sshd_config et d'y appliquer les paramètres désirés et de redémarrer le service pour que les changements soient pris en compte.

Tout est prêt. Il ne reste plus qu'à ouvrir une connexion en SSH pour tester le bon déroulement de l'installation. Une fois la connexion établie avec son login Windows usuel, il suffit d'appeler le moteur Powershell pour faire ce que l'on souhaite :

En utilisant la console, on peut tomber sur quelques bugs d'affichage si jamais la console Putty a été redimensionnée :

En dehors de ces petites imperfections qui montrent que ces binaires n'en sont pas encore à un véritable état de release, la connexion est stable. Je me suis pris au jeu d'effectuer quelques tâches et c'est relativement plaisant de pouvoir effectuer du Powershell à distance via SSH, WinRM étant bloqué dans mon environnement. OpenSSH permet par ailleurs de faire du transfert de fichier via SSH, grâce à WinSCP par exemple :

Powershell : liste des répertoires DFS portés par un serveur

Travaillant actuellement sur la migration de serveurs de fichiers, j'ai conçu un script Powershell prenant en paramètre le nom d'un serveur afin de lister tous les partages DFS qui y sont hébergés. Le script ne fait pas de distinguo entre les liens DFS actifs et inactifs.

Param([Parameter(Mandatory=$true)][string] $Server)
$roots = Get-DfsnRoot
foreach($root in $roots){
    $path = $root.Path+"\*"
	Get-DfsnFolder $path | Where-Object { (Get-DfsnFolderTarget $_.Path).TargetPath -like "\\$server*" } | select Path -ExpandProperty Path
}

Powershell : création d'un batch de users AD sur import CSV

Ce script permet de créer en masse des utilisateurs dans un AD sur base d'un fichier CSV.

$userlist = Import-CSV usertocreate.csv
foreach($user in $userlist){
	New-AdUser -Name $user.Name -GivenName $user.FirstName -Surname $user.LastName -Path $user.OrgUnit -StreetAddress $user.Address -City $user.City -PostalCode $user.PostalCode -Country FR -HomeDrive "U:" -HomeDirectory $user.HomeDir -AccountPassword (ConvertTo-SecureString -AsPlainText $user.MatriculeID -Force) -UserPrincipalName $user.UPN
    Enable-ADAccount $user.Name
	Set-ADUser -Identity $user.Name -ChangePasswordAtLogon $true
	$GrpList = $user.Groups
	while($GrpList.IndexOf(",") -ne -1) {
		$commaindex = $GrpList.IndexOf(",")
		Add-AdGroupMember -Identity $GrpList.SubString(0,$commaindex) -Members $user.Name
		$GrpList = $GrpList.SubString($commaindex+1,$GrpList.Length-($commaindex+1))
	}
	Add-AdGroupMember -Identity $GrpList -Members $user.Name
} 

Le script (une version commentée est disponible en fin d'article, comme toujours) prend quelques propriétés pour la création du compte en sus des usuelles. Il pourra donc être nécessaire d'en retirer, en rajouter ou de modifier les noms attribués dans le script pour s'adapter au fichier d'entrée qui pourra être généré à partir d'une application RH par exemple.

Le script ajoute également l'utilisateur fraîchement créé aux groupes AD spécifiés, en découpant la liste située entre guillemets (afin qu'elle soit interprétée comme une chaîne de caractères unique) en fractions séparées par une virgule.

Le mot de passe est basé sur un numéro matricule fictif que seul l'utilisateur final connaît ; cependant pour des raisons de sécurité, ce mot de passe sera à modifier à la première connexion.