Getting name of folder which has specific ACL - powershell

I've recently had the task of updating the permissions structures on our user home drives. I have a directory called home and a folder per user below home. There are two groups at the Home level, and these are forced down to each of the user folders. The user folders are set to inherit from their parent, and then the user is set to access only their folder.
I'm trying to write a powershell script which will show me if any folders have a specific ACL left behind. This is what I ended up with, and it just seems to return the sub-folder list for the specified directory where as I want only the folders which have the specified ACL.
$path = "\\server\share"
$folders = Get-ChildItem $path | where {$_.psiscontainer}
foreach ($folder in $folders)
{
$domain = "domname"
$aclname = "ACLname"
$aclfullname ="$domain\$aclname"
Get-Acl | select -ExpandProperty Access | where {$_.identityreference -contains $aclfullname}
Write-Host $folder.FullName
}
If I use the following, it returns only one result, but it returns the ACL and not the folder name.
$path = "\\node1\home"
$domain = "morgan-cole"
$aclname = "gsherlock"
$aclfullname ="$domain\$aclname"
Get-ChildItem $path | where {$_.psiscontainer} | Get-Acl | select -ExpandProperty Access | where {$_.identityreference -contains $aclfullname}
Any ideas? Hopefully my requirements make sense.

This will do the trick:
Get-ChildItem $path | where {$_.psiscontainer} | where { $_ | Get-Acl | select -ExpandProperty Access | where {$_.IdentityReference -contains $aclfullname}}
Some explanation:
The reason why yours was not working the way you wanted in the second example is that it starts off as a folder in the pipeline, but is then transformed to an ACL that matches what you are looking for. However, it is now transformed into an ACL and you wanted to folder - not the actual ACL.
So, the "trick" is to keep the folder in the pipeline but filter the folder based on the ACL. This is achieved by nesting another pipeline inside the second where-object clause.
PS. There is probably a way to combine the first part that looks for psicontainer into the second where clause but let's leave that for another day.

Related

Im trying to create a powershell script that will find all of the groups or user names for multiple folders

I have successfully retrieved a list of folders from the selected drive and would like to iterate over this list for a list of groups or user names with access to the folder. What this means is that I am checking the permissions of each folder within the drive. Below is the code that I currently have.
#Import active directory module for running AD cmdlets
Import-Module activedirectory
#Get list of folders from the O drive
$folders = Get-ChildItem –Directory "O:\" | Select-Object -ExpandProperty Name
#for each folder retrieve the groups then export
ForEach ($folder in $folders)
{
$groups = Get-ACL "O:\$folder" | %{ $_.Access } | ft -property IdentityReference, AccessControlType, FileSystemRights
$folder | Export-CSV -Path FolderMembership.csv -Append
$groups | Export-CSV -Path FolderMembership.csv -Append
}
pause
When I run this code my csv file is filled with a length number and in between each length number are an arbitrary number of spaces that I believe coincide with the number of security groups for the folder that was supposed to be there. Can anyone help me figure out what is wrong with my get-ACL command? Also if there is a better command for this I would be happy to know what it is!

Removing old folders and keeping the most recent

I have been working on a script of late and have come across a snag. I am in the process of removing folders which are automatically created. I want to delete the older versions of those files whilst keeping the new folders untouched, for example:
18.212.1021.0008 //Created on the 19/11/2018 12:12
18.212.1021.0008_1 //Created on the 19/11/2018 12:23
18.212.1021.0008_2 //Created on the 19/11/2018 12:27
18.212.1021.0008_3 //Created on the 19/11/2018 12:32
I would want to keep 18.212.1021.008_3 so I guess I would need to keep the folder with the most recent creation date.
Please see the code below:
$Versionarray = 13..20
Get-ChildItem "$env:LOCALAPPDATA\Microsoft\OneDrive" -Recurse | Where-Object {
# Recusivly deletes OneDrive version folders within
# Appdata\local which build up everytime OneDrive
# is installed/script is run
$item = $_
$item -is [System.IO.DirectoryInfo] -and (
$Versionarray | Where-Object { $item.Name.Contains($_) }
)
} | Remove-Item -Recurse -Confirm:$false -ErrorAction SilentlyContinue
If the newest folder you want to keep is also the one with the newest creation time, you can use this simple one-liner:
Get-ChildItem "$env:LOCALAPPDATA\Microsoft\OneDrive" -Directory | sort CreationTime | select -SkipLast 1 | Remove-Item -Recurse -Force
If you want to filter out only a specific type of folders by name, you could use a simple regex match. I cannot help you with the exact regex (since I would have to know your folder naming pattern) but it would look something like this:
Get-ChildItem "$env:LOCALAPPDATA\Microsoft\OneDrive" -Directory | where Name -match '\d\d+' | sort CreationTime | select -SkipLast 1 | Remove-Item -Recurse -Force
(Note that this is syntax might not work if you use an old Powershell version. If that's the case, let me know and I will provide a compatible fallback solution.)
UPDATE
In response to your comment: Your requirements are still a bit unclear, but here is something to get you started:
If you want to make sure to only delete folders that "look like" version folders, you can adjust the regex in the where-filter. _\d+$ will match anything with an underscore and numbers at the end:
where $_.Name -match '_\d+$'
If you also want to make sure, that this is actually a versioned copy of another existing folder, you could check that too:
where { $_.FullName -match '^(?<OriginalPath>.+)_\d+$' -and (Test-Path $Matches.OriginalPath) }

Return the second level directory names only

I would like to retrieve all (and only) second level directory names of my disk. For example, C:\folder1\folder2 and C:\folder1\folder3, I need to retrieve only folder2 and folder3.
I write this and the PS displays all the directory names:
Get-ChildItem -Recurse | ?{ $_.PSIsContainer} | Select-Object Name
I found this help, and I modify the previous command in this way:
Get-ChildItem -Recurse | `Where-Object {($_.directory -match '^*\\\S*$')} ` | ForEach-Object {?{ $_.PSIsContainer} | Select-Object Name }
but when I use it the PS doesn't display anything.
I can't understand why, someone can help me? Thank you!
Only files appear to have a .directory property, directories do not, so you will never get something which passes your (.directory matches a pattern) filter and also passes your (PSIsContainer) filter.
Except that your PSIsContainer filter doesn't work:
| ForEach-Object {?{ $_.PSIsContainer} | Select-Object Name }
this doesn't make sense; you can only filter the pipeline using ? with cmdlet | ? {}, you cannot filter at the start of a loop scriptblock with no input and get anything useful. This is running where-object {} over and over in a loop, - and that has no output.
Using -Recurse will be very slow, as you go into every single directory all the way to the end, and make [fileinfo] objects for all the files as well.
Apart from Matt's wildcard answer, assuming PS v3 or above, you could list all the directories in the root, and then all the directories inside those, and stop there:
Get-ChildItem c:\ -Directory | Get-ChildItem -Directory | Select -ExpandProperty Name
or
gci c:\ -Dir | ForEach { (gci $_ -Dir).Name }
You should just be able to use some fun wildcards to get what you want here.
Get-ChildItem \*\*\ | Where-Object{$_.PSIsContainer}
Or if you have at least PowerShell 3.0 this would be faster
Get-ChildItem \*\*\ -Directory
Then if you wanted just the names tack on | Select-Object -ExpandProperty Name
Here with full path, network compatible:
(Get-ChildItem "\\folder1\" -Dir).fullname | ForEach {(Get-ChildItem $_ -Dir).name}
Want it stored in an array?
$subfolders = (Get-ChildItem "\\folder1\" -Dir).fullname | ForEach {(Get-ChildItem $_ -Dir).name}

PowerShell to find usernames that have access

I am trying to get the ACL of a set of folders to see if a specific user is listed
For example
Users
|
---Person1
---Person2
---Person3
Person1 to 3 are home folders. We recently ran an icacls command to modify the folder permission. Some of them have the owner set to "IT Employee" instead of Person1
If it was only 3 folders, I would do this manually. However there are at least 1000 folders and manually would not be feasible to get the data back in a timely manner.
Basically there are 6 IT Employees and I want to make sure their name is not in any Person home folder (or it's sub folders). If it is there then I want to be able to remove them or at least get a console log.
I am using PowerShell 2 on Windows Server 2008
I can also execute VBScript or JavaScript
You could try something like this to get you started. I'm not connected to a network with a file server atm., so I'm not sure if Owner and IdentityReference contains DOMAIN\Username or SID (this happends for non-exisiting users, ex. deleted ones). I get <DOMAIN or ComputerName>\Username when I run it on m local machine. You may have to modify it to handle that.
$rootpath = "c:\users"
#Get all folders
Get-ChildItem -Path $rootpath -Recurse | Where-Object { $_.PSIsContainer }
#Get ACL for the folders
Get-Acl |
#Find ACLs with IT Employee-reference
Where-Object {
#Check if owner matches 'IT Employee' or ACL Access rules contains 'IT Employee'
if(($_.Owner -match 'IT Employee') -or ($_.Access | Where-Object { $_.IdentityReference.Value -match 'IT Employee' })) { $_ }
} |
#Process
ForEach-Object {
#Show folderpath...
$_.Path
#Here you could access the ACL-object $_, modify it (change owner/remove access rules) and save it by using 'Set-Acl -Path $_.Path -AclObject $_' etc.
}

Delete files containing string

How can I delete all files in a directory that contain a string using powershell?
I've tried something like
$list = get-childitem *.milk | select-string -pattern "fRating=2" | Format-Table Path
$list | foreach { rm $_.Path }
And that worked for some files but did not remove everything. I've tried other various things but nothing is working.
I can easily get the list of file names and can create an array with the path's only using
$lista = #(); foreach ($f in $list) { $lista += $f.Path; }
but can't seem to get any command (del, rm, or Remove-Item) to do anything. Just returns immediately without deleting the files or giving errors.
Thanks
First we can simplify your code as:
Get-ChildItem "*.milk" | Select-String -Pattern "fRating=2" | Select-Object -ExcludeProperty path | Remove-Item -Force -Confirm
The lack of action and errors might be addressable by one of two things. The Force parameter which:
Allows the cmdlet to remove items that cannot otherwise be changed,
such as hidden or read-only files or read-only aliases or variables.
I would aslo suggest that you run this script as administrator. Depending where these files are located you might not have permissions. If this is not the case or does not work please include the error you are getting.
Im going to guess the error is:
remove-item : Cannot remove item C:\temp\somefile.txt: The process cannot access the file 'C:\temp\somefile.txt'
because it is being used by another process.
Update
In testing, I was also getting a similar error. Upon research it looks like the Select-String cmd-let was holding onto the file preventing its deletion. Assumption based on i have never seen Get-ChildItem do this before. The solution in that case would be encase the first part of this in parentheses as a sub expression so it would process all the files before going through the pipe.
(Get-ChildItem | Select-String -Pattern "tes" | Select-Object -ExpandProperty path) | Remove-Item -Force -Confirm
Remove -Confirm if deemed required. It exists as a precaution so that you don't open up a new powershell in c:\windows\system32 and copy paste a remove-item cmdlet in there.
Another Update
[ and ] are wildcard searches in powershell in order to escape those in some cmdlets you use -Literalpath. Also Select-String can return multiple hits in files so we should use -Unique
(Get-ChildItem *.milk | Select-String -Pattern "fRating=2" | Select-Object -ExpandProperty path -Unique) | ForEach-Object{Remove-Item -Force -LiteralPath $_}
Why do you use select-string -pattern "fRating=2"? You would like to select all files with this name?
I think the Format-Table Path don't work. The command Get-ChildItem don't have a property called "Path".
Work this snipped for you?
$list = get-childitem *.milk | Where-Object -FilterScript {$_.Name -match "fRating=2"}
$list | foreach { rm $_.FullName }
The following code gets all files of type *.milk and puts them in $listA, then uses that list to get all the files that contain the string fRating=[01] and stores them in $listB. The files in $listB are deleted and then the number of files deleted versus the number of files that contained the match is displayed(they should be equal).
sv -name listA -value (Get-ChildItem *.milk); sv -name listB -value ($listA | Select-String -Pattern "fRating=[01]"); (($listB | Select-Object -ExpandProperty path) | ForEach-Object {Remove-Item -Force -LiteralPath $_}); (sv -name FCount -value ((Get-ChildItem *.milk).Count)); Write-Host -NoNewline Files Deleted ($listA.Count - $FCount)/($listB.Count)`n;
No need to complicate things:
1. $sourcePath = "\\path\to\the\file\"
2. Remove-Item "$sourcePath*whatever*"
I tried the answer, unfortunately, errors seems to always come up, however, I managed to create a solution to get this done:
Without using Get-ChilItem; You can use select-string directly to search for files matching a certain string, yes, this will return the filename:count:content ... etc, but, internally these have names that you can chose or omit, the one you need is the "filename" to do this pipe this into "select-object" choosing the "FileName" from the output.
So, to select all *.MSG files that has the pattern of "Subject: Webservices restarted", you can do the following:
Select-String -Path .*.MSG -Pattern 'Subject: WebServices Restarted'
-List | select-object Filename
Also, to remove these files on the fly, you could pip into a ForEach statement with the RM command as follows:
Select-String -Path .*.MSG -Pattern 'Subject: WebServices Restarted'
-List | select-object Filename | foreach { rm $_.FileName }
I tried this myself, works 100%.
I hope this helps