PowerShell - extracting nested object - powershell

I am fairly new to PowerShell and am trying to use a module called PoshWAPI. This module is used to query Infoblox. One of the commands is Get-IBobject which allows me to query infoblox for a record type.
This is my code so far:
Set-IBConfig -ProfileName 'MyGrid' -WAPIHost dns.example.com -WAPIVersion latest -Credential (Get-Credential) -SkipCertificateCheck
$result = Get-IBObject -ObjectType record:host 'name=host1.network.example.com' -ReturnAll
That works well and returns the following
_ref : record:host/ZG5zLmhvc3QkLl9kZWZhdWx0LnRtY3MuaW5mb3NlYy5zMGE0OTFlMDAtMjMudDkwNC5zb25lc3N1czAx:host1.network.example.com/default
allow_telnet : False
comment : TOSD-37248
configure_for_dns : True
ddns_protected : False
disable : False
disable_discovery : False
dns_name : host1.network.example.com
extattrs :
ipv4addrs :{#{_ref=record:host_ipv4addr/ZG5zLmhvc3RfYWRkcmVzcyQuX2RlZmF1bHQudG1jcy5pbmZvc2VjLnMwYTQ5MWUwMC0yMy50OTA0LnNvbmVzc3VzMDEuMTAuNzMuMzAuMTYu:10.73.30.16/host1.network.example.com/default;
configure_for_dhcp=False; host=host1.network.example.com; ipv4addr=10.73.30.16}}
name : host1.network.example.com
network_view : default
rrset_order : cyclic
use_cli_credentials : False
use_snmp3_credential : False
use_snmp_credential : False
use_ttl : False
view : default
zone : network.example.com
The info I actually require from there are the name field and within the ipv4addrs field is a field called ipv4addr.
Currently I export it all to CSV using this command
$result | Export-Csv -Path C:\Users\neil.bloyce\Documents\2023\2.Feb\InfoBlox.csv
It isn't elegant but gets some of the info I want, however within the ipv4addrs I don't get any info except for System.Object[].
How do I adjust my code so that I can pull the ipv4addr out of ipv4addrs so I can see it in the CSV, Ideally the only info I actually need is dns_name and ipv4addr.
I am not sure how I attach the CSV file.
Kind regards

Use the Select-Object cmdlet to copy and transform only a subset of the properties from the object:
$result |Select-Object Name,#{Name='Addresses';Expression={ $_.ipv4addrs.ipv4addr -join ', ' }} |Export-Csv -Path C:\Users\neil.bloyce\Documents\2023\2.Feb\InfoBlox.csv -NoTypeInformation
Here, we extract the Name property from the original object, and then create a new calculated property that extracts the nested ipv4addr values and joins them together in a single string (thus causing Export-Csv to render the value correctly).

Related

Convert Single Line to Multiple Lines

I am new to this Powershell.
I am trying to learn how to modified output.
When I run "Write-output $result | format-list" I have the following output
userDetails : #{id=AA:BB:CC:DD:11:22; connectionStatus=CONNECTED; hostType=WIRELESS;
authType=WPA2/WPA3+802.1x/FT-802.1x}
connectedDevice : {#{deviceDetails=}}
How do I rewrite this output to below using powershell 7.2 ? I would like to have
userDetails :
connectionStatus= CONNECTED
hostType = WIRELESS
authType = WPA2/WPA3+802.1x/FT-802.1x
connectedDevice :
Thank you for your help.
Note: I'm assuming that you're looking for a friendlier display representation of your data. For programmatic processing, Format-* cmdlets should be avoided, for the reasons explained in this answer.
What you're looking for is for Format-List to work recursively, i.e. to not only list the individual properties and their values for each input object itself, but also for nested objects contained in property values.
Format-List does not support this:
Nested objects are represented by their single-line .ToString() representations.
If they're part of a collection (enumerable), the individual elements' representations are joined with , on a single line, and are enclosed in {...}(!) as a whole. How many elements are shown at most is controlled by the $FormatEnumerationLimit preference variable, which defaults to 4.
However, you can approximate recursive listing behavior with Format-Custom; using a simplified example:
# Nested sample object to format.
[pscustomobject]#{
userDetails = [pscustomobject] #{
id = 'AA:BB:CC:DD:11:22'
connectionStatus= 'CONNECTED'
hostType = 'WIRELESS'
authType = 'WPA2/WPA3+802.1x/FT-802.1x'
}
connectedDevice = '...'
} |
Format-Custom -Depth 1 # use higher -Depth levels for multi-level expansion
Output:
class PSCustomObject
{
userDetails =
[
class PSCustomObject
{
id = AA:BB:CC:DD:11:22
connectionStatus = CONNECTED
hostType = WIRELESS
authType = WPA2/WPA3+802.1x/FT-802.1x
}
]
connectedDevice = ...
}
Note:
Caveat: If a custom view happens to be defined for a given input object's type via associated formatting data, it is that custom view that Format-Custom will invoke, not the structural representation shown above; however, this is rare ([datetime] is a rare example).
Apart from the output showing the structure recursively, the format differs from that of Format-List as follows:
Complex objects are enclosed in class <typeName> { ... }
Elements of collections (enumerables) each render on their own (group of) line(s), enclosed in [ ... ] overall. However, as with Format-List, the number of elements that are shown at most is limited by $FormatEnumerationLimit.
To prevent excessively nested output, Format-Custom stops recursing at a depth of 5 by default; you can control the recursion depth via the -Depth parameter, 1 meaning that only objects in immediate child properties are expanded.
When the recursion depth limit is reached, non-collection objects are represented by their .ToString() representations, as with Format-List.
Here is some code that produces output close to your desired output:
# Create sample data
$result = [pscustomobject] #{
userDetails = [pscustomobject]#{ id="AA:BB:CC:DD:11:22"; connectionStatus="CONNECTED"; hostType="WIRELESS"; authType="WPA2/WPA3+802.1x/FT-802.1x"}
connectedDevice = [pscustomobject]#{ deviceDetails=$null }
}
# Produce output
"userDetails :"
($result.userDetails |
Format-List -Property connectionStatus, hostType, authType |
Out-String).Trim() -replace '(?m)(?<=^[^:]+):', '='
"`nconnectedDevice :"
# TODO: add similar code as for .userDetails
Output:
userDetails :
connectionStatus = CONNECTED
hostType = WIRELESS
authType = WPA2/WPA3+802.1x/FT-802.1x
connectedDevice :
Using member access .userDetails to select a child object (similar to Select-Object -ExpandProperty userDetails).
Using Format-List -Property to output a list of the given properties
Using Out-String to create a string from the formatting data that is produced by Format-List. This string looks exactly like the output you normally see on the console.
Use String method .Trim() to remove whitespace (in this case newlines) from the beginning and end.
Use the -replace operator to replace the first : of each line by =. See this regex101 demo for more information.

ConvertFrom-Json is giving System.Object[] for some sections in Powershell

I am very new to Windows PowerShell. I am trying to fire ConvertFrom-Json command for the below Json :-
$ret=[{"ID":"ABC","type":"Test","code":"AD","enabled":true,"sourceMappings":[{"source":"Test","values":[{"code":"AD","value":"Anderson","enabled":true,"canonicalValue":true,"downStreamDefaultValue":true}]}],"startDate":0,"endDate":0,"updatedBy":"YY","updateDate":1590085877449,"version":4}]
and I am getting below output :-
ID : ABC
type : Test
code : AD
enabled : True
sourceMappings : {#{source=Test; values=System.Object[]}}
startDate : 0
endDate : 0
updatedBy : YY
updateDate : 1590085877449
version : 4
If you notice the values been assigned to System.Object[].Please help me how to get the original values in the output.
I think the converting is all fine.
You can access the values via:
$tmp = $ret | ConvertFrom-Json
$tmp.sourcemappings
$tmp.sourcemappings.values.code
and so on
Lets first make your json file more readable:
{
"ID":"ABC",
"type":"Test",
"code":"AD",
"enabled":true,
"sourceMappings":[{"source":"Test","values":[{"code":"AD","value":"Anderson","enabled":true,"canonicalValue":true,"downStreamDefaultValue":true}]}],
"startDate":0,
"endDate":0,
"updatedBy":"YY",
"updateDate":1590085877449,
"version":4
}
Second of all, your way of defining json variable is incorrect. In powershell, you need to define json variables is surrounding the json content with #" and "#. The correct way to set $ret equal to your json file would be like this:
$ret=#"
{
"ID":"ABC",
"type":"Test",
"code":"AD",
"enabled":true,
"sourceMappings":[{"source":"Test","values":[{"code":"AD","value":"Anderson","enabled":true,"canonicalValue":true,"downStreamDefaultValue":true}]}],
"startDate":0,
"endDate":0,
"updatedBy":"YY",
"updateDate":1590085877449,
"version":4
}
"#
Where $ret would be considered a string data type. You could then convert the json with:
$var = ConvertFrom-Json -InputObject $ret
Where you could now interact with it like normal. For example, $var.ID would output ABC and if you tried checking the object type again with $var.gettype() it would output:
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True False PSCustomObject System.Object
Where the type would be a PSCustomObject
Update: It seems as if I misunderstood the question, my bad!
The reason it says
sourceMappings : {#{source=Test; values=System.Object[]}}
Is because powershell doesn't want to display the whole value since there are many components in the value. You can view the values in the sourceMappings class with $var.sourceMappings where the output would be:
source values
------ ------
Test {#{code=AD; value=Anderson; enabled=True; canonicalValue=True; downStreamDefaultValue=True}}
and you can keep on viewing deeper if you add on like $var.sourceMappings.code etc. etc.

Powershell command $table.Columns.Add("NewFieldName") results in unexpected output to console

The following PowerShell script results in unexpected, and in my case unwanted, output to the console:
$table = New-Object System.Data.DataTable
$table.Columns.Add("NewFieldName")
Unexpected output:
AllowDBNull : True
AutoIncrement : False
AutoIncrementSeed : 0
AutoIncrementStep : 1
Caption : NewFieldName
ColumnName : NewFieldName
Prefix :
DataType : System.String
DateTimeMode : UnspecifiedLocal
DefaultValue :
Expression :
ExtendedProperties : {}
MaxLength : -1
Namespace :
Ordinal : 0
ReadOnly : False
Table : {}
Unique : False
ColumnMapping : Element
Site :
Container :
DesignMode : False
Expected output:
 
My questions are:
Why?
How can I prevent this output from going out to the console with the rest of my output?
TLDR: Add [void] like this
[void]$table.Columns.Add("NewFieldName")
Why? Because PowerShell tends to output the command's return value/object to the console (or whatever is receiving the output) by default.
How can I prevent this output from going out to the console with the rest of my output? Thank you to #PetSerAl for pointing out the solution of adding [void] to the left of the command. This casts the command's return type to a [void] which will not output to the console.
After #PetSerAl pointed me in the right direction I found these pages that helped clear things up:
Into the [void]
Gotcha #10 in PowerShell Gotchas

How to prevent Powershell not showing script processing data

I have created a powershell script for updating SharePoint List. When i run it shows all the backend processing to the console for example all the list schema info. I want it only show script created Out put (The only output with Write-Host) and not out of the box backend processing. Is there any command available to prevent that?
it shows counting like below when I use .add method of ArrayList where ever in script
0
1
2
3
and it shows List XML Schema when i store List items in some variable in the script like below:
Sealed : False
Version : 28
DisplayFormTemplateName : DocumentLibraryForm
EditFormTemplateName : DocumentLibraryForm
NewFormTemplateName : DocumentLibraryForm
NewFormUrl :
MobileNewFormUrl :
EditFormUrl :
MobileEditFormUrl :
DisplayFormUrl :
MobileDisplayFormUrl :
Id : 0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF39009893A4DD0A05487AAE05EAE8D183333C003933922476541344B8A065CBACE8178D
ReadOnly : False
Name : NewsPage
NameResource : Microsoft.SharePoint.SPUserResource
FeatureId : 00000000-0000-0000-0000-000000000000
Description : Used to create news articles.
JSLink :
DescriptionResource : Microsoft.SharePoint.SPUserResource
Hidden : False
Some .Net Methods and external programs output their exit code and other output to stdout.
You can either add the void keyword to the arraylist "Add" command or pipe it to Out-Null
$MyList = [System.Collections.ArrayList]#()
1..10 | % { $MyList.Add($_) } # outputs 0 to 9
1..10 | % { [void]$MyList.Add($_) } # no output
1..10 | % { $MyList.Add($_) | Out-Null } # no output

Need a powershell script to use a parameter to manipulate/read in fields from another csv file

I have and existing PowerShell script (not written by me!) which is designed to use a passed-in parameter (SCOM Alert ID) to then set additional information in the alert, e.g. $alert.CustomField1 = $alert.PrincipleName etc etc.
I am looking to add functionality to this script to be able to add additional 'custom' information which is stored in a separate text/CSV file. The text/CSV file has header line
ServerName,ServiceOwner,Application Tier
so a row in the file would be
MYSERVER.CONTOSO.COM,Joe Bloggs, Tier 1
There will be one unique row for each server in our environment (over 600 rows).
So what I need to do is using the passed-in alert ID. I can use $alert.PrincipleName to find the corresponding row in the text file and pull in the additional details stored in field 2 and 3, i.e. ServiceOwner and ApplicationTier.
This logic holds for the majority of alerts but IF server name is a specific value (e.g. MYSTSERVER.CONTOSO.COM) then instead of using $alert.PrincipleName to match servername in text file, I need to match on another alert property $alert.MonitoringObjectDisplayName. However, the server name in this field is part of a larger string in the format, e.g. User Services Watcher for Pool [MYTARGETSERVER.CONTOSO.COM] - so I need to extract the severname from between the square brackets of the string to then perform the match with the text file.
Hopefully I have explained what I'm trying to do clearly - if not I'm happy to provide further clarification and can also post up the existing PS Script I'm trying to modify if thats any help.
You can react to the server name like this:
$csv = Import-Csv 'C:\path\to\your.csv'
...
if ( $alert.PrincipalName -eq 'MYSTSERVER.CONTOSO.COM' ) {
if ( $alert.MonitoringObjectDisplayName -match '.*\[(.*?)\]' ) {
$targetserver = $matches[1]
}
} else {
$targetserver = $alert.PrincipalName
}
$newdata = $csv | ? { $_.ServerName -eq $targetserver } `
| select ServiceOwner, 'Application Tier'
...
$alert.CustomField2 = $newdata.ServiceOwner
$alert.CustomField3 = $newdata.'Application Tier'
...