Exchange et Powershell : nettoyage des références caduques dans les ACL

Les ACL des boîtes aux lettres Exchange ne sont pas en synchronisation permanente avec l'ActiveDirectory, ce qui signifie que si l'on ajoute par exemple sur la boîte scranton@dundermifflin.inc l'utilisateur DUNDERMIFFLIN\jhalpert et que ce compte vient à disparaître de l'ActiveDirectory, alors cette boîte aura dans ses ACL une référence de type "S-1-5-***". Il se produit par exemple la même chose côté Windows si un compte de domaine est ajouté dans les profils locaux du système et que ce compte de domaine vient à disparaître.

Afin de faire un peu de nettoyage sur des boîtes aux lettres partagées, j'ai donc développé un script pour l'Exchange Management Shell (EMS) permettant de lister toutes les références à ces comptes qui n'existent plus. Cette liste est ensuite exportée au format CSV, ce qui permet ensuite de passer ce fichier si besoin dans un script supprimant ces références.

$Output = @()
$mailboxes = Get-Mailbox -ResultSize Unlimited
foreach($mailbox in $mailboxes){
	$acls = Get-MailboxPermission $mailbox | where-object { $_.User -like "S-1-5-21*" -and $_.IsInherited -eq $false -and $_.AccessRights -contains "FullAccess" }
	if($acls -ne $null){
		foreach ($acl in $acls) { 
			$obj = New-Object PSCustomObject
			$obj | Add-Member -MemberType NoteProperty -Name "Mailbox" -Value $mailbox.Name
			$obj | Add-Member -MemberType NoteProperty -Name "SID" -Value $acl.User
			$Output += $obj 
		}
	}
}
$Output | Export-CSV s1521-acl.csv -Encoding UTF8

Dans cet exemple, je ne touche qu'aux permissions qui ont été rajoutées manuellement (IsInherited -eq $false) et aux droits complets (FullAccess). Si je ne recommande pas de changer le premier critère, il est possible de changer FullAccess pour un autre valeur si besoin. Je vous renvoie vers la documentation Microsoft concernant la commande Get-MailboxPermission pour plus d'informations sur le sujet.

Une fois le fichier CSV obtenu, je peux me servir de ce snippet pour le parcourir et procéder à la suppression des entrées relevées :

$acls = Import-CSV s1521-acl.csv
foreach($acl in $acls){
	Write-Host "Processing"$acl.Mailbox"with SID"$acl.SID
	Remove-MailboxPermission -Identity $acl.Mailbox -User $acl.SID -AccessRights FullAccess -Confirm:$false
}

Ce premier script est disponible dans une version commentée à télécharger.

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

Octroi de droits à un compte sur un service grâce à subinacl

En recherchant un moyen d'accorder les droits de démarrage / arrêt sur un service particulier pour un compte d'utilisateur standard, je suis tombé sur cet article sur Serverfault. Le but était d'éviter d'accorder des privilèges d'administration à l'utilisateur en question.

Il existe donc un outil nommé SubInACL, qui fait partie du Windows Resource Kit (aux côtés de nombreux autres outils que je connaissais comme LockOutStatus). Cet outil qui s'exécute en ligne de commande permet de choisir précisément quels privilèges accorder sur un service pour un utilisateur, qu'il soit local ou du domaine.

J'ai testé cet outil sur un serveur de test en 2008 R2 et appliqué le fonctionnement sur un 2016 ; son exécution est très simple. Microsoft explique toute la syntaxe disponible à ce lien. Si je souhaite accorder à l'utilisateur dschrute le droit de voir l'état et de relancer le service NTP, je vais devoir envoyer cette instruction :

subinacl.exe /service w32time /GRANT=DUNDERMIFFLIN\dschrute=STO

Voici sur un Server 2016, le résultat de la commande pour le service Windows Update :

Si SubInACL n'est plus disponible sur le site de Microsoft, il est disponible en téléchargement sur le miroir du blog à ce lien.

Utilisation de Get-ACL sur une arborescence ActiveDirectory

Cet article fait plus ou moins écho à celui d'hier où j'explique comment accorder des droits spécifiques à un utilisateur ou un groupe sur une OU ActiveDirectory. Get-ACL est une instruction Powershell permettant de retourner les permissions d'un fichier, répertoire ou même clef de registre. Par défaut, Get-ACL ne permet pas de retourner les autorisations d'un objet ActiveDirectory, mais il existe une technique de sioux permettant d'utiliser cette instruction sur un AD.

Sur une station ayant les RSAT d'installés ou bien directement sur un contrôleur de domaine, il suffit d'importer le module AD pour Powershell :

Import-Module ActiveDirectory

Ensuite, se placer dans l'arborescence AD :

Set-Location AD:

Un simple dir permet de retourner des informations nous confirmant que nous sommes bien dans l'AD.

Lancer un Get-ACL à la racine de AD: n'aura aucun effet. Il est tout de même nécessaire d'indiquer un chemin à la commande ; on utilisera alors le DistingushedName. Par exemple, pour récupérer l'intégralité des permissions à la racine du domaine (attention, ce n'est pas récursif !) :

(Get-Acl (Get-ADDomain).DistinguishedName).Access

Ainsi, il est possible de se concocter un petit snippet si l'on souhaite parcourir tout l'AD à la recherche de privilèges accordés à un groupe ou à un utilisateur sur des OU, par exemple :

Import-Module ActiveDirectory
Set-Location AD:
$oulist = Get-ADOrganizationalUnit -Filter * | select DistinguishedName
foreach($ou in $oulist){
$privileges = (Get-Acl $ou.DistinguishedName).Access | Where-Object { $_.IdentityReference -eq "DUNDERMIFFLIN\gclooney" }
if($privileges -ne $null) { Write-Host "OU:"$ou.DistinguishedName ; $privileges }
}

Par exemple, ayant accordé les droits à l'utilisateur gclooney sur l'OU End-Users, il récupère par héritage ces mêmes privilèges sur les 3 OU enfant.

Cela se vérifie avec le snippet plus haut :

Ce code peut naturellement être modifié pour interroger des groupes ou des utilisateurs. Par exemple, je vais octroyer des droits au groupe US-Presidents sur le groupe Actors. La capture suivante montre bien que le droit WriteProperty a été attribué.

En jouant sur la clause Where-Object de ce snippet, on peut modifier le filtre de recherche. Par exemple, afin rechercher tous les utilisateurs ayant le droit WriteProperty sur un objet défini, il suffit de modifier le snippet :

$grplist = Get-ADgroup -Identity "Actors" | select DistinguishedName
$privileges = (Get-Acl $grp.DistinguishedName).Access | Where-Object { $_.ActiveDirectoryRights -like "*WriteProperty*" }

Au final, la seule limite de Get-Acl et du code que l'on adapte autour, ce sont les informations que l'on souhaite récupérer et les actions éventuelles à mener pour cadrer au mieux les privilèges attribués au fil du temps !