Exchange Online Microsoft Graph APIs for permanent deletion

Exchange Online Microsoft Graph APIs for permanent deletion

Hi All,

A few Weeks ago Microsoft has announced the Permanent Deletion of mailbox items.

It’s relatively simple. Instead of the “DELETE” HTTP Method you use the “POST” Method and add “/permanentDelete” to the URI.

###############################################################################
#Delete
#https://learn.microsoft.com/en-us/graph/api/message-delete?view=graph-rest-1.0&tabs=http
###############################################################################
DELETE /users/{UserId}/messages/{MessageId}
DELETE /users/{UserId}/mailFolders/{mailFolderId}/messages/{MessageId}

###############################################################################
#Permanently Delete
#https://learn.microsoft.com/en-us/graph/api/message-permanentdelete?view=graph-rest-1.0&tabs=http
###############################################################################
POST /users/{UserId}/messages/{MessageId}/permanentDelete
POST /users/{UserId}/mailFolders/{mailFolderId}/messages/{MessageId}/permanentDelete

Time for me to test the Functionality.

Entra Application

I’ve created an Entra App

with Application and Delegated Permissions

Note: In production Environement you should not mix Application and Delegated Permissions

I am using Application Access Policy

Connect-ExchangeOnline -ShowBanner:$false
Get-ApplicationAccessPolicy | Where-Object {$_.Appid -eq "c1a5903b-cd73-48fe-ac1f-e71bde968412"}

The Application Permission is limited to the Members of the Distribution Group

Get-DistributionGroupMember -Identity 05c4f6cf-e3e7-40a1-b3b0-f1eb680f78c9

Application Permissions

Getting the AccessToken with the PowerShell Module PSMSALNet that requires PowerShell 7.4.

###############################################################################
#Setting up the Variables for all Examples
###############################################################################
Import-Module PSMSALNet #Need PowerShell 7.4
$TenantId = "46bbad84-29f0-4e03-8d34-f6841a5071ad"
$AppID = "c1a5903b-cd73-48fe-ac1f-e71bde968412" #DelegatedMail
$RedirectUri = "https://login.microsoftonline.com/common/oauth2/nativeclient"

###############################################################################
#Authenticate with Certificate
###############################################################################
$CertificateThumbprint = "A3A07A3C2C109303CCCB011B10141A020C8AFDA3" #O365Powershell4.cer
$Certificate = Get-ChildItem -Path cert:\CurrentUser\my\$CertificateThumbprint
$HashArguments = @{
  ClientId = $AppID
  ClientCertificate = $Certificate
  TenantId = $TenantId
  Resource = "GraphAPI"
}
$Token = Get-EntraToken -ClientCredentialFlowWithCertificate @HashArguments
$AccessToken = $Token.AccessToken

Let’s check the AccessToken with JWTDetails

#Install-Module JWTDetails
Get-JWTDetails $AccessToken

Mailbox Folders

In the Postmaster Mailbox there is a Mail with the Subject “Claim your Exclusive AAVE Airdrop”.

List the Mailbox Folders of the Postmaster Mailbox

###############################################################################
# List Mailbox Folders
###############################################################################
$Mailbox = "Postmaster@icewolf.ch"
$URI = "https://graph.microsoft.com/v1.0/users/$Mailbox/mailFolders"

$ContentType = "application/json"
$Headers = @{"Authorization" = "Bearer "+ $AccessToken}
$Result = Invoke-RestMethod -Method "GET" -Uri $uri -Headers $Headers -ContentType $ContentType
$Result.value | Format-Table displayName, id

$Result.value | Where-Object {$_.displayName -eq "Posteingang"} | fl

There is a Subfolder with the Name “SubfolderPostmaster”

List the Subfolders of the Inbox Folder

###############################################################################
# List Child Mailbox Folders
# https://learn.microsoft.com/en-us/graph/api/mailfolder-list-childfolders?view=graph-rest-1.0&tabs=http
###############################################################################
$Mailbox = "Postmaster@icewolf.ch"
$FolderID = "AAMkADExY2U2ZWY2LTI0YzEtNGQ3Mi1iODY0LTZmNzQ2MWQxOWJlYQAuAAAAAADI11bk3aFKQJXy4z2GgQYRAQD4k93uZqwxSo0-0gbfaWPWAAAAr8HVAAA=" #Posteingang
$URI = "https://graph.microsoft.com/v1.0/users/$Mailbox/mailFolders/$FolderId/ChildFolders"
$ContentType = "application/json"
$Headers = @{"Authorization" = "Bearer "+ $AccessToken}
$Result = Invoke-RestMethod -Method "GET" -Uri $uri -Headers $Headers -ContentType $ContentType
$Result.value | Format-List displayName, id

Let’s delete the Subfolder “Posteingang/SubfolderPostmaster”

###############################################################################
# Permanently delete Child Mailbox Folder
###############################################################################
$Mailbox = "Postmaster@icewolf.ch"
$FolderID = "AAMkADExY2U2ZWY2LTI0YzEtNGQ3Mi1iODY0LTZmNzQ2MWQxOWJlYQAuAAAAAADI11bk3aFKQJXy4z2GgQYRAQD4k93uZqwxSo0-0gbfaWPWAAAAr8HVAAA=" #Posteingang
$URI = "https://graph.microsoft.com/v1.0/users/$Mailbox/mailFolders/$FolderID/permanentDelete"
$ChildFolderID = "AAMkADExY2U2ZWY2LTI0YzEtNGQ3Mi1iODY0LTZmNzQ2MWQxOWJlYQAuAAAAAADI11bk3aFKQJXy4z2GgQYRAQD9DAdvUOIbRK2TMu1gBCF9AAaGE3zBAAA=" #Posteingang/Subfolder
$URI = "https://graph.microsoft.com/v1.0/users/$Mailbox/mailFolders/$FolderID/childFolders/$ChildFolderID/permanentDelete"

$ContentType = "application/json"
$Headers = @{"Authorization" = "Bearer "+ $AccessToken}
$Result = Invoke-RestMethod -Method "POST" -Uri $uri -Headers $Headers -ContentType $ContentType
$Result

The Subfolder is deleted and nothing appears in the “Deleted Items” Folder

Emails

List Mailbox Message

###############################################################################
# List Mailbox Message
# https://docs.microsoft.com/en-us/graph/api/user-list-messages?view=graph-rest-1.0&tabs=http
###############################################################################
$Mailbox = "Postmaster@icewolf.ch"
$URI = "https://graph.microsoft.com/v1.0/users/$Mailbox/messages"
$FolderID = "AAMkADExY2U2ZWY2LTI0YzEtNGQ3Mi1iODY0LTZmNzQ2MWQxOWJlYQAuAAAAAADI11bk3aFKQJXy4z2GgQYRAQD4k93uZqwxSo0-0gbfaWPWAAAAr8HVAAA=" #Posteingang
$URI = "https://graph.microsoft.com/v1.0/users/$Mailbox/mailFolders/$FolderID/messages"

$ContentType = "application/json"
$Headers = @{"Authorization" = "Bearer "+ $AccessToken}
$Result = Invoke-RestMethod -Method "GET" -Uri $uri -Headers $Headers -ContentType $ContentType
$Result.value[0] | Format-List

#List Messages
#$Result.value | Format-List id,receivedDateTime, subject, hasAttachments, importance, internetMessageId,isRead

Let’s permanently delete that Message

###############################################################################
# Permanent Delete Mailbox Message
# https://learn.microsoft.com/en-us/graph/api/message-permanentdelete?view=graph-rest-1.0&tabs=http
###############################################################################
$Mailbox = "Postmaster@icewolf.ch"
$MessageID = "AAMkADExY2U2ZWY2LTI0YzEtNGQ3Mi1iODY0LTZmNzQ2MWQxOWJlYQBGAAAAAADI11bk3aFKQJXy4z2GgQYRBwD4k93uZqwxSo0-0gbfaWPWAAAAr8HVAAD9DAdvUOIbRK2TMu1gBCF9AAaBvvhvAAA="
$URI = "https://graph.microsoft.com/v1.0/users/$Mailbox/messages/$MessageID/permanentDelete"

$ContentType = "application/json"
$Headers = @{"Authorization" = "Bearer "+ $AccessToken}
$Result = Invoke-RestMethod -Method "POST" -Uri $uri -Headers $Headers -ContentType $ContentType
$Result

The Email with the Subject “Claim your Exclusive AAVE Airdrop” has been deletet

And its not in the “Deleted Items” Folder

And it’s not in the “Recoverable Items” Folder

Calendar

Let’s have a look into the Calendar

Attendee

Let’s have a look at the Meeting with the Subject “Lunch” where Postmaster is an Attendee

###############################################################################
#List Events
#https://docs.microsoft.com/en-us/graph/api/user-list-events?view=graph-rest-1.0&tabs=http
###############################################################################
$Mailbox = "postmaster@icewolf.ch"
$headers = @{"Authorization" = "Bearer "+ $AccessToken}
$uri = "https://graph.microsoft.com/v1.0/users/$Mailbox/calendar/events"
$Events = Invoke-RestMethod -Method GET -uri $uri -headers $headers
$Event = $Events.Value | Where-Object {$_.Subject -eq "Lunch"}
$Event | Format-List id,subject,start,end,location
$Event.attendees
$Event.organizer

The Meeting with the Subject “Lunch” ist organized by a.bohren@icewolf.ch and has been accepted

Tracking Status of the Organizer

###############################################################################
# permanentDelete Event
# https://learn.microsoft.com/en-us/graph/api/event-permanentdelete?view=graph-rest-1.0&tabs=http
###############################################################################
$Mailbox = "postmaster@icewolf.ch"
$EventId = "AAMkADExY2U2ZWY2LTI0YzEtNGQ3Mi1iODY0LTZmNzQ2MWQxOWJlYQBGAAAAAADI11bk3aFKQJXy4z2GgQYRBwD4k93uZqwxSo0-0gbfaWPWAAAAr8HeAAD9DAdvUOIbRK2TMu1gBCF9AAaEx9GSAAA=" #Lunch
$headers = @{"Authorization" = "Bearer "+ $AccessToken}
$uri = "https://graph.microsoft.com/v1.0/users/$Mailbox/calendar/events/$EventID/permanentDelete"
$Result = Invoke-RestMethod -Method "POST" -uri $uri -headers $headers

The Tracking Status now shows “Declined”

Organizer

Let’s have a look at the Meeting “Important Meeting” where a.bohren is the Organizer

###############################################################################
#List Events
#https://docs.microsoft.com/en-us/graph/api/user-list-events?view=graph-rest-1.0&tabs=http
###############################################################################
$mailbox = "a.bohren@icewolf.ch"
$headers = @{"Authorization" = "Bearer "+ $AccessToken}
$uri = "https://graph.microsoft.com/v1.0/users/$Mailbox/calendar/events"
$Events = Invoke-RestMethod -Method GET -uri $uri -headers $headers
$Event = $Events.Value | Where-Object {$_.Subject -eq "Important Meeting"}
$Event | Format-List id,subject,start,end,location
$Event.attendees
$Event.organizer

Let’s permanently delete the Meeting as Organizer

###############################################################################
# permanentDelete Event
# https://learn.microsoft.com/en-us/graph/api/event-permanentdelete?view=graph-rest-1.0&tabs=http
###############################################################################
$mailbox = "a.bohren@icewolf.ch"
$EventId = "AQMkADU4NGU4M2ViLWM5NjctNGI0YS05ZmJhLTIyADdmYWI0MjRkYmQARgAAAzqJ2GWaRBxKv-EJWOBGbRAHAEZu88iLm85MjHqnrJ10b8oAAAIXkwAAAcb9AhgmYESSPal9iQNu6wADI9gFawAAAA==" #Important Meeting
$headers = @{"Authorization" = "Bearer "+ $AccessToken}
$uri = "https://graph.microsoft.com/v1.0/users/$Mailbox/calendar/events/$EventID/permanentDelete"
$Result = Invoke-RestMethod -Method "POST" -uri $uri -headers $headers

A cancelation Email has been sent out.

Regards
Andres Bohren

Exchange Logo

PowerShell Logo