Split two objects from one - powershell

I've imported an objects thats actually two different objects to a single variable:
PS> $object | gm
TypeName: Deserialized.System.Management.Automation.PSCustomObject
...
TypeName: System.Security.Cryptography.X509Certificates.X509Certificate2
...
I can only access information from the first object. Is there a way to split this into two variables based on TypeName?

PowerShell supports destructuring / parallel assignments, officially known as multiple assignment.
If you know the order of the objects contained in collection $object:
$custObj, $cert = $object # $custObj receives $object[0], $cert the rest.
$custObj will receive the 1st object contained in $object, and $cert the rest - which in the case of a 2-element collection is the 2nd element (as a scalar; if the collection had more elements, $cert would receive an array ([object[]])).
Otherwise, in PowerShell v4+, you can use the .Where() collection method to split a collection in two based on a condition:
$cert, $custObj = $objects.Where(
{ $_ -is [System.Security.Cryptography.X509Certificates.X509Certificate2] },
'Split'
)

From this question I assume you know the types in advance, but you do not necessarily know the order they will appear in the array $object.
The code below will extract the items of each known type from th list:
$customObject = $object | ? { $_.GetType().Name -like "*PSCustomObject" }
$certficate = $object | ? { $_.GetType().Name -like "*X509Certificate2" }

Related

Export data in the order it was added - PowerShell Export-Csv

I have this code
function get-data()
{
$rec=[PSCustomObject]#()
$DLGP = "" | Select "Name","Grade","Score"
foreach($record in $data)
{
$DLGP.Name=$record.name
$DLGP.Grade=$record.grade
$DLGP.Score=$record.score
$rec += $DLGP
}
return $rec
}
$mydata=get-data
$mydata | Export-Csv -Path $outputPath -NoTypeInformation
The problem is, the data is not exported in the order that I have added it to $rec
How can I get it exported in the order it was added?
Without even using your function, a simple
$mydata = $data | select Name,Grade,Score
would yield the desired result.
Your function is IMO overcomplicated:
$Data = #"
Name,Grade,Score,Dummy
Anthoni,A,10,v
Brandon,B,20,x
Christian,C,30,y
David,D,40,z
"# | ConvertFrom-Csv
function get-data() {
ForEach($record in $data) {
[PSCustomObject]#{
Name =$record.name
Grade=$record.grade
Score=$record.score
}
}
}
$mydata=get-data
$mydata # | Export-Csv -Path $outputPath -NoTypeInformation
Returns here the pretty same order:
Name Grade Score
---- ----- -----
Anthoni A 10
Brandon B 20
Christian C 30
David D 40
LotPings' helpful answer provides effective solutions.
As for what you tried:
By constructing only a single [pscustomobject] instance outside the loop:
$DLGP = "" | Select "Name","Grade","Score"
and then only updating that one instance's properties in each loop iteration:
$DLGP.Name=$record.name
# ....
you effectively added the very same [pscustomobject] instance multiple times to the result array instead of creating a distinct object in each iteration.
Since the same object was being updated repeatedly, that object ended up having the properties of the last object in the input collection, $data.
As an aside:
[PSCustomObject] #() is effectively the same as #(): the [PSCustomObject] is ignored and you get an [object[]] array.
To type the array as one containing [PSCustomObject] instances, you'd have to use an array-typed cast: [PSCustomObject[]] #().
However, given that instances of any type can be cast to [PSCustomObject] - which is really the same as [psobject] - this offers no type safety and no performance benefit.
Also, since your $rec variable isn't type-constrained (it is defined as $rec = [<type>] ... rather than [<type>] $rec = ...) and you're using += to "add to" the array (which invariably requires creation of a new instance behind the scenes), $rec would revert to an [object[]] array after the first += operation.

Listing all user properties but excluding whitespace/empty fields in Powershell [duplicate]

How do I get properties that ONLY have populated values?
So for example if I run
Get-QADUser -Identity "SomeOne" -IncludeAllProperties
the output would of course include.. all properties, including those with and those without values. I want a listing of properties with values only. How is it done generally speaking?
This wouldn't be restricted to Quest Cmdlets, I only use Get-QADUser as an example.
You could try using the built-in (hidden) property of PowerShell objects called PSObject, which includes a property called Properties, i.e. a list of all properties on the parent object.
Maybe easier with an example. Take Get-Process... a process can have many attributes (properties) with or without values. In order to get just the ones with values you do this:
(Get-Process | Select -First 1).PSObject.Properties | ?{$_.Value -ne $null} | FT Name,Value
Note that I limited this to just the first process returned by Get-Process. We then get all the properties defined on that object, filtering where Value is not null and then displaying just the Name and Value for those properties.
To complement Charlie Joynt's helpful answer:
Below is convenience function Remove-NullProperties, which creates custom-object copies of its input objects populated with only the non-$null properties of the input objects.
Example use:
# Sample input collection, with 2 objects with different $null-valued
# properties.
$coll = [pscustomobject] #{ one = 'r1c1'; two = $null; three = 'r1c3' },
[pscustomobject] #{ one = 'r2c1'; two = 'r2c2'; three = $null }
# Output copies containing only non-$null-valued properties.
# NOTE: The `ForEach-Object { Out-String -InputObject $_ }` part is solely
# there to ensure that *all* resulting properties are shown.
# With the default output, only the properties found on the FIRST
# input object would be used in the output table.
$coll | Remove-NullProperties |
ForEach-Object { Out-String -InputObject $_ }
This yields the following - note how the respective null-valued properties were removed:
one three
--- -----
r1c1 r1c3
one two
--- ---
r2c1 r2c2
Remove-NullProperties source code:
<#
.SYNOPSIS
Removes properties with $null values from custom-object copies of
the input objects.
.DESCRIPTION
Note that output objects are custom objects that are copies of the input
objects with copies of only those input-object properties that are not $null.
CAVEAT: If you pipe multiple objects to this function, and these objects
differ in what properties are non-$null-valued, the default output
format will show only the non-$null-valued properties of the FIRST object.
Use ... | ForEach-Object { Out-String -InputObject $_ } to avoid
this problem.
.NOTES
Since the output objects are generally of a distinct type - [pscustomobject] -
and have only NoteProperty members, use of this function only makes sense
with plain-old data objects as input.
.EXAMPLE
> [pscustomobject] #{ one = 1; two = $null; three = 3 } | Remove-NullProperties
one three
--- -----
1 3
#>
function Remove-NullProperties {
param(
[parameter(Mandatory,ValueFromPipeline)]
[psobject] $InputObject
)
process {
# Create the initially empty output object
$obj = [pscustomobject]::new()
# Loop over all input-object properties.
foreach($prop in $InputObject.psobject.properties) {
# If a property is non-$null, add it to the output object.
if ($null -ne $InputObject.$($prop.Name)) {
Add-Member -InputObject $obj -NotePropertyName $prop.Name -NotePropertyValue $prop.Value
}
}
# Give the output object a type name that reflects the type of the input
# object prefixed with 'NonNull.' - note that this is purely informational, unless
# you define a custom output format for this type name.
$obj.pstypenames.Insert(0, 'NonNull.' + $InputObject.GetType().FullName)
# Output the output object.
$obj
}
}
These answers didn't work for me in the case of importing an object from an Infoblox csv file. Some values were the empty string, but not null. Testing whether a property is true or not, seems to work better for me. And the result is an object.
$a = [pscustomobject]#{one='hi';two='';three='there'}
$prop = $a.psobject.Properties | where value | foreach name
$a | select $prop
one three
--- -----
hi there
You first get its properties (since Get-QADUser depends on AD schema, the properties list is dynamic) with get-member -type property, then filter out those that don't have \{.*(get).*\} in its definition (that is, they are not "gettable"), then enumerate the resultant list by name and filter out nulls.
$someone=Get-QADUser -Identity "SomeOne" -IncludeAllProperties
$members=$someone|get-member -type property| where {$_.definition -match '\{.*(get).*\}'}
foreach ($member in $members) {
if ($someone[$member.name] -ne $null) {
write-host $member.name $someone[$member.name]
}
}

How do I get properties that ONLY have populated values?

How do I get properties that ONLY have populated values?
So for example if I run
Get-QADUser -Identity "SomeOne" -IncludeAllProperties
the output would of course include.. all properties, including those with and those without values. I want a listing of properties with values only. How is it done generally speaking?
This wouldn't be restricted to Quest Cmdlets, I only use Get-QADUser as an example.
You could try using the built-in (hidden) property of PowerShell objects called PSObject, which includes a property called Properties, i.e. a list of all properties on the parent object.
Maybe easier with an example. Take Get-Process... a process can have many attributes (properties) with or without values. In order to get just the ones with values you do this:
(Get-Process | Select -First 1).PSObject.Properties | ?{$_.Value -ne $null} | FT Name,Value
Note that I limited this to just the first process returned by Get-Process. We then get all the properties defined on that object, filtering where Value is not null and then displaying just the Name and Value for those properties.
To complement Charlie Joynt's helpful answer:
Below is convenience function Remove-NullProperties, which creates custom-object copies of its input objects populated with only the non-$null properties of the input objects.
Example use:
# Sample input collection, with 2 objects with different $null-valued
# properties.
$coll = [pscustomobject] #{ one = 'r1c1'; two = $null; three = 'r1c3' },
[pscustomobject] #{ one = 'r2c1'; two = 'r2c2'; three = $null }
# Output copies containing only non-$null-valued properties.
# NOTE: The `ForEach-Object { Out-String -InputObject $_ }` part is solely
# there to ensure that *all* resulting properties are shown.
# With the default output, only the properties found on the FIRST
# input object would be used in the output table.
$coll | Remove-NullProperties |
ForEach-Object { Out-String -InputObject $_ }
This yields the following - note how the respective null-valued properties were removed:
one three
--- -----
r1c1 r1c3
one two
--- ---
r2c1 r2c2
Remove-NullProperties source code:
<#
.SYNOPSIS
Removes properties with $null values from custom-object copies of
the input objects.
.DESCRIPTION
Note that output objects are custom objects that are copies of the input
objects with copies of only those input-object properties that are not $null.
CAVEAT: If you pipe multiple objects to this function, and these objects
differ in what properties are non-$null-valued, the default output
format will show only the non-$null-valued properties of the FIRST object.
Use ... | ForEach-Object { Out-String -InputObject $_ } to avoid
this problem.
.NOTES
Since the output objects are generally of a distinct type - [pscustomobject] -
and have only NoteProperty members, use of this function only makes sense
with plain-old data objects as input.
.EXAMPLE
> [pscustomobject] #{ one = 1; two = $null; three = 3 } | Remove-NullProperties
one three
--- -----
1 3
#>
function Remove-NullProperties {
param(
[parameter(Mandatory,ValueFromPipeline)]
[psobject] $InputObject
)
process {
# Create the initially empty output object
$obj = [pscustomobject]::new()
# Loop over all input-object properties.
foreach($prop in $InputObject.psobject.properties) {
# If a property is non-$null, add it to the output object.
if ($null -ne $InputObject.$($prop.Name)) {
Add-Member -InputObject $obj -NotePropertyName $prop.Name -NotePropertyValue $prop.Value
}
}
# Give the output object a type name that reflects the type of the input
# object prefixed with 'NonNull.' - note that this is purely informational, unless
# you define a custom output format for this type name.
$obj.pstypenames.Insert(0, 'NonNull.' + $InputObject.GetType().FullName)
# Output the output object.
$obj
}
}
These answers didn't work for me in the case of importing an object from an Infoblox csv file. Some values were the empty string, but not null. Testing whether a property is true or not, seems to work better for me. And the result is an object.
$a = [pscustomobject]#{one='hi';two='';three='there'}
$prop = $a.psobject.Properties | where value | foreach name
$a | select $prop
one three
--- -----
hi there
You first get its properties (since Get-QADUser depends on AD schema, the properties list is dynamic) with get-member -type property, then filter out those that don't have \{.*(get).*\} in its definition (that is, they are not "gettable"), then enumerate the resultant list by name and filter out nulls.
$someone=Get-QADUser -Identity "SomeOne" -IncludeAllProperties
$members=$someone|get-member -type property| where {$_.definition -match '\{.*(get).*\}'}
foreach ($member in $members) {
if ($someone[$member.name] -ne $null) {
write-host $member.name $someone[$member.name]
}
}

Hashtable vs array of custom objects regarding iteration

I frequently write lists of things and perform enumeration against them to perform some get/set.
I hate enumerating hashtables, since whenever I have to do it, I have to bend my mind backwards to work with hashtable objects.
$hashtablelistofitems = #{}
$hashtablelistofitems.add("i'm a key", "i'm a value")
foreach ($item in $hashtablelistofitems.keys) {
$item
$hashtablelistofitems.item($item)
}
Instead, I usually revert to using a single dimensional array of a custom object with two noteproperties.
$array = #()
$listofitems = "" | select key,value
$listofitems.key = "i'm a key"
$listofitems.value = "i'm a value"
$array += $listofitems
foreach ($item in $listofitems) {
$item.key
$item.value
}
Why should I be using a hashtable over this method? Simply because it only guarantees a single value per key?
You should use a hashtable if you want to store a key value list and not create an array containing a custom object with two properties (key / values) for mainly two reasons:
You might want to pass your hashtable to a function that expect a hashtable.
Hashtable is a built-in PowerShell type which users are aware of. Your second approach is harder to read / maintain for other users.
Note:
You can iterate over a hashtable almost the same way as your approach by calling the GetEnumerator() function:
foreach ($item in $listofitems.GetEnumerator()) {
$item.key
$item.value
}
Also, the hashtable comes with handy methods that you might want to use:
#{} | Get-Member | Where-Object MemberType -eq Method | Select Name
Output:
Name
----
Add
Clear
Clone
Contains
ContainsKey
ContainsValue
CopyTo
Equals
GetEnumerator
GetHashCode
GetObjectData
GetType
OnDeserialization
Remove
ToString
This is nowhere near as useful as Martin's compendium, but it's useful enough.
It's an MSDN article about how to convert back and forth between Hashtables and PSCustomObjects. The article

PowerShell update nested value

I have the following output from a PowerShell command and want to update the value for EmployeeID
I can filter the output with $test.identifiers | where {$_.name -like "EmployeeID" }
But if I try to update the value with
$test.identifiers | where {$_.name -like "EmployeeID" } | foreach {$_.values.value = "098324"}
I get an error
How can I update this nested value?
$_.values contains an array (or collection) objects, which explains why you can get (read) the .value property, but not set (write) it (see below).
If you expect the array to have just one element, simply use [0] to access that element directly:
$test.identifiers | where {$_.name -like "EmployeeID" } | foreach {
$_.values[0].value = '098324'
}
If there are multiple elements, use
$_.values | foreach { $_.value = '098324' } to assign to them all, or, alternatively in PSv4+,
$_.values.ForEach('value', '098324')
In PSv3+ a feature called member-access enumeration allows you to access a property on a collection and have the property values from the individual elements returned as an array.
However, that only works for getting properties, not for setting them.
When you try to set, only the collection's own properties are considered, which explains the error you saw - an array itself has no .value property.
While this asymmetry is by design, to avoid potentially unwanted bulk modification, the error message could certainly be more helpful.
Simple reproduction of the problem:
Create an object with property one containing a single-element array with another object, with property two:
$obj = [pscustomobject] #{ one = , [pscustomobject] #{ two = 2 } }
The default output looks as follows:
PS> $obj
one
---
{#{two=2}}
The outer {...} indicate an array, as in your case, and what's inside is a
hashtable-like notation that PowerShell uses to represent custom objects.
Getting the nested-inside-an-array object's two property works as intended:
PS> $obj.two
2
Trying to set it fails:
PS> $obj.two = 2.1
The property 'two' cannot be found on this object. Verify that the property exists and can be set.
To set, use .ForEach(), for instance:
PS> $obj.ForEach('two', 2.1); $obj
one
---
{#{two=2.1}}
Have you tried it this way with the full object path:
$test.identifiers | where {$_.name -like "EmployeeID" } | foreach {$_.identifiers.values.value = "098324"}