Solución al error “No flags can be set. Parameter name: inheritanceFlags” en PowerShell

¿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.

Índice

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 con AddAccessRule().

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:

  1. identity – Puede ser un nombre de cuenta (DOMINIO\Usuario) o un SecurityIdentifier.
  2. fileSystemRights
  3. inheritanceFlags
  4. propagationFlags
  5. 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

ObjetivoRecomendación breve
Mantener la herencia existenteUsa $acl.SetAccessRuleProtection($false,$true) si solo vas a añadir reglas y no deseas quebrar la herencia del directorio padre.
Tratar elementos especialesComprueba que Get-Acl pueda leer permisos: Get-Acl -ErrorAction Stop y captura con Try/Catch.
Rendimiento en volúmenes grandesItera carpetas primero: Get‑ChildItem -Directory, aplica reglas; después procesa archivos con Get‑ChildItem -File.
Deshacer cambios en caso de errorGuarda ACL originales en una colección y, ante excepción, recorre el historial para restaurar con Set-Acl.
Estandarizar permisosCrea funciones que acepten listas de usuarios y derechos; así evitas duplicar lógica y facilitas pruebas unitarias.
SeguridadEjecuta el script con la menor cuenta privilegiada posible y registra en un archivo de log cada cambio.
AuditoríaActiva 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&nbsp;‑Parallel</code></h3>
<p>Si usas PowerShell&nbsp;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 propiedad PSIsContainer 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.

Índice