I am recursively getting a list of folders with their respective permissions in a powershell script, however when the recursive part happens my output string keeps printing the folder structure each time an example of this is:
I have a folder called C:\temp, within that folder are 2 empty folders C:\temp\folder1 and C:\temp\folder2. With my script the output would be:
I have left out the permissions for readability
C:\temp
C:\temp\folder1
C:\temp
C:\temp\folder2
I don't want this to happen I want a list of folders with their permissions and then if the permissions on a child folder are different then look at the get the child folders of that folder. This works apart from the string building which I think I need a fresh pair of eyes to look at it because I'm getting nowhere.
Appreciate the help in advance,
Sam
CODE:
Add-Type -AssemblyName System.Windows.Forms
Import-Module ActiveDirectory
$info = ""
$OutputString
$step = 0
function DisplayForm{
#Some GUI code
#$textBox takes in the base folder from the user
if ($result -eq [System.Windows.Forms.DialogResult]::OK)
{
$baseFolder = $textBox.Text
$ParentProperties = (Get-Acl $baseFolder).Access| Select-Object -ExpandProperty IdentityReference
$OutputString = $OutputString + $baseFolder + "`r`n" + $ParentProperties + "`r`n`r`n"
$ChildFolders = Get-ChildItem $baseFolder | where {$_.Attributes -eq 'Directory'}
FindPriorities($baseFolder)
$info = "SAVED TO FOLDER"
outputList
}
}
function FindPriorities{
param($fileName)
$ChildFolders = Get-ChildItem $fileName | where {$_.Attributes -eq 'Directory'}
$step = $step + 1
$TempString = ""
foreach ($folder in $ChildFolders){
$child = $fileName + "\\" + $folder.name
$ParentProperties = (Get-Acl $fileName).Access| Select-Object -ExpandProperty IdentityReference
$ChildProperties = (Get-Acl $child).Access| Select-Object -ExpandProperty IdentityReference
$parentString=""
foreach ($p in $ParentProperties){
$parentString= $parentString + $p
}
$childString=""
foreach ($c in $childProperties){
$childString = $childString + $c
}
if($childString -ne $parentString){
$OutputString = $OutputString + $child + "`r`n" + $ChildProperties + "`r`n`r`n"
FindPriorities ($child)
}else{
$OutputString = $OutputString + $child + "`r`n" + $ChildProperties + "`r`n`r`n"
}
}
}
function outputList{
$OutputString
}
DisplayForm
I think I understood what you want to do.
Please give this snippet a try:
function Get-IdentityReference($path) {
Get-Acl $path |
Select-Object -ExpandProperty Access |
Select-Object -ExpandProperty IdentityReference
}
function Extract-Permissions($baseFolder) {
$folders = Get-ChildItem $baseFolder | Where-Object { $_.PSisContainer }
$baseACL = Get-IdentityReference $baseFolder
"$baseFolder : $baseACL"
foreach($folder in $folders) {
$folderACL = Get-IdentityReference $folder.FullName
$childFolders = Get-ChildItem $folder.FullName | Where-Object { $_.PSisContainer }
"$($folder.FullName) : $folderACL"
foreach($childFolder in $childFolders) {
$childACL = Get-IdentityReference $childFolder.FullName
if(Compare-Object $childACL $folderACL) {
Extract-Permissions $childFolder.FullName
} else {
"$($childFolder.FullName) : $childACL"
}
}
}
}
$baseFolder = "$env:USERPROFILE\Desktop"
Extract-Permissions $baseFolder
Related
I have a script which will compare two very similar directories to see which has newer, updated files. The two paths have the same files but the path names are locations that are slightly different. The two folders have about the same amount of files, maybe two or three less than the other.
$path1 = "E:\docs\training\files"
$path2 = "D:\docs\training - Copy\files"
$outdatedFiles = #()
foreach($file in $Folder1)
{
foreach($file2 in $Folder2)
{
if($file2.BaseName -match $file.BaseName)
{
if($file.LastWriteTime -gt $file2.LastWriteTime)
{
$Result = "" | Select OutDatedFile,LastWriteTime
$Result.OutDatedFile = $file2.FullName
$Result.LastWriteTime = $file2.LastWriteTime
$outdatedFiles += $Result
}
}
}
}
In the $outdatedFiles array, I get files that are not newer than their counterpart in the other directory. I think it might be due to my comparison in the if statement, I tried -match, -contains, and -ccontains to see if any of these would give me what I wanted. Neither worked. It might be that the foreach doesn't work due to the slightly different amount of files in each folder. Any suggestions?
EDIT
I tried building a hash but this did not find all the updated files:
$outdatedFiles = #()
foreach($file in $Folder1)
{
foreach($file2 in $Folder2)
{
if($file2.Name -like $file.Name)
{
#compare hash here
$Hash2 = Get-FileHash $file2.FullName -Algorithm SHA256
$Hash1 = Get-FileHash $file.FullName -Algorithm SHA256
if($Hash2.Hash -ne $Hash1.Hash)
{
$Result = "" | Select OutDatedFile,LastWriteTime
$Result.OutDatedFile = $file2.FullName
$Result.LastWriteTime = $file2.LastWriteTime
$outdatedFiles += $Result
}
}
}
}
EDIT
This was my solution
$Differences1 = #()
foreach($file in $Folder1)
{
foreach($file2 in $Folder2)
{
<#Trim path then compare#>
#File1
$file1part1 = ($file.FullName).Split("\")[-2]
$file1part2 = ($file.FullName).Split("\")[-1]
$newPath1 = $file1part1 + "\" + $file1part2
#File2
$file2part1 = ($file2.FullName).Split("\")[-2]
$file2part2 = ($file2.FullName).Split("\")[-1]
$newPath2 = $file2part1 + "\" + $file2part2
if($newPath1 -like $newPath2)
{
$Differences1 += Compare-Object (gci $file2.FullName) -DifferenceObject (gci $file.FullName) -Property LastWriteTime -PassThru | Select Name,FullName,LastWriteTime | Sort-Object -Property Name
}
}
}
if($Differences1 -ne $null)
{
$Differences1 | Out-File $textFile -Append
}
else
{
"No folders have different modified dates" | Out-File $textFile -Append
}
In the end, the solution was more complex than I wanted, or maybe I just made it that way.
The problem was I had multiple files with the same name and similar paths, as in the subfolder was named the same. I had to trim the path to be able to get a better comparison:
$Differences1 = #()
foreach($file in $Folder1)
{
foreach($file2 in $Folder2)
{
<#Trim path then compare#>
#File1
$file1part1 = ($file.FullName).Split("\")[-2]
$file1part2 = ($file.FullName).Split("\")[-1]
$newPath1 = $file1part1 + "\" + $file1part2
#File2
$file2part1 = ($file2.FullName).Split("\")[-2]
$file2part2 = ($file2.FullName).Split("\")[-1]
$newPath2 = $file2part1 + "\" + $file2part2
if($newPath1 -like $newPath2)
{
$Differences1 += Compare-Object (gci $file2.FullName) -DifferenceObject (gci $file.FullName) -Property LastWriteTime -PassThru | Select Name,FullName,LastWriteTime | Sort-Object -Property Name
}
}
}
if($Differences1 -ne $null)
{
$Differences1 | Out-File $textFile -Append
}
else
{
"No folders have different modified dates" | Out-File $textFile -Append
}
My objective is to write a powershell script that will recursively check a file server for any directories that are "x" (insert days) old or older.
I ran into a few issues initially, and I think I got most of it worked out. One of the issues I ran into was with the path limitation of 248 characters. I found a custom function that I am implementing in my code to bypass this limitation.
The end result is I would like to output the path and LastAccessTime of the folder and export the information into an easy to read csv file.
Currently everything is working properly, but for some reason I get some paths output several times (duplicates, triples, even 4 times). I just want it output once for each directory and subdirectory.
I'd appreciate any guidance I can get. Thanks in advance.
Here's my code
#Add the import and snapin in order to perform AD functions
Add-PSSnapin Quest.ActiveRoles.ADManagement -ea SilentlyContinue
Import-Module ActiveDirectory
#Clear Screen
CLS
Function Get-FolderItem
{
[cmdletbinding(DefaultParameterSetName='Filter')]
Param (
[parameter(Position=0,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[Alias('FullName')]
[string[]]$Path = $PWD,
[parameter(ParameterSetName='Filter')]
[string[]]$Filter = '*.*',
[parameter(ParameterSetName='Exclude')]
[string[]]$ExcludeFile,
[parameter()]
[int]$MaxAge,
[parameter()]
[int]$MinAge
)
Begin
{
$params = New-Object System.Collections.Arraylist
$params.AddRange(#("/L","/S","/NJH","/BYTES","/FP","/NC","/NFL","/TS","/XJ","/R:0","/W:0"))
If ($PSBoundParameters['MaxAge'])
{
$params.Add("/MaxAge:$MaxAge") | Out-Null
}
If ($PSBoundParameters['MinAge'])
{
$params.Add("/MinAge:$MinAge") | Out-Null
}
}
Process
{
ForEach ($item in $Path)
{
Try
{
$item = (Resolve-Path -LiteralPath $item -ErrorAction Stop).ProviderPath
If (-Not (Test-Path -LiteralPath $item -Type Container -ErrorAction Stop))
{
Write-Warning ("{0} is not a directory and will be skipped" -f $item)
Return
}
If ($PSBoundParameters['ExcludeFile'])
{
$Script = "robocopy `"$item`" NULL $Filter $params /XF $($ExcludeFile -join ',')"
}
Else
{
$Script = "robocopy `"$item`" NULL $Filter $params"
}
Write-Verbose ("Scanning {0}" -f $item)
Invoke-Expression $Script | ForEach {
Try
{
If ($_.Trim() -match "^(?<Children>\d+)\s+(?<FullName>.*)")
{
$object = New-Object PSObject -Property #{
ParentFolder = $matches.fullname -replace '(.*\\).*','$1'
FullName = $matches.FullName
Name = $matches.fullname -replace '.*\\(.*)','$1'
}
$object.pstypenames.insert(0,'System.IO.RobocopyDirectoryInfo')
Write-Output $object
}
Else
{
Write-Verbose ("Not matched: {0}" -f $_)
}
}
Catch
{
Write-Warning ("{0}" -f $_.Exception.Message)
Return
}
}
}
Catch
{
Write-Warning ("{0}" -f $_.Exception.Message)
Return
}
}
}
}
Function ExportFolders
{
#================ Global Variables ================
#Path to folders
$Dir = "\\myFileServer\somedir\blah"
#Get all folders
$ParentDir = Get-ChildItem $Dir | Where-Object {$_.PSIsContainer -eq $True}
#Export file to our destination
$ExportedFile = "c:\temp\dirFolders.csv"
#Duration in Days+ the file hasn't triggered "LastAccessTime"
$duration = 800
$cutOffDate = (Get-Date).AddDays(-$duration)
#Used to hold our information
$results = #()
#=============== Done with Variables ===============
ForEach ($SubDir in $ParentDir)
{
$FolderPath = $SubDir.FullName
$folders = Get-ChildItem -Recurse $FolderPath -force -directory| Where-Object { ($_.LastAccessTimeUtc -le $cutOffDate)} | Select-Object FullName, LastAccessTime
ForEach ($folder in $folders)
{
$folderPath = $folder.fullname
$fixedFolderPaths = ($folderPath | Get-FolderItem).fullname
ForEach ($fixedFolderPath in $fixedFolderPaths)
{
#$fixedFolderPath
$getLastAccessTime = $(Get-Item $fixedFolderPath -force).lastaccesstime
#$getLastAccessTime
$details = #{ "Folder Path" = $fixedFolderPath; "LastAccessTime" = $getLastAccessTime}
$results += New-Object PSObject -Property $details
$results
}
}
}
}
ExportFolders
I updated my code a bit and simplified it. Here is the new code.
#Add the import and snapin in order to perform AD functions
Add-PSSnapin Quest.ActiveRoles.ADManagement -ea SilentlyContinue
Import-Module ActiveDirectory
#Clear Screen
CLS
Function ExportFolders
{
#================ Global Variables ================
#Path to user profiles in Barrington
$Dir = "\\myFileServer\somedir\blah"
#Get all user folders
$ParentDir = Get-ChildItem $Dir | Where-Object {$_.PSIsContainer -eq $True} | where {$_.GetFileSystemInfos().Count -eq 0 -or $_.GetFileSystemInfos().Count -gt 0}
#Export file to our destination
$ExportedFile = "c:\temp\dirFolders.csv"
#Duration in Days+ the file hasn't triggered "LastAccessTime"
$duration = 1
$cutOffDate = (Get-Date).AddDays(-$duration)
#Used to hold our information
$results = #()
$details = $null
#=============== Done with Variables ===============
ForEach ($SubDir in $ParentDir)
{
$FolderName = $SubDir.FullName
$FolderInfo = $(Get-Item $FolderName -force) | Select-Object FullName, LastAccessTime #| ft -HideTableHeaders
$FolderLeafs = gci -Recurse $FolderName -force -directory | Where-Object {$_.PSIsContainer -eq $True} | where {$_.GetFileSystemInfos().Count -eq 0 -or $_.GetFileSystemInfos().Count -gt 0} | Select-Object FullName, LastAccessTime #| ft -HideTableHeaders
$details = #{ "LastAccessTime" = $FolderInfo.LastAccessTime; "Folder Path" = $FolderInfo.FullName}
$results += New-Object PSObject -Property $details
ForEach ($FolderLeaf in $FolderLeafs.fullname)
{
$details = #{ "LastAccessTime" = $(Get-Item $FolderLeaf -force).LastAccessTime; "Folder Path" = $FolderLeaf}
$results += New-Object PSObject -Property $details
}
$results
}
}
ExportFolders
The FolderInfo variable is sometimes printing out multiple times, but the FolderLeaf variable is printing out once from what I can see. The problem is if I move or remove the results variable from usnder the details that print out the folderInfo, then the Parent directories don't get printed out. Only all the subdirs are shown. Also some directories are empty and don't get printed out, and I want all directories printed out including empty ones.
The updated code seems to print all directories fine, but as I mentioned I am still getting some duplicate $FolderInfo variables.
I think I have to put in a condition or something to check if it has already been processed, but I'm not sure which condition I would use to do that, so that it wouldn't print out multiple times.
In your ExportFolders you Get-ChildItem -Recurse and then loop over all of the subfolders calling Get-FolderItem. Then in Get-FolderItem you provide Robocopy with the /S flag in $params.AddRange(#("/L", "/S", "/NJH", "/BYTES", "/FP", "/NC", "/NFL", "/TS", "/XJ", "/R:0", "/W:0")) The /S flag meaning copy Subdirectories, but not empty ones. So you are recursing again. Likely you just need to remove the /S flag, so that you are doing all of your recursion in ExportFolders.
In response to the edit:
Your $results is inside of the loop. So you will have a n duplicates for the first $subdir then n-1 duplicates for the second and so forth.
ForEach ($SubDir in $ParentDir) {
#skipped code
ForEach ($FolderLeaf in $FolderLeafs.fullname) {
#skipped code
}
$results
}
should be
ForEach ($SubDir in $ParentDir) {
#skipped code
ForEach ($FolderLeaf in $FolderLeafs.fullname) {
#skipped code
}
}
$results
The problem with this code is that for each folder, I only get 1 group or 1 user depending on the folder (mostly the last one), while I want to get all the security groups, and members of the folder, for each folder in the drive.
$title = "ADPermissions.csv"
$title2 = "ADPermissions2.csv"
$ss =$Selection1 -replace '[\W]', ''
$subtitle = "HardDrive"+ $ss
$exclude = #("BUILTIN|NT AUTHORITY|EVERYONE|CREATOR OWNER|NT AUTHORITY\SYSTEM|SYSTEM")
$OutFile = ($Selection2+"\"+$subtitle+$title)
Write-Host = $OutFile
if(Test-Path $OutFile -PathType Leaf)
{
$result3 = [System.Windows.MessageBox]::Show("The file already exists in the selected path`n"+"Do you want to delete it and proceed ?",'File already exists','YesNoCancel','Exclamation')
if ($result3 = "Yes")
{
Remove-Item $OutFile
$Header = "Folder Path,IdentityReference,names"
$RootPath = $Selection
$Folders = dir $RootPath | where {$_.psiscontainer -eq $true}
#To the point:
try {
foreach ($Folder in $Folders){$ACLs = get-acl $Folder.fullname | ForEach-Object { $_.Access } | where {$_.identityreference -notmatch $exclude}
Foreach ($ACL in $ACLs){
$strAcls = $ACL.IdentityReference.ToString()
$strUsers=#()
$strNames=$strAcls.Remove(0,12)
$user = $(try {Get-ADUser $strNames} catch {$null})
if ($strNames -ne $null -and $user -eq $null) {
$A += Get-ADGroupMember -identity $strNames -Recursive | Get-ADUser -Property DisplayName | Select Name | Sort-Object Name
} else {
}
foreach ($env:USERNAME in $A){
$strUsers +=$env:USERNAME
}
$OutInfo = $Folder.fullname + "," + $ACL.IdentityReference + $strUsers
}
Add-Content -Value $OutInfo -Path $OutFile | sort-Object
}
}catch [System.IO.IOException] {
}
}
I:\Dropbox,GESCOEUROPE\GR_G-FCASB-INT-ALL#{Name=CAPPUCCILLI FEDERICO}
#{Name=De Fruyt Frederik}
I:\General,GESCOEUROPE\GR_G-FCASB-INT-ADMIN#{Name=CAPPUCCILLI
FEDERICO} #{Name=De Fruyt Frederik#{Name=VANDEWALLE MARIA}
#{Name=VANSTEELANDT LUCRECE}
I:\ICT,GESCOEUROPE\GR_G-FCASB-INT-ADMIN#{Name=CAPPUCCILLI FEDERICO}
#{Name=De Fruyt Frederik} #{Name=FREDERIK DE FRUYT (ADM)}
#{Name=GAILITE ZANETE} #{Name=Geldhof Francine} #{Name=GOEMAERE
GWENNY}
I:\PaymentFollow-Up,GESCOEUROPE\GR_G-FCASB-INT-ALL#{Name=CAPPUCCILLI
FEDERICO}
this is the output i get, as you see for each folder I have only 1
group, But thats incorrect because some folders have more than 1 group
I have a directory that our work order program dumps xml files into. I need to search those files for a specific string and then copy them to another location based on that string. I modified the below code from another post and while I don't get any errors it also doesn't work. I'm very much a scripting newbie so any help would be greatly appreciated.
[string] $FileDirectory = "D:\Temp";
[string] $OutputPath = "D:\Temp\Temp_NY";
[string] $OutputPath2 = "D:\Temp\TEMP_FL";
foreach ($FilePath in Get-ChildItem $FileDirectory | Select-Object -ExpandProperty FullName)
{
[string] $Header = Get-Content $FilePath -First 0
if ($Header -match 'PARTNER |TEST_NY') {
Copy-Item $FilePath $OutputPath
}
elseif ($Header -match 'PARTNER |TEST_FL*') {
Copy-Item $FilePath $OutputPath2
}
}
The header would be -First 1 (first line only). -First 0 returns nothing. Try:
$FileDirectory = "D:\Temp";
$OutputPath = "D:\Temp\Temp_NY";
$OutputPath2 = "D:\Temp\TEMP_FL";
Get-ChildItem $FileDirectory | ? { !$_.PSIsContainer } | ForEach-Object {
$FilePath = $_.FullName
$Header = Get-Content $FilePath -First 1
if ($Header -match 'PARTNER |TEST_NY') {
Copy-Item $FilePath $OutputPath
}
elseif ($Header -match 'PARTNER |TEST_FL*') {
Copy-Item $FilePath $OutputPath2
}
}
I need create this list to allow an other program to properly work. I use this code:
function analyse {
Param(
[parameter(Mandatory=$true)]
[String]$newPath
)
cd $newPath
dir | Foreach-Object {
$data = Get-Content -Path o:\******\public\ParcoursArborescence\Limitless\data.txt
if ($_.PsisContainer -eq $True) {
$testPath = $_.FullName + ";"
$name = $testPath
$testPath = $data -match [regex]::escape($testPath)
$testpath
if($testPath.Length -eq 0) {
$name | Out-File -Append "o:\******\public\ParcoursArborescence\Limitless\data.txt"
if ($_.FullName.Length -gt 248) {
"ecriture"
$result += $_.FullName + "`r"
} else {
"nouvelle analyse"
$_.Fullname
analyse $_.FullName
}
}
} else {
$testPath = $_.Directory.FullName + ";"
$name = $testPath
$testPath = $data -match [regex]::escape($testPath)
if($testPath.Length -eq 0) {
$name | Out-File -Append "o:\******\public\ParcoursArborescence\Limitless\data.txt"
$_.FullName.Length
if ($_.FullName.Length -gt 260) {
"ecriture2"
$result += $_.Directory.Name + "`r"
}
}
}
}
$result | Out-File -Append "o:\******\public\ParcoursArborescence\Limitless\bilanLimitless.txt"
}
But it takes hours and hours... I need to use this in thousands of folders. So, do you have any idea about how could it get faster ?
Maybe I'm oversimplifying things here, but why not list all the files at once, and test their FullName Length (PS 3.0 needed for the -File parameter of Get-ChildItem) ?
$maxLength = 248
Get-ChildItem $newPath -Recurse |
Where-Object { ($_.FullName.Length -gt $maxLength) } |
Select-Object -ExpandProperty DirectoryName -Unique |
Out-File "overlength_paths.txt"
For PS 2.0:
$maxLength = 248
Get-ChildItem $newPath -Recurse -File |
Where-Object { ($_.FullName.Length -gt $maxLength) -and (-not $_.PSisContainer) } |
Select-Object -ExpandProperty DirectoryName -Unique |
Out-File "overlength_paths.txt"