powershellvariablespsobject

PowerShell, unexpected different results depending upon what is removed from a JSON structure


Hoping that someone might be able to explain why the first removal does not affect the first variable, but the second removal does affect the first variable, please. Thank you.

    $X = @'
    {
      "$schema": "myschema",
      "parameters": {
        "avSetName": {
          "type": "string"
        },
        "faultDomains": {
          "type": "int",
          "defaultValue": 2
        }
      },
      "variables": {
        "templateRef": "singleavset v1",
        "osProfile9": "blahblah"
      },
      "resources": [
        {
          "apiVersion": "2019-07-01",
          "type": "Microsoft.Compute/availabilitySets",
          "name": "[parameters('avSetName')]",
          "location": "[resourceGroup().location]"
        }
      ]
    }
    '@
 
 
    # this affects only $B...
    $A = ConvertFrom-Json -InputObject $X
    $B = $A.PSObject.Copy()
    $A
    $B
    $B.PSObject.Properties.Remove( "variables" )
    $A
    $B
 
 
    # this affects $A and $B...
    $A = ConvertFrom-Json -InputObject $X
    $B = $A.PSObject.Copy()
    $A
    $B
    $B.Variables.PSObject.Properties.Remove( "osprofile9" )
    $A
    $B

When loading $B, then if I simply use $B = $A then I get why $A would be affected, because PowerShell is I believe using a reference pointer behind the scenes. But, if I use $B = $A.PSObject.Copy() then I would not expect $A to be affected.


Solution

  • The .Copy() method of type [pscustomobject] creates a shallow clone (copy) of a given instance rather than a deep clone (and the type fundamentally doesn't offer deep copying).

    Thus, any nested [pscustomobject] instances in your input object - by virtue of being instances of .NET reference types - are not copied; specifically, it is a reference to them that gets copied, which means that both the input object and its copy point to (reference) the very same nested instance. See this answer for background information.

    Specifically, this relates to your modifications as follows:

    Since your data source is ultimately JSON, the simplest way to create two fully independent [pscustomobject] instances is to call ConvertFrom-Json twice, and capture each call in a separate variable:

    $A = ConvertFrom-Json $X
    $B = ConvertFrom-Json $X