Delete internal emails from Dynamics 365 or Dataverse

Let’s start with some health warnings:

  1. Don’t delete stuff unless you are really sure you don’t want it back (without dredging through a rollback/restore process)

  2. Tracking all emails in Dynamics 365 (or Dataverse) can have storage implications (but they are a bit less implicating now that the descriptions and attachments are offloaded into blob storage YAY)

  3. Emails tagged with sensitivity ‘Confidential’ will still be tracked in Dataverse, with the body of the email redacted. An email subject/recipient details could however be damaging enough

  4. Stop slouching

Automatically tracking emails in Dynamics 365 is a really helpful way of providing visibility to all Dynamics 365 users off ongoing communications with a lead or customer, it’s applicable across all realms of customer relationship management from sales to service and anything else in between. Once a user has been enabled and correctly set up with the Dynamics 365 App for Outlook, their user settings can be updated to automatically track all emails into Dynamics 365 (you can also do this in bulk using the ‘User Settings Utility’ in XRM Toolbox - detailed post here ps. ‘backend’ tehehe).

When it says tracking ‘All Email messages’ it really means it, but this can be a little problematic especially when other sensitive topics are discussed via email such as HR investigations, payslips, redundancies and confidential company intel, which you might not want everyone to be able to see in Dynamics 365. But it gets better, now that Dataverse Search allows you to search across all tables including things like email contents - its now even easier to accidentally (or intentionally) find things you probably shouldn’t.

Create a scheduled flow

I scheduled the flow to run every 5 minutes, so in theory the email could hang around for a short amount of time, you could run it more frequently if you decide the importance is worth API calls. You could also trigger a flow to run on create of each email and screen it accordingly, but the most useful value I can add here is how to find the internal emails so lets carry on.

What time was it 6 minutes ago Mr Wolf?

Add a compose step to calculate the time stamp for 6 minutes ago.

Name: '6MinutesAgo'

Why you may ask? ‘Last x minutes’ is not available ad a date filter operator so we have to be a bit smarter. After some light reading of a detailed run down on FetchXML date filtering by the expert himself Mark Carrington I worked out that we can use ‘greater than’ combined with the time calculation of 6 minutes ago (1 extra minute than needed to be on the safe side of course).

Who is Mr Wolf?

formatDateTime(addMinutes(utcNow(),-300),'o')

Find any internal emails (created in the last 6 minutes)

Using a Dataverse ‘List rows’ action we can use the power of FetchXML to list all the emails where all the recipients are internal users to the company (using their email domain) and filter it down to anything created since the last run 5 minutes ago. Note in the below FetchXML query to update the domain to align to your organisation.

<fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="true" >
  <entity name="email" >
    <attribute name="activityid" />
    <order attribute="actualend" descending="true" />
    <filter type="and" >
      <condition entityname="aanot" attribute="activitypartyid" operator="null" />
<condition attribute="actualend" operator="gt" value="@{outputs('6MinutesAgo')}" />
    </filter>
    <link-entity name="activityparty" from="activityid" to="activityid" link-type="outer" alias="aa" >
      <attribute name="addressused" />
      <filter type="or" >
        <condition attribute="addressused" operator="like" value="%ameyholden.com%" />
        <condition attribute="addressused" operator="null" />
      </filter>
    </link-entity>
    <link-entity name="activityparty" from="activityid" to="activityid" link-type="outer" alias="aanot" >
      <filter>
        <condition attribute="addressused" operator="not-like" value="%ameyholden.com%" />
        <condition attribute="addressused" operator="not-null" />
      </filter>
    </link-entity>
  </entity>
</fetch>

Initialise an email ID array variable

We will append to this array with the activity ID of any email record found matching our criteria.

Name: EmailIDArray
Type: Array
Value: []

Did you find any emails?

Using a Condition control we can check if the list rows action fund any emails for us to delete. If no Cancel, the flow run will show as cancelled rather than successful so its easier to identify if/when emails were actually deleted rather than not found. If yes proceed to next steps.

empty(body('InternalEmails')?['value'])

Append email ID array variable (AppendEmailIDs)

Using an ‘Apply to each’ action with a ‘Append to array variable’ action - append all the IDs of the email records found meeting your delete criteria.

Because there are many activity parties matching to a single email, our query will return the same email more than once. We need to first gather the IDs to then deduplicate and delete. if you try to delete the same thing twice, the second delete action inevitably causes your flow to fail.

outputs('InternalEmails')?['body/value']

items('AppendEmailIDs')?['activityid']

Deduplicate the EmailIDArray

In a ‘Compose’ step we can use the union() funciton to combine the Email ID Array with itself, which leave only unique values behind. Then set the array variable to the deduplicated array created.

union(variables('EmailIDArray'),variables('EmailIDArray'))

outputs('DeDuplicateArray')

Delete the internal emails

Finally, using the deduplicated array within an ‘Apply to Each’ action we can delete the internal emails.

variables('EmailIDArray')

item()

Notes from the author

Yes that section title is ridiculous and totally tongue in cheek, I couldn’t help myself.

A few things to bear in mind - Deleting emails in Dynamics 365 does not delete them from the individuals inbox, it is just a copy of the email. Ideally we would prevent these internal emails from landing in Dynamics 365 in the first place but there is no way to find tune the automatic tracking at this level unfortunately and the cost of turning off the automatic tracking of external conversations could be huge. That’s all, hopefully this helps you from any future issues with people seeing emails that they really shouldn’t.

Previous
Previous

Mapping Lookup values from Dynamics 365 Marketing Form Submissions with Power Automate

Next
Next

Create Dynamics 365 Marketing Segments with Power Automate