I have looked all over and I can only find examples of filtering an array by a single value, not an object by an array.
Here is what I have that works but seems kludgy to use a foreach loop, is there a way to filter my object by my array of "bad users"?
$permissions = dir -Recurse $path | where { $_.PsIsContainer } | ForEach-Object { $path1 = $_.fullname; Get-Acl $_.Fullname | ForEach-Object { $_.access }}
$arrFilterDefaulsOut = #('NT AUTHORITY\SYSTEM','BUILTIN\Administrators','BUILTIN\Users','CREATOR OWNER')
foreach ($filter in $arrFilterDefaulsOut) {
$permissions = $permissions | Select-Object * | Where-Object -property IdentityReference -ne $filter
}
$permissions | Select-Object *| Export-Csv $finalReport
I have tried using -notcontains but that seems to do nothing
If I understand you correctly you want to filter out the defined identities, so you can do:
$permissionFiltered = $permissions | where-object {$arrFilterDefaulsOut -notcontains $_.IdentityReference}
I think I understand what you were trying to do before with Add-Member before, this is how I would streamline the process keeping it efficient using an anonymous function that filters those FileSystemAccessRule where IdentityReference is not in the $arrFilterDefaulsOut array and creates new objects from those rules adding the absolute path of the folders.
Get-ChildItem -Recurse $path -Directory -PipelineVariable folder | & {
begin {
$arrFilterDefaulsOut = #(
'NT AUTHORITY\SYSTEM'
'BUILTIN\Administrators'
'BUILTIN\Users'
'CREATOR OWNER'
)
}
process {
foreach($rule in (Get-Acl $_.FullName).Access) {
if($rule.IdentityReference -notin $arrFilterDefaulsOut) {
$rule | Select-Object #{ N='Path'; E={ $folder.FullName }}, *
}
}
}
} | Export-Csv path\to\report.csv -NoTypeInformation
Related
I have the following code that returns the ntfs permissions for a particular folder path.
$folders = Get-ChildItem -path d:\test" -recurse -force | ?{ $_.psiscontainer }
$output = #()
foreach($folder in $folders)
{
$rights = Get-Acl -path $folder.fullname
foreach($right in $rights.Access)
{
$properties = [ordered]#{'foldername'=$folder.fullname;'username'=$right.identityreference;'permissions'=$right.filesystemrights}
$output += New-Object -TypeName psobject -Property $properties
}
}
$output | export-csv d:\output\folders_temp1.csv -NoTypeInformation -Encoding UTF8
Though it displays the username along with its respective permissions, I would like to display the first and last name associated to that user from the active directory.
Any ideas on how can that be achieved?
Thank you for your help.
Here is how I would approach this, using Group-Object so that you're not querying Active Directory for the same user over and over for each Access Control List.
It's important to note that #() and += is inefficient and that PSCustomObject can be casted which is also, more efficient than using an ordered hashtable and then converting it to a New-Object.
Another efficiency improvement, thanks to Mathias R. Jessen for his helpful feedback, is to implement a hash table ($map) to have a reference of the IdentityReference already queried, by doing so we would only be querying Active Directory only once per unique user.
# $_.PSIsContainer => Can be replaced with -Directory
$folders = Get-ChildItem -Path "D:\test" -Recurse -Force -Directory
$map = #{}
$output = foreach($folder in $folders)
{
$rights = Get-Acl -Path $folder.fullname
$groups = $rights.Access | Group-Object IdentityReference
foreach($group in $groups)
{
if(-not $map.ContainsKey($group.Name))
{
$ref = Split-Path $group.Name -Leaf
$user = Get-ADUser -LDAPFilter "(name=$ref)"
$map[$group.Name] = $user
}
$aduser = $map[$group.Name]
foreach($acl in $group.Group)
{
[pscustomobject]#{
GivenName = $aduser.GivenName
Surname = $aduser.Surname
Foldername = $folder.Fullname
UserName = $acl.IdentityReference
Permissions = $acl.FilesystemRights
}
}
}
}
$output | Export-Csv D:\output\folders_temp1.csv -NoTypeInformation -Encoding UTF8
this script works for finding a particular user who has any explicit permissions on a folder within a folder structure, which is good! However, is there an easier way to list the folder path and $_.Access.IdentityReference.Value without having to loop my way in? Or, is this actually okay?
$foldStruct = get-childitem "C:\temp" -recurse -Attributes D | get-acl
ForEach ($fold in $foldStruct) {
ForEach ($perm in $fold.Access.IdentityReference) {
ForEach ($user in $perm.Value) {
If ($user -like "Dom\A*" -or $user -like "Dom\B*") {
Write-Host $user
Write-Host $fold.Path
}
}
}
}
It's debatable if this IS easier.
A more PowerShell way is to have objects as output.
instead of two -like I'd use the RegEx based -match with an alternation
the nested ForEach could be replaced with a Where-Object and a Select-Object
the Path would include Microsoft.PowerShell.Core\FileSystem:: I'd remove that with a -split '::'.
## Q:\Test\2019\02\07\SO_54569198.ps1
$Base = "C:\temp"
# to use a -match instead of -like anchor at begin and escape the \ => \\
$Users = "^Dom\\A|^Dom\\B"
$folderACLs = Get-ChildItem $Base -Recurse -Directory | Get-Acl |
Where-Object {$_.Access.IdentityReference.Value -match $Users } |
Select-Object #{n='User';e={($_.Access.IdentityReference.Value|?{$_ -match $Users})}},
#{n='Path';e={($_.Path -split '::')[1] }}
The output could/would include multiple users in the column, so to seperate them:
ForEach($folderACL in $folderACLs){
ForEach($User in ($folderACL.User){
[PSCustomObject]#{
User = $User
Path = ($_.folderACL.Path -split '::')[1]
}
}
}
I was thinking somewhere around the same thing as LotPings, using a regex -match to filter the users instead of doing -like twice.
I came up with this:
$users = '^Dom\\[AB].*' # regex to find usernames beginning with 'A' or 'B'
$subfolders = Get-ChildItem -Path "C:\Temp" -Recurse -Directory
foreach ($folder in $subfolders) {
$folder | Get-Acl | ForEach-Object { $_.Access } |
Where-Object {$_.IdentityReference.Value -match $users} | ForEach-Object {
[PSCustomObject]#{
'Folder' = $folder.FullName
'User' = $_.IdentityReference
# add extra info about access type for this user if you like
# 'AccessControlType' = $_.AccessControlType
# 'IsInherited' = $_.IsInherited
# 'InheritanceFlags' = $_.InheritanceFlags
# 'PropagationFlags' = $_.PropagationFlags
}
}
}
I need help removing the #{} extensions from object output.
The code bellow is listing the last modified file inside a folder. But the output is inside extension #{}.
I have tried the Out-String but it is not working.
function scriptA() {
Get-ChildItem $path | Where-Object {!$_.PsIsContainer} | Select fullname -last 1
}
function scriptB() {
Get-ChildItem $path2 | Where-Object {!$_.PsIsContainer} | Select fullname -last 1
}
$data1=ScritA
$data2=ScriptB
$result=#()
$list=#{
FirstFile=$data1
SecondFile=$data2
}
$result+= New-Object psobject -Property $list
$result | Export-Csv -append -Path $csv
This will output:
FirstFile #{data1} and SecondFile #{data2}
Change your functions slightly to this -
function scriptA() {
Get-ChildItem $path | Where-Object {!$_.PsIsContainer} | Select-Object -ExpandProperty fullname -last 1
}
function scriptB() {
Get-ChildItem $path2 | Where-Object {!$_.PsIsContainer} | Select-Object -ExpandProperty fullname -last 1
}
Doing that will let you select only the FullName property.
OR
If you do not want to change the functions, change the $list assignment to -
$list=#{
FirstFile = $data1.FullName
SecondFile = $data2.FullName
}
New-Object PSObject -Property #{FirstFile = ($a = (Get-ChildItem $path1),(Get-ChildItem $path2) |
Where-Object {!$_.PSIsContainer} | ForEach-Object {$_[-1].FullName})[0];SecondFile = $a[1]} |
Export-Csv $csv -NoTypeInformation
I have a script that filters my logs, but the problem is that when I would like to delete everything else but certain files I get errors of Unrecognized escape sequence. I've been trying to split the values but it seems that nothing works. I also tried -exclude before, but didn't get it to work. It's supposed to remove all the other files but $result and $clr.
$files = #()
[xml]$photonconfig = Get-Content C:\Users\Administrator\Desktop\PhotonServer.config
$photonconfig.SelectNodes("Configuration/*") | Select-Object -Expand Name | % {
$_.Replace("xxx","")
} | ForEach {
$files+= Get-ChildItem C:\Users\Administrator\Desktop\log\log/*$_*.log |
sort -Property LastWriteTime -Descending |
Select-Object -First 3
}
$result = $files | Sort-Object LastAccessTime -Descending |
Select-Object -First 3
$clr = "PhotonCLR.log"
$all = Get-ChildItem C:\Users\Administrator\Desktop\log\log/* |
Where-Object { $_.Name -notmatch $result } |
Remove-Item
The second operand of the -match and -notmatch operators is a regular expression, not an array of file names. Use the -contains operator instead:
... | Where-Object { $result -notcontains $_.Name } | ...
On PowerShell v3 and newer you can also use the -notin operator, which feels a little more "natural" to most people:
... | Where-Object { $_.Name -notin $result } | ...
Note that for this to work you also need to expand the Name property when building $result:
$result = $files | Sort-Object LastAccessTime -Descending |
Select-Object -First 3 -Expand Name
I m trying to write a script to compare permissions of two folders at two different locations. The Folder name at both locations would be the ADID of an user and some users may have two or more AD accounts.
$OutPath = ".\out1.csv"
$sourcepath1 = Get-Content ".\mwd_src.txt"
foreach ($path1 in $sourcepath1) {
$name1 = (Get-Item $Path1).name
$FolderAcl1 = (Get-Acl $Path1).Access | Select-Object IdentityReference
$Source = $FolderAcl1 | Where-Object { $_.IdentityReference -like "*$name1*"} |
Select-Object #{ Label = "Path"; Expression = { echo $Path1 } }, #{ Label = "Access"; Expression = { $_.IdentityReference } }
}
$sourcepath2 = Get-Content ".\mwd_dest.txt"
foreach ($path2 in $sourcepath2) {
$name2 = (Get-Item $Path2).Name
$FolderAcl2 = (Get-Acl $Path2).Access | Select-Object IdentityReference
$Dest = $FolderAcl2 | Where-Object { $_.IdentityReference -like "*$name2*" } |
Select-Object #{ Label = "Path"; Expression = { echo $Path2 } }, #{ Label = "Access"; Expression = { $_.IdentityReference } }
}
$Source1 = $Source | Select-Object -Unique
$Dest1 = $Dest | Select-Object -Unique
$Out = Compare-Object -ReferenceObject $Source1 -DifferenceObject $Dest1
$Out1 = $Out | Where-Object { $_.SideIndicator -match "=>" }
foreach($OutItem in $Out1) {
$Outitem.InputObject | Add-Content $OutPath
}
But getting the following error
"Compare-Object : Cannot bind argument to parameter 'DifferenceObject' because it is null.
"
Please assist.
As I am missing information to your paths, I cannot reproduce your code but I tried to compare the ACLs for two local files with Compare-Object. I create two files which initially had the same accesses. If there is no difference, nothing is returned. I removed my account then from the permissions of the second file and got the output. (I shortened the output to avoid a horizontal bar.) The SideIndicator shows with the arrow to which object the difference belong.
InputObject SideIndicator
----------- -------------
{System.Security.AccessControl.FileSystemAccessRule, System.Security... =>
{System.Security.AccessControl.FileSystemAccessRule, System.Security... <=
My test code:
$ref = Get-Acl -Path .\Desktop\test1.txt
$diff = Get-Acl - Path .\Desktop\test2.txt
Compare-Object -ReferenceObject $ref.Access -DifferenceObject $diff.Access
I selected the Access property for comparison.
Regarding your error, ensure that the DifferenceObject is populated with the information you expect.