Creating PDF documents with images using Flow

One of the nice document generation features of Flow is to create PDF documents from structured data, such as HTML.  However, if you want to integrate images, you may not see the expected result.  For demonstration purposes, suppose you want to have a PDF image that only includes.  The flow may look something like the image below:


The Flow is broken down into 4 actions:

  1. Compose HTML File - used to compose the HTML body.  In it, you can see the HTML markup that points to an image (e.g. https://<YOUR TENANT>postmediacanoe.files.wordpress.com/2018/09/jackolanterns1000.jpg).
  2. Create file - this is where the HTML is converted into a temporary file, temp.html
  3. Convert file using path - here, the HTML file is converted into PDF.  Note that this action doesn't actually save the PDF file; it just converts the content.
  4. Create file 2 - now, I use the content from action 3 to save the PDF content into a PDF file.
If you look at the temp.html file, you will see the image as expected:

However, if you try to open the corresponding PDF file, the image is missing.

The reason for this is that the Convert file using path action is expecting all the content to exist within the source document (the HTML file).  So, the way to get around this is by extracting the content of the image and including it directly within the HTML body.  Let's have a look at the updated Flow:
I added the Get file content using path to capture the content of the Halloween.png file.  Next, I modified the Compose action to (specifically the <img ...> tag) to include the content of the image.  For the image content itself, you need to include the following expression:


body('Get_file_content_using_path')?['$content']


Note that the name of the body object may be different in your case.  Now, when you generate the PDF, you get the image included in it.

Final Thoughts

As you can see in the final image, the Halloween picture is larger in the PDF file than in the HTML file.  Make sure to structure your HTML carefully to properly size the images and other content.

Bypassing Connector Authentication in PowerApps

Have you ever had a PowerApp request your permission to access various services before it would load?  If so, then that PowerApp is leveraging these connectors in some way.  But what if you don't want each user to have to do that?  What if some of the services require permissions that the end user doesn't have?  In this post, I'll share an interesting work-around to this problem, broken down into 3 scenarios.



When you are using Connectors in PowerApps, each user accessing the PowerApp needs to give permission to the specific connector the first time they use it.  If there are multiple connectors, then the user needs to authenticate each of them, as shown below.



For this post, I've built a simple app to display the user's given name.  To get the given name, I'm using the Office 365 Users connector.



So, how do we get it without having every user give explicit permissions to the Office 365 Users connector?

Scenario 1 - Getting the Given Name directly from PowerApps

In this first scenario, I have added the Office 365 connector directly to the PowerApp (View > Data sources > + Add data source > Office 365 Users).  The text field, then uses the Default method to set the value using Office365Users.UserProfileV2(User().Email).givenName


If you try to access this PowerApp from another account, the first time, the user will be prompted with the following message:


No good!  Try again.

Scenario 2 - Use PowerApp to call a Flow to retrieve the Given Name

In the second scenario, I have removed the Office 365 connector from the PowerApp and replaced it with a Flow called .  This way, the Flow is doing the work with the connector.  The Flow is fairly simple in structure as shown below.



I also had to make the following modifications to the PowerApp

  1. Remove the Office 365 Users connector  (View > Data sources >  Office 365 Users > ... > Remove).  There's no more need for it
  2. On the Screen1, add a Flow for OnStart (Screen 1 > Actions > Flows > Get Given Name).  We need to connect the Flow to the PowerApp when the PowerApp is launched
  3. Change the function for OnStart to Collect(GivenName,'Get Given Name'.Run(User().Email)).  This way, the value will get loaded into a Collection
  4. Change the text field Default value to First(GivenName).givenname.  As the result is stored in a collection, we want to retrieve the first record and get the givenname field.

Save, publish, and try again.  Strike 2!  The Flow is keeping the context of the user and is still requesting the user for permission.

Scenario 3 - Leverage a Secondary Flow to do all the Connector work

In this third scenario, I am leveraging a secondary flow to access the Office 365 connector to retrieve the user's given name.  The secondary flow is invoked using an HTTP trigger.


Here's what the revised first Flow looks like


By doing so, the Flow that is called from the PowerApp and the PowerApp itself have never leverage the Office 365 connector.


Avoiding never-ending Flow triggering

When Microsoft created the Do Until control action, fortunately they took care to avoid situations where loops would run forever by adding control limits that terminate the loop even if the logic condition is not met.  By default, a Do Until loop will terminate after 60 iterations or 1 hour (PT1H in ISO8601 notation).  Using these controls, you can limit the actions to loop up to 5000 times and within the span of 1 month (P1M in ISO 8601 notation). 


However, when building flows that involve triggers which respond to item changes, it is possible to get into situations, where the same Flow will call itself forever.  Consider the example below.


In this example, the Flow is triggered by When an item is created or modified  when an item is changed in the Email Recipients SharePoint list.  Following the trigger is an action that modifies the same item which triggered the Flow in the first place.  As a result, another iteration of the same Flow will be triggered as soon as that action completes. 

You may not be able to completely avoid situations where Flow that is triggered by an item change event will change the same item.  In those scenarios, you could create an additional field who's sole purpose is to determine whether the item should be updated again or not. 


Now, your field will be used to control when the changes should be occurring to the item and, thus, when the Flow should be triggered.

Conditions, Loops, and Switches have moved

If you've been using Flow recently, you may have noticed that the location of the Condition, Do Until, Apply to each, Switches, and Scopes have moved.  Previously they used to be available as part of the ...More button next to the + New step button or as part of the popup menu.



No more.  You will notice that the ...More button as been replaced with a Save button.



Instead, they are now integrated as new actions.



A bit confusing, but same functionality.

Sending Custom Emails with Flow

About a year ago, I wrote an article on how you can use Flow to Mail Merge using Flow using a couple of SharePoint lists.  This works great for sending the same email to all recipients or even structuring the email in different ways based on metadata captured in the Flow.

However, I have faced more and more scenarios, where Flows are created using a service account with elevated permissions that only a select number of users have access to, typically IT.  The content of the email, however, may be set by someone else, such as a marketing or communications person.  In this scenario, changing the email template would require contacting the IT person and requesting for specific changes to be made - not ideal.

In this article, I am discussing the concept of using tokens inside an email template to allow easy formatting without any access to the Flow itself.

Creating the Email Template


We begin again with the email template.  In this example, we will create a template that has a few tokens incorporated in it.  I will create my email template in HTML format, but that is not a strict requirement.  Here's my template

<h1>Welcome email</h1>
<p>Hello <--FIRSTNAME-->,</p>
<p>I have to say, your last name "<--LASTNAME-->" is very nice.</p>

Not the best example, but I hope you get the point.  I am storing this example in my email template list in SharePoint.


Building the Flow


The flow is composed of four steps

  1. Get the email template
  2. Get list of recipients
  3. Replace tokens with desired text
  4. Send the emails

Get the email template

Start by retrieving the email message using the Get items.  To limit the results returned, use the Filter Query option.



Make sure that you only have one email with the same title.  Otherwise, you may get the wrong template.  This can be set by making the Title field in the SharePoint list unique.

Get list of recipients

Similarly to getting the template, use the Get items action to retrieve the list of users who should receive an email.  In my case, I have a field called SendEmail to indicated whether a user should receive an email or not.


Replace tokens with desired text

Here's where the fun begins.  I used the Compose action for each token to replace the token placeholder with the actual value.


Before updating the tokens, I store the email template in a variable.  This way, I can always retrieve the original template for the next email being generated.  You may recall that I have two tokens in this example - <--FIRSTNAME--> and <--LASTNAME-->.  Each one is being replaced separately, although it's possible to do all the updates in a single action.

To replace the <--FIRSTNAME--> token with the name from the user in the SharePoint list, I use the following expression in the Compose action.

replace(variables('Email Template'),'<--FIRSTNAME-->',items('For_each_Recipient')?['First_x0020_Name'])


This action effectively looks up the <--FIRSTNAME--> token in my Email Template variable declared above and replaces it with the First Name of the user from the SharePoint list item.

Next, I proceed with replacing the <--LASTNAME--> token with the Last Name of the user from the SharePoint list item.

replace(outputs('Replace_First_Name'),'<--LASTNAME-->',items('For_each_Recipient')?['Last_x0020_Name'])

You'll note that the second expression input is different.  That is because the input for the second Compose action is in fact the output of the Replace First Name Compose action.

Send Email

In the final step, I am sending the email to the users by looping through the list of all recipients from the Get Email Recipients action.  The body of the email is Output, which is the output of the Replace Last Name Compose action.


Make sure that if you're structuring your email template as HTML (as I did), that you indicate in the Send an email action that it's HTML format.  Otherwise, you'll get a lot of tags in the body of your email.

Once the email is sent, I then reset the Email Template variable to what it originally was with the token place holders so that the next iteration can correctly replace them.

This approach works well, but there are two caveats to consider:
  1. if the tokens are incorrectly written, then they will not be updated in the email itself
  2. if you want to add additional tokens, then IT will need to add rules for parsing it and replacing it.

Keep it Flow-ing...

Employee Check-in Simplified using Power Automate

Company employees who move between offices are often faced with a tedious process of having to check-in at the security desk to get a tempor...