2

I have a PSCustomObject which has list of sub-objects like this:

vmssSystemUpdatesMonitoringEffect                                   : @{type=String; metadata=; allowedValues=System.Object[]; defaultValue=AuditIfNotExists}
vmssEndpointProtectionMonitoringEffect                              : @{type=String; metadata=; allowedValues=System.Object[]; defaultValue=AuditIfNotExists}
vmssOsVulnerabilitiesMonitoringEffect                               : @{type=String; metadata=; allowedValues=System.Object[]; defaultValue=AuditIfNotExists}
systemUpdatesMonitoringEffect                                       : @{type=String; metadata=; allowedValues=System.Object[]; defaultValue=AuditIfNotExists}
systemConfigurationsMonitoringEffect                                : @{type=String; metadata=; allowedValues=System.Object[]; defaultValue=AuditIfNotExists}

etc.

Part of the object as JSON:

{
  "vmssSystemUpdatesMonitoringEffect": {
    "type": "String",
    "metadata": {
      "displayName": "System updates on virtual machine scale sets should be installed",
      "description": "Enable or disable virtual machine scale sets reporting of system updates"
    },
    "allowedValues": [
      "AuditIfNotExists",
      "Disabled"
    ],
    "defaultValue": "AuditIfNotExists"
  },
  "vmssEndpointProtectionMonitoringEffect": {
    "type": "String",
    "metadata": {
      "displayName": "Endpoint protection solution should be installed on virtual machine scale sets",
      "description": "Enable or disable virtual machine scale sets endpoint protection monitoring"
    },
    "allowedValues": [
      "AuditIfNotExists",
      "Disabled"
    ],
    "defaultValue": "AuditIfNotExists"
  },
  "vmssOsVulnerabilitiesMonitoringEffect": {
    "type": "String",
    "metadata": {
      "displayName": "Vulnerabilities in security configuration on your virtual machine scale sets should be remediated",
      "description": "Enable or disable virtual machine scale sets OS vulnerabilities monitoring"
    },
    "allowedValues": [
      "AuditIfNotExists",
      "Disabled"
    ],
    "defaultValue": "AuditIfNotExists"
  }
}

Keys I get to array with

$Keys = $Hash | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name

I can get the keys into array and iterate them but I cannot access properties by giving the key with variable:

foreach ($key in $Keys) {
        Write-Host "key" $key
        $data = $KeyValue.$key  
}

Result: key aadAuthenticationInServiceFabricMonitoringEffect

and data empty

However, this works:

$KeyValue.vmssSystemUpdatesMonitoringEffect

And this:

$key= "aadAuthenticationInServiceFabricMonitoringEffect"
$KeyValue.$key

How could I get this working with variable?

js2010
  • 23,033
  • 6
  • 64
  • 66
Kamsiinov
  • 1,315
  • 2
  • 20
  • 50
  • If i look at `$json.vmssEndpointProtectionMonitoringEffect.psobject.Properties.name` after importing your json, it work... If you do a `.gettype()` on `aadAuthenticationInServiceFabricMonitoringEffect`, what's the object type ? (since the one you have problems with is not in the json sample) – Sage Pourpre Feb 06 '20 at 16:28
  • Object type is PSCustomObject – Kamsiinov Feb 06 '20 at 17:09
  • I'm afraid it is not possible to diagnose until a reproducible sample is provided. PSCustomObject will return it's member. Maybe try `Get-Member` to see if it's properties could be somewhat not of type `NoteProperty`. Also make sure that the path to `aadAuthenticationInServiceFabricMonitoringEffect` is good as if you do `$Keyvalue.aadAuthenticationInServiceFabricMonitoringEffect` and the actual path is something else, for instance : `$Keyvalue.Me.aadAuthenticationInServiceFabricMonitoringEffect` then that would explain why you do not get data. – Sage Pourpre Feb 06 '20 at 17:21
  • if you happen to have Azure Az-module installed then you can run: $Policyset = Get-AzPolicySetDefinition -Name 1f3afdf9-d0c9-4c3d-847f-89da613e70a8 $policyHash = $Policyset.Properties.parameters – Kamsiinov Feb 06 '20 at 17:56
  • See my edited answer. That is the same principle I exposed in my original answer but I used the exact commands you provided and it is working. Let me know if I misunderstood what you were trying to do or if it work, mark it as accepted :) – Sage Pourpre Feb 06 '20 at 18:11
  • My goal is to iterate through all of those parameters that you have in policyHash variable so that I can get defaultvalues, displaynames etc. – Kamsiinov Feb 06 '20 at 18:35
  • See the "additional note" section I added. There's another answer out there specifically for that bit that will allow you to go through nest psobject in a loop. – Sage Pourpre Feb 06 '20 at 19:39
  • What is $keyvalue? – js2010 Feb 06 '20 at 20:59

3 Answers3

4

To iterate over properties of a PSObject, you need to loop through the properties using $YourObject.psobject.Properties.Name

See the example below, which is based off the information you provided.

$Policyset = Get-AzPolicySetDefinition
$Policyset = Get-AzPolicySetDefinition -Name 1f3afdf9-d0c9-4c3d-847f-89da613e70a8 
$policyHash = $Policyset.Properties.parameters

$DataSet = $policyHash.aadAuthenticationInServiceFabricMonitoringEffect
$Keys = $DataSet.psobject.Properties.name

foreach ($key in $Keys) {
    Write-Host $Key -ForegroundColor Cyan
    Write-Host $DataSet.$key
}

Result

Result

Additional note Since you added you wanted to iterate over nested properties, look at the answer provided here. iterate-over-psobject-properties-in-powershell. Pay attention to the bit about infinite looping due to reference to parent object since this does apply in your case.

Sage Pourpre
  • 9,932
  • 3
  • 27
  • 39
  • ```$data = $KeyValue.$key``` did you mean ```$item.$key``` ? – Mike L'Angelo Feb 06 '20 at 14:32
  • @MikeL'Angelo Absolutely. I got distracted. Thanks for the catch :) – Sage Pourpre Feb 06 '20 at 14:39
  • This is how I would think it works. However, as I wrote on my question this does not work for me for some reason. – Kamsiinov Feb 06 '20 at 16:09
  • @Kamsiinov Can you take your object, use `Convert-to-Json` on it and add that to your question (after redacting anything that shouldn't be seen by other values) ? It will be easy to provide an accurate answer if we see the actual object structure and can convert it back to an object. – Sage Pourpre Feb 06 '20 at 16:16
  • I have added is a JSON. This object looks normal to me but it does not behave like a normal object as it is PSCustomObject – Kamsiinov Feb 06 '20 at 16:20
2

Assuming you have an object that looks similar to this:

$KeyValue = @{
vmssSystemUpdatesMonitoringEffect = @{
    type='String'; 
    metadata=''; 
    allowedValues=@(1,2,3); 
    defaultValue='AuditIfNotExists'}
}

We essentially have a key-value pair in which the top level contains just one key, vmssSystemUpdatesMonitoringEffect, with a value of its own nested hashtable.

We can parse through this pretty easily by first looking for and .Keys in the hashtable, and then foreach of them, looking for any .Keys there and getting their values.

$KeyValue = @{vmssSystemUpdatesMonitoringEffect = @{type='String'; metadata=''; allowedValues=@(1,2,3); defaultValue='AuditIfNotExists'}}
foreach($key in $KeyValue.Keys){
    $nestedKeys = $KeyValue.$key.Keys
    "parsing node $key in `$KeyValue` which has $($nestedKeys.Count) nested keys"

    foreach($nestedkey in $nestedKeys){
        "--parsing nested key $nestedKey"
        "--$($KeyValue.$key.$nestedKey)"
        }
}

Which will give us an output of:

parsing node vmssSystemUpdatesMonitoringEffect in $KeyValue which has 4 nested keys
--parsing nested key defaultValue
--AuditIfNotExists
--parsing nested key allowedValues
--1 2 3
--parsing nested key type
--String
--parsing nested key metadata
--

This should get you started down the path you're interested in.

If you have a PSCustomObject which contains a hashtable

First of all, I am so, so sorry that you're ending up in this pain.

Secondly, you must make use of two techniques to enumerate the nodes of your unholy bastard object if you're in this case.

$KeyValue = [pscustomobject]@{vmssSystemUpdatesMonitoringEffect = @{type='String'; metadata=''; allowedValues=@(1,2,3); defaultValue='AuditIfNotExists'}}
$keys = get-member -InputObject $keyvalue -MemberType NoteProperty 
foreach($key in $keys){
    $nestedKeys = $KeyValue.$($key.Name).Keys
    "parsing node $($key.Name) in `$KeyValue` which has $($nestedKeys.Count) nested keys"

    foreach($nestedkey in $nestedKeys){
        "--parsing nested key $nestedKey"
        "--$($KeyValue.$($key.Name).$nestedKey)"
        }
}

The big difference is that we have to retrieve all of the keys using the Get-Member cmdlet, and specifying that we want to retrieve the members with a NoteProperty type. This gives us all of the properties of the CustomObject which will we will then step through, looking for hashtables with properties.

The next set of weirdness comes with this line $nestedKeys = $KeyValue.$($key.Name).Keys which use's PowerShell's subExpression operator to run run the item within $( )symbols and treat the output as a string. It's akin to running $KeyValue.vmssSystemUpdatesMonitoringEffect.Keys.

Beyond that, the syntax is largely the same.

FoxDeploy
  • 12,569
  • 2
  • 33
  • 48
2

In your example, wouldn't it be:

$hash = get-content file.json | convertfrom-json
$Keys = $Hash | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name
$data = foreach ($key in $Keys) {
  $hash.$key  
}
$data


type   metadata                     allowedValues
----   --------                     -------------
String @{displayName=Endpoint prote {AuditIfNotExists, Disabled}
String @{displayName=Vulnerabilitie {AuditIfNotExists, Disabled}
String @{displayName=System updates {AuditIfNotExists, Disabled}

To me, if the object is hard to work with, the design is bad. I prefer it this way, as an array of 3 similar objects:

[
    {
        "header":  "vmssSystemUpdatesMonitoringEffect",
        "type":  "String",
        "metadata":  {
                         "displayName":  "System updates on virtual machine scale sets should be installed",
                         "description":  "Enable or disable virtual machine scale sets reporting of system updates"
                     },
        "allowedValues":  [
                              "AuditIfNotExists",
                              "Disabled"
                          ],
        "defaultValue":  "AuditIfNotExists"
    },
    {
        "header":  "vmssEndpointProtectionMonitoringEffect",
        "type":  "String",
        "metadata":  {
                         "displayName":  "Endpoint protection solution should be installed on virtual machine scale sets",
                         "description":  "Enable or disable virtual machine scale sets endpoint protection monitoring"
                     },
        "allowedValues":  [
                              "AuditIfNotExists",
                              "Disabled"
                          ],
        "defaultValue":  "AuditIfNotExists"
    },
    {
        "header":  "vmssOsVulnerabilitiesMonitoringEffect",
        "type":  "String",
        "metadata":  {
                         "displayName":  "Vulnerabilities in security configuration on your virtual machine scale sets should be remediated",
                         "description":  "Enable or disable virtual machine scale sets OS vulnerabilities monitoring"
                     },
        "allowedValues":  [
                              "AuditIfNotExists",
                              "Disabled"
                          ],
        "defaultValue":  "AuditIfNotExists"
    }
]

Then:

cat file.json | convertfrom-json

header        : vmssSystemUpdatesMonitoringEffect
type          : String
metadata      : @{displayName=System updates on virtual machine scale sets should be installed; description=Enable or disable virtual machine scale sets reporting of system updates}
allowedValues : {AuditIfNotExists, Disabled}
defaultValue  : AuditIfNotExists

header        : vmssEndpointProtectionMonitoringEffect
type          : String
metadata      : @{displayName=Endpoint protection solution should be installed on virtual machine scale sets; description=Enable or disable virtual machine scale sets endpoint
                protection monitoring}
allowedValues : {AuditIfNotExists, Disabled}
defaultValue  : AuditIfNotExists

header        : vmssOsVulnerabilitiesMonitoringEffect
type          : String
metadata      : @{displayName=Vulnerabilities in security configuration on your virtual machine scale sets should be remediated; description=Enable or disable virtual machine scale
                sets OS vulnerabilities monitoring}
allowedValues : {AuditIfNotExists, Disabled}
defaultValue  : AuditIfNotExists
js2010
  • 23,033
  • 6
  • 64
  • 66
  • Yes it would be like this. Initially I get the object as PSCustomObject. Then I would need to convert it to JSON, put it into disk, read content from there and read it back to PSObject. Instead I would like to skip the JSON stuff and work directly with PS. – Kamsiinov Feb 07 '20 at 05:33