diff --git a/Calendar/CalLogHelpers/CalLogCSVFunctions.ps1 b/Calendar/CalLogHelpers/CalLogCSVFunctions.ps1 index 457eec2cd..e5e783c38 100644 --- a/Calendar/CalLogHelpers/CalLogCSVFunctions.ps1 +++ b/Calendar/CalLogHelpers/CalLogCSVFunctions.ps1 @@ -131,7 +131,6 @@ function BuildCSV { Write-Host "Starting to Process Calendar Logs..." $GCDOResults = @() - $IsFromSharedCalendar = @() $LogType = @() $script:MailboxList = @{} Write-Host "Creating Map of Mailboxes to CNs..." @@ -157,8 +156,6 @@ function BuildCSV { } } - $IsFromSharedCalendar = ($null -ne $CalLog.externalSharingMasterId -and $CalLog.externalSharingMasterId -ne "NotFound") - # Record one row $GCDOResults += [PSCustomObject]@{ 'LogRow' = $Index @@ -169,7 +166,7 @@ function BuildCSV { 'LogClientInfoString' = $CalLog.LogClientInfoString 'TriggerAction' = $CalLog.CalendarLogTriggerAction 'ItemClass' = $ItemType - 'Seq:Exp:ItemVersion' = $CalLog.AppointmentSequenceNumber.ToString() + ":" + $CalLog.AppointmentLastSequenceNumber.ToString() + ":" + $CalLog.ItemVersion.ToString() + 'Seq:Exp:ItemVersion' = CompressVersionInfo($CalLog) 'Organizer' = $CalLog.From.FriendlyDisplayName 'From' = GetBestFromAddress($CalLog.From) 'FreeBusy' = $CalLog.FreeBusyStatus.ToString() @@ -178,7 +175,6 @@ function BuildCSV { 'LogFolder' = $CalLog.ParentDisplayName 'OriginalLogFolder' = $CalLog.OriginalParentDisplayName 'SharedFolderName' = MapSharedFolder($CalLog.ExternalSharingMasterId) - 'IsFromSharedCalendar' = $IsFromSharedCalendar 'ReceivedBy' = $CalLog.ReceivedBy.SmtpEmailAddress 'ReceivedRepresenting' = $CalLog.ReceivedRepresenting.SmtpEmailAddress 'MeetingRequestType' = $CalLog.MeetingRequestType.ToString() @@ -192,7 +188,7 @@ function BuildCSV { 'RecurrencePattern' = $CalLog.RecurrencePattern 'AppointmentAuxiliaryFlags' = $CalLog.AppointmentAuxiliaryFlags.ToString() 'DisplayAttendeesAll' = $CalLog.DisplayAttendeesAll - 'AttendeeCount' = ($CalLog.DisplayAttendeesAll -split ';').Count + 'AttendeeCount' = GetAttendeeCount($CalLog.DisplayAttendeesAll) 'AppointmentState' = $CalLog.AppointmentState.ToString() 'ResponseType' = $CalLog.ResponseType.ToString() 'ClientIntent' = $CalLog.ClientIntent.ToString() @@ -223,3 +219,38 @@ function ConvertDateTime { } return [DateTime]$DateTime } + +function GetAttendeeCount { + param( + [string] $AttendeeCollection + ) + if ($From.SmtpAddress -ne "NotFound") { + return ($AttendeeCollection -split ';').Count + } else { + return "-" + } +} + +function CompressVersionInfo { + param( + $CalLog + ) + [string] $CompressedString = "" + if ($CalLog.AppointmentSequenceNumber -eq "NotFound" -or [string]::IsNullOrEmpty($CalLog.AppointmentSequenceNumber)) { + $CompressedString = "-:" + } else { + $CompressedString = $CalLog.AppointmentSequenceNumber.ToString() + ":" + } + if ($CalLog.AppointmentLastSequenceNumber -eq "NotFound" -or [string]::IsNullOrEmpty($CalLog.AppointmentLastSequenceNumber)) { + $CompressedString += "-:" + } else { + $CompressedString += $CalLog.AppointmentLastSequenceNumber.ToString() + ":" + } + if ($CalLog.ItemVersion -eq "NotFound" -or [string]::IsNullOrEmpty($CalLog.ItemVersion)) { + $CompressedString += "-" + } else { + $CompressedString += $CalLog.ItemVersion.ToString() + } + + return $CompressedString +} diff --git a/Calendar/CalLogHelpers/CreateTimelineRow.ps1 b/Calendar/CalLogHelpers/CreateTimelineRow.ps1 index 0df30ef4b..5ed3a44ff 100644 --- a/Calendar/CalLogHelpers/CreateTimelineRow.ps1 +++ b/Calendar/CalLogHelpers/CreateTimelineRow.ps1 @@ -97,13 +97,13 @@ function CreateTimelineRow { } else { switch ($CalLog.Client) { ResourceBookingAssistant { - [array] $Output = "ResourceBookingAssistant $($Action) a $($MeetingRespType) Meeting Response message." + [array] $Output = "ResourceBookingAssistant $($Action) a $($MeetingRespType) Meeting Response message$($Extra)." } Transport { - [array] $Output = "[$($CalLog.From)] $($Action) $($MeetingRespType) Meeting Response message." + [array] $Output = "[$($CalLog.From)] $($Action) $($MeetingRespType) Meeting Response message$($Extra)." } default { - [array] $Output = "[$($CalLog.ResponsibleUser)] $($Action) [$($CalLog.Organizer)]'s $($MeetingRespType) Meeting Response with $($CalLog.Client)." + [array] $Output = "[$($CalLog.ResponsibleUser)] $($Action) [$($CalLog.Organizer)]'s $($MeetingRespType) Meeting Response with $($CalLog.Client)$($Extra)." } } } @@ -114,7 +114,7 @@ function CreateTimelineRow { } Exception { if ($CalLog.ResponsibleUser -ne "Calendar Assistant") { - [array] $Output = "[$($CalLog.ResponsibleUser)] $($CalLog.TriggerAction)d Exception to the meeting series with $($CalLog.Client)." + [array] $Output = "[$($CalLog.ResponsibleUser)] $($CalLog.TriggerAction)d Exception starting $($CalLog.StartTime) to the meeting series with $($CalLog.Client)." } } Ipm.Appointment { diff --git a/Calendar/CalLogHelpers/ExcelModuleInstaller.ps1 b/Calendar/CalLogHelpers/ExcelModuleInstaller.ps1 index 3f542ee0c..b04a2b449 100644 --- a/Calendar/CalLogHelpers/ExcelModuleInstaller.ps1 +++ b/Calendar/CalLogHelpers/ExcelModuleInstaller.ps1 @@ -34,7 +34,9 @@ function CheckExcelModuleInstalled { # Install ImportExcel module Install-Module -Name ImportExcel -Force -AllowClobber - Write-Host "Done. ImportExcel module is now installed." + Write-Host -ForegroundColor Green "Done. ImportExcel module is now installed." + Write-Host -ForegroundColor Yellow "Please rerun the Script to get your Calendar Logs." + exit } } } diff --git a/Calendar/CalLogHelpers/ExportToExcelFunctions.ps1 b/Calendar/CalLogHelpers/ExportToExcelFunctions.ps1 index a818d0da5..66e566e9c 100644 --- a/Calendar/CalLogHelpers/ExportToExcelFunctions.ps1 +++ b/Calendar/CalLogHelpers/ExportToExcelFunctions.ps1 @@ -15,6 +15,50 @@ function Export-CalLogExcel { # Export Raw Logs for Developer Analysis Write-Host -ForegroundColor Cyan "Exporting Raw CalLogs to Excel Tab [$($ShortId + "_Raw")]..." $script:GCDO | Export-Excel -Path $FileName -WorksheetName $($ShortId + "_Raw") -AutoFilter -FreezeTopRow -BoldTopRow -MoveToEnd + LogScriptInfo +} + +function LogScriptInfo { + # Only need to run once per script. + if ($null -eq $script:CollectedCmdLine) { + $RunInfo = @() + $RunInfo += [PSCustomObject]@{ + Key = "Script Name" + Value = $($script:command.MyCommand.Name) + } + $RunInfo += [PSCustomObject]@{ + Key ="RunTime" + Value = Get-Date + } + $RunInfo += [PSCustomObject]@{ + Key = "Command Line" + Value = $($script:command.Line) + } + $RunInfo += [PSCustomObject]@{ + Key = "Script Version" + Value = $script:Version + } + $RunInfo += [PSCustomObject]@{ + Key = "User" + Value = whoami.exe + } + $RunInfo += [PSCustomObject]@{ + Key = "PowerShell Version" + Value = $PSVersionTable.PSVersion + } + $RunInfo += [PSCustomObject]@{ + Key = "OS Version" + Value = $(Get-CimInstance -ClassName Win32_OperatingSystem).Version + } + $RunInfo += [PSCustomObject]@{ + Key = "More Info" + Value = "https://learn.microsoft.com/en-us/exchange/troubleshoot/calendars/analyze-calendar-diagnostic-logs" + } + + $RunInfo | Export-Excel -Path $FileName -WorksheetName "Script Info" -MoveToEnd + $script:CollectedCmdLine = $true + } + # If someone runs the script the script again logs will update, but ScriptInfo done not update. Need to add new table for each run. } function Export-TimelineExcel { @@ -68,13 +112,14 @@ $ConditionalFormatting = $( New-ConditionalText "Other ?BA" -ConditionalTextColor Orange -BackgroundColor $null New-ConditionalText "TimeService" -ConditionalTextColor Orange -BackgroundColor $null New-ConditionalText "Other REST" -ConditionalTextColor DarkRed -BackgroundColor $null - New-ConditionalText "[Unknown Rest Client]" -ConditionalTextColor DarkRed -BackgroundColor $null + New-ConditionalText "Unknown" -ConditionalTextColor DarkRed -BackgroundColor $null New-ConditionalText "ResourceBookingAssistant" -ConditionalTextColor Blue -BackgroundColor $null - #LogType - New-ConditionalText -Range "C3:C9999" -ConditionalType ContainsText -Text "Ignorable" -ConditionalTextColor DarkRed -BackgroundColor $null - New-ConditionalText -Range "C:C" -ConditionalType ContainsText -Text "Cleanup" -ConditionalTextColor DarkRed -BackgroundColor $null + #LogType -Would like to Hide "Ignorable" and "Cleanup" rows by default. + New-ConditionalText -Range "C3:C9999" -ConditionalType ContainsText -Text "Ignorable" -ConditionalTextColor Orange -BackgroundColor $null + New-ConditionalText -Range "C:C" -ConditionalType ContainsText -Text "Cleanup" -ConditionalTextColor Orange -BackgroundColor $null New-ConditionalText -Range "C:C" -ConditionalType ContainsText -Text "Sync" -ConditionalTextColor Blue -BackgroundColor $null + New-ConditionalText -Range "C:C" -ConditionalType ContainsText -Text "Core" -ConditionalTextColor Green -BackgroundColor $null # TriggerAction New-ConditionalText -Range "G:G" -ConditionalType ContainsText -Text "Create" -ConditionalTextColor Green -BackgroundColor $null @@ -92,13 +137,17 @@ $ConditionalFormatting = $( New-ConditionalText -Range "L3:L9999" -ConditionalType ContainsText -Text "Busy" -ConditionalTextColor Green -BackgroundColor $null #Shared Calendar information - New-ConditionalText -Range "Q3:Q9999" -ConditionalType NotEqual -Text "Not Shared" -ConditionalTextColor Blue -BackgroundColor $null + New-ConditionalText -Range "Q3:Q9999" -ConditionalType Equal -Text "Not Shared" -ConditionalTextColor Blue -BackgroundColor $null + New-ConditionalText -Range "Q3:Q9999" -ConditionalType Equal -Text "TRUE" -ConditionalTextColor Blue -BackgroundColor Orange #MeetingRequestType New-ConditionalText -Range "T:T" -ConditionalType ContainsText -Text "Outdated" -ConditionalTextColor DarkRed -BackgroundColor LightPink + #CalendarItemType + New-ConditionalText -Range "AA3:AA9999" -ConditionalType ContainsText -Text "RecurringMaster" -ConditionalTextColor $null -BackgroundColor Plum + #AppointmentAuxiliaryFlags - New-ConditionalText -Range "AC3:AC9999" -ConditionalType ContainsText -Text "Copied" -ConditionalTextColor DarkRed -BackgroundColor LightPink + New-ConditionalText -Range "AD3:AD9999" -ConditionalType ContainsText -Text "Copied" -ConditionalTextColor DarkRed -BackgroundColor LightPink New-ConditionalText -Range "AC3:AC9999" -ConditionalType ContainsText -Text "ForwardedAppointment" -ConditionalTextColor DarkRed -BackgroundColor $null #ResponseType @@ -118,8 +167,8 @@ function FormatHeader { Set-CellComment -Text "This is the Enhanced Calendar Logs for [$Identity] for MeetingID `n [$($script:GCDO[0].CleanGlobalObjectId)]." -Row $HeaderRow -ColumnNumber $n -Worksheet $sheet $sheet.Column(++$n) | Set-ExcelRange -Width 20 -NumberFormat "m/d/yyyy h:mm:ss" -HorizontalAlignment center #LogTimestamp Set-CellComment -Text "LogTimestamp: Time when the change was recorded in the CalLogs. This and all Times are in UTC." -Row $HeaderRow -ColumnNumber $n -Worksheet $sheet - $sheet.Column(++$n) | Set-ExcelRange -Width 11 -HorizontalAlignment center # LogType - Set-CellComment -Text "LogType: Grouping of logs so ignorable ones can be quickly filtered?" -Row $HeaderRow -ColumnNumber $n -Worksheet $sheet + $sheet.Column(++$n) | Set-ExcelRange -Width 11 -HorizontalAlignment center # LogType + Set-CellComment -Text "LogType: Core logs are what to focus on, to start with, filter all the others out." -Row $HeaderRow -ColumnNumber $n -Worksheet $sheet $sheet.Column(++$n) | Set-ExcelRange -Width 20 -HorizontalAlignment Left # SubjectProperty Set-CellComment -Text "SubjectProperty: The Subject of the Meeting." -Row $HeaderRow -ColumnNumber $n -Worksheet $sheet $sheet.Column(++$n) | Set-ExcelRange -Width 20 -HorizontalAlignment Left # Client @@ -146,7 +195,7 @@ function FormatHeader { Set-CellComment -Text "LogFolder (ParentDisplayName): The Log Folder that the CalLog was in." -Row $HeaderRow -ColumnNumber $n -Worksheet $sheet $sheet.Column(++$n) | Set-ExcelRange -Width 16 -HorizontalAlignment Left # OriginalLogFolder Set-CellComment -Text "OriginalLogFolder (OriginalParentDisplayName): The Original Log Folder that the item was in / delivered to." -Row $HeaderRow -ColumnNumber $n -Worksheet $sheet - $sheet.Column(++$n) | Set-ExcelRange -Width 15 -HorizontalAlignment Right # SharedFolderName + $sheet.Column(++$n) | Set-ExcelRange -Width 15 -HorizontalAlignment Left # SharedFolderName Set-CellComment -Text "SharedFolderName: Was this from a Modern Sharing, and if so what Folder." -Row $HeaderRow -ColumnNumber $n -Worksheet $sheet $sheet.Column(++$n) | Set-ExcelRange -Width 10 -HorizontalAlignment Left # ReceivedBy Set-CellComment -Text "ReceivedBy: The Receiver of the Calendar Item. Should always be the owner of the Mailbox." -Row $HeaderRow -ColumnNumber $n -Worksheet $sheet @@ -154,9 +203,9 @@ function FormatHeader { Set-CellComment -Text "ReceivedRepresenting: Who the item was Received for, of then the Delegate." -Row $HeaderRow -ColumnNumber $n -Worksheet $sheet $sheet.Column(++$n) | Set-ExcelRange -Width 10 -HorizontalAlignment center # MeetingRequestType Set-CellComment -Text "MeetingRequestType: The Meeting Request Type of the Meeting." -Row $HeaderRow -ColumnNumber $n -Worksheet $sheet - $sheet.Column(++$n) | Set-ExcelRange -Width 20 -NumberFormat "m/d/yyyy h:mm:ss" -HorizontalAlignment center # StartTime + $sheet.Column(++$n) | Set-ExcelRange -Width 23 -NumberFormat "m/d/yyyy h:mm:ss" -HorizontalAlignment center # StartTime Set-CellComment -Text "StartTime: The Start Time of the Meeting. This and all Times are in UTC." -Row $HeaderRow -ColumnNumber $n -Worksheet $sheet - $sheet.Column(++$n) | Set-ExcelRange -Width 20 -NumberFormat "m/d/yyyy h:mm:ss" -HorizontalAlignment center # EndTime + $sheet.Column(++$n) | Set-ExcelRange -Width 23 -NumberFormat "m/d/yyyy h:mm:ss" -HorizontalAlignment center # EndTime Set-CellComment -Text "EndTime: The End Time of the Meeting. This and all Times are in UTC." -Row $HeaderRow -ColumnNumber $n -Worksheet $sheet $sheet.Column(++$n) | Set-ExcelRange -Width 17 -NumberFormat "m/d/yyyy h:mm:ss" -HorizontalAlignment Left # OriginalStartDate Set-CellComment -Text "OriginalStartDate: The Original Start Date of the Meeting." -Row $HeaderRow -ColumnNumber $n -Worksheet $sheet @@ -170,13 +219,13 @@ function FormatHeader { Set-CellComment -Text "IsException: Is this an Exception?" -Row $HeaderRow -ColumnNumber $n -Worksheet $sheet $sheet.Column(++$n) | Set-ExcelRange -Width 20 -HorizontalAlignment Left # RecurrencePattern Set-CellComment -Text "RecurrencePattern: The Recurrence Pattern of the Meeting." -Row $HeaderRow -ColumnNumber $n -Worksheet $sheet - $sheet.Column(++$n) | Set-ExcelRange -Width 30 -HorizontalAlignment Center # AppointmentAuxiliaryFlags + $sheet.Column(++$n) | Set-ExcelRange -Width 30 -HorizontalAlignment Center # AppointmentAuxiliaryFlags Set-CellComment -Text "AppointmentAuxiliaryFlags: The Appointment Auxiliary Flags of the Meeting." -Row $HeaderRow -ColumnNumber $n -Worksheet $sheet $sheet.Column(++$n) | Set-ExcelRange -Width 30 -HorizontalAlignment Left # DisplayAttendeesAll Set-CellComment -Text "DisplayAttendeesAll: List of the Attendees of the Meeting." -Row $HeaderRow -ColumnNumber $n -Worksheet $sheet - $sheet.Column(++$n) | Set-ExcelRange -Width 10 -HorizontalAlignment center # AttendeeCount + $sheet.Column(++$n) | Set-ExcelRange -Width 10 -HorizontalAlignment center # AttendeeCount Set-CellComment -Text "AttendeeCount: The Attendee Count." -Row $HeaderRow -ColumnNumber $n -Worksheet $sheet - $sheet.Column(++$n) | Set-ExcelRange -Width 20 -HorizontalAlignment Left # AppointmentState + $sheet.Column(++$n) | Set-ExcelRange -Width 20 -HorizontalAlignment Left # AppointmentState Set-CellComment -Text "AppointmentState: The Appointment State of the Meeting." -Row $HeaderRow -ColumnNumber $n -Worksheet $sheet $sheet.Column(++$n) | Set-ExcelRange -Width 10 -HorizontalAlignment center # ResponseType Set-CellComment -Text "ResponseType: The Response Type of the Meeting." -Row $HeaderRow -ColumnNumber $n -Worksheet $sheet @@ -192,16 +241,17 @@ function FormatHeader { Set-CellComment -Text "IsAllDayEvent: Is this an All Day Event?" -Row $HeaderRow -ColumnNumber $n -Worksheet $sheet $sheet.Column(++$n) | Set-ExcelRange -Width 10 -HorizontalAlignment center # IsSeriesCancelled Set-CellComment -Text "IsSeriesCancelled: Is this a Series Cancelled Meeting?" -Row $HeaderRow -ColumnNumber $n -Worksheet $sheet - $sheet.Column(++$n) | Set-ExcelRange -Width 30 -HorizontalAlignment Left # SendMeetingMessagesDiagnostics + $sheet.Column(++$n) | Set-ExcelRange -Width 30 -HorizontalAlignment Left # SendMeetingMessagesDiagnostics Set-CellComment -Text "SendMeetingMessagesDiagnostics: Compound Property to describe why meeting was or was not sent to everyone." -Row $HeaderRow -ColumnNumber $n -Worksheet $sheet - $sheet.Column(++$n) | Set-ExcelRange -Width 50 -HorizontalAlignment Left # AttendeeCollection + $sheet.Column(++$n) | Set-ExcelRange -Width 50 -HorizontalAlignment Left # AttendeeCollection Set-CellComment -Text "AttendeeCollection: The Attendee Collection of the Meeting, use -TrackingLogs to get values." -Row $HeaderRow -ColumnNumber $n -Worksheet $sheet - $sheet.Column(++$n) | Set-ExcelRange -Width 40 -HorizontalAlignment Center # CalendarLogRequestId + $sheet.Column(++$n) | Set-ExcelRange -Width 40 -HorizontalAlignment Center # CalendarLogRequestId Set-CellComment -Text "CalendarLogRequestId: The Calendar Log Request ID of the Meeting." -Row $HeaderRow -ColumnNumber $n -Worksheet $sheet # Update header rows after all the others have been set. # Title Row $sheet.Row(1) | Set-ExcelRange -HorizontalAlignment Left + Set-CellComment -Text "For more information see: https://learn.microsoft.com/en-us/exchange/troubleshoot/calendars/analyze-calendar-diagnostic-logs." -Row 1 -ColumnNumber 1 -Worksheet $sheet # Set the Header row to be bold and left aligned $sheet.Row($HeaderRow) | Set-ExcelRange -Bold -HorizontalAlignment Left diff --git a/Calendar/CalLogHelpers/TimelineFunctions.ps1 b/Calendar/CalLogHelpers/TimelineFunctions.ps1 index 6d0eac026..7a33eefc7 100644 --- a/Calendar/CalLogHelpers/TimelineFunctions.ps1 +++ b/Calendar/CalLogHelpers/TimelineFunctions.ps1 @@ -81,6 +81,8 @@ function BuildTimeline { } Write-DashLineBoxColor " TimeLine for: [$Identity]", + "CollectionDate: $($(Get-Date).ToString("yyyy-MM-dd HH:mm:ss"))", + "ScriptVersion: $ScriptVersion", " Subject: $($script:GCDO[0].NormalizedSubject)", " Organizer: $Script:Organizer", " MeetingID: $($script:GCDO[0].CleanGlobalObjectId)" diff --git a/Calendar/Get-CalendarDiagnosticObjectsSummary.ps1 b/Calendar/Get-CalendarDiagnosticObjectsSummary.ps1 index ae6c1ee7e..e635b625b 100644 --- a/Calendar/Get-CalendarDiagnosticObjectsSummary.ps1 +++ b/Calendar/Get-CalendarDiagnosticObjectsSummary.ps1 @@ -82,6 +82,11 @@ if (Test-ScriptVersion -AutoUpdate -VersionsUrl "https://aka.ms/CL-VersionsUrl" return } +$script:command = $MyInvocation +Write-Host -ForegroundColor Blue "The script was started with the following command line:" +Write-Host -ForegroundColor Blue "Name:" $command.MyCommand.name +Write-Host -ForegroundColor Blue "Command Line:" $command.line + Write-Verbose "Script Versions: $BuildVersion" # =================================================================================================== @@ -191,12 +196,13 @@ if (-not ([string]::IsNullOrEmpty($Subject)) ) { } Write-DashLineBoxColor "Hope this script was helpful in getting and understanding the Calendar Logs.", +"More Info on Getting the logs: https://learn.microsoft.com/en-us/exchange/troubleshoot/calendars/get-calendar-diagnostic-logs", +"and on Analyzing the logs: https://learn.microsoft.com/en-us/exchange/troubleshoot/calendars/analyze-calendar-diagnostic-logs", "If you have issues or suggestion for this script, please send them to: ", -"`t CalLogFormatterDevs@microsoft.com" -Color Yellow -DashChar = +"`t CalLogFormatterDevs@microsoft.com" -Color Yellow -DashChar "=" if ($ExportToExcel.IsPresent) { Write-Host Write-Host -ForegroundColor Blue -NoNewline "All Calendar Logs are saved to: " Write-Host -ForegroundColor Yellow ".\$Filename" - Write-Host }