I recently ran into a problem where I need to have a user added to every folder underneath a folder structure on one of our local file servers. Now normally, this wouldn’t be a problem as we are using NTFS and could just add the new permission at the top and let it propagate down. The kicker in this case was that the file server housed our user home shares, and inheritance was blocked at the root of each user folder; and I was told that I was not allowed to overwrite permissions.
The server administrators assigned to assist me in the project started bantering about writing C# programs, VB Scripts and performing the changes manually. With over 30,000 user folders to chug through, I threw out the last idea immediately. Being a former server administrator, it struck me that both of the server administrators I was working with were overlooking an obvious solution which they had at hand, PowerShell Scripting.
Windows PowerShell is a command line shell that supports a scripting language designed to ease administrative tasks for IT professionals. It contains over 130 different standard command line tools useful for gathering information and automating routines.
For this program, I wanted to do the following things:
- Accept a path parameter from the command line
- Add a Read-Only permission at the root level of the path parameter, or the root location where the script was called.
- For each of the sub directories of the path, add a Read-Only permission if the sub-directory had inheritance blocked.
As I was only writing a script, I skipped creating a master function, and used the param() keyword to designate my path parameter on execution.
# This script takes an optional parameter of a path. If excluded, the path will
# be taken from the location where the PowerShell script is called.
param([String[]] $path=(get-location).Path)
This method is fairly straight forward, it declares a parameter of type String Array named $path and sets it equal to the path of the current location.
Since I was going to have to add the same permission to every object whose inheritance was blocked, I felt it more efficient to define the permissions rule once, outside of the for loop which would cycle through the child objects. In researching how to set permissions, I discovered this article that described the process in great detail and also pointed out some of the limitations with using PowerShell. This article pointed out that I needed to use the System.Security.AccessControl.FileSystemAccessRule() function to create a rule to be added using the Get-ACL and Set-ACL PowerShell cmdlets as well as an excellent reference.
# The following information determines the user/group that will be used and the
# permissions that will be set accordingly at every location that matches the
# criteria of having inheritance blocked.
# $Identity – Identity of the user or group being granted permissions
# $Rights – The rights to be assigned
# $Inheritance - Defines if subsequent objects and containers will receive the
# the new permissions.
# $Propagation – Defines how permissions are propagated to all child objects
# $Type – Defines whether access is Allowed or Denied
$Identity = “DOMAIN\USER”
$Rights = “Read”
$Inheritance = @(“ObjectInherit”, “ContainerInherit”)
$Propagation = “None”
$Type = “Allow”
$Rule = New-Object System.Security.AccessControl.FileSystemAccessRule( `
$Identity, $Rights, $Inheritance, $Propagation, $Type)
Once I had the rule created and instantiated, using it took three simple steps:
- Get the security descriptor for the top level patch
- Add the access rule to the security descriptor
- Save the security descriptor
# Get the security descriptor for the $path or location where the script was
# run from and add the previous rule to the location.
$TopACL = Get-ACL $path
$TopACL.AddAccessRule($Rule)
Set-ACL $path -AclObject $TopACL
With the top level set with the Read-Only permissions, all sub-directories and files that inherit those permissions are now correctly set, so all that was left was to check for sub-directories where inheritance had been blocked and assign permissions accordingly. I found the following snippet posted by AbqBill, a moderator at The Official Scripting Guys Forum!, on June 30, 2009 at 9:29 PM (you may need to search by date). AbqBill came up with the idea to use the Get-ACL.Access.Count method to set a value in an output property. I took this snippet and modified it down to two lines which set a Boolean value that I could then check for inheritance.
The steps I took for this stage were:
- Iterate through the sub-directories of the root path
- Check for the inheritance flag, if found, set $inherit = TRUE
- If $inherit == FALSE, set the Read-Only permission and save the new permissions
# Recurse all folders and sub-folders contained in the original path or location
# where the script was called
get-childitem $path -recurse -force | where-object { $_.PsIsContainer } |
foreach-object {
# Get the access rules for the child object
$access = (get-acl $_.FullName).Access# Select the IsInherited field and set $inherit to the number of times it
# occurs, should be 1 or 0, or TRUE/FALSE respectively.
$inherit = $access.Count -eq ($access | where-object { $_.IsInherited }).Count
# If inheritance is blocked, $inherit is NOT true, add the accessrule to the
# child object and apply it.
if( !$inherit )
{
$ACL.AddAccessRule($Rule)
Set-ACL $_.FullName -AclObject $ACL
}}
As you can see in the last code block, to set the permissions on the sub-directories I used roughly the same syntax and $Rule that I setup before my foreach-object loop.
As with all snippets and utility code, you should test thoroughly. In order to run this script you will need elevated privileges on the file share or drive you want to modify permissions for. Below you will find the full text of the PowerShell script, I hope that you find it as useful as I did. To use it, simply copy the contents of into a .ps1 file and run from within PowerShell.
Edit – 2010/2/22 – As pointed out by bdees, you’ll need to change the DOMAIN\USER in the script to coincide with the user in your domain, or utilize the SID as described in the comments. Apologies for the oversight, and thanks for the comments.
Cheers!
Jason
# This script takes an optional parameter of a path. If excluded, the path will
# be taken from the location where the PowerShell script is called.
param([String[]] $path=(get-location).Path)# The following information determines the user/group that will be used and the
# permissions that will be set accordingly at every location that matches the
# criteria of having inheritance blocked.
# $Identity – Identity of the user or group being granted permissions
# $Rights – The rights to be assigned
# $Inheritance – Defines if subsequent objects and containers will receive the
# the new permissions.
# $Propagation – Defines how permissions are propagated to all child objects
# $Type – Defines whether access is Allowed or Denied
$Identity = “DOMAIN\USER”
$Rights = “Read”
$Inheritance = @(“ObjectInherit”, “ContainerInherit”)
$Propagation = “None”
$Type = “Allow”
$Rule = New-Object System.Security.AccessControl.FileSystemAccessRule( `
$Identity, $Rights, $Inheritance, $Propagation, $Type)# Get the security descriptor for the $path or location where the script was
# run from and add the previous rule to the location.
$TopACL = Get-ACL $path
$TopACL.AddAccessRule($Rule)
Set-ACL $path -AclObject $TopACL# Recurse all folders and sub-folders contained in the original path or location
# where the script was called
get-childitem $path -recurse -force | where-object { $_.PsIsContainer } |
foreach-object {
# Get the security descriptor for the child object
$ACL = Get-ACL $_.FullName# Get the access rules for the child object
$access = (get-acl $_.FullName).Access# Select the IsInherited field and set $inherit to the number of times it
# occurs, should be 1 or 0, or TRUE/FALSE respectively.
$inherit = $access.Count -eq ($access | where-object { $_.IsInherited }).Count# If inheritance is blocked, $inherit is NOT true, add the accessrule to the
# child object and apply it.
if( !$inherit )
{
$ACL.AddAccessRule($Rule)
Set-ACL $_.FullName -AclObject $ACL
}
}
{ 1 comment }









from Frys here in Sacramento and took it home. The packaging was clean without a lot of extra material, which I appreciate being in California (less garbage to throw away) and the device was a no-frills, extra switch setup…which theoretically meant a plug-n-play installation. Sadly, my experience wasn’t that simple.
the installation procedures again and behold…the little green light came on and I was receiving picture on my television.