¿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.fileSystemRightsinheritanceFlagspropagationFlagsaccessControlType
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
InheritanceFlagscon la propiedadPSIsContainery elige dinámicamente entre herencia total o nula. - Si trabajas con SID, crea un objeto
SecurityIdentifierpara 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.
