combine object properties into one object in PowerShell - 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

Related

Search Print queues using powershell

I'm working on a quick string of code that allows me to search a specific print server and also its contents such as printers. What I want for it to do is once getting the printer information to be able to find a specific printer using the port name which in this case is an IP address not quite sure if is possible but I haven't found a command that lets me define a search using those values.
Get-Printer -computerName "server01"|select-object -Property Name, PortName
Name PortName
Printer01 X.XX.XXX.X
Prnter02 X.XX.XX.XX
is there a way to be able to find a printer using the Get-Printer commandlet and utilizing the port name to find the specific printer?
I'm just explaining in more detail #JeffZeitlin very correct answer.
Get-Printer - The Get-Printer cmdlet retrieves a list of printers installed on a computer. You can also use Get-Printer to retrieve the properties of a single printer, and then use that information as input into other cmdlets.
Get-Printer | Get-Member - The Get-Member cmdlet gets the members, the properties and methods, of objects.
Get-Printer | get-member
Get-Printer | Select-Object - The Select-Object cmdlet selects specified properties of an object or set of objects. It can also select unique objects, a specified number of objects, or objects in a specified position in an array.
Get-Printer | select-object -Property Name, PortName
Get-Printer | Select-Object | Where-Object - The Where-Object cmdlet selects objects that have particular property values from the collection of objects that are passed to it. For example, you can use the Where-Object cmdlet to select files that were created after a certain date, events with a particular ID, or computers that use a particular version of Windows.
Get-Printer | select-object -Property Name, PortName | where PortName -eq "PORTPROMPT:"
Get-Printer -ComputerName
Get-Printer -ComputerName $env:Computername | select-object -Property Name, PortName | where PortName -eq "PORTPROMPT:"
Scripting Example Get-Printer
$server = "$env:Computername"
$printersportnames = Get-Printer -ComputerName $server | select-object -ExpandProperty PortName
#Write-Host $printersportnames
$results = New-Object System.Collections.ArrayList # Empty Array
ForEach ($printerportname in $printersportnames) {
$printerportname = $printerportname.ToString()
#Write-Host $printerportname
$output = Get-Printer -ComputerName $server | where-object {$_.PortName -eq $printerportname}
$x = $output.Name
$y = $output.PortName
$z = $output.CommunicationStatus
$a = $output.OperatingStatus
$Object = New-Object PSObject
$Object | Add-Member -Name 'Name' -MemberType Noteproperty -Value $x
$Object | Add-Member -Name 'PortName' -MemberType Noteproperty -Value $y
$Object | Add-Member -Name 'CommStatus' -MemberType Noteproperty -Value $z
$Object | Add-Member -Name 'OperStatus' -MemberType Noteproperty -Value $a
$results += $object
}
$results

Fetching domain users from local groups with powershell

I want to migrate from one server to another one and because of that it is needed to add some local groups in the new server. In these local groups the users added belong to the domain.
Ex.:
Server | Members
---------------------------|------------------
Server\Group1 | Domain\User1, Domain\User2
Server\Group2 | Domain\User2, Domain\User3
The following link https://www.petri.com/use-powershell-to-find-local-groups-and-members seems to resolve this, but I am getting an unexpected result
This is the PowerShell script
# set variables
$server = $env:COMPUTERNAME
$localgroup = "Administrators"
$Group= [ADSI]"WinNT://$Server/$LocalGroup,group"
# get users name
$members = $Group.psbase.Invoke("Members")
$members | ForEach-Object
{
$_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null)
}
Get-WMIObject win32_group -filter "LocalAccount='True'" -computername $Server | Select PSComputername,Name,#{Name="Members";Expression={$_.GetRelated("Win32_UserAccount").Name -join ";"}}
The shown output is a two columns(although it should be 3, but PSComputerName is not being displayed where the Members column is empty)
Well, this is how I achieved the output and also exported it to a *.csv file
# set variables
$server = $env:COMPUTERNAME
$tableOutput = New-Object System.Collections.ArrayList
# get members
Function Get-Members($groupName){
$testgroup = [ADSI]"WinNT://$Server/$tmpGroupName,group"
$members = New-Object System.Collections.ArrayList
$testgroup.psbase.Invoke("Members") | ForEach-Object{
$searchFilter = $_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null) -replace "\."," "
$tmpUser = Get-ADUser -Filter {(Name -like $searchFilter) -or (SamAccountName -like $searchFilter)}
if($tmpUser){
[void]$members.Add($tmpUser.UserPrincipalName)
}
}
$members
}
Get-WMIObject win32_group -Filter { (LocalAccount='True') } -computername $Server | ForEach-Object{
$tmpGroup = $_
# get data
$tmpGroupName = $tmpGroup.Name
$members = Get-Members($tmpGroupName)
$tmpGroupDescription = $tmpGroup.Description
# save into object
$groupObject = New-Object -TypeName PSObject
$groupObject | Add-Member -MemberType NoteProperty -Name GroupName -Value $tmpGroupName
$groupObject | Add-Member -MemberType NoteProperty -Name GroupDescription -Value $tmpGroupDescription
$groupObject | Add-Member -MemberType NoteProperty -Name UsersList -Value $members
[void]$tableOutput.Add($groupObject)
}
$tableOutput | Select GroupName, GroupDescription, #{Name='Users';Expression={$_.UsersList -join ','}} | Export-CSV -Path 'C:\test\users.csv' -Delimiter ';' -NoTypeInformation
Any correction would be appreciated.

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)} }

Combining output of many objects into a CSV

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

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}}