Powershell EWS API delete mixed deployment Office365 and Exchange messages

Keywords: Windows less encoding

Environment requirements: Powershell 5.0, plus domain computer

  1. Install EWS API to default location
    https://www.microsoft.com/en-us/download/details.aspx?id=42951
  2. For permission configuration, add the ApplicationImpersonation role in the Exchange management center of Office365 and the Exchange management center and administrator role, and add the executive account as a member
  3. Delete message script breakdown
    3.1 connect EWS API with execution account
    Import-Module -Name "C:\Program Files\Microsoft\Exchange\Web Services\2.2\Microsoft.Exchange.WebServices.dll" #Import API module
    $username = "Executive account email"            
    $password = "Execute account password"
    $exchService = new-object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2016_CU7) 
    $ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2016_CU7
    $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)
    $service.Credentials = New-Object Microsoft.Exchange.WebServices.Data.WebCredentials -ArgumentList $userName, $password

    3.2 specify the account number of the deleted email

$deleteusersamid = "xxxx"      #Deleted email account samid
$userinfo = get-aduser $deleteusersamid -Properties EmailAddress,targetAddress
$targetAddress =$userinfo.targetAddress
if($targetAddress){
#Delete office 365 email
$uri=[system.URI] "https://outlook.office365.com/EWS/Exchange.asmx"
}else{
#Delete Exchange mailbox messages
$uri=[system.URI] "https://email.localexchange.com/ews/exchange.asmx" 
}
$upn = $userinfo.EmailAddress
$service.url = $uri
$service.ImpersonatedUserId = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId ([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SMTPAddress,$upn)  

3.3 specify delete mail conditions

#New search logical collection
$sfCollection = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+SearchFilterCollection([Microsoft.Exchange.WebServices.Data.LogicalOperator]::And);
#Specify start time
$filter1 = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+IsGreaterThanOrEqualTo([Microsoft.Exchange.WebServices.Data.ItemSchema]::DateTimeReceived, $starttime)
$sfCollection.add($filter1)
#Specify end time
$filter2 = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+IsLessThanOrEqualTo([Microsoft.Exchange.WebServices.Data.ItemSchema]::DateTimeReceived, $endtime)
$sfCollection.add($filter2)
#Specify the subject of the email. The subject is fuzzy matching. It is OK to take a section of the subject. The content should not contain special matching
$filter3 = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+ContainsSubstring([Microsoft.Exchange.WebServices.Data.ItemSchema]::Subject,$subject)
$sfCollection.add($filter3)
#Specify delivery address
$filter4 = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.EmailMessageSchema]::Sender, $Sender)  
$sfCollection.add($filter4)
#Specify the content of the text. The text is fuzzy matching. It is OK to take a paragraph of the text. The content should not contain special match
$filter5 = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+ContainsSubstring([Microsoft.Exchange.WebServices.Data.ItemSchema]::Body,$Body)
$sfCollection.add($filter5)
#Specify the maximum number of messages to read at a time
$view = New-Object Microsoft.Exchange.WebServices.Data.ItemView(1000)
#Specify the maximum number of folders to read at a time
$FolderList = new-object Microsoft.Exchange.WebServices.Data.FolderView(100)

3.4 in inbox, sent mail, deleted mail search mail

#Search inbox for messages, no subfolders
$mails = $service.FindItems([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::inbox,$sfCollection,$view)
#Search for messages in sent messages
$mails = $service.FindItems([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::SentItems,$sfCollection,$view)
#Search for messages in deleted messages
$mails = $service.FindItems([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::DeletedItems,$sfCollection,$view)

3.5 search for messages in subfolders of inbox

#Get all folders in inbox
$inbox = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,[Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Inbox)
$FolderList = new-object Microsoft.Exchange.WebServices.Data.FolderView(100)
$FolderList.Traversal = [Microsoft.Exchange.WebServices.Data.FolderTraversal]::Deep
$findFolderResults = $inbox.FindFolders($FolderList)
$sortfindFolderResults = $findFolderResults | where{$_.FolderClass -eq "IPF.Note"}  
#Search for messages in each subfolder
foreach($subfoler in $sortfindFolderResults){
$displayname = $subfoler.displayname
$echocontent = "Querying $upn folder $displayname Messages in"
Write-Host $echocontent -ForegroundColor gray
$id = $subfoler.id
$folder_ms= ""
$folder_ms= [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$id)
$mails = ""
$mails= $service.FindItems($id,$sfCollection,$view)
if($mails){
$findResults += $mails
}
}

3.6 delete all the searched messages. The optional values of deletion type are as follows:
MoveToDeletedItems: messages moved to deleted folders
Softdelete: soft delete, which can be recovered in the recovery of deleted messages
harddelete: hard delete, permanent delete, unable to recover

foreach($item in $findResults){
$item.Delete([Microsoft.Exchange.WebServices.Data.DeleteMode]::softdelete) 
}

4. Delete mail function

function EWSDeleteEmail
{
<#
.SYNOPSIS
//Delete the specified message content
EWSDeleteEmail -recepient extest7 -sender user@domain.cn -starttime "2018.12.24 10:48" -endtime "2018.12.24 10:51" -subject "234" -Confirm:$false
#>
 [CmdletBinding(
    SupportsShouldProcess = $true,
    ConfirmImpact = 'High')]
    param
(
        [Parameter(Mandatory=$true)]
        $sender,
        [Parameter(Mandatory=$true)]
        $subject,
        [Parameter(Mandatory=$true)]        
        [System.datetime]$starttime ,
        [Parameter(Mandatory=$true)]        
        [System.datetime]$endtime ,
        [Parameter(Mandatory=$false)]
        $recepient
)
if($PSCmdlet.ShouldProcess($param)) {
if($recepient -or ($file -match ".csv")){
if($endtime -gt $starttime){
if($subject){
if($sender -match "\@"){
$result = @()
Import-Module -Name "C:\Program Files\Microsoft\Exchange\Web Services\2.2\Microsoft.Exchange.WebServices.dll"
$username = "user@domain.com"           
$password = "Password" 
$exchService = new-object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2016_CU7) 
$ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2016_CU7
$service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)
$service.Credentials = New-Object Microsoft.Exchange.WebServices.Data.WebCredentials -ArgumentList $userName, $password
$filter1 = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+IsGreaterThanOrEqualTo([Microsoft.Exchange.WebServices.Data.ItemSchema]::DateTimeReceived, $starttime)
$filter2 = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+IsLessThanOrEqualTo([Microsoft.Exchange.WebServices.Data.ItemSchema]::DateTimeReceived, $endtime)
$filter3 = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+ContainsSubstring([Microsoft.Exchange.WebServices.Data.ItemSchema]::Subject,$subject)
$filter4 = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.EmailMessageSchema]::Sender, $Sender)  
$sfCollection = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+SearchFilterCollection([Microsoft.Exchange.WebServices.Data.LogicalOperator]::And);
$sfCollection.add($filter1)
$sfCollection.add($filter2)
$sfCollection.add($filter3)
$sfCollection.add($filter4)
if($recepient){
$recsamid = ($recepient -split "\@")[0]
$userinfo = ""
$userinfo = get-aduser $recsamid -Properties EmailAddress,targetAddress
$useremail = ""
$useremail = $userinfo.EmailAddress
if($useremail){
$targetAddress = ""
$targetAddress =$userinfo.targetAddress
if($targetAddress){
$uri=[system.URI] "https://outlook.office365.com/EWS/Exchange.asmx"
}else{
$uri=[system.URI] "https://email.domain.com/ews/exchange.asmx" 
}
$upn = $userinfo.EmailAddress
$service.url = $uri
$service.ImpersonatedUserId = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId ([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SMTPAddress,$upn)
$inbox = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,[Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Inbox)
$FolderList = new-object Microsoft.Exchange.WebServices.Data.FolderView(100)
$FolderList.Traversal = [Microsoft.Exchange.WebServices.Data.FolderTraversal]::Deep
$findFolderResults = $inbox.FindFolders($FolderList)
$view = New-Object Microsoft.Exchange.WebServices.Data.ItemView(1000)
$findResults = @()
$mails = ""
$echocontent = "Querying $upn Messages in inbox"
Write-Host $echocontent -ForegroundColor gray
$mails = $service.FindItems([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::inbox,$sfCollection,$view)
$mails | Add-Member -MemberType NoteProperty -Name foldername -Value "inbox"
if($mails){
$findResults += $mails
}
$mails = ""
$mails = $service.FindItems([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::SentItems,$sfCollection,$view)
$mails | Add-Member -MemberType NoteProperty -Name foldername -Value "SentItems"
if($mails){
$findResults += $mails
}
$mails = ""
$mails = $service.FindItems([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::DeletedItems,$sfCollection,$view)
$mails | Add-Member -MemberType NoteProperty -Name foldername -Value "DeletedItems"
if($mails){
$findResults += $mails
}
if(!$findResults){
$sortfindFolderResults = $findFolderResults | where{$_.FolderClass -eq "IPF.Note"}
foreach($subfoler in $sortfindFolderResults){
$displayname = $subfoler.displayname
$echocontent = "Querying $upn folder $displayname Messages in"
Write-Host $echocontent -ForegroundColor gray
$id = $subfoler.id
$folder_ms= ""
$folder_ms= [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$id)
$mails = ""
$mails= $service.FindItems($id,$sfCollection,$view)
if($mails){
$mails | Add-Member -MemberType NoteProperty -Name foldername -Value $displayname
$findResults += $mails
}
}
}
if($findResults){
$num = ($findResults | measure).count
$outinfo = $findResults | select DateTimeReceived,Sender,Subject,Size,foldername
$outinfotxt = $outinfo | fl | Out-String
$echocontent = @"
//Query criteria:
//To: $upn
//From: $Sender
//Subject: $subject
//Time period: $starttime-$endtime
//Number of search results: $num
//Details: $outinfotext
"@
Write-Host $echocontent -ForegroundColor gray
foreach($item in $findResults){
$Error.Clear()
$item.Delete([Microsoft.Exchange.WebServices.Data.DeleteMode]::softdelete) 
if($Error){
$Errortxt = $Error | Out-String 
$item | Add-Member -MemberType NoteProperty -Name action -Value "failed"
$item | Add-Member -MemberType NoteProperty -Name errorinfo -Value $Errortxt
}else{
$item | Add-Member -MemberType NoteProperty -Name action -Value "succeed"
}
$item | Add-Member -MemberType NoteProperty -Name searchuser -Value $recsamid
$newitem = $item | select searchuser,DateTimeReceived,Sender,Subject,Size,foldername,action,errorinfo
$result += $newitem
}
}else{
$userobject = New-Object psobject -Property @{
searchuser = $recsamid
DateTimeReceived = ""
Sender= $Sender
Subject= $subject
Size= ""
foldername= ""
action= ""
errorinfo= "Search result is 0"
}
$result += $userobject
}
}else{
$userobject = New-Object psobject -Property @{
searchuser = $recsamid
DateTimeReceived = ""
Sender= $Sender
Subject= $subject
Size= ""
foldername= ""
action= ""
errorinfo= "Not Exsit Recipent"
}
$result += $userobject
}
}
return $result
}else{
Write-Host "Please enter the sender's full address" -ForegroundColor Red
}
}else{
Write-Host "Please enter the subject keywords" -ForegroundColor Red
}
}else{
Write-Host "starttime Should be less than endtime" -ForegroundColor Red
}
}else{
Write-Host "Please provide recipient information" -ForegroundColor Red
}
}
}

5. How to use the delete mail function
Short term test: open Powershell and copy the above code to run in Powershell.
Use the following command to delete a message:

EWSDeleteEmail -sender sender@domain.com -subject "testsubject" -starttime "2020.4.24 9:50" -endtime "2020.4.24 9:55" -recepient recipient@domain.com

Long term use: it is recommended to save the function to Powershell personal Profile

#Check whether there is a Profile
$profile
#Create a Profile (it is found in the previous step that the existing Profile skips this step)
New-Item –Path $Profile –Type File –Force
#Open Profile, add function copy to the last
notepad $Profile

Then every time you open Powershell, you can use the EWSDeleteEmail function directly.

EWSDeleteEmail -sender sender@domain.com -subject "testsubject" -starttime "2020.4.24 9:50" -endtime "2020.4.24 9:55" -recepient recipient@domain.com

6. How to delete mail in batch
6.1. The list user.csv to be deleted is in the following format

email
user1@domain.com
user2@domain.com
user3@domain.com

6.2. Batch run delete mail and save the results to the result.csv file

$userlist = Import-Csv user.csv
foreach($user in $userlist){
$recepient = $user.email
$info = EWSDeleteEmail -sender sender@domain.com -subject "Test" -starttime "2020.4.24 9:50" -endtime "2020.4.24 9:55" -recepient $recepient
$info | Export-Csv result.csv -Encoding UTF8 -Append
}

7. Extended usage
If you need to query whether a user has received a message or whether the message has been read, you can cancel the part of the deleted message in the function and return the search result $findResults. Then you can see the message related information, such as whether it has been read, etc

7.1 read specified mail content function

function ReadSpecificEmail
{
<#
.SYNOPSIS
//Read the specified mailbox message
ReadSpecificEmail -recepient extest7 -sender user@domain.cn -starttime "2018.12.24 10:48" -endtime "2018.12.24 10:51" -subject "234" -Confirm:$false
#>
 [CmdletBinding(
    SupportsShouldProcess = $true,
    ConfirmImpact = 'High')]
    param
(
        [Parameter(Mandatory=$true)]
        $sender,
        [Parameter(Mandatory=$true)]
        $subject,
        [Parameter(Mandatory=$true)]        
        [System.datetime]$starttime ,
        [Parameter(Mandatory=$true)]        
        [System.datetime]$endtime ,
        [Parameter(Mandatory=$false)]
        $recepient
)
if($PSCmdlet.ShouldProcess($param)) {
if($recepient -or ($file -match ".csv")){
if($endtime -gt $starttime){
if($subject){
if($sender -match "\@"){
$result = @()
Import-Module -Name "C:\Program Files\Microsoft\Exchange\Web Services\2.2\Microsoft.Exchange.WebServices.dll"
$username = "user@domain.com"           
$password = "Password" 
$exchService = new-object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2016_CU7) 
$ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2016_CU7
$service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)
$service.Credentials = New-Object Microsoft.Exchange.WebServices.Data.WebCredentials -ArgumentList $userName, $password
$filter1 = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+IsGreaterThanOrEqualTo([Microsoft.Exchange.WebServices.Data.ItemSchema]::DateTimeReceived, $starttime)
$filter2 = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+IsLessThanOrEqualTo([Microsoft.Exchange.WebServices.Data.ItemSchema]::DateTimeReceived, $endtime)
$filter3 = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+ContainsSubstring([Microsoft.Exchange.WebServices.Data.ItemSchema]::Subject,$subject)
$filter4 = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.EmailMessageSchema]::Sender, $Sender)  
$sfCollection = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+SearchFilterCollection([Microsoft.Exchange.WebServices.Data.LogicalOperator]::And);
$sfCollection.add($filter1)
$sfCollection.add($filter2)
$sfCollection.add($filter3)
$sfCollection.add($filter4)
if($recepient){
$recsamid = ($recepient -split "\@")[0]
$userinfo = ""
$userinfo = get-aduser $recsamid -Properties EmailAddress,targetAddress
$useremail = ""
$useremail = $userinfo.EmailAddress
if($useremail){
$targetAddress = ""
$targetAddress =$userinfo.targetAddress
if($targetAddress){
$uri=[system.URI] "https://outlook.office365.com/EWS/Exchange.asmx"
}else{
$uri=[system.URI] "https://email.domain.com/ews/exchange.asmx" 
}
$upn = $userinfo.EmailAddress
$service.url = $uri
$service.ImpersonatedUserId = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId ([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SMTPAddress,$upn)
$inbox = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,[Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Inbox)
$FolderList = new-object Microsoft.Exchange.WebServices.Data.FolderView(100)
$FolderList.Traversal = [Microsoft.Exchange.WebServices.Data.FolderTraversal]::Deep
$findFolderResults = $inbox.FindFolders($FolderList)
$view = New-Object Microsoft.Exchange.WebServices.Data.ItemView(1000)
$findResults = @()
$mails = ""
$echocontent = "Querying $upn Messages in inbox"
Write-Host $echocontent -ForegroundColor gray
$mails = $service.FindItems([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::inbox,$sfCollection,$view)
$mails | Add-Member -MemberType NoteProperty -Name foldername -Value "inbox"
if($mails){
$findResults += $mails
}
$mails = ""
$mails = $service.FindItems([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::SentItems,$sfCollection,$view)
$mails | Add-Member -MemberType NoteProperty -Name foldername -Value "SentItems"
if($mails){
$findResults += $mails
}
$mails = ""
$mails = $service.FindItems([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::DeletedItems,$sfCollection,$view)
$mails | Add-Member -MemberType NoteProperty -Name foldername -Value "DeletedItems"
if($mails){
$findResults += $mails
}
if(!$findResults){
$sortfindFolderResults = $findFolderResults | where{$_.FolderClass -eq "IPF.Note"}
foreach($subfoler in $sortfindFolderResults){
$displayname = $subfoler.displayname
$echocontent = "Querying $upn folder $displayname Messages in"
Write-Host $echocontent -ForegroundColor gray
$id = $subfoler.id
$folder_ms= ""
$folder_ms= [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$id)
$mails = ""
$mails= $service.FindItems($id,$sfCollection,$view)
if($mails){
$mails | Add-Member -MemberType NoteProperty -Name foldername -Value $displayname
$findResults += $mails
}
}
}
if($findResults){
$num = ($findResults | measure).count
$outinfo = $findResults | select DateTimeReceived,Sender,Subject,Size,foldername
$outinfotxt = $outinfo | fl | Out-String
$echocontent = @"
//Query criteria:
//To: $upn
//From: $Sender
//Subject: $subject
//Time period: $starttime-$endtime
//Number of search results: $num
//Details: $outinfotext
"@
Write-Host $echocontent -ForegroundColor gray
$result = $findResults
}else{
$userobject = New-Object psobject -Property @{
searchuser = $recsamid
DateTimeReceived = ""
Sender= $Sender
Subject= $subject
Size= ""
foldername= ""
action= ""
errorinfo= "Search result is 0"
}
$result += $userobject
}
}else{
$userobject = New-Object psobject -Property @{
searchuser = $recsamid
DateTimeReceived = ""
Sender= $Sender
Subject= $subject
Size= ""
foldername= ""
action= ""
errorinfo= "Not Exsit Recipent"
}
$result += $userobject
}
}
return $result
}else{
Write-Host "Please enter the sender's full address" -ForegroundColor Red
}
}else{
Write-Host "Please enter the subject keywords" -ForegroundColor Red
}
}else{
Write-Host "starttime Should be less than endtime" -ForegroundColor Red
}
}else{
Write-Host "Please provide recipient information" -ForegroundColor Red
}
}
}

Posted by darkfunkel on Sat, 25 Apr 2020 08:09:23 -0700