1

I'm trying to parse the results of PowerShell's Get-NetIPConfiguration in Python.

The results contain the values I want in the default formatting (Format-List), but not when converted to JSON, which is the format I would like to consume.

Note how DNSServer is serialized by Format-List:

PS C:\Users\BoppreH> Get-NetIPConfiguration | Format-List
InterfaceAlias       : VirtualBox Host-Only Network
InterfaceIndex       : 23
InterfaceDescription : VirtualBox Host-Only Ethernet Adapter
IPv4Address          : 192.168.56.1
IPv6DefaultGateway   :
IPv4DefaultGateway   :
DNSServer            : fec0:0:0:ffff::1
                       fec0:0:0:ffff::2
                       fec0:0:0:ffff::3

[...]

while ConvertTo-Json -Depth 1 serializes the DNSServer attribute differently (in a completely useless way):

PS C:\Users\BoppreH> Get-NetIPConfiguration | ConvertTo-Json -Depth 1
[
    {
        "Detailed":  false,
        "ComputerName":  "BOPPREH-DESKTOP",
        "InterfaceAlias":  "VirtualBox Host-Only Network",
        "InterfaceIndex":  23,
        "InterfaceDescription":  "VirtualBox Host-Only Ethernet Adapter",
        "CompartmentId":  1,
        "NetAdapter":  "MSFT_NetAdapter (CreationClassName = \"MSFT_NetAdapter\", DeviceID = \"{EAF79493-7C78-44D2-ADB4-F3EF196D2F49}\", SystemCreationClassName = \"CIM_NetworkPort\", SystemName = \"boppreh-desktop\")",
        "NetCompartment":  "MSFT_NetCompartment (InstanceID = \";55;\")",
        "NetIPv6Interface":  "MSFT_NetIPInterface (Name = \"??55??55;\", CreationClassName = \"\", SystemCreationClassName = \"\", SystemName = \"\")",
        "NetIPv4Interface":  "MSFT_NetIPInterface (Name = \"??55?55;\", CreationClassName = \"\", SystemCreationClassName = \"\", SystemName = \"\")",
        "NetProfile":  null,
        "AllIPAddresses":  "192.168.56.1 fe80::d83f:9609:86ff:2b57%23",
        "IPv6Address":  "",
        "IPv6TemporaryAddress":  "",
        "IPv6LinkLocalAddress":  "fe80::d83f:9609:86ff:2b57%23",
        "IPv4Address":  "192.168.56.1",
        "IPv6DefaultGateway":  null,
        "IPv4DefaultGateway":  null,
        "DNSServer":  "MSFT_DNSClientServerAddress (Name = \"23\", CreationClassName = \"\", SystemCreationClassName = \"\", SystemName = \"23\") MSFT_DNSClientServerAddress (Name = \"23\", CreationClassName = \"\", SystemCreationClassName = \"\", SystemName = \"2\")"
    },
[...]

It's not until depth level 4 that the addresses become visible, but by then the output is several times larger and much harder to navigate.

My current alternative is to pipe the results in Select-Object and use calculated properties to convert the values myself (in the case of DNSServer it's $_.DNSServer.ServerAddresses -join " "), but this is cumbersome to do for each property and makes other properties serialize differently too.

How can I force the JSON serializer to format values like the list formatter?

BoppreH
  • 8,014
  • 4
  • 34
  • 71
  • This serialization problem, coupled with a 2.5 second delay when invoking PowerShell from Python, and lack of network mask information, made me switch to manually and shamefully parsing `ipconfig /all`. – BoppreH Nov 17 '21 at 12:37

1 Answers1

3

Note how DNSServer is serialized by Format-List

The Format-* cmdlets do not serialize, they produce for-display string representations, using PowerShell's output-formatting system (as opposed to its serialization infrastructure).

These representations are not meant to be used for programmatic processing, but if you do want to process them as strings, you can pipe them to Out-String:

# Returns a single-line string; add -Stream to get an array of lines.
# Add -Width to explicitly specify a line width (console window width is the default).
# Note: Since Format-List is used for formatting *by default*, 
#       you don't strictly need the Format-List here.
#       Alternatively, use Format-Table for a *table* representation
$stringRep = Get-NetIPConfiguration | Format-List | Out-String

How can I force the JSON serializer to format values like the list formatter?

Your only option is indeed to simplify the object graph by constructing your own [pscustomobject] instances, such as via Select-Object and calculated properties, or in a ForEach-Object loop with [pscustomobject] literals (e.g.
[pscustomobject] @{ foo = 'bar'; baz = 'quux' }).

For instance:

Get-NetIPConfiguration | ForEach-Object {
  [pscustomobject] @{
    InterfaceAlias = $_.InterfaceAlias
    InterfaceIndex = $_.InterfaceIndex
    InterfaceDescription = $_.InterfaceDescription
    'NetProfile.Name' = $_.NetProfile.Name
    IPv4Address = $_.IPv4Address -join "`n"
    IPv6DefaultGateway = $_.IPv6DefaultGateway.NextHop
    IPv4DefaultGateway = $_.IPv4DefaultGateway.NextHop
  }
}

Piping the above to ConvertTo-Json gives a reasonable representation.

Do note that Get-NetIPConfiguration's formatting data has additional logic built in that varies the output fields in its for-display representations by adapter type, which the above doesn't take into account.

mklement0
  • 382,024
  • 64
  • 607
  • 775
  • Unfortunately this requires custom expressions for each and every member, such as `$_.IPv4DefaultGateway.NextHop` for the default gateway address and `$_.DNSServer.ServerAddresses -join " "` for the DNS servers. Isn't there a way to format the member the same way it would be in a table view? I tried `Out-String` and `.ToString()` and they all gave different (and useless) representations. – BoppreH Nov 17 '21 at 09:12
  • @BoppreH, if you want a table view, pipe to `Format-Table | Out-String` - please see my update. Generally, `Out-String` gives you the same representation as in the console as a multi-line string - or, with `-Stream` - as an array of lines. You can use it with or without a preceding explicit `Format-*` cmdlet call. – mklement0 Nov 17 '21 at 15:08
  • 1
    By "table/list view" I meant the way that individual *fields* are formatted in a table, not the table itself as a big string. I was basically looking for the same data that `Format-List` gave me, but in JSON. Anyway, you did most of the hard work in your ``ForEach-Object` example, so I'm accepting the answer. Thank you. – BoppreH Nov 17 '21 at 17:06