I've the following PSObj with some properties stored in an $array :
ComputerName : MyComputer
Time : 08/11/2022 13:57:53
DetectionFile : MyBadFile.exe
ThreatName : WS.Reputation.1
Action : 12
I'm trying to replace the action ID number by it's corresponding description. I've a hashtable with the possibles reasons behind the Action ID
$ActionId = @{
0 = 'Unknown'
1 = 'Blocked'
2 = 'Allowed'
3 = 'No Action'
4 = 'Logged'
5 = 'Command Script Run'
6 = 'Corrected'
7 = 'Partially Corrected'
8 = 'Uncorrected'
10 = 'Delayed Requires reboot to finish the operation.'
11 = 'Deleted'
12 = 'Quarantined'
13 = 'Restored'
14 = 'Detected'
15 = 'Exonerated No longer suspicious (re-scored).'
16 = 'Tagged Marked with extended attributes.'
}
I'm trying to parse each item of this array, and each value of the reason ID to replace the ID by the reason string
# parse array
foreach ($Item in $array) {
# parse possible values
foreach ($value in $ActionId) {
if ($value -eq $item.Action) {
$Item.Action = $ActionId[$value]
$Item.Action
}
}
From my understanding, I'm missing the correct syntax here
$Item.Action = $ActionId[$value]
I do not get any errors, but from the debugger, I'm replacing the action property by $null with the above...
The immediate fix is to loop over the keys (.Keys
) of your $ActionId
hashtable:
foreach ($Item in $array) {
# parse possible values
foreach ($value in $ActionId.Keys) {
if ($value -eq $item.Action) {
$Item.Action = $ActionId[$value]
$Item.Action # diagnostic output
}
}
}
Note:
To avoid confusion, consider renaming $value
to $key
.
Generally, note that hashtables are not enumerated in the pipeline / in looping constructs in PowerShell.
That is, foreach ($value in $ActionId) ...
doesn't actually loop over the hashtable's entries, and is the same as $value = $ActionID
)
If you want to enumerate a hashtable's entries - as key-value pairs of type System.RuntimeType
- you would need to use the .GetEnumerator()
method; in your case, however, enumerating the keys is sufficient.
However, the simpler and more efficient solution is to test whether the $Item.Action
value exists as a key in your hashtable, using the latter's .Contains()
method:[1]
foreach ($Item in $array) {
if ($ActionId.Contains($Item.Action)) {
$Item.Action = $ActionId[$Item.Action]
$Item.Action # diagnostic output
}
}
You can further streamline this as follows, though it is conceptually a bit obscure:
foreach ($Item in $array) {
if ($null -ne ($value = $ActionId[$Item.Action])) {
$Item.Action = $value
$Item.Action # diagnostic output
}
}
=
is only ever PowerShell's assignment operator; for equality / non-equality comparison, -eq
/ -ne
is required.
Here, an assignment to $value
is indeed being performed and the assigned value then acts as the RHS of the -ne
operation; in other words: you can use assignment as expressions in PowerShell.
If hashtable $ActionId
has no key with value $Item.Action
, $ActionId[$Item.Action]
quietly returns $null
.
Finally - in PowerShell (Core) 7+ only - an even more concise (though not necessarily faster) solution is possible, using ??
, the null-coalescing operator:
foreach ($Item in $array) {
$Item.Action = $ActionId[$Item.Action] ?? $Item.Action
$Item.Action # diagnostic output
}
That is, the value of $ActionId[$Item.Action]
is only used if it isn't $null
; otherwise, $Item.Action
, i.e. the current value, is used (which is effectively a no-op).
[1] .ContainsKey()
works too, and while this name is conceptually clearer than .Contains()
, it is unfortunately not supported by PowerShell's [ordered]
hashtables (System.Collections.Specialized.OrderedDictionary
) and, generally speaking, not supported by other dictionary (hashtable-like types), given that the System.Collections.IDictionary
interface only has .Contains()