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 :

Quelques billes pour automatiser la préparation d’un serveur en Powershell

Je déploie régulièrement des VM à partir d’un template, seulement il reste pas mal d’opérations à faire à la main, comme le changement de nom, la configuration du réseau, l’installation de certaines applications et paramètres qui redescendent pas forcément par GPO.

Je vais donc présenter plusieurs instructions et snippets Powershell qui pourront fournir une bonne base pour la création d’un script complet pouvant être intégré dans un template et exécuté directement après le déploiement.

Tout d’abord, le changement du nom du serveur. On peut faire quelque chose de sympa comme ceci :

$hostname = $env:computername
Write-Host « Current Server name: $hostname »
$newhostname = Read-Host « New hostname »
Rename-Computer -NewName $newhostname
Write-Host « A reboot is required and will be performed in approx. 10 seconds. The script will automatically start after reboot to continue post-deployment operations. »
shutdown -r -t 10

On affiche le nom actuel du serveur, puis un prompt permettant de saisir le nouveau nom de machine. Un reboot permettra de prendre en compte le changement.

Après le redémarrage, on peut passer à la configuration de la carte réseau. En fonction de la configuration de la machine, cela peut être délicat puisqu’il faut être sûr d’affecter l’IP à la bonne carte réseau ; pour cela, on va donc récupérer toutes les cartes réseaux branchées sur le système, puis faire un filtre par nom et qui fonctionnent en IPv4. Par exemple, si la carte réseau s’appelle Ethernet0 :

$ipv4 = Read-Host « IPV4 address »
$gateway = Read-Host « Gateway »
$dnsprim = Read-Host « Primary DNS »
$dnssec = Read-Host « Secondary DNS »

$networkcards = Get-NetIPInterface | Where-Object {$_.InterfaceAlias -eq « Ethernet0 » -and $_.AddressFamily -eq « IPv4 »}
$interfaceindex = $networkcards[0].ifIndex
New-NetIPAddress -InterfaceIndex $interfaceindex -IPAddress $ipv4 -PrefixLength 24 -DefaultGateway $gateway
Set-DnsClientServerAddress -InterfaceIndex $interfaceindex -ServerAddresses ($dnsprim,$dnssec)

Get-NetIPInterface avec son pipe nous permet de rechercher les interfaces ayant pour nom Ethernet0 et configurées en IPv4. On récupère donc la carte qui est retournée par l’instruction et son index (qui n’est naturellement pas 1, ce serait trop facile !), qui nous permet ensuite de pouvoir le passer en paramètre de l’instruction New-NetIPAddress qui va se charger de coller la configuration IP à la carte. Le masque de sous réseau ne s’exprime pas en 255.255.255.0 mais notation CIDR, soit 24. Des tables de conversion sont facilement accessibles grâce à n’importe quel moteur de recherche, vous pouvez utiliser celle-ci. Si l’instruction est bien passée, elle retournera alors plusieurs informations quant à l’interface.

Ensuite, maintenant que le réseau est configuré, on peut joindre la machine au domaine.



Add-Computer -DomainName « super-domaine.local »
if ($?){ Write-Host « A reboot is required and will be performed in approx. 10 seconds. The script will automatically start after reboot to continue post-deployment operations. »
shutdown -r -t 10 }

Un prompt devrait apparaître pour demander un identifiant et un mot de passe d’un compte autorisé à ajouter des machines au domaine. Si la jonction se fait bien, alors le code dans la boucle if est exécuté et la machine redémarre après environ 10 secondes.

La machine mise sur le domaine, les divers paramètres poussés par GPO devraient redescendre (il faut bien évidemment pas oublier de déplacer l’objet Ordinateur qui s’est créé dans l’AD dans la bonne OU, sinon ça risque de ne pas bien marcher 😱). Cependant, d’autres opérations ou paramètres peuvent être modifiés par Powershell ; en voici quelques-uns.

Autorisation de l’exécution de scripts Powershell sans restriction :

Write-Host « Setting Powershell execution policy to Unrestricted… »
Set-ExecutionPolicy Unrestricted

Désactivation du pare-feu pour le domaine :

Set-NetFirewallProfile -Profile Domain -Enabled False 

Si on souhaite tout automatiser et placer dans un même script, il faut prévoir la phase de reboot suite au changement de nom de la machine et la jonction au domaine (bien que la commande Add-Computer tolère un renommage de la station dans l’instruction). Pour n’utiliser qu’un seul script, et faire en sorte qu’il reprenne exactement là où il s’était arrêté, on peut placer chaque étape dans une fonction, et placer en paramètre facultatif le numéro de l’étape sur laquelle le script démarre ; ensuite une tâche planifiée démarre le script à l’ouverture de session de l’administrateur local.

Déclaration du paramètre :

Param
([string]$step)

Ensuite, une fonction pour créer la tâche, qui elle-même va prendre un paramètre :

function createtask{
param($rebootstep)
$TaskDetails = New-ScheduledTaskPrincipal -UserId $env:USERNAME -LogonType ServiceAccount -RunLevel Highest
$TaskAction = New-ScheduledTaskAction -Execute « powershell.exe » -Argument « C:\post-deployment.ps1 -s $rebootstep »
$TaskSched = New-ScheduledTaskTrigger -AtLogOn
$TaskName = « PostDeployReboot »
Register-ScheduledTask -Action $TaskAction -Principal $TaskDetails -Trigger $TaskSched -TaskName $TaskName
}

Et on appelle cette fonction après le renommage de la machine par exemple :

Rename-Computer -NewName $newhostname
createtask(2)

Ainsi, au début du script, on peut placer cette instruction qui appellera la fonction setnetwork dans laquelle le code pour paramétrer la carte réseau sera placé :

if ($step -eq « 2 ») { setnetwork }

La tâche planifiée exécutant le script avec le paramètre -s 2, le script démarrera directement sur cette étape.

Powershell : modification du registre pour contourner CredSSP

Au mois de mars, des correctifs Microsoft ont corrigé certaines failles par rapport à CredSSP qui est notamment utilisé pour sécuriser des connexions RDP (voir ce lien). Un message d’erreur peut survenir si un client patché tente de se connecter à un serveur non patché ou vice-versa. Pour faire simple, il est nécessaire que client comme serveur soient patchés pour qu’ils puissent communiquer.

J’ai réalisé un script Powershell permettant de contourner la sécurité via le registre afin que le système ne tienne pas compte du delta de version entre le CredSSP client et serveur. Dans un monde parfait, cela ne devrait pas exister car postes de travail et serveurs devraient être patchés en temps et en heure, mais la réalité est bien évidemment tout autre pour tout un tas de raisons. 👍

#Requires -RunAsAdministrator
$CredSspPath = « HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System »
$ParametersPath = « HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\CredSSP »
$AllowEncryptionOraclePath = « HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\CredSSP\Parameters »
New-Item -Path $CredSspPath -Name « CredSSP » -Force
New-Item -Path $ParametersPath -Name « Parameters » -Force
New-ItemProperty -Path $AllowEncryptionOraclePath -Name « AllowEncryptionOracle » -PropertyType DWORD -Value « 2 » -Force
Write-Host « If everything went fine, please reboot the system for the changes to take effect. »
Sleep 5

Le script doit être lancé en administrateur ; une fois les clefs déployées, il est nécessaire de redémarrer la station qui devrait pouvoir se connecter sans le message d’erreur plus haut, ce qui ne dispense évidemment pas d’une campagne de patch ! 😉

Vous pouvez trouver une version commentée du script sur le serveur de fichiers en cliquant sur ce lien. 💾