Powershell Script Help

Associate
Joined
2 Aug 2005
Posts
589
What I'm wanting to achieve is a script that sets the following permissions on the root of a share:

Domain Admin: Full control, folders, subfolders and files

Domain Users: This folder only, read and traverse.

Once the script is set, I want it to match the user folders (in the same format as their AD user account) and give them full control over their own folder, subfolders and files.

Unfortunately I'm having a number of issues (mostly around the set-acl section). Can someone help please?

Code:
#Fix Home folder Permissions v1.0

#-------------------Editable Variables----------------------#

# Where is the root of the home drives?
$homeDrivesDir=""
# Report only? ($false = fix problems)
$reportMode = $false
# Print all valid directories?
$verbose = $false
# What domain are your users in?
$domainName = ""

$homeFolders = Get-ChildItem $homeDrivesDir | Where {$_.psIsContainer -eq $true}

#--------------------User Inputs-----------------------------#

$homeDrivesDir = Read-Host "Please type the name of the share, e.g \\SERVER\SHARE\. Please include the trailing \"
$domainName = Read-Host "Please enter the domain name"

#------------------Share Permissions-------------------------#

    # Put the permissions of the share into a variable
    $homeDrivesDiracl = Get-Acl $homeDrivesDir

    #Removes all access to share
    $homeDrivesDiracl.Access | %{$homeDrivesDiracl.RemoveAccessRule($_)}

    #Puts Domain admin permissions into a variable
    Write-Host "Setting permissions for" $homeDrivesDir "Share" -foregroundcolor white -backgroundcolor red
    # Add the domain admin into the $username variable
    $username = "domain\domain admins"
    # Grant the user full control
    $accessLevel = "FullControl"
    # Should permissions be inherited from above?
    $inheritanceFlags = "ContainerInherit, ObjectInherit"
    # Should permissions propagate to below?
    $propagationFlags = "None"
    # Is this an Allow/Deny entry?
    $accessControlType = "Allow"

    $accessRule = New-Object System.Security.AccessControl.FileSystemAccessRule($username,$accessLevel,$inheritanceFlags,$propagationFlags,$accessControlType)

    #Applies above variable to top-level folder

    $homeDrivesDiracl.SetAccessRule($accessRule)
    Set-Acl -path $homeDrivesDir $homeDrivesDiracl

    # Add the domain users into the $username variable
    $username = "domain\domain users"
    # Grant the user full control
    $accessLevel = "ReadandExecute"
    # Should permissions be inherited from above?
    $inheritanceFlags = "None, None"
    # Should permissions propagate to below?
    $propagationFlags = "None"
    # Is this an Allow/Deny entry?
    $accessControlType = "Allow"

    $accessRule = New-Object System.Security.AccessControl.FileSystemAccessRule($username,$accessLevel,$inheritanceFlags,$propagationFlags,$accessControlType)

    #Applies above variable to top-level folder
    $homeDrivesDiracl.SetAccessRule($accessRule)
    Set-Acl -path $homeDrivesDir $homeDrivesDiracl
  

#------------------Background Guff---------------------------#

# Save the current working directory before we change it (purely for convenience)
pushd .
# Change to the location of the home drives
set-location $homeDrivesDir

# Warn the user if we will be fixing or just reporting on problems
write-host ""

#-----------------Reports or Fix Mode------------------------#

if ($reportMode) {
 Write-Host "Report mode is on. Not fixing problems"
} else {
 Write-Host "Report mode is off. Will fix problems"
}

write-host ""

# Initialise a few counter variables. Only useful for multiple executions from the same session
$goodPermissions = $unfixablePermissions = $fixedPermissions = $badPermissions = 0
$failedFolders = @()

# For every folder in the $homeDrivesDir folder
foreach($homeFolder in $homeFolders) {

 # Put the current ACL in a variable
    $Acl = Get-Acl $homeFolder

    # create a permission mask in the form of DOMAIN\Username where Username=foldername
    #    (adjust as necessary if your home folders are not exactly your usernames)
    $compareString = "*" + $domainName + "\" + $homeFolder.Name + " Allow  FullControl*"

 # if the permission mask is in the ACL
 if ($Acl.AccessToString -like $compareString) {

 # everything's good, increment the counter and move on.
 if ($verbose) {Write-Host "Permissions are valid for" $homeFolder.Name -backgroundcolor green -foregroundcolor white}
    $goodPermissions += 1

 } else {
    # Permissions are invalid, either fix or report
    # increment the number of permissions needing repair
    $badPermissions += 1
    # if we're in report mode
 if ($reportMode -eq $true) {
    # reportmode is on, don't do anything
    Write-Host "Permissions not valid for" $homeFolder.Name -backgroundcolor red -foregroundcolor white
 } else {
    # reportmode is off, fix the permissions
    Write-Host "Setting permissions for" $homeFolder.Name -foregroundcolor white -backgroundcolor red
    # Add the user in format DOMAIN\Username
    $username = $domainName + "\" + $homeFolder.Name
    # Grant the user full control
    $accessLevel = "FullControl"
    # Should permissions be inherited from above?
    $inheritanceFlags = "ContainerInherit, ObjectInherit"
    # Should permissions propagate to below?
    $propagationFlags = "None"
    # Is this an Allow/Deny entry?
    $accessControlType = "Allow"
 try {
    # Create the Access Rule
    $accessRule = New-Object System.Security.AccessControl.FileSystemAccessRule($username,$accessLevel,$inheritanceFlags,$propagationFlags,$accessControlType)

    # Attempt to apply the access rule to the ACL
    $Acl.SetAccessRule($accessRule)
    Set-Acl -path $homeFolder $Acl
    # if it hasn't errored out by now, increment the counter
    $fixedPermissions += 1
 } catch {
    # It failed!
    # Increment the fail count
    $unfixablePermissions += 1
    # and add the folder to the list of failed folders
    $failedFolders += $homeFolder
 }
 } #/if
 } #/if
} #/foreach

# Print out a summary

Write-Host ""
Write-Host $goodPermissions "permissions correctly applied"
Write-Host $badPermissions "permissions needing repair"
if ($reportMode -eq $false) {Write-Host $fixedPermissions "permissions fixed"}
if ($unfixablePermissions -gt 0) {
 Write-Host $unfixablePermissions "ACLs could not be repaired."
 foreach ($folder in $failedFolders) {Write-Host " -" $folder}
}

# Cleanup
popd
 
Last edited:
These are a couple of things off the top of my head:
1)If you load your script into Powershell ISE and turn on debug mode, you can step through each part of the script by hand and hover over each variable as you go to make sure the values are as expected.

2)I think it's just a little tweak you need to do. Instead of:
$homeDrivesDiracl.SetAccessRule($accessRule)

Try:
$homeDrivesDiracl.AddAccessRule($accessRule)

3)There's also no reason this won't work on a UNC path but Powershell interprets different pathing structures differently. You may need to tweak this if it's not working properly:
foreach(($homeFolder.pspath) in $homeFolders)

I think that should be all it is. If you still have issues I'll try and sit down this weekend and go through it by hand.


A tip on formatting; when you're commenting on a single line, put the # after the command. This is much easier to read (imo):

Code:
Write-Host "Setting permissions for" $homeFolder.Name -foregroundcolor white -backgroundcolor red   # reportmode is off, fix the permissions
$username = $domainName + "\" + $homeFolder.Name   # Add the user in format DOMAIN\Username
$accessLevel = "FullControl"   # Grant the user full control

Than this:
Code:
# reportmode is off, fix the permissions
Write-Host "Setting permissions for" $homeFolder.Name -foregroundcolor white -backgroundcolor red
# Add the user in format DOMAIN\Username
$username = $domainName + "\" + $homeFolder.Name
# Grant the user full control
$accessLevel = "FullControl"

If you're commenting on a block of code then it's fine to put the comment on the line above.
 
Last edited:
I wrote a similar script recently to move home folders between two servers in different domains. If you're having problems with the set-acl part, see below for my example.

I created the actual root share manually (let's call it \\fs1\home$), with the correct permissions, so the powershell script just had to create a folder for each user, which would then be referenced \\fs1\home$\username. Not sure what you're trying to accomplish, but if it's home folders, roaming profiles or such having a share per user is not a good idea.

Code:
# create the home drive folder
New-Item -Path \\fs1\home$\$username -type directory

# set permissions on home drive folder
$acl = Get-Acl \\fs1\home$\$username
$arguments = "domain.local\$username","FullControl","ContainerInherit, ObjectInherit","None","Allow"
$accessRule = New-Object System.Security.AccessControl.FileSystemAccessRule $arguments
$acl.SetAccessRule($accessRule)
$acl | Set-Acl \\fs1\home$\$username

Let me know if that works.
 
Last edited:
@warnox why is it not a good idea with home folders?

Even with the fixes above, i'm still having issues. The share permissions are being set correctly, but the users permissions on each of their folders aren't. I'm getting no errors to go off, but even trying to clear all the permissions from each of the folders prior to putting the new permissions on isn't working

This is mighty frustrating I have to say
 
What benefit are you going to get out of creating a share per user? You will just end up with 100's of share on a file server which will be a nightmare to manage.

So share out the root, with share permissions as 'Everyone:FULL' and control access via NTFS permissions (http://blogs.technet.com/b/migreene/archive/2008/03/24/3019467.aspx).

Then, using my script, you will be able to set individual user NTFS permissions on each folder as required.
 
Hi warnox, there isn't a share per user, there is one share, with the user folders within in it.

What I dont want to happen is for everyone:full. Its not secure and it not right in my book.

It should be:

Share
Domain Admins: Full control, Folder, subfolders and files
Domain Users: Read, list and traverse, This folder only

User Folders
Domain Admins: Inherited from share permissions
%username%: Full control, This folder, subfolders and files.

The issue at the moment, is that the permissions are set incorrectly and I want a script to fix it.
 
I did this over a decade ago for user profile directories with a CMD script, run from the top-level directory. (Well, there was a pair of scripts, one calling the other.) It did two things: grant Domain Admins Full Control to each home drive with CACLS, and then add the relevant user with Change Control. Note that the first removes existing permissions.

Anyway, for user home drives, I suggest you take a step back and have a think and do it properly. How many home drives are shared? E.g. by a managers and their secretaries. How do you keep track of that? How many people really need access to the whole lot?

I suggest you consider creating some AD groups to help you simplify matters. The first would be Homedrive_Admins, and have members like Domain Admins, Server Admins, etc. That group would have Full Control over the whole structure. Then you create groups U_%username% for each user, give that group Change access to the user's home drive, and add each user to that. This gives you trackability, accountability, lets you give people temporary access (e.g. a manager to the area of a subordinate who's left, secretary as above), and means your first line call-centre people don't have access to the MD's home drive. Setting up a user requires a little extra work, of course.
 
User Folders
Domain Admins: Inherited from share permissions
%username%: Full control, This folder, subfolders and files.

I don't have a great deal to add on the scripting front, my colleague belted out a vb script to set the rights as required at creation and is serving us well.

We don't give full control rights to any user anywhere, not even their home drive. We really don't want users able to change permissions, as they could 'accidentally':
1) Give access to everyone...
2) Deny access to backup software, then delete everything for it to be your fault the files aren't restorable...
3) Who knows how many other things users with 'tinkerer' proclivities could mess up.

Sorry I can't assist in your scripting, but I can let you know the rights we do configure on our homedrives if it's of interest?
 
What I dont want to happen is for everyone:full. Its not secure and it not right in my book.

It should be:

Share
Domain Admins: Full control, Folder, subfolders and files
Domain Users: Read, list and traverse, This folder only

User Folders
Domain Admins: Inherited from share permissions
%username%: Full control, This folder, subfolders and files.

I think you're confusing your permissions. If you set SHARE permissions to Everyone:Full, but NTFS permissions for the same folder to Everyone:ReadOnly, everyone will only have read only permissions. It just makes troubleshooting a lot easier.

If the permissions are currently incorrect you will probably need to first take ownership (takeown), then set admin permissions as you like (icacls) and add individual user permissions back in (icacls). This might be simpler with a batch file :)
 
@Quartz, do you still have your script? It might be worth me just calling icacls within powershell and performing the permissions change that way?

Sorry, it's over a decade and several jobs ago. Pre Active Directory! Off the top of my head it was very simple - something along the lines of

Set_Profile_Permissions.cmd
@Echo off
rem list every directory that isn't an admin
For %%f /f in ('dir *. /b | find /v "admin"') do SetPProf2.cmd %%f

SetPProf2.cmd
@Echo off
rem Grant Admins full control, replace current ACL
cacls %1 /T /G User_Admins:F
rem Edit ACL to allow the user Change control
cacls %1 /T /E %1:C
rem All other permissions go here
etc

The first batch file just lists all the directories to be cycled through, and you'll likely want to create an explicit list instead. The second batch file is the one that does the work. You'll need to rework it for icacls, and I urge you to create groups to control folder access. Call the group something like U_username_C - U_ to denote that it's a group that controls User areas and _C to denote that the group gives Change access.
 
Hi warnox. I'm not wanting to use Everyone at all. Its ridiculous imo to use this in a domain environment. I'm talking NTFS permissions only here, I used Share as in the share folder as opposed to share permissions.

I'm thinking this is maybe more sensible using a batch file and icacls over powershell
 
Definitely don't use Everyone as that allows Guest accounts access, which is definitely a bad idea with all the malware floating around.
 
We use setacl. It's a 3rd party tool, but an incredibly powerful one. I found icacls to be too cumbersome and unwieldy for the finer grained rights we wanted.

Our use:
setacl -on <Path to User Folder> -ot file -actn ace -ace n:"<Username>";p:Read,Write,Del_Child -ace n:"<Username>";p:File_Traverse;i:np

1) This sets the Basic 'Read' and 'Write' permissions, and the advanced 'Delete subfolders and files' right to 'This folder, subfolders and files' for the user.
2) It also sets the 'Traverse folder / execute file' to 'This folder only'

2 might need some explanation to our reasoning. We didn't want users to be able to execute files from their home drives, but this right is actually required to map the folder to a drive letter - hence it not being inheritied.
If you don't care about users executing files, you could bundle this right in with part 1 and just modify the command accordingly.
 
and the following should do the job.


$Folders = (Get-ChildItem "\\Server\FolderPathofUserFolders")

foreach ($Folder in $Folders)
{
$GetACL = Get-Acl $Folder.FullName

$InheritanceFlag = [System.Security.AccessControl.InheritanceFlags]::ContainerInherit -bor [System.Security.AccessControl.InheritanceFlags]::ObjectInherit
$PropagationFlag = [System.Security.AccessControl.PropagationFlags]::None
$objType = [System.Security.AccessControl.AccessControlType]::Allow

$permission = "$Folder", "Modify", $InheritanceFlag, $PropagationFlag, $objType
$ACLChange = New-Object System.Security.AccessControl.FileSystemAccessRule $permission
$GetACL.SetAccessRule($ACLChange)
Set-Acl $Folder.FullName $GetACL
}
 
Back
Top Bottom