Issue in export Array to CSV file - csv

I have list of machine in text file and I am trying to get the details of physical drives, OS architecture and physical memory. With the help of Matt (SO user) here is the powershell script.
$server = Get-Content .\Server.txt
#$infoObject11 = #{}
$infoObject11 = #{}
foreach ($server in $servers) {
# Gather all wmi drives query at once
$alldisksInfo = Get-WmiObject -Query "SELECT * FROM Win32_DiskDrive" -ComputerName $server -ErrorAction SilentlyContinue | Group-Object __Server
# Figure out the maximum number of disks
$MaximumDrives = $alldisksInfo | Measure-Object -Property Count -Maximum | Select-Object -ExpandProperty Maximum
# Build the objects, making empty properties for the drives that dont exist for each server where need be.
$server | ForEach-Object {
# Clean the hashtable
$infoObject1 = #{}
# Populate Server
$infoObject1.Server = $server
$HOSTNAME = Get-WMIObject -Query "Select * from Win32_OperatingSystem" -ComputerName $infoObject1.Server
# Add other simple properties here
$infoObject1.PhysicalMemory = (Get-WmiObject Win32_PhysicalMemory -ComputerName $infoObject1.Server | Measure-Object Capacity -Sum).Sum/1gb
$infoObject1.OSarchitecture =$HOSTNAME.osarchitecture
# Add the disks information from the $diskInfo Array
$serverDisksWMI = $alldisksInfo | Where-Object{$_.Name -eq $infoObject1.Server} | Select-Object -ExpandProperty Group
for ($diskIndex =0; $diskIndex -lt $MaximumDrives;$diskIndex++) {
$infoObject1."PhysicalDisk$diskIndex" = [Math]::Round(($serverDisksWMI | Where-Object{($_.DeviceID -replace "^\D*") -eq $diskIndex} | Select -Expand Size)/1GB)
}
}
# Create the custom object now.
New-Object -TypeName psobject -Property $infoObject1 | Export-Csv -path .\Server_Inventory_$((Get-Date).ToString('MM-dd-yyyy')).csv -NoTypeInformation
}
Problem is in the CSV file I am getting single machine details but in server.txt file there are more than 1 machine. If I print $infoObject1 before New-Object then I can see there are details of multiple machine. It seems like some issue with array and I am not able to export it in CSV.
Can anybody please suggest on this.

It looks like you are having issues integrating my code. You have added a second loop that should not be there. Also as other users pointed out you are not creating the per server object outside the loop. The answer, from where your code comes from, has that part correct. I had even showed you where to put the Export-CSV.
$servers = Get-Content .\Server.txt
# Gather all wmi drives query at once
$alldisksInfo = Get-WmiObject -Query "SELECT * FROM Win32_DiskDrive" -ComputerName $servers -ErrorAction SilentlyContinue | Group-Object __Server
# Figure out the maximum number of disks
$MaximumDrives = $alldisksInfo | Measure-Object -Property Count -Maximum | Select-Object -ExpandProperty Maximum
# Build the objects, making empty properties for the drives that dont exist for each server where need be.
$servers | ForEach-Object {
# Clean the hashtable
$infoObject1 = #{}
# Populate Server
$infoObject1.Server = $_
# Add other simple properties here
$infoObject1.PhysicalMemory = (Get-WmiObject Win32_PhysicalMemory -ComputerName $infoObject1.Server | Measure-Object Capacity -Sum | Select-Object -ExpandProperty Sum)/1GB
$infoObject1.OSarchitecture = Get-WMIObject -Query "Select * from Win32_OperatingSystem" -ComputerName $infoObject1.Server | Select-Object -ExpandProperty OSArchitecture
# Add the disks information from the $diskInfo Array
$serverDisksWMI = $alldisksInfo | Where-Object{$_.Name -eq $infoObject1.Server} | Select-Object -ExpandProperty Group
for ($diskIndex =0; $diskIndex -lt $MaximumDrives;$diskIndex++) {
$infoObject1."PhysicalDisk$diskIndex" = [Math]::Round(($serverDisksWMI | Where-Object{($_.DeviceID -replace "^\D*") -eq $diskIndex} | Select-Object -ExpandProperty Size)/1GB)
}
# Create the custom object now for this pass in the loop.
New-Object -TypeName psobject -Property $infoObject1
} | Export-Csv -path .\Server_Inventory_$((Get-Date).ToString('MM-dd-yyyy')).csv -NoTypeInformation

foreach ($server in $servers) {
...
New-Object -TypeName PSObject -Property $infoObject1 |
Export-Csv -Path .\Server_Inventory_$((Get-Date).ToString('MM-dd-yyyy')).csv -NoTypeInformation
}
You're exporting inside the loop without using the parameter -Append (available in PowerShell v3 and newer). That overwrites your output file with each iteration, leaving you with just the data of the last server.
Either use the parameter -Append (if you have PowerShell v3 or newer):
foreach ($server in $servers) {
...
New-Object -TypeName PSObject -Property $infoObject1 |
Export-Csv -Append -Path .\Server_Inventory_$((Get-Date).ToString('MM-dd-yyyy')).csv -NoTypeInformation
}
or move Export-Csv outside the loop (works with all PowerShell versions):
(foreach ($server in $servers) {
...
New-Object -TypeName PSObject -Property $infoObject1
}) | Export-Csv -Path .\Server_Inventory_$((Get-Date).ToString('MM-dd-yyyy')).csv -NoTypeInformation
Note that you need to run the loop in parentheses for this to work, as foreach loops don't output to the pipeline.
You could also replace the foreach loop with ForEach-Object if you want to feed the pipeline directly:
Get-Content .\Server.txt | ForEach-Object {
$server = $_
...
New-Object -TypeName PSObject -Property $infoObject1
} | Export-Csv -Path .\Server_Inventory_$((Get-Date).ToString('MM-dd-yyyy')).csv -NoTypeInformation

Related

Adding JSON content to PSCustomObject - The property cannot be found on this object

Quite new to objects in PS.
I'm trying to create pscustomobject, adding JSON contents to it via ConvertFrom-JSON and then I'm trying to get contents from another JSON to be set on one of the properties ( nested hierarchy)
$combinedObject=#()
$props = #{
ServiceDefinitions = #()
DataCenters = #()
}
$combinedObject = New-Object -TypeName PSCustomObject -Property $props
$servicedefinitions = Get-ChildItem -Path .\ServiceDefinitions\ | Select Name
$datacenters = Get-ChildItem -Path .\DataCenters\ | Select Name
$environments = #("Production")
$env="TEST"
Foreach ($datacenter in $datacenters)
{
$datacenterdata = $null
write-host "new run"
write-host $datacenter.Name
$datacentername = $datacenter.Name
$datacenterdata = Get-Content -Path .\DataCenters\$datacentername\config.json -Raw
$datacenterformatteddata = $datacenterdata | ConvertFrom-Json -Depth 5
$combinedObject.DataCenters += $datacenterformatteddata
$combinedObject.DataCenters.$datacentername
}
Foreach ($datacenter in $datacenters)
{
$pods = $null
$datacetnername = $null
$datacentername = $datacenter.Name
$pods = Get-ChildItem -Path .\DataCenters\$datacentername\$env\Pod\ | Select Name
Foreach ($pod in $pods)
{
$podname = $pod.Name
$poddata = Get-Content -Path .\DataCenters\$datacentername\$env\Pod\$podname\config.json -Raw
#echo $combinedObject.DataCenters
write-host $datacentername
$podformatteddata = $poddata | ConvertFrom-Json -Depth 5
$combinedObject.DataCenters.$datacentername.pods += $podformatteddata
}
}
For each loop iterations I receive
The property 'pods' cannot be found on this object. Verify that the property exists and can be set.
I can query the pods but cannot set it, it looks to be of a system type System.Object[] do I need to convert it somehow to PSCustomObject for the contents of the next JSON file to be added to it?
Resolved by changing JSON from
pods:[] to
podlist:{ pods:[]}
and referencing
$combinedObject.DataCenters.$datacentername.podlist.pods
to set the value.

Modifying JSON file using data from CSV in PowerShell

I'm trying to modify some specific values in a .json file based on two columns in a .csv file. If the current value in the .json file is identical to the one in the left column, I want to change it to the one in the right column.
This is my first time with PowerShell though, so I'm struggling to figure out how to go about doing this. I feel like my solution is not only wrong, but is using a double for loop when it might not need to. Here's what I have so far.
$jsonData = Get-Content -Path $jsonFile | ConvertFrom-Json
$csvData = Get-Content -Path $csvFile | Select-Object -Skip 1 # Skipping the header
foreach ($jsonItem in $jsonData.'Placeable List') {
foreach ($csvRow in $csvData) {
$splitRow = $csvRow -split ","
$lCol = $splitRow[0]
$rCol = $splitRow[1]
$currentItem = $jsonItem.'value'.'Appearance'.'value'
if ($currentItem -eq $lCol) {
$currentItem -eq $rCol
}
}
}
I managed to figure it out.
$csvData = Get-Content -Path $csvFile | Select-Object -Skip 1 # Skipping the header
$jsonData = Get-Content -Path $jsonFile -raw | ConvertFrom-Json
foreach($csvRow in $csvData) {
$splitRow = $csvRow -split ","
$lCol = $splitRow[0]
$rCol = $splitRow[1]
foreach($item in $jsonData.'Placeable List'.value) {
$item.Appearance | % {
if ($_.value -eq $lCol) {
$_.value = $rCol
}
}
}
}
$jsonData | ConvertTo-Json -depth 32 | Set-Content $jsonFile

Powershell sort PSObject alphabetically

Given a custom powershell object (bar) that is created from json (foo.json)
How would you sort the object alphabetically by key?
foo.json
{
"bbb": {"zebras": "fast"},
"ccc": {},
"aaa": {"apples": "good"}
}
Desired output
foo.json
{
"aaa": {"apples": "good"},
"bbb": {"zebras": "fast"},
"ccc": {}
}
Example
$bar = get-content -raw foo.json | ConvertFrom-Json
$bar.gettype()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True False PSCustomObject System.Object
I've tried the following using sort-object
$bar = $bar | Sort
$bar = $bar | Sort-Object
Sort-Object -InputObject $bar
Sort-Object -InputObject $bar -Property Name
Sort-Object -InputObject $bar -Property #{Expression="Name"}
Sort-Object -InputObject $bar -Property #{Expression={$_.PSObject.Properties.Name}}
I've also tried converting the PSObject to a hashtable (hashtables appear to automatically sort based on name), then convert that hashtable back to json, but it looses the order again.
$buzz = #{}
$bar.psobject.properties |Foreach { $buzz[$_.Name] = $_.Value }
ConvertTo-Json $buzz -Depth 9
Update
Changed foo.json to include values aswell as keys
As Mathias R. Jessen notes, there is no collection to sort here, just a single object whose properties you want to sort, so you need reflection via Get-Member to obtain the object's properties:
$bar = get-content -raw foo.json | ConvertFrom-Json
# Build an ordered hashtable of the property-value pairs.
$sortedProps = [ordered] #{}
Get-Member -Type NoteProperty -InputObject $bar | Sort-Object Name |
% { $sortedProps[$_.Name] = $bar.$($_.Name) }
# Create a new object that receives the sorted properties.
$barWithSortedProperties = New-Object PSCustomObject
Add-Member -InputObject $barWithSortedProperties -NotePropertyMembers $sortedProps
A more streamlined version that uses -pv (-PipelineVariable) to "cache" the unsorted custom object produced by ConvertFrom-Json:
$barSortedProps = New-Object PSCustomObject
Get-Content -Raw foo.json | ConvertFrom-Json -pv jo |
Get-Member -Type NoteProperty | Sort-Object Name | % {
Add-Member -InputObject $barSortedProps -Type NoteProperty `
-Name $_.Name -Value $jo.$($_.Name)
}
what about this:
Function Sort-PSObject {
[CmdletBinding()]
Param(
[Parameter(ValueFromPipeline=$true)]$inputString
)
process {
($inputString | out-string).trim() -split "`r`n" | sort
}
}
Can send direct from pipeline
A combined version of #mklement0 and #EricWeintraub's answers:
Function Sort-PSObjectMembers {
[CmdletBinding()]
Param(
[Parameter(ValueFromPipeline=$true)]$inputObj
)
process {
$sortedProps = [ordered] #{}
Get-Member -Type NoteProperty -InputObject $inputObj | Sort-Object Name | ForEach-Object { $sortedProps[$_.Name] = $inputObj.$($_.Name) }
# Create a new object that receives the sorted properties.
$sortedObj = New-Object PSCustomObject
Add-Member -InputObject $sortedObj -NotePropertyMembers $sortedProps
return $sortedObj
}
}
So you can use it like this:
$elements | Sort-PSObjectMembers | ConvertTo-Json -Depth 32 | Out-File "elements.json" -Encoding utf8

How can I combine fields in a .csv based off of a shared value in powershell?

I have two files in identical formats, one containing destination IP addresses and URLs, and one that contains only the destination IP addresses. I am attempting to write a powershell script to add the URL field from the first file to the second file for that row if the destination IP addresses are equal. Here is an example of the two files:
File Containing URLs:
Date;Time;Source;Destination;Port;User;URL
3/7/2016;0:00:07;168.254.25.6;10.0.1.27;80;jsmith;abcnet
File to add URLs to:
Date;Time;Source;Destination;Port;User;URL
3/7/2016;0:00:09;168.254.25.6;10.0.1.27;80;;
Whenever I run the code below, it appears to be caught in an infinite loop because it does not run to completion, but it throws no errors. My data set is thousands of lines long, but it works when I test it with a sample set that is only a few lines long.
$noURLs = Import-Csv C:\Path\to\noURLs.csv
$containsURLs = Import-Csv C:\Path\to\containsURLs.csv | Select-Object Destination, URL
$outputFile = "C:\Path\to\output.csv"
if(Test-Path $outputFile){
Remove-Item $outputFile
}
foreach($line in $noURLs){
$cpDest = $line.Destination
$destURL = $containsURLs | Where-Object {$_.Destination -eq $cpDest} | Select-Object -ExpandProperty URL | Select-Object -Unique
if($destURL -ne $null){
if( $destURL.Count -gt 1) {
$destURL = $destURL -join ';'
}
}
$line.URL = $destURL
}
$noURLs | Export-Csv $outputFile
I forgot to add a -unique switch to my select object, so for every one record in the first csv, it was looping through every single line of the second csv. Fixed code looks like this:
$noURLs = Import-Csv C:\Path\to\noURLs.csv
$containsURLs = Import-Csv C:\Path\to\containsURLs.csv | Select-Object -Unique Destination, URL
$outputFile = "C:\Path\to\output.csv"
if(Test-Path $outputFile){
Remove-Item $outputFile
}
foreach($line in $noURLs){
$cpDest = $line.Destination
$destURL = $containsURLs | Where-Object {$_.Destination -eq $cpDest} | Select-Object -ExpandProperty URL | Select-Object -Unique
if($destURL -ne $null){
if( $destURL.Count -gt 1) {
$destURL = $destURL -join ';'
}
}
$line.URL = $destURL
}
$noURLs | Export-Csv $outputFile -NoTypeInformation

Powershell Certificate Extensions export-csv

I'm trying to gather certificate extensions into a csv. The PowerShell commands seem to display just fine within PowerShell but when piped to a csv they display other types of data?
$cert = Get-ChildItem cert:\localmachine -Recurse
($cert.Extensions | Where-Object {$_.Oid.FriendlyName -eq "Key Usage"}).Format(1) | Export-Csv C:\Folder\File.csv
After searching the web I tried something different but still was unable to get the data to get captured and displayed as it does within PowerShell.
$cert=Get-ChildItem cert:\localmachine -Recurse
$sanExt=$cert.Extensions | Where-Object {$_.Oid.FriendlyName -match "subject alternative name"}
$sanObjs = new-object -ComObject X509Enrollment.CX509ExtensionAlternativeNames
$altNamesStr=[System.Convert]::ToBase64String($sanExt.RawData)
$sanObjs.InitializeDecode(1, $altNamesStr)
Foreach ($SAN in $sanObjs.AlternativeNames) {$SAN.strValue}
$sanExt.Format(1) | Export-Csv C:\Folder\File.csv
For others looking to also gather the extension fields, this was my resolution:
$cert = Get-ChildItem cert:\localmachine -Recurse
($cert.Extensions | Where-Object {$_.Oid.FriendlyName -eq "Key Usage"}).Format(1) |
Select-Object -Property #{Name="Certificate";Expression={$_}} |
Export-csv -Path C:\Folder\file.csv`