powershellhashtable

Powershell hashtable add new element from template and modify uniquely


I have a hashtable below (which holds server related info) with a ‘template’ key value. I have a loop which duplicates the value and assigns with a new key

Then in the new key values I would modify the values with some unique data before deleting the template key value. But my problem is that I believe the key value reference of the the new key and template is the same

$test=@{
    "serverTest"=@{
        “hostname”=“abc”
        “services”=@{
            "serviceTest"=@{
                "filePath"="C:\hi";
                "group"=@("TEST" )
            }
        }
    }
}

$serverList = @{“server1”=“group1”; “server2”=“group2”;}

Foreach($server in $serverList.Keys){
    $test[$server]=$test.serverTest
    Foreach($service in $test.serviceTest.services){
        $test[$server].services[$service].group+= $serverList[$server]
    }
}
$test.remove(‘serviceTest’)

What I expect is two unique elements that can separately be modified, so output is:

$test=@{
    "server1"=@{
        “hostname”=“abc”
        “services”=@{
            "serviceTest"=@{
                "filePath"="C:\hi";
                "group"=@("TEST",”group1” )
            }
        }
    }
    "server2"=@{
        “hostname”=“abc”
        “services”=@{
            "serviceTest"=@{
                "filePath"="C:\hi";
                "group"=@("TEST",”group2” )
            }
        }
    }
}

But what I get is both elements get the same modifications:

$test=@{
    "server1"=@{
        “hostname”=“abc”
        “services”=@{
            "serviceTest"=@{
                "filePath"="C:\hi";
                "group"=@("TEST",”group1”,“group2”)
            }
        }
    }
    "server2"=@{
        “hostname”=“abc”
        “services”=@{
            "serviceTest"=@{
                "filePath"="C:\hi";
                "group"=@("TEST",”group1”,“group2”)
            }
        }
    }
}

Solution

  • What I expect is two unique elements

    Then you'll need to adjust your expectations - when you assign an existing reference-type object to a new entry in a hashtable, the new hashtable entry will reference (rather than copy) the existing object.

    Instead of attempting to "template" your hashtable ahead of time, just construct it at the callsite:

    $serverTable = @{}
    
    $serverList = @{
      server1 = "group1"
      server2 = "group2"
    }
    
    foreach ($serverName in $serverList.psbase.Keys){
        # construct hashtable to use as server entry value
        $serverTable[$serverName] = @{
            hostname = $serverName
            services = @{
                serviceTest= @{
                    filePath = "C:\hi"
                    group = @('TEST') + $serverList[$serverName]
                }
            }
        }
    }
    

    Notice that this isn't any more code than before - we've just moved the "template" into the loop body


    If you still insist on using a "template" table, simply wrap its construction in a function - this way you'll create a new one every time:

    function New-ScaffoldTable {
        return @{
            hostname = "abc"
            services = @{
                serviceTest = @{
                    filePath = "C:\hi"
                    group = @("TEST" )
                }
            }
        }
    }
    
    # ...
    
    foreach ($serverName in $serverList.psbase.Keys) {
        $serverTable[$serverName] = New-ScaffoldTable
        # continue modifying `$serverTable[$serverName]` as you see fit, it's unique ...
    }