¿Tu script de PowerShell se detiene con la temida excepción “No flags can be set. Parameter name: inheritanceFlags” cuando intentas aplicar permisos de manera recursiva? A continuación encontrarás una guía completa —desde la causa raíz hasta buenas prácticas avanzadas— para que tu automatización de control de acceso (ACL) funcione sin tropiezos y con el máximo rendimiento.
Contexto técnico: ¿qué dispara el error?
La instrucción AddAccessRule()
invoca un constructor de FileSystemAccessRule
que espera valores coherentes de InheritanceFlags
. Sin embargo, los archivos individuales no pueden portar los indicadores ContainerInherit
ni ObjectInherit
, porque, a diferencia de las carpetas, no contienen objetos secundarios. Cuando el script les asigna esos flags, el motor de seguridad .NET arroja el error.
Resumen del escenario típico
- Ruta objetivo:
D:\test\Vol2
(contiene subcarpetas y distintos tipos de archivo como .doc, .pdf, .xlsx…) - User1 debe tener permisos de lectura.
- Group1 debe tener control total.
- El script recorre todo el árbol mediante
Get‑ChildItem –Recurse
, crea reglas de acceso y las agrega conAddAccessRule()
.
El punto de quiebre llega cuando el bucle intenta aplicar a un archivo reglas que incluyen herencia: el constructor detecta la incongruencia y lanza la excepción.
Solución paso a paso
Diferenciar archivos y carpetas
La clave es comprobar si el elemento iterado es contenedor ($item.PSIsContainer
). En tiempo de ejecución PowerShell marca como contenedores a las carpetas y como no contenedores a los archivos. Así podemos asignar:
- Carpetas:
InheritanceFlags "ContainerInherit,ObjectInherit"
para que los permisos fluyan a todos los niveles. - Archivos:
InheritanceFlags::None
, eliminando cualquier referencia heredable y evitando el error.
foreach ($item in Get‑ChildItem -Recurse -Path "D:\test\Vol2") {
$acl = Get-Acl $item.FullName
if ($item.PSIsContainer) { # Carpeta: admite herencia
$inherit = [System.Security.AccessControl.InheritanceFlags]"ContainerInherit,ObjectInherit"
} else { # Archivo: sin herencia
$inherit = [System.Security.AccessControl.InheritanceFlags]::None
}
$propagate = [System.Security.AccessControl.PropagationFlags]::None
$ruleUser = New-Object System.Security.AccessControl.FileSystemAccessRule(
"User1","Read",$inherit,$propagate,"Allow")
$ruleGroup = New-Object System.Security.AccessControl.FileSystemAccessRule(
"Group1","FullControl",$inherit,$propagate,"Allow")
$acl.AddAccessRule($ruleUser)
$acl.AddAccessRule($ruleGroup)
Set-Acl -Path $item.FullName -AclObject $acl
}
Con esta lógica condicional el script se ejecuta sin excepciones, tanto en entorno de pruebas como en producción, porque nunca intenta aplicar banderas de herencia a un objeto que no las soporta.
Comprender los constructores de FileSystemAccessRule
Existen varias sobrecargas de FileSystemAccessRule
. La forma más usada en scripting recibe cinco argumentos:
identity
– Puede ser un nombre de cuenta (DOMINIO\Usuario
) o unSecurityIdentifier
.fileSystemRights
inheritanceFlags
propagationFlags
accessControlType
Cuando reemplazas un nombre de cuenta por su SID como cadena (ej. “S‑1‑5‑21‑…”) y mantienes los cinco parámetros, PowerShell busca la sobrecarga que espera un objeto SecurityIdentifier
; al no encontrarla, informa “Cannot find an overload… argument count: 5”.
Corrección con SID
$sid = New-Object System.Security.Principal.SecurityIdentifier("S-1-5-21-...")
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule(
$sid, $rights, $inherit, $propagate, "Allow")
Alternativamente, mantén el formato DOMINIO\Usuario
o Equipo\Usuario
y simplifica la llamada.
Buenas prácticas recomendadas
Objetivo | Recomendación breve |
---|---|
Mantener la herencia existente | Usa $acl.SetAccessRuleProtection($false,$true) si solo vas a añadir reglas y no deseas quebrar la herencia del directorio padre. |
Tratar elementos especiales | Comprueba que Get-Acl pueda leer permisos: Get-Acl -ErrorAction Stop y captura con Try/Catch . |
Rendimiento en volúmenes grandes | Itera carpetas primero: Get‑ChildItem -Directory , aplica reglas; después procesa archivos con Get‑ChildItem -File . |
Deshacer cambios en caso de error | Guarda ACL originales en una colección y, ante excepción, recorre el historial para restaurar con Set-Acl . |
Estandarizar permisos | Crea funciones que acepten listas de usuarios y derechos; así evitas duplicar lógica y facilitas pruebas unitarias. |
Seguridad | Ejecuta el script con la menor cuenta privilegiada posible y registra en un archivo de log cada cambio. |
Auditoría | Activa Audit.FileSystem mediante GPO o auditpol.exe para monitorear quién crea, modifica o elimina ACL. |
Desglose conceptual: InheritanceFlags vs PropagationFlags
InheritanceFlags define qué se hereda; PropagationFlags especifican cómo llega la herencia a subniveles:
ContainerInherit
— la ACL se aplica a carpetas hijas.ObjectInherit
— la ACL se aplica a archivos hijos.None
— no hay herencia.InheritOnly
(Propagation) — la regla no afecta al contenedor actual, solo a descendientes.NoPropagateInherit
(Propagation) — los hijos no transmiten la regla a sus propios descendientes.
Asignar ContainerInherit,ObjectInherit
a una carpeta raíz permite que todos los permisos fluyan homogéneamente, salvo que los detengas con NoPropagateInherit
en niveles inferiores.
Patrones avanzados de scripting
Pipeline + Hashtable de derechos
Cuando varios roles requieren combinaciones de permisos, define un hashtable y genera reglas dinámicamente:
$permisos = @{
'User1' = 'Read'
'Group1' = 'FullControl'
'Auditor' = 'ReadAndExecute'
}
Get-ChildItem -Recurse "D:\test\Vol2" | ForEach-Object {
\$acl = Get-Acl \$*.FullName
\$inherit = \$*.PSIsContainer ?
\[System.Security.AccessControl.InheritanceFlags]"ContainerInherit,ObjectInherit" :
\[System.Security.AccessControl.InheritanceFlags]::None```
foreach ($cuenta in $permisos.Keys) {
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule(
$cuenta, $permisos[$cuenta], $inherit,
[System.Security.AccessControl.PropagationFlags]::None, 'Allow')
$acl.AddAccessRule($rule)
}
Set-Acl $_.FullName $acl
```
}</code></pre>
<h3>Multithreading con <code>ForEach‑Object ‑Parallel</code></h3>
<p>Si usas PowerShell 7+, puedes acelerar la asignación distribuyendo la carga entre threads. Asegúrate de encapsular el objeto ACL dentro del bloque <code>-Parallel</code> para evitar <em>thread affinity</em> issues:</p>
<pre><code class="language-powershell">Get-ChildItem -Recurse "D:\test\Vol2" | ForEach-Object -Parallel {
$acl = Get-Acl $_.FullName
# Lógica idéntica…
}</code></pre>
<h2>Pruebas y validación</h2>
<h3>Comparar ACL antes y después</h3>
<p>Conviene capturar el estado inicial y final para certificar que el script hizo exactamente lo esperado:</p>
<pre><code class="language-powershell"># Snapshot inicial
Get-ChildItem -Recurse "D:\test\Vol2" |
Select-Object FullName, @{N='ACL';E={ (Get-Acl $_.FullName).AccessToString }} |
Export-Csv 'antes.csv' -NoTypeInformation
... Ejecuta tu script ...
Snapshot final
Get-ChildItem -Recurse "D:\test\Vol2" |
Select-Object FullName, @{N='ACL';E={ (Get-Acl $\_.FullName).AccessToString }} |
Export-Csv 'despues.csv' -NoTypeInformation
Dif rápido
Compare-Object (Import-Csv antes.csv) (Import-Csv despues.csv) -Property FullName,ACL |
Out-GridView</code></pre>
<h3>Unit tests con Pester</h3>
<p>Automatiza la validación usando Pester para comprobar que cada carpeta o archivo contiene las ACE esperadas:</p>
<pre><code class="language-powershell">Describe 'Permisos en Vol2' {
$paths = Get-ChildItem -Recurse 'D:\test\Vol2'```
foreach ($p in $paths) {
Context $p.FullName {
It 'debe incluir Read para User1' {
(Get-Acl $p.FullName).Access |
Where-Object { $_.IdentityReference -match 'User1' -and $_.FileSystemRights -eq 'Read' } |
Should -Not -BeNullOrEmpty
}
}
}
```
}
Conclusiones clave
- El error “No flags can be set” nace de asignar herencia a archivos.
- Controla
InheritanceFlags
con la propiedadPSIsContainer
y elige dinámicamente entre herencia total o nula. - Si trabajas con SID, crea un objeto
SecurityIdentifier
para cumplir con la sobrecarga correcta. - Aplica buenas prácticas de rendimiento, logging, pruebas automatizadas y multithreading para scripts robustos y auditables.
Con estos fundamentos y fragmentos de código listos para copiar y pegar, podrás asignar ACL de forma masiva de manera segura y eficiente, eliminando de raíz el obstáculo de inheritanceFlags.