PowerShell - Show Property of Parent Object and Child Object - powershell

If I have a list of email metadata in a hashtable and each email has a hashtable with a list of attachments inside that object, like this:
$Emails = #{
ID = "E123";
Subject = "Check this out";
Attachments = #{
ID = "A123";
Name = "FunnyPic.jpg"
}
}
And then that hashtable is converted to a PSObject like this:
$EmailsObject = New-Object -TypeName PSObject -Property $Emails
And I want to do something like this:
$EmailsObject | Select ID, Attachments.ID
How would I do that?
I want to associate the ID of the Email with the ID's of the associated attachments.

You will want to use calulated properties.
$Emails | Select-Object -Property #{Name='ID';Expression={$_.ID};},#{Name='AttachmentId';Expression={$_.Attachments.ID};}
You can shorten Name and Expression to n and e.
See this article for more.

you need to use a calculated property if you use Select-Object. something like this ...
$Emails = #{
ID = "E123";
Subject = "Check this out";
Attachments = #{
ID = "A123";
Name = "FunnyPic.jpg"
}
}
$EmailsObject = New-Object -TypeName PSObject -Property $Emails
$EmailsObject |
Select-Object ID,
#{
n = 'AttID'
e = {$_.Attachments.ID}
}
output ...
ID AttID
-- -----
E123 A123

Related

How to fix System Object value in PowerShell

I'm Importing a CSV file and reading a column that look like this
Exchange Mailboxes
Include:[john.doe#outlook.com]
Include:[david.smith#outlook.com]
Include:[kevin.love#outlook.com]
I use Get-EXOMailbox to get their DisplayName and Id. After that I'm trying to pass it in my New-Object like below so that I can export it. The problem I have is when I look at my Excel file, it showing System.Object[] on every row instead of showing each actual DisplayName and Id.
Any help on how to display it correctly would be really appreciated.
$result = Import-Csv "C:\AuditLogSearch\Dis\Modified-Audit-Log-Records.csv" |
Where-Object { -join $_.psobject.Properties.Value } |
ForEach-Object {
$exoMailbox = ($_.'Exchange Mailboxes' -split '[][]')[1]
$exoUser = Get-EXOMailbox -Filter "PrimarySmtpAddress -eq '$exoMailbox'"
# Construct and output a custom object with the properties of interest.
[pscustomobject] #{
UserName = $exoUser.DisplayName
UserId = $exoUser.Identity
}
}
New-Object PsObject -Property #{
'Searched User' = $result.UserName //I'm trying to pass here
'SharePoint URL' = $spUrl
'Searched User GMID' = $result.UserId //and here
'Site Owner' = $spositeOwner
User = $u.User
"Result Status" = $u."Result Status"
"Date & Time" = $u."Date & Time"
"Search Conditions" = $u."Search Conditions"
"SharePoint Sites" = $u."SharePoint Sites"
"Exchange Public Folders" = $u."Exchange Public Folders"
"Exchange Mailboxes" = $u."Exchange Mailboxes".Split([char[]]#('[', ']'))[1]
"Case Name" = $u."Case Name"
"Search Criteria" = $u."Search Criteria"
"Record Type" = $u."Record Type"
"Hold Name" = $u."Hold Name".Split(('\'))[1]
"Activity" = if ($null -ne ($importData | where-object { $_.Name -eq $u."Activity" }).Value) { ($importData | where-object { $_.Name -eq $u."Activity" }).Value }
else { $u."Activity" }
} | Select-object -Property User, "Date & Time", "Case Name", "Hold Name", "Record Type", "Activity" , "Searched User", "Searched User GMID", "SharePoint URL", "Exchange Mailboxes", "Exchange Public Folders" , "Search Criteria", "Result Status"
}
$xlsx = $result | Export-Excel #params
$ws = $xlsx.Workbook.Worksheets[$params.Worksheetname]
$ws.Dimension.Columns
$ws.Column(1).Width = 20
$ws.Column(2).Width = 20
$ws.Column(3).Width = 15
$ws.Column(4).Width = 15
$ws.Column(5).Width = 15
$ws.Column(6).Width = 160
$ws.View.ShowGridLines = $false
Close-ExcelPackage $xlsx
$result is an array of objects, containing an object for each non-empty row in your input CSV; thus, adding values such as $result.UserName to the properties of the object you're creating with New-Object will be arrays too, which explains your symptom (it seems that Export-Excel, like Export-Csv doesn't meaningfully support array-valued properties and simply uses their type name, System.Object[] during export).
It sounds like the easiest solution is to add the additional properties directly in the ForEach-Object call, to the individual objects being constructed and output via the existing [pscustomobject] literal ([pscustomobject] #{ ... }):
$result =
Import-Csv "C:\AuditLogSearch\Dis\Modified-Audit-Log-Records.csv" |
Where-Object { -join $_.psobject.Properties.Value } | # only non-empty rows
ForEach-Object {
$exoMailbox = ($_.'Exchange Mailboxes' -split '[][]')[1]
$exoUser = Get-EXOMailbox -Filter "PrimarySmtpAddress -eq '$exoMailbox'"
# Construct and output a custom object with the properties of interest.
[pscustomobject] #{
UserName = $exoUser.DisplayName
UserId = $exoUser.Identity
# === Add the additional properties here:
'Searched User' = $exoUser.UserName
'SharePoint URL' = $spUrl
'Searched User GMID' = $exoUser.UserId
'Site Owner' = $spositeOwner
# ...
}
}
Note:
The above shows only some of the properties from your question; add as needed (it is unclear where $u comes from in some of them.
Using a custom-object literal ([pscustomobject] #{ ... }) is not only easier and more efficient than a New-Object PSObject -Property #{ ... }[1] call, unlike the latter it implicitly preserves the definition order of the properties, so that there's no need for an additional Select-Object call that ensures the desired ordering of the properties.
[1] Perhaps surprisingly, PSObject ([psobject]) and PSCustomObject ([pscustomobject]) refer to the same type, namely System.Management.Automation.PSObject, despite the existence of a separate System.Management.Automation.PSCustomObject, which custom-objects instances self-report as (([pscustomobject] #{}).GetType().FullName) - see GitHub issue #4344 for background information.

Add user to Meeting using Update-MgUserEvent

I'm trying to add a user to an existing meeting using "Update-MgUserEvent."
I can pull the existing event fine. However, I'm having trouble merging the existing attendees and the new attendee into something that the command will accept. Anytime I am close, it will add the new attendee and send cancellation requests to the existing attendees.
If anybody has any tips or any help at all, it would be GREATLY appreciated! Here is what I am working with so far. I'm trying to merge the "$New Attendees" and "$ExistingAttendees" data and send it back to the event as an update:
# New user to test.
$NewAttendees = #{
Attendees = #(
#{
EmailAddress = #{
Address = "bill.cosby#test.com"
Name = "Bill Cosby"
}
Type = "required"
}
)
}
# Get user start date.
$EmpStartDate = Read-Host -prompt "Enter employee start date (YYYY-MM-DD)"
# Get existing events.
Clear-Variable Existing*
$ExistingEvent = Get-MgUserEvent -UserId "michael.scott#test.com" -filter "Subject eq 'I.T. Orientation'" | Select-Object Id,Attendees -ExpandProperty start | Where-Object {$_.DateTime -like "*$EmpStartDate*"}
$ExistingAttendees = #{
Attendees = #(
#{
EmailAddress = #{
Address = $ExistingEvent.Attendees.EmailAddress | Select-Object -ExpandProperty Address
Name = $ExistingEvent.Attendees.EmailAddress.Name
}
Type = "required"
}
)
}
# Update the event.
If ($ExistingEvent) {
Update-MgUserEvent -UserId "michael.scott#test.com" -EventId $ExistingEvent.Id -Attendees ?????
}

Format-Table not taking effect (Exchange - powershell)

first of all sorry if my english is not the best. but ill try to explain my issue with as much detail as i can
Im having an issue where i cant get Format-Table to effect the output i give it.
below is the part im having issues with atm.
cls
$TotalSize = $($mailboxes. #{name = ”TotalItemSize (GB)”; expression = { [math]::Round((($_.TotalItemSize.Value.ToString()).Split(“(“)[1].Split(” “)[0].Replace(“,”, ””) / 1GB), 2) } });
$UserN = $($mailboxes.DisplayName)
$itemCount = $($mailboxes.ItemCount)
$LastLogonTime = $($mailboxes.ItemCount)
$allMailboxinfo = #(
#lager dataen som skal inn i et objekt
#{Username= $UserN; ItemCount = $itemCount; LastLogonTime = $($mailboxes.ItemCount); Size = $TotalSize}) | % { New-Object object | Add-Member -NotePropertyMembers $_ -PassThru }
$Table = $allMailboxinfo | Format-Table | Out-String
$Table
the output of this gives me what almost looks like json syntax below each title of the table.
Username LastLogonTime ItemCount Size
-------- ------------- --------- ----
{username1, username2,username3,userna...} {$null, $null, $null, $null...} {$null, $null, $null, $null...} {$null, $null, $null, $null...}
running the commands by themselves seem to work tho. like $mailboxes.DisplayName gives the exact data i want for displayname. even in table-format.
the reason im making the table this way instead of just using select-object, is because im going to merge a few tables later. using the logic from the script below.
cls
$someData = #(
#{Name = "Bill"; email = "email#domain.com"; phone = "12345678"; id = "043546" }) | % { New-Object object | Add-Member -NotePropertyMembers $_ -PassThru }
$moreData = #(
#{Name = "Bill"; company = "company 04"}) | % { New-Object object | Add-Member -NotePropertyMembers $_ -PassThru }
$Merge = #(
#plots the data into a new table
#{Name = $($someData.Name); e_mail = $($someData.email); phone = $($someData.phone); id = $($someData.id); merged = $($moreData.company) }) | % { New-Object object | Add-Member -NotePropertyMembers $_ -PassThru }
#formatting table
$Table = $Merge | Format-Table | Out-String
#print table
$Table
if you are wondering what im doing with this.
My goal, all in all. is a table with using the info from Exchange;
DisplayName, TotalItemSize(GB), ItemCount, LastLogonTime, E-mail adress, archive + Maxquoata, Quoata for mailbox.
You're creating a single object where each property holds an array of property values from the original array of mailbox objects.
Instead, create 1 new object per mailbox:
# construct output objects with Select-Object
$allMailBoxInfo = $mailboxes |Select #{Name='Username';Expression='DisplayName'},ItemCount,#{Name='LastLogonTime';Expression='ItemCount'},#{Name='Size';Expression={[math]::Round((($_.TotalItemSize.Value.ToString()).Split("(")[1].Split(" ")[0].Replace(",", "") / 1GB), 2) }}
# format table
$Table = $allMailBoxInfo | Format-Table | Out-String
# print table
$Table

Add values to PS object from other PS command

I created the following object:
$PSOhash = #{
ConnectedNode = $ConnectedNode
ConnectedNodeDeviceNumber = $ConnectedNodeDeviceNumber
Serialnumber = $Serialnumber
ProductId = $ProductId
}
$ClusterNodeSSDs = New-Object PSObject -Property $PSOhash
and want to add values from the following command into it:
$SSDModel = "xyz123"
$ClusterNode = "Node1"
gwmi -Namespace root\wmi ClusPortDeviceInformation| select ConnectedNode,ConnectedNodeDeviceNumber, Serialnumber, ProductId | sort ConnectedNodeDeviceNumber | where {($_.ConnectedNode -eq $ClusterNode) -and ($_.ProductId -match "$SSDModel")}
which returns the proper informations, but need them as properties in the object for further processing.
If you want to add a set of property-value pairs to an already existing PSObject ($MyObject in this example) that currently does not have those properties, you can use the Add-Member command for this:
$PSOhash = #{
ConnectedNode = $ConnectedNode
ConnectedNodeDeviceNumber = $ConnectedNodeDeviceNumber
Serialnumber = $Serialnumber
ProductId = $ProductId
}
$MyObject = $MyObject | Add-Member -NotePropertyMembers $PSOHash
Explanation:
The -NotePropertyMembers parameter allows you do add a hash table of property-value pairs to a custom object.
Optionally, you can use a combination of Add-Member's -NotePropertyValue and -NotePropertyName to add properties one at a time.
If you want to update one object's property values with property values (same property names) from another object, you can just use direct assignment and the member access operator (.).
$SSDModel = "xyz123"
$ClusterNode = "Node1"
$WmiObjects = Get-WmiObject -Namespace root\wmi ClusPortDeviceInformation |
Select-Object ConnectedNode,ConnectedNodeDeviceNumber, Serialnumber, ProductId |
Sort-Object ConnectedNodeDeviceNumber |
where {($_.ConnectedNode -eq $ClusterNode) -and ($_.ProductId -match "$SSDModel")}
$ClusterNodeSSDs = foreach ($WmiObject in $WmiObjects) {
$PSOhash = #{
ConnectedNode = $WmiObject.ConnectedNode
ConnectedNodeDeviceNumber = $WmiObject.ConnectedNodeDeviceNumber
Serialnumber = $WmiObject.Serialnumber
ProductId = $WmiObject.ProductId
}
[pscustomobject]$PSOhash
}
Explanation:
Note the use of the foreach loop here because the Get-WmiObject will likely return a collection. So you will need to iterate all of them to create custom objects. However, it just seems that you can just use the Get-WmiObject | Select-Object output to perform the same thing.

Find matches in two different Powershell objects based on one property

I am trying to find the matching names in two different types of Powershell objects
$Object1 has two properties - Name (string), ResourceID (uint32)
$object2 has one noteproperty - Name (system.string)
This gives me a list of the matching names but I also want the corresponding resourceID property from $object1.
$computers = Compare-Object $Object1.name $WSD_CM12 | where {$_.sideindicator -eq "=>"} | foreach {$_.inputobject}
These are big objects with over 10,000 items so I'm looking for the most efficient way to accomplish this.
If I'm understanding what you're after, I'd start by creating a hash table from your Object1 collection:
$object1_hash = #{}
Foreach ($object1 in $object1_coll)
{ $object1_hash[$object1.Name] = $object1.ResourceID }
Then you can find the ResourceID for any given Object2.name with:
$object1_hash[$Object2.Name]
Test bed for creating hash table:
$object1_coll = $(
New-Object PSObject -Property #{Name = 'Name1';ResourceID = 001}
New-Object PSObject -Property #{Name = 'Name2';ResourceID = 002}
)
$object1_hash = #{}
Foreach ($object1 in $object1_coll)
{ $object1_hash[$object1.Name] = $object1.ResourceID }
$object1_hash
Name Value
---- -----
Name2 2
Name1 1
Alternative method:
# Create sample list of objects with both Name and Serial
$obj1 = New-Object -Type PSCustomObject -Property:#{ Name = "Foo"; Serial = "1234" }
$obj2 = New-Object -Type PSCustomObject -Property:#{ Name = "Cow"; Serial = "4242" }
$collection1 = #($obj1, $obj2)
# Create subset of items with only Name
$objA = New-Object -Type PSCustomObject -Property:#{ Name = "Foo"; }
$collection2 = #($objA)
#Everything above this line is just to make sample data
# replace $collection1 and $collection2 with $Object1, $WSD_CM12
# Combine into one list
($collection1 + $collection2) |
# Group by name property
Group-Object -Property Name |
# I only want items that exist in both
Where { $_.Count -gt 1 } |
# Now give me the object
Select -Expand Group |
# And get the properties
Where { $_.Serial -ne $null }