How to get outlook recurring meeting items in powershell - powershell

I am using below script to pull outlook calendar items and it works for events that occur once. It fails for recurring meeting invites as the start date on it would be when the invite was sent and not the occurrence start time. Also, i dont see any variable referencing the occurrence end date, so cant dynamically lookup for the occurrence. Any suggestions on how to get that?
Function Get-OutlookCalendar {
Add-type -assembly “Microsoft.Office.Interop.Outlook” | out-null
$olFolders = “Microsoft.Office.Interop.Outlook.OlDefaultFolders” -as [type]
$outlook = new-object -comobject outlook.application
$namespace = $outlook.GetNameSpace(“MAPI”)
$folder = $namespace.getDefaultFolder($olFolders::olFolderCalendar)
$folder.items |
Select-Object -Property start,RequiredAttendees, Subject, IsRecurring, Organizer,ConversationID,RecurrenceState,OptionalAttendees,Body
}
$start_date=(Get-date).AddDays(-2) |Get-Date -Format 'MM/dd/yyyy'
$end_date=Get-Date -Format 'MM/dd/yyyy'
Get-OutlookCalendar| where-object { $_.start -gt [datetime]$start_date -and $_.start -le [datetime]$end_date } | sort-object start

You can try below switch:
switch ($item.IsRecurring)
{
$false {
if ($item.start -ge $dt -and $item.start -lt ($dt).AddDays(1))
{
$item | Select-Object -Property Subject, Start, Duration, Location, RequiredAttendees
}
}
$true {
try {
$rec=$item.GetRecurrencePattern()
$recitem=$rec.GetOccurrence($dt.ToString("yyyy-MM-dd") + " " + $item.Start.ToString("HH:mm"))
$recitem | Select-Object -Property Subject, Start, Duration, Location, RequiredAttendees, IsRecurring
}
catch {}
finally {}
}
}

Related

Downloading .xlsx attachment from Outlook of Specific Date using Powershell

I have the below script. This $Tests shows the list of .xlsx attachment of specific date but is not able to download and throws an error. Please find the below script.
Add-type -assembly "Microsoft.Office.Interop.Outlook"
$olDefaultFolders = "Microsoft.Office.Interop.Outlook.olDefaultFolders" -as [type]
$outlook = New-Object -comobject Outlook.Application
$mapi = $outlook.GetNameSpace(“MAPI”)
$inbox = $mapi.GetDefaultFolder(6)
$FilePath= "c:\temp\Test\"
$subfolder = $inbox.Folders | Where-Object {$_.Name -eq “Test”}
$mail=$subfolder.Items |Select-Object -Property "ReceivedTime",#{name="Attachments";expression={$_.Attachments|%{$_.DisplayName}}} | Where-Object{$_.attachments -match ".xlsx" -and ($_.receivedtime -match "9/15/2020")} | Select-Object "attachments"
$Test = $mail.attachments
foreach ($out in $test) {$_.attachments|foreach {
Write-Host $_.filename
$Filename = $_.filename
If ($out.Contains("xlsx")) {
$_.saveasfile((Join-Path $FilePath "$out")) }}}
I am able to filter the .xlsx Attachments with Specific Date. But after this, I don't know how to save/download them.
Working with com objects can be rather frustrating in powershell. I recommend you get extremely familiar with Get-Member. You really have to interrogate each object. I've simplified your script as well as tested thoroughly. It will download each matching attachment (name) from each match email (received date)
Add-type -assembly "Microsoft.Office.Interop.Outlook"
$olDefaultFolders = "Microsoft.Office.Interop.Outlook.olDefaultFolders" -as [type]
$outlook = New-Object -comobject Outlook.Application
$mapi = $outlook.GetNameSpace(“MAPI”)
$inbox = $mapi.GetDefaultFolder(6)
$FilePath= "c:\temp\Test\"
$subfolder.Items | Where-Object {$_.receivedtime -match "9/20/2020" -and $($_.attachments).filename -match '.xlsx'} | foreach {
$filename = $($_.attachments | where filename -match '.xlsx').filename
foreach($file in $filename)
{
Write-Host Downloading $file to $filepath -ForegroundColor green
$outpath = join-path $filepath $file
$($_.attachments).saveasfile($outpath)
}
}
You may use this for more of an "in-line" approach.
Add-type -assembly "Microsoft.Office.Interop.Outlook"
$olDefaultFolders = "Microsoft.Office.Interop.Outlook.olDefaultFolders" -as [type]
$outlook = New-Object -comobject Outlook.Application
$mapi = $outlook.GetNameSpace(“MAPI”)
$inbox = $mapi.GetDefaultFolder(6)
$FilePath= "c:\temp\Test\"
$subfolder.Items | Where-Object {$_.receivedtime -match "9/20/2020" -and $($_.attachments).filename -match '.xlsx'} | foreach {
foreach($attachment in $($_.attachments | where filename -match '.xlsx'))
{
Write-Host Downloading $attachment.filename to $filepath -ForegroundColor green
$attachment.SaveAsFile((join-path $FilePath $attachment.filename))
}
}

PowerShell won't iterate through a document

A PowerShell script won't iterate through a document to change each hyperlink in the document.
The script runs through a document library on SharePoint online and can open each document in the library. Then it should iterate through each document and pull any hyperlinks that it finds and then split the hyperlink into two parts. The script should then add the second half onto a new URL and update the Address to be the new, updated URL.
add-type -AssemblyName "Microsoft.Office.Interop.Word"
$wdunits = “Microsoft.Office.Interop.Word.wdunits” -as [type]
$donotsave = “Microsoft.Office.Interop.Word.wdDoNotSaveChanges” -as [type]
$save = “Microsoft.Office.Interop.Word.wdSaveChanges” -as [type]
$application = New-Object -ComObject Word.Application
$application.Visible = $false
$tenancy = "https://tenancy.sharepoint.com"
$url = "https://tenancy.sharepoint.com/sites/siteName/"
Connect-PnPOnline -Url $url -UseWebLogin
$library = Get-PnPList | Where-Object {$_.Title -eq "libraryName"}
$items = Get-PnPListItem -List $library
foreach ($item in $items) {
if ($item["FileLeafRef"] -match ".doc*") {
Write-Host "File Name: "$item["FileLeafRef"]
$item["FileLeafRef"]
$item["FileRef"]
Write-Host `
$documentLocation = "https://tenancy.sharepoint.com"+$item["FileRef"]
$documentLocation
$document = $application.Documents.Open($documentLocation)
$docURLS = #($document.Hyperlinks)
$docURLS | foreach{
Start-Sleep -Seconds 7
$newURI = ([uri]$_.address).AbsoluteUri
$result = $newURI.Split("=") | Select-Object -Skip 1 -First 1
$result
$newUrl = "https://tenancy.sharepoint.com/sites/siteName/_layouts/DocIdRedir.aspx?ID="+$result
$_.address = $newUrl
Write-Verbose ("Updating {0} to {1}" -f $_.Address,$newUrl) -Verbose
}
$document.save()
$document.close([Ref]$save)
$item.File.Update()
}
}
$application.quit()
Disconnect-PnPOnline
The script can currently iterate through the library and open each document, the issue comes when there are multiple hyperlinks in the document.
It changes the first URL correctly, but every other link after that receives the following errors:
Object has been deleted.
At C:\filepath.ps1 :36 char:5
+ $_.address = $newUrl
The object invoked has disconnected from its clients. (Exception from HRESULT: 0x80010108 (RPC_E_DISCONNECTED))
At C:\filepath.ps1:39 char:9
+ $document.save()
The object invoked has disconnected from its clients. (Exception from HRESULT: 0x80010108 (RPC_E_DISCONNECTED))
At C:\filepath.ps1:40 char:9
+ $document.close([Ref]$save)
You cannot call a method on a null-valued expression.
At C:\filepath.ps1:33 char:5
+ $result = $newURI.Split("=") | Select-Object -Skip 1 -First 1
If the $_.address value like "/sites/team?ID=1", the $newURI will null, then run $newURI.Split("=") | Select-Object -Skip 1 -First 1 will get "You cannot call a method on a null-valued expression".
You can check if the $newURI is null before use $newURI.Split method.
Or we can replace the code below.
$newURI = ([uri]$_.address).AbsoluteUri
$result = $newURI.Split("=") | Select-Object -Skip 1 -First 1
with
if($_.Address)
{
$result = $_.Address.Split("=") | Select-Object -Skip 1 -First 1
}
else
{
$_
}

how to access email from a shared outlook mailbox

I need to get a the latest email from a shared outlook mailbox.
The shared inbox is eg. "Server Backup" and the email that i want to get is located
inbox\backup report\
Here is some code that I can access my Inbox but can't do it for a subfolder
$Outlook = New-Object -ComObject Outlook.Application
$OutlookFolders = $Outlook.Session.Folders.Item(1).Folders
$OutlookInbox = $Outlook.session.GetDefaultFolder(6)
#read the latest email
$latestmail=$OutlookInbox.items | select -last 1
What you are after is to navigate a little further down.
$outlook = New-Object -Com Outlook.Application
$MAPI = $Outlook.GetNamespace("MAPI")
# Gets all mailboxes tied to the account
$Mailbox = $MAPI.Folders("SharedEmail#Company.com")
# Gets the Inbox folder
$Inbox = $mailbox.Folders("Inbox").Folders("backup report")
# Shows all emails from the Inbox
$contents = $Inbox.Items
$contents.Sort("ReceivedTime", $true)
$contents | select Subject, SenderName, CreationTime -First 1
EDIT: Since the above didn't work, try the below. It will search for the Inbox, then pipe that through to look for the Server Backup.
$Outlook = New-Object -ComObject Outlook.Application
$OutlookFolders = $Outlook.Session.Folders.Item(1).Folders
(($OutlookFolders | Where-Object {$_.FolderPath -like "*Inbox*"}).Folders | `
Where-Object {$_.FolderPath -like "*Server Backup*"}).Items | `
select Subject, SenderName -Last 1
$outlook = New-Object -comobject outlook.application
$namespace = $outlook.application.GetNamespace("MAPI")
$folder = $namespace.GetDefaultFolder(6)
#goto the inbox\backup eport and select the latest email and place it in variable
#$newreport
$newreport = $namespace.Folders.Item("Server
Backup").Folders.Item('Inbox').Folders.item('backups').items | select -first 1
Add-Type -assembly "Microsoft.Office.Interop.Outlook"
Add-Type -assembly "System.Runtime.Interopservices"
$outlook = new-object -com outlook.application;
$namespace = application.GetNameSpace("MAPI")
$i = 1
$Sender = "abc#123.com" # email id
$Sender1 = "cde#123.com" # email id
$myRecipient = $namespace.CreateRecipient("Email Id") # Email id of the shared mail box
$inbox = $Namespace.GetSharedDefaultFolder($myRecipient ,[Microsoft.Office.Interop.Outlook.OlDefaultFolders]::olFolderInbox)
# Save email contect to a file in a location
$inbox.Items | ?{$_.SenderName -match $Sender -and $_.UnRead -eq "No" } | sort receivedtime -desc |
%{
$Filepath = "C:\temp\" + "email" + $filecount +".txt"
echo $_.body | Out-File -FilePath $Filepath #do stuff with body
$i = $i + 1
$filecount = " (" + $i + ")"
$_.Unread= $False #mark as read
}
# Save email attachment to a location
$inbox.Items | ?{$_.SenderName -match $Sender1 -and $_.UnRead -eq "No" } | sort receivedtime -desc |
%{
$Filepath = "C:\Temp\"
$_.attachments| ?{$_.saveasfile((Join-Path $filePath $_.FileName)) }
$_.Unread= $False #mark as read
}

Display duration of outlook appointment?

I am unable to display the duration of each appointment in my list box. I'm trying to print in the listbox 11:30; (60) B/H or 11:30-12:30; B/H, however unsure of how to do this, please may someone help?
I have tried $Duration and $._Duration, putting them between "[...]Start.ToString('HH:mm'), $_.Subject } " 'HH:mm' and $_. subject however these haven't been successful
#listbox for calendar entry
$Listboxcal= New-Object System.Windows.Forms.ListBox
$Listboxcal.Location = New-Object System.Drawing.Size(10,55)
$Listboxcal.Size = New-Object System.Drawing.Size(80,270)
$Listboxcal.Height = 150
add-type -assembly “Microsoft.Office.Interop.Outlook” | out-null
$outlook = new-object -comobject outlook.application
$namespace = $outlook.GetNameSpace(“MAPI”)
# https://learn.microsoft.com/en-us/dotnet/api/microsoft.office.interop.outlook.oldefaultfolders?view=outlook-pia
# Calendar = olFolderCalendar = 9
$calendar = $namespace.GetDefaultFolder(9)
# get today's calendar items!
$cItems = $calendar.Items | Where-Object {$_.Start -ge $(Get-Date $StrCaldate) -and $_.Start -le $(Get-Date $StrCaldate).AddDays(1)} |Sort-Object -Property Start | foreach { "{0}; {1}" -f $_.Start.ToString('HH:mm'), $_.Subject }
ForEach ($calendars in $cItems) {[void]$Listboxcal.Items.Add($calendars)}
The calendar appointments have a duration property. You just need to add that to the $cItems variable
$cItems = $calendar.Items | Where-Object {$_.Start -ge $(Get-Date $StrCaldate) -and $_.Start -le $(Get-Date $StrCaldate).AddDays(1)} |Sort-Object -Property Start | foreach { "{0}; ({1}) {2}" -f $_.Start.ToString('HH:mm'), $_.Duration, $_.Subject }
When I run this on my own calendar, I get the following:
16:30; (60) Appointment 1
18:30; (120) Appointment 2
It is working for me with the Duration property:
$calendar.Items | Select -First 1 | %{ $_.StartUTC.Tostring("hh:mm") `
+ "; (" + $_.Duration + ") " + $_.Subject}

calendar, exporting Outlook emails to CSV

My job is to export emails from Outlook default inbox folder to CSV file.
Here is the script:
cls
Function Get-OutlookInBox #getoutlookcontent
{
Add-type -assembly "Microsoft.Office.Interop.Outlook" | out-null
$olFolders = "Microsoft.Office.Interop.Outlook.olDefaultFolders" -as [type]
$outlook = new-object -comobject outlook.application
$namespace = $outlook.GetNameSpace("MAPI")
$folder = $namespace.getDefaultFolder($olFolders::olFolderInBox)
$folder.items |
Select-Object -Property Subject, ReceivedTime, SenderName, to
}
write-host "select date greater or equal to and hit enter"
Function date1
#calendar
{
[void]
[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
$objForm = New-Object Windows.Forms.Form
$objForm.Text = "Select a Date"
$objForm.Size = New-Object Drawing.Size #(190,190)
$objForm.StartPosition = "CenterScreen"
$objForm.KeyPreview = $True
$objForm.Add_KeyDown({
if ($_.KeyCode -eq "Enter")
{
$global:dtmDate=$objCalendar.SelectionStart
$objForm.Close()
}
})
$objForm.Add_KeyDown({
if ($_.KeyCode -eq "Escape")
{
$objForm.Close()
}
})
$objCalendar = New-Object System.Windows.Forms.MonthCalendar
$objCalendar.ShowTodayCircle = $False
$objCalendar.MaxSelectionCount = 1
$objForm.Controls.Add($objCalendar)
$objForm.Topmost = $True
$objForm.Add_Shown({$objForm.Activate()})
[void] $objForm.ShowDialog()
}
date1
$gt = (get-date).tostring() #getcurrent date
Get-OutlookInbox | #exporting
where { $_.ReceivedTime -gt [datetime] "$globa:dtmdate" -AND $_.ReceivedTime
-le
[datetime] "$gt" } | Export-Csv D:\Users\XXX\Desktop\test1.csv -
Encoding ASCII -NoTypeInformation -force
Parts of code are works perfectly fine separately.
I am able export emails when i am putting date in to code like that:
Get-OutlookInbox | #exporting
where { $_.ReceivedTime -gt [datetime] "8/21/2017" -AND $_.ReceivedTime
-le
[datetime] "8/24/2017" } | Export-Csv D:\Users\XXX\Desktop\test1.csv -
Encoding ASCII -NoTypeInformation -force
but while I am trying to put variable instead of exact values I am getting error msg Cannot convert value "" to type "System.DateTime". Error: "String was not recognized as a valid DateTime."
My question is how to convert variable in to datetime.
Second thing which I notice in this part of code
Get-OutlookInbox | #exporting
where { $_.ReceivedTime -gt [datetime] "8/21/2017" -AND $_.ReceivedTime
-le
[datetime] "8/24/2017" } | Export-Csv D:\Users\XXX\Desktop\test1.csv -
Encoding ASCII -NoTypeInformation -force
In that case operator -gt works perfectly fine for me even it shouldn't. Script exporting emails from date which I set not day after. But second part -le works like less than not less and equal. Which is confusing since I need to set date day+1. I have tried to put several comparison operators but result is always the same.