Vous tentez d’automatiser l’attribution d’ACL NTFS à tout un volume en PowerShell, mais vous tombez sur l’erreur « No flags can be set. Parameter name: inheritanceFlags ». Voici une explication claire des causes et un script robuste prêt à l’emploi pour corriger le problème sans casser l’héritage existant.
Problème principal
Automatiser, avec PowerShell, l’attribution d’ACL (droits NTFS) — ajout de User1 et Group1 — à tous les sous‑dossiers et fichiers d’un volume. L’auteur obtient l’exception :
Exception calling "AddAccessRule" with "1" argument(s):
"No flags can be set. Parameter name: inheritanceFlags"
Analyse des causes
Fichiers vs dossiers
- Les indicateurs
ContainerInherit
(CI) etObjectInherit
(OI) n’ont de sens que pour les conteneurs (dossiers). - Quand la boucle tombe sur un fichier, lui appliquer ces indicateurs provoque l’erreur ci‑dessus : un fichier ne contient pas d’objets enfants, donc les flags d’héritage ne sont pas acceptés.
Composition des indicateurs
- L’écriture suivante crée deux valeurs indépendantes plutôt qu’une composition binaire attendue par le constructeur :
[InheritanceFlags]::"ContainerInherit", "ObjectInherit"
Il faut combiner les indicateurs avec l’opérateur -bor
ou en les listant dans une unique chaîne convertible :
$InheritanceFlags = [System.Security.AccessControl.InheritanceFlags] "ContainerInherit,ObjectInherit"
# ou
$InheritanceFlags = [InheritanceFlags]::ContainerInherit -bor [InheritanceFlags]::ObjectInherit
Signature du constructeur
- En passant un SID ou des paramètres mal typés, PowerShell ne trouve plus la bonne surcharge :
Cannot find an overload for 'FileSystemAccessRule' and the argument count: 5
Vérifier les types et l’ordre :
FileSystemAccessRule(
string identity,
FileSystemRights rights,
InheritanceFlags inheritance,
PropagationFlags propagation,
AccessControlType type
)
Solution : script robuste
Ce script gère la distinction fichier/dossier, compose correctement les flags et applique des règles propres. Adaptez DOMAIN\User1
et DOMAIN\Group1
à votre environnement.
# Parcours récursif
Get-ChildItem -Path "D:\Test\Vol2" -Recurse | ForEach-Object {
$path = $_.FullName
$acl = Get-Acl -Path $path
$rightsRead = [FileSystemRights]::Read
$rightsFull = [FileSystemRights]::FullControl
$propFlags = [PropagationFlags]::None
$accType = [AccessControlType]::Allow
if ($_.PSIsContainer) {
# Dossier : on autorise l’héritage descendant
$inhFlags = [InheritanceFlags]::ContainerInherit -bor `
[InheritanceFlags]::ObjectInherit
$acl.AddAccessRule( [FileSystemAccessRule]::new("DOMAIN\User1" , $rightsRead , $inhFlags , $propFlags , $accType) )
$acl.AddAccessRule( [FileSystemAccessRule]::new("DOMAIN\Group1", $rightsFull , $inhFlags , $propFlags , $accType) )
}
else {
# Fichier : pas d’indicateurs d’héritage
$inhFlags = [InheritanceFlags]::None
$acl.AddAccessRule( [FileSystemAccessRule]::new("DOMAIN\User1" , $rightsRead , $inhFlags , $propFlags , $accType) )
$acl.AddAccessRule( [FileSystemAccessRule]::new("DOMAIN\Group1", $rightsFull , $inhFlags , $propFlags , $accType) )
}
Set-Acl -Path $path -AclObject $acl
}
Points clés du correctif
Aspect | Explication |
---|---|
Détection fichier/dossier | $_.PSIsContainer permet d’appliquer des règles distinctes. |
Indicateurs d’héritage | None pour les fichiers ; ContainerInherit + ObjectInherit pour les dossiers. |
Protection de l’héritage existant | SetAccessRuleProtection($true,$false) bloque l’héritage depuis le dossier parent si nécessaire ; inverser les booléens pour le réactiver. |
Combinaison des flags | Utiliser -bor ou une chaîne unique, jamais deux arguments séparés. |
Explications pas à pas
- Lecture ACL :
Get-Acl
retourne la DACL actuelle du chemin traité (fichier ou dossier). - Choix des droits :
[FileSystemRights]::Read
et::FullControl
couvrent la plupart des scénarios. Remplacez au besoin par::Modify
ou des combinaisons fines. - Propagation :
[PropagationFlags]::None
indique qu’on ne force ni l’application uniquement aux enfants (InheritOnly
) ni l’arrêt à un seul niveau (NoPropagateInherit
). - Type d’accès :
[AccessControlType]::Allow
crée des ACE de type autorisation (vsDeny
que l’on évite sauf cas très spécifique). - Dossiers : on compose
CI
+OI
pour propager vers sous‑dossiers et fichiers. - Fichiers : on met
InheritanceFlags::None
pour éviter l’exception « No flags can be set ». - Écriture ACL :
Set-Acl
persiste les nouvelles ACE sur l’objet courant.
Méthode alternative : fonction réutilisable (avec WhatIf)
Pour industrialiser, voici une fonction paramétrable, compatible Windows PowerShell 5.1 et PowerShell 7+, qui gère l’inclusion du dossier racine, l’exclusion des points de jonction et une option pour désactiver l’héritage parent.
function Set-NtfsAclRecursive {
[CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
param(
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[string]$Path,
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[string]$User, # ex. "DOMAIN\User1"
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[string]$Group, # ex. "DOMAIN\Group1"
[System.Security.AccessControl.FileSystemRights]$UserRights = [FileSystemRights]::Read,
[System.Security.AccessControl.FileSystemRights]$GroupRights = [FileSystemRights]::FullControl,
[switch]$DisableParentInheritance, # désactive l’héritage parent sur les dossiers traités
[switch]$IncludeRoot # inclut le dossier racine lui‑même
)
if (-not (Test-Path -LiteralPath $Path)) {
throw "Chemin introuvable : $Path"
}
$propFlags = [PropagationFlags]::None
$accType = [AccessControlType]::Allow
# Construit la liste des cibles : racine (optionnel) + contenu récursif
$targets = @()
if ($IncludeRoot) { $targets += Get-Item -LiteralPath $Path -Force }
# Exclut par défaut les répertoires de type jonction/symlink pour éviter les boucles
$targets += Get-ChildItem -LiteralPath $Path -Recurse -Force -Attributes !ReparsePoint
foreach ($item in $targets) {
try {
$acl = Get-Acl -Path $item.FullName
if ($DisableParentInheritance -and $item.PSIsContainer) {
if ($PSCmdlet.ShouldProcess($item.FullName, 'SetAccessRuleProtection')) {
$acl.SetAccessRuleProtection($true, $false) # bloque l’héritage parent, sans conserver les ACE héritées
}
}
$inhFlags = if ($item.PSIsContainer) {
[InheritanceFlags]::ContainerInherit -bor [InheritanceFlags]::ObjectInherit
} else {
[InheritanceFlags]::None
}
# Utilise New-Object pour compatibilité maximale avec 5.1
$ruleUser = New-Object System.Security.AccessControl.FileSystemAccessRule($User, $UserRights, $inhFlags, $propFlags, $accType)
$ruleGroup = New-Object System.Security.AccessControl.FileSystemAccessRule($Group, $GroupRights, $inhFlags, $propFlags, $accType)
$null = $acl.AddAccessRule($ruleUser)
$null = $acl.AddAccessRule($ruleGroup)
if ($PSCmdlet.ShouldProcess($item.FullName, 'Set-Acl')) {
Set-Acl -Path $item.FullName -AclObject $acl
}
} catch {
Write-Warning ("ACL non appliquée à {0} : {1}" -f $item.FullName, $_.Exception.Message)
}
}
}
Exemples d’usage :
# Simulation (aucun changement) :
Set-NtfsAclRecursive -Path 'D:\Partage' -User 'CONTOSO\User1' -Group 'CONTOSO\Group1' -IncludeRoot -WhatIf
# Exécution réelle en bloquant l’héritage parent sur les dossiers visités :
Set-NtfsAclRecursive -Path 'D:\Partage' -User 'CONTOSO\User1' -Group 'CONTOSO\Group1' -IncludeRoot -DisableParentInheritance
Astuce : Si votre hôte PowerShell n’accepte pas la syntaxe [Type]::new()
, remplacez‑la systématiquement par New-Object Type ...
comme dans la fonction ci‑dessus.
Choisir les bons drapeaux : aide‑mémoire pratique
Objectif | InheritanceFlags | PropagationFlags | Effet |
---|---|---|---|
Appliquer au dossier et à tout son contenu (sous‑dossiers + fichiers) | ContainerInherit + ObjectInherit | None | Autorise sur le dossier courant et propage aux descendants. |
Uniquement au dossier courant | None | None | Aucune propagation vers les enfants. |
Uniquement aux enfants (pas au dossier courant) | ContainerInherit + ObjectInherit | InheritOnly | ACE héritables non appliquées au conteneur lui‑même. |
Uniquement aux fichiers (pas aux sous‑dossiers) | ObjectInherit | InheritOnly | Idéal pour donner un droit sur les fichiers sans l’accorder aux dossiers. |
Limiter l’héritage à un seul niveau | ContainerInherit + ObjectInherit | NoPropagateInherit | Les enfants héritent, mais pas les petits‑enfants. |
Validation et contrôle après exécution
Validez que les ACE ont bien été ajoutées aux cibles attendues et qu’aucune erreur silencieuse n’est passée inaperçue.
# Vérifier un dossier et un fichier échantillons
$testFolder = 'D:\Test\Vol2\SousDossier'
$testFile = 'D:\Test\Vol2\SousDossier\exemple.txt'
(Get-Acl -Path $testFolder).Access |
Where-Object IdentityReference -match 'DOMAIN\(User1|Group1)' |
Format-Table IdentityReference, FileSystemRights, IsInherited, InheritanceFlags, PropagationFlags
(Get-Acl -Path $testFile).Access |
Where-Object IdentityReference -match 'DOMAIN\(User1|Group1)' |
Format-Table IdentityReference, FileSystemRights, IsInherited, InheritanceFlags, PropagationFlags
Pro tip : pour auditer rapidement un grand ensemble, exportez en CSV :
Get-ChildItem -Path 'D:\Test\Vol2' -Recurse -File |
ForEach-Object {
$acl = Get-Acl $_.FullName
foreach ($ace in $acl.Access) {
[pscustomobject]@{
Path = $_.FullName
IdentityReference = $ace.IdentityReference
Rights = $ace.FileSystemRights
Inherited = $ace.IsInherited
InheritanceFlags = $ace.InheritanceFlags
PropagationFlags = $ace.PropagationFlags
}
}
} | Export-Csv -NoTypeInformation -Encoding UTF8 'D:\rapport-acl.csv'
Performance et robustesse sur grands volumes
- Itérations distinctes : effectuer deux parcours peut être plus rapide et plus simple à raisonner : un pour les dossiers (
-Directory
) puis un pour les fichiers (-File
), au lieu de testerPSIsContainer
à chaque tour. - Exclure les reparse points :
-Attributes !ReparsePoint
évite boucles et volumes montés accidentels. - Forcer l’inclusion :
-Force
parcourt aussi les éléments cachés/système. - 64 bits : préférez PowerShell 64 bits pour éviter des limites de mémoire sur d’énormes hiérarchies.
- PS 7+ :
ForEach-Object -Parallel
peut accélérer, mais n’écrivez pas en parallèle sur une même DACL ; segmentez par sous‑arbres. - Chemins longs : si nécessaire, préfixez
\\?\
pour dépasser 260 caractères (Long Path) sur des OS qui le permettent.
Journalisation et reprise
Ajoutez une journalisation légère pour relancer proprement en cas d’erreur non bloquante.
$log = 'D:\acl.log'
Get-ChildItem -Path 'D:\Test\Vol2' -Recurse -Attributes !ReparsePoint | ForEach-Object {
try {
# ... récupération ACL, construction des ACE, etc.
# $acl.AddAccessRule(...); Set-Acl ...
}
catch {
Add-Content -Path $log -Value ("{0};{1}" -f $_.FullName, $_.Exception.Message)
}
}
Cas particuliers et pièges
- Héritage parent : si le parent accorde déjà un droit plus large, votre nouvelle ACE Allow ne restreindra rien. Pour verrouiller, activez
SetAccessRuleProtection($true,$false)
sur les dossiers cibles, puis définissez explicitement toutes les ACE requises. - ACE Deny : elles prennent effet avant les Allow. N’en ajoutez qu’en dernier recours et avec parcimonie, sinon vous bloquez des accès légitimes.
- Ordre canonique : Windows réordonne parfois les ACE. C’est normal ; ne tentez pas de forcer un ordre « visuel » arbitraire.
- Propriété/SACL :
Get-Acl/Set-Acl
manipule la DACL. Modifier le propriétaire ou la SACL nécessite des privilèges élevés spécifiques. - Résolution d’identités : si le domaine est lent ou indisponible, préférez utiliser des NTAccount pré‑résolues (ou SIDs) et vérifiez la portée de confiance inter‑domaines.
Variantes utiles
- Groupes dynamiques : si le groupe cible dépend du nom de dossier (ex.
Local_MonDossier_W
), générez le nom avant d’ajouter les règles :$grp = 'Local_{0}_W' -f $_.Name
, puis créez l’ACE avec$grp
. La logique « fichier/dossier » reste identique. - Affiner les droits : pour une écriture sans suppression, combinez les droits :
[FileSystemRights] "Read,Write"
. - Compatibilité : si vous ciblez des hôtes anciens, remplacez tous les
[Type]::new
parNew-Object
.
FAQ rapide
« AddAccessRule », « SetAccessRule » ou « ResetAccessRule » ?AddAccessRule
ajoute une ACE supplémentaire (risque de doublons). SetAccessRule
remplace une ACE existante correspondante (identité/type/héritage) ou l’ajoute si absente. ResetAccessRule
supprime toutes les ACE de l’identité puis ajoute la nouvelle (plus intrusif).
Pourquoi l’erreur « No flags can be set » n’apparaît qu’avec les fichiers ?
Parce que les fichiers ne sont pas des conteneurs : ils ne peuvent pas porter de ContainerInherit
/ObjectInherit
.
Puis‑je tout faire avec icacls
?
Oui, mais PowerShell offre un contrôle objet fin et des tests -WhatIf
au même endroit que vos autres automatisations.
Annexe : équivalent icacls
(à titre indicatif)
# Autoriser User1 (Lecture) et Group1 (Contrôle total) sur dossiers + fichiers descendants
icacls "D:\Test\Vol2" /grant "DOMAIN\User1:(OI)(CI)(RX)" "DOMAIN\Group1:(OI)(CI)(F)" /T
# (OI) = ObjectInherit, (CI) = ContainerInherit, (RX) = Read & execute, (F) = Full control
Résumé
Le message « No flags can be set » traduit l’application de flags d’héritage à un fichier. Corrigez la logique en distinguant fichiers et dossiers, composez correctement les InheritanceFlags avec -bor
, vérifiez la signature FileSystemAccessRule
, puis sécurisez l’héritage selon vos besoins. Les scripts fournis ci‑dessus sont prêts à l’emploi et adaptables à des volumes très larges, avec options de simulation et de journalisation pour une mise en production sereine.
Informations complémentaires utiles
- Performance : Exécuter le script en mode 64 bits et préférer des parcours dédiés
-Directory
/-File
peut réduire le temps sur de très grands volumes. ExcluezReparsePoint
pour éviter les boucles. - Journalisation : Ajoutez
Try/Catch
et une écriture fichier (ouWrite-EventLog
) pour tracer les erreurs non bloquantes. - Groupes dynamiques : Si les groupes varient selon le nom du dossier (Local_MonDossier_W), la même logique « fichier/dossier » reste applicable ; adaptez simplement le calcul du groupe avant l’ajout des règles.