arrayspowershellpscustomobject

Create an array of pscustomobjects which include a hasthable for export


I create a custom object

$ToExport = [pscustomobject]@{
           "samAccountName" = ''
           "forwarded" = @{
                "ForwardTo" = ""
                "RedirectTo" = ""
                "Enabled" = ""
                "Description" = ""
           }
}  

The i parse through mailboxes and their forwarding rules, fill the properties and try to put the object into an array:

$Testarray = @()

foreach($m in $mbx){
    $ToExport.samAccountName = $m.SamAccountName    
    $rule = Get-InboxRule -Mailbox $m.SamAccountName
    foreach($r in $rule){
        if(($r.ForwardTo) -OR ($r.RedirectTo)){
            if($r.ForwardTo -like "*smtp*" -and ($AcceptedDomains | %{"$($r.ForwardTo)" -like "*$_*"}) -notcontains $true){
                $ToExport.forwarded.ForwardTo += $r.ForwardTo}
            if($r.RedirectTo -like "*smtp*" -and ($AcceptedDomains | %{"$($r.RedirectTo)" -like "*$_*"}) -notcontains $true){
                $ToExport.forwarded.RedirectTo += $r.RedirectTo}
            $ToExport.forwarded.Description += $r.Description
            $ToExport.forwarded.enabled += $r.Enabled
        }
    }
    Write-Host($ToExport.samAccountName)
    if($ToExport.forwarded.ForwardTo -ne $null -or $ToExport.forwarded.RedirectTo -ne $null){
       $Testarray += $ToExport
    }
    
    $ToExport.forwarded.ForwardTo = $null
    $ToExport.forwarded.RedirectTo = $null
    $ToExport.forwarded.Enabled = $null
    $ToExport.forwarded.Description = $null 
}

The Problem is when i then check the content of the array all Objects in it have the same samAccountName and when i ope forwarded all properties in there are empty. I dont see where i either overwrite the name of all existing array objects or delete the content under the object: forwarded

$Testarray

samAccountName forwarded                                    
-------------- ---------                                    
Name1       {RedirectTo, ForwardTo, Description, Enabled}
Name1        {RedirectTo, ForwardTo, Description, Enabled}
Name1        {RedirectTo, ForwardTo, Description, Enabled}
Name1        {RedirectTo, ForwardTo, Description, Enabled}

$Testarray[0].forwarded

Name                           Value                                                                                                                                                                                                                                                                                                                      
----                           -----                                                                                                                                                                                                                                                                                                                      
RedirectTo                                                                                                                                                                                                                                                                                                                                                
ForwardTo                                                                                                                                                                                                                                                                                                                                                 
Description                                                                                                                                                                                                                                                                                                                                               
Enabled                              

I want to put it all into one array so i can later on export it as a json or xml file.


Solution

  • When $ToExport is a reference-type object and you do:

    $Testarray += $ToExport
    

    What you're really doing is adding a reference to $ToExport to $TestArray, not a copy of it.

    So when you then subsequently update property values on $ToExport or one of it's members (like forwarded), it's reflected by all references to said object.

    You've essentially added the same object to the array 4 times, overwriting the property values every time.

    Instead, create a new [ordered] dictionary on each loop iteration - when you subsequently convert the hashtable to a custom object, PowerShell will copy the properties to a new, distinct object, and you'll no longer see the behavior observed:

    $Testarray = @()
    
    foreach($m in $mbx){
        $forwarded = [ordered]@{
            'ForwardTo'   = @()
            'RedirectTo'  = @()
            'Enabled'     = @()
            'Description' = @()
        }
        
        $rule = Get-InboxRule -Mailbox $m.SamAccountName
        foreach($r in $rule){
            if(($r.ForwardTo) -OR ($r.RedirectTo)){
                if($r.ForwardTo -like "*smtp*" -and ($AcceptedDomains | %{"$($r.ForwardTo)" -like "*$_*"}) -notcontains $true){
                    $forwarded.ForwardTo += $r.ForwardTo}
                if($r.RedirectTo -like "*smtp*" -and ($AcceptedDomains | %{"$($r.RedirectTo)" -like "*$_*"}) -notcontains $true){
                    $forwarded['RedirectTo'] += $r.RedirectTo}
                $forwarded['Description'] += $r.Description
                $forwarded['Enabled'] += $r.Enabled
            }
        }
        Write-Host($m.samAccountName)
        if($forwarded['ForwardTo'].Length -or $forwarded['RedirectTo'].Length){
            # create new object
            $Testarray += [pscustomobject]@{
                'samAccountName' = $m.SamAccountName
                # convert ordered hashtable to object
                'forwarded'      = [pscustomobject]$forwarded
            }
        }
        
        # no need to clean object template, it'll get recreated
    }
    

    If your goal is to export the list of forwarding rules to a flat format (like CSV), then I'd suggest attaching the SAMAccountName value to each rule, and then export those:

    $testArray = foreach($m in $mbx){
        Get-InboxRule -Mailbox $m.SamAccountName |Where-Object {
            $r = $_
            ($r.ForwardTo -like "*smtp*" -and ($AcceptedDomains | %{"$($r.ForwardTo)" -like "*$_*"}) -notcontains $true) -or 
            $r.RedirectTo -like "*smtp*" -and ($AcceptedDomains | %{"$($r.RedirectTo)" -like "*$_*"}) -notcontains $true
        } |Select -Property @{Name='SamAccountName';Expression={$m.SamAccountName}} RedirectTo,ForwardTo,Description,Enabled
    }
    
    $testArray |Export-Csv path\to\forward_rules_export.csv -NoTypeInformation
    

    Piping to Select-Object -Property will also cause the creation of a new, distinct object instance, once again preempting the problematic behavior you describe.