I need to get the value of an environment variable from a kubernetes pod. I have my values listed in a hash table.
I call
$hash["service-testurl"].spec.template.spec.containers.env
And it returns a table:
name value
---- -----
ADDR https://test.com
TOKEN 123456789
CERT_PATH public-certs/test
ENVIRONMENT dev
I need to get https://test.com into a variable in my ps1 script, but i'm not sure how to get this value. (consider that for each deployment the url will be different, like abc.com, def.com, ghj.com... so i can't filter by the name test.com)
I was looking for something like $hash["service-testurl"].spec.template.spec.containers.env.name["ADDR"].value
Running $hash["service-testurl"].spec.template.spec.containers.env.PSTypeNames
returns
System.Object[]
System.Array
System.Object
CodePudding user response:
I was able to do it with something similar that @iRon proposed:
$hash["service-testurl"].spec.template.spec.containers.env.where{$_.name -eq 'ADDR'}.value
Thanks!
CodePudding user response:
To complement your own effective solution:
Even though your display output of $hash["service-testurl"].spec.template.spec.containers.env
looks like the representation of a (single) hashtable, the value is actually:
(a) an array, as your diagnostic output with
.pstypenames
demonstrates,(b) whose elements are (presumably)
[pscustomobject]
instances that each have a a.name
and a.value
property (an easy way to tell is that the display output's column headers arename
andvalue
, whereas with hashtables they would beName
andValue
).
Leaving aside that the identifier ADDR
is a property value rather than a property / key name in your case, you fundamentally cannot use key-based index notation (['ADDR']
) on an array - that generally only works on a (single) hashtable (or, more generally, dictionary).[1]
In your case, you need to find the array element whose .name
property value is 'ADDR'
, which then allows you to return its .value
property value.
For collections already in memory, the intrinsic .Where()
method (as used in your own solution) is a more efficient - and more flexible - alternative to filtering a collection via the Where-Object
cmdlet.
It will often not matter in practice, but you can optimize a .Where()
call to stop filtering once the first match has been found, if you expect or are only interested in one match:
$hash["service-testurl"].spec.template.spec.containers.env.Where(
{ $_.name -eq 'ADDR' },
'First'
).value
Note that .Where()
always returns an array-like collection, even if only a single value is matched - see this answer for details. As such, the .value
property access is attempted on that collection, which, however, still works as intended, courtesy of the PowerShell feature known as member-access enumeration.
Note how using (...)
around the arguments is now a syntactic necessity.
While with only a single argument - the filter script block ({ ... }
) - you can get away with not using (...)
- .Where{ $_.name -eq 'ADDR' }
as shorthand for .Where({ $_.name -eq 'ADDR' })
- omitting the (...)
is problematic for two reasons:
Given that the
Where-Object
cmdlet can also be referred to asWhere
(via a built-in alias), the two command forms could be confused, and given thatWhere-Object
allows and is typically used with a space separating the command name from its script-block argument (e.g,1..3 | Where { $_ -eq 2 }
, it is tempting to also try to use a space with the.Where()
method, which does not work:# !! BROKEN, due to space before "{" (1..3).Where { $_ -eq 2 }
If you add another argument later, you need to remember to use
(...)
[1] The fact that key-based index notation does not work with member-access enumeration, i.e. doesn't work on an array of hashtables (only dot notation does, which PowerShell supports for hashtables too) could be considered an inconsistency; e.g. @( @{ foo=1 } ).foo
works (dot notation), but @( @{ foo=1 } )['foo']
does not, due to the array wrapper.
However, this inconsistency was declared to be by design - see GitHub issue #17514.