Export to CSV using PowerShell 2.0? - powershell

I have the following code that prints the file system rights of each account enabled on the folder with path "C:\Temp\CSM\*" & "C:\Temp\CSM\*\*" . How do I write the output in a comma-separated CSV? As this is for PowerShell 2.0 Export-Csv -Append parameter cannot be used.
$FolderPath = dir -Directory -Path "C:\Temp\CSM\*", "C:\Temp\CSM\*\*" -Force
foreach ($Folder in $FolderPath) {
$Acl = Get-Acl -Path $Folder.FullName
foreach ($Access in $acl.Access) {
Write-Host $Folder.FullName "," $Access.IdentityReference "," $Access.FileSystemRights
}
}

Prior to PowerShell v3 if you wanted to append to an existing CSV you need something like this:
... | ConvertTo-Csv -NoType | Select-Object -Skip 1 | Add-Content
However, in your scenario that probably isn't necessary. If you replace your foreach loops with a pipeline you can write the CSV in one go without having to append to it in a loop (which isn't recommended anyway).
$folders = "C:\Temp\CSM\*", "C:\Temp\CSM\*\*"
Get-ChildItem -Path $folders -Directory -Force | ForEach-Object {
$path = $_.FullName
Get-Acl -Path $path |
Select-Object -Expand Access |
Select-Object #{n='Path';e={$path}}, IdentityReference, FileSystemRights
} | Export-Csv 'C:\output.csv' -NoType

Related

Run Powershell script on multiple files

I have the following code to add new columns to a csv file. I would like to amend the code to run on multiple csv files within a folder and output it to a different folder.
$source = "C:\input_folder\input.csv"
$destination = "C:\output_folder\output.csv"
(Import-CSV $source |
Select-Object *,#{Name='column1';Expression={'data1'}} |
Select-Object *,#{Name='column2';Expression={'data2'}} |
ConvertTo-csv -NoTypeInformation |
Select-Object -Skip 0) -replace '"' | Set-Content $destination
You could do something like this. You might also want to add a check that the files in the folder are .csv type and not something else.
$source = "C:\input_folder"
$destinationFolder = "C:\output_folder"
$folderContents = Get-ChildItem $source
foreach ($item in $folderContents) {
if (Test-Path -Path $item -PathType Leaf == True) {
(Import-CSV $item |
Select-Object *,#{Name='column1';Expression={'data1'}} |
Select-Object *,#{Name='column2';Expression={'data2'}} |
ConvertTo-csv -NoTypeInformation |
Select-Object -Skip 0) -replace '"' | Set-Content ("$destinationFolder\$item" + "_formatted.csv")
}
}

recursively count files in folder and output to file

I want to count files for every folder on an E-drive, and output the folder path and file count to a text file using PowerShell (version 2).
I have found this script, but it outputs to console. How do I change it to output to a text file?
Set-Location -Path E:\
Get-ChildItem -recurse | Where-Object{ $_.PSIsContainer } | ForEach-Object{ Write-Host $_.FullName (Get-ChildItem $_.FullName | Measure-Object).Count }
I think it would be best to get an array of resulting objects where you can store both the directory path and the number of files it contains. That way, you can afterwards show it in the console and also save it to a structured CSV file you can open in Excel.
This is for PowerShell 2:
# to keep the property order in PS version < 3.0, create an
# Ordered Dictionary to store the properties first
$dict = New-Object System.Collections.Specialized.OrderedDictionary
# now loop over the folders
$result = Get-ChildItem -Path 'E:\' -Recurse -Force -ErrorAction SilentlyContinue |
Where-Object { $_.PSIsContainer } |
ForEach-Object {
# add the results in the temporary ordered dictionary
$dict.Add('Directory', $_.FullName)
$dict.Add('Files', #(Get-ChildItem -Path $_.FullName -Force -ErrorAction SilentlyContinue |
Where-Object { !$_.PSIsContainer }).Count)
# and output a PSObject to be collected in array '$result'
New-Object PSObject -Property $dict
$dict.Clear()
}
# output on screen
$result | Format-Table -AutoSize
#output to CSV file
$result | Export-Csv -Path 'D:\Test\FileCount.csv' -NoTypeInformation
The -Force switch makes sure you also count items that otherwise can't be accessed by the user, such as hidden or system files.
Get-ChildItem c:\tmp -recurse |
Where-Object{ $_.PSIsContainer } |
ForEach-Object {
"$($_.Fullname) $((Get-ChildItem $_.FullName | Where-Object{!$_.PSIsContainer}).count)"
} |
Out-File c:\tmp\out.txt
You can use the > operator for this:
Set-Location -Path E:\
(Get-ChildItem -recurse | Where-Object{ $_.PSIsContainer } | ForEach-Object{ Write-Host $_.FullName (Get-ChildItem $_.FullName | Measure-Object).Count }) >"OUTPUTFILEPATH.txt"

Add NTFS access to files with subfolders from CSV file

I try to add NTFS access to the shortcuts.
I have the csv file that contains:
Name,AD
Steps Recorder,Group_312312
Snipping Tool,Group_545345
$FolderPath = "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Accessories\"
$file = "C:\Users\adm\Desktop\Group.csv"
$groups = Get-Content $file | ConvertFrom-Csv
foreach ($group in $groups){
Add-NTFSAccess -Path (Join-Path -Path $FolderPath -ChildPath ($group.Name+".lnk")) `
-Account $group.AD `
-AccessRights ReadAndExecute `
}
I have a lot of subfolders with *.lnk files in $FolderPath. But in this way, the scripts find only in $FolderPath without subfolders. How can I change the script to find all *.lnk files include subfolders?
For example:
C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Accessories\My_programs\OneDrive.lnk
For this, I think you need a different approach, where you get a collection of *.lnk files recursively and filter to get only those which have a BaseName property that can be found in the CSV.
Next, use Group-Object to group (make sub-collections) of these FileInfo objects, based on their BaseName.
According to the docs, the Path parameter on the Add-NTFSAccess cmdlet can take an array of paths (FullName properties) and these can be piped through to it, so we can send each subcollection all at once:
$FolderPath = "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Accessories"
$file = "C:\Users\adm\Desktop\Group.csv"
$groups = Import-Csv -Path $file
# get a list of *.lnk FileIfo objects where the file's BaseName can be found in the
# CSV column 'Name'. Group these files on their BaseName properties
$linkfiles = Get-ChildItem -Path $FolderPath -Filter '*.lnk' -File -Recurse -Force |
Where-Object { $groups.Name -contains $_.BaseName } |
Group-Object BaseName
# iterate through the grouped *.lnk files
$linkfiles | ForEach-Object {
$baseName = $_.Name # the name of the Group is the BaseName of the files in it
$adGroup = ($groups | Where-Object { $_.Name -eq $baseName }).AD
# pipe all items in the group through to the Add-NTFSAccess cmdlet
# see parameter Path at https://ntfssecurity.readthedocs.io/en/latest/Cmdlets/Add-NTFSAccess/
$_.Group | Add-NTFSAccess -Account $adGroup -AccessRights ReadAndExecute
}
UPDATE
# this is where the output 'log' csv file goes
$outputFile = "C:\Users\adm\Desktop\GroupReport.csv"
# get a list of *.lnk FileIfo objects where the file's BaseName can be found in the
# CSV column 'Name'. Group these files on their BaseName properties
$linkfiles = Get-ChildItem -Path $FolderPath -Filter '*.lnk' -File -Recurse -Force |
Where-Object { $groups.Name -contains $_.BaseName } |
Group-Object BaseName
# iterate through the grouped *.lnk files and add the group permission
# capture the results in a variable $log to output as CSV
$linkfiles | ForEach-Object {
$baseName = $_.Name # the name of the Group is the BaseName of the files in it
$adGroup = ($groups | Where-Object { $_.Name -eq $baseName }).AD
# create a new access rule
# see: https://learn.microsoft.com/en-us/dotnet/api/system.security.accesscontrol.filesystemaccessrule
$rule = [System.Security.AccessControl.FileSystemAccessRule]::new($adGroup, "ReadAndExecute", "Allow")
$_.Group | ForEach-Object {
# get the current ACL of the file
$acl = Get-Acl -Path $_.FullName
# add the new rule to the ACL
$acl.SetAccessRule($rule)
$acl | Set-Acl $_.FullName
# output for logging csv
[PsCustomObject]#{
'Group' = $adGroup
'File' = $_.FullName
}
}
} | Export-Csv -Path $outputFile -NoTypeInformation

List file count by subfolder

I am trying to use powershell to produce a list of folder names and how many files are in each folder.
I have this script
$dir = "C:\Users\folder"
Get-ChildItem $dir -Recurse -Directory | ForEach-Object{
[pscustomobject]#{
Folder = $_.FullName
Count = #(Get-ChildItem -Path $_.Fullname -File).Count
}
} | Select-Object Folder,Count
Which lists the file count, but it puts the full path (i.e. C:\Users\name\Desktop\1\2\-movi...). Is there any way to just display the last folder ("movies") as well as save the result to a .txt file?
Thank you
Instead of $_.FullName, use $_.Name to only get the directory name.
Your Select-Object call is redundant - it is effectively a no-op.
While it's easy to send the results to a .txt file with >, for instance, it's better to use a more structured format for later programmatic processing.
In the simplest form, that means outputting to a CSV file via Export-Csv; generally, however, the most faithful way of serializing objects to a file is to use Export-CliXml.
Using Export-Csv for serialization:
$dir = 'C:\Users\folder'
Get-ChildItem -LiteralPath $dir -Recurse -Directory | ForEach-Object {
[pscustomobject] #{
Folder = $_.Name
Count = #(Get-ChildItem -LiteralPath $_.Fullname -File).Count
}
} | Export-Csv -NoTypeInformation results.csv
Note that you could streamline your command by replacing the ForEach-Object call with a Select-Object call that uses a calculated property:
$dir = 'C:\Users\folder'
Get-ChildItem -LiteralPath $dir -Recurse -Directory |
Select-Object Name,
#{ n='Count'; e={#(Get-ChildItem -LiteralPath $_.Fullname -File).Count} } |
Export-Csv -NoTypeInformation results.csv
You mean something like this...
Clear-Host
Get-ChildItem -Path 'd:\temp' -Recurse -Directory |
Select-Object Name,FullName,
#{Name='FileCount';Expression = {(Get-ChildItem -Path $_.FullName -File -Recurse| Measure-Object).Count}} `
| Format-Table -AutoSize
# Results
Name FullName FileCount
---- -------- ---------
abcpath0 D:\temp\abcpath0 5
abcpath1 D:\temp\abcpath1 5
abcpath2 D:\temp\abcpath2 5
Duplicates D:\temp\Duplicates 12677
EmptyFolder D:\temp\EmptyFolder 0
NewFiles D:\temp\NewFiles 4
PngFiles D:\temp\PngFiles 4
results D:\temp\results 905
...

PowerShell - searching for existing files generates empty output

I wish to search for specific files listed in searchFiles and pipe their locations to TestFileLocation.CSV. However, my current script only generates an empty CSV. What am I missing?
My TestFindFile.csv is of the form:
Name
123.pdf
321.pdf
aaa.pdf
SNIPPET
$searchFiles = Import-CSV 'C:\Data\SCRIPTS\PS1\TestFindFile.csv' -Header ("Name")
$source = 'C:\Data'
ForEach($File in $searchFiles)
{
Get-ChildItem $source -Filter $File -rec | where {!$_.PSIsContainer} | select-object FullName | export-csv -notypeinformation -delimiter '|' -path c:\data\scripts\ps1\TestFileLocation.csv
}
You were overwriting the CSV for each iteration of the loop.
$searchFiles = Import-CSV 'C:\Data\SCRIPTS\PS1\TestFindFile.csv' -Header ("Name")
$source = 'C:\Data'
$outputPath = 'c:\data\scripts\ps1\TestFileLocation.csv'
$searchFiles | ForEach-Object {
# Silently continue to try to ignore error like
# not being able to read path's which are too long
Get-ChildItem $source -Filter $_ -rec -ErrorAction SilentlyContinue | where {!$_.PSIsContainer} | select-object FullName
} | export-csv -notypeinformation -delimiter '|' -path $outputPath
Example using AlphaFS
A comment asked for an example using AlphaFS because it claims to overcome the long path issue. I'm not going into all the details, but here is how I got it to work.
# download and unzip to c:\alpahfs
# dir C:\AlphaFS\* -Recurse -File | Unblock-File
[System.Reflection.Assembly]::LoadFrom('C:\AlphaFS\lib\net451\AlphaFS.dll')
$searchFiles = Import-CSV 'C:\Data\SCRIPTS\PS1\TestFindFile.csv' -Header ("Name")
$source = 'C:\Data'
$outputPath = 'c:\data\scripts\ps1\TestFileLocation.csv'
$searchFiles | ForEach-Object {
$files = [Alphaleonis.Win32.Filesystem.Directory]::EnumerateFiles($source,'*',[System.IO.SearchOption]::AllDirectories)
$files | ForEach-Object { [PSCustomObject] #{FileName = $_} }
} | export-csv -notypeinformation -delimiter '|' -path $outputPath
# type $outputPath
If your .csv file contains the header "Name", there is no need to again declare it when running Import-Csv.
The reason the output is empty is that you are searching for an Object which contains the property Name (imported from the TestFindFile.csv). Search for $File.Name. Also pull commands outside the loop that don't need to be there:
$searchFiles | Select -ExpandProperty Name | % {
Get-ChildItem $source -Filter $_ -Recurse | where {!$_.PSIsContainer}
} | select-object FullName | export-csv -notypeinformation -delimiter '|' -path c:\data\scripts\ps1\TestFileLocation.csv