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.

Répertoires DFS visibles sans autorisation d'accès

J'ai été confronté à un problème provenant de la bureautique. Des utilisateurs voyaient dans leur liste de répertoires partagés sous Windows des partages DFS auxquels ils n'avaient pas accès, en plus de leurs répertoires DFS usuels. Normalement, Windows n'affiche pas de répertoire partagé si l'utilisateur n'a pas d'accès en lecture. Par exemple, si l'on considère la racine \\dundermifflin.inc\ et 3 répertoires DFS Sales, Accounting et Boss, si l'utilisateur n'a accès qu'au dernier il ne peut pas voir les dossiers Sales et Accounting.

Or dans mon cas, ces répertoires étaient bien visibles. La cause provient de la manière dont les permissions ont été positionnées dans la console DFS. Lors de la création d'un répertoire DFS, il est possible de positionner les privilèges de deux manières : en utilisant soit les permissions NTFS, soit DFS.

En choisissant l'option Use inherited permissions from the local file system, Windows ne va pas appliquer de permissions DFS sur le répertoire. Ainsi, comme son nom l'indique, les droits seront les mêmes que ceux positionnés sur le serveur qui héberge le répertoire en question. C'est justement ce réglage qui déclenche l'affichage dans l'explorateur. N'ayant aucune permission DFS de configurée, Windows ne sait pas avant d'interroger le serveur qui porte le répertoire si l'utilisateur a le droit de se connecter au non ; le répertoire est donc affiché. Ce n'est que lorsque l'utilisateur tente d'y accéder que Windows va récupérer les ACL et empêcher l'accès. Le comportement est par ailleurs expliqué dans la fenêtre ci-dessus.

La deuxième option, Set explicit view permissions on the DFS folder, place donc des permissions DFS directement sur le lien DFS (traduction de \\FileServer03.dundermifflin.inc\Shares vers \\dundermifflin.inc\shares). Ainsi, dès l'ouverture de l'explorateur, Windows lit ces ACL et n'affiche pas le répertoire puisqu'il sait que l'utilisateur n'y a pas accès.

Ce script Powershell permet d'afficher les répertoires DFS qui sont configurés avec la première option et qui apparaissent dans l'explorateur, si jamais vous souhaitez faire une passe complète sur tous vos shares DFS afin d'adopter une configuration homogène :

Param([Parameter(Mandatory=$true)][string] $Namespace)
$fNameSpace = "$Namespace\*"
Get-DfsnFolder $fNameSpace | where-object { (Get-DfsnAccess $_.Path) -eq $null }

Il suffit de l'appeler de cette manière :

.\DFS-NoPerm.ps1 -Namespace \\dundermifflin.inc\shares

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
}