Read Column data from CSV - powershell

I have a CSV file
Name,Age,Data
Test,22,Yes
Test2,23,No
Test3,43,Yes
How can I process this file using PowerShell, so that I can replicate this functionality:
foreach(var HeaderName in CSV.HeaderName)
{
//Sample value Name
foreach(var data in HeaderColumn.Data)
{
//Do Something with data
//Sample values as we loop through will be
//Test,Test2,Test3
}
}
Where CSV.HeaderName should be having the values Name, Age, Data
and HeaderColumn.Data will have the column data for Name, Age and Data as we process the headers.

The PowerShell equivalent of your pseudo code would be something like this:
$csv = Import-Csv 'C:\path\to\your.csv'
$headers = $csv | Get-Member -MemberType NoteProperty | select -Expand Name
foreach ($header in $headers) {
foreach ($data in $csv.$header) {
# do stuff with $data
}
}
For better answers take a step back and describe the actual problem you're trying to solve instead of what you perceive as the solution.
Demonstration:
PS C:\> $csvfile = 'C:\temp\test.csv'
PS C:\> Get-Content $csvfile
Name,Age,Data
Test,22,Yes
Test2,23,No
Test3,43,Yes
PS C:\> $csv = Import-Csv $csvfile
PS C:\> $csv | Format-Table -AutoSize
Name Age Data
---- --- ----
Test 22 Yes
Test2 23 No
Test3 43 Yes
PS C:\> $headers = $csv | Get-Member -MemberType NoteProperty | select -Expand Name
PS C:\> $headers
Age
Data
Name
PS C:\> foreach ($header in $headers) {
>> foreach ($data in $csv.$header) {
>> Write-Host $data
>> }
>> }
>>
22
23
43
Yes
No
Yes
Test
Test2
Test3

Related

Powershell Convert NetTCPConnection or netstat To String

Hello and thanks to anyone in advance who can help.
I am trying to write a powershell script that gets a list of all connections to a server containing a certain keyphrase and outputs the DNS names and corresponding IPs to a csv file.
I have run into some typing issues. This is my code for an attempt with NetTCPConnection:
# get remote addrs
$a = (Get-NetTCPConnection | Select-Object RemoteAddress)
# get IPs of valid addrs
Workflow Get-DNSNames([string[]]$IPAddresses){
foreach -parallel ($IP in $IPAddresses | Select-Object -Unique){
# $IP.ToString()
[system.net.dns]::GetHostEntry($IP)).hostname
try{
#{$IP = $(([system.net.dns]::GetHostEntry($IP)).hostname)}
}catch{
#{$IP = "N/A"}
}
}
}
# output to txt
$List = Get-DNSNames -IPAddresses $a
$List | Out-File "C:\temp\getBAS\IPAddresses_Complete.txt"
And this is my attempt with netstat:
# get netstat output
$a = netstat
$b = $a[3..$a.count] | ConvertFrom-string | Select p4 | Where p4 -like "*52*"
# get IP and create name, IP pairs
$c = #()
foreach ($DNS in $b) {
# name , IP
$rowToAdd = New-Object psobject
$name = $DNS
$IP = [System.Net.Dns]::GetHostAddresses($DNS.ToString())
$rowToAdd | Add-Member -Member NoteProperty -Name "Name" -value $name
$rowToAdd | Add-Member -Member NoteProperty -Name "IP" -value $IP
$c += $rowToAdd
}
$c
# output to csv
$final | Export-Csv -Path "C:\temp\getBAS\NetworkConnectionsBAS.csv" -NoTypeInformation
The problem with the first attempt is that the values from NetTCPConnection are objects, not strings, and strings are needed in the GetHost function. I could not figure out a way to convert each individual addr object to a string.
The problem with my second attempt is that I get a wacky csv output.
I am frankly stuck and would appreciate any help. Thanks
$a = (Get-NetTCPConnection | Select-Object RemoteAddress)
This is making $a an object with the RemoteAddress property in it, you aren't converting this to a string anywhere that I can see.
You can either change this line to
$a = (Get-NetTCPConnection | Select-Object -ExpandProperty RemoteAddress)
to get a String containing the value of RemoteAddress
or change
$List = Get-DNSNames -IPAddresses $a
to
$List = Get-DNSNames -IPAddresses "$($a.RemoteAddress)"
this will pull just the value from the property which should then be treated as a string.

Unable to export custom objects to CSV in powershell

I have written powershell scirpt to generate IIS information to CSV.
$bindingip = Get-WmiObject -Namespace root\webadministration -Class sslbinding2 | Select-Object -
Property PSComputerName,IPAddress
$sitedetails = Get-Website | Select-Object -Property Name,ID,State
$report = New-Object psobject
$report | Add-Member -MemberType NoteProperty -Name "Hostname" -Value $bindingip.PSComputerName
$report | Add-Member -MemberType NoteProperty -Name "IPAddress" -Value $bindingip.IPAddress
$report | Add-Member -MemberType NoteProperty -Name "Site Name" -Value $sitedetails.name
$report | Add-Member -MemberType NoteProperty -Name "ID" -Value $sitedetails.id
$report | Add-Member -MemberType NoteProperty -Name "State" -Value $sitedetails.state
$report | Export-Csv C:\content.csv -NoTypeInformation
Output of CSV:
Hostname IPAddress Site Name ID State
System.Object[] System.Object[] System.Object[] System.Object[] System.Object[]
Do i need to add anything to the code to get exact output, Can anyone help on this.
As Abraham and Ash pointed out in their comments, the values of the properties on your variables $bindingip and $sitedetails are an array instead of a string. This is why when you export the report you get the object type instead of it's actual value.
You could see this by doing for example this:
$bindingip.PSComputerName.GetType()
Which would return something like this:
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
However if you select just the first element on the array PSComputerName
$bindingip.PSComputerName[0].GetType()
You would see:
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object
A workaround for this, is to either convert the values to a multiline string by the use of Out-String or by joining the elements of the arrays with a delimiter i.e. -join '//'.
$bindingip = Get-WmiObject -Namespace root\webadministration -Class sslbinding2
$sitedetails = Get-Website
# Like this (thanks mklement0):
$report = [pscustomobject]#{
Hostname = "$($bindingip.PSComputerName)"
IPAddress = "$($bindingip.IPAddress)"
'Site Name' = "$($sitedetails.name)"
ID = "$($sitedetails.id)"
State = "$($sitedetails.state)"
}
# Or like this:
$report = [pscustomobject]#{
Hostname = ($bindingip.PSComputerName | Out-String).trim()
IPAddress = ($bindingip.IPAddress | Out-String).trim()
'Site Name' = ($sitedetails.name | Out-String).trim()
ID = ($sitedetails.id | Out-String).trim()
State = ($sitedetails.state | Out-String).trim()
}
# Or like this:
$report = [pscustomobject]#{
Hostname = $bindingip.PSComputerName -join '//'
IPAddress = $bindingip.IPAddress -join '//'
'Site Name' = $sitedetails.name -join '//'
ID = $sitedetails.id -join '//'
State = $sitedetails.state -join '//'
}
$report | Export-Csv C:\content.csv -NoTypeInformation
Edit
This can also work, which is what I think mklement0 suggested. It will create a new object for each element on the values of the properties:
$compName = $bindingip.PSComputerName
$ipAddr = $bindingip.IPAddress
$name = $sitedetails.name
$id = $sitedetails.id
$state = $sitedetails.state
$top = ($compName.Count,$ipAddr.Count,$name.Count,$id.Count,$state.Count | Measure-Object -Maximum).Maximum
$report = for($i = 0;$i -lt $top;$i++)
{
[pscustomobject]#{
Hostname = $compName[$i]
IPAddress = $ipAddr[$i]
'Site Name' = $name[$i]
ID = $id[$i]
State = $state[$i]
}
$report | Export-Csv...
In addition to Santiago's very thorough and excellent answer, it appears to me that we are just combining the objects of one array with another to produce all possible combinations. I might do something like the following to accomplish this.
$bindingip = #(
[PSCustomObject]#{
PSComputerName = 'Public1'
IPAddress = '192.168.0.1'
},
[PSCustomObject]#{
PSComputerName = 'Public1'
IPAddress = '127.0.0.1'
}
)
$siteDetails = #(
[PSCustomObject]#{
Name = 'site 1'
Id = 'site1'
State = 'up'
},
[PSCustomObject]#{
Name = 'site 2'
Id = 'site2'
State = 'down'
}
)
$combined = foreach ($ip in $bindingip) {
foreach ($details in $siteDetails) {
$out = [ordered]#{}
$ip.psobject.properties | ForEach-Object {
$out[$_.Name] = $_.Value
}
$details.psobject.properties | ForEach-Object {
$out[$_.Name] = $_.Value
}
[pscustomobject]$out
}
}
$combined | Format-Table
Output
PSComputerName IPAddress Name Id State
-------------- --------- ---- -- -----
Public1 192.168.0.1 site 1 site1 up
Public1 192.168.0.1 site 2 site2 down
Public1 127.0.0.1 site 1 site1 up
Public1 127.0.0.1 site 2 site2 down
One might wrap this in a function for reusability
function Combine-ObjectsFromTwoArrays {
param (
[array]$array1,
[array]$array2
)
foreach ($obj1 in $array1) {
foreach ($obj2 in $array2) {
$out = [ordered]#{}
$obj1.psobject.properties | ForEach-Object {
$out[$_.Name] = $_.Value
}
$obj2.psobject.properties | ForEach-Object {
$out[$_.Name] = $_.Value
}
[pscustomobject]$out
}
}
}

Change arraylist rows to column using Powershell

I am trying to change the format of an arraylist after I have used
group-object
to count all the entries in the list.
this is a sample
$list = [System.Collections.ArrayList]#()
$list = "letter","solo","nap","nap","nap","sharp","ignite","tap","tap","tap","tap","evoke"
$list | Group-Object | select name,count
This is the sample output
Name Count
---- -----
letter 1
solo 1
nap 3
sharp 1
ignite 1
tap 4
evoke 1
What I would like is
letter solo nap sharp ignite tap evoke
-------- ----- ---- ---- ----- ------ ----
1 1 3 4 1 4 1
Then when exporting to excel it would format like this
Everything I have tried doesn't seem to pay off, or even get close to what I am trying to do and I think I am missing something obvious or have hit my PowerShell skill limitations. Could someone please help. Thank you
You could create a PSObject, add the properties to it with Add-Member, then format the output to a table with Format-Table:
$list = "letter","solo","nap","nap","nap","sharp","ignite","tap","tap","tap","tap","evoke"
$groups = $list | Group-Object | Select-Object Name, Count
$psObject = New-Object -TypeName psobject
foreach ($group in $groups) {
$psObject | Add-Member -NotePropertyName $group.Name -NotePropertyValue $group.Count
}
$psObject | Format-Table
Output:
evoke ignite letter nap sharp solo tap
----- ------ ------ --- ----- ---- ---
1 1 1 3 1 1 4
Skip Group-Object altogether - instead, use a dictionary to keep track of the count, then cast the whole dictionary to a custom object:
$properties = [ordered]#{}
$list |ForEach-Object {
$properties[$_]++
}
$counts = [pscustomobject]$properties
$counts will now hold an object like what you describe, formatting as a table gives you:
PS C:\> $counts |Format-Table
letter solo nap sharp ignite tap evoke
------ ---- --- ----- ------ --- -----
1 1 3 1 1 4 1
You may try something like:
$list = [System.Collections.ArrayList]#()
$list = "letter","solo","nap","nap","nap","sharp","ignite","tap","tap","tap","tap","evoke"
$group = $list | Group-Object | select name,count
$a = [PSCustomObject]#{}
foreach ($item in $group) {
$a | Add-Member -NotePropertyName $item.name -NotePropertyValue $item.count
}
$a | ft
One solution would be to put it into an PsObject and then export that object into a CSV:
$list = [System.Collections.ArrayList]#()
$list = "letter","solo","nap","nap","nap","sharp","ignite","tap","tap","tap","tap","evoke"
$hash = $list | Group-Object | select name,count
$object = New-Object psobject
foreach( $item in $hash ) {
$column_name = $item.Name
$row_value = $item.Count
$object | Add-Member -MemberType NoteProperty -Name $column_name -Value $row_value
}
$object | Export-csv 'Path to your CSV' -NoTypeInformation

How to add header to a CSV file initially with PowerShell

I want to add headers to a CSV file initially. The reason I want to add the headers initially is for some rows, some column values might be empty.
As per Microsoft documentation, the export-csv only takes headers/columns which is present in first row.
When you submit multiple objects to Export-CSV, Export-CSV organizes the file >based on the properties of the first object that you submit. If the remaining >objects do not have one of the specified properties, the property value of >that object is null, as represented by two consecutive commas. If the >remaining objects have additional properties, those property values are not >included in the file.
https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/export-csv?view=powershell-6#notes
What I have tried so far:
$csvContents =#()
foreach ($item in $items) {
$row = New-Object System.Object # Create an object to append to the array
$row | Add-Member -MemberType NoteProperty -Name "header1" -Value "value1"
$row | Add-Member -MemberType NoteProperty -Name "header2" -Value "value2"
$row | Add-Member -MemberType NoteProperty -Name "header3" -Value "value3"
$csvContents += $row # append the new data to the array
}
$csvContents | Export-CSV -NoTypeInformation -Path $ResultCsvPath
The problem is for example Item1 may have only header1 and header3, means these columns are dynamic. So after export the result csv will only contain header1 and header3 only and header2 will be missing.
What I expect is that I want to add header1, header2, header3 initially.
With large collections this may take up some time, but here's something that might work for you:
Suppose this is your collection
$obj = #(
[pscustomobject]#{
'header1' = 'value1'
'header3' = 'value3'
},
[pscustomobject]#{
'header1' = 'value1'
'header2' = 'value2'
},
[pscustomobject]#{
'header3' = 'value3'
},
[pscustomobject]#{
'header1' = 'value1'
'header2' = 'value2'
'header3' = 'value3'
}
)
Then you can add the missing properties like:
# Try and find all headers by looping over all items.
# You could change this to loop up to a maximum number of items if you like.
# The headers will be captured in the order in which they are found.
$headers = $obj | ForEach-Object {($_.PSObject.Properties).Name} | Select-Object -Unique
# Find the missing headers in the first item of the collection
# and add those with value $null to it.
$headers | Where-Object { ($obj[0].PSObject.Properties).Name -notcontains $_ } | ForEach-Object {
$obj[0] | Add-Member -MemberType NoteProperty -Name $_ -Value $null
}
# output on console
$obj
# output to csv file
$obj | Export-Csv -Path 'D:\test.csv' -NoTypeInformation
Output:
header1 header3 header2
------- ------- -------
value1 value3
value1 value2
value3
value1 value3 value2
Make sure if the data is missing for the column in a row that you use a $null value. I just tested this and get the output you are expecting.
$row = New-Object System.Object # Create an object to append to the array
$row | Add-Member -MemberType NoteProperty -Name "header1" -Value "value1"
$row | Add-Member -MemberType NoteProperty -Name "header2" -Value $null
$row | Add-Member -MemberType NoteProperty -Name "header3" -Value "value3"
$row | Export-Csv -NoTypeInformation test.csv
Output (from CSV)
"header1","header2","header3"
"value1",,"value3"
Depending on how complex you want to go, Ive done something similar to this in the past (I;ve changed it to use [pscustomobject] too):
$csvcontents = $items | foreach-object {
If (-not $_.Header1) { $value1 = '' } Else { $value1 = $_.Value1 }
If (-not $_.Header2) { $value2 = '' } Else { $value1 = $_.Value2 }
If (-not $_.Header3) { $value3 = '' } Else { $value1 = $_.Value3 }
[pscustomobject]#{header1 = $_.value1;header2=$value2;$header3=$value3}
}
Disclaimer, not tested the above, but it gives you the gist of the idea.
Please find here an example creating a CSV based of a generated directory output:
$basedir = "E:\20220205MassUpload1"
$picDir = "$($baseDir)\pics"
$res=ls $picDir
$outputArray = #()
foreach($line in $res) {
$name = $($line.name).Replace(".png","")
$outputArray += (
[pscustomobject]#{
Title = "$name"
Brand = 'Brand'
BulletPoint1='BulletPoint1'
BulletPoint2='BulletPoint2'
Description='Description'
Price='19.95'
Tags='Tags'
ImagePath="$($picDir)\$($line.name)"
});
}
$outputArray | Export-csv -NoTypeInformation "$basedir\upload.csv"
What it does it, take a list of all elements in a directory.
Create a "pscustomobject" for each result and create the values for the column, some are "fixed" and some "calculated".
Last the Array will be piped to a CSV.
I'm sure the other methods are the "right" way to do it, but I stumbled on a much easier way at some point in the past.
When I start a new CSV, I use something like this:
$vOutputFileName = ".\DDG-Info.csv"
"Name,DisplayName,RecipientFilter" | Out-File $vOutputFileName -encoding ASCII
Then in the script, I do the same thing:
$vOutput = "$vDDGName,$vDisplayName,$vRF"
"$vOutput" | Out-File $vOutputFileName -Append -encoding ASCII
Seems to work just fine, no messing around with explicitly creating objects etc.

Script output that will work on the console as well as with Export-Csv

I'm working on a basic PowerShell script that inputs a pair of dates then gets all accounts with passwords expiring between those times. I'd like to output the data to the console in a way that is compatible with Export-Csv. That way the person running the script can either just view in the console, or get a file.
Here is my script:
[CmdletBinding()]
param(
[string]$StartDate = $(throw "Enter beginning date as MM/DD/YY"),
[string]$EndDate = $(throw "Enter end date as MM/DD/YY")
)
$start = Get-Date($StartDate)
$end = Get-Date($EndDate)
$low = $start.AddDays(-150)
$high = $end.AddDays(-150)
$passusers = Get-ADUser -Filter { PasswordLastSet -gt $low -and PasswordLastSet -lt $high -and userAccountControl -ne '66048' -and userAccountControl -ne '66080' -and enabled -eq $true} -Properties PasswordLastSet,GivenName,DisplayName,mail,LastLogon | Sort-Object -Property DisplayName
$accts = #()
foreach($user in $passusers) {
$passLastSet = [string]$user.PasswordLastSet
$Expiration = (Get-Date($passLastSet)).addDays(150)
$obj = New-Object System.Object
$obj | Add-Member -MemberType NoteProperty -Name Name -Value $user.DisplayName
$obj | Add-Member -MemberType NoteProperty -Name Email -Value $user.mail
$obj | Add-Member -MemberType NoteProperty -Name Expiration -Value $expiration
$accts += $obj
}
Write-Output ($accts | Format-Table | Out-String)
This prints to the console perfectly:
Name Email Expiration
---- ----- ----------
Victor Demon demonv#nsula.edu 1/3/2016 7:16:18 AM
However when called with | Export-Csv it doesn't:
#TYPE System.String
Length
5388
I've tried multiple variations using objects, and data tables, however it seems like I can only get it to work for console or for CSV, not for both.
Replace
Write-Output ($accts | Format-Table | Out-String)
with
$accts
That way your users can run your script any way they like, e.g.
.\your_script.ps1 | Format-Table
.\your_script.ps1 | Format-List
.\your_script.ps1 | Export-Csv
.\your_script.ps1 | Out-GridView
...
Format-Table | Out-String converts your output to a single string whereas Export-Csv expects a list of objects as input (the object properties then become the columns of the CSV). If Export-Csv is fed a string, the only property is Length, so you get a CSV with one column and one record.
$accts | ConvertTo-Csv | Tee -File output.csv | ConvertFrom-Csv