Flow Blueprint: “Sync Site Pages” (Copy pages from Source site to Target site) — list-driven with Power Automate
Below is a ready-to-build blueprint you can reproduce in Power Automate. The flow is triggered by one specific SharePoint list item (a row that contains Source/Target URLs + a “Sync” command). It then copies .aspx files from the Site Pages library in the source site to the target site.
This design uses:
- Standard SharePoint connector triggers/actions (Microsoft Learn)
- Trigger conditions to only run when the user requests sync (Microsoft Learn)
- Send an HTTP request to SharePoint when you need REST flexibility (Microsoft Learn)
- SharePoint REST patterns for working with folders/files (Microsoft Learn)
1) SharePoint “Control List” schema (the row you click/mark to run sync)
Create a list (example: SitePageSyncRequests). Each item represents one sync job.
Columns (recommended)
- Title (text) – friendly job name
- SourceSiteUrl (text) –
https://contoso.sharepoint.com/sites/SourceSite - TargetSiteUrl (text) –
https://contoso.sharepoint.com/sites/TargetSite - Status (Choice) –
Draft | Requested | Running | Completed | Failed - Scope (Choice) –
AllPages | ModifiedSince - ModifiedSince (DateTime) – optional
- IncludeSubfolders (Yes/No)
- OverwriteExisting (Yes/No)
- PublishAfterCopy (Yes/No) – optional
- CopiedCount (Number)
- FailedCount (Number)
- LastRunUtc (DateTime)
- LastResult (Multiple lines)
2) Trigger options (choose one)
Option A (recommended): “When an item is created or modified”
This is the most common pattern, and it’s documented as a core SharePoint trigger. (Microsoft Learn)
Option B: “For a selected item”
This gives you a “Run flow” button directly from the list item UI. If your team prefers a manual click instead of changing Status, it’s often ideal. (Microsoft Learn)
In the blueprint below I’ll use Option A, because it’s the most reliable for automation and auditability.
3) Flow build (Step-by-step)
Step 0 — Trigger
Trigger: SharePoint → When an item is created or modified (Microsoft Learn)
- Site Address: (the site where your control list lives)
- List Name:
SitePageSyncRequests
Trigger condition (IMPORTANT)
Add a trigger condition so the flow runs only when the item is explicitly requesting sync. This pattern is widely used and recommended to avoid useless runs. (Microsoft Learn)
If you use Status (Choice), use:
@equals(triggerBody()?['Status']?['Value'], 'Requested')
Step 1 — Initialize variables
Add these actions:
- Initialize variable →
varSourceSiteUrl(String)
Value:
@{triggerBody()?['SourceSiteUrl']}
- Initialize variable →
varTargetSiteUrl(String)
Value:
@{triggerBody()?['TargetSiteUrl']}
- Initialize variable →
varIncludeSubfolders(Boolean)
Value:
@{coalesce(triggerBody()?['IncludeSubfolders'], false)}
- Initialize variable →
varOverwriteExisting(Boolean)
Value:
@{coalesce(triggerBody()?['OverwriteExisting'], false)}
- Initialize variable →
varPublishAfterCopy(Boolean)
Value:
@{coalesce(triggerBody()?['PublishAfterCopy'], false)}
- Initialize variable →
varScope(String)
Value:
@{triggerBody()?['Scope']?['Value']}
- Initialize variable →
varModifiedSince(String)
Value:
@{triggerBody()?['ModifiedSince']}
- Initialize variable →
varCopiedCount(Integer) = 0 - Initialize variable →
varFailedCount(Integer) = 0
Step 2 — Mark as Running
Update item (on the control list item)
- Status =
Running - LastRunUtc =
utcNow() - LastResult =
Starting page sync...
Step 3 — List pages from the source “Site Pages” library
You can do this with either:
- SharePoint connector “Get files (properties only)”
- Or REST using “Send an HTTP request to SharePoint”
I recommend REST here (more consistent across sites)
Use Send an HTTP request to SharePoint (Source site connection). This action is specifically meant for calling SharePoint REST when connector actions aren’t enough or you need precise control. (Microsoft Learn)
Action: SharePoint → Send an HTTP request to SharePoint
- Site Address: Use a connection pointing to the source site (see note below)
- Method: GET
- Uri:
/_api/web/GetFolderByServerRelativeUrl('SitePages')/Files?$select=Name,ServerRelativeUrl,TimeLastModified
- Headers:
{ "Accept": "application/json;odata=nometadata"}
This folder/files REST pattern is documented in Microsoft Learn. (Microsoft Learn)
Important note about dynamic SourceSiteUrl
Power Automate SharePoint actions typically want a “Site Address” chosen from the connector UI. If you need fully dynamic source/target URLs per row, the most robust approach is to create two flows (or two connections) per environment or per managed set of sites. In many organizations, that’s acceptable because you control which sites can be synced.
Step 4 — Parse JSON (files list)
Add Parse JSON with the body from the HTTP action.
Content:
@body('Send_an_HTTP_request_to_SharePoint')
Schema (works for the $select above):
{ "type": "object", "properties": { "value": { "type": "array", "items": { "type": "object", "properties": { "Name": { "type": "string" }, "ServerRelativeUrl": { "type": "string" }, "TimeLastModified": { "type": "string" } }, "required": [ "Name", "ServerRelativeUrl" ] } } }}
Step 5 — Filter only .aspx pages (and optionally by ModifiedSince)
Add Filter array over:
@body('Parse_JSON')?['value']
Condition (only .aspx)
@endswith(item()?['Name'], '.aspx')
Optional: apply ModifiedSince filter
If Scope = ModifiedSince, add a Condition around a second Filter array.
Example condition:
@and( equals(variables('varScope'),'ModifiedSince'), greaterOrEquals(item()?['TimeLastModified'], variables('varModifiedSince')))
Tip: Keep it simple first (AllPages), then add ModifiedSince once the baseline works.
Step 6 — Apply to each (copy pages)
Add Apply to each using the filtered array output.
Inside the loop:
6.1 Get file content from source page
Use Send an HTTP request to SharePoint again (Source site).
- Method: GET
- Uri:
@{concat('/_api/web/GetFileByServerRelativeUrl(''', item()?['ServerRelativeUrl'], ''')/$value')}
- Headers:
{ "Accept": "application/octet-stream"}
This “working with folders/files with REST” guidance covers file retrieval patterns and is the right reference for these operations. (Microsoft Learn)
6.2 Create/Overwrite file in target Site Pages
Use SharePoint connector Create file (Target site).
- Site Address: target site
- Folder Path:
Site Pages - File Name:
@{item()?['Name']}
- File Content:
@body('HTTP_Get_Page_Binary')
Overwrite behavior
Depending on your tenant/connector version, “Create file” may support overwrite. If not, use this pattern:
- Get files (properties only) (Target) with filter on name
- If exists:
- Delete file
- Create file
(Yes, it’s extra calls, but it’s consistent and easy to troubleshoot.)
Step 7 — Optional: Publish / approve after copy
If your pages library requires check-in/publish, you can run an additional REST call(s) after creating the file.
This is exactly the kind of case where Send an HTTP request to SharePoint is intended to fill gaps in connector coverage. (Microsoft Learn)
Condition: varPublishAfterCopy = true
Then call REST methods to check in / publish. (Exact endpoints vary by library settings and whether approval is enabled.)
Practical advice: implement publishing only after you’ve validated that “copy the file” works end-to-end. Publishing is frequently the first “environment-dependent” step.
Step 8 — Update counters (success/failure) and continue
Wrap the loop body with a Scope pattern:
- Scope:
Try_Copy_Page - Scope:
Catch_Copy_Page(run after if Try failed)
On success:
set variable varCopiedCount = add(variables('varCopiedCount'), 1)
On failure:
set variable varFailedCount = add(variables('varFailedCount'), 1)
Also append a short message to LastResult if you want a per-page log (optional—can become large).
Step 9 — Finalize the control list item
After the loop:
Update item
- CopiedCount =
varCopiedCount - FailedCount =
varFailedCount - Status:
- If FailedCount > 0 →
Failed(orCompletedWithErrors) - Else →
Completed
- If FailedCount > 0 →
- LastResult:
@{concat('Copied: ', string(variables('varCopiedCount')), ' | Failed: ', string(variables('varFailedCount')))}
4) Hardening recommendations (so it survives production)
- Concurrency control: set Apply to each concurrency = 1 initially
- Small delay (100–300ms) between pages if you see throttling
- Do not fail the whole run because one page failed: always continue and log
5) Final table (build checklist)
| Step | Component | Action name | Key config | Result |
|---|---|---|---|---|
| 0 | Trigger | When an item is created or modified | Trigger condition: Status = Requested (Microsoft Learn) | Flow runs only when asked |
| 1 | Setup | Initialize variables | Source/Target URLs + flags | Clean, readable flow |
| 2 | Status | Update item | Status = Running | Operator sees progress |
| 3 | Enumerate | Send an HTTP request to SharePoint (GET) | GetFolderByServerRelativeUrl('SitePages')/Files (Microsoft Learn) | Returns list of pages |
| 4 | Parse | Parse JSON | Schema with Name/ServerRelativeUrl/TimeLastModified | Structured data |
| 5 | Filter | Filter array | .aspx only (+ optional ModifiedSince) | Limits what you copy |
| 6 | Copy | Apply to each | Per page: GET $value, Create file | Copies each page |
| 7 | Optional | Publish/check-in step | Conditional via flag | Pages visible/published (if required) |
| 8 | Logging | Counters + error handling | Try/Catch scopes | Reliable runs |
| 9 | Finalize | Update item | Completed/Failed + counts | Audit + transparency |
