Add user to Meeting using Update-MgUserEvent - powershell

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 ?????
}

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.

(PowerShell) How do I filter usernames with Get-EventLog

I'm working on a Powershell script to get all users who have logged in/out of a server in the past 7 days, where their name is not like "*-organization". The below works, but no matter what I try I'm not able to filter names
$logs = get-eventlog system -ComputerName $env:computername -source Microsoft-Windows-Winlogon -After (Get-Date).AddDays(-7)
$res = #()
ForEach ($log in $logs)
{
if($log.instanceid -eq 7001){
$type = "Logon"
}
Elseif ($log.instanceid -eq 7002){
$type = "Logoff"
}
Else { Continue }
$res += New-Object PSObject -Property #{Time = $log.TimeWritten; "Event" = $type; User = (New-Object System.Security.Principal.SecurityIdentifier $Log.ReplacementStrings[1]).Translate([System.Security.Principal.NTAccount])}};
$res
I've tried adding this line in various places and ways, but no matter what I can't get it to filter. It either fails and tells me my operator must have a property and value, or it runs fine and ignores any username filtering.
| Where-Object $_.User -notlike "*-organization"
Is it even possible to filter the login username with this method? If so, what am I doing wrong? If it's not possible, is there another way I can get what I need?
There would have to be a property named 'user' for that to work. Get-eventlog is actually obsolete now, and replaced by get-winevent. Unfortunately, you have to get into the xml to filter by usersid. I've included a time filter.
$a = get-winevent #{logname='system';
providername='Microsoft-Windows-Winlogon'} -MaxEvents 1
$e = $a.ToXml() -as 'xml'
$e.event.EventData
Data
----
{TSId, UserSid}
get-winevent #{logname='system';providername='Microsoft-Windows-Winlogon';
data='S-2-6-31-1528843147-473324174-2919417754-2001';starttime=(Get-Date).AddDays(-7);
id=7001,7002}
In powershell 7 you can refer to the eventdata named data fields directly:
get-winevent #{logname='system';providername='Microsoft-Windows-Winlogon';
usersid='S-2-6-31-1528843147-473324174-2919417754-2001'}
The get-winevent docs say you can use "userid" in the filterhashtable, but I can't get that to work.
EDIT: Actually this works. But without limiting it too much, at least for me.
get-winevent #{logname='system';userid='js2010'}
get-winevent #{providername='Microsoft-Windows-Winlogon';userid='js2010'}
You can do this with the -FilterXPath parameter like below:
$filter = "(*[System/EventID=7001] or *[System/EventID=7002]) and *[System/Provider[#Name='Microsoft-Windows-Winlogon']]"
$result = Get-WinEvent -LogName System -FilterXPath $filter | ForEach-Object {
# convert the event to XML and grab the Event node
$eventXml = ([xml]$_.ToXml()).Event
$eventData = $eventXml.EventData.Data
$userSID = ($eventData | Where-Object { $_.Name -eq 'UserSid' }).'#text'
$userName = [System.Security.Principal.SecurityIdentifier]::new($userSID).Translate([System.Security.Principal.NTAccount])
# you can add username filtering here if you like.
# remember the $userName is in formal DOMAIN\LOGONNAME
# if ($username -notlike "*-organization") {
# output the properties you need
[PSCustomObject]#{
Time = [DateTime]$eventXml.System.TimeCreated.SystemTime
Event = if ($eventXml.System.EventID -eq 7001) { 'LogOn' } else { 'LogOff' }
UserName = $userName
UserSID = $userSID
Computer = $eventXml.System.Computer
}
# }
}
# output on screen
$result
# output to CSV file
$result | Export-Csv -Path 'X:\TheOutputFile.csv' -NoTypeInformation
Note, I have commented out the username filtering in the code. It is just there to give you an idea of where to put it. Of course, you can also filter the $result afterwards:
$result | Where-Object { $_.UserName -notlike "*-organization" }
Adding to #js2010's helpful answer, and with the assumption you're using PowerShell 5.1. I usually identify the property array index and use Select-Object to create a custom property as needed.
$WinEvents =
get-winevent #{logname='system'; providername='Microsoft-Windows-Winlogon'} |
Select-Object #{Name = 'Time'; Expression = {$_.TimeCreated}},
#{Name = 'Event'; Expression = { If($_.ID -eq 7001){'Logon'} ElseIf($_.ID -eq 7002){ 'Logoff' } } },
#{Name = 'User'; Expression = { [System.Security.Principal.SecurityIdentifier]::new( $_.Properties[1].Value ).Translate([System.Security.Principal.NTAccount]) } }
In your case this should add a property called User with a value like DomainName\UserName to the objects. I also added expressions to derive the other properties you were adding to your custom objects. Select-Object emits custom objects as well so this should give the result you're looking for.
Let me know if this helps.
Update
Respectfully, the other 2 answers make the assumption that you are looking for logon/off events for a specific user. That's not how I read the question; in particular:
"get all users who have logged in/out of a server"
While PowerShell 7+ does let you directly cite UserID in the FilterHashtable, it's not very useful here because we're not seeking events for a specific user. Furthermore, it seems unhelpful for the ultimate output as by default it echoes as a SID. It would still need to be translated, not only for display but for further filtering. I'm also not positive that UserID will always be the same as Properties[1], there's certainly some variance when looking at other event IDs.
The XML work is very cool, but I don't think it's called for here.
There were some issues with my answer as well. I overlooked filtering the event IDs & dates up front. I also realized we don't need to instantiate [System.Security.Principal.SecurityIdentifier] class because the property is already typed as such. Along with some readability improvements I corrected those issues below.
# Should be the 1st line!
using NameSpace System.Security.Principal
$ResolveEventType = #{ 7001 = 'Logon'; 7002 = 'Logoff' }
$FilterHashTable =
#{
LogName = 'system'
ProviderName = 'Microsoft-Windows-Winlogon'
ID = 7001,7002
StartTime = (Get-Date).AddDays(-7)
}
[Array]$WinEvents =
Get-WinEvent -FilterHashtable $FilterHashTable |
Select-Object #{ Name = 'Time'; Expression = { $_.TimeCreated } },
#{ Name = 'Event'; Expression = { $ResolveEventType[ $_.ID ] } },
#{ Name = 'User'; Expression = { $_.Properties[1].Value.Translate( [NTAccount] ) } }
$WinEvents |
Where-Object{ $_.UserName -notlike "*-organization" } |
Format-Table -AutoSize
This tested good in PowerShell 5.1 & 7.0. I added Format-Table to display the output, but you can just change that out for an Export-Csv command as needed
Note: The last 2 pipelines can be combined, but I thought this was a
little more readable.
Let me know if this helps.

Is there a AD lockout script showing actual machine

Does anyone know or have a script which tells you the actual device locking out an AD account. I have a working script which lists all users locked out in the last 3 days which tells me the DC its locked out. Rather than having to connect to this or via event log and locate the event id, i wanted to know if there was a PS script out there which would output where. Then we can go to said device and fix.
Google has brought up a few suggestions but not the clearest and some just do what i can already get via the current script.
Thanks
This returns an array of PsObjects, where:
property TargetUserName holds the user SamAccountName that is locked out
property TargetDomainName contains the computer name where the lockout originated from
property EventDate will show the time and date the lockout occurred
Code:
# get the domain controller that has the PDC Emulator Role
$pdc = (Get-ADDomain).PDCEmulator
$splat = #{
FilterHashtable = #{LogName="Security";Id=4740}
MaxEvents = 100
ComputerName = $pdc
Credential = Get-Credential -Message "Please enter credentials for '$pdc'"
}
$lockedOut = Get-WinEvent #splat | ForEach-Object {
# convert the event to XML and grab the Event node
$eventXml = ([xml]$_.ToXml()).Event
# create an ordered hashtable object to collect all data
# add some information from the xml 'System' node first
$evt = [ordered]#{
EventDate = [DateTime]$eventXml.System.TimeCreated.SystemTime
Level = [System.Diagnostics.Tracing.EventLevel]$eventXml.System.Level
}
# next see if there are childnodes under 'EventData'
if ($eventXml.EventData.HasChildNodes) {
$eventXml.EventData.ChildNodes | ForEach-Object {
$name = if ($_.HasAttribute("Name")) { $_.Name } else { $_.LocalName }
$value = $_.'#text'
if ($evt[$name]) {
# if an item with that name already exists, make it an array and append
$evt[$name] = #($evt[$name]) + $value
}
else { $evt[$name] = $value }
}
}
# output as PsCustomObject. This ensures the $result array can be written to CSV easily
[PsCustomObject]$evt
}
# output on screen
$lockedOut | fl *
# output to csv file
$lockedOut | Export-Csv -Path 'D:\lockedout.csv' -NoTypeInformation
If you want to search for a specific user (SamAccountName) for instance, just do
$lockedOut | Where-Object { $_.TargetUserName -eq 'UserSamAccountName' }
Hope that helps

PowerShell - Show Property of Parent Object and Child Object

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

Windows PowerShell Filtering by data range

I have a powershell script to get deactivated accounts from our SSO app but would like to filter it down to only those that were deactivated more than 90 days ago.
I then have another script to take the results and deletes those users from the SSO app.
Can you tell me how to add a filter to the below script to exclude results were the StatusChanged date is greater than 90 days from current date.
$users = oktaListDeprovisionedUsers -oOrg PREV
$toexport = New-Object System.Collections.ArrayList
Foreach ($u in $users)
{
$line = #{
status = $u.status
employeeid = $u.profile.employeeNumber
firstName = $u.profile.firstName
lastName = $u.profile.lastName
email = $u.profile.email
department = $u.profile.department
supervisor = $u.profile.manager
created = $u.created
lastUpdated = $u.lastUpdated
login = $u.profile.login
title = $u.profile.title
GroupName = $u.profile.Group_Name
Organization = $u.profile.organization
Location = $u.profile.workday_location
User_type = $u.profile.userType
StatusChanged = $u.StatusChanged
}
$obj = New-Object psobject -Property $line
$_c = $toexport.Add($obj)
}
#Path for utility will have to be changed to a more generic location.
$toexport | Select-Object "login", "StatusChanged", "employeeid", "firstName","lastName", "email", "title","supervisor","department","Organization","Location", "GroupName" | >Export-Csv -Path "C:\OktaExport\user-list.csv" -NoTypeInformation
You can filter the $users object by a Where-Object
$users = $users | Where-Object{((Get-Date) - $_.StatusChanged).TotalDays -gt 90}
Add this to the 2nd line of your script.