Recent Updates

 

Description

The PowerShell Access Control module provides a way to manage Windows access control for most securable objects using Windows PowerShell. For files, folders, registry keys, and Active Directory objects, it can either fully replace the Get-Acl and Set-Acl cmdlets, or it can be used as a supplement to them (see below for details). It has the following features:

The following securable objects have built-in support:

Each of the previous examples used the Get-AccessControlEntry function, but the input objects can be used with any of the following functions:

If an object doesn't have built-in support, that doesn't mean the module can't be used. If you have access to the SDDL or binary forms of a security descriptor, you can still use the New-AdaptedSecurityDescriptor function to create a security descriptor object that you can work with, then you can convert the SD back into the SDDL or binary forms (there is an example of this below).


The module was developed for PowerShell version 3, but it should work with version 2. If you find any problems with it, please let me know via the 'Questions and Answers' section.

If you're looking for version 2.1 of the module, there is a link to download it in the history section at the bottom of this page.

 

Installation

To install the module, follow these steps:

  1. "Unblock" the downloaded .zip file (choose one of the following steps):
    • In Explorer, right click the downloaded file and select 'Properties'. On the 'General' tab, click the 'Unblock' button   
    • In PowerShell v3 or higher, type the following command: Unblock-File -Path "PathToZipFileHere" 
  2. Extract the zip file to one of the folders found in the $env:PSModulePath environment variable
    • You can actually extract it anywhere you want, but you'll have to manually import the module with the full path if you don't extract it to one of the module paths.   
    • I suggest either 'C:\Users\<username>\Documents\WindowsPowerShell\Modules' (for the current user only) or 'C:\Program Files\WindowsPowerShell\Modules' (for all users of the machine; use this path if you're going to use the DSC resources)   
    • Final folder structure should look like this: <RootModulePath>\PowerShellAccessControl\<module files here>
  3. For PowerShell version 2 (or if you didn't extract the module to a default module path), import the module:
    • PowerShell
      Edit|Remove
      Import-Module PowerShellAccessControl
  4. View the available commands:
    • PowerShell
      Edit|Remove
      Get-Command -Module PowerShellAccessControl 

 

Usage

Below are a few examples. Please see the help included with the module for more information, including many more examples.

Using the module as a supplement to native PowerShell cmdlets

The native Get-Acl and Set-Acl cmdlets are very useful for files, folders, registry keys, and Active Directory objects (they support more than that, but that's what I use them for). If you're not comfortable using a non-Microsoft approved tool to modify security descriptors (proper access control is VERY important after all), then youc can use this module to work with the security descriptors, but you can still depend on the native PowerShell cmdlets to actually get and set the security descriptors. Take a look at the following example, which shows the native PowerShell way to add an ACE to a folder, and a few different ways to use the PowerShell Access Control module to ease the process:

 

PowerShell
Edit|Remove
#--------------------------------- 
# Native PS way to add an ACE that grants 'Modify' rights to 'Everyone': 
#--------------------------------- 
 
$Acl = Get-Acl C:\temp 
$Ace = New-Object System.Security.AccessControl.FileSystemAccessRule ( 
    "Everyone", 
    "Modify", 
    "ContainerInherit, ObjectInherit", 
    "None", 
    "Allow" 
) 
$Acl.AddAccessRule($Ace$Acl | Set-Acl 
# Under certain scenarios, Set-Acl will not work when it should. If 
# you're just working with discretionary access (no auditing), then 
# this would also work: 
[System.IO.File]::SetAccessControl($Acl.Path, $Acl) 
 
 
#--------------------------------- 
# PAC Module Example 1 - New-AccessControlEntry 
#--------------------------------- 
$Acl = Get-Acl C:\temp 
# Notice that the following line replaces the .NET constructor used above: 
$Ace = New-AccessControlEntry -Principal Everyone -FolderRights Modify 
$Acl.AddAccessRule($Ace$Acl | Set-Acl 
 
 
#--------------------------------- 
# PAC Module Example 2 - New-AccessControl and Add-AccessControl 
#--------------------------------- 
$Acl = Get-Acl C:\temp 
$Ace = New-AccessControlEntry -Principal Everyone -FolderRights Modify 
# Notice that the following line replaces the AddAccessRule() method above 
$Acl | Add-AccessControlEntry -AceObject $Ace 
$Acl | Set-Acl 
 
 
#--------------------------------- 
# PAC Module Example 3 - Add-AccessControl only 
#--------------------------------- 
$Acl = Get-Acl C:\temp 
# Notice that the following line replaces the New-AccessControlEntry function 
$Acl | Add-AccessControlEntry -Principal Everyone -FolderRights Modify 
$Acl | Set-Acl 
 
 
#--------------------------------- 
# PAC Module Example 4 - Fully replace Get-Acl and Set-Acl (use -Force to suppress prompt) 
#--------------------------------- 
# This one line does everything that the previous examples do: 
Get-Item C:\temp | Add-AccessControlEntry -Principal Everyone -FolderRights Modify 
Get-AccessControlEntry can also be used against any security descriptors returned from Get-Acl. Normally, to view access and audit entries, you must use the Access and Audit properties found on the security descriptor object. The output from those properties has no default formatting, either, so the output is displayed as a list. Get-AccessControlEntry lets you view both access and audit entries by default (there is a switch to control the types of ACEs to display). Compare the following outputs:

Viewing Security Descriptors

View ACEs for 'C:\Program Files' (not recursive):

PowerShell
Edit|Remove
# Method 1: Pass path directly to Get-AccessControlEntry 
Get-AccessControlEntry -Path 'C:\Program Files' 
 
# Method 2: Pipe items from Get-SecurityDescriptor function (found in this module) 
dir 'C:\Program Files' | Get-SecurityDescriptor -Audit | Get-AccessControlEntry 
 View ACEs for services:
PowerShell
Edit|Remove
# Show the BITs service: 
Get-Service bits | Get-AccessControlEntry 
 
# Alternate way: 
$SD = Get-Service bits | Get-SecurityDescriptor 
$SD.Access 
$SD.Audit 
$SD 
$SD | fl 
 
# Filter the ACEs 
Get-Service b* | Get-AccessControlEntry -Principal Administrators 
Get-Service b* | Get-AccessControlEntry -Principal Administrators -AceType SystemAudit 
Get-Service b* | Get-AccessControlEntry -Principal Administrators -AceType AccessAllowed -ServiceAccessRights FullControl 
View share SDs:
PowerShell
Edit|Remove
# List all share permissions: 
Get-SmbShare | Get-AccessControlEntry -ErrorAction SilentlyContinue 
 
# List NTFS permissions for all shares: 
Get-SmbShare | select path | Get-AccessControlEntry -ErrorAction SilentlyContinue 
 
View ACEs for misc WMI objects (must be run from elevated PS for __SystemSecurity):
PowerShell
Edit|Remove
# Logical shares: 
Get-WmiObject Win32_LogicalShareSecuritySetting | Get-AccessControlEntry 
 
# Printers: 
Get-WmiObject Win32_Printer | Get-AccessControlEntry 
 
# root/cimv2 Namespace 
Get-CimInstance __SystemSecurity -Namespace root/cimv2 | Get-AccessControlEntry
 

WsMan security: 

PowerShell
Edit|Remove
dir wsman:\ -Recurse | ? { $_.Name -eq "Sddl" } | Get-AccessControlEntry
 

Creating an ACE with New-AccessControlEntry

PowerShell doesn't provide a cmdlet for creating ACEs, so the only way to natively create one is using .NET constructors. Creating an access ACE for a folder is slightly different than creating an access ACE for a file, which is different than creating an access ACE for a registry key, which is significantly different than creating an access ACE for an Active Directory object (and all of those are different than creating audit ACEs). Want to create an ACE for a service or printer? Those are different, too.

The New-AccessControlEntry tries to fix that problem. Creating ACEs for each of the previously mentioned objects is very similar. The only difference is the access rights parameter (-FolderRights, -FileRights, -RegistryRights, -ActiveDirectoryRights, -ServiceRights, -PrinterRights).


The ACEs that are output by the -FolderRights, -FileRights, -RegistryRights, and -ActiveDirectoryRights can be used by SD objects from Get-Acl (unless you use the -Generic switch; don't do that) or Get-SecurityDescriptor.

Here are some examples:

 

PowerShell
Edit|Remove
# Create a deny ACE for a file: 
New-AccessControlEntry -FileRights Delete -Principal Everyone -AceType AccessDenied 
 
# Create an audit ACE for a folder that only applies to child files (Audit failures): 
New-AccessControlEntry -AceType SystemAudit  -Principal Everyone -FolderRights FullControl -AppliesTo ChildObjects -AuditFailure 
 
# Create an ACE for a service that allows Users to start and stop 
New-AccessControlEntry -Principal Users -ServiceAccessRights Start, Stop 
 
# Create an ACE for AD object that gives the 'TestUser' the ability to read the 'Public-Information' property set: 
# (See the section on Active Directory below for more information) 
New-AccessControlEntry -Principal TestUser -ActiveDirectoryRights ReadProperty -ObjectAceType Public-Information  
 

Modifying ACLs

The following function deal with modifying security descriptors:

 

Here's an example of modifying the DACL for the current user's temp folder:

PowerShell
Edit|Remove
# Get-Acl could be used here, too 
$SD = Get-SecurityDescriptor $env:temp 
 
# Give Users 'Modify' rights; apply only to sub folders and files (not the current folder), and allow inheritance 
$SD | Add-AccessControlEntry -FolderRights Modify -Principal Users -AppliesTo ChildContainers, ChildObjects 
 
# Give 'Authenticated Users' the 'CreateFiles' right, and only apply it to direct sub folders and files (not sub folders of sub folders).  
# Notice that param is -AceObject, and the object being passed was created with New-AccessControlEntry. This is an alternate way to 
# provide the ACE (the line above is another way): 
$SD | Add-AccessControlEntry -AceObject (New-AccessControlEntry -FolderRights CreateFiles -Principal "Authenticated Users" -OnlyApplyToThisContainer) 
 
# This would save it (if -WhatIf wasn't supplied) 
$SD | Set-SecurityDescriptor -WhatIf 
 
 
# Alternate way to do the previous example in a single line: 
Get-Item $env:temp |  
    Add-AccessControlEntry -FolderRights Modify -Principal Users -AppliesTo ChildContainers, ChildObjects -PassThru |  
    Add-AccessControlEntry -FolderRights CreateFiles -Principal "Authenticated Users" -OnlyApplyToThisContainer -Apply -WhatIf 
 

Here's an example of modifying the DACL and SACL for a service:

PowerShell
Edit|Remove
Get-Service bits |  
    Get-SecurityDescriptor -Audit |  
    Add-AccessControlEntry -ServiceAccessRights Start, Stop -Principal Users -PassThru |  
    Add-AccessControlEntry -AceType SystemAudit -ServiceAccessRights Start, Stop -Principal Users -AuditSuccess -AuditFailure -Apply -WhatIf 
  
# Confirm settings (new settings won't show here if -WhatIf was passed in previous command) 
Get-Service bits | Get-AccessControlEntry -Audit 
 
 
# Here's how to modify more than one service at a time: 
Get-Service | select -first 5 | Add-AccessControlEntry -Principal TestUser -ServiceAccessRights Start,Stop -WhatIf
 
Here's an example of replacing all child objects with permissions from the parent (assuming you have rights to change the permissions on each object; if not, you may have to use Set-Owner to take ownership). This will only set this for the DACL. There are switches available for both functions to also have this apply to the SACL:
PowerShell
Edit|Remove
dir C:\Folder -Recurse |  
    Remove-AccessControlEntry -RemoveAllAccessEntries -PassThru |  
    Enable-AclInheritance -Apply -WhatIf
And finally, a few more examples of using Get-AccessControlEntry with Add or Remove-AccessControlEntry:
PowerShell
Edit|Remove
# Copy ACEs from one security descriptor to another: 
Get-AccessControlEntry C:\temp -Principal Users | Add-AccessControlEntry -Path C:\folder -WhatIf 
 
# Remove certain access (this would remove Write access from any user that contains 'PartialUserName' in their name) 
Get-AccessControlEntry C:\temp -Principal *PartialUserName* -FolderRights Write -NotInherited | Remove-AccessControlEntry -WhatIf 
 

Working with Active Directory Objects

Here are a few examples of giving a specific user or group the ability to join workstations to a domain. The permissions apply to an OU. See this forum topic for more details:

 

PowerShell
Edit|Remove
$Principal = "UserOrGroupName" 
$DistinguishedName = "OU=ComputerOU,DC=Domain,DC=Local" 
 
# Confirm that the SD doesn't contain the access already: 
Get-AccessControlEntry $DistinguishedName -Principal $Principal 
 
 
#--------------------------------- 
# Method 1 uses 3 lines and the native PS cmdlets Get-Acl and Set-Acl: 
#--------------------------------- 
$SD = Get-Acl "AD:\$DistinguishedName" 
$SD | 
    Add-AccessControlEntry -Principal $Principal -ActiveDirectoryRights CreateChild -ObjectAceType computer -PassThru | 
    Add-AccessControlEntry -Principal $Principal -ObjectAceType Reset-Password -InheritedObjectAceType computer -PassThru | 
    Add-AccessControlEntry -Principal $Principal -ActiveDirectoryRights WriteProperty -ObjectAceType Account-Restrictions -InheritedObjectAceType computer -PassThru | 
    Add-AccessControlEntry -Principal $Principal -ObjectAceType Validated-write-to-service-principal-name -InheritedObjectAceType computer -PassThru | 
    Add-AccessControlEntry -Principal $Principal -ObjectAceType Validated-write-to-DNS-host-name -InheritedObjectAceType computer 
$SD | Set-Acl 
 
# Confirm that the SD has been chagned: 
Get-AccessControlEntry $DistinguishedName -Principal $Principal 
 
 
#--------------------------------- 
# Method 2 is the same as method 1 except that the ACEs are contained in an array (it  
# makes the Add-AccessControlEntry command shorter) 
#--------------------------------- 
$Aces = @( 
    New-AccessControlEntry -Principal $Principal -ActiveDirectoryRights CreateChild -ObjectAceType computer 
    New-AccessControlEntry -Principal $Principal -ObjectAceType Reset-Password -InheritedObjectAceType computer 
    New-AccessControlEntry -Principal $Principal -ActiveDirectoryRights WriteProperty -ObjectAceType Account-Restrictions -InheritedObjectAceType computer 
    New-AccessControlEntry -Principal $Principal -ObjectAceType Validated-write-to-service-principal-name -InheritedObjectAceType computer 
    New-AccessControlEntry -Principal $Principal -ObjectAceType Validated-write-to-DNS-host-name -InheritedObjectAceType computer 
) 
 
$SD = Get-Acl "AD:\$DistinguishedName" 
$SD | Add-AccessControlEntry -AceObject $Aces 
$SD | Set-Acl 
 
 
#--------------------------------- 
# Method 3 removes the Get-Acl and Set-Acl calls. Get-SecurityDescriptor and  
# Set-SecurityDescriptor could be used in their place, but most of the SD modification  
# functions will call them automatically when necessary. The simplified method looks 
# like this: 
#--------------------------------- 
$Aces = @( 
    New-AccessControlEntry -Principal $Principal -ActiveDirectoryRights CreateChild -ObjectAceType computer 
    New-AccessControlEntry -Principal $Principal -ObjectAceType Reset-Password -InheritedObjectAceType computer 
    New-AccessControlEntry -Principal $Principal -ActiveDirectoryRights WriteProperty -ObjectAceType Account-Restrictions -InheritedObjectAceType computer 
    New-AccessControlEntry -Principal $Principal -ObjectAceType Validated-write-to-service-principal-name -InheritedObjectAceType computer 
    New-AccessControlEntry -Principal $Principal -ObjectAceType Validated-write-to-DNS-host-name -InheritedObjectAceType computer 
) 
$DistinguishedName | Add-AccessControlEntry -AceObject $Aces 
The -ObjectAceType parameter above (and -InheritedObjectAceType) will take a GUID or a string, and the string can have wildcards. You can also use the Get-AdObjectAceGuid function to either supply the parameter, or just to get a list of names and GUIDs for the current domain's Properties, PropertySets, Validated Writes, Extended Rights, and Class Objects. I knew what strings to use because of IntelliSense in the ISE in PowerShell v3 (and higher): 

 

Mandatory Integrity Labels

The Get-MandatoryIntegrityLabel has the ability to view a mandatory integrity label if one has been set. If one isn't set, the function will return null. Here's a screenshot showing what the results look like:

Effective Access

The Get-EffectiveAccess function can get the simple effective access access mask, and a more detailed view of which rights are allowed and which ones aren't. Here's a screenshot:

 

Working with WMI Security Descriptors

If you work with WMI security descriptors a lot, the module can help with that, too. The module has a function that will get a Win32_SecurityDescriptor from any WMI/CIM object that supports those. There are also functions to convert those into SDDL and/or binary representations, and from those back into Win32_SecurityDescriptor objects. You can also pipe a Win32_SecurityDescriptor object into Get-AccessControlEntry, too.

 

Desired State Configuration

The module contains three DSC resources that are in beta:

See about_PowerShellAccessControl_DscResources for more information. There's also an 'examples' folder in the module that provides a few examples of each DSC resource in use.

 

Working with Unsupported Securable Objects

If you come across an unsupported securable object, you can still probably work with it using the module. Here's an example of converting a binary security descriptor into a form that can be worked with:

PowerShell
Edit|Remove
$SD = New-AdaptedSecurityDescriptor -BinarySD (gp HKLM:\SOFTWARE\Microsoft\Ole | select -exp MachineAccessRestriction) 
# Modification goes here 
 
# Sddl representation: 
$SD.Sddl 
 
# Binary representation: 
$SD.GetSecurityDescriptorBinaryForm() 

Misc

PowerShell v3 users will have a much better experience with the module. When using any of the access mask enumerations, Intellisense (in the ISE) and tab completion (in the console) work. PSv2 users need to know what strings are contained in the enumerations. Here's an example of viewing the valid strings in an enumeration (see example above on how to list the enumerations):

PowerShell
Edit|Remove
[enum]::GetValues([PowerShellAccessControl.ServiceAccessRights])
 

 

History

1.0 (7/2013):

1.1 (12/2103):

1.2 (1/4/2014):

2.0 (1/31/2014):

2.1 (2/17/2014):

2.1 Quick Fix (3/1/2014):

3.0