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
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.
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 *
@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.
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…
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):
- You create your first object, and it has three properties: Name, SID, and UPN
- 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.
- 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.
- The first object is written to the host (all three properties are shown)
- The second object is created, and it has three properties: NValue, SValue, and UValue
- 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.
- 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.
that was a nice explanation Rohn, thumbs up!
@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?
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.