In today's WTF episode I share a different method in my flow in Power Automate for the use case of sending an email to multiple recipients dynamically using an email template.
Replicating sending an email with multiple recipients using an Email Template in Power Automate
The thing is, today in classic workflows it's not possible to achieve this out-of-the-box. You can elect to send to multiple recipients by selecting values from lookup fields when configuring the send email step and applying the "Use Template" option. However this is an explicit reference as it requires someone to reference those lookup fields within the send email step.Another option is to add records directly through the To field in the send email step but again, this is an explicit reference.
You're not dynamically adding recipients to the send email step, and you definitely cannot do this without a custom workflow activity in the scenario where you want to send an email to contacts based on associated records from a 1:N relationship. It is common to come across a business process where an email is required to be sent based on Contacts that live in related records. Refer to the use case.
Use Case
I'm referring to a use that is relates to the insurance industry.
"As an insurance advisor,
I want to send Trustees an email reminder that their policy is due to expire,
so that their policy can be renewed."
Using a flow in Power Automate, this is achievable 😊💙
Using a flow in Power Automate, this is achievable 😊💙
Process
The process will be that a Case is created and an insurance advisor will update a field to trigger the Power Automate. This can be automated where a scheduled Power Automate runs daily but for the purpose of this WTF episode I am triggering it manually. Once triggered an email is sent to all Trustees associated to the Trust using an Email Template.
Sounds like a piece of cake right? 😅
The data model in Dynamics 365 or CDS
I am using the out-of-the-box entity of Connections which is used to link a record to another record in Dynamics 365 or CDS. You can associate a connection role which represents the relationship of a record to another. What I have done is used Connections to link a Contact to an Account where the role associated to the Contact is "Trustee" and role associated to the Account is "Trust."
This is because Trustees can exist as contacts but are associated to their primary account already. To prevent duplicate contact records, connections is a good way to link a Trustee to a Trust which may also be an existing account record.
I have chosen to send the email to the Trustees where they are Bcc'd so that I can show you that my flow in Power Automate is flexible to cater for any activity party types that Dynamics 365 or CDS supports.
What my flow in Power Automate looks like
It has more actions than the previous flow you saw in my last WTF episode however don't forget that we can't do this out-of-the-box in a classic workflow today. My flow in Power Automate enables emails to be sent to multiple recipients that are dynamically referenced. You can have 30 Trustees or 5 Trustees, my flow in Power Automate will handle the recipients dynamically.
The last three actions in my flow is a result of AK's investigation and the core part of it is from my method that I figured out on how to form the email activity parties array and the recipient + sender objects dynamically. Combining our findings resulted in my pretty cool flow. Thanks AK.
This flow was created within a solution and I am using the CDS current environment connector.
I have also configured the trigger condition where the field must equal Yes.
1.0 Trigger - when a Case field is updated
I created a custom Two Option field called Send Trust policy renewal in the Case entity and placed it on the Case entity form so that I can update it to trigger my Power Automate. I set the filtering attribute to this field.I have also configured the trigger condition where the field must equal Yes.
1.1 Get Account record
Using the CDS Get record action, retrieve the account record based on the Customer lookup value of the Case.
1.2 Retrieve connection records
Using the CDS List records action a FetchXml query can be applied which is defined using Advanced Find in Dynamics 365 or CDS. My FetchXml query will display all connections that have a connection role of Trustee and is associated to a specific account. The following is a screenshot of what my query looks like in Advanced Find.
The following is a screenshot of what my FetchXml looks like when downloaded.
Copy and paste this into the FetchXml query of the CDS List records action and replace the explicit references of account name and the account ID with the dynamic content values from the Get Account record. This is ensure that my Power Automate will work for any Trust and its associated Trustees that are represented by connections.
1.3 Temporary - Body of list
This is a Compose action where I am referencing the output of the CDS List records action. Currently as of writing this blog post there is a known issue where a status reason of 200 will display without an output of the JSON response. I am using the Compose action to verify that the correct connections records are returned since I don't have visibility in the CDS List records action while this issue is occurring. This is an optional action.
1.10 odata.type workaround + 1.11 Email target + 1.12 Perform an unbound action
I'm going to jump to two actions later in my flow because to understand the core part of my flow, I need to explain AK's method.
AK pointed out that there is another unbound action called SendEmailFromTarget. The part that tripped us up was the Target payload. It's documented how to define this through the SDK but as JSON, well that's another challenge which AK figured out. AK figured out the JSON payload for the Email Target from another fellow Microsoft MVP's blog post, Andrew Butenko. You can read Andrew Butenko's blog post here.
AK pointed out that there is another unbound action called SendEmailFromTarget. The part that tripped us up was the Target payload. It's documented how to define this through the SDK but as JSON, well that's another challenge which AK figured out. AK figured out the JSON payload for the Email Target from another fellow Microsoft MVP's blog post, Andrew Butenko. You can read Andrew Butenko's blog post here.
AK figured out that he needed to serialize the @odata.type in order for it to be applicable in the Initialize Variable action that forms the Email Target object in the JSON payload. The Email Target object is an array of the email activity parties and the odata.type of email.
In my action 1.10 odata.type workaround, I'm using AK's method where Initialize Variable is used to form the @odata property.
Problem solving time
While AK was investigating, I too was doing the exact same thing and found a different forum post from our community. When I was reviewing the screenshot in the bottom of the forum post I studied the email_activity_party array in the Target object.
Essentially when you study the JSON payload in the screenshot there's three elements (errr not sure if this is the right word but I'll go with it):
The technique that I've applied is to use the following actions
Now that the recipients have been taken care of, the sender needs some TLC. It's essentially the same technique as 1.5 Recipients where an Initialize Variable action is used to form the Sender object but this time we define the JSON payload for the object since we do not need to loop through records. There is always going to be one sender whereas the number of recipients can be dynamic.
- I knew that the each of the rows in the array represented an email activity party.
- I knew that I also had to dynamically loop through the connections to identify the contact that would be used as the Bcc recipients.
Essentially when you study the JSON payload in the screenshot there's three elements (errr not sure if this is the right word but I'll go with it):
- The array which is the email activity parties.
- The object that represents the sender including the activity party type value.
- The object that represents the recipient including the activity party type value.
Since it is an array, I figured there can be more than one recipient which would be represented by multiple rows in the array where the type of recipient is defined by the ActivityParty.ParticipationTypeMask attribute.
The technique that I've applied is to use the following actions
- Initialize Variables
- Append to array variable
- Set Variable
I'll explain the core of my Power Automate in the next few sections.
1.4 Email activity parties
To form the array the Initialize Variable action is used where Type is equal to array.
1.5 Recipients
To form the row in each array the Initialize Variable action is used where Type is equal to Object.
1.6 Apply to each Trustee
Since connections are retrieved from my FetchXml query in the CDS List records action, I needed to loop through each record to reference the contact ID which would be used in the email activity parties array.1.7 Set Recipients Variable
To form the row for the contact to be used in the email activity parties array, the Set Variable action is used where I'm referencing my recipients variable from my action 1.5 Recipients. I am also forming the object that will represent the row in the array by the following,
- Reference the contact using the contact ID from the CDS List records action. This is defined by the Connected To field in the Connection entity.
- Reference the activity party type. Since the activity party type is Bcc, the value of 4 is used.
My expression to reference the contact ID in No.1 is
item()?['_record2id_value']
1.8 Append to activity parties array variable
The final action within the Apply to each control is to append the recipients variable defined in 1.7 Set Recipients Variable to the array variable in 1.4 Email activity parties. The Append to array variable action is used.
This action is important because it will dynamically form the row in the email activity parties array. Essentially when all connections have been looped through, all of the recipients will be appended to the activity parties array.
1.7 Sender
**note this should be named as 1.9 but I only noticed my mistake in the naming convention for the later half of my flow in Power Automate after filming. To keep it consistent with the vlog I'll keep it as-is in case you're creating your own flow by watching my vlog and reading this blog post at the same time**
Now that the recipients have been taken care of, the sender needs some TLC. It's essentially the same technique as 1.5 Recipients where an Initialize Variable action is used to form the Sender object but this time we define the JSON payload for the object since we do not need to loop through records. There is always going to be one sender whereas the number of recipients can be dynamic.
1.8 Append Sender to array variable
Using an Append to array variable action, the sender variable is appended to the array variable in 1.4 Email activity parties.
1.9 Email activity parties array
This action is optional, you do not need this at all. I have this Compose action that's referencing the array variable output for two reasons,
- To check that my array that was from the appended variables of recipients and senders was correct.
- To show you in the vlog what the array variable looks like to validate that it is in fact correctly structured and defined.
1.10 odata.type workaround
As mentioned earlier this was a hack AK originally came up with to serialize the @odata.type.
**💡 BUT WAIT 💡**
Since filming another Microsoft community person, Ryan Maclean, tweeted an issue about his unbound action and I commented to share AK's method which then also led to Andrew Butenko chipping in to solve it.
Then came the question of why from Matt, why does flow behave like this?
Whenever the flow life gives me lemons 🍋🍋🍋 I turn to the Flow Ninja, John Liu 🐱👤. John is a supreme flow being (no joke) cause he's super intelligent when it comes to Logic Apps or flow in Power Automate. John shared this microsoft.com article with me (scroll down) which I then shared in Ryan's thread. He takes it one step further by sharing a hack. Ryan also came up with a hack too.
Go follow these two on Twitter if you haven't already done so! Do it.
So you can now skip action 1.10 odata.type workaround by following John or Ryan's hack.
Then came the question of why from Matt, why does flow behave like this?
Whenever the flow life gives me lemons 🍋🍋🍋 I turn to the Flow Ninja, John Liu 🐱👤. John is a supreme flow being (no joke) cause he's super intelligent when it comes to Logic Apps or flow in Power Automate. John shared this microsoft.com article with me (scroll down) which I then shared in Ryan's thread. He takes it one step further by sharing a hack. Ryan also came up with a hack too.
Go follow these two on Twitter if you haven't already done so! Do it.
1.11 Email target
Now that we have the email activity parties array from the appended variables of recipients and senders, it can now be referenced in the overall JSON payload. I am using an Initialize Variable to honour AK's method but you could probably define this directly in the final action.
This is based on AK's investigation and my investigation where I studied the JSON payload from the Dynamics 365 Community Forum post I provided earlier.
1.12 Perform an unbound action
The final action is to use the CDS perform an unbound action where the SendEmailFromTemplate action is used.
The information required by this action are
- The action which is SendEmailFromTemplate
- The ID of the email template. This can be found in the URL of your email template record (refer to vlog)
- Regarding which is the Case record
- Target which is the JSON payload defined in my 1.11 Email target action
Woo hoo, we are ready to automate! #LetsAutomate
Power Automate in action
Good to go, update the Send Trust policy renewal field in my Case record and away it goes!
Summary
What previously couldn't be achieved using out-of-the-box classic workflows can be achieved with flow in Power Automate - sending emails to multiple recipients (dynamically) using an Email Template. This excites me because it takes Dynamics 365 and CDS to the next level as use cases can be met using flows in Power Automate.
Thanks for checking out this WTF episode and catch you in my next one.
Toodles.
Thanks for checking out this WTF episode and catch you in my next one.
Toodles.
0 comments:
Post a Comment