Combining output of many objects into a CSV - powershell

I'd like to take specific output of four objects and put them into a CSV.
$obj1 = get-wmiobject win32_computersystem | select-object name,manufacturer,model,totalphysicalram
$obj2 = get-wmiobject win32_processor | select-object deviceid,name,maxclockspeed,numberofcores
$obj3 = get-wmiobject win32_bios | select-object serialnumber
$obj4 = get-wmiobject win32_operatingsystem | select-object osarchitecture
I'd like to combine all of those into the same output row in a csv.
How can I do this with powershell? Should I build an array of objects?

I don't fault you for not having a start on this, I wouldn't have had any idea where to go with code a year ago myself. Try this out and see how it strikes you...
$obj1 = get-wmiobject win32_computersystem | select-object name,manufacturer,model,TotalPhysicalMemory
$obj2 = get-wmiobject win32_processor | select-object deviceid,#{l="Proc";e={$_.name}},maxclockspeed,numberofcores
$obj3 = get-wmiobject win32_bios | select-object serialnumber
$obj4 = get-wmiobject win32_operatingsystem | select-object osarchitecture
$Combined = New-Object -Type PSObject
$obj1,$obj2,$obj3,$obj4|%{$CurObj = $_;$_|gm|?{$_.MemberType -match "NoteProperty"}|%{$NewMember = $_.Name;$Combined|Add-Member -MemberType NoteProperty -Name $NewMember -Value $CurObj.$NewMember}}
I fixed TotalPhysicalMemory (not totalphysicalram), and changed the proc query so it renamed the Name property to Proc, that way you don't have 2 properties with the same name. Then I fed all 4 objects into a ForEach loop, and for each object I got it's members and filtered for the NoteProperty members (getting rid of Methods and what not). For each one I took the member's name and created a property in a new PSCustomObject that I had created with that same name, and assigned the value from the original object associated with that property. I hope that makes sense, I found it a little hard to follow and I wrote the thing...
Edit: Dur... I didn't answer the original question, I just got all the prep work done. Here's how you output it:
$Combined|Export-CSV -Path C:\SomeFolder\MachineSpecs.CSV -NoTypeInfo

I tried running TheMadTechnician's answer on PSv3 and it didn't like the Out-CSV cmdlet. So I replaced it with Export-CSV and it worked fine.
$obj1 = get-wmiobject win32_computersystem | select-object name,manufacturer,model,TotalPhysicalMemory
$obj2 = get-wmiobject win32_processor | select-object deviceid,#{l="Proc";e={$_.name}},maxclockspeed,numberofcores
$obj3 = get-wmiobject win32_bios | select-object serialnumber
$obj4 = get-wmiobject win32_operatingsystem | select-object osarchitecture
$Combined = New-Object -Type PSObject
$obj1,$obj2,$obj3,$obj4|%{$CurObj = $_;$_|gm|?{$_.MemberType -match "NoteProperty"}|%{$NewMember = $_.Name;$Combined|Add-Member -MemberType NoteProperty -Name $NewMember -Value $CurObj.$NewMember}}
$Combined | Export-CSV -Path H:\MachineSpecs.CSV -NoTypeInfo
Gives the output of:
"manufacturer","model","name","TotalPhysicalMemory","deviceid","maxclockspeed","numberofcores","Proc","serialnumber","osarchitecture"
"Hewlett-Packard","HP EliteBook 8760w","LAUCA-44A135","8537874432","CPU0","2201","4","Intel(R) Core(TM) i7-2670QM CPU # 2.20GHz","USH244A135","64-bit"

Here's a readable syntax I like for this task, hope this helps. This creates a hashtable of properties and adds it to a PSObject, then adds that to an array. You can do as you wish with the resulting array. I typically run this in a foreach loop against a list of remote computers. You can glom together all kinds of disparate data for reporting very easily this way.
$Results = #()
$obj1 = get-wmiobject win32_computersystem
$obj2 = get-wmiobject win32_processor
$obj3 = get-wmiobject win32_bios
$obj4 = get-wmiobject win32_operatingsystem
$Obj = new-object psobject -property#{
PropertyName = $Obj2.Property
SomeOtherPropertName = $obj1.Property
SerialNumber = $obj3.SerialNumber
DateCollected = (get-date).DateTime
OSArchitecture = $obj4.OsArchitecture
}
$Results+=$obj

Related

How can you add text to a PowerShell table result?

I'm trying to figure out how I can add some text such as MHz or GB to the results in the PowerShell table. Is it possible with a foreach loop?
Get-WmiObject Win32_PhysicalMmory |
Format-Table Banklabel, Manufacturer,
#{Label="Speed"; Expression={$_.ConfiguredClockSpeed}},
#{Label="Capacity"; Expression={[int64]($_.Capacity/1GB)}} -AutoSize
I have another way that comes close, but will not work with multiple results as it doesnt add the informtion into new rows.
$Disk = Get-WmiObject -Class Win32_LogicalDisk
$DiskSpace = [int64]($Disk.Size/1GB)
$FreeSpace = [INT64]($Disk.FreeSpace/1GB)
$UsedSpace = (($DiskSpace-$FreeSpace)/$DiskSpace).ToString("P0")
$OBJ = New-Object PSObject
$OBJ | Add-Member NoteProperty "ID" ($Disk.DeviceID)
$OBJ | Add-Member NoteProperty "Name" ($Disk.VolumeName)
$OBJ | Add-Member NoteProperty "Format" ($Disk.FileSystem)
$OBJ | Add-Member NoteProperty "Capacity" ("$DiskSpace-GB")
$OBJ | Add-Member NoteProperty "Free Space" ("$FreeSpace-GB")
$OBJ | Add-Member NoteProperty "Used" ($UsedSpace)
Write-Output $OBJ | Format-Table
You can use much more complex expression, see following example:
Get-WmiObject win32_physicalmemory | Format-Table Banklabel, Manufacturer, #{Label="Speed";`
Expression={$_.ConfiguredClockSpeed}}, #{Label="Capacity";`
Expression={([INT64]($_.Capacity/1GB)).ToString()+" GB"}} -autosize
use the concatenation for your requirement.
replace
Get-WmiObject win32_physicalmemory | Format-Table Banklabel, Manufacturer, #{Label="Speed";` Expression={$_.ConfiguredClockSpeed}}, #{Label="Capacity";` Expression={[INT64]($_.Capacity/1GB)}} -autosize
with this:
Get-WmiObject win32_physicalmemory | Format-Table Banklabel, Manufacturer, #{Label="Speed";` Expression={"$($_.ConfiguredClockSpeed) MHz"}}, #{Label="Capacity";` Expression={"$([INT64]($_.Capacity/1GB)) GB"}} -autosize
Hope it helps.
Because you're already using calculated properties it's easy to extend them. It depends what you try to achieve. I would recommend to place the unit into table header like this:
Get-WmiObject win32_physicalmemory |
Select-Object -Property Banklabel, Manufacturer, #{Name='Speed in MHz';Expression={$_.ConfiguredClockSpeed}}, #{Name='Capacity in GB'; Expression={[INT64]($_.Capacity/1GB)}}
If you really need it in the table you could do it like this:
Get-WmiObject win32_physicalmemory |
Select-Object -Property Banklabel, Manufacturer, #{Name='Speed';Expression={"{0} MHz" -f $_.ConfiguredClockSpeed}}, #{Name='Capacity in GB'; Expression={"{0} GB" -f [INT64]($_.Capacity/1GB)}}
But this way you loose the possibility to calculate with your results. It's just strings now.

Powershell Script to join two function results based on common key columns

Could someone provide a PowerShell script to join two function results based on common key columns.
Example:
Result1 and Result2 has common field 'Name'. I want to join both the results and fetch the below informations.
$Result1 = get-wmiobject -ComputerName localhost -Class win32_service
$Result2 = get-service
Result
Name : wuauserv
DisplayName : Windows Update
Status : Running
StartMode : Manual
ProcessId : 400
Use below PowerShell script to join both the results based on common key column (Name).
$Result1=get-wmiobject -ComputerName localhost -Class win32_service
$Result2=get-service
$Result=#()
for($i=0;$i -lt $Result2.count;$i++)
{
$startmode=($Result1 | where{$_.Name -eq $Result2[$i].Name})|Select StartMode,ProcessId
$tempObj=new-object PSObject
$tempObj | Add-member noteproperty Name $Result2[$i].Name
$tempObj | Add-member noteproperty DisplayName $Result2[$i].DisplayName
$tempObj | Add-member noteproperty Status $Result2[$i].Status
$tempObj | Add-member noteproperty StartMode $startmode.StartMode
$tempObj | Add-member noteproperty ProcessId $startmode.ProcessId
$Result += $tempObj
}
$Result
No need to combine the output of the two commands. All of the properties are already out from the Get-WMIObject. (Personally I like using the CIM cmdlets instead of WMI wherever possible too)
Get-CimInstance Win32_Service | select Name, DisplayName, State, StartMode, ProcessId
Edit: The output of State from gcim is the Status property of Get-Service (Here's a calculated property to fix that, if that's an issue)
Get-CimInstance Win32_Service | select Name, DisplayName, #{Name="Status";Expression={$_.State}}, StartMode, ProcessId
try this
$result=get-wmiobject -ComputerName localhost -Class win32_service | %{ New-Object psobject -Property #{ objectwmi=$_; objectgetservice=(get-service | where name -eq $_.Name | select -first 1)} }

combine object properties into one object in PowerShell

I am trying to combine multiple object properties into one object.
When I have the following code the objects properties are combined.
$computer = gwmi win32_computersystem | select numberOfProcessors, NumberOfLogicalProcessors, HypervisorPresent
$osInfo = gwmi win32_operatingsystem | select version, caption, serialnumber, osarchitecture
Foreach($p in Get-Member -InputObject $osInfo -MemberType NoteProperty)
{
Add-Member -InputObject $computer -MemberType NoteProperty -Name $p.Name -Value $osInfo.$($p.Name) -Force
}
$computer
However, if I replace the above computer and osInfo variables with
$computer = Get-Process | Select processname, path
$osInfo = Get-Service | Select name, status
then the $computer variables does not have the properties of the $osInfo variable after the for loop is executed. ie: the second object is not combined with the first object.
The original code deals with cmdlets that returns two single objects relating to the same source.
You're trying to use it with cmdlets that return arrays of multiple objects.
The following basically merges the two arrays.
$computer = 'Server01'
$collection = #()
$services = Get-Service | Select name, status
$processes = Get-Process | Select processname, path
foreach ($service in $services) {
$collection += [pscustomobject] #{
ServiceName = $service.name
ServiceStatus = $service.status
ProcessName = ""
ProcessPath = ""
}
}
foreach ($process in $processes) {
$collection += [pscustomobject] #{
ServiceName = ""
ServiceStatus = ""
ProcessName = $process.processname
ProcessPath = $process.path
}
}
$collection
Personally, I'd just use the two lines for $services and $processes and be done.
Your problem comes from the bad usage of Get_Member in the case of a collection.
Get-Member -InputObject ACollection gives the members of the collection.
ACollection | Get-Member gives the members of each element of the collection.
So in you case it will work with :
Foreach($p in ($osInfo | Get-Member -MemberType NoteProperty))
{
}
Edited
$computer is also à collection so what do you expect. I add the code of what I think you expect.
$processes = Get-Process | Select processname, path, Id
Foreach($p in $processes)
{
$services = Get-WmiObject "Win32_Service" -filter "ProcessId=$($p.Id)"
Add-Member -InputObject $p -MemberType NoteProperty -Name "Services" -Value $(($services | % {$_.name}) -join ',') -Force
}
$processes

Select-Object with output from 2 cmdlets

Suppose I have the following PowerShell script:
Get-WmiObject -Class Win32_Service |
Select DisplayName,#{Name="PID";Expression={$_.ProcessID}} |
Get-Process |
Select Name,CPU
This will:
Line 1: Get all services on the local machine
Line 2: Create a new object with the DisplayName and PID.
Line 3: Call Get-Process for information about each of the services.
Line 4: Create a new object with the Process Name and CPU usage.
However, in Line 4 I want to also have the DisplayName that I obtained in Line 2 - is this possible?
One way to do this is to output a custom object after collecting the properties you want. Example:
Get-WmiObject -Class Win32_Service | foreach-object {
$displayName = $_.DisplayName
$processID = $_.ProcessID
$process = Get-Process -Id $processID
new-object PSObject -property #{
"DisplayName" = $displayName
"Name" = $process.Name
"CPU" = $process.CPU
}
}
A couple of other ways to achieve this:
Add a note property to the object returned by Get-Process:
Get-WmiObject -Class Win32_Service |
Select DisplayName,#{Name="PID";Expression={$_.ProcessID}} |
% {
$displayName = $_.DisplayName;
$gp = Get-Process;
$gp | Add-Member -type NoteProperty -name DisplayName -value $displayName;
Write-Output $gp
} |
Select DisplayName, Name,CPU
Set a script scoped variable at one point in the pipeline, and use it at a later point in the pipeline:
Get-WmiObject -Class Win32_Service |
Select #{n='DisplayName';e={($script:displayName = $_.DisplayName)}},
#{Name="PID";Expression={$_.ProcessID}} |
Get-Process |
Select #{n='DisplayName';e={$script:displayName}}, Name,CPU
Using a pipelinevariable:
Get-CimInstance -ClassName Win32_Service -PipelineVariable service |
Select #{Name="PID";Expression={$_.ProcessID}} |
Get-Process |
Select Name,CPU,#{Name='DisplayName';Expression={$service.DisplayName}}

PowerShell - Select-Object from Win32_OperatingSystem displays rather oddly

First time poster here, I'm a bit of a beginner and I've been keen to get my PowerShell scripting skills up to scratch and I'm come across something rather confusing...
I've made a script to query a collection of computers and I want to query Win32_OperatingSystem but only extrapolate the Build number so I can populate my PSObject with it. I'm trying to add some If logic so that if the build number is 7601, I can write a message under my OS column.
The problem I'm having is that the BuildNumber values are coming out as #{BuildNumber=7601} instead of 7601 for instance. That, and my If statement is borked.
$Machines = Get-Content .\Computers.txt
Foreach($Machine in $Machines)
{
$sweet = (Get-WmiObject -Class Win32_OperatingSystem -computer $Machine | Select-Object BuildNumber)
$dversion = if ($sweet -eq "#{BuildNumber=7601}") {Yes!} else {"Nooooo!"}
New-Object PSObject -Property #{
ComputerName = $Machine
Sweet = $sweet
OS = $dversion
}
}
The issue is that the Get-WMIObject cmdlet is returning a Hash Table. Then the Select-Object is returning just the BuildNumber section you want, the BuildNumber property and it's value. You need to add the -ExpandProperty parameter to only get the value back, not the name/value pair.
Get-WMIObject -Class Win32_OperatingSystem | Select-Object BuildNumber
Returns
#{BuildNumber=7601}
With ExpandProperty
Get-WMIObject -Class Win32_OperatingSystem | Select-Object -ExpandProperty BuildNumber
Returns
7601
Just another option with a ping test to skip unavailable machines.
Get-Content .\Computers.txt | Where-Object {Test-Connection -ComputerName $_ -Count 1 -Quiet} | Foreach-Object {
$sweet = Get-WmiObject -Class Win32_OperatingSystem -ComputerName $_ | Select-Object -ExpandProperty BuildNumber
New-Object PSObject -Property #{
ComputerName = $_.__SERVER
Sweet = $sweet
OS = if ($sweet -eq 7601) {'Yes!'} else {'Nooooo!'}
}
}