Creating multiple Objects in the same script

This topic contains 8 replies, has 5 voices, and was last updated by Profile photo of David DeHerrera David DeHerrera 1 year, 9 months ago.

  • Author
    Posts
  • #24045
    Profile photo of David DeHerrera
    David DeHerrera
    Participant

    I am running into an issue where attempting to create multiple objects in the same script, even if I put them in their own Functions, I do not get the proper output.

    I have a script depending on success or failure which object it will send in an email. No matter how I write this it will not show both.
    Here is an example, if you put this in one ISE window it will only show the first object. Any ideas?

    $objOutput = New-Object System.Object
    $objOutput | Add-Member -Type NoteProperty -Name "Name" -Value $("Name 1")
    $objOutput | Add-Member -Type NoteProperty -Name "SID" -Value $("SID 1")
    $objOutput | Add-Member -Type NoteProperty -Name "UPN" -Value $("UPN 1")
    Write-Output $objOutput
    
    $objOutput2 = New-Object System.Object
    $objOutput2 | Add-Member -Type NoteProperty -Name "NValue" -Value $("Name 2")
    $objOutput2 | Add-Member -Type NoteProperty -Name "SValue" -Value $("SID 2")
    $objOutput2 | Add-Member -Type NoteProperty -Name "UValue" -Value $("UPN 2")
    Write-Output $objOutput2
    
  • #24047
    Profile photo of Rob Simmers
    Rob Simmers
    Participant

    I typically generate objects using this method:

    $object1 = @[]
    $object1 += New-Object -TypeName PSObject -Property @{Name="Name 1";SID="SID1";UPN="UPN1"}
    
    $object2 = @[]
    $object2 += New-Object -TypeName PSObject -Property @{NValue="Name 2";SValue="SID2";UValue="UPN2"}
    
    $object3 = for[$i=1;$i -le 5;$i++] {
        $props = @{NValue="Name$i";
                            SValue="SID$i";
                            UValue="UPN$i"}
        New-Object -TypeName PSObject -Property $props
    }
    
    $object1 | Format-Table -AutoSize
    $object2 | Format-Table -AutoSize
    $object3 | Format-Table -AutoSize
    

    Output:

    SID  Name   UPN 
    ---  ----   --- 
    SID1 Name 1 UPN1
    
    
    
    UValue NValue SValue
    ------ ------ ------
    UPN2   Name 2 SID2  
    
    
    
    UValue NValue SValue
    ------ ------ ------
    UPN1   Name1  SID1  
    UPN2   Name2  SID2  
    UPN3   Name3  SID3  
    UPN4   Name4  SID4  
    UPN5   Name5  SID5

    I think it's easy to read and also easy to implement in for loops versus Add-Member requiring you to -Force to re-add properties to the object. Add-Member is good for adding a value to all rows with a single value or adding advanced properties like AliasProperty to an existing object. I don't see anything necessarily incorrect in the code you posted per se. As a side note, you don't need to use Write-Output and it's generally frowned upon unless there is a specific reason to use it, such as coloring output.

  • #24062
    Profile photo of Kiran Reddy
    Kiran Reddy
    Participant

    If you use format-list then both objects are displayed. Why dont the objects display without format-list? I do not know 🙂
    Note: using format command changes the type of the object and depending on what you want to do may make it useless for further processing.


    Write-Output $objOutput2 | fl *

  • #24086
    Profile photo of David DeHerrera
    David DeHerrera
    Participant

    @RobSimmers "As a side note, you don't need to use Write-Output and it's generally frowned upon unless there is a specific reason to use it, such as coloring output"

    I usually do not use Write-Output on adding the object, I usually do $Output += $objOutput.

    I will have to give your way a try and maybe that will be my new way of getting it done. thank you.

    @KiranReddy – I am not sure why this happens while trying to create two objects in the same script. I have tried many different ways to edit, copy, ToString, ToEverything... and nothing has worked. It looks like Robs method might be the only answer.

  • #24090
    Profile photo of Sam Boutros
    Sam Boutros
    Participant

    Here's how I would do this:

    #Requires -Version 4
    
    $objArray = @()
    
    $Props = [ordered]@{
        Name = 'Name1'
        SID  = 'SID1'
        UPN  = 'UPN1'
    }
    $objArray += New-Object -TypeName PSObject -Property $Props
    
    $Props = [ordered]@{
        Name = 'Name2'
        SID  = 'SID2'
        UPN  = 'UPN2'
    }
    $objArray += New-Object -TypeName PSObject -Property $Props
    
    $objArray | FT -a 
    

    This is mainly because the middle section can be replaced with a loop or a code block to build up the array output. For example:

    #Requires -Version 4
    
    # Initilaize
    $objArray = @()
    
    # Processing
    Get-Content .\users.csv | % {
        $Props = [ordered]@{
            Name = $_.Name 
            SID  = $_.SID 
            UPN  = $_.UPN 
        }
        $objArray += New-Object -TypeName PSObject -Property $Props
    }
    
    # Output
    $objArray | FT -a 
    $objArray | Out-GridView
    $objArray | Export-Csv .\MyUsers.csv 
    

    In this example, I'm reading the info from CSV file and compiling the object..

  • #24091
    Profile photo of Rohn Edwards
    Rohn Edwards
    Participant

    David,

    Both objects are being output, and the ISE is trying to show both of them to you. Here's what's going on as I understand it (I may be off on a few details, so anybody can feel free to correct me where I'm wrong):

    1. You create your first object, and it has three properties: Name, SID, and UPN
    2. Since it's not being captured, it needs to go to the screen. PowerShell's formatting rules kick in, and the object is inspected. It has less than five properties, so it decides to format it as a table.
    3. Since it's going to be formatted as a table, the headers have to be written first. The headers are written using the properties that were discovered in the last step.
    4. The first object is written to the host (all three properties are shown)
    5. The second object is created, and it has three properties: NValue, SValue, and UValue
    6. It needs to go to the screen. The formatting system is all geared up to write the output as a table with Name, SID, and UPN properties.
    7. The formatting system checks the Name, SID, and UPN properties of the second object; they don't exist, so it outputs null. The other three properties aren't shown because the table wasn't set up for them.

    Here are a few ways to demonstrate what I'm talking about. There have been a lot of different ways to create objects in this thread, so I'm going to use my favorite (it requires PSv3 or higher):

    $Output = @(
        [PSCustomObject] @{
            Name = "Name 1"
            SID = "SID 1"
            UPN = "UPN 1"
        }
    
        [PSCustomObject] @{
            NValue = "Name 2"
            SValue = "SID 2"
            UValue = "UPN 2"
        }
    
        [PSCustomObject] @{
            Name = "Name 3"
            SID = "SID 3"
            UValue = "UPN 3"
        }
    )
    

    And here's how it looks from the command line:

    # Notice the blank line where the second object should be:
    PS C:\> $Output
    
    Name                       SID                        UPN                       
    ++++                       +++                        +++                       
    Name 1                     SID 1                      UPN 1                     
                                                                                    
    Name 3                     SID 3                                                
    
    # Here's what it looks like when you tell Format-Table all of the columns
    # to show:
    PS C:\> $Output | ft Name, SID, UPN, NValue, SValue, UValue
    
    Name          SID           UPN           NValue       SValue       UValue      
    ++++          +++           +++           ++++++       ++++++       ++++++      
    Name 1        SID 1         UPN 1                                               
                                              Name 2       SID 2        UPN 2       
    Name 3        SID 3                                                 UPN 3       
    

    So, your data is all there, it's just not being shown because the objects are so different.

  • #24093
    Profile photo of Kiran Reddy
    Kiran Reddy
    Participant

    that was a nice explanation Rohn, thumbs up!

  • #24098
    Profile photo of David DeHerrera
    David DeHerrera
    Participant

    @Rohn – Thank you for the explanation, I am having a much better understanding for my headaches. So you can only have one (custom object or table) per script?

    What I would like is to be able to have multiple custom objects created throughout the script in order to output or email depending on Error, Warning, Success or Informational. They should all be separate emails.

    Here is a kind of layout for some of my scripts, I create a CSS in order to convert the objects into HTML style emails. Then those emails can go out to different groups in the company in order to do something, also they will not get information they do not need.
    Not sure if this makes sense or if I am thinking about this in the wrong manner, but this is what I am stuck on. If you run this, you will get two emails but with only the computer information.

      
    
    
    # Script Variables
    $ScriptLocation = '     "\\Server\C$\1T_Tools\Scheduled Tasks\Script.ps1"'
    
    # Email Variables
    $MailTo = "email@domain.com"
    $MailFrom = "Alerts-EMail@domain.com"
    $MailSubject = "Subject Line"
    $MailBody = "*** This body uses Variables, not this line ***"
    $MailRelay = "relay.domain.com"
    
    $style = "TABLE{border-collapse: collapse;border: 1px solid black;width: 100%;}TH{border-collapse: collapse;border: 1px solid black;background-color: Gray;color: white;text-align:`"left`";}TD{font-size: .75em;border-collapse: collapse;border: 1px solid black;vertical-align:`"Bottom`";padding: 3px 7px 2px 7px;}"
    $Output = @()
    $Output1 = @()
    $Output2 = @()
    
    Try
    {
        $StartTime = Get-Date
    
        # Get information from within the script to add to the $Output variable.
        $objOutput = New-Object System.Object
        $objOutput | Add-Member -Type NoteProperty -Name "Computer" -Value $("Say Computer Name")
        $objOutput | Add-Member -Type NoteProperty -Name "Status" -Value $("Say Status")
        $Output += $objOutput
    
        # Get information from within the script to add to the $Output variable.
        $objOutput1 = New-Object System.Object
        $objOutput1 | Add-Member -Type NoteProperty -Name "User" -Value $("Say User Name")
        $objOutput1 | Add-Member -Type NoteProperty -Name "Status" -Value $("User Status")
        $Output1 += $objOutput1
    
    }
    Catch
    {
        # Get information from within the script to add to the $Output variable.
        $objOutput2 = New-Object System.Object
        $objOutput2 | Add-Member -Type NoteProperty -Name "Computer" -Value $("Error Computer Name")
        $objOutput2 | Add-Member -Type NoteProperty -Name "Status" -Value $("Error Status")
        $Output2 += $objOutput2
    
    
    }
    Finally
    {
        # Email information at the end of the script
        $EndTime = Get-Date
    
        If($Output -ne $Null)
            {
                $NewEMail = $Output | Sort-Object Status | ConvertTo-Html -Head $Style
                Send-MailMessage -To $MailTo -From $MailFrom -Subject $MailSubject -BodyAsHtml "Start Time: $($StartTime) - End Time: $($EndTime) $($ScriptLocation) $($NewEMail)" -SmtpServer $MailRelay
            }
        If($Output1 -ne $Null)
            {
                $NewEMail = $Output1| Sort-Object Status | ConvertTo-Html -Head $Style
                Send-MailMessage -To $MailTo -From $MailFrom -Subject $MailSubject -BodyAsHtml "Start Time: $($StartTime) - End Time: $($EndTime) $($ScriptLocation) $($NewEMail)" -SmtpServer $MailRelay
            }
        If($Output2 -ne $Null)
            {
                $NewEMail = $Output2 | Sort-Object Status | ConvertTo-Html -Head $Style
                Send-MailMessage -To $MailTo -From $MailFrom -Subject $MailSubject -BodyAsHtml "Start Time: $($StartTime) - End Time: $($EndTime) $($ScriptLocation) $($NewEMail)" -SmtpServer $MailRelay
            }
    } 

    What I would like to do is create multiple objects to display or get emailed throughout the script. But it is looking that I create one object with all the data then parse that to different outputs?

  • #24099
    Profile photo of David DeHerrera
    David DeHerrera
    Participant

    Ok I am stumped once again, now the email works but if I add:

    $Output
    $Output1
    $Output2
    

    Into the Finally section, the Output is still wrong.

You must be logged in to reply to this topic.