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?

8
  • 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) Commented Feb 6, 2020 at 16:28
  • Object type is PSCustomObject Commented Feb 6, 2020 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. Commented Feb 6, 2020 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 Commented Feb 6, 2020 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 :) Commented Feb 6, 2020 at 18:11

3 Answers 3

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.

Sign up to request clarification or add additional context in comments.

5 Comments

$data = $KeyValue.$key did you mean $item.$key ?
@MikeL'Angelo Absolutely. I got distracted. Thanks for the catch :)
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 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.
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
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.

2 Comments

This object is a PSCustomObject so it does not seem to work as normal keyValue pair.
I updated with an example of doing this as a PowerShell object.
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

1 Comment

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.