Given your fixed structure of nested JSON arrays, convert them to [pscustomobject]
instances as follows:
# Note: Note how the whole ConvertFrom-Json call
# must be enclosed in (...) in *Windows PowerShell*.
(ConvertFrom-Json (
aws ec2 --profile $profile --region $region describe-instances --query "Reservations[*].Instances[*].[InstanceId,LaunchTime,State]"
)) |
ForEach-Object {
# Construct and implicitly output a [pscustomobject]
# instance from the elements of each nested array.
[pscustomobject] @{
instanceid = $_[0][0]
launchdateb = [datetimeoffset] $_[0][1]
code = $_[0][2].Code
name = $_[0][2].Name
}
}
Note:
Enclosing the ConvertFrom-Json
call as a whole in (...)
ensures that the array resulting from the parsed JSON is enumerated in the pipeline, meaning that its elements are sent to ForEach-Object
, one by one.
This is only necessary in Windows PowerShell, where ConvertFrom-Json
unexpectedly outputs arrays as a whole; this has been corrected in PowerShell (Core) 7+, so you don't strictly need the (...)
enclosure there - see this answer for details.
The above directly outputs the resulting [pscustomobject]
instances and produces display output similar to the one you request in the question; to capture these objects in a variable, as an array, prepend something like $objects =
to the entire command.
Note that casting the .launchdateb
property values to [datetimeoffset]
or [datetime]
is only needed in Windows PowerShell; in PowerShell (Core) 7+, JSON string property values containing ISO 8601 timestamp strings are automatically converted to - invariably local - [datetime]
instances by ConvertFrom-Json
.
The PowerShell 7+ behavior - up to at least v7.3.6 - is somewhat unfortunate, in that the automatic conversion to a local [datetime]
(System.DateTime
) involves the loss of the specific UTC offset (+00:00
in your data) in the JSON data (which a subsequent [datetimeoffset]
cannot recover).
GitHub issue #13598 is a proposal to add a -DateKind
parameter to ConvertFrom-Json
, which would enable -DateKind Offset
, causing [datetimeoffset]
(System.DateTimeOffset
) instances that preserve the UTC offset to be returned. The proposal has been green-lit, but is yet to be implemented as of this writing.
Storing [datetimeoffset]
/ [datetime]
instances in the .launchdateb
properties should make it easy to perform date comparisons.
To spell out how to capture each [pscustomobject]
in a variable and operate on it inside the ForEach-Object
script block, providing the JSON input via a here-string:
(@'
[
[
[
"i-0123456789",
"2023-07-17T04:00:18+00:00",
{
"Code": 8,
"Name": "running"
}
]
],
[
[
"i-9876543210",
"2023-07-17T04:00:16+00:00",
{
"Code": 16,
"Name": "running"
}
]
]
]
'@ | ConvertFrom-Json) |
ForEach-Object {
$obj =
[pscustomobject] @{
instanceid = $_[0][0]
launchdateb = [datetimeoffset] $_[0][1]
code = $_[0][2].Code
name = $_[0][2].Name
}
# Output a string representation of the captured object.
"Object: $obj"
}
The above yields (note the hashtable-like representation of [pscustomobject]
s in the context of string interpolation (inside "..."
, an expandable (double-quoted) string)):
Object: @{instanceid=i-0123456789; launchdateb=07/17/2023 04:00:18 +00:00; code=8; name=running}
Object: @{instanceid=i-9876543210; launchdateb=07/17/2023 04:00:16 +00:00; code=16; name=running}
Note:
- Do not use
"Object: " + $obj.ToString()
in lieu of "Object: $obj"
above: due to a long-standing bug - see GitHub issue #6163 - calling .ToString()
on [pscustomobject]
instances yields the empty string, up to at least PowerShell v7.3.6; by contrast, string interpolation ("$obj"
) is not affected.