Powershell : automatisation de la création d'un répertoire partagé DFS

J'ai développé un script Powershell permettant d'automatiser tout le process de création d'un répertoire partagé et son ajout à un namespace DFS.

Ce script, prenant en paramètre le nom du partage, va réaliser les opérations suivantes :

  • Création d'un répertoire sur le disque et création d'un partage SMB sur ce même répertoire ;
  • Création et peuplement de groupes AD qui permettront de définir les droits en lecture et écriture ;
  • Ajout des ACL sur le partage ;
  • Ajout du répertoire dans le namespace DFS.

Il sera nécessaire, avant exécution du script, d'éventuellement modifier les variables déclarées en début de code afin de les adapter à l'environnement d'exécution ou au process habituellement suivi.

param([Parameter(Mandatory=$true)][string]$ShareName)
 $path = "D:\DFS"
 $dfspath = "\\dundermifflin.com\DFS"
 $srvname = "PWFILE01"
 $ou = "OU=DFS,OU=Access_Groups,DC=dundermifflin,DC=com"
 $readarray = @()
 $writearray = @()
 $readgroup = "DFS-$ShareName-RX"
 $writegroup = "DFS-$ShareName-W"
 New-Item -path $path -name $ShareName -ItemType "Directory" -ErrorAction Stop | Out-Null 
 New-ADGroup -Name $readgroup -GroupCategory Security -GroupScope DomainLocal -Path $ou -Description "\\dundermifflin.com\DFS\$ShareName - Read Only" -ErrorAction Stop
 New-ADGroup -Name $writegroup -GroupCategory Security -GroupScope DomainLocal -Path $ou -Description "\\dundermifflin.com\DFS\$ShareName - Write" -ErrorAction Stop
 New-SmbShare -Name $ShareName -Path "$path\$ShareName" -FullAccess "BUILTIN\Administrators" -ReadAccess $readgroup -ChangeAccess $writegroup -ErrorAction Stop
 Write-Host "You will be prompted the usernames to add to the read-only access group. Please input only one user at a time. When you are done, leave the field empty and press enter."
 do {
     $readacc = Read-Host "Account name to add to the READ group"
     if($readacc -ne "") {$readarray+=$readacc}
 }
 while($readacc -ne "")
 Write-Host "Now you will be prompted the usernames to add to the modification group. Please input only one user at a time. When you are done, leave the field empty and press enter."
 do {
     $writeacc = Read-Host "Account name to add to the WRITE group"
     if($writeacc -ne "") {$writearray+=$writeacc}
 }
 while($writeacc -ne "")
 if($readarray -ne $null) { Add-ADGroupMember -Identity $readgroup -Members $readarray }
 if($writearray -ne $null) { Add-ADGroupMember -Identity $writegroup -Members $writearray }
 $acl = Get-Acl "$path\$ShareName"
 $acl.SetAccessRuleProtection($true,$false)
 $readacl = New-Object Security.AccessControl.FileSystemAccessRule("DUNDERMIFFLIN\$readgroup","ReadAndExecute, Synchronize",3,0,"Allow")
 $acl.SetAccessRule($readacl)
 $writeacl = New-Object Security.AccessControl.FileSystemAccessRule("DUNDERMIFFLIN\$writegroup","Modify, Synchronize",3,0,"Allow")
 $acl.SetAccessRule($writeacl)
 $sysacl = New-Object Security.AccessControl.FileSystemAccessRule("NT AUTHORITY\System","FullControl",3,0,"Allow")
 $acl.SetAccessRule($sysacl)
 $admacl = New-Object Security.AccessControl.FileSystemAccessRule("BUILTIN\Administrators","FullControl",3,0,"Allow")
 $acl.SetAccessRule($admacl)
 $acl | Set-Acl
 New-DfsnFolder -path "$dfspath\$ShareName" -Target "\\$srvname\$ShareName"

Une version commentée du script est disponible en téléchargement.

The account is not authorized to login from this station

J'ai obtenu cette erreur en tentant de monter un partage réseau d'une machine en Windows XP sur un Server 2016, suite à une migration d'un Server 2003 sur lequel tout fonctionnait bien.

Après comparaison de multiples clefs de registre sur des machines Windows XP similaires pour lesquelles la connexion du partage réseau était possible, j'ai fait chou blanc ; jusqu'à tomber sur ce réglage de GPO locale (les machines sont toutes en standalone, je n'ai donc pas de politique de domaine) :

Situé dans Configuration Ordinateur > Paramètres Windows > Paramètres de sécurité > Stratégies locales > Options de sécurité > Accès réseau : modèle de partage et de sécurité pour les comptes locaux, ce paramètre était réglé sur Classique pour la machine incriminée et sur Guest (invité) sur toutes les autres. Une fois le paramètre passé sur Guest, j'ai pu monter ce partage SMB.

Il semblerait donc que les versions suivantes de SMB présentes avec 2016 (3.1+) aient logiquement des politiques de sécurité renforcées par rapport au SMB 1.x livré avec 2003. Ce billet d'un blog TechNet offre plus d'informations concernant les évolutions de Server Message Block.

Powershell : liste des utilisateurs de tous les partages SMB d'un serveur

J'ai réalisé un script permettant de générer une liste d'utilisateurs des partages SMB d'un serveur, ce qui peut être pratique à des fins d'audit ou si l'on souhaite simplement contacter les utilisateurs d'un serveur de fichiers pour une maintenance par exemple.

Il existe bien l'instruction Get-SmbShare mais elle n'est disponible qu'à partir de 2012 R2 ! Afin de permettre une compatibilité avec les OS 2008 (qui seront par ailleurs bientôt plus maintenus 😱), j'ai utilisé la classe WMI Win32_share.

Le script fonctionne en plusieurs phases : tout d'abord, il liste les partages existants (tels qu'on peut les voir dans la console de Computer Management), puis liste leurs ACL et récupère les membres des groupes AD éventuellement positionnés pour n'obtenir au final que des identifiants utilisateur. Naturellement, il est nécessaire de lancer le script avec des droits d'administration sur la machine afin d'être sûr de pouvoir lire toutes les ACL et d'avoir les modules AD pour Powershell ; sans quoi il vous faudra vous résoudre à utiliser un Invoke-Command qui a de grandes chances d'être bloqué par le pare-feu.

function parse{
param($objectName)
$doneparsing = $false
for($i=0; $doneparsing -eq $false; $i++){
if ($i+1 -gt $parsedArray.Count) { Write-Host "Added user $objectName." ; $parsedArray.Add($objectName) > $null; $doneparsing = $true }
if ($parsedArray[$i] -eq $objectName){ $doneparsing = $true }
}
}

Write-Host "Share user ACL listing script"
Write-Host "============================="
Write-Host "This script will retrieve all the SMB shares and their ACLs with a 'MYDOMAIN' domain object filter to a file located in the temp folder."
$parsedArray = New-Object System.Collections.ArrayList
$sharelist = Get-WmiObject -Class Win32_Share
foreach($share in $shareList){
if ($share.Path -ne ""){
$aclList = (Get-Acl $share.Path).Access | Where-Object {$_.IdentityReference.ToString().Contains('MYDOMAIN') }
foreach ($acl in $aclList){
$objectName = $acl.IdentityReference.Value
$canoName = $objectName.ToString().SubString(9,$objectName.Length-9)
$objectType = Get-ADObject -Filter {CN -eq $canoName}
if ($objectType.ObjectClass -eq "user"){ parse($objectName) }
if ($objectType.ObjectClass -eq "group") {
Write-Host "Processing group $canoName..."
$PplInGrp = Get-ADGroupMember $canoName
foreach($ppl in $PplInGrp){ parse($ppl.SamAccountName) }
}
}
}
}
cd $env:temp
foreach ($line in $parsedArray){
Add-Content share-acl.txt $line }
Write-host "Done processing."

Fausse alerte de quota maximal atteint sur un répertoire partagé

J'ai eu le cas aujourd'hui d'un répertoire partagé limité par un quota d'1 Go sur un serveur Windows 2008 R2 qui était considéré comme plein par la station de travail l'ayant monté alors que la taille des fichiers présents n'excède pas 200 Mo. Un démontage et remontage du répertoire ainsi qu'un redémarrage de la station n'ayant rien changé, je me suis connecté sur le serveur pour vérifier l'occupation réelle du répertoire : même résultat que sur la station cliente, et pourtant, dans la console des partages, j'obtiens un espace libre de 670 Ko.

Afin de forcer un rafraîchissement des données du quota, il existe l'outil utilisable en ligne de commande dirquota qui est installé en même temps que le rôle de serveurs de fichiers. Pour rafraîchir le quota du répertoire, en assumant que son chemin local soit C:\sharesengue :

dirquota quota scan /path:C:\sharesengue

Ensuite, un petit tour dans la console permet de voir que le quota a bien été mis à jour et qu'on ne se fera plus jeter par l'OS client pour cause d'espace disque insuffisant :