Export-CSV powershell loop - loops

What is the correct practice on outputting to a CSV file from a loop. My goal is to check the attributes of all files matching a series of extensions that have not been accessed in over a month and output to a csv file for reading later.
it only outputs the information for the first file. and errors the following:
Add-Member : Cannot add a member with the name "FileName" because a member with that name already exists. If you want to overwrite the member a
nyway, use the Force parameter to overwrite it.
At C:\Users\claw\Desktop\checkFileAge.ps1:10 char:25
+ $out2csv | add-member <<<< -MemberType NoteProperty -Name FileName -Value $i.fullname
+ CategoryInfo : InvalidOperation: (#{FileName=C:\u...012 9:33:46 AM}:PSObject) [Add-Member], InvalidOperationException
+ FullyQualifiedErrorId : MemberAlreadyExists,Microsoft.PowerShell.Commands.AddMemberCommand
Here is my example:
$out2csv = New-Object PSObject;
foreach ($i in get-childitem c:\users -recurse -include *.doc, *.xls, *.ppt, *.mdb, *.docx, *.xlsx, *.pptx, *.mdbx, *.jpeg, *.jpg, *.mov, *.avi, *.mp3, *.mp4, *.ogg)
{if ($i.lastaccesstime -lt ($(Get-Date).AddMonths(-1)))
{
$out2csv | add-member -MemberType NoteProperty -Name FileName -Value $i.fullname
$out2csv | add-member -MemberType NoteProperty -Name LastAccess -Value $i.LastAccessTime
}
} $out2csv | Export-Csv "C:\FileAccessInformation.csv" -NoTypeInformation -Force

Try something like this. It is a bit more canonical PowerShell.
$ext = '*.doc', '*.xls',...
Get-ChildItem C:\Users -r -inc $ext |
Where {$_.LastAccessTime -lt [DateTime]::Now.AddMonths(-1)} |
Select FullName, LastAccessTime |
Export-Csv -NoTypeInformation -Force C:\FileAccessInformation.csv

Related

Parsing a text file line by line and extracting string if it matches

We have a text log file similar to below with many users
<user>sandip</user>
something
<time>4:38 PM</time>
anything
<elapsed time> 60 mins </elapsed time>
We want to extract all users and we did the same simply by
Get-Content "C:\LOG\test.txt" | Select-String '(<user>.+</user>)' | ForEach-Object {
$_.Matches[0].Groups[1].Value
}
We want to parse test file line by line, check if it contains
user/time/elapsed time and [insert it in a dynamic variable if required] make a table of the same
Considering that your Log File follows the same format that i tested with:
(i.e something like this one:)
LogFile
This code should work just fine:
*
$Lines = get-content .\log.txt
$array = #()
foreach ($line in $lines)
{
if($line -like "<user>*")
{
$obj = New-Object psobject
Add-Member -InputObject $obj -MemberType NoteProperty -Name "UserName" -value $line.Replace("<user>","").Replace("</user>","")
}
if($line -like "<time>*")
{
Add-Member -InputObject $obj -MemberType NoteProperty -Name "Time" -value $line.Replace("<time>","").Replace("</time>","")
}
if($line -like "<elapsed time>*")
{
Add-Member -InputObject $obj -MemberType NoteProperty -Name "ElapsedTime" -value $line.Replace("<elapsed time>","").Replace("</elapsed time>","")
$array += $obj
}
}
$array | Export-Csv .\test.csv
*
A quite compact solution using two regular expressions,
with a nonconsuming positive
lookahead to
split the source file into sections starting with <user>
inside an if to get -match the current line with an alternation and a backreference to insert the found key and value into the current Row of the new table. See this RegEx live on regex101.com
## Q:\Test\2018\06\22\SO_50988379.ps1
$Table = ForEach ($Section in ((Get-Content .\Test.log -raw) -split '(?=<user>)' -ne '')) {
$Row = New-Object psobject
ForEach ($Line in ($Section -split "`r?`n")) {
if($Line -match "<(user|time|elapsed time)>([^<]+)</\1>"){
Add-Member -InputObject $Row -MemberType NoteProperty `
-Name "$($Matches[1])" -value $Matches[2].Trim()
}
}
$Row
}
$Table #| Export-Csv .\test.csv
Sample output:
> .\SO_50988379.ps1
user time elapsed time
---- ---- ------------
sandip 4:38 PM 60 mins
Joshi 8:15 PM 60 mins

Powershell add-member calculated value

I'm writing a script that takes unstructured input, reformats it and spits out a CSV.
In one of the members, I want to do an NSLOOKUP of another member to get the DNS entry associated with the IP. However, a number of the entries have an IP of 0.0.0.0 e.g. it's a listening port, there's no client yet connected.
These throw up errors from NSLOOKUP, quite rightly, because there's no DNS entry for it.
So, I amended my code to only do the NSLOOKUP if the IP was not 0.0.0.0, otherwise return an empty string.
But I can't make it work.
The following throws up no errors:
$srcdata -split "`r`n"|foreach {
$obj = New-Object System.Object
$obj|Add-Member -MemberType NoteProperty -Name Connection_ID -Value $_.Split(",")[0]
$obj|Add-Member -MemberType NoteProperty -Name Filename -Value $_.Split(",")[1]
$obj|Add-Member -MemberType NoteProperty -Name MCP_Port -Value $_.Split(",")[2]
$obj|Add-Member -MemberType NoteProperty -Name Client_Port -Value $_.Split(",")[3]
$obj|Add-Member -MemberType NoteProperty -Name Port_State -Value $_.Split(",")[4]
$obj|Add-Member -MemberType NoteProperty -Name Client_IP -Value $_.Split(",")[5]
$obj|Add-Member -MemberType NoteProperty -Name Client_DNS -value {If ($obj.Client_IP -ne "0.0.0.0") {(NSLOOKUP $obj.Client_IP|Select-String Name).Line.ToString().Replace(" ","").Split(":")[1]} else {""}}
$obj|Add-Member -MemberType NoteProperty -Name Protocol_Stack -Value $_.Split(",")[6]
$outfile += $obj
}
But then if I inspect one of the objects in the array I see:
Connection_ID : 1
Filename : CCF_MARC1
MCP_Port : 2001
Client_Port : 0
Port_State : LISTEN
Client_IP : 0.0.0.0
Client_DNS : If ($obj.Client_IP -ne "0.0.0.0") {(NSLOOKUP $obj.Client_IP|Select-String Name).Line.ToString().Replace(" ","").Split(":")[1]} else {""}
Protocol_Stack : LEGACY
If I wrap in parentheses instead of in a scriptblock, so the line setting up the Client_DNS is as follows:
$obj|Add-Member -MemberType NoteProperty -Name Client_DNS -value (If ($obj.Client_IP -ne "0.0.0.0") {(NSLOOKUP $obj.Client_IP|Select-String Name).Line.ToString().Replace(" ","").Split(":")[1]} else {""})
I get:
If : The term 'If' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
At K:\CodeSnippets\PowerShell\NW_CONN_TO_CSV.ps1:21 char:91
+ ... NS -value (If ($obj.Client_IP -ne "0.0.0.0") {(NSLOOKUP $obj.Client_IP|Selec ...
+ ~~
+ CategoryInfo : ObjectNotFound: (If:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
I'm sure I could get it to work as two separate lines of code, so do the If statement and put the result in a variable, then use that variable as the value in the add-member command, but I'm sure what I'm trying to do is eminently possible, I'm just missing something!
Any ideas?
You're interested in a ScriptProperty rather than a NoteProperty:
$obj |Add-Member -MemberType ScriptProperty -Name Client_DNS -Value {
nslookup $this.Client_IP |Select-String Name
}
You could use a regex to extract the name, something like this:
$outfile = $srcdata -split "[\r\n]" | % {
$v = $_.Split(",")
$re = [regex]::match((NSLOOKUP $v[5]), '(?<=Name:\s+)[^\s]+')
New-Object psobject -Property #{
Client_DNS = #('', $re.value)[$re.success]
Connection_ID = $v[0]
Filename = $v[1]
MCP_Port = $v[2]
Client_Port = $v[3]
Port_State = $v[4]
Client_IP = $v[5]
Protocol_Stack = $v[6]
}
}

How to save powershell script results to a file

I am using the script below to display all the subfolders' names and the count of files in those folders. I cannot figure out how to save the results to a file. I have tried everything I could and searched online for hours.
dir -recurse | ?{ $_.PSIsContainer } | %{ Write-Host $_.FullName (dir $_.FullName | Measure-Object).Count }
Do not use Write-Host. That cmdlet writes to the console only (i.e., you cannot redirect it to a file).
Use something like this instead:
Get-ChildItem -Recurse | Where-Object { $_.PSIsContainer } | ForEach-Object {
$_.FullName
}
Found another way of doing it that lets me save to a file:
ls -rec | ? {$_.mode -match 'd'} | select FullName, #{N='Count';E={(ls $_.FullName | measure).Count}} | Out-File C:\results.txt
I took your code and I upgraded it. try this:
$OUserOut = #()
Get-ChildItem -Recurse | Where-Object { $_.PSIsContainer } | ForEach-Object {
$ObjProperties = New-Object PSObject
Add-Member -InputObject $ObjProperties -MemberType NoteProperty -Name FullName -Value $_.FullName
Add-Member -InputObject $ObjProperties -MemberType NoteProperty -Name InternalObj -Value (dir $_.FullName | Measure-Object).Count
$OUserOut += $ObjProperties
}
$OUserOut | Out-GridView -Title "Objects"
$OUserOut | Export-Csv $env:USERPROFILE\Desktop\Out.csv
The two last lines have the information for the output file. The "Out-GridView" it's a table for show you the information, and the other line it's for export the result to a csv file.

How do I convert an array object to a string object in PowerShell?

While trying to create an CSV file with information about certificates I have an issue to store the userrights on the private key.
The problem is that I want to store multiple values in one attribute so I use an array.
At first I had no errors, however the column in my csv-file remained empty even in the case where the array has a value.
With a simple Write-Host I can see my array has the expected value so this part works okay.
For further investigations I have added the line:
Get-Member $certs.GetValue("UserRights")
This gives an error indicating I have to convert my variable to a string-variable.
So next I have tried to convert this array to a single string.
I have tried several ways but my error doesn't disappear so it doesn't work.
Underneath is my full code with some former attempts commented.
cls $certs = Get-ChildItem cert:\LocalMachine -Recurse | Where-Object {-not $_.PSIsContainer} | Select * Write-Host ("There were {0} certificates" -f ($certs | Measure-Object).Count)
foreach($certificate in $certs) {
if($certificate.HasPrivateKey)
{
Write-Host "Certificate's PSChildName is" $certificate.PSChildName
$rsaFile = $certificate.PrivateKey.CspKeyContainerInfo.UniqueKeyContainerName
$fullPath = "C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys\" + $rsaFile
$acl = Get-Acl -Path $fullPath
foreach($accessrule in $acl.Access)
{
Write-Host "User" $accessrule.IdentityReference "has the following rights:" $accessrule.FileSystemRights
}
Write-Host "------"
$UserRechten = #()
foreach($accessrule in $acl.Access)
{
$UserRechten += "{0}:{1};" -f ($accessrule.IdentityReference,$accessrule.FileSystemRights)
}
Write-Host "================================================================"
# -join $UserRechten
# $Userrechten | out-string
# $UserRechten = [system.String]::Join(" ", $UserRechten)
$separator = ";"
[string]::Join($separator,$UserRechten)
$certs | Add-Member -MemberType NoteProperty -Name "UserRights" -Value $UserRechten -Force
Write-Host "UserRechten has value : "$UserRechten
Get-Member $certs.GetValue("UserRights")
Write-Host "================================================================"
} }
$Certs | Add-Member -MemberType NoteProperty -Name "MachineName" -Value $env:COMPUTERNAME -Force
# $certs | Add-Member -MemberType NoteProperty -Name "Store" -Value 'My' -Force $RunDate = Get-Date -Format 'yyyy-MM-dd' $certs | Add-Member -MemberType NoteProperty -Name "RunDate" -Value $RunDate -Force $certs | Add-Member -MemberType NoteProperty -Name "Owner" -Value $env:USERNAME -Force
$Certs | Select * | Export-Csv c:\Certificaten\LocalCertsAll_$env:COMPUTERNAME.csv
$Certs | Select MachineName, Owner, PSParentPath, DnsNameList, PSChildName, NotBefore, NotAfter, Rundate, EnhancedKeyUsageList, HasPrivateKey, SerialNumber, Issuer, Subject, FriendlyName, UserRigthts |
Export-CSV c:\Certificaten\Localcerts_$env:COMPUTERNAME.csv
As noted in the comments, Get-Member is probably not what you're looking for
You (almost certainly) don't want to add the UserRights member property to the $Certs array, but rather to the individual objects in $Certs.
(I removed a bunch of superfluous Write-Host statements for readability):
$CertsAmended = foreach($Certificate in $certs)
{
if($certificate.HasPrivateKey)
{
$rsaFile = $certificate.PrivateKey.CspKeyContainerInfo.UniqueKeyContainerName
$fullPath = "C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys\" + $rsaFile
$acl = Get-Acl -Path $fullPath
# Create the UserRights value using -join
$UserRechten = #(foreach($accessrule in $acl.Access){
Write-Host "User" $accessrule.IdentityReference "has the following rights:" $accessrule.FileSystemRights
"{0}:{1}" -f ($accessrule.IdentityReference,$accessrule.FileSystemRights)
}) -join ";"
# Add the property to the individual object
$Certificate | Add-Member -MemberType NoteProperty -Name "UserRights" -Value $UserRechten
Write-Host "Userrights: " $UserRechten
# "Drop" the certificate object (now with a UserRights value) back onto the pipeline
$Certificate
}
}
Now you can export the $CertsAmended array to CSV all you want
If you find the $var = #(foreach($item in $collection){}) -join ';' displeasing to the eye, break it into two statements:
$UserRechten = foreach($accessrule in $acl.Access)
{
# Create UserRight string here, without ;
}
$UserRechten = $UserRechten -join ';'
For the $fullPath variable, you may want to use the Join-Path cmdlet:
$fullPath = Join-Path "C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys\" $rsaFile

Creating and populating a hash table

I am looking to create either a non-jagged array or a hash table (I am not sure the difference nor what I need to get the job done). Here is what I am trying to do.
I would like to query a list of servers for several values and then store those values for output to a CSV file. Here is the code.
$allServers = "svr1","svr2","svr3"
$a = #{}
$outData = New-Object PSObject
$allServers | ForEach-Object {
$cpu = (Get-WmiObject win32_processor -ComputerName $_).length
$mem = (Get-WmiObject win32_physicalmemory -ComputerName $_).Capacity /1GB
$outData | Add-Member -Type NoteProperty -Name "SERVERNAME" -Value $_
$outData | Add-Member -Type NoteProperty -Name "#CPU" -Value $cpu
$outData | Add-Member -Type NoteProperty -Name "#GBRAM" -Value $mem
}
Write-Host $outData
I am getting errors because it seems like it is trying to create the same entries repeatedly. Is it possible to make an empty hash table (or non-jagged array) with column names and then just populate values?
Create the object inside the loop, output it, and assign the value of the whole pipeline to your array variable:
$allServers = "svr1","svr2","svr3"
$a = $allServers | ForEach-Object {
$cpu = (Get-WmiObject win32_processor -ComputerName $_).length
$mem = (Get-WmiObject win32_physicalmemory -ComputerName $_).Capacity /1GB
$outData = New-Object PSObject
$outData | Add-Member -Type NoteProperty -Name "SERVERNAME" -Value $_
$outData | Add-Member -Type NoteProperty -Name "#CPU" -Value $cpu
$outData | Add-Member -Type NoteProperty -Name "#GBRAM" -Value $mem
$outData
}
$a | Export-Csv $path -NoTypeInformation

Resources