TechNet Magazine > Home > Issues > 2008 > February >  Automate changes to Permissions with Get-ACL, S...
Windows PowerShell Shell Permissions
Don Jones

Download the code for this article: PowerShell2008_02.exe (151KB)

When discussing Windows PowerShell, I am often asked about permissions, particularly if and how the shell lets you automate changes to permissions. File permissions are perhaps the most common request, although directory and registry permissions also come up quite frequently. When it comes to permissions and nearly
any command-line or scripting interface (including Windows PowerShellTM and VBScript), the news I have to give is both good and bad.
The bad news is that permissions in Windows® are just innately complicated, and there's not much Windows PowerShell can do to fix this. The problem stems from the fact that on a Windows operating system, there are several objects when it comes to permissions, regardless of whether you're looking at the file system, Active Directory®, the registry, or nearly anywhere else.
First there is the resource itself—for instance, a directory object, a file, or a folder. Next is the Access Control List (ACL) object, which is attached to the resource. The ACL consists of one or more Access Control Entries, or ACE objects. An ACE essentially ties together a security principal (a user or group, which is yet another object) with a permission (such as Read or Full Control) and a Grant or Deny status. That's no less than five objects: the resource, the ACL, an ACE, a principal, and a permission.
Permissions are further complicated by the fact that every type of resource has different permission types. Directory objects, for example, have complex permissions governing access to individual object attributes. File permissions are relatively uncomplicated in comparison.
And, technically, all of this doubles up when it comes to auditing. Each resource really has two ACLs. First there is the Discretionary ACL (DACL), which governs access to the resource. Then there is also a Security ACL (SACL), which controls auditing on the resource. Trying to work with these permissions means wading through a pile of fairly complicated-looking programming objects, and there's only so much a shell like Windows PowerShell can do to simplify that for you.

Limited by the .NET Framework
Can't Get Enough?
For more Windows PowerShell resources, including a searchable library of cmdlets, and to connect with Don Jones and other Windows PowerShell experts, visit www.PowerShellCommunity.org.

The nature of Windows PowerShell makes things a bit more complicated. Being built on the Microsoft® .NET Framework, Windows PowerShell uses the underlying classes of the .NET Framework to represent and work with permissions. Those underlying .NET Framework classes are designed to provide software developers with granular and precise control over permissions. Of course, "granular and precise" means lots of information and often translates to "very complicated."
Additionally, the .NET Framework doesn't provide classes that represent the permissions in every type of Windows resource. For example, while the .NET Framework provides classes that let you manipulate file security, it doesn't provide classes that let you work with the security on shared folders. The result is that Windows PowerShell can't manipulate permissions on every type of Windows resource. Simply put, Windows PowerShell is limited to the capabilities exposed by the .NET Framework.

Permissions in the Shell
Permissions management in Windows PowerShell is derived from two cmdlets: Get-ACL and Set-ACL. As you might expect, Get-ACL retrieves the ACL from a resource. You can then modify the ACL to suit your needs and use Set-ACL to write it back to the resource. Both of these cmdlets are generic and rely on the Windows PowerShell system of PSDrive providers. Hence, these two ACL cmdlets could in theory work with any type of resource, provided there is a PSDrive provider capable of understanding a given type of resource and capable of modifying the permissions on that type of resource. The FileSystem and Registry PSDrive providers, which are included with Windows PowerShell, support permissions management via the two ACL cmdlets. (Other PSDrive providers may not support this.)
The sample script shown in Figure 1 allows you to specify a starting directory, a security principal, and a permission. The script then applies the permission you specified for that principal to all files and folders within the specified directory. The script recurses subdirectories, too.
#ChangeACL.ps1
$Right="FullControl"

#The possible values for Rights are 
# ListDirectory, ReadData, WriteData 
# CreateFiles, CreateDirectories, AppendData 
# ReadExtendedAttributes, WriteExtendedAttributes, Traverse
# ExecuteFile, DeleteSubdirectoriesAndFiles, ReadAttributes 
# WriteAttributes, Write, Delete 
# ReadPermissions, Read, ReadAndExecute 
# Modify, ChangePermissions, TakeOwnership
# Synchronize, FullControl

$StartingDir=Read-Host "What directory do you want to start at?"
$Principal=Read-Host "What security principal do you want to grant" `
"$Right to? `n Use format domain\username or domain\group"

#define a new access rule.
#note that the $rule line has been artificially broken for print purposes.
#it needs to be one line. the online version of the script is properly
#formatted.
$rule=new-object System.Security.AccessControl.FileSystemAccessRule
($Principal,$Right,"Allow")

foreach ($file in $(Get-ChildItem $StartingDir -recurse)) {
  $acl=get-acl $file.FullName
 
  #Add this access rule to the ACL
  $acl.SetAccessRule($rule)
  
  #Write the changes to the object
  set-acl $File.Fullname $acl
  }

The meat of this script is where it defines a new access rule in the variable $rule. To do this, I am using a "raw" .NET Framework class, which is perhaps the most complicated part of permissions management under Windows PowerShell. The script then retrieves the ACL from each file and folder in turn, using Get-ACL, applies the new rule using the ACL's SetAccessRule method, and writes the modified ACL back to the resource using Set-ACL.
In reality, this is not that complicated, although more complex operations like removing an ACE from an ACL are more involved. In my opinion, what makes it appear complicated is the fact that it involves so many steps: get the ACL, define a rule, modify the ACL, and write the ACL. It's not a single, clean command that does everything all at once. And since I wanted to modify the ACLs on multiple files, I had to wrap everything in a foreach loop, using Get-ChildItem to actually access all of the files and folders.

I'll Take the Easiest Way
Cmdlet of the Month: Get-QADUser
This cmdlet is a must-have if you're managing Active Directory. It isn't built into Windows PowerShell, but it is available as a free download as part of Quest Software's ActiveRoles Management Shell for Active Directory (www.quest.com/activeroles-server/arms.aspx).
Once you've got the snap-in installed, you can use Get-QADUser to retrieve user objects from the directory. Of course, simply running the cmdlet returns all of your users, which isn't exactly helpful for most administrative tasks. But the cmdlet allows you to specify filtering criteria, which are applied at the domain controller so that only the users you are concerned with are actually returned to Windows PowerShell. This is a much faster technique than first getting all the users and then filtering them with the Where-Object cmdlet. For instance, Get-QADUser -l Redmond will just return those users whose "l" attribute contains "Redmond." (The "l" attribute, by the way, stands for locality and is listed as city in the GUI.) You can then pipe those users to other cmdlets in order to generate an HTML report, place them into a group, or even delete them.

I don't find my example to be complex, but I know that many administrators would prefer a more simplified way of working with permissions. Perhaps they want to remove a particular user group from a set of folders all at once but don't want to write a multi-step script like the one in Figure 1.
Fortunately, Windows PowerShell does offer a simplified means of working with permissions, and you're probably already familiar with it.
Windows PowerShell doesn't make you give up on the tried-and-true ways of doing things. You can still use command-line tools—such as Dsacls.exe, Cacls.exe, and Xcacls.exe—that were specifically designed to provide a simpler way to work with various types of permissions. While their syntax is completely different from the standardized syntax used by Windows PowerShell cmdlets, these command-line utilities no longer have to be used in an ad hoc way at the command-line as they were used under the old cmd.exe shell. And you can use Windows PowerShell to automate these utilities.
I have run across some folks—I'll call them "purists"—who insist that using an old-style command-line utility within Windows PowerShell is somehow wrong. I don't quite follow their logic. I'm all about getting the job done, and if the easiest way to do that is to use a 10-year-old command-line utility, then sign me up. I am not going to spend added hours trying to do something a certain way just because it seems "more pure." And the Microsoft Windows PowerShell team holds a similar attitude. If an existing tool does the job, then Windows PowerShell lets you use that tool within the shell. Why reinvent the wheel?
Figure 2 shows a short script—taken from my book Windows PowerShell: TFM (SAPIEN Press, 2006)—that uses Windows PowerShell to automate Cacls.exe. This script is just a demonstration, meaning you'll have to tweak it if you want to make it into a real automation tool. But it does show how a Windows PowerShell script can collect information and use it to launch Cacls.exe, just as if Cacls.exe were a "native" Windows PowerShell command. (Although this script collects information by prompting the user for the information, the parameters could also be read from a file or database.)
#SetPermsWithCACLS.ps1
# CACLS rights are usually
# F = FullControl
# C = Change
# R = Readonly
# W = Write

$StartingDir=Read-Host "What directory do you want to start at?"
$Right=Read-Host "What CACLS right do you want to grant? Valid choices are F, C, R or W"
Switch ($Right) {
  "F" {$Null}
  "C" {$Null}
  "R" {$Null}
  "W" {$Null}
  default {
    Write-Host -foregroundcolor "Red" `
    `n $Right.ToUpper() " is an invalid choice. Please Try again."`n
    exit
  }
}

$Principal=Read-Host "What security principal do you want to grant?" `
"CACLS right"$Right.ToUpper()"to?" `n `
"Use format domain\username or domain\group"

$Verify=Read-Host `n "You are about to change permissions on all" `
"files starting at"$StartingDir.ToUpper() `n "for security"`
"principal"$Principal.ToUpper() `
"with new right of"$Right.ToUpper()"."`n `
"Do you want to continue? [Y,N]"

if ($Verify -eq "Y") {

 foreach ($file in $(Get-ChildItem $StartingDir -recurse)) {
  #display filename and old permissions
  write-Host -foregroundcolor Yellow $file.FullName
  #uncomment if you want to see old permissions
  #CACLS $file.FullName
  
  #ADD new permission with CACLS
  CACLS $file.FullName /E /P "${Principal}:${Right}" >$NULL
  
  #display new permissions
  Write-Host -foregroundcolor Green "New Permissions"
  CACLS $file.FullName
 }
}


Success with Permissions?
I've heard it argued that Windows PowerShell falls short when it comes to permissions because it fails to provide a native cmdlet that offers simplified, universal permissions management. I don't agree. First, this argument is often made by administrators with a strong UNIX background, and the fact is that the Windows permissions system is vastly more complicated than what UNIX uses. When exposing that system, Windows PowerShell will also be complicated. This is not a problem of Windows PowerShell, though.
In addition, Windows PowerShell does allow you to use simplified tools in the form of good old command-line utilities, as I've demonstrated here. True, the .NET Framework needs some expansion to more fully cover permissions management across the entire Windows platform. But overall, Windows PowerShell gives you both worlds: simplified permissions management as well as access to granular and precise management tools.

Don Jones is a contributing editor for TechNet Magazine and is the coauthor of Windows PowerShell: TFM (SAPIEN Press, 2007). He teaches Windows PowerShell (see www.ScriptingTraining.com) and can be reached through the Web site www.ScriptingAnswers.com.
© 2008 Microsoft Corporation and CMP Media, LLC. All rights reserved; reproduction in part or in whole without permission is prohibited.
Page view tracker