Tuesday, 20 October 2020

How to do Proper Case in flow

06:24 Posted by Benitez Here , , , , , No comments
Earlier this year I faced a challenge with flow in Power Automate - I needed to make a value that was in uppercase and format it as proper case.

What is Proper Case? 

Proper Case is 

...any text that is written with each of the first letters of every word being capitalized.

For example

Hello World Today

where in bold is the first letter of the word displayed as uppercase

What's supported today with flow in Power Automate?

Proper Case is a function supported today in Canvas apps.

However with flows in Power Automate, the function does not yet exist. You can vote for the function to be made available from this Power Automate Idea.

Don't worry, in this #WTF episode I share with you how to do Proper Case with flow in Power Automate using some actions until the function is supported in flow. My brain came up with the idea to first make the value all in lowercase and then make the first letter of each word as as uppercase.

Let's flow

The following is what my flow in Power Automate looks like.

The trigger

For the purpose of learning in this WTF episode I have used the trigger of Manually trigger a flow with a text input. The text input will be where a value in uppercase will be entered and the output therefore will be used in the next action.

Compose - Split into array

The value to be transformed into Proper Case will be in uppercase - HELLO WORLD TODAY as an example. As mentioned earlier, the value will be transformed to lowercase in order to make the first letter uppercase. The first action is Compose and I am referencing a couple of functions
  1. Using the tolower function transforms the value from uppercase to lowercase - note this can be skipped if your value is already as lowercase
  2. Then by using an additional function of split where we split by space, an array will be formed so that we can loop through the words to make the first letter as uppercase.
The following is my expression used in the compose action.

split(tolower(triggerBody()['text']), ' ')

The following is the output of the compose action where the words in the string value is all in lowercase.

Apply to each

Since the output from the previous action is an array the apply to each action can be used to loop through the words.

Compose action - Uppercase first letter

In this action I am referencing multiple functions in a single expression. In my vlog I broke it down to explain the purpose of the different functions in the expression. What I did forget to mention though in my vlog is that I came across this expression from a Power Automate community forum post. Credit to the author of the post and not to me for this part.

The breakdown is in the following screenshot.

How to make the first letter as uppercase (#1)

To make the first letter as uppercase, two functions are used.
  1. toupper will make the value as uppercase
  2. by wrapping it with first function, only the first character in the value will be capitalized
The following is my expression where I'm referencing the item from the Apply to each action.

first(toupper(item()))

The output is the following where the h from hello is capitalized.

How to identify the length of the word (#2)

The purpose of this function is to know how many characters make up the word for the next two functions.

The following is my expression where I'm referencing the item from the Apply to each action.

length(item())

The output is the following where 5 is the number of characters in hello.

How to identify the difference of characters from the first letter of the word (#3)

The purpose of this function is to be able subtract the first letter, which is 1, from 5 since it is the number of characters in hello. This will make sense in the next two functions.

The following is my expression where I'm referencing the item from the Apply to each action.

sub(length(item()), 1)

The output is the following where 4 is the remaining difference when you subtract 1 from 5 (which is the number of characters in hello).

How to retrieve the remaining characters (#4)

A substring function is then wrapped around the previous expression so that from position 1 of the string value, h, the remaining characters in the string is retrieved.

The following is my expression where I'm referencing the previous two functions (2 & 3).

substring(item(), 1, sub(length(item()), 1))

The output is the following where ello is the characters retrieved.

Wrapping it altogether

Using the concat function will then combine all of the functions together to form the single expression.

The following is my expression.

concat(first(toupper(item())), substring(item(), 1, sub(length(item()), 1)))

The output is the following where the uppercase letter H is combined with ello so that the value is in Proper Case.

Join Array

I'm going to pause here as there's something I need to share and explain, Pieter's method.

Pieter's method

Pieter's method was something I didn't know of until my friend John Liu showed me. In the past whenever I've had to reference an array downstream in a flow where the array is formed from an apply to each, I've used the Initialize Variable and Set Variable actions.


Pieter's method removes the need for these two actions by referencing the output in the Apply to each in a compose action outside of the Apply to each action. In my scenario I can reference the output from the Compose action.

If you try reference the output through Dynamic Content, you won't see it.


What I did was create another Compose action within the apply to each action and referenced the output from the previous compose action.


Next step is to grab the expression by clicking on the ellipsis of the action and select peek code.


From here I copied the expression and used it in my compose action outside of the array.


Now that I know what the expression is of the output, I can use it in my join function for the Compose action. Since the capitalized words are in the form of an array, we need to 'join' them back together. This is where the join function is handy. 

The following is my expression

join(outputs('Uppercase_first_letter'), ' ')

This will result in the following as the output, hooray!

Flow in action

Time to see the flow in action 😃

Manually trigger the flow using the test feature and enter HELLO WORLD TODAY in the text input. The end result will be the words as Proper Case. Ta da!

Summary

Proper Case is a function that is not supported today but in this #WTF episode I outline what can be done in the meantime. The method I share is one way of achieving it.

Till next time 😊 #LetsAutomate

Wednesday, 7 October 2020

How to create a dynamic Record URL for a model-driven app

In classic workflows we can create a hyperlink in the email message content of an email activity. One neat feature is the ability to insert a Record URL to enable the recipient to click and browse to the record immediately. Now this is only relevant if that recipient is a licensed user of Dynamics 365 or CDS. If the user does not have the appropriate license, does not have the appropriate entity permissions they won't be able to view the record.

The question is: How do we replicate this classic workflows feature in a flow with Power Automate? I learnt how to do this earlier this year and Scott Durow shared a tip that he learnt from #LowCode queen Sara Lagerquist - might have been from their collab presentation at Scottish Summit 2020. In this WTF episode I'll share my method with you.

The Use Case

As the newly assigned Owner of a Case,

I want to receive an email with a hyperlink to the Case record,

so that I can easily view the Case and action accordingly.

Whenever a new Owner is assigned to a Case, an email is to be sent to the user with a hyperlink that directs them to the Case record within the Customer Service Hub app.

Understanding the URL of a model-driven app

If you look closely at the URL in your browser when viewing a record in a model-driven app it will be the following.

https://benitezheredev.crm6.dynamics.com/main.aspx?appid=fc11177c-0f6b-e811-a95c-000d3ae13628&pagetype=entityrecord&etn=incident&id=02193772-695f-4185-93f2-867abd56ac6c

This is what we want to replicate. The answers are all there in the URL staring at us. I'll break it down.

1. This is the Organisation Base URL of the instance.

2. As per the docs.microsoft.com article,  "All entity forms and views are displayed in the main.aspx page. Query string parameters passed to this page control what will be displayed." This is required to load the record within a model-driven app.

3. This is the ID of the model-driven app.

4. This defines what type of page you want to load. In this scenario the user is directed to a record, therefore the value is entity record. It also defines defines what entity you want to load where you need to reference the logical name of the entity. In this scenario it is the Case record. For more information about this please refer to this docs.microsoft.com article.

5. This is the GUID of the record you want to load.

What I'll cover next is how to retrieve 1, 3, 4 and 5 

How to identify the Organisation URL (#1)

This is straight forward. Behold, I give you Natraj's blog post.

Whenever you use a CDS action in flow with Power Automate there will be a property value of @odata.id which will contain the Organisation Base URL. Natraj's technique is to retrieve that value and apply some functions so that only the Organisation Base URL is extracted.

My expression is the following

first(split(outputs('Get_a_record')?['body/@odata.id'], '/api/'))

The functions used here is split and first.

The split allows us to separate the Organisation URL from the rest of the value. A split will result in an array and we only want the first row in the array which is where the first function comes in handy.

In my vlog I initially showed you the split function,

split(outputs('Get_a_record')?['body/@odata.id'], '/api/')

which will result in an array with two rows as the output. The first row is the Base Organisation URL.


The next action I forgot to show the run history of in my vlog. When you wrap the split function around with a first function, it will retrieve only the first row which is the Organisation Base URL.

first(split(outputs('Get_a_record')?['body/@odata.id'], '/api/'))


For your learning, I have this expression in a Compose action to demonstrate what this expression achieves. Later in the flow I use this same expression in my expression that forms the Record URL rather than the output of the
Compose since it's for educational purposes.

How to retrieve the model-driven app ID (#3)

As mentioned earlier, I learnt from the beloved Scott Durow who learnt it from the #LowCode Queen Sara Lagerquist. I now have the privilege of sharing it with you 😊

When you create a custom model-driven app, the following screen will be presented to you where you have to fill in the name and other details.


What happens behind the scenes is that a new record will be created in an entity called appmodule and each record will have an appmoduleid.

To test this out, take a look at your model-driven app URL and copy the ID you see.


In a new browser tab you're going to call the Dynamics 365/CDS API using the copied ID value.

https://YOURORG.crm6.dynamics.com/api/data/v9.1/appmodules?$filter=appmoduleid eq COPIED ID VALUE

You should now see the details of the app displayed.


Since there are multiple model-driven apps in every instance, we can narrow it down using a CDS List Records action with a filter query applied. The Name value of the model-driven app will be used to only return the appmodule so that we can retrieve the appmoduleid.

How to identify the logical name for the page type definition (#5)

Since we are loading a record, the page type value is 'entity record.'

The next part is to provide the logical name of the record. The clue is already in the Dynamics 365 record URL. It's after the "etn=" reference in the URL, this is the logical name of the entity of the Dynamics 365 record.

For more details about page type please refer to this docs.microsoft.com article.

How to retrieve the record GUID (#5)

There should be an action in your flow that has the GUID of the record you want to generate the Dynamics Record URL for. Reference the field that represents the GUID, the unique identifier.

Putting it all together

The last bit is to combine it altogether. The finishing touch is using the concat function to tie it all up.

The expression is the following.

concat(first(split(outputs('Get_User_record')?['body/@odata.id'], '/api/')),'/main.aspx?appid=',outputs('Retrieve_appmodule')?['body']?['value'][0]?['appmoduleid'],'&pagetype=entityrecord&etn=incident&id=',triggerOutputs()?['body/incidentid'])
It may look like a pile of words and characters but I'll break it down again so that it's easier for you understand and learn 😊

1. This is the expression that will retrieve the first row after we split the @odata.id value as explained earlier in this blog post.

2. This is required to load an entity record within a model-driven app.

3. This is the expression to retrieve the ID of the model-driven app where we used a CDS List Records action to retrieve the appmodule based on the Name of the app, Customer Service Hub. In this expression I am using one of the method's from another #WTF episode - How to Avoid the Apply to Each from appearing where we reference the first row in the output of the CDS List Records action.

4. This defines that the page type to load is entity record and the entity to load is the Case entity. The Case entity logical name is incident.

5. This is the GUID of the Case record from the trigger of the flow. When a Case record Owner is updated, it will trigger this flow.

That is the beautiful expression!

Using the expression

I'm using a Compose action to not only use the expression to embed the Record URL as a hyperlink but also format the email with HTML.

The output of this compose action is then referenced in the Description of the email activity that is created through a CDS Create a new record action.


What my flow in Power Automate looks like

As seen in my vlog the following is my flow.



The HTML content is a Compose action that has the expression that forms the Record URL as seen earlier in this blog post.
The last three actions are ones I have mentioned in previously #WTF episodes to create and send a CDS email activity via flow so check them out if you want to learn more about them.

Power Automate in action

Time to test the flow by assigning a new Owner to the Case record.


Awesome sauce, as you can see the Record URL created by the expression in flow successfully directs the user to the instance of a specified model-driven app and loads the Case record.

Keeping up with Application Lifecycle Management [ALM]

This method is what I call "ALM" friendly and compatible. When you are customizing, configuring or extending Dynamics 365 or CDS it is best practice to do so in a sandbox environment such as a DEV instance. When you're ready to move your components across to a target instance such as UAT, you move it through solutions. 

When you view records across different instances like DEV and UAT, the appmoduleid will be different. If your method of forming the Record URL does not accommodate loading the app based on the instance, there's a high chance the end user will encounter the error like the following because the appmoduleid does not exist in the target environment.

By using the CDS List Records action to retrieve the appmoduleid value this method is compatible with any instance as long as

  1. the entity is in the target instance. If it isn't then the hyperlink will not work
  2. the name of the model-driven app is the same across all environments

The name of the model-driven app is usually the same in each instance such as DEV, UAT and Production. If the name of the model-driven app is different in each instance, then this method isn't going to work because of how the filter in the CDS List Records action uses the name value.

Other methods blogged about in our community

What I've shared with you in this #WTF episode is one way of forming the Record URL in flow with Power Automate. Linn Zaw Win shared his method in his blog post. 

Summary

By using a functions in a single expression you can form the Record URL and embed it as a hyperlink for a CDS email activity or even a Microsoft Teams message.

Catch you in the next #WTF episode

Don't forget to do a shout on Twitter or leave a comment in my vlog if this has helped you out
🤗

Thursday, 11 June 2020

PDF Automation series - Part 3: How to automate generating a PDF and attach to an outbound email

The final of my PDF Automation series covers how to use the custom connector for Dynamics 365 to automate generating a PDF and attach to an outbound email. Refer to the following as a prerequisite to what the previous two WTF episodes covered which will be reference in this finale of my PDF Automation series.
**I'd like to make it clear that as of today, Convert to PDF is configured and supported in the Dynamics 365 Sales Hub app**

In this WTF episode I will cover the flow in Power Automate that references the Document Template record from Part 1 and the custom connector for Dynamics 365 from Part 2.

Use Case

Before we jump into the How To's, a reminder that the use case for this series will be

As a customer,

I want to receive a Tax Receipt of my purchase in an email,

so that I have confirmation of my purchase.

The life cycle of the process will be
  1. A Dynamics 365 Invoice record is updated to paid.
  2. My flow in Power Automate is triggered that handles all the magic.
  3. The customer associated to the Invoice will receive an email with a PDF attached that represents their Tax Receipt.

What my flow in Power Automate looks like

My flow uses the custom connector and both actions that you would have read/seen in Part 2: How to create a custom connector for Dynamics 365. The flow is created in a solution and I'm using the CDS current environment connector.

1.0 Trigger - When an Invoice is updated to Paid

The trigger is when the status reason is Updated to Paid where the end user in the Dynamics 365 Sales Hub App clicks on Invoice Paid in the ribbon. 


I have also configured two filters for the trigger
  1. Only trigger if the Status Reason field has been updated
  2. And if the Status Reason field equals Paid

1.1 Retrieve queues

Using the CDS List records action I am retrieving a Queue with the name of "No Reply" as defined in my Filter Query field. To learn more about the configuration required to be able to send from a Queue, refer to Episode 23 -  Sending an email from a Queue with Power Automate.


1.2 Create a new email activity record

Using the CDS Create a record action an email activity will be created where the sender will be the Queue from 1.1 Retrieve queues and the To recipient is the customer of the Invoice.

I've cut out area of the action as some it is not used otherwise it would have been a long screenshot 😅 The other configuration is providing a Description, setting the Regarding value to the Invoice and the Subject of the email.

1.3 Retrieve Document Template record

Using the CDS List records action I am retrieving a Document Template record where I had uploaded a Word Template I created. This would be Document Template record from Part 1. This will be the template used to generate the PDF for an a paid Invoice where it includes information such as the customer, the bill to address, the invoice items and so forth.

The filter query is the name of the Document Template record.

1.4 Generate PDF

Woo hoo! This is where the action created in the custom connector is used. The Generate PDF action is used where the values are provided for the JSON payload of the API request. I covered this in Part 2.
  • The EntityTypeCode represents the entity that the PDF will be generated against. In my use case it is the Invoice entity where the value is 1090.
  • The @odata.type is Microsoft.Dynamics.CRM.documenttemplate
  • The documenttemplateid is the GUID of a Document Template record. Refer to Part 1. My expression since the CDS List record action is used to retrieve the Document Template record is the following to avoid the Apply to Each from appearing
    • outputs('1.3_Retrieve_Document_Template_record')?['body/value'][0]?['documenttemplateid']
  • The SelectedRecords property is the GUID of the Invoice record from the trigger that the PDF will be generated against. I included the additional characters the API request expects which I explained in Part 2.

1.5 Create Email Attachment

The other custom action used is creating an activitymimeattachment record by 
  • binding to an email message which is from 1.2 Create a new email activity record action
  • referencing "email" as the objecttypecode
  • using the base64 PdfFile property in the JSON response of 1.4 Generate PDF action
  • using the Invoice ID from the trigger as the file name and subject
    • note that for the file name ".pdf" is used as the file format

1.6 Perform a bound action

The last step is the CDS Perform a bound action using the SendEmail action. Simply reference the email message from 1.2 Create a new email activity record and set the IssueSend value to Yes.

Power Automate in action

Update an existing Invoice to Paid and refresh to see the email activity created with a PDF that represents a Tax Receipt.

Unit testing

The following are screenshots to confirm the attachment record was correctly created. I mentioned in Part 2 that I learnt from this forum post of how the activitymimeattachment should be created first where Dynamics 365 will then create the corresponding attachment record.

This was the Invoice that was updated to Paid.


Here's the activitymimeattachment response from the custom connector. The activitymimeattachment was successfully created.

I queried the activitymimeattachment API to retrieve the record details. 
  1. The activitymimeattachmentid matches the id from the response.
  2. I can see the corresponding attachment record was created too.
  3. I can also see the activitymimeattachment record was binded to the created email activity.

I queried the attachment API to retrieve the record details. I can see the attachmentid matches the property value from the activitymimeattachment record and the filename and subject matches the Invoice ID.


Lastly, I queried the emails API to retrieve the record details. I can see that the activityid value matches the _objectid_value from the activitymimeattachment and that the Invoice ID referenced in the email subject matches the paid Invoice. It all ties together nicely 👍

Another handy blog post

Another blog post you can refer to that has a different flow in Power Automate but achieves the same result is by another Microsoft MVP in our community, Aric Levin which you can read here. What I like about Aric's blog post is that it would be an option for customers who use CDS but not Dynamics 365 as the Convert to PDF functionality as of today resides in the Dynamics 365 Sales Hub app. Aric also uses the Office 365 action to send the email with the generated PDF file which means the storage consumption of CDS will not be impacted. The exception is if the App for Outlook is installed and tracking is enabled which means the sent email from Outlook may be created as an email activity based on the configuration of tracked emails for the user.

Summary

Automating generating PDFs from the Dynamics 365 Sales Hub app is achievable with Power Automate through
  1. A Word Template uploaded against a Document Template record
  2. A custom connector for Dynamics 365 that calls the ExportPdfDocument API and the activitymimeattachment API
  3. A flow that references the Document Template from No.1 and use the custom actions created in the custom connector for Dynamics 365 from No.2
I hope you enjoyed my PDF Automation series 🙂

Your Vote Counts!

If you want Convert to PDF across custom entities then vote for this Dynamics 365 Idea to gain momentum. I would love to see this functionality across the Dynamics 365 platform so let's do our part by voting for it.

via GIPHY

Where my Starship Troopers fans at?! 😁

Wednesday, 10 June 2020

PDF Automation series - Part 2: How to create a custom connector for Dynamics 365

In Part 1: How to create a Word template in Dynamics 365 of my PDF automation series I covered how to create a Word Template in Dynamics 365 as a Document Template record is referenced in the API request that will generate the PDF based on the entity defined in the Word Template.

In this episode, Part 2 will cover how to create a custom connector for Dynamics 365 in Power Automate. In the custom connector two actions will be created where there will be 
  1. An action that calls the ExportPdfDocument Dynamics 365 API request - this will generate a PDF based on a Document Template record that has a defined Word Template
  2. An action that calls the activitymimeattachments Dynamics 365 API request - this will attach the PDF file generated from the previous action to an email activity record
Prior to creating a custom connector, a prerequisite task is to create an App Registration in Azure Portal which I will cover.

**I'd like to make it clear that as of today, Convert to PDF is configured and supported in the Dynamics 365 Sales Hub app**

Use Case

Before we jump into the How To's, a reminder that the use case for this series will be

As a customer,

I want to receive a Tax Receipt of my purchase in an email,

so that I have confirmation of my purchase.

The life cycle of the process will be
  1. A Dynamics 365 Invoice record is updated to paid.
  2. My flow in Power Automate is triggered that handles all the magic.
  3. The customer associated to the Invoice will receive an email with a PDF attached that represents their Tax Receipt.

Why is a custom connector needed?

This PDF Automation series stemmed from my curiosity of whether we can automate the Convert to PDF functionality. As mentioned in my vlog, as of today the Convert to PDF is what we refer to as "on-demand" where it requires an end user to select a Document Template before creating or emailing the converted Word Template associated to the Document Template record as a PDF. 

Stefan Strube another Microsoft MVP in our community published this blog post where he shared how to automatically generate a PDF which is fundamental to my PDF Automation series. Thanks Stefan and hello! 😄 The action used in Stefan's blog post is the Invoke an HTTPs request which works.

If there is a use case for an end user to trigger the automation on selection of a record the following experience will appear where they are prompted to provide the configuration information for the Invoke an HTTPs request action. Chances are the end user may not know this information and it's not a smooth end user experience. The workaround is to use a custom connector that has the authentication configured to prevent the end user from having to enter in details regarding the Dynamics 365 instance for the Sales Hub app.

How to create an App Registration in Azure Portal for your Dynamics 365 instance

An App Registration is required to allow the custom connector to use Azure AD as a way of authentication to the Dynamics 365 instance. Whenever connectors are used in flows within Power Automate, a connection using credentials to the service is needed. For example when using a SharePoint action you have the ability to select a connection. Same concept applies to a custom connector. A couple of material you can read,
  1. This docs.microsoft.com article clearly explains the steps required to create an App Registration in Azure portal.
  2. This docs.microsoft.com article provides some further explanation on what is an App Registration.
The following are the steps that I showed in my WTF vlog.

Log into Azure portal and search for App Registration.


Click on +New Registration.


Enter a Name and select whether you want a single tenant or multiple tenant followed by the register button. We'll come back to the Redirect URI configuration after the customer connector has been created.


There will be a message that indicates the App Registration is being created.


Once created you'll see details of the App Registration which will be referenced when the custom connector for Dynamics 365 is created.


A couple of things need to be configured afterwards. The first one I showed in my vlog was to enable API permissions for Dynamics 365. The custom connector will access with the Dynamics 365 API using the connection configured for the selected custom action (this will make sense in Part 3 of my PDF Automation series). Head over to API permissions and click on +Add a permission and select the Dynamics CRM option.


Tick the checkbox for the permission that is displayed and click Add permissions.


A message will appear that confirms the permissions has been added and it will be listed.


The other configuration is to create a Client Secret which will be for the security of the custom connector for Dynamics 365. Head to Certificates & secrets, click +New client secret and provide a name for the Client Secret. Select the option Never for the expiry setting and then click add.


The Client Secret will be created. Copy the Client Secret value and save it some where as this will be used for the security of the custom connector for Dynamics 365.

How to create a custom connector for Dynamics 365 in Power Automate

Once the App Registration for Dynamics 365 has been created a custom connector can be created in Power Automate. Head to the Power Automate maker site and select Custom connectors. Click on +New custom connector and select the option to Create from blank.


Name the custom connector and click continue.


An icon can be selected to identify the custom connector. In my vlog I was using a Pineapple icon from icons8. A background colour can also be defined. For the Scheme value, leave it as HTTPS.


The Host value will be the organisation part of the Dynamics 365 instance URL.


Click Create connector to create the custom connector before moving on to the Security configuration step.


How to configure the security for the custom connector

Next security for the custom connector needs to be configured. Click Edit and then select the option of OAuth 2.0.


For the Client ID and Client Secret values, copy and paste from the App Registration in Azure Portal. You would have saved your Client Secret some where from the earlier step. Copy and paste the full organisation URL of the Dynamics 365 instance including the HTTPS for the Resource URL value


Remember that Redirect URI configuration field in the App Registration when the App Registration was created? The Redirect URI configuration field in the App Registration needs to be updated with the Redirect URL generated in the Security configurations of the custom connector for Dynamics 365.


Before moving on to creating the custom actions in the Definition step of the custom connector, click Update connector. Always click this whenever you apply any configuration.

How to create Actions for the custom connector

When we use connectors available today in flow within Power Automate, we have the ability to select either a trigger or an action for the connector.


Now that the custom connector has been created, the next step is to create actions which involve providing the API request and JSON Payload of that API request.

1. Creating an action that will generate a PDF

As seen in Stefan Strube's blog post, the Dynamics 365 API request to call that is used by the Dynamics 365 Sales Hub app is the ExportPdfDocument using the POST method. I am using the same JSON payload that Stefan shared but with one adjustment where I am excluding the following highlighted in Red. When I was putting this all together earlier this year I found that it would fail if I included these character references.
{
       "EntityTypeCode": 1084,
       "SelectedTemplate": {
              "@odata.type": "Microsoft.Dynamics.CRM.documenttemplate",
              "documenttemplateid": "153dc496-d79d-e711-8109-e0071b65ce81"
       },
       "SelectedRecords": "[\"{E3A79DA1-9B91-E811-8133-E0071B65CE81}\"]"
}
To create a new action
  1. Click on +New action.
  2. Enter a name.
  3. Enter a value for the Operation ID, I use the same value as the name but all in lowercase.
  4. Click on +Import from sample to create the API request.
  5. Select the method of the API request. In my use case it is POST.
  6. Provide the URL of the API request. In my use case it is
    • https://ORG.crm6.dynamics.com/api/data/v9.1/ExportPdfDocument
  7. Provide the JSON Payload.
  8. Click Import.
  9. Click Update Connector.

My JSON Payload is the following
{
    "EntityTypeCode": 1090,
    "SelectedTemplate": {
      "@odata.type": "Microsoft.Dynamics.CRM.documenttemplate",
      "documenttemplateid": "d2b253f9-5433-ea11-a813-000d3a795c19"
    },
    "SelectedRecords": "4c339def-0c43-ea11-a812-000d3a795c19"
}
  • The EntityTypeCode represents the entity that the PDF will be generated against. In my use case it is the Invoice entity.
  • The SelectedTemplate block represents the Document Template record with the Word Template that the API request will use to generate the PDF. This will be the Document Template record from Part 1: How to create a Word template in Dynamics 365.
  • The SelectedRecords property represents the Invoice record the PDF will be generated against.
To retrieve the EntityTypeCode value of the record for the JSON Payload you can call the EntityDefinition API Request with some filters to obtain the value.
https://ORG.crm6.dynamics.com/api/data/v9.1/EntityDefinitions?$select=LogicalName,ObjectTypeCode&$filter=LogicalName eq 'invoice'
Once the connector has been updated you'll see that your action has been created.

One of the things I love about the custom connector in Power Automate is the ability to test the API request directly within the custom connector configuration. Click on Test and create a new connection to authenticate with the request using your credentials.


Here comes the fun part, the testing! Enter in the property values for the JSON payload. A reminder from earlier what the properties represent,
  • The EntityTypeCode represents the entity that the PDF will be generated against. In my use case it is the Invoice entity where the value is 1090.
  • The SelectedTemplate block represents the Document Template record with the Word Template that the API request will use to generate the PDF. This will be the Document Template record from Part 1: How to create a Word template in Dynamics 365.
  • The SelectedRecords property represents the Invoice record the PDF will be generated against.

To retrieve the Document Template ID value, open the Document Template record of the Word Template to be referenced. Copy the GUID at the end of the record URL.


To retrieve the Invoice ID value of an Invoice record to be referenced, open an existing Invoice record. Copy the GUID at the end of the record URL.


If you test the operation right now it will fail. Remember how we didn't include the extra characters when creating the Request of the action? We need to include some of those characters in the SelectedRecords field. The characters to include is the following highlighted in yellow.
[{"21ce9319-22a3-ea11-a812-000d3a7981c5"}]
[ ] Means make it an array, " " means make it a string, so this is saying make it an array of strings. I interpreted this as the API request likely needs an array of all the GUIDs that you want to generate a PDF for.

Once the SelectedRecords property value has been updated, it's ready to test! The status response should be 200 and you'll see the JSON returned where there is a PdfFile property with a base64 value that represents the encoded PDF file that was generated.

2. Creating an action that will create the PDF as an activitymimeattachment record

Activitymimeattachment is the entity record which will contain information of the attachment such as the encoded base64 value. This forum post explains it well.

The reason why we are using an API request to create the activitymimeattachment record is because today if we attempt to use the Create action from the CDS current environment connector and select the second attachments option when searching for the entity (from memory) the following error will be thrown,
The 'Create' method does not support entities of type 'attachment'.
If you then try the other attachments option you'll still come across a problem as the Item ID is required which can't really be provided since the attachment record has not been created. Therefore I thought of calling the activitymimeattachments API request instead as I learnt from this forum post that the activitymimeattachment should be created first where Dynamics 365 will then create the corresponding attachment record.

Side note: Linn Zaw Win did write a blog post that shows you how to send an email which uses a combination of the CDS standard connector and CDS CE standard connector.

Same steps as before, create a new action, provide a name and operation id value and lastly the JSON payload request. Make sure you click Update connector once the Request has been created.


I worked out the JSON Payload request from this blog post where the following properties are
  • objectid_activitypointer@odata.bind
    • The email activity record to bind the activitymimeattachment
  • objecttypecode
    • The value is email since the activitymimeattachment will be associated to an email activity record
  • body
    • This is the encoded base64 value of what would be the generated PDF. In my WTF vlog I was using this site to generate an encoded sample of  "Hello World. This is a demo"
  • filename
    • This is the file name. It is important in this step to include a reference to the file type. In my WTF vlog I was using a .txt
  • subject
    • This is a descriptive subject for the attachment. In my WTF vlog I used "Hello World" as an example
The next part is testing it out again. To bind the activitymimeattachment to an email open an existing record and copy the GUID from the email record URL.


For the encoded base64 value I used this site to generate a sample value.


Once all the property values have been entered, click test operation. The status response should be 204 and you'll see the JSON returned. Open the email activity record in the Dynamics 365 Sales Hub to double check the file was created and attached to the email activity record 👍

Check the custom connector is visible when creating a new flow in Power Automate

Now that we've learnt how to do an App Registration and create a custom connector with actions for Dynamics 365 we can now check it out in flow! Head to the Power Automate maker site and create a new flow. Once the trigger has been defined add a new action and click on the Custom tab. The two actions created will be displayed and can be selected to add to the flow. Awesome sauce 🙌😄💙

Summary

It's pretty cool that we can create a custom connector for Dynamics 365 if the actions available in the CDS current environment connector will not suffice what needs to be achieved. The beauty of Dynamics 365 and CDS is that the APIs have existed for years and with flow in Power Automate it's now even more easier to perform API requests as the authentication can be configured directly in Power Automate. For PDF automation all it requires is
  1. An App Registration through Azure Portal for the Dynamics 365 instance of the Sales Hub App
  2. Create a custom connector using the App Registration details with custom actions using the Dynamics 365/CDS APIs
Once this is all fine tuned, the custom connector will be available to use in the environment for you to continue making flows in Power Automate.

Thanks for reading this detailed blog post of Part 2 of my automation series. Part 3 will cover all of this in action using actions I've covered in previous WTF episodes.

Your Vote Counts!

If you want Convert to PDF across custom entities then vote for this Dynamics 365 Idea to gain momentum. I would love to see this functionality across the Dynamics 365 platform so let's do our part by voting for it.

via GIPHY

Where my Starship Troopers fans at?! 😁