Create and fill an Excel file dynamically with Power Automate
In this article, I will describe how to create an Excel file dynamically with Power Automate and fill it with data from the Common Data Service. Creating those dynamically can be a bit tricky. Therefore, I will provide step by step description of how to do that.
As mentioned by Alex in the comments the “Create File” step does output the GraphId of the new file. This means the step “Get GraphId” is not needed anylonger. Thanks to Alex for pointing that out.
Scenario
Let’s assume AB Company is using Dynamics 365 for Marketing and manages events within this module. The handling of Dynamics is centralized and not every event Organizer has access to the system.
Since it is crucial for the event Organizers to have a correct list of persons that have registered (and therefore will participate) one must send this information to the Organizer.
Exactly that is what a customer brought as a requirement to me a few weeks ago. I quickly logged into the Power Automate portal and searched whether there is an excel connector. I found two of those.
I thought: “Sure that’s possible. Should not be a big deal and not a problem at all”.
Problem
While creating the flow(s) it got clear that this first thought/impression was quite foolish. There are some pitfalls one must know about and avoid.
Creating a file
The first problem I encountered was that it is neither possible to create an excel file from scratch nor to change it just in “memory” of a Power Automate flow. The Actions of the Excel Online connector do need the file to exist either on OneDrive, or SharePoint.
Reference file
The UI of the actions of the Excel connector is a bit misleading here. When choosing a file that exists the Action will show the file Path. One could think that that is what needs to be provided when handling dynamic files as well, but actually we need the GraphId of the file.
Root folder
The actions of the SharePoint trigger are not uniform when it comes to the root folder. Some have the root of the SharePoint Site as a start and some do already start in the “Shared Documents” folder.
Solution
The idea is to create a template excel file that will be stored on SharePoint. Within the template, we will create a table that will be used by the action. We then copy that file in SharePoint and fill it with the needed data.
Thanks to Matt Collins-Jones for giving me the hint to this solution.
Our flow will execute the following steps whenever it is executed.
- Load the content of our template file
- Load Event Name
- Create a file in SharePoint with the content of the template file
- Get the graphId of the created file
- Load all Event Registrations
- For every Event Registration:
- Compose the row to add
- Add a row to the table
- Get the content of the file
- Send an email with the file as an attachment
We will store the file in SharePoint and use the “Excel Online (Business)” connector to add data.
Matt Collins-Jones has a YouTube series on the “Excel Online (Business)” connector. For every action of the connector, he has created an explanation video. I strongly encourage you to check it out.
Template
First of all, we have to create a template file. To do so we open excel and add the names of the desired columns. For this demo we choose:
- Name
- Role
The Excel connector can’t handle special characters in the column names. If you have some you will get an error that you have to provide a value for “item”. So make sure to avoid those.
The second step is to add a table to the file. We go to Insert –> Table, in the popup we check the box beside “My table has headers” and press “OK”.
The last step within Excel is to configure a proper name for our table. Unter the tab “Table Design” you will find “Table Name” on the far-left side. We will name it “EventRegistrations”.
We will now upload the file to the SharePoint site.
As you can see, we have created a folder “FlowRunFolder” within the root folder. Here we can find a “Temp” folder that will hold the files that the flow is creating.
Flow
Now it is time to get our hands dirty and create the flow.
Trigger
For the ease of this article, we will use the “When a record is selected” trigger of the Common Data Service connector.
Please notice that this trigger is only available in the Common Data Service connector and not within the Common Data Service (current environment) connector.
Please also notice that you should never rename this step. If you do it will/might not appear in the model-driven app.
Environment
As the Environment we will choose “Default”.
Note: The UI is misleading here. Even if you choose “Default” as the environment the Connector will go against the current environment. Thanks, Sarah Lagerquist for clearing that up to me.
Entity Name
Here we choose “Events”
Steps
Let’s dive into the interesting part of this article.
Load content of Template
We will use the “Get file content” action of the SharePoint connector.
Here we first have to choose the Site we would like to use and then we can pick the file by using the little folder icon on the right.
Load Event Name
To load the event name we use the “Get a record” action of the Common Data Service (current environment) connector.
Entity name
As the Entity we choose “Events”
Item ID
The Item ID is the Event Unique identifier from the trigger
Select Query
We select the Event name (“msevtmgt_name”) as well as an custom field that contains the email we should send the mail to.
Create file
To create the temporary file, we will use the “Create file” action of the SharePoint connector.
Site Address
You choose the SharePoint site you would like to use for the temporary file.
Folder Path
Here you choose the folder you would like to store the temporary files in, by using the folder icon on the right.
File Name
In this field, you can decide on the file name (including the ending, in our case “xlsx”). We will use a combination of “AttendeeList”, Event Name, and current Date and Time. All the spaces in the event name we replace with “_”. The Date will be in the format “yyyy-MM-dd_HH-mm-ss”. See the forms below
replace(outputs('Get_Event')?['body/msevtmgt_name'], ' ', '_')
convertFromUtc(utcNow(), 'W. Europe Standard Time', 'yyyy-MM-dd_HH-mm-ss')
File Content
The file content is the output of the previous step.
Get GraphId
Since the “Create File” step is outputting the GraphId already this step isn’t needed anylonger.
This step isn’t necessary if you use oneDrive. Go directly to “Load Event Registrations“
To get the graphID of the created file we have to send an HTTP request to SharePoint. To do so we use the “Send an HTTP request to SharePoint” action of the SharePoint connector.
Site Address
You choose the SharePoint site you created the temporary file in.
Method
The method should be “GET”.
Uri
This is the complex part of this step. The Uri starts with version 2.0 of the API followed by the path to the file (including the file name). The path starts with a leading “/” and should not contain the “Shared Documents” folder. To make this work we have to transform the path to a uri (so for example all “/” will be replaced with “%2F”). Fortunately, there is a function within Power Automate that does exactly that, “uriComponent“.
The prefix
_api/v2.0/drive/root:
The expression for the path
uriComponent(concat('/FlowRunFolder/Temp','/',outputs('Create_temp_file')?['body/Name']))
Headers
As a header, we use “accept” as the key and “application/json” as the value.
Load Event Registrations
To load all Event Registrations, we will use the “List Records” action of the Common Data Service (current environment) connector.
Entity name
We do select “Event Registrations” as the entity.
Select Query
We would like to include the Role and contact in our list. Therefore we select the primary role and the contact field
msevtmgt_primaryrole,_msevtmgt_contactid_value
Filter Query
To get only those Event Registrations that are related to the Event we handle we will filter on the event ID (which comes from the trigger). Since we have an expand query on the contact we would like to make sure that this field is present as well.
_msevtmgt_eventid_value eq @{triggerBody()?['entity']?['msevtmgt_eventid']} and msevtmgt_ContactId/contactid ne null
Expand Query
In the Excel we would like to include the fullname and email of the contact. Therefore we add an expand query to fetch those information.
msevtmgt_ContactId($select=fullname,emailaddress1)
Creating all the queries can easily be done with the FetchXML Builder from Jonas Rapp.
Loop
We have to execute the next two steps for every Event Registration we fetched. Therefore we will add an “Apply to each” action.
Input
As the input we select the output of the list Event Registrations step
Compose row
To add a row dynamically to an excel file we have to compose the row in Json format. For that, we use the build in “Compose” action.
This step should be within the loop.
Input
The Input of this action is a JSON object that represents a row of our Excel. Every Column should be present and referenced by the name we typed in the Template. For the demo those are “Name”, “E-Mail” and “Role”. The schema is
"<Column Name>":"<value>"
Name and Email are from the contact and can be selected from our List Event Registration straight away. The Role is a OptionSet. Here we would like to select the label and not the value (that means Attendee and not a number that no one understands). Use the following expression to achieve this
items('For_all_Event_Registrations')?['msevtmgt_primaryrole@OData.Community.Display.V1.FormattedValue']
{ "Name": "@{items('For_all_Event_Registrations')?['msevtmgt_contactid/fullname']}", "E-Mail": "@{items('For_all_Event_Registrations')?['msevtmgt_contactid/emailaddress1']}", "Role": "@{items('For_all_Event_Registrations')?['msevtmgt_primaryrole@OData.Community.Display.V1.FormattedValue']}" }
Add row to Excel
To add our composed row to the table we use the “Add a row into a table” action of the Excel Online (Business) connector.
This step should be within the loop.
Some configurations of this Action do only appear when the previous one was provided.
Location
We the same SharePoint site we used to create the file in.
Sometimes the site will not be shown in the dropdown. It is also possible to copy the URL from one of the SharePoint steps and paste it in.
Document Library
We have to choose the correct Document Library. Usually we only have one option, “Documents”.
File
Since we are handling a file that will be created dynamically we can not choose the file from the file selector. We have to provide the graphId of our temporary file.
outputs('Get_GraphId_of_temp_file')?['body/id']
If you are using oneDrive you skipped the part of getting the graphId. To get the correct file we will use the last part (after the dot) of the Id which is returned by the oneDrive create file step. To do so you can use the following expression
split(outputs('Create_file')?['body/Id'], '.')[1]
Table
Here we choose “Enter custom value” and write the name of our table we created in the Template. For this demo it is “EventRegistrations”
Row
This should be the output of our previous compose step.
Wait step
Since the step to add rows to the file is working async we have to wait a bit to be sure that the rows where added. We use the built-in “Delay” action to wait for 2 minutes.
Count
Should be “2”.
Unit
Should be “Minutes”.
Load file content
To be able to send out the file as an attachment we have to load the content of the file we created and filled. We use the same action as we used earlier, “Get file content”.
Site Address
This should be the same SharePoint Site we used everywhere earlier.
File Identifier
Here we will use the Id we get as an output from the step which created our temp file.
outputs('Create_temp_file')?['body/Id']
Send email
The last step is to send an email with the file attached. Here you could use whatever email connector you would like to (for example google). For this demo, we will use the “Send an email (V2)” action of the Office 365 Outlook connector.
To
As the recipient we choose the value of the custom field we created on the Event Entity and fetched with our event data.
outputs('Get_Event')?['body/???_email']
Subject
Choose whatever subject you would like. I have included the Event name.
Body
Write a text that should be included in the mail. I have as well included the Event Name.
Attachments Name – 1
Here we choose the Name of the file we created. This is the output of the “create” step.
outputs('Create_temp_file')?['body/Name']
Attachments Content – 1
Here we choose the output of the previous step that has fetched the file content.
outputs('Get_content_of_temp_file')?['body']
Complete Flow
In the following screenshot you can see the complete flow.
There are obviously several things that could be improved in this demo flow.
- Delete temp file
- Check if there are Event Registrations and terminate if not
- Recurrent trigger
- Error handling
I might take a look at those in future blog posts.
Conclusion
As you can see the solution isn’t overly complicated if you know what to look out for, as well as knowing how to tackle it.
I hope this article helped you. Feel free to contact me if you have any questions. I am always happy to help.
Please also look at my speaking engagements if you would like to hear me talk about something related to Dynamics 365/CDS.
You can also subscribe and get new blog posts emailed to you directly.
Hi Benedikt,
Great article – I think this does exactly what I need but i’m getting an “unexpected response from the service” error message 400 on the “Send an HTTP request to SharePoint” action. I think this might be to do with permissions for Graph or something to do with the interaction with graph – could you advise if there are any obvious steps that need to be taken to set this up?
Many thanks, Dan
Hej Dan,
thank you, great it could help you.
Unfortunately, I am not an expert on Graph or SharePoint. But as you mentioned I would guess it has something to do with the permission. There were not obvious steps other than those in the article. I would check the following things:
– Is the URL correct?
– Can you find a more specific error message somewhere? Maybe in the Developer Tools of your browser when you try to request the URL your self.
Hope it helps.
Hi. Thanks for the article! Do you have an example of how the Compose Row action might look like?
I have tried to format it in different ways to make it work, but my “Add a row into a table” action keeps failing. I get the error message “A value must be provided for item.”
Right now the Compose action is formatted like this:
{
“Column A” : “ABC”,
“Column B”: “CDE”,
“Column C”: “123”
}
I tried to format it in this way but it didn’t help either
{
“Column A” : [“ABC”],
“Column B”: [“CDE”],
“Column C”: [“123”]
}
Thanks!
Hej Sam,
the first format you wrote should work. In the chapter Compose row I describe how the JSON has to be formatted.
It is important to have all the columns of the Excel file within the composed object.
Are you sure that the problem is the composed object?
“A value must be provided for item.” as an error could also mean the excel file.
Thanks to your comment I double-checked my code and noticed that I had a misspelled one of the columns. Thanks a lot for your reply.
Hi Benedikt
After fighting for a while with Power Automate and Excel, I wanted a fool-proof way to create a brand new Excel file without having to rely on a template somewhere to copy. I found a solution, which i mention here in case someone else stumbles on your blog trying to do the same thing.
The way I tackled it was to create a workflow that can be called as a child workflow. In the workflow I start with a hard-coded Base 64 encoded “blank.xlsx” in a variable. Then i can create a new xlsx whenever I need using the in-built base64ToBinary function to generate the binary file content.
Thanks for this Flow.
I have been working on my own template & have made it to the “Get Content of Temp File”
It can not seem to find the file identifier “File not found”
Have you had this issue?
Thanks in advance
Hej Jason,
glad you like the post.
You have uploaded your file to your SharePoint, selected it via the small folder icon beside the “File Identifier” field in the get content step and you still get a “File not found” error?
In general, I had problems using the same approach with OneDrive.
Let me know. I am eager to help.
//Benedikt
Thanks Benedikt
We were able to get this flow working with the different steps i needed to create a report from template
See ya next time. On to the next One
Perfect. Glad I was able to help in the end.
I even updated the blog here to reflect the findings we had in our session.
Awesome post! We’re currently trying to implement a version of it, but we are getting a ‘Unable to find table’ error in the Add row to excel file step. We made sure to check the file after it was created to see if the table was created and named correctly, and it was correct.
Any chance you came across this issue throughout your building of this flow and were you able to resolve the issue?
Thanks!
Glad you like it.
I have not crossed this issue yet.
Do you select the Table with dynamic value?
If you still have the problem let me know. We could arrange a meeting to take a look at it.
//Benedikt
Great guide, really helpful, thank you very much!
I went ahead and tried switching the step “Send an HTTP Request” for “Get File Metadata” then inserted the dynamic ID in the “File” field in “Add a Row Into a Table” and it worked as well. Do you know if there may be a future potential problem with it?
Thanks in advance!
Great solution! Might be the better way to do it. Thanks for the tip, I will try it.
I am not aware of any potential problem with this.
Thank you so much !!!! such a massive help and solved something I’ve been trying to sort out for a while.
Great article! This is exactly what I needed. For anyone pulling in lookup values and not wanting to display the GUID, you can easily add additional Dataverse calls and Get a Row by ID inside the loop before composing the JSON. Worked like a charm.
Great article. This was very helpful!
Since this is currently the top search result for “Power Automate create excel file” I though I’d share an improvement.
This might have come in an update since writing the article, but the HTTP Request step is not necessary.
The “Create file” action outputs the Dynamic Content “Id” which is the GraphId. You can go ahead and use that directly in the File input field of the “Add a row into a table” action.
This saves you the most cumbersome part of your current setup.
Thanks for this input, Alex. I have added the information to the post.
Thank you very much for the advice.
I’ve tried this with a ‘when item is created’ from a SharePoint list, so it’s only handling a single row of data (perhaps that’s the issue).
My table in my XLSX is called ‘Results’, but I’m getting the error ‘No table was found with the name ‘Results’, in my ‘Add a row to a table’. I’ve checked my Temp folder, and the files are being generated, albeit without the .xlsx extension (the extension is defined in the create file).
Hej Steve,
I think the problem is that the file does not have the .xlsx extension. Which is weird. Could you send some screenshots of your flow?
//Benedikt
Hi Benedikt,
Thanks very much. I’ve fixed the extension issue, but the problem remains. It generates the temporary spreadsheet with .xlsx, but the error happens when it gets to populating it.
The Flow:
https://drive.google.com/file/d/1ycIyM50H-CYcXdkdB-LXkzeABsSRz67O/view?usp=sharing
The Error:
https://drive.google.com/file/d/14zREScl7CoRWQcuTCad3_RD_He1BJMdw/view?usp=sharing
Thanks for the Screenshots. Could you provide the file you use as well?
…The ‘file’ field is as follows:
split(outputs(‘Create_file’)?[‘body/Id’], ‘.’)[1]
Thanks. This is it:
https://docs.google.com/spreadsheets/d/17-of50xPElr_Gwc_DPSB-4uP7VOwwKr8/edit?usp=sharing&ouid=110538730891239678176&rtpof=true&sd=true
I _appear_ to have resolved the issue. It now works.
I don’t know if this is bad practice, but in the ‘File’ field of the ‘Add a row into a table’ action, I replaced ‘split(outputs(‘Create_file’)?[‘body/Id’], ‘.’)[1]’ with “Path” from the Dynamic Content library under ‘Create File’.
Again, thank you SO much for these instructions, you seem to be the only person on the internet to have come up with this solution.
Hi Benedikt,
Thank you for this useful tutorial. On a previous comment you mentioned, “In the Chapter Compose row…..is important to have all the columns of the Excel file within the composed object.” Does this include hidden columns?
Best regards,
Veronica
This is a good question. I have not tested whether it is working with hidden columns. I will test it.
Hi Benedikt,
Great tutorial!
For anybody struggling with the HTTP request, you can use “Get file properties” action from Sharepoint, giving the ItemId in the “Id” field.
When adding the row in the table, you can reference the “Identifier” from “Get file properties” as the “File”.
The “Get file metadata” mentioned above should work similarly. But the HTTP request is always a good exercise, because sometimes you really need it.
Hope it helps!
I’m stumped by a strange error. In my scenario the flow is triggered from a power app, which sends the json data to add to excel. The flow creates the excel file in onedrive and then adds the rows to it. However, it only works when I do it myself. When any other user tries to execute it, the file is created in their OneDrive, but the table actions fail with a 404 not found error.
Unfortunately, I don’t have so much experience with oneDrive. But it sounds like something with the access rights. Do you use your account when connecting to oneDrive in the step where you create the file? In that case the problem might be that the other user doesn’t have access to the file.
Thanks for this wonderful post.
I need some help here. While using above steps I can add 1000 rows only, can you please suggest if there is some kind of limitations?
I don’t know of any limitations. What exactly is limiting you? Is it getting the records from Dataverse (so you don’t receive more than 1000) or something else?
Hello,
I have the same issue that Steven had with adding a row into the table. I can’t seem to dynamically access the file that was just created on SharePoint. I used both ‘path’ and ‘id’ from the ‘create file’ output and the ‘get file metadata’ output but still no luck. I tried to use the ‘Get tables’ and it says item not found so the issue appears to be with the file and not the table within the file. Excellent
Hej Jason,
could you specify which error you get when you try to use the path or id?
//Benedikt
Hi Benedikt,
Thank you for your response. I am not sure why but I cannot seem to reproduce the error. It works now after a few days. Thank you for creating this guide. It helped me a lot.
Need to update and/or create many rows in Excel?
Try Excel batch actions in Power Automate:
https://youtu.be/HiEU34Ix5gA
Hi Benedikt, I need to extract a table from an excel file, and fill that data into a newly created file. Any tips on getting the data from the first excel file and filling it in?
Hej,
sorry for the late reply. I personally never have done that but if there is a table defined in our excel you could potentially extract it using the “List rows present in table” action of the Excel Power Automate connector.
https://learn.microsoft.com/en-us/connectors/excelonlinebusiness/#list-rows-present-in-a-table