how to access email from a shared outlook mailbox - powershell

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
}

Related

Powershell script to display a list of users and respective permissions of a shared mailbox

I thought I'd post this here just in case I'm barking up the wrong tree. I'm looking to put together a Powershell script that can list all of the members of a shared mailbox and their respective permissions (Limited to "UserName", "Name", "Mailbox", "Full Access", "Send As", "SOBO"). My plan is for the script to ask for an email address and output to look something like this:
User Name Mailbox Full Access Send As SOBO Success
---- ---- ------- ----------- ------- ----- --------
ACB123 Smith, James Examplebox Yes Yes No Success
ABC213 Smith, Pete Examplebox Yes No Yes Success
I was surprised when I couldn't find anything online that is even similar to this.
My script, so far, grabs a list of users that have Full Access (well, it's supposed to, it seems to grab the lesser permissions too, but this actually serves my purpose). The script then strips the collected info down to usernames, then runs a for each to gather information to complete the table above. It runs an AD query for the display names as running Get-ADPermission is not an option.
I haven't got as far as to do the Send As and SoBo parts of the table because I can't get the table to be bigger than 4 columns before it turns into a list, instead of a table. I know there's the Format-Table command but I can't seem to integrate it into my current pscustomobject setup - which is currently set up to split successful queries from failed ones.
This is what I have so far, it's pretty dirty, but this is just my best guess to how something like this should work:
import-moduleactivedirectory
Install-ModuleExchangeOnlineManagement
Connect-ExchangeOnline-ShowBanner:$false
Clear-Host
$ErrorActionPreference='Stop'
$status=[System.Collections.Generic.List[pscustomobject]]::new()
$Entered_email=$Entered_email.trim()
$Collect_SendAs=Get-Mailbox$Entered_email-resultsizeunlimited|Get-RecipientPermission|where {($_.trustee -ne"NT AUTHORITY\SELF")} |where {($_.trustee -match"#")} |selectTrustee
$Collect_users=Get-Mailbox-Identity$Entered_email-ResultSize:Unlimited|Get-MailboxPermission|?{($_.IsInherited -eq$False) -and-not ($_.User -match"NT AUTHORITY")} #|select -ExpandProperty user
$status=foreach($Aliasin$Collect_users)
{
try
{
$User= ($Alias.User.Split("#")[0])
$Access=$Alias.AccessRights
$User_name=Get-ADUser-identity$User-propertiesDisplayName|select-expandpropertyDisplayName
# $Has_SendAs = ($Collect_SendAs.Split("#")[0])
# if ($User -like "*Has_SendAs*") {$User_SendAs = "yes"
# }else{$User_SendAs = "No"}
[pscustomobject]#{
User =$user
Name =$user_name.Split(',')[1..0]-join' '
Mailbox =$Entered_email
'Access Rights'=$Access.Trim("{","}")
'Has Send As'=$User_SendAs
Status ='SUCCESS'
}
}
catch
{
[pscustomobject]#{
User =$user
Status ='FAILED'
Message =$_.Exception.Message
}
}
}
$success,$failed=$status.Where({$_.Status -eq'SUCCESS'},'Split')
$success|selectUser,Name,Mailbox,'Access Rights','Has Send As'|Format-Table|Out-String|Write-Host-ForegroundColorGreen
$failed |selectUser,Message|Out-String|Write-Host-ForegroundColorRed
$SoBo=Get-Mailbox$Entered_email|select #{l='SendOnBehalfOf';e={$_.GrantSendOnBehalfTo -join"`n"}}
$Sobo_Output=$SoBo-replace"#{SendOnBehalfOf=",''-replace"}",''
If ($Sobo_Output-ge1) {
Write-Host"Users With Send on Belhalf Permissions"-ForegroundColorGreen
Write-Host"--------------------------------------"-ForegroundColorGreen
Write-Host$SoBo_Output-ForegroundColorGreen
Write-Host""
}else{
Write-Host"Users With Send on Belhalf Permissions"-ForegroundColorGreen
Write-Host"--------------------------------------"-ForegroundColorGreen
Write-Host"No users found with this permission level"-ForegroundColorGreen
Write-Host""
}
Disconnect-ExchangeOnline-Confirm:$false-InformationActionIgnore-ErrorActionSilentlyContinue
Pause
Any advice would be appreciated at this stage, I definitely could use help with the table, I could probably figure out how to add the Send As and SoBo searches, but if anyone knows some really efficient ones please let me know.
Thanks in advance.
UPDATED
I've amended the script above, because I couldn't figure out how toadd another message.
I've taken on board the changes suggested by #TheMadTechnician, and abandoned the idea of adding SoBo to the table as the SoBo users information is saved as some weird string of names and usernames, so I've rigged it so that this information pops out on a separate table below the access level table.
I've added line 10 ($Collect_SendAs), this is a line that can pull the email addresses (username#domain.com) of all users that have Send As access to the mailbox, I'm looking to get this integrated into the access level table and have made a few wrong turns trying to do this (lines 22-24 are my latest failed attempts to do this).
What I would like to do with the info collected in line 10 is to strip out the #domain part, then compare it with the usernames extracted in line 11, if there match, add a "yes" to the Send As column for the user, and if there is no match, add a "No".
If anyone can help with this, that would be amazing.
UPDATE
Think I've got it:
Import-Module ActiveDirectory
Install-Module ExchangeOnlineManagement
Connect-ExchangeOnline -ShowBanner:$false
Clear-Host
$ErrorActionPreference = 'Stop'
$status = [System.Collections.Generic.List[pscustomobject]]::new()
$Entered_email = Read-host "Enter a mailbox address"
$Entered_email = $Entered_email.trim()
$Collect_SendAs = Get-Mailbox $Entered_email -resultsize:unlimited | Get-RecipientPermission | where {($_.trustee -ne "NT AUTHORITY\SELF")} | where {($_.trustee -match "#")} | select -ExpandProperty Trustee
$Collect_users = Get-Mailbox -Identity $Entered_email -ResultSize:Unlimited | Get-MailboxPermission | ?{($_.IsInherited -eq $False) -and -not ($_.User -match "NT AUTHORITY")}
$status = foreach ($Alias in $Collect_users)
{
try
{
$User = ($Alias.User.Split("#")[0])
$User_name = Get-ADUser -identity $User -properties DisplayName | select -expandproperty DisplayName
if ($Collect_SendAs -match $User) {$User_SendAs = "yes"
}else{$User_SendAs = "No"}
$Access = $Alias.AccessRights
[pscustomobject]#{
User = $user
Name = $user_name.Split(',')[1..0]-join' '
Mailbox = $Entered_email
'Access Rights' = $Access.Trim("{","}")
'Has Send As' = $User_SendAs
Status = 'SUCCESS'
}
}
catch
{
[pscustomobject]#{
User = $user
Status = 'FAILED'
Message = $_.Exception.Message
}
}
}
$success, $failed = $status.Where({$_.Status -eq 'SUCCESS'},'Split')
$success | select User, Name, Mailbox,'Access Rights','Has Send As' | Format-Table | Out-String | Write-Host -ForegroundColor Green
$failed | select User, Message | Out-String | Write-Host -ForegroundColor Red
$SoBo = Get-Mailbox $Entered_email |select #{l='SendOnBehalfOf';e={$_.GrantSendOnBehalfTo -join"`n"}}
$Sobo_Output = $SoBo -replace "#{SendOnBehalfOf=",'' -replace"}",''
If ($Sobo_Output -ge 1) {
Write-Host "Users With Send on Behalf Permissions" -ForegroundColor Green
Write-Host "--------------------------------------" -ForegroundColor Green
Write-Host $SoBo_Output -ForegroundColor Green
Write-Host ""
}else{
Write-Host "Users With Send on Behalf Permissions" -ForegroundColor Green
Write-Host "--------------------------------------" -ForegroundColor Green
Write-Host "No users found with this permission level" -ForegroundColor Green
Write-Host ""
}
Disconnect-ExchangeOnline -Confirm:$false -InformationAction Ignore -ErrorAction SilentlyContinue
Pause
Thanks a lot to everyone who posted.
You are duplicating efforts a LOT with this. Line 9 would return you all the users that have access and the access they have, but you discard everything but the user's account name, and then later loop through those users and get their access one at a time. Here I keep that info, then use it inside the loop to reduce calls to Exchange to get perms again and again. I also changed a variable name since you re-used $User for different things which can be very confusing.
import-module activedirectory
Install-Module ExchangeOnlineManagement
Connect-ExchangeOnline -ShowBanner:$false
Clear-Host
$ErrorActionPreference='Stop'
$status=[System.Collections.Generic.List[pscustomobject]]::new()
$Entered_email = Read-host "Enter a mailbox address"
$Collect_users = Get-Mailbox -Identity $Entered_email -ResultSize:Unlimited|Get-MailboxPermission|?{($_.IsInherited -eq $False) -and -not ($_.User -match"NT AUTHORITY")} #|select -ExpandProperty user
$status=foreach($Alias in $Collect_users)
{
try
{
$User= ($Alias.User.Split("#")[0])
$Access=$Alias.AccessRights
if ($Access -like "*FullAccess*") {$Access_Result="yes"
}else{$Access_Result="No"}
$User_name = Get-ADUser -identity $User -properties DisplayName|select -expandproperty DisplayName
[pscustomobject]#{
User = $user
Name = $user_name
Mailbox = $Entered_email.Split("#")[0]
'Full Access'= $Access_Result
Status ='SUCCESS'
}
}
catch
{
[pscustomobject]#{
User = $user.user
Status ='FAILED'
Message = $_.Exception.Message
}
}
}
$success,$failed=$status.Where({$_.Status -eq'SUCCESS'},'Split')
$success|Out-String|Write-Host-ForegroundColorGreen
$failed |Out-String|Write-Host-ForegroundColorRed
Pause
I started making a "mailbox manager" gui last year and never got around to finishing it, but maybe there's some useful stuff you could pull from it. The last thing I was trying to add was who has access/sendas TO the selected account (bottom part of the gui), which isn't quite working...but the first part (see what accounts/sendas an account has permission for) is working.
The eventual idea was to add a "save" button in so you could check/uncheck the tickboxes as you'd like in the gui and it'd adjust the permissions.
#Init
$logpath = "C:\Scripts\Logs\MailboxPermissions.log"
#Create session on 365 Exchange server
if ((Get-PSSession).Computername -notmatch "outlook.office365.com"){Connect-365}#or: Connect-ExchangeOnline -ShowBanner:$false
$UPNs = Get-Mailbox -Identity * | select -ExpandProperty UserPrincipalName
$AccToDatasource = [System.Collections.ArrayList]::new()
$BS1 = [System.Windows.Forms.BindingSource]::new()
$BS1.DataSource = $AccToDatasource
$AccByDatasource = [System.Collections.ArrayList]::new()
$BS2 = [System.Windows.Forms.BindingSource]::new()
$BS2.DataSource = $AccByDatasource
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.Application]::EnableVisualStyles()
$get_permissions={
$AccToDatasource.Clear()
$AccByDatasource.Clear()
$user = $ComboBox1.Text
write-host "Checking $($user)"
Get-EXOMailboxPermission -Identity $user | ?{$_.User -ne "NT AUTHORITY\SELF"} | %{
$tablerow = New-Object psobject
$data = #{Email="$($_.User)";Inbox=1;SendAs=0}
$tablerow | Add-Member -NotePropertyMembers $data -TypeName tablerow
$AccToDatasource.Add($tablerow)
}
Get-EXORecipientPermission -Identity $user | ?{$_.Trustee -ne "NT AUTHORITY\SELF"} | %{
$indx = [array]::IndexOf($AccToDatasource.Email,$_.Trustee)
if($indx -ne -1){
$AccToDatasource[$indx].SendAs = 1
}else{
$tablerow = New-Object psobject
$data = #{Email="$($_.Trustee)";SendAs=1}
$tablerow | Add-Member -NotePropertyMembers $data -TypeName tablerow
$AccToDatasource.Add($tablerow)
}
}
$BS1.ResetBindings($true)
<##Attempt 1
Get-EXOMailbox -MailboxPlan "ExchangeOnlineEnterprise" -Properties UserPrincipalName | select -ExpandProperty UserPrincipalName | %{
Start-ThreadJob {
Get-EXOMailboxPermission -Identity $using:_ -User $using:user -ErrorAction SilentlyContinue
} | Wait-Job | Receive-Job | %{
$tablerow = New-Object psobject
$data = #{Email="$($_.Trustee)";Inbox=1;SendAs=0}
$tablerow | Add-Member -NotePropertyMembers $data -TypeName tablerow
$AccByDatasource.Add($tablerow)
}
}
$BS2.ResetBindings($true)
#>
#Attempt 2
Get-EXOMailbox -MailboxPlan "ExchangeOnlineEnterprise" -Properties UserPrincipalName | select -ExpandProperty UserPrincipalName | %{Start-ThreadJob {Get-EXOMailboxPermission -Identity $using:_ -User $using:user -ErrorAction SilentlyContinue}} | Wait-Job | Receive-Job | %{
$tablerow = New-Object psobject
$data = #{Email="$($_.Trustee)";Inbox=1;SendAs=0}
$tablerow | Add-Member -NotePropertyMembers $data -TypeName tablerow
$AccByDatasource.Add($tablerow)
}
$BS2.ResetBindings($true)
}
#Form Init
$Form = New-Object system.Windows.Forms.Form
$Form.ClientSize = New-Object System.Drawing.Point(600,650)
$Form.text = "Mailbox Manager"
$Form.TopMost = $false
$ComboBox1 = New-Object system.Windows.Forms.ComboBox
$ComboBox1.width = 370
$ComboBox1.height = 30
$ComboBox1.location = New-Object System.Drawing.Point(137,25)
$ComboBox1.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10)
$ComboBox1.Items.AddRange($UPNs)
$ComboBox1.AutoCompleteSource = "ListItems"
$ComboBox1.AutoCompleteMode = "SuggestAppend"
#$ComboBox1.add_SelectedIndexChanged({
# if ($ComboBox1.Text.Length -gt 10){
# get-permissions -user $ComboBox1.Text
# }
#})
$Label1 = New-Object system.Windows.Forms.Label
$Label1.text = "Select 365 User:"
$Label1.AutoSize = $true
$Label1.width = 25
$Label1.height = 30
$Label1.location = New-Object System.Drawing.Point(14,25)
$Label1.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10)
$Label2 = New-Object system.Windows.Forms.Label
$Label2.text = "Has access to:"
$Label2.AutoSize = $true
$Label2.width = 25
$Label2.height = 30
$Label2.location = New-Object System.Drawing.Point(14,55)
$Label2.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10)
$Label3 = New-Object system.Windows.Forms.Label
$Label3.text = "Accessible by:"
$Label3.AutoSize = $true
$Label3.width = 25
$Label3.height = 30
$Label3.location = New-Object System.Drawing.Point(14,358)
$Label3.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10)
$DataGridView1 = New-Object system.Windows.Forms.DataGridView
$DataGridView1.text = "Emails"
$DataGridView1.width = 560
$DataGridView1.height = 250
$DataGridView1.AutoGenerateColumns = $false
$DataGridView1.Columns.Add((new-object -TypeName System.Windows.Forms.DataGridViewTextBoxColumn -Property #{"Name"="Email"})) | Out-Null
$DataGridView1.Columns['Email'].DataPropertyName = "Email"
$DataGridView1.Columns.Add((new-object -TypeName System.Windows.Forms.DataGridViewCheckBoxColumn -Property #{"Name"="Inbox"})) | Out-Null
$DataGridView1.Columns['Inbox'].DataPropertyName = "Inbox"
$DataGridView1.Columns.Add((new-object -TypeName System.Windows.Forms.DataGridViewCheckBoxColumn -Property #{"Name"="SendAs"})) | Out-Null
$DataGridView1.Columns['SendAs'].DataPropertyName = "SendAs"
$DataGridView1.Columns.Add((new-object -TypeName System.Windows.Forms.DataGridViewTextBoxColumn -Property #{"Name"="Start"})) | Out-Null
$DataGridView1.Columns['Start'].DataPropertyName = "Start"
$DataGridView1.Columns.Add((new-object -TypeName System.Windows.Forms.DataGridViewTextBoxColumn -Property #{"Name"="End"})) | Out-Null
$DataGridView1.Columns['End'].DataPropertyName = "End"
$DataGridView1.ColumnHeadersVisible = $true
$DataGridView1.AutoSizeColumnsMode = 10
$DataGridView1.DataSource = $BS1
$DataGridView1.location = New-Object System.Drawing.Point(11,82)
$DataGridView1.BackColor = [System.Drawing.ColorTranslator]::FromHtml("#e0dede")
$DataGridView1.add_DataError({write-host "hit error"})
$DataGridView2 = New-Object system.Windows.Forms.DataGridView
$DataGridView2.width = 560
$DataGridView2.height = 250
$DataGridView2.location = New-Object System.Drawing.Point(9,383)
$DataGridView2.AutoGenerateColumns = $false
$DataGridView2.Columns.Add((new-object -TypeName System.Windows.Forms.DataGridViewTextBoxColumn -Property #{"Name"="Email"})) | Out-Null
$DataGridView2.Columns['Email'].DataPropertyName = "Email"
$DataGridView2.Columns.Add((new-object -TypeName System.Windows.Forms.DataGridViewCheckBoxColumn -Property #{"Name"="Inbox"})) | Out-Null
$DataGridView2.Columns['Inbox'].DataPropertyName = "Inbox"
$DataGridView2.Columns.Add((new-object -TypeName System.Windows.Forms.DataGridViewCheckBoxColumn -Property #{"Name"="SendAs"})) | Out-Null
$DataGridView2.Columns['SendAs'].DataPropertyName = "SendAs"
$DataGridView2.Columns.Add((new-object -TypeName System.Windows.Forms.DataGridViewTextBoxColumn -Property #{"Name"="Start"})) | Out-Null
$DataGridView2.Columns['Start'].DataPropertyName = "Start"
$DataGridView2.Columns.Add((new-object -TypeName System.Windows.Forms.DataGridViewTextBoxColumn -Property #{"Name"="End"})) | Out-Null
$DataGridView2.Columns['End'].DataPropertyName = "End"
$DataGridView2.ColumnHeadersVisible = $true
$DataGridView2.AutoSizeColumnsMode = 10
$DataGridView2.DataSource = $BS2
$DataGridView2.BackColor = [System.Drawing.ColorTranslator]::FromHtml("#e0dede")
$loadButton = New-Object system.Windows.Forms.Button
$loadButton.text = "Load"
$loadButton.width = 60
$loadButton.height = 30
$loadButton.location = New-Object System.Drawing.Point(509,25)
$loadButton.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10)
$loadButton.Add_Click($get_permissions)
$saveButton = New-Object system.Windows.Forms.Button
$saveButton.text = "Save"
$saveButton.width = 60
$saveButton.height = 30
$saveButton.location = New-Object System.Drawing.Point(509,341)
$saveButton.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10)
$Form.controls.AddRange(#($Label1,$Label2,$Label3,$ComboBox1,$DataGridView1,$DataGridView2,$saveButton, $loadButton))
[void]$Form.ShowDialog()

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 Error: "There is not enough memory or disk to complete the operation"

I'm running a powershell script to read multiple word documents. When running to around 700 documents, it shows error "There is not enough memory or disk to complete the operation".
Here is my code
$excel = New-Object -ComObject Excel.application
$source = 'powershell/attachments'
$docs = Get-ChildItem -Path $source -Recurse -Filter *cover*.docx
$XL = New-Object -ComObject Excel.Application
#Open the workbook
$WB = $XL.Workbooks.Open("powershell/result.xlsx")
#Activate Sheet1, pipe to Out-Null to avoid 'True' output to screen
$WB.Sheets.Item("Sheet1").Activate() | Out-Null
$SearchArray = #('employment', 'source of income', 'US address', 'residential address', 'ID', 'driver license', 'visa', 'passport', 'I-20', 'Social Security Card', 'information update form', 'w9', 'w8', 'tax', 'email address')
$word = New-Object -ComObject Word.application
foreach ($doc in $docs) {
$Document = $word.Documents.Open($doc)
$CVSInfo = $Document.Paragraphs | ForEach-Object{
foreach ($SerchText in $SearchArray) {
$_.Range.Text | Where-Object { $_-match $SerchText} | ForEach-Object {
$_-split ' ' | Select-Object -Last 1
}
}
}
$PathArray = $doc.FullName
#Launch Excel
#Find first blank row #, and activate the first cell in that row
$FirstBlankRow = $($xl.ActiveSheet.UsedRange.Rows)[-1].Row + 1
$XL.ActiveSheet.Range("A$FirstBlankRow").Activate()
#Create PSObject with the properties that we want, convert it to a tab delimited CSV, and copy it to the clipboard
$Record = [PSCustomObject]#{
'ID' = $PathArray
'Context' = $CVSInfo
}
$Record | ConvertTo-Csv -Delimiter "`t" -NoTypeInformation | Clip
#Paste at the currently active cell
$XL.ActiveSheet.Paste() | Out-Null
# Save and close
$WB.Save() | Out-Null
}
$WB.Close() | Out-Null
$XL.Quit() | Out-Null
#Release ComObject
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($XL)
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($word)
Thanks in advance!
It looks like you have hundreds of Word documents open. Don't forget to close them in each iteration of the loop:
$Document.Close()

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}

Powershell and Outlook

I have a problem readiing emails from an Inbox using powershell - however I can red emails from another folder (Test) without a problem. The code I have is
$account = "aaaa.bbb#xxxx.net"
$o = New-Object -comobject outlook.application
$n = $o.GetNamespace(“MAPI”)
$Account = $n.Folders | ? {$_.Name -eq $account}
$f = $Account.Folders | ? {$_.Name -match "Test"}
$f.Items | ForEach {
Do Stuff
}
How do I amend the code so it read the emails from the Inbox and not the folder Test. Another thing is that there's another email account attached and that to has an inbox. How do I make sure I'm looking at the Inbox for the email address specified?
Thanks in Advance
G
I'm doing something similar and I'm using the following (I haven't tested yet on multiple mailboxes yet but it works for my own email)
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")
$emailAddress = "..."
$recipient = $namespace.CreateRecipient($emailAddress)
$folder = $namespace.GetSharedDefaultFolder($recipient, $olFolders::olFolderInbox)