# InfoPath is gone: getting your form data out and rebuilding in Power Apps

> InfoPath Forms Services retired on July 14, 2026. Your forms don't render anymore, but every submission is still sitting there as XML. Here's how to inventory what you had, extract the data before it's stranded, and rebuild the survivors across Power Apps and Power Automate.

[Home](https://tenantcraft.ca/)  [Insights](https://tenantcraft.ca/insights)  Power Platform

Execute Aug 14, 2026  13 min read

Geri Crroj

Microsoft 365 consultant, Niagara, Ontario

## The problem

Two dates have already passed. Publishing new InfoPath forms, or updates to existing ones, was blocked after **May 18, 2026**. InfoPath Forms Services itself was removed from SharePoint Online after **July 14, 2026**, the same day InfoPath Client 2013 went out of support, and Microsoft was explicit that there would be no option to extend past it.

So if you’re scoping this now, you’re not planning ahead of a deadline. You’re doing recovery on forms that already stopped rendering.

The useful part: your data didn’t go anywhere. Every InfoPath submission is still an `.xml` file sitting in its library, downloadable and parseable with a text editor or a few lines of PowerShell. What retired was the rendering path, the piece that turned that XML back into a form in the browser. SharePoint no longer knows how to display it. The file hasn’t moved an inch.

Microsoft’s replacement is the Power Platform, split across two products: Power Apps for the form itself, Power Automate for the workflow that used to run behind it. Most InfoPath forms were half of a pair, so budget for both. A rebuilt form that submits into a list nobody routes for approval has replaced the typing and dropped the process.

The 5-minute version

1.  **Extract form-library data first.** Every submission is still there as XML, but only until someone decommissions the old environment.
2.  **Script the inventory.** Clicking List Settings site by site works at ten sites and falls apart at two hundred.
3.  **Triage into four buckets** before rebuilding anything, because bucket (d) isn’t a Power Apps job at all.
4.  **Check delegation** on every repeating-table rebuild, or your replacement will silently drop rows.

## The admin UI still shows InfoPath, which fools people

Worth knowing before you go looking for confirmation in the tenant. The SharePoint admin center still lists InfoPath under **More features**, described as “Enable browser-based InfoPath forms,” with a working Open button.

![The More features page in the SharePoint admin center, showing an InfoPath card described as Enable browser-based InfoPath forms with an Open button, alongside User profiles, Search, Apps, Records management and Hybrid picker](https://tenantcraft.ca/images/infopath-more-features-admin-center.png)

SharePoint admin center · More features

A month after retirement, the InfoPath entry is still sitting in More features between Records management and Hybrid picker.

Open it and the classic configuration page loads normally, with the browser-enable and render checkboxes both cleared.

![The classic Configure InfoPath Forms Services page showing User Browser-enabled Form Templates with both checkboxes unchecked, and the Exempt User Agents list containing Googlebot, msnbot, crawler, MSOffice, MS Search and Slurp](https://tenantcraft.ca/images/infopath-forms-services-settings-retired.png)

SharePoint admin center · InfoPath

The settings page still renders, and the exempt user agent list is still populated. None of it changes anything now that Forms Services is gone.

Don’t read either page as evidence the service is alive. The admin surface outlived the thing it configured, which is normal for retired SharePoint features and misleading if you’re checking whether you still have a problem. The test that matters is whether a user can open a form. They can’t.

## Step 1: inventory with a script, not a site crawl

You need PnP PowerShell and an Entra ID app registration with **Sites.FullControl.All** as an application permission, admin consented. Read-only scopes won’t enumerate content types across every site collection, so you need the write scope even though you’re only reading. Connect with a certificate rather than an interactive login.

```
# Requires: PnP.PowerShell, app registration with Sites.FullControl.All
Connect-PnPOnline -Url "https://contoso-admin.sharepoint.com" `
    -ClientId $clientId -Tenant "contoso.onmicrosoft.com" -CertificatePath $certPath

$sites = Get-PnPTenantSite -IncludeOneDriveSites:$false

$inventory = foreach ($site in $sites) {
    try {
        Connect-PnPOnline -Url $site.Url -ClientId $clientId `
            -Tenant "contoso.onmicrosoft.com" -CertificatePath $certPath

        # RootFolder is a nested client object that is NOT loaded by default.
        # Ask for it explicitly or the fallback check below throws.
        $lists = Get-PnPList -Includes ItemCount, RootFolder
    }
    catch {
        Write-Warning "Could not connect to $($site.Url): $($_.Exception.Message)"
        continue
    }

    foreach ($list in $lists) {
        # Per-list try/catch, so one unreadable list doesn't abandon the site
        try {
            $ct = Get-PnPContentType -List $list -ErrorAction SilentlyContinue |
                Select-Object -First 1
            $formUrl = $ct.NewFormUrl
            $isInfoPath = ($formUrl -like "*.xsn*") -or ($formUrl -like "*FormServer.aspx*")

            if (-not $isInfoPath -and $list.RootFolder.ServerRelativeUrl) {
                # Some customized list forms never surface the .xsn in NewFormUrl
                $templatePath = "$($list.RootFolder.ServerRelativeUrl)/Item/template.xsn"
                $isInfoPath = $null -ne (Get-PnPFile -Url $templatePath -ErrorAction SilentlyContinue)
            }

            [PSCustomObject]@{
                SiteUrl      = $site.Url
                ListTitle    = $list.Title
                ItemCount    = $list.ItemCount
                FormTemplate = $formUrl
                IsInfoPath   = $isInfoPath
            }
        }
        catch {
            Write-Warning "Skipped $($list.Title) on $($site.Url): $($_.Exception.Message)"
        }
    }
}

$inventory | Where-Object IsInfoPath |
    Export-Csv "C:\InfoPathInventory.csv" -NoTypeInformation
```

The fallback check matters more than it looks. A customized list form doesn’t set `NewFormUrl` to something ending in `.xsn`. It points at a `FormServer.aspx` page with the `.xsn` buried in the query string, so a plain `-like "*.xsn"` test comes back empty on most tenants. Checking for `{list}/Item/template.xsn` catches the forms that skip `NewFormUrl` entirely.

The property-loading trap that makes this under-report

PnP hands back list objects with only some properties populated. Anything nested, `RootFolder` included, has to be named in `-Includes` or touching it throws `The property or field has not been initialized`.

That matters more than a normal error because of where it lands. If the only try/catch is wrapped around the whole site, one uninitialized property aborts that site and the loop moves to the next one, so every remaining list there goes uncounted. You get a clean run, a CSV, and a warning line nobody reads. Catch per list, and treat any warning as a site you have to revisit rather than noise.

Past about 50 sites, expect this to run for hours, and expect 429 throttling. That’s what the try/catch is for, so one bad site doesn’t kill the job. On a couple hundred sites, pre-filter instead: run a tenant search for `FileExtension:xsn` and only crawl the sites that come back.

Then run a second pass for form libraries, which are a different list type (`BaseTemplate` 115, XMLForm) and don’t always surface as a content type form. Sweep ordinary document libraries (`BaseTemplate` 101) at the same time, because stray `.xsn` templates get parked there and missed.

```
foreach ($site in $sites) {
    try {
        Connect-PnPOnline -Url $site.Url -ClientId $clientId `
            -Tenant "contoso.onmicrosoft.com" -CertificatePath $certPath

        $formLibraries = Get-PnPList | Where-Object { $_.BaseTemplate -eq 115 }
        $docLibraries  = Get-PnPList | Where-Object { $_.BaseTemplate -eq 101 }

        foreach ($lib in $formLibraries + $docLibraries) {
            Get-PnPListItem -List $lib -PageSize 500 |
                Where-Object { $_["FileLeafRef"] -like "*.xsn" } |
                ForEach-Object {
                    [PSCustomObject]@{
                        SiteUrl  = $site.Url
                        Library  = $lib.Title
                        FileName = $_["FileLeafRef"]
                    }
                }
        }
    }
    catch {
        Write-Warning "Failed on $($site.Url): $($_.Exception.Message)"
    }
}
```

The output is flat: site, list, item count, template path, and a true/false flag. Export it to CSV and that’s your project plan. `ItemCount` tells you how much is riding on each row.

## Form libraries are the high-risk bucket

List forms and form libraries are not the same problem, and the difference decides your sequencing.

A form library stores every submission as an `.xml` file that only ever rendered correctly against its `.xsn` template, and that rendering path is what got removed. If a library holds years of expense claims or incident reports, getting the data out is the first job, not the last.

```
# Requires: PnP.PowerShell, connected to the site holding the form library
$library = "Expense Claims"
$items = Get-PnPListItem -List $library -PageSize 500

$rows = foreach ($item in $items) {
    $content = Get-PnPFile -Url $item["FileRef"] -AsString
    [xml]$xml = $content

    # InfoPath fields live under the form's own my: namespace. Copy the real
    # xmlns:my value from the root element of one submission before running this.
    $ns = New-Object System.Xml.XmlNamespaceManager($xml.NameTable)
    $ns.AddNamespace("my", "http://schemas.microsoft.com/office/infopath/2003/myXSD")

    [PSCustomObject]@{
        FileName    = $item["FileLeafRef"]
        Requestor   = $xml.SelectSingleNode("//my:Requestor", $ns).InnerText
        Amount      = $xml.SelectSingleNode("//my:Amount", $ns).InnerText
        SubmittedOn = $xml.SelectSingleNode("//my:SubmittedDate", $ns).InnerText
    }
}

$rows | Export-Csv "C:\ExpenseClaimsExtract.csv" -NoTypeInformation
```

Every form template stamps its own GUID into that namespace URI, so open one submission in a text editor and copy the actual `xmlns:my` value before pointing this at a different form. Get the namespace wrong and it fails silently, producing blank fields rather than an error, which is the failure mode most likely to survive a casual spot check.

For a repeating table the same child element repeats once per row. Swap `SelectSingleNode` for `SelectNodes`, loop it, and write one row per node into the child list described below, using the parent item’s ID as the lookup value.

Output has two sensible destinations. `Add-PnPListItem` writes straight into a new SharePoint list if the process is still live, or export to CSV for audit-only records nobody will edit again.

Sequence this before the rebuild, not after

A form you haven’t rebuilt yet is an inconvenience to the people who used it. Historical submissions nobody can open anymore are a records-retention problem, and those escalate to a different set of people. Run the extraction now, while the old environment is still standing.

## Step 2: triage with a rubric

Sort every row from the CSV into one of four buckets. Open each form and check for the specific features. Don’t classify from memory, because the ones that look simple are the ones with a hidden repeating section.

**(a) Fields, required validation, show/hide logic.** A handful of text fields, some required-field rules, maybe a section that appears based on a dropdown. Rebuild as a customized list form. Budget 2 to 4 hours.

**(b) Cascading dropdowns, a lookup to one other list, calculated defaults.** Still a list form, but you’re writing Power Fx now instead of clicking through a wizard. Budget about a day.

**(c) Repeating tables, data from multiple lists, heavy attachment use.** This doesn’t fit inside a single list form. Build a standalone canvas app with a child list behind it. Budget 3 to 5 days.

**(d) Digital signatures, print or PDF output, code-behind, admin-approved templates.** Stop here. Power Apps has no native signature capture and no print-layout engine, so this isn’t a straight rebuild. Three honest options: a third-party forms product built for signatures and document output, a Power Apps front end paired with a document-generation flow in Power Automate, or retiring the process if nobody can explain why it still needs a signed printed form.

Have the bucket (d) conversation early. It usually turns into a procurement decision rather than a build task, and that takes longer to get approved than anything else on the list.

## Translating InfoPath rules to Power Fx

Most InfoPath logic lands on a small set of Power Fx patterns. Keep this open while you rebuild.

| InfoPath rule | Power Fx equivalent |
| --- | --- |
| Required-field validation | `If(IsBlank(DataCardValue1.Text), Notify("Title is required", NotificationType.Error), SubmitForm(Form1))` on the submit button |
| Conditional show/hide | `Visible: If(Dropdown1.Selected.Value = "Yes", true, false)` on the card |
| Conditional formatting | `Fill: If(ThisItem.Status = "Overdue", Color.Red, Color.White)` |
| Cascading dropdown filtering | `Items: Filter(Cities, Country = Dropdown1.Selected.Value)` on the dependent dropdown |
| Default value from user profile | `Default: User().Email` or `User().FullName` on the field |
| Submit, then email | `Patch(RequestsList, Defaults(RequestsList), {Title: TextInput1.Text}); 'NotifyRequester'.Run(User().Email)` |

## Step 3: rebuild simple forms as customized list forms

For buckets (a) and (b), skip the standalone app and customize the list form directly.

One wrinkle first, because most write-ups still get this wrong. **Integrate is not a tab on the list command bar anymore.** It moved under the overflow, so if you go looking for a tab you will not find one. From the list, select the **`...`** button, then **Integrate**, then **Power Apps**, then **Customize forms**.

![A SharePoint list command bar with the overflow menu open, showing Integrate expanded to Power Apps, which is expanded to Create an app, See all apps, and Customize forms](https://tenantcraft.ca/images/integrate-powerapps-customize-forms.png)

SharePoint list · overflow menu

All three levels open at once. Integrate sits under the ... button beside Workflows, not on the command bar itself.

That opens the form in Power Apps Studio wired through the **SharePointIntegration** control. Its `OnNew`, `OnEdit`, and `OnSave` properties fire when a user starts a new item, opens an existing one, or submits, which gives you three clean places to attach validation, defaults, and conditional behaviour without touching the list schema.

A silent 15 second screen demonstration recorded in a live tenant. A pointer moves to the three dots on the SharePoint list command bar and clicks, opening the overflow menu. It then clicks Integrate, then Power Apps, then Customize forms, and Power Apps Studio opens on the list form with the SharePointIntegration control selected in the tree view. ([download the clip](https://tenantcraft.ca/video/customize-list-form-howto.mp4))

Demo · 15 seconds, no audio

Four clicks, recorded in a live tenant in August 2026. The written steps above cover the same ground, so there is nothing in the clip you need sound or sight of to follow along.

Users press New or Edit on the list exactly as before. There’s no separate app link to distribute, and no separate sharing step, because the form inherits the list’s permissions.

## Step 4: repeating tables, the part that bites

InfoPath’s repeating table doesn’t map onto a column. Build it as two lists: a **parent list** for the main record, and a **child list** with one row per line item and a lookup column back to the parent.

Bind a gallery in the parent form to the filtered child list:

```
Filter('Purchase Request Lines', ParentRequest.Id = SharePointForm1.Item.ID)
```

On save, write each row back with `Patch` inside a `ForAll`:

```
ForAll(
    LinesGallery.AllItems,
    Patch(
        'Purchase Request Lines',
        LookUp('Purchase Request Lines', ID = ThisRecord.ID),
        {
            ItemDescription: ThisRecord.ItemDescription,
            Quantity: Value(ThisRecord.Quantity)
        }
    )
)
```

The `LookUp` inside `ForAll` has to key off `ThisRecord.ID`, the row the loop is currently on, and not a gallery’s `.Selected` property, which stays pinned to whatever the user last clicked. Get that wrong and every iteration patches the same record.

New rows need their own branch, since a blank ID means the row was never saved: wrap it in `If(IsBlank(ThisRecord.ID), Patch(..., Defaults(...), {...}), Patch(...))`. Deleted rows won’t clean themselves up either, so track what the user removed and `Remove()` those records in the same save action, or they sit orphaned.

**Backfilling.** Creating the new lists doesn’t move the old data. Reuse the extraction script, but instead of writing CSV, `Add-PnPListItem` each submission into the parent list, capture the returned item ID, and loop the repeating nodes into the child list with that ID as the lookup. Do it in a batch before cutover, then spot-check migrated records against the original XML.

Delegation will drop rows without telling you

`Filter()` against a SharePoint list is only delegable for some operators. Once a formula falls outside what SharePoint can delegate, Power Apps pulls data locally and applies the app’s row limit, which defaults to 500 and maxes out at 2000. Past that, rows silently don’t load. No banner, no error.

Raise the data row limit in **App settings > Advanced settings** before you publish, and test with a parent record that has more child rows than the limit. A repeating-table replacement that quietly drops line items is worse than the frozen InfoPath form it replaced, because the old form at least showed everything it had.

## Gotchas before you publish

-   **Quick Edit and grid view bypass the custom form entirely**, including your validation.
-   **The mobile app renders the same customized form**, but test it separately. Small screens expose layout problems browsers hide.
-   **“Return to default SharePoint form” is your rollback.** It switches the list back to the out-of-the-box form immediately if a publish breaks something.
-   **Form versions are kept**, so a bad publish can be rolled back rather than rebuilt.
-   **The attachments control has limits** and doesn’t replicate every InfoPath attachment behaviour. Test with the real file types and sizes the form actually receives.
-   **Sharing differs by build type.** A customized list form inherits list permissions. A standalone canvas app is its own object and has to be shared explicitly, or it opens for you and nobody else.

## Licensing: what’s seeded and what isn’t

Customizing a SharePoint list form, and building canvas apps against SharePoint, Outlook, or Teams data, is covered by the standard Microsoft 365 seeded licence. No extra cost.

The moment a form uses a premium connector, connects to Dataverse, or goes through an on-premises data gateway, it moves into per-user or per-app Power Apps licensing. Check the connector list on every rebuilt form before go-live. The Power Platform admin centre flags premium connector usage, and a five-minute check is cheaper than explaining a licence bill at renewal.

## Step 5: rebuild the workflow layer from evidence

If the form fed a SharePoint 2013 workflow or a Nintex approval, that layer needs rebuilding too, in Power Automate, and you can’t reverse-engineer it from memory.

For a SharePoint 2013 workflow, read the workflow history list on real items. It shows what actually fired, which is more reliable than the design view if the workflow was edited over the years. For Nintex, use **Workflow Settings > Export** to pull it as XML, which is faster to read than reopening the designer.

Either way, capture the same things: the trigger condition, who approves and how they’re resolved, escalation and reminder timings, what happens on rejection, the wording of any email templates, and which fields get written back on completion.

That list is your build spec. In Power Automate it becomes an automated cloud flow triggered on item creation, a _Start and wait for an approval_ action, a condition on the outcome, and an update back to the item. A like-for-like approval rebuild uses only the SharePoint, Approvals, and Outlook connectors, all standard, so it stays inside your existing Microsoft 365 licensing. Approvers need no additional licence. The [Power Automate rebuild post](https://tenantcraft.ca/insights/sharepoint-workflow-rebuild-power-automate) walks through that anatomy step by step.

One caveat on using the old workflow as a spec. The SharePoint 2013 workflow engine was itself retired from SharePoint Online on April 2, 2026, so if it stopped firing before anyone noticed, items may be sitting in an incomplete state and the history may be misleading. Confirm that before treating it as the source of truth. The [workflow recovery post](https://tenantcraft.ca/insights/sharepoint-2013-workflows-recovery) covers that side of it.

## Bottom line

The order matters more than the tooling. Extract the form-library XML now, while the libraries are still there and still parseable, because nothing is going to render those submissions as forms again. Script the inventory instead of crawling sites. Sort what’s left with the rubric, and get a real decision on bucket (d) rather than forcing it into Power Apps.

Then rebuild, check delegation on anything with a repeating table, and check the connector list before go-live. The form that derails the project is usually one nobody had on the inventory at all, which is why the scripted sweep is worth the afternoon it costs.

Need to route every surviving form to a destination? Use the decision tree.

InfoPath Forms Decision Tree

PDF · 7 pages

[Email me the PDF](https://tenantcraft.ca/resources/guides/infopath-forms-decision-tree)

TWENTY MINUTES, NO PITCH

## Tell me what is stuck. I will tell you what it takes.

Same consultant from the first email to the last cutover. If I am not the right fit, I will refer you to someone who is.

[Book a 20-min call](https://tenantcraft.ca/contact) [See published rates](https://tenantcraft.ca/pricing)

---

_Canonical HTML: https://tenantcraft.ca/insights/infopath-replacement-power-apps · Agent guide: https://tenantcraft.ca/llms.txt · Site map: https://tenantcraft.ca/sitemap-index.xml_
