I need assistance in filtering what is going in to my output.
Here is my code:
$FolderPath = Get-ChildItem -Directory -Path "Z:\D482\F11\SECURE" -Recurse -Force
$Output = #()
ForEach ($Folder in $FolderPath) {
$Acl = Get-Acl -Path $Folder.FullName
ForEach ($Access in $Acl.Access) {
$Properties = [ordered]#{'Folder Name'=$Folder.FullName;'Group/User'=$Access.IdentityReference }
$Output += New-Object -TypeName PSObject -Property $Properties
}
}
$Output | ConvertTo-Csv | Out-File C:\Temp\Secured.txt
My output looks like this:
#TYPE System.Management.Automation.PSCustomObject
"Folder Name","Group/User"
"Z:\D482\F11\SECURE\QA\CDM\To_Load\Duals\Load_Completed","S-1-5-21-1275210071-879983540-1801674531-105509"
"Z:\D482\F11\SECURE\QA\Files\CDM\To_Load\Duals\Load_Completed","S-1-5-21-1275210071-879983540-1801674531-121910"
"Z\D482\F11\SECURE\QA\D482\To_Load\Duals\Load_Completed","DOMAIN\CXL3708"
In my output, I only want lines that contain our domain name ( as illustrated by the line with DOMAIN in it.
I have not been successful - either I get nothing, or I get error messages in the console.
here is one way to do the job ... [grin]
what it does ...
sets the constants
builds a parameter splat for the Get-ChildItem call
grabs the dirs from the source path
iterates thru the dir list
gets the acl list for the current dir & filters for those that start with the required domain name
note that i don't have a .IdentityReference property in my ACL, so i used .Owner instead.
iterates thru those acl items
builds a [PSCustomObject] for the current acl
sends that out to the $AccessInfo collection
displays the content of the above on screen
saves the collection to a csv file
the code ...
$SourcePath = $env:TEMP
$DomainName = $env:USERDOMAIN
$ReportFile = "SO_Erich_Powershell - How To Filter Output.csv"
$FullReportFile = Join-Path -Path $env:TEMP -ChildPath $ReportFile
$GCI_Params = #{
LiteralPath = $SourcePath
Directory = $True
Force = $True
Recurse = $True
ErrorAction = 'SilentlyContinue'
}
$DirList = Get-ChildItem #GCI_Params
$AccessInfo = foreach ($DL_Item in $DirList)
{
$AclList = Get-Acl -LiteralPath $DL_Item.FullName |
Where-Object {
$_.Owner -match "^$DomainName"
}
foreach ($AL_Item in $AclList)
{
[PSCustomObject]#{
DirName = $DL_Item.FullName
# my single system has no ".IdentityReference" property
# so i used ".Owner"
GroupOrUser = $AL_Item.Owner
}
}
}
# display the data
$AccessInfo
# send to a csv file
$AccessInfo |
Export-Csv -LiteralPath $FullReportFile -NoTypeInformation
truncated screen output ...
DirName GroupOrUser
------- -----------
C:\Temp\1 MySysName\AnotherUserName
C:\Temp\2 MySysName\AnotherUserName
C:\Temp\3 MySysName\AnotherUserName
[*snip ...*]
C:\Temp\vscode-update-system-x64 MySysName\MyUserName
C:\Temp\WPF MySysName\MyUserName
C:\Temp\mbam\qt-jl-icons MySysName\MyUserName
truncated csv file content ...
"DirName","GroupOrUser"
"C:\Temp\1","MySysName\AnotherUserName"
"C:\Temp\2","MySysName\AnotherUserName"
[*snip ...*]
"C:\Temp\WPF","MySysName\MyUserName"
"C:\Temp\mbam\qt-jl-icons","MySysName\MyUserName"
Related
I'm trying to get the script to read the first line of files.txt, grab the data metadata requested and then output the .xml, move onto the next line and repeat.
I expect each line to have its own individual file with the meta data and then the next line to do the same.
Currently it creates all the individual files, but the data is combined and duplicated across them.
The files.txt contains the full path and files that is being
collected with the metadata e.g.
D:\data\testscript.ps1
D:\data\workingfile.doc
C:\Windows\temp\output.txt
Filesv2.txt contain the filename of the xml output and is consistent
in array with files.txt e.g
D_data_testscript.ps1
D_data_workingfile.doc
C_Windows_temp_output.txt
$logdir = "C:\Users\gnome\Documents"
$inputPath = Get-Content -Path "C:\Users\gnome\Documents\files.txt"
$inputSave = Get-Content -Path "C:\Users\gnome\Documents\filesv2.txt"
#Get-*
$hash = Get-FileHash -Path $inputPath
$acl = Get-Acl -Path $inputPath | Select-Object *
$metadata = Get-ChildItem -Path $inputPath | Select-Object *
#Loop each directory in $inputPath
#ForEach ($path in $inputPath){
$output = ForEach ($path in $inputPath){
Write-host Checking $path
ForEach($inputSave in $inputSave){
#{
#$log = "$logdir\$inputSave.xml"
sha256Hash = $hash
acl = $acl
metadata =$metadata
}
$output | Export-Clixml "$logdir\test1_$inputSave.xml"
}
}
'''
From your comment, files.txt stores full path and filenames and filesv2.txt has new names for these files according to some naming convention to be used for the output xml filename.
Having both arrays separate from eachother in separate files is somewhat accident-prone, because all that links the file name to the convention name is the index in both arrays..
Below first creates a Hashtable from these arrays assuming the indices match and both arrays have the same number of elements
$logdir = "C:\Users\gnome\Documents"
$inputPath = #(Get-Content -Path "C:\Users\gnome\Documents\files.txt") # full path and filenames
$inputSave = #(Get-Content -Path "C:\Users\gnome\Documents\filesv2.txt") # naming convention for the output
# create a Hashtable where the input from files.txt is key and the naming convention for the output xml is value
$filesHash = #{}
for ($i = 0; $i -lt $inputPath.Count; $i++) {
$filesHash[$inputPath[$i]] = $inputSave[$i]
}
# now iterate
$filesHash.GetEnumerator() | ForEach-Object {
Write-host Checking $_.Key
$output = [PsCustomObject]#{
sha256Hash = Get-FileHash -Path $_.Key -Algorithm SHA256
acl = Get-Acl -Path $_.Key
metadata = Get-Item -Path $_.Key
}
$outFile = Join-Path -Path $logdir -ChildPath ('{0}.xml' -f $_.Value)
$output | Export-Clixml -Path $outFile
}
I am using the following script to read a list of file names which are then deleted. Is there a way can get an output of the date and time each file is deleted?
$targetFolder = "D:\" $fileList = "C:\DeleteList.txt" Get-ChildItem
-Path "$targetFolder\*" -Recurse -Include #(Get-Content $fileList) | Remove-Item -Verbose
Thanks for any help.
You could keep track of the files that are deleted and the time of deletion by outputting an object with the file's fullname and current date.
This output can then be saved as structured CSV file
$targetFolder = "D:\"
$fileList = Get-Content -Path "C:\DeleteList.txt"
$deleted = Get-ChildItem -Path $targetFolder -Recurse -Include $fileList | ForEach-Object {
# output an object with the current date and the file FullName
$_ | Select-Object #{Name = 'DeletedOn'; Expression = {(Get-Date)}}, FullName
$_ | Remove-Item -WhatIf
}
# output on screen
$deleted | Format-Table -AutoSize
# output to csv file
$deleted | Export-Csv -Path 'C:\RemovedFiles.csv' -NoTypeInformation
Remove the -WhatIf safety-switch if you are satisfied with the results shown on screen.
Would this work?
$targetFolder = "D:"
$fileList = "C:\DeleteList.txt"
$Files = Get-ChildItem -Path "$targetFolder" -Recurse -Include #(Get-Content $fileList)
# Once you have the desires files stored in the $Files variable, then run a Foreach loop.
$Obj = #() # create an array called $Obj
Foreach ($File in $Files)
{
# store info in hash table
$hash = #{
DateTime = (get-date)
fileName = $File.name
fullpath = $File.fullname
}
Write-Host "deleting file $($file.name)" -for cyan
Remove-Item $File.fullname # *** BE VERY CAREFUL!!!***
# record information in an array called $Obj
$Obj += New-Object psobject -Property $hash
}
$Obj | select fileName, DateTime | Export-csv C:\...
I am trying to create a script to read each folder name in a directory, count of zip files in each folder and then count of files in each zip. The output need to be written in an output file.
I came up with below:
$ZipRoot = 'C:\Users\Main Folder'
$ZipFiles = Get-ChildItem -Path $ZipRoot -Recurse -Filter '*.zip'
$Shell = New-Object -ComObject Shell.Application
$Results = foreach( $ZipFile in $ZipFiles ){
$FileCount = $Shell.NameSpace($ZipFile.FullName).Items() |
Measure-Object |
Select-Object -ExpandProperty Count
[pscustomobject]#{
FullName = $ZipFile.FullName
FileCount = $FileCount
}
}
$Results |
Export-Csv -Path 'C:\Users\mlkstq\Desktop\FFNS\ZipReport.csv' -NoTypeInformation
Output
Fullname Filecount
C:\Users\Main Folder\Subfolder1\Zip1 3
C:\Users\Main Folder\Subfolder2\Zip2 5
The problem is that I am having trouble getting the Subfolder name in putput file. Also want to substring subfolder name to get valid name. Whatever i try it fails.
If I've got you right I'd do it this way:
$ZipRoot = 'C:\Users\Main Folder'
$Shell = New-Object -ComObject Shell.Application
$subFolderList = Get-ChildItem -Path $ZipRoot -Recurse -Directory
$Result = foreach ($subFolder in $subFolderList) {
$zipFileList = Get-ChildItem -Path $subFolder.FullName -File -Filter *.zip
foreach ($ZipFile in $zipFileList) {
[PSCustomObject]#{
subFolder = $subFolder.FullName
zipFilesCount = $zipFileList.Count
zipFile = $ZipFile.Name
fileCount = $Shell.NameSpace($zipFile.FullName).Items().Count
}
}
}
Format-Table -InputObject $Result -AutoSize -InputObject $Result
Export-Csv -InputObject $Result -Path 'C:\Users\mlkstq\Desktop\FFNS\ZipReport.csv' -NoTypeInformation
In my opinion it just does not look that good to have the count of the zip files per subfolder repeated for each line of the subfolder
Okay i am not a programmer and my Powershell experience is basic. But here goes. I have been asked to collect some info on a Directory we are migrating off our network.
It collects sub dirs names, size, #of files and folders and datestamp and exports to csv.
I cannot for the life of me make the folder creation date work so i gave up on that and have been looking to get the lastwritetime for the folders as i am trying to figure out what has been used recently. It only works for a few folders but the rest in excel have system.object[] in the cell. Super frustrating.
Here is the code. It uses a gui directory picker.
#Refresh network drives for session
net use i: /delete
net use m: /delete
net use i: "\\wfs.queensu.ca\ADV\Workgroups"
net use m: "\\wfs.queensu.ca\ADVMedia"
Function Get-Folder($initialDirectory)
{
[System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms")|Out-Null
$foldername = New-Object System.Windows.Forms.FolderBrowserDialog
$foldername.Description = "Select a folder"
$foldername.rootfolder = "MyComputer"
if($foldername.ShowDialog() -eq "OK")
{
$folder += $foldername.SelectedPath
}
return $folder
}
$forDir = Get-Folder
#Change this to the parent directory that you want counts for
#$forDir = "\\wfs.queensu.ca\adv\workgroups\ADV Services\$seldir"
$Dirs = Get-ChildItem $forDir -Directory -Name
$Tab = [char]9
$results = #()
Write-Host $forDir
foreach($Dir in $Dirs)
{
$dirSize = "{0:N2} MB" -f ((Get-ChildItem $forDir/$Dir -Recurse | Measure-Object -Property Length
-Sum -ErrorAction Stop).Sum / 1MB)
$dirFiles = Get-ChildItem $forDir/$Dir -Recurse -File | Measure-Object | %{$_.Count}
$dirFolders = Get-ChildItem $forDir/$Dir -Recurse -Directory | Measure-Object | %{$_.Count}
#$dirDate = (Get-ChildItem $forDir/$Dir).LastWriteTime.ToString
$dirDate = #(Get-ChildItem $forDir/$Dir | % {$_.LastWriteTime})
$details = [ordered] #{
dir = $Dir
No_Files = $dirFiles
No_Folders = $dirFolders
size = $dirSize
date = $dirDate
}
$results += New-Object PSobject -Property $details
}
#This line finds the last index of the slash and adding one char
$Dirlength = $forDir.LastIndexOf('\') + 1
#This line takes the entire length of the string minus the postion above leaving the directory name
$sublength = $forDir.length - $Dirlength
#Assigns the remaining characters from the substring to the varibale to be used as the filename
$DirName = $forDir.SubString($Dirlength, $sublength)
$results | Export-Csv "C:\$DirName.csv" -NoTypeInformation
Write-Host ("Complete WOW!")
Get-ChildItem .\dir gives you all files contained in the directory .\dir not the directory itself.
That is why the following line in your script creates an array of LastWriteTimes for all files that are contained in the directory that $forDir/$Dir resolves to in your foreach loop:
$dirDate = #(Get-ChildItem $forDir/$Dir | % {$_.LastWriteTime})
The array in $dirDate will return ​System.Object[] when its toString() method is called. This is the reason, why you see this string in your excel, where you expect the folder's timestamp.
I bet that those folders, that seem to work do have exactly one childitem...
To get the LastWriteTime of the directory itself use Get-Item instead of Get-ChildItem.
$dirDate = Get-Item $forDir/$Dir | Select-Object -Expand LastWriteTime
try this...
Get-ChildItem -Path 'D:\temp' -Recurse |
Where-Object { $_.PSIsContainer } |
Select-Object -Property Name, LastWriteTime
<#
# Results
Name LastWriteTime
---- -------------
est 17-Feb-20 15:50:53
LogFiles 11-Mar-20 11:37:28
NewFolder 06-Feb-20 14:56:48
ParentFolder 12-Feb-20 14:24:25
Reference 03-Feb-20 11:55:47
Source 06-Feb-20 14:56:48
Target 24-Feb-20 22:03:56
New folder 03-Feb-20 11:55:24
temp 20-Jan-20 11:17:42
ChildFolder 12-Feb-20 14:08:11
GrandchildFolder 12-Feb-20 14:08:32
#>
# Or in v3 and beyond
Get-ChildItem -Path 'D:\temp' -Directory -Recurse |
Select-Object -Property Name, LastWriteTime
<#
# Results
Name LastWriteTime
---- -------------
est 17-Feb-20 15:50:53
LogFiles 11-Mar-20 11:37:28
NewFolder 06-Feb-20 14:56:48
ParentFolder 12-Feb-20 14:24:25
Reference 03-Feb-20 11:55:47
Source 06-Feb-20 14:56:48
Target 24-Feb-20 22:03:56
New folder 03-Feb-20 11:55:24
temp 20-Jan-20 11:17:42
ChildFolder 12-Feb-20 14:08:11
GrandchildFolder 12-Feb-20 14:08:32
#>
I know this question has already been answered, but for completeness, here's another way of doing this by utilizing the GetFileSystemInfos method every DirInfo object has.
$rootFolder = 'X:\YourRootPath'
Get-ChildItem -Path $rootFolder -Directory -Recurse | ForEach-Object {
# GetFileSystemInfos() (needs .NET 4+) is faster than Get-ChildItem and returns hidden objects by default
# See: https://devblogs.microsoft.com/powershell/why-is-get-childitem-so-slow/
$fsObjects = $_.GetFileSystemInfos('*', 'TopDirectoryOnly') # TopDirectoryOnly --> do not recurse
# you can also use Get-ChildItem here of course.
# To also get hidden files, with Get-ChildItem you need to add the -Force switch
# $fsObjects = Get-ChildItem -Path $_.FullName -Filter '*' -Force
# from the $fsObjects array, filter out the files and directories in order to get the count
$files = $fsObjects | Where-Object { $_ -is [System.IO.FileInfo] } # or: !($_.Attributes -band 'Directory')
$folders = $fsObjects | Where-Object { $_ -is [System.IO.DirectoryInfo] } # or: $_.Attributes -band 'Directory'
# emit a PSObject with all properties you wish to collect
[PsCustomObject]#{
Path = $_.FullName
FileCount = $files.Count
DirCount = $folders.Count
DirSize = "{0:N2} MB" -f (($files | Measure-Object -Sum -Property Length).Sum / 1MB)
DirDate = $_.LastWriteTime
}
} | Export-Csv -Path "X:\YourFolder_Info.csv" -NoTypeInformation -UseCulture
I need to get a list of all the folders owners on a shared network drive. However, I want to limit the recursion to just 3 folders deep (some of our users will create folders several levels deep, despite us telling them not to). I've found the below script, and slightly modified it to just give folder owner (it originally returned a lot more information for ACLs), but it still goes down through every folder level. How can I modify this to only return 3 folder levels?
$OutFile = "C:\temp\FolderOwner.csv" # indicates where to input your logfile#
$Header = "Folder Path;Owner"
Add-Content -Value $Header -Path $OutFile
$RootPath = "G:\" # which directory/folder you would like to extract the acl permissions#
$Folders = dir $RootPath -recurse | where {$_.psiscontainer -eq $true}
foreach ($Folder in $Folders){
$Owner = (get-acl $Folder.fullname).owner
Foreach ($ACL in $Owner){
$OutInfo = $Folder.Fullname + ";" + $owner
Add-Content -Value $OutInfo -Path $OutFile
}
}
You should be able to add a '*' to your path for each level. For example, this should return items three levels deep under C:\Temp:
dir c:\temp\*\*\*
Here's a sample function you can use (it's written for PowerShell v3 or higher, but it can be modified to work for version 2):
function Get-FolderOwner {
param(
[string] $Path = "."
)
Get-ChildItem $Path -Directory | ForEach-Object {
# Get-Acl throws terminating errors, so we need to wrap it in
# a ForEach-Object block; included -ErrorAction Stop out of habit
try {
$Owner = $_ | Get-Acl -ErrorAction Stop | select -exp Owner
}
catch {
$Owner = "Error: {0}" -f $_.Exception.Message
}
[PSCustomObject] #{
Path = $_.FullName
Owner = $Owner
}
}
}
Then you could use it like this:
Get-FolderOwner c:\temp\*\*\* | Export-Csv C:\temp\FolderOwner.csv
If you're after all items up to and including 3 levels deep, you can modify the function like this:
function Get-FolderOwner {
param(
[string] $Path = ".",
[int] $RecurseDepth = 1
)
$RecurseDepth--
Get-ChildItem $Path -Directory | ForEach-Object {
# Get-Acl throws terminating errors, so we need to wrap it in
# a ForEach-Object block; included -ErrorAction Stop out of habit
try {
$Owner = $_ | Get-Acl -ErrorAction Stop | select -exp Owner
}
catch {
$Owner = "Error: {0}" -f $_.Exception.Message
}
[PSCustomObject] #{
Path = $_.FullName
Owner = $Owner
}
if ($RecurseDepth -gt 0) {
Get-FolderOwner -Path $_.FullName -RecurseDepth $RecurseDepth
}
}
}
And use it like this:
Get-FolderOwner c:\temp -RecurseDepth 3 | Export-Csv C:\temp\FolderOwner.csv
Any help?
resolve-path $RootPath\*\* |
where { (Get-Item $_).PSIsContainer } -PipelineVariable Path |
Get-Acl |
Select #{l='Folder';e={$Path}},Owner