PowerShell Get ACL For Folders In A Text File - file-permissions

I have a text file with the exact paths of the folders that I want to get permission information from, and I am trying to use PowerShell to get the information from each folder. I want to add the information to a text file, with a delimiter value of ":"
Can someone please tell me what I am doing wrong...
$FullList = Get-Content "C:\Temp\ListofFoldersToCheck.txt"
$DataOutFile = "C:\Temp\PermissionInformation.txt"
Foreach ($Folder in $FullList)
{
$ACLs = get-acl $Folder.Fullname | ForEach-Object { $_.Access }
Foreach ($ACL in $ACLs)
{
$DataOutInfo = $Folder.FullName + ":" + $ACL.IdentityReference
Add-Content -Value $DataOutInfo -Path $DataOutFile
}
}
It is returning the error message: Get-Acl: Cannot validate argument on parameter 'Path'. The argument is null or empty. Provide an argument that is not null or empty, and then try
the command again.
The $FullList data is separated by new lines, if that makes any difference.
Please help, this is driving me insane...

Hey The Woo you miss something here :
$Folder is a string, not a FileInfo (you should use Get-Item for that), so you should begin by : get-acl $Folder and not get-acl $Folder.Fullname.
Then you should just write :
$ACLs = (get-acl $Folder).Access

Related

Powershell comparing data in a CSV against files in a folder

I'm fairly new to powershell.
I'm trying to compare data in a CSV File against random files in a specific folder.
I want to see if and what has changed and then log that in another column called "Changed".
Here's what I've done below, it seems to create a new column called 'Changed' but doesn't input the changes in it.
$Spreadsheet = 'C:\Powershell\CSV\inv.csv'
$SpreadSheetPath = "C:\Powershell\CSV"
Import-Csv $Spreadsheet -Delimiter "|" -Encoding Default | ForEach-Object -
{
$Path += $_.Path
$Filename += $_.Filename
$DateModified += $_.DateModified
$FileSize += $_.FileSize
$MD5Hash += $_.MD5Hash
}
{
$Msg1 = "Path changed"
$Msg2 = "File Name changed"
$Msg3 = "Date Modified changed"
$Msg4 = "File Size changed"
$Msg5 = "MD5 changed"
$Msg6 = "Files are the same"
$psdata = "D:\ps-test\data\*.*"
}
If (($Path -eq $psdata))
{
Import-Csv C:\Powershell\CSV\inv.csv |
Select-Object *,#{Name='Changed';Expression={$Msg6}} |
Export-Csv C:\Powershell\CSV\NewSpreadsheet4.csv
}
Else
{
Import-Csv C:\Powershell\CSV\inv.csv |
Select-Object *,#{Name='Changed';Expression={$Msg1}} |
Export-Csv C:\Powershell\CSV\NewSpreadsheet4.csv
}
Here is an example of what the CSV looks like:
Path Filename Date Modified File Size MD5 Hash
D:\ps-test\data adminmodeinfo.htm 03/11/2010 22:42 1079 BD1C9468D71FD33BB35716630C4EC6AC
E:\ps-test\data admintoolinfo.htm 03/11/2010 22:42 868 24B99B6316F0C49C23F27FEA6FF1C6AC
E:\ps-test\data admin_ban.bmp 03/11/2010 22:42 63480 C856F1F3C58962B456E749F2EA9C933A
E:\ps-test\data baseline.dat 03/20/2010 03:18:33 173818 F13183D88AABD1A725437802F8551A06
E:\ps-test\data blueRule.gif 03/11/2010 22:42 815 D1AEFE884935095DAB42DAFD072AA46F
E:\ps-test\data deffactory.dat 03/20/2010 03:18:33 706 862D4DFD2F49021BB7C145BDAFE62F6F
E:\ps-test\data dividerArt.jpg 03/11/2010 22:42 367 F7050C596C097C0B01A443058CD15E35
There are many issues with your code.I will try to highlight a few of the issues, link to documentation and point you in the right direction so that you can resolve your issues. A proper solution would require getting many more requirements, or writing code (off-topic for StackOverflow)
Change
| ForEach-Object -
{
to
| ForEach-Object {
In the Foreach-Object, you are concatenating values from each line because you are using +=.
On the first run, $Path contains D:\ps-test\data.
After the second run, it contains D:\ps-test\dataE:\ps-test\data.
At the end of your test data, it contains D:\ps-test\dataE:\ps-test\dataE:\ps-test\dataE:\ps-test\dataE:\ps-test\dataE:\ps-test\dataE:\ps-test\data
The messages are contained in a script block, but it does not look like this is intentional as this is never executed. So after the scriptblock, the variable $Msg1 has not been created; it's blank.
If (($Path -eq $psdata))
double brackets not required.
will always be false because the variable $psdata does not exist as it was stated inside a script block.
will always be false because you are attempting to equate the strings; your input does not literally contain "D:\ps-test\data\*.*". You probably want -like instead of -eq.
will always be inaccurate because even if the paths are compared, there is no check that the file actually exists on the system.
Useful links
Test-Path to check if file exists.
Get-FileHash to get MD5 hash and compare to file.
Get-ChildItem to get a list of directories/files in a directory.
Write-Output so that you can print variables and make sure they contain what you expect.
about_comparison_operators - -in and -contains will help you.
This is a suggestion to help you get started. It's not complete and not tested! Let me know if it works as expected and if you have any questions.
Import-Csv 'C:\Powershell\CSV\inv.csv' -Delimiter "|" -Encoding Default | foreach {
$Path += $_.Path
$Filename += $_.Filename
$DateModified += $_.DateModified
$FileSize += $_.FileSize
$MD5Hash += $_.MD5Hash
$file = [System.IO.FileInfo](Join-Path $Path $Filename)
if (-not $file.Exists) {
$message = "File does not exist"
}
elseif ($file.LastWriteTime -ne [DateTime]$DateModified) {
$message = "Dates differ"
}
elseif ($file.Length -ne [int]$FileSize) {
$message = "Sizes differ"
}
# and so on...
# (You cannot really compared a changed file name btw)
New-Object -Type PSObject -Prop #{
Path = $Path
Filename = $Filename
DateModified = $DateModified
FileSize = $FileSize
MD5Hash = $MD5Hash
Message = $message
}
} | Export-CSV 'C:\Powershell\CSV\NewSpreadsheet4.csv'

Powershell - Pulling string from txt, splitting it, then concatenating it for archive

I have an application where I am getting a list of new\modified files from git status, then I take the incomplete strings from that file, concatenate them with the root dir file path, then move those files to an archive. I have it half working, but the nature of how I am using powershell does not provide error reports and the process is obviously erroring out. Here is the code I am trying to use. (It has gone through several iterations, please excuse the commented out portions) Basically I am trying to Get-Content from the txt file, then replace ? with \ (for some reason the process that creates the txt love forward slashes...), then split that string at the spaces. The only part of the string I am interested in is the last part, which I am trying to concatenate with the known working root directory, then I am attempting to move those to an archive location. Before you ask, this is something we are not willing to track in git, due to the nature of the files (they are test outputs that are time stamped, we want to save them on a per test run basis, not in git) I am still fairly new to powershell and have been banging my head against this rock for far too long.
Get-Content $outfile | Foreach-Object
{
#$_.Replace("/","\")
#$lineSplit = $_.Split(' ')
$_.Split(" ")
$filePath = "$repo_dir\$_[-1]"
$filePath.Replace('/','\')
"File Path Created: $filePath"
$untrackedLegacyTestFiles += $filePath
}
Get-Content $untrackedLegacyTestFiles | Foreach-Object
{
Copy-Item $_ $target_root -force
"Copying File: $_ to $target_root"
}
}
the $outfile is a text file where each line has a partial file path leading to a txt file generated by a test application we use. This info is provided by git, so it looks like this in the $outfile txt file:
!! Some/File/Path/Doc.txt
The "!!" mean git sees it as a new file, however it could be several characters from a " M" to "??". Which is why I am trying to split it on the spaces and take only the last element.
My desired output would be to take the the last element of the split string from the $outfile (Some/File/Path/Doc.txt) and concatenate it with the $repo_dir to form a complete file path, then move the Doc.txt to an archive location ($target_root).
To combine a path in PowerShell, you should use the Join-Path cmdlet. To extract the path from your string, you can use a regex:
$extractedPath = [regex]::Match('!! Some/File/Path/Doc.txt', '.*\s(.+)$').Groups[1].Value
$filePath = Join-Path $repo_dir $extractedPath
The Join-Path cmldet will also convert all forward slashes to backslashes so no need to replace them :-).
Your whole script could look like this:
Get-Content $outfile | Foreach-Object {
$path = Join-Path $repo_dir ([regex]::Match($_, '.*\s(.+)$').Groups[1].Value)
Copy-Item $path $target_root -force
}
If you don't like to use regexin your code, you can also extract the path using:
$extractedPath = '!! Some/File/Path/Doc.txt' -split ' ' | select -Last 1
or
$extractedPath = ('!! Some/File/Path/Doc.txt' -split ' ')[-1]

Capture Get-ChildItem results into string array for HTML output

I'm writing a PowerShell script and I want to capture the list of files in a directory and store into a variable. Once captured I'd like to print it to an HTML file. I've captured the data like this:
$listDownloads = Get-ChildItem -Force "C:\Users\Someuser\Downloads"
Now, I want to convert $listDownloads into a string and split it. How do I go about doing that?
I've tried this:
$listSplit = $listDownloads.ToString().Split(" ")
but I get this output:
System.Object[]
Updated:
$html = '<html>'
$body = '<body>'
$p = '<p>'
$pClose = '</p>'
$StatusDownloads = 'Directory of the Downloads Folder'
$pTags = $p.ToString() + $StatusDownloads + $pClose.ToString();
$listDownloads = (Get-ChildItem -Force "C:\Users\Someuser\Downloads").Name;
function createHTML($addtoFile){
$addtoFile | Add-Content 'status.html'
}
function printData($printData){
Write-Output $printData
}
$html | Set-Content 'status.html'
createHTML($html)
createHTML($body)
createHTML($pTags)
createHTML($listDownloads)
When I try to print $listDownloads it gives me the data all on the same line (i.e. File 1 File 2) I want each file or folder displayed on a new line. How would I do that? If I were to type Get-ChildItem -Force "C:\Users\Someuser\Downloads" into Powershell it would give the list of files line by line. I want that type of output on my HTML page.
The only reason I can think you want to split is to get an array of filenames.
If that's the case, you can do:
$list = (Get-ChildItem -Force "C:\Users\Someuser\Downloads").FullName
From your update you would need to pass into your function as:
createHTML($listDownloads -join "`n")

Outputting PowerShell data to a string

This is really PowerShell 101, I realise, but I'm stuck.
I'm trying to iterate through a folder tree, getting each subfolder name and a count of files. No problems there.
The new requirement is to get the ACLs on each subfolder as well. All of this data needs to be output as a CSV file, with a line consisting of each folder name, the file count, and the ACLs in a single string in one field of the CSV (I was going to delimit them with semicolons).
I am open to exporting to XML if the data can be viewed in Excel.
The part where I'm stuck is getting the ACL information into a single string for the CSV.
Get-ACL on each directory shows the data as follows (I'm doing a Select to just get the IdentityReference and FileSystemRights, which is all we're interested in):
IdentityReference FileSystemRights
----------------- ----------------
BUILTIN\Users ReadAndExecute, Synchronize
BUILTIN\Users AppendData
BUILTIN\Users CreateFiles
I would like the output file formatted with one line per subdirectory, similar to
#filecount,folder,perms
51,C:\temp,BUILTIN\Users:ReadAndExecute,Synchronize;BUILTIN\Users:AppendData...
I however can't get any kind of join working to have it presented in this way. I don't care about what combination of delimiters are used (again, must be readable in Excel).
The script, such as it is, is as follows. The output file has its line of data appended with each directory it traverses. I'm sure this isn't very efficient, but I don't want the process consuming all the server memory either. The bits I can't figure out are prepended with ###.
(Get-ChildItem C:\temp -recurse | Where-Object {$_.PSIsContainer -eq $True}) | foreach {
$a = ($_.GetFiles().Count)
$f = $_.FullName
$p = (get-acl $_.FullName).Access | select-object identityreference,filesystemrights
### do something with $p?
Out-File -FilePath c:\outfile.csv -Append -InputObject $a`,$f`,###$p?
}
Since you want all ACEs of a folder mangled into a single line you need something like this:
Get-ChildItem 'C:\temp' -Recurse | ? { $_.PSIsContainer } | % {
# build a list of "trustee:permissions" pairs
$perms = (Get-Acl $_.FullName).Access | % {
"{0}:{1}" -f $_.IdentityReference, $_.FileSystemRights
}
New-Object -Type PSObject -Property #{
'Filecount' = $_.GetFiles().Count
'Folder' = $_.FullName
'Permissions' = $perms -join ';' # join the list to a single string
}
} | Export-Csv 'c:\outfile.csv' -NoType
Repeated appending inside a loop usually guarantees poor performance, so it should be avoided whenever possible. The outer loop creates a list of custom objects, which can then be exported via Export-Csv in a single go.

Powershell: Splitting String into CSV

I have a (hopefully) quick question that I can't seem to work out on my own.
I have a string:
ITEM1 Quantity: 12x 355ml bottlePrice: $23.95 $23.50 SAVE $0.45
That I would like to split out and insert into a CSV file. The columns in the generated CSV, and their values, would be:
Name: (everything before "Quantity:" in the above string.)
Quantity: (between Quantity and x)
Size: (Everything between x and bottle)
OrigPrice: (Everything after Price and the second $ sign)
SalePrice: (Everything between OrigPrice and SAVE)
Savings: (Everything after "SAVE")
I hope this all makes sense, I can provide more info if needed.
I appreciate the help!
How about something like:
$subject = 'ITEM1 Quantity: 12x 355ml bottlePrice: $23.95 $23.50 SAVE $0.45'
if ($subject -cmatch '^(?<Name>.*)Quantity:(?<Quantity>.*)x(?<Size>.*)bottle\s*Price:(?<OrigPrice>.*)\s*(?<SalePrice>\$.*)SAVE(?<Savings>.*)$') {
$result = $matches[0]
} else {
$result = ''
}
"Matches:"
$matches
I couldn't tell if there really needed to be a space between bottle & Price (it didn't look like it, but it'll handle it if there is).
If you need the name, you can access it like:
$matches["Name"]
A better solution (and one that actually gets it to CSV format, would be something like the following (thanks to #nickptrvc for pointing out what I missed):
function Read-Data {
[cmdletbinding()]
param(
[parameter(Mandatory)][string]$Path
)
$reader = [System.IO.File]::OpenText($Path)
while(( $subject = $reader.ReadLine()) -ne $null ) {
if ($subject -cmatch '^(?<Name>.*)Quantity:(?<Quantity>.*)x(?<Size>.*)bottle\s*Price:(?<OrigPrice>.*)\s*(?<SalePrice>\$.*)SAVE(?<Savings>.*)$') {
$result = $matches[0]
$obj = [PSCustomObject]#{
Name=$matches["Name"].trim();
Quantity=$matches["Quantity"].trim();
Size=$matches["Size"].trim();
OrigPrice=$matches["OrigPrice"].Trim();
SalePrice=$matches["SalePrice"].Trim();
Savings=$matches["Savings"].Trim()
}
$obj
}
}
}
Then, to use it, save this to a file (I called mine Read-Data.ps1), source the file, and then you have two options: 1) you can use ConvertTo-Csv to simply convert the objects to CSV, and return the result to the screen, or you can use Export-Csv to save it to a file:
. C:\Test\Convert-Data.ps1
Read-Data -Path C:\Test\datafile.dat | ConvertTo-Csv -NoTypeInformation
or
Read-Data -Path C:\Test\datafile.dat | Export-Csv -NoTypeInformation -Path C:\Test\datafile.csv

Resources