web
You’re offline. This is a read only version of the page.
close
Skip to main content

Announcements

News and Announcements icon
Community site session details

Community site session details

Session Id :
Power Platform Community / Forums / Power Automate / Power Automate flow: C...
Power Automate
Suggested Answer

Power Automate flow: Compare CSV email attachment with Excel in cloud and append new rows

(1) ShareShare
ReportReport
Posted on by 6

Hi all,

 

I’m trying to build a Power Automate flow but I’m stuck. The goal is:

 

  1. Check for incoming emails in Outlook.


  2. If an email has a CSV attachment, read its contents.


  3. Compare the CSV rows with an existing Excel file stored in OneDrive/SharePoint.


  4. Append only the new rows (ones not already in the Excel file) into that Excel file.



  5.  
 

I’ve tried setting this up but couldn’t get the compare + append logic working.

 

Has anyone built something similar, or could share the steps / expressions needed to:

 

  • Parse the CSV into usable rows/columns,


  • Compare each row against existing Excel table rows, and


  • Append the unique ones?


  •  
 

Any help or example flows would be much appreciated!

 

Thanks!

Categories:
I have the same question (0)
  • Suggested answer
    Michael E. Gernaey Profile Picture
    53,976 Moderator on at
     
    For someone reason I swore I answered this...but must have not.
     
    It's best to always share what it is you have done, as we do not like regularly writing full flows for people, it limits who/how many we can help
     
    As for your question, much depends on how you determine what is a duplicate.
     
    I can only give you base details based on what has been shared and asked.
     
    1. Create your Automated Flow to trigger when you receive an email with the proper attachment (possibly subject)
    2. Initialize an Array Variable that will hold all the lines of your CSV, initialize it as null (blank)
    3. even though you only hopefully have one, use Get Attachments
    4. Create an Apply to each to iterate through the attachments
    5. use Get attachment from the loop iteration
    5a. add a condition to make sure the attachment you are looping to has the right name (since there can be other attachments)
    5b. Add an Append to Array action. In here use a Split expression on the Content of the attachment, based on the line terminator (probably carriage return)
     
    Exit the get attachments loop
     
    6. Add another Apply to each, with the Array as your input
    6a. I suggest adding a compose where you use Split to split the current iteration of our CSV line by the comma. The output of this compose will enable you to access the values like this outputs('ComposeName')[0] (so ordinal position).
     
    6b. Add a list rows present in a table, adding in a filter based on field names and their values in the ordinal position of 6a
    6c. Add a condition and to check if the length of the returned items in 6b is equal to 1, if so then you know it exists, if not then add it in the No side.
     
    Another option
     
    starting at step 6
     
    6. Do a List Rows present in a Table
    7. Add an Apply to each on the CSV array (not step 6)
    7a. again do a compose to split the value of the iteration by the comma and access values by their ordinal position
    7b. add a Filter Array
    --inside the Filter Array, you input is actually the outputs from Step 6
    --and then use an advanced filter to compare the entire array from 6 with the values from the Compose you did in 7a
    7c. Add a condition
    check if the length of 7b is greater than 0, if so do not add a row, if not add a row.
     

    If these suggestions help resolve your issue, Please consider Marking the answer as such and also maybe a like.

    Thank you!
    Sincerely, Michael Gernaey
     
  • CU08091219-0 Profile Picture
    6 on at
    Hi @Michael E. Gernaey, thank you for the response and sorry for not sharing any other details as this was my first post and I am very new with Power Automate. Post this I have been going back and forth with ChatGPT and am able to make the flow but then it is copying all the rows and not just the new ones. I will try and put the flow below to the best of my ability

    this flow will run only when a new email arrives and this email will have only 1 CSV attachement and comes only from one person and the CSV and the Excel does not have 0 before the case number.
     
    Full flow — exact actions & expressions
     
    0) Create a new Automated cloud flow with trigger:
    Trigger: When a new email arrives (V3)
    Folder: Inbox (or your folder)
    Include attachments: Yes
    (Optional) Subject filter: set if you want only specific emails
    After adding this trigger, create the rest of the steps below.
     
    1) Get attachment (V2) — (single attachment)
    Add action Get attachment (V2) and set:
    Message Id → choose Message Id from the trigger dynamic content
    Attachment Id → Expression (fx) — paste:
    first(triggerBody()?['attachments'])?['id']
     
    2) Decode CSV (Compose) — name: Decode_CSV
    Add action Compose and in Expression (fx) paste exactly:
    base64ToString(body('Get_Attachment_(V2)')?['contentBytes'])
    (Saves the decoded CSV text.)
     
    3) Split Rows (Compose) — name: Split_Rows
    Add action Compose and in Expression paste:
    split(replace(replace(outputs('Decode_CSV'), decodeUriComponent('%0D%0A'), '\n'), decodeUriComponent('%0D'), '\n'),'\n')
    This normalizes CRLF/CR into \n and splits into an array of lines.
     
    4) Clean Rows (Filter array) — name: Clean_Rows
    Add Filter array action:
    From → pick Outputs of Split_Rows (from Dynamic content)    "outputs('Split_Rows')"
    Click Edit in advanced mode and paste:
    and(not(equals(trim(item()), '')), not(equals(trim(item()), ',,,,,,')))
    This removes blank lines and rows that are only commas.
     
    5) CSV_Data_Rows (Compose) — name: CSV_Data_Rows
    Add Compose and in Expression paste:
    skip(body('Clean_Rows'), 1)
    This skips header row; if your CSV has no header, use outputs('Clean_Rows') instead.
     
    6) List rows present in a table (Excel) — name: Excel_ListRows
    Add List rows present in a table (Excel Online (Business)) and configure:
    Location: OneDrive for Business
    Document Library: OneDrive
    File: navigate to your Excel file (you will update this in the UI)
    Table: Table1
    Important: this runs once, outside the CSV loop. It loads existing Excel rows. (Leave paging defaults.)
     
    7) Select (Data Operations) — name: ExcelCaseNumbers_Select
    Add Select action:
    From → choose value from Excel_ListRows (that is the list of Excel rows)     "outputs('Excel_ListRows')?['body/value']"
    Map:
    Key (type manually): CaseNumber
    Value → click Expression (fx) and paste:
    item()?['Case Number']
    Result: a simple array of objects like [ {"CaseNumber":"1588208"}, {"CaseNumber":"1897848"} ]
     
    8) Apply to each — name: Apply_to_each_CSV_Row
    Add Apply to each. In Select an output use Expression and paste:
    outputs('CSV_Data_Rows')                                                "outputs('CSV_Data_Rows')"
    Settings for this loop: click ellipsis (...) → Settings → Concurrency: turn Off.
    (Important: Excel connector can fail with parallel inserts. Inside this loop add the following actions (order matters):
     
    8.1) Compose_Split (Compose)
    Add Compose named Compose_Split. In Expression, paste:
    split(string(item()), ',')
    This splits the current CSV line into an array of columns. Access with outputs('Compose_Split')[0], [1], etc.
     
    8.2) ExtractCaseNumber (Compose)
    Add Compose named ExtractCaseNumber. In Expression, paste:
    if(equals(trim(outputs('Compose_Split')[0]), ''), '', string(int(replace(replace(trim(outputs('Compose_Split')[0]), '"', ''), ',', ''))))
    This returns the first column (Case Number) cleaned of quotes/spaces. Example output: 01898281 or 1898281 depending on CSV.
    If you need to drop leading zeros (because Excel stores numbers without leading zeros), replace with the safe conversion below only if all your case numbers are numeric and non-empty:
    if(equals(trim(outputs('Compose_Split')[0]), ''), '', string(int(replace(replace(trim(outputs('Compose_Split')[0]), '"', ''), ',', ''))))
    But `int()` will fail on any non-numeric rows — use only when you’re sure the Case Number column is numeric.
     
    8.3) Filter_CheckExisting (Filter array)
    Add Filter array named Filter_CheckExisting.
    From → select the output of ExcelCaseNumbers_Select (the Select action).    "body('ExcelCaseNumbers_Select')"
    Click Edit in advanced mode and paste:
    @equals(@{trim(string(item()?['CaseNumber']))},@{trim(replace(split(string(items('Apply_to_each_CSV_Row')), ',')[0], '"', ''))})
    This compares each Excel CaseNumber to the CSV Case Number.
    Run-check: After one test run inspect Filter_CheckExisting -> Outputs -> Body. For an existing case it should be [ {"CaseNumber":"..."} ]. For a non-existing case it should be [].
     
    8.4) FoundCount (Compose)
    Add Compose named FoundCount. In Expression paste:
    length(body('Filter_CheckExisting'))
    This yields 0 if not found, >=1 if found.
     
    8.5) Condition_IsNew (Condition)
    Add Condition and set:
    Left box: select Outputs of FoundCount from Dynamic content     "outputs('FoundCount')"
    Middle: is equal to
    Right box: 0                      (not sure if I need to just type this as 0 or do a expression and add the 0)
    If yes branch (meaning not found, new row) → add the row.
    If no branch → leave empty.
     
    8.6) Add a row into a table (only inside If yes branch)
    Add Add a row into a table (Excel Online (Business)), configure same File/Table as step 6. For each column, click the field → Expression and paste the mapping below:
    Case Number - outputs('ExtractCaseNumber')
    Case Owner - trim(replace(outputs('Compose_Split')[1], '"', ''))
    Institution Name - trim(replace(outputs('Compose_Split')[2], '"', ''))
    Subject - trim(replace(outputs('Compose_Split')[3], '"', ''))
    Status - trim(replace(outputs('Compose_Split')[4], '"', ''))
    Date/Time Opened - trim(replace(outputs('Compose_Split')[5], '"', ''))
    Date/Time Closed - trim(replace(outputs('Compose_Split')[6], '"', ''))
     
    9) After the loop — mark email processed (recommended)
    Add either Move email (V2) 
    Move email (V2) → Message Id: Message Id from trigger → Destination folder: “Processed”
  • CU08091219-0 Profile Picture
    6 on at
    I was able to work with Gemini and ChatGPT and build a new flow but even after a lot of back and forth it is still copying all the rows 
    1. Trigger
    When a new email arrives (V3)
    Folder: Inbox
    Include Attachments: Yes
    (Optionally filter by From/Subject)
    2. Action – List rows present in a table
    Location: OneDrive for Business
    File: your Excel file
    Table: Table1
    This gets all existing rows in Excel.
    3. Action – Get attachment (V2)
    Message Id: from Trigger
    Attachment Id:
    first(triggerBody()?['attachments'])?['id']
    4. Action – Compose (Decode Base64 Content)
    base64ToString(body('Get_attachment_(V2)')?['contentBytes'])
    5. Action – Compose (Prepare CSV Data)
    skip(split(outputs('Decode_Base64_Content'), uriComponentToString('%0D%0A')), 1)
    6. Action – Filter array (Clean Empty Rows)
    From: Outputs of Step 5
    Condition:
    trim(item())
    is not equal to
    (leave blank)
    7. Action – Select (Build Excel Case List)
    From: value (output of List rows present in a table)
    Map:
    CaseNumber →
    string(item()?['Case Number'])
    This creates a simple array of just Case Numbers from Excel.
    8. Action – Compose (All Excel Case Numbers)
    Input:
    join(body('Select_(Build_Excel_Case_List)'), ',')
    Now you have a single string with all Excel Case Numbers, like: 12345,67890,01902170
    9. Control – Apply to each
    Input: Body from Clean Empty Rows
    10. Inside the loop
    a) Compose (CSV Case Number)
    int(replace(split(items('Apply_to_each'), ',')[0], '"', ''))
    b) Condition (Does Excel already have this Case Number?)
    contains(outputs('All_Excel_Case_Numbers'), string(outputs('CSV_Case_Number')))
    If Yes → Do nothing (skip)
    If No → proceed
    c) Action – Add a row into a table (inside "If No")
    Map fields as before:
    Case Number → outputs('CSV_Case_Number')
    Case Owner → trim(replace(split(items('Apply_to_each'), ',')[1], '"', ''))
    Institution Name → trim(replace(split(items('Apply_to_each'), ',')[2], '"', ''))
    Subject → … etc (continue same as original Gemini flow)

Under review

Thank you for your reply! To ensure a great experience for everyone, your content is awaiting approval by our Community Managers. Please check back later.

Helpful resources

Quick Links

Introducing the 2026 Season 1 community Super Users

Congratulations to our 2026 Super Users!

Kudos to our 2025 Community Spotlight Honorees

Congratulations to our 2025 community superstars!

Congratulations to the March Top 10 Community Leaders!

These are the community rock stars!

Leaderboard > Power Automate

#1
Haque Profile Picture

Haque 569

#2
Valantis Profile Picture

Valantis 484

#3
Vish WR Profile Picture

Vish WR 460

Last 30 days Overall leaderboard