Custom Object in Pipeline Function

Hi All,

Hope you can help as I'm totally stuck on why my function is working for some input and not another.

 

$DebugPreference = "SilentlyContinue"

Function Get-InventoryInfo
{
	BEGIN 
	{
	}
	
	PROCESS 
	{
	$obj = $null
	Write-Debug "Process begun. Pipeline is $_ and obj = $obj"
		$obj = New-Object psobject
		$obj | Add-Member noteproperty ComputerName ($_)
		$compsystem = gwmi win32_computersystem -computerName $_ -ErrorAction SilentlyContinue 
		Write-Debug "first WMI call. Compsystem is $compsystem" 
			if (-not $compsystem){$obj | Add-Member noteproperty WMIWorking $False;Write-Debug "No WMI. obj is $obj"}
				else
				{
				$obj | Add-Member noteproperty WMIWorking $True
				$obj | Add-Member noteproperty Architecture ($compsystem.systemtype)
				Write-Debug "Yes WMI. obj is $obj"
				}
	Write-Debug "About to output obj = $obj"
	write-output $obj
	Write-Debug "After output obj = $obj"
	}
	END 
	{
	}
}


"localhost", "nohost" | get-inventoryinfo 
#"nohost", "localhost"| get-inventoryinfo 

Open in new window


What I'm trying to do here is pass to this function a list of hostnames on the pipeline and it will return me an object for each hostname that I can use to pass through to the next element on the pipeline. I'm finding that it works exactly as I want it to if the first host is available. I then get:

ComputerName                               WMIWorking Architecture
------------                               ---------- ------------
localhost                                        True x64-based PC
nohost                                          False

But if I send the non-working host it will not then show me the Architecture field.

ComputerName                                                         WMIWorking
------------                                                         ----------
nohost                                                                    False
localhost                                                                  True

I get the feeling that if I were to add the members to the object first (perhaps in the Begin block, and then populate if available that might work? I'm not sure how to create the properties of the object first and then populate if available. I find if I try

Add-Member noteproperty Architecture

It doesn't allow me to because it expects an input?

Thanks for any help,

Shaun

LVL 27
shauncroucherAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Chris DentPowerShell DeveloperCommented:
Hi Shaun,

I would be tempted to build the object with its members first:


$Object = New-Object PSObject -Property @{ ComputerName = ""; WmiWorking = $False; Architecture = "" }


Then I would modify properties on that object as we go through. e.g.


$Object.ComputerName = $_
$compsystem = gwmi win32_computersystem -computerName $_ -ErrorAction SilentlyContinue
If ($compsystem) {
  $Object.WmiWorking = $True
  $Object.Architecture = $compsystem.systemtype
}


You don't have to set the $False for WmiWorking because we set that as the default when we created an instance of the object.

Finally, if you want to dictate the order the properties return in, use this in place of Write-Output:


$Object | Select-Object ComputerName, WmiWorking, Architecture


HTH

Chris

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
shauncroucherAuthor Commented:
Hi Chris,

Hope you are well.

Thanks for the advice on the script, works perfectly!!

Cheers
Shaun

shauncroucherAuthor Commented:
Hi Chris,

I've just had another look at your script and what is interesting is that if I use your method of piping the final object to the select statement to return the results,  I can then populate the results into an array variable no trouble.

If I use my method of write-output, when I try to catch the results to an array it writes every element of the array and populates it with the result of the last object processed.

So, for example on the example above:

If I use the write-output method and simple allow the Out-Default ("nohost", "localhost"| get-inventoryinfo )  to display the results on screen I will get:

ComputerName                                                         WMIWorking
------------                                                         ----------
nohost                                                                    False
localhost                                                                  True


But if I try to catch that into an array variable ($results  = "nohost", "localhost"| get-inventoryinfo)

I will get :

ComputerName                                                         WMIWorking
------------                                                         ----------
localhost                                                                  True
localhost                                                                  True

Using your method of selecting the objects the results are correctly stored in the array (the same as they are displayed on screen).

Do you know why it is behaving this way?

Thanks

Shaun
OWASP: Forgery and Phishing

Learn the techniques to avoid forgery and phishing attacks and the types of attacks an application or network may face.

Chris DentPowerShell DeveloperCommented:

Yeah, it must have a new instance of the object created each time you pass through the loop. It looks like you're getting a single instance of the object and changing its value in each pass. You end up with an array full of the same object.

For instance, you should be able to do:

$Results[0].ComputerName = "Bob"

And you should find every single entry changes to reflect that.

Would you post the problematic code?

Chris
shauncroucherAuthor Commented:
Hi Chris

I tried array[0].ComputerName = "Bob" and it does indeed change all elements in the array.

I've attached the two methods I use, and the results are as below:

# Write-output

ComputerName                                                    Model                                                        
------------                                                    -----                                                        
2312d                                                        OptiPlex GX620                                                
2312d                                                        OptiPlex GX620                                                
2312d                                                        OptiPlex GX620                                                
2312d                                                        OptiPlex GX620  

# Select-object

ComputerName                                                    Model                                                        
------------                                                    -----                                                        
2549d                                                        OptiPlex 760                                                  
0002v                                                        Virtual Machine                                              
2447d                                                        OptiPlex 755                                                  
2312d                                                        OptiPlex GX620                                                


I think it is related to the $compsystem variable which gets changed on each iteration, but what is odd is that when outputting to the console, the write-output method shows the correct results, it only fails when directing output to the variable $host.

How come the select-object method works ok?

Thanks Chris,

Shaun


# Write-output method

function Get-computername
{
	begin 
	{
		$Obj = New-Object PSObject -Property @{ ComputerName = "";Model="Nothin"}
	}
	process 
	{
		$compsystem = gwmi win32_computersystem -computerName $($_) 
		$Obj.computername = $_; $obj.model = $compsystem.model;write-output $obj
	}
}


$hosts = "2549d", "0002v", "2447d", "2312d" | get-computername
$hosts 



# Select-Object method
function Get-computername
{
	begin 
	{
		$Obj = New-Object PSObject -Property @{ ComputerName = "";Model="Nothin"}
	}
	process 
	{
		$compsystem = gwmi win32_computersystem -computerName $($_) 
		$Obj.computername = $_; $obj.model = $compsystem.model;$obj | select ComputerName,Model
	}
}


$hosts = "2549d", "0002v", "2447d", "2312d" | get-computername
$hosts

Open in new window

Chris DentPowerShell DeveloperCommented:
There we go :)

You need $Obj in the Process block, not Begin. If you have it in Begin and do this:

MultipleInputObjects | Get-ComputerName

It will run Begin once, then Process for each input element.

The Select-Object version is creating a brand new object using the current input object as the source.

Chris
# Write-output method

function Get-computername
{
	process 
	{
		$Obj = New-Object PSObject -Property @{ ComputerName = "";Model="Nothin"}
		$compsystem = gwmi win32_computersystem -computerName $($_) 
		$Obj.computername = $_; $obj.model = $compsystem.model;write-output $obj
	}
}

Open in new window

shauncroucherAuthor Commented:
Ahhh, I see, that makes sense now, I didn't appreciate that the select statement will create a new object each time it runs.

Thanks very much Chris,

Shaun
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
.NET Programming

From novice to tech pro — start learning today.