Over the summer, the PowerShell Access Control module got some DSC resources to help manage security descriptors for for some of the supported object types. I’ve tested them a little bit, but I haven’t had enough time to really make sure they work as well as I’d like. Also, they’re still missing some functionality, and there are still some design decisions that haven’t been finalized. When I saw that the PowerShell Summit’s DSC Hackathon has a scenario for creating a resource that handles file and folder ACLs, I thought this would be a good time to show what’s currently in the module. I’m hoping that other people will test the resources and help me figure out what’s missing or what needs to be changed (I already know that the code needs to be cleaned up and the Get-TargetResource functions need some work).
If you download the latest version from the repository, you’ll see that the module includes three resources: cAccessControlEntry, cSecurityDescriptorSddl, and cSecurityDescriptor. Each is described in a little more detail below.
NOTE: The types of securable objects that these work against is currently limited to Files, Folders, Registry Keys, WMI Namespaces, and Services. The only reason the other object types that the module supports won’t work is that I haven’t really documented the path format. I mention that a little bit below when describing each of the properties for the cAccessControlEntry resource. Look for more supported objects in a future release, especially Active Directory objects.
cAccessControlEntry
The first of the three resources provides the least amount of control over a security descriptor. cAccessControlEntry provides a way to check that a DACL contains (or doesn’t contain) certain access or that a SACL contains (or doesn’t contain) entries that will generate certain audits. Here are a few scenarios that you can use it for:
- Make sure Users group has Modify rights to a folder, but not any of its sub folders and files
- Make sure Users group doesn’t have Delete right to a file or folder
- Make sure Users group is explicitly denied Delete right on a file or folder
- Make sure Users will generate an audit when any failed access attempt is performed
- Make sure Users have Start and Stop rights to a specific service
The resource has the following properties:
- AceType (Required) – The type of ACE; options are AccessAllowed, AccessDenied, and SystemAudit
- ObjectType (Required) – The type of the securable object. Currently limited to File, Directory, RegistryKey, Service, and WmiNamespace. The only difference between File and Directory is the default AppliesTo value (if you don’t specify AppliesTo, a File object will use Object and a Directory object will use Object, ChildContainers, ChildObjects)
- Path (Required) – The path to the securable object. This is obvious for files, folders, and registry keys, but not necessarily for other object types. You can get the path to your securable object by using Get-SecurityDescriptor and copying the SdPath property.
- Principal (Required) – User/group/etc that is being granted/denied access or audited.
- AccessMask (Required unless Ensure is set to Absent) – An integer that specifies the access to grant/deny/audit.
- Ensure (Optional) – Controls whether an ACE for the specified properties should be present or absent from the DACL/SACL.
- AppliesTo (Optional) – This is only used when dealing with a container object (an object that can have children, like folders, registry keys, WMI namespaces). It allows you to control where the ACE will apply. If you don’t use it, the default is used, which may be different depending on the ObjectType.
- OnlyApplyToThisContainer (Optional) – Used like AppliesTo. This sets the NoPropagateInherit propagation flag, which means that children of the object that Path points to will inherit the ACE described, but their children (the object’s grandchildren) will not.
- Specific (Optional) – Makes sure that the ACE described by the supplied properties is exactly matched. For example, if you want to make sure Users have Read access, and they already have Modify, testing for the desired access would normally pass since Modify contains Read. If you supply a value of $true for this property, though, the test would fail since Modify is not the same as Read. If this was set to $true in the previous example, the Modify ACE would be removed and a new Read ACE would be added.
- AuditSuccess and AuditFailure (Only valid when AceType is SystemAudit) – At least one of these properties must be set to $true when describing an audit ACE.
The resource will currently only check against explicitly defined ACE entries. That means that inherited entries are completely ignored. If you’re ensuring access is granted or denied, that shouldn’t be a problem, but it could be a problem if you want to make sure access isn’t granted (Ensure = Absent). Let me demonstrate with a few examples:
Example 1: Make sure Users group has Modify rights to c:\powershell\dsc\test folder and its subfolders (but not files)
First, lets look at the DACL before making any changes:

Notice that Users already has Modify rights, but they’re being inherited from the parent folder. If we run the following DSC configuration, a new explicit ACE will be added since the DSC resource ignores inherited ACEs:
configuration DscAceTest {
param(
[string[]] $ComputerName = "localhost"
)
Import-DscResource -Module PowerShellAccessControl
cAccessControlEntry UsersModifyFolder {
AceType = "AccessAllowed"
ObjectType = "Directory"
Path = "C:\powershell\dsc\test"
Principal = "Users"
AccessMask = [System.Security.AccessControl.FileSystemRights]::Modify
AppliesTo = "Object, ChildContainers" # Apply to the folder and subfolders only
}
}

If you were to change the cAccessControlEntry node shown above to include Ensure = ‘Absent’, the DACL would go back to what it looked like in the first screenshot. The inherited ACE would still be there, though, and the LCM would tell you that the configuration was successfully applied (and Test-DscConfiguration would return $true).
Example 2: Make sure Users don’t have Delete rights on the folder itself (but don’t worry about sub folders or files)
For this example, we’ll actually pick up where the last one left off, so see the last screenshot. Users have an ACE that is not inherited that grants Modify rights to the folder and subfolders (Object and ChildContainers). Lets assume that we didn’t set that up with DSC (that just so happens to be what the folder’s DACL currently looks like), and we just want to make sure that Users can’t delete the folder. To do that, you could run the following configuration:
configuration DscAceTest {
param(
[string[]] $ComputerName = "localhost"
)
Import-DscResource -Module PowerShellAccessControl
cAccessControlEntry UsersCantDeleteFolder {
AceType = "AccessAllowed"
ObjectType = "Directory"
Path = "C:\powershell\dsc\test"
Principal = "Users"
AccessMask = [System.Security.AccessControl.FileSystemRights]::Delete
AppliesTo = "Object" # Only apply to the folder
Ensure = "Absent" # Make sure permission isn't granted
}
}
And you’d get a DACL that looks like this:
What happened there? When the configuration was run, the LCM saw that it needed to make some changes because Users had Delete permission to the folder object. When the configuration was applied, only Delete permissions were removed from the folder itself, so the single ACE needed to be split into two ACEs: one that gives Modify minus Delete to the folder and subfolders, and one that gives Delete to just the subfolders. In the end, the LCM did exactly what it was asked, which was ensure that Delete permission wasn’t granted to the folder itself.
Remember that there is still an inherited ACE that grants that permission to the Users group. To get around this, you’ll need to use the cSecurityDescriptorSddl or cSecurityDescriptor resources instead since they have the ability to control DACL and SACL inheritance.
cSecurityDescriptorSddl
This one is really simple to explain, but, since it uses SDDL, its kind of tough to use. You get to control a lot more with this resource than with cAccessControlEntry because this lets you control the entire security descriptor. There are only three properties, and they are all required:
- Path – This is the same as the Path property for cAccessControlEntry above.
- ObjectType – This is the same as the ObjectType property for cAccessControlEntry above.
- Sddl – This is a string representation of the security descriptor. The neat thing about this is that you can include any combination of the four security descriptor sections: Owner, Group, DACL, or SACL. Any section that is missing from the SDDL string shouldn’t be tested or touched.
To use it, I recommend configuring an object the way you want it, and running the following to get the SDDL string:
# This is if you want the entire SD:
(Get-SecurityDescriptor C:\powershell\dsc\DscAceTest).Sddl
# If you only want certain sections, do this (the latest builds of the PAC
# module have this method exposed to the SD object itself, so this format
# won't work in a future version without a slight modification)
# Valid arguments for the GetSddlForm() method are All, Owner, Group, Access,
# and Audit:
$SD = Get-SecurityDescriptor C:\powershell\dsc\DscAceTest
$SD.SecurityDescriptor.GetSddlForm("Owner, Access")
Let’s continue from the example above. We can’t control the ACEs that are being inherited (unless we modify the parent object), but we can tell the folder to disable DACL inheritance. The following configuration contains an SDDL string that only modifies the DACL (so the Owner, Group, and SACL aren’t touched), disables DACL inheritance, and specifies each of the ACEs that were being inherited as explicit entries instead:
configuration DscDaclTest {
param(
[string[]] $ComputerName = "localhost"
)
Import-DscResource -Module PowerShellAccessControl
cSecurityDescriptorSddl TestDacl {
ObjectType = "Directory"
Path = "C:\powershell\dsc\test"
Sddl = "D:PAI(A;OICIIO;SDGXGWGR;;;AU)(A;;0x1301bf;;;AU)(A;OICI;FA;;;SY)(A;OICI;FA;;;BA)(A;CI;0x1201bf;;;BU)(A;CIIO;SD;;;BU)"
}
}
After running that, the folder’s DACL looked like this for me:

Using that resource means that you can force that DACL to look like it does above every single time the DSC configuration is run. cAccessControlEntry only cared about the specific ACE properties supplied to it, and it didn’t care about the rest of the ACL. This resource, when the DACL or SACL sections are specified, controls the whole ACL. That’s pretty powerful, but it’s really, really hard to read. Thankfully, the last resource fixes the readability part (I hope).
cSecurityDescriptor
This resource does the exact same thing as cSecurityDescriptorSddl, except it doesn’t use SDDL. It has the following properties:
- Path – This is the same as the Path property for cAccessControlEntry above.
- ObjectType – This is the same as the ObjectType property for cAccessControlEntry above.
- Owner – A string specifying who/what the owner should be set to
- Group – A string specifying who/what the group should be set to
- Access – A CSV that specifies the explicit DACL ACEs that should be present. If the explicit ACEs don’t match this list, all explicit ACEs will be removed, and ACEs specified here will be applied. The headers are parameter names that would be passed to the New-AccessControlEntry function.
- AccessInheritance – Controls DACL inheritance. Valid values are Enabled and Disabled.
- Audit – A CSV that specifies the explicit SACL ACEs that should be present. If the explicit ACEs don’t match this list, all explicit ACEs will be removed, and ACEs specified here will be applied. The headers are parameter names that would be passed to the New-AccessControlEntry function.
- AuditInheritance – Controls SACL inheritance. Valid values are Enabled and Disabled.
The following configuration does the same thing as the cSecurityDescriptorSddl example above:
configuration DscDaclTest {
param(
[string[]] $ComputerName = "localhost"
)
Import-DscResource -Module PowerShellAccessControl
cSecurityDescriptor TestFolderSdDacl {
Path = "c:\powershell\dsc\test"
ObjectType = "Directory"
AccessInheritance = "Disabled"
Access = @"
AceType,Principal,FolderRights,AppliesTo
AccessAllowed,Authenticated Users,"Modify,Synchronize"
AccessAllowed,SYSTEM,FullControl
AccessAllowed,Administrators,FullControl
AccessAllowed,Users,"Write,ReadAndExecute,Synchronize","Object,ChildContainers"
AccessAllowed,Users,Delete,ChildContainers
"@
}
}
If you download the module, there are more examples of each of the resources in the \examples\dsc\ folder. Please grab the latest version and give these resources a try. If you have any questions, suggestions, or criticisms, please leave a comment below and let me know. Thanks!