Skip to content
Menu
The Lonely Administrator
  • PowerShell Tips & Tricks
  • Books & Training
  • Essential PowerShell Learning Resources
  • Privacy Policy
  • About Me
The Lonely Administrator

Get ACL Information with PowerShell

Posted on June 21, 2012June 21, 2012

I got a question in the "Ask Don and Jeff" forum on PowerShell.com that intrigued me. The issue was working with the results of the Get-ACL cmdlet. The resulting object includes a property called Access which is a collection of access rule objects. Assuming you are using this with the file system, these are System.Security.AccessControl.FileSystemAccessRule objects that look like this:

Manage and Report Active Directory, Exchange and Microsoft 365 with
ManageEngine ADManager Plus - Download Free Trial

Exclusive offer on ADManager Plus for US and UK regions. Claim now!

FileSystemRights : ReadAndExecute, Synchronize
AccessControlType : Allow
IdentityReference : BUILTIN\Users
IsInherited : False
InheritanceFlags : ContainerInherit, ObjectInherit
PropagationFlags : None

If I'm understanding the original problem, the poster wanted to identify folders that had a single non-system entry. That is, a folder where someone added a single entry. It doesn't matter who. So this got me to thinking about a tool that would look at a folder ACL and report on how many access rules were found and then break that count down by system and user. I figured that any rule where the identity reference name included "Builtin", "NT Authority", "Everyone" or "Creator Owner" would be considered a system rule. Anything else would be considered a user rule.

In the console, I could run a command like this:


PS S:\> get-acl c:\work | select -expand access | where {$_.identityreference -notmatch "BUILTIN|NT AUTHORITY|EVERYONE|CREATOR OWNER"}

FileSystemRights : FullControl
AccessControlType : Allow
IdentityReference : SERENITY\Jeff
IsInherited : False
InheritanceFlags : None
PropagationFlags : None

Because I ran this interactively, I know what folder has this potential issue. So the next step is to turn this into a tool that will write ACL summary information to the pipeline. Here's my function, and then I'll explain a few things. The download version includes comment based help.


Function Get-ACLInfo {

[cmdletbinding()]

Param(
[Parameter(Position=0,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[ValidateScript({Test-Path $_})]
[Alias('PSPath','Fullname')]
[string[]]$Path="."
)

Begin {
Write-Verbose "Starting $($myinvocation.mycommand)"

#create a format file on the fly
$xml=@"




JDH.ACLInfo

JDH.ACLInfo




50



8


9


7






Path


Owner


TotalACL


SystemACL


UserACL








"@
#create a temp file
$tmpfile=[system.io.path]::GetTempFileName()
#add the necessary file extension
$tmpfile+=".ps1xml"

#pipe the xml text to the temp file
Write-Verbose "Creating $tmpfile"
$xml | Out-File -FilePath $tmpfile

<# update format data. I'm setting error action to SilentlyContinue because everytime you run the function it creates a new temp file but Update-FormatData tries to reload all the format files it knows about in the current session, which includes previous versions of this file which have already been deleted. #>
Write-Verbose "Updating format data"
Update-FormatData -AppendPath $tmpfile -ErrorAction SilentlyContinue

} #Begin

Process {
Foreach ($folder in $path) {
Write-Verbose "Getting ACL for $folder"
#get the folder ACL
$acl=Get-ACL -Path $path

#a regex to get a file path
[regex]$regex="\w:\\\S+"

#get full path from ACL object
$folderpath=$regex.match($acl.path).Value

#get Access rules
$access=$acl.Access

#get builtin and system ACLS
$sysACL=$access | where {$_.identityreference -match "BUILTIN|NT AUTHORITY|EVERYONE|CREATOR OWNER"}

#get non builtin and system ACLS
$nonSysACL=$access | where {$_.identityreference -notmatch "BUILTIN|NT AUTHORITY|EVERYONE|CREATOR OWNER"}

#grab some properties and add them to a hash table.
$hash=@{
Path=$folderpath
Owner=$acl.Owner
TotalACL=$access.count
SystemACL=($sysACL | measure-object).Count
UserACL=($nonSysACL | measure-object).Count
AccessRules=$access
}

#write a new object to the pipeline
$obj=New-object -TypeName PSObject -Property $hash
#add a type name for the format file
$obj.PSObject.TypeNames.Insert(0,'JDH.ACLInfo')
$obj

} #foreach

} #Process

End {
#delete the temp file if it still exists
if (Test-Path $tmpfile) {
Write-Verbose "Deleting $tmpfile"
Remove-Item -Path $tmpFile
}
Write-Verbose "Ending $($myinvocation.mycommand)"
} #end

} #end function

When I run the function here's what the result looks like:


PS S:\> get-aclinfo | select *

SystemACL : 7
Owner : SERENITY\Jeff
UserACL : 0
AccessRules : {System.Security.AccessControl.FileSystemAccessRule, System.Security.AccessControl.Fi
leSystemAccessRule, System.Security.AccessControl.FileSystemAccessRule, System.Securi
ty.AccessControl.FileSystemAccessRule...}
Path : C:\scripts\
TotalACL : 7

The function takes a path parameter and passes that to Get-ACL.


$acl=Get-ACL -Path $path

I will be using some of the properties of this object in the custom object I'll eventually write to the pipeline. One thing I want is the full path. Unfortunately, I need to parse that out of the path property. I decided to use a regular expression.


#a regex to get a file path
[regex]$regex="\w:\\\S+"

#get full path from ACL object
$folderpath=$regex.match($acl.path).Value

Next, I need to count the access rule entries and determine which are system and which are user.


#get Access rules
$access=$acl.Access

#get builtin and system ACLS
$sysACL=$access | where {$_.identityreference -match "BUILTIN|NT AUTHORITY|EVERYONE|CREATOR OWNER"}

#get non builtin and system ACLS
$nonSysACL=$access | where {$_.identityreference -notmatch "BUILTIN|NT AUTHORITY|EVERYONE|CREATOR OWNER"}

I like creating new objects with hash tables, which will get even easier in PowerShell 3.0.


#grab some properties and add them to a hash table.
$hash=@{
Path=$folderpath
Owner=$acl.Owner
TotalACL=$access.count
SystemACL=($sysACL | measure-object).Count
UserACL=($nonSysACL | measure-object).Count
AccessRules=$access
}

#write a new object to the pipeline
$obj=New-object -TypeName PSObject -Property $hash

The object shows counts of the different ACL "types" and also includes a property with the full access rules should you want to look at them in more detail. But now for the "scoop of ice cream on the side" part of this function.

In PowerShell 2.0, hash tables are unordered meaning there's no guarantee what order your properties will be display. Plus, you may want to have more control over how PowerShell formats the resulting objects. The way we handle this is by creating a format file and loading it into the shell with Update-FormatData. I'm not going to go into the mechanics of custom format files here. I know the topic is covered in the Windows PowerShell 2.0: TFM book, and the Month of Lunches books among others.

Now, if I had created a module out of this function I could have packaged it with a separate format ps1xml file. But I had a thought of trying to use a format file "on the fly". In the Begin script block, I have the XML that would normally go into the format.ps1xml file. I create a temp file and add the xml to it.


#create a temp file
$tmpfile=[system.io.path]::GetTempFileName()
#add the necessary file extension
$tmpfile+=".ps1xml"

#pipe the xml text to the temp file
Write-Verbose "Creating $tmpfile"
$xml | Out-File -FilePath $tmpfile

The format file apparently needs to have .ps1xml file extension so I have to update the file name. Once I have this file, I call Update-FormatData to load it.


Update-FormatData -AppendPath $tmpfile -ErrorAction SilentlyContinue

Normally, I'm not a big fan of turning off errors, but in this case I need to. When you run Update-FormatData, PowerShell reloads all the format files that it knows in this session. The first time I run the function, the temp file is created and loaded. But in the End script block, I delete it.


if (Test-Path $tmpfile) {
Write-Verbose "Deleting $tmpfile"
Remove-Item -Path $tmpFile
}

The next time I run the function, I recreate the format file. But Update-FormatData also looks for the previous temp files which have since been deleted, which normally raises an exception. I'm not saying this approach of "on the fly formatting" is perfect but so far it works for me in these situations. The formatting file takes my custom object and creates a table with all properties except the access rules.

My format file allows for plenty of space for the file path. But you can always tighten things up.

If I want to work with the underlying access rules, I still can.

Download Get-ACLInfo and let me know what you think.


Behind the PowerShell Pipeline

Share this:

  • Click to share on X (Opens in new window) X
  • Click to share on Facebook (Opens in new window) Facebook
  • Click to share on Mastodon (Opens in new window) Mastodon
  • Click to share on LinkedIn (Opens in new window) LinkedIn
  • Click to share on Pocket (Opens in new window) Pocket
  • Click to share on Reddit (Opens in new window) Reddit
  • Click to print (Opens in new window) Print
  • Click to email a link to a friend (Opens in new window) Email

Like this:

Like Loading...

Related

1 thought on “Get ACL Information with PowerShell”

  1. Pingback: Working with Access Rules in PowerShell | The Lonely Administrator

Comments are closed.

reports

Powered by Buttondown.

Join me on Mastodon

The PowerShell Practice Primer
Learn PowerShell in a Month of Lunches Fourth edition


Get More PowerShell Books

Other Online Content

github



PluralSightAuthor

Active Directory ADSI Automation Backup Books CIM CLI conferences console Friday Fun FridayFun Function functions Get-WMIObject GitHub hashtable HTML Hyper-V Iron Scripter ISE Measure-Object module modules MrRoboto new-object objects Out-Gridview Pipeline PowerShell PowerShell ISE Profile prompt Registry Regular Expressions remoting SAPIEN ScriptBlock Scripting Techmentor Training VBScript WMI WPF Write-Host xml

©2025 The Lonely Administrator | Powered by SuperbThemes!
%d