Recursively Copy Files Between OneDrive Accounts Using Azure Logic Apps
Author: Ofir Gavish
β¨ What I Built
I built an Azure Logic App that recursively copies all files and subfolders from a folder in one OneDrive account to another, preserving the full folder structure and only copying files that have not been modified in the last 30 days.
π¬ Tech Stack
- Azure Logic Apps (Consumption)
- OneDrive for Business connectors
- Dynamic expressions and variables
- GitHub for hosting the JSON code view
π My Goal
- Recursively copy all files from a OneDrive folder and its subfolders
- Only copy files that have not been modified in the last 30 days
- Maintain the original folder structure in the destination
π§ How It Works
- Use a recurrence trigger
- Get root folder ID via metadata lookup
- Initialize variables for folder queue and current context
- Use an
Until
loop to walk folders recursively - List children in each folder, append subfolders to the queue
- Copy eligible files with updated paths
π« Challenges I Faced
No folder trigger or folder metadata action
Logic Apps doesn't provide a dedicated trigger for changes in OneDrive folders, nor does it have a clear action named "Get folder metadata." I initially searched for a way to target folders directly but realized the available "Get file metadata using path" action also works for folders. Once I confirmed that folders could be treated as file items, I used this action with a path like /
to retrieve the root folder ID and continue from there.
Path-based access failed
At first, I tried using folder paths (like /TSLog
) in the Logic App to access subfolders. However, I consistently received 404 errors, even though the folders existed. The fix came from inspecting the Logic App's behavior in code view: I noticed it was using double-encoded OneDrive item IDs instead of folder paths. I switched the folder listing logic to use
encodeURIComponent(encodeURIComponent(id))
instead of a plain path, and it worked flawlessly.
SetVariable logic was out of order
In the Until
loop, I was pulling the current folder values (id
, name
, path
) from the foldersToProcess
array. However, I placed the SetVariable
steps before updating the array with skip(...)
. As a result, the same folder was processed repeatedly. I resolved this by placing the SetVariable
steps after the array was updated with skip(variables('foldersToProcess'), 1)
, so each loop correctly pulled the next folder to process.
Array wasnβt updating
I used a Compose
action to get the updated array of folders with @skip(...)
, but forgot to assign its output back to the foldersToProcess
variable. Without that assignment, the array never changed, and I kept seeing the loop process the same folder repeatedly. I fixed this by adding a SetVariable
step that stored the output of Compose
into foldersToProcess
.
lastModifiedDateTime was wrong
I tried filtering files based on a property called lastModifiedDateTime
, assuming it was a standard field. This led to runtime errors because that field didnβt exist in the OneDrive metadata response. After checking the actual response from the OneDrive API, I found that the correct property was LastModified
. I updated my condition to compare against that field, and the date filtering worked as expected.
π Folder Structure Preservation
I added a path
property to every folder in the queue and used that to recreate subfolders in the destination. Files are saved under /TSLogArchive/{relativePath}
.
π¦ Full Code View on GitHub
You can find the full Logic App JSON here. Just update the OneDrive connections and root folder ID to make it your own.
π Lessons Learned
- Always validate what Logic Apps puts into expressions
- Debug with Compose steps frequently
- Using item IDs is more reliable than paths
- Dynamic content often inserts strings, not true expressions
π Whatβs Next?
- Add email notification on completion
- Track copy history or audit log
- Enhance with file type filters or metadata sync
β¨ Wrap-Up
This Logic App makes it easy to sync content between OneDrive accounts, fully automated and low-code. I hope it saves you time and effort. Contributions welcome!