Migrating Attachments and Notes Into Dataverse Without a Storage Bill Shock
A technical guide for the migration with several legacy sources and a long retention obligation: where files should live, notes as records with links, retention and legal hold, per source mechanics, reconciliation and a storage estimate before you commit.
Before migrating attachments and notes into Dataverse, decide where the files will live, because note and email attachments count against Dataverse file capacity, and a ten-year retention obligation only ever grows. Dataverse capacity is usually a far more expensive place to keep old documents than SharePoint or Azure Blob Storage, so check current Microsoft capacity add-on pricing before you load anything. Keep notes as Dataverse records, but store the files in SharePoint through document management, or in Blob Storage with a pointer record holding the link, size and hash. Document retention and legal hold where the files live, migrate each source with configured tools, reconcile counts, bytes and hashes, and estimate storage before committing.
Why do attachments and notes cause a storage bill shock in a Dataverse migration?
Because they are planned as a detail of the record migration and turn out to be most of the volume. A legacy CRM, a file share and an email archive can hold many times more bytes in documents than in every account, contact and case put together. Loaded straight into notes, those documents land in Dataverse capacity that is pooled across the tenant, and the first capacity report after go live is where the problem is noticed.
Long retention makes it worse. If a regulation or contract requires documents to be kept for ten years, nothing can be cleaned up during that period, and new documents keep arriving on top of the migrated ones. The decision about where files live is therefore a design decision to make before the first load, not a cleanup to schedule afterwards.
If you are already over capacity in a live environment, start with our Dataverse storage capacity cleanup guide, which covers finding the full bucket and moving existing attachments out safely. This page is about not creating the problem during a migration.
Where do migrated attachments and notes land in Dataverse capacity?
Dataverse capacity is split into database, file and log buckets, each with its own entitlement and its own add-on. The per unit price differs by bucket and Microsoft revises it, so check current Microsoft capacity add-on pricing and your own per table breakdown in the Power Platform admin center rather than relying on a figure from any article. The distinction that matters for a migration is which parts of a note or email go where.
| What you migrate | Where it lands | Design consequence |
|---|---|---|
| Note text, subject, owner, dates and regarding record (annotation) | Database capacity, as a row | Small per note; keep notes as records |
| File attached to a note | File capacity in the current storage model | The main cost driver; move files out for long retention |
| Email activity body and metadata | Database capacity | Large bodies add up; decide which emails need to be activities at all |
| Email attachments (activitymimeattachment) | File capacity in the current storage model | Signatures and logos are often most of the count; filter them out |
| File and image columns | File capacity | Convenient, but the same cost consideration as note attachments |
| Pointer record to SharePoint or Blob Storage | Database capacity, as a small row | The file bytes are stored and retained outside Dataverse |
What storage design holds ten years of attachments without filling Dataverse?
Separate the record from the file. Dataverse keeps what users search, filter and report on: the note, the email metadata and a reference to the document. The file bytes live in storage designed for documents and long retention. Choose the destination by how users need to open the files and what the retention policy demands.
| Design | Best for | Trade off |
|---|---|---|
| Files stay in Dataverse notes or file columns | Small volumes, short retention, documents users open daily | Every byte counts against Dataverse capacity for as long as it is kept |
| SharePoint document management integration | Documents users still open from the record, with Microsoft Purview retention and holds | Folder structure and permissions need design; SharePoint permissions are separate from Dataverse security |
| Azure Blob Storage with a Dataverse pointer record | Large archives opened rarely, strict immutability, lowest cost storage tiers | Opening a file from the record needs a secure link to be generated, which is a small build rather than configuration |
| Archive only, outside the CRM | Closed history nobody works on, kept only to satisfy retention | Users search the archive rather than the record; agree this with them first |
- A pointer record is a custom table with a lookup to the parent record, the storage path or document location, file name, content type, size in bytes, a content hash, the source system and its original identifier, and the retention class.
- With SharePoint integration, Dataverse holds document location records that point to a folder per record, and users see the documents on the record form.
- Keep the hash and size on the pointer record from day one: they are what the reconciliation and any later integrity check rely on.
How should notes be migrated: as records with links rather than embedded files?
Migrate every note as a note, with its original text, author, created date and regarding record, and leave the file out of it. Where the source note had an attachment, put a link to the document in SharePoint or a reference to the pointer record into the note, so a user reading the timeline can still reach the file in one step.
Preserve the original created date and owner, because a timeline of notes all dated on migration weekend is useless as evidence. How overriddencreatedon and ownership are carried across is covered in our data migration guide, so it is not repeated here.
- Convert rich text or HTML note bodies to the format your Dataverse note rendering supports, and test long notes for truncation.
- Skip empty notes created only to carry a file, and represent those files as pointer records or documents instead.
- Keep the source note identifier on the note or pointer record, so a rerun updates rather than duplicates.
- Keep notes and emails that genuinely need to stay live in Dataverse, and move closed history the business agrees nobody works on to the archive.
How do retention and legal hold work across Dataverse, SharePoint and Azure Blob Storage?
Write the retention and legal hold policy before the design is final, because the storage choice decides which controls are available. The policy should state each document class, its retention period and trigger, who can place and release a hold, and where evidence of disposal is kept. Involve your records manager and data protection officer: keeping personal data for ten years has to be justified as well as enforced. This page is technical guidance, not legal advice, and how retention and erasure work for personal data in Dynamics 365 is covered in our guide to GDPR retention and erasure.
| Where the files live | Retention controls | Legal hold |
|---|---|---|
| Dataverse notes and file columns | Bulk deletion jobs and long-term data retention policies for inactive rows; check current Microsoft documentation | No native hold on individual rows comparable to SharePoint or Blob Storage; holds depend on process and on excluding rows from deletion jobs |
| SharePoint | Microsoft Purview retention policies and retention labels applied to sites, libraries or documents | Microsoft Purview eDiscovery holds preserve content even if a user deletes it |
| Azure Blob Storage | Lifecycle management rules and time-based retention policies on immutable storage | Legal hold on immutable storage prevents deletion and modification until the hold is cleared |
How do you estimate the storage before you commit to a design?
From an inventory of the real files, not from a guess based on record counts. The estimate is what turns the design choice into a decision finance can sign off, and it usually changes the scope before anything is loaded.
- Inventory every source: file count, total bytes, sizes by file type and by year, and the parent record each file belongs to.
- Hash every file and remove duplicates: the same contract attached to five emails and three records is one file.
- Exclude noise such as signature images, logos, calendar files and zero byte files, and record the exclusion rule.
- Split the inventory by retention class and by whether users still open the files, which maps directly to the design options.
- Project growth over the retention period from the current rate of new documents, not only the migrated backlog.
- Run a pilot load of a representative sample into a sandbox and compare the per table capacity before and after, allowing for the delay before capacity reporting refreshes.
- Price each design option against current Microsoft capacity add-on pricing and your SharePoint and Azure storage costs, and record the assumptions next to the numbers.
How do you migrate attachments and notes from each legacy source?
Every source needs the same output: files in the chosen destination, a manifest per file, and notes or pointer records linked to the right parent. How you get there differs by source. Files always load after their parent records exist, as described in the load order in our data migration guide.
| Source | How files and notes come out | How they go in |
|---|---|---|
| Salesforce | Attachment, Note, ContentNote and ContentVersion content with ContentDocumentLink rows naming the parent, through the Data Export service with files included or a data integration tool | Notes into Dataverse on the Salesforce Id; files to SharePoint or Blob with pointer records resolved through the parent Id |
| File shares | Folder paths that encode the customer or case, plus file system metadata | SharePoint Migration Manager or AzCopy for the files; a mapping from folder to parent record for document locations or pointer records |
| Email archives | Messages exported as individual files with sender, recipients, date and subject, using an archive or eDiscovery export | Only business relevant threads as email activities; the rest stored as files with pointer records against the matched contact or account |
| Legacy CRM | A database table of attachments holding binary content or a file path, joined to the parent key | Extracted to files with a manifest, then the same destination and pointer pattern |
| Spreadsheets | Rows with note text and sometimes a file path or hyperlink column | Notes through a dataflow or import; linked files copied and turned into pointer records |
What can be done without a developer, and where does code become necessary?
More than most teams expect, as long as someone owns the manifest. Configured tools cover the bulk movement; code is only unavoidable for a few specific jobs. Plan for that honestly rather than discovering it halfway through.
- SharePoint Migration Manager and AzCopy move large file sets into SharePoint or Blob Storage without code, with their own logs.
- Dataflows and the Dataverse import handle notes and pointer records from a manifest spreadsheet or table, using alternate keys on the source identifier.
- Data integration tools with Dataverse, SharePoint and Blob components, such as KingswaySoft or Azure Data Factory, are configured rather than coded, and are compared in our data migration guide.
- Power Automate cloud flows suit low volumes, such as a department's remaining files or the delta after cutover. Each file is a run, so check current Power Automate request limits before using it for a large archive.
- Code is usually needed to extract binary content from a legacy CRM database, to generate secure links for opening Blob files from a record, and for very large volumes where throughput matters. Throttling and batching for that case are covered in the Dataverse bulk import strategy.
How do you reconcile the migration to prove no attachment or note was lost?
With a manifest built before the load and checked after it, so completeness is a number rather than a feeling. The manifest lists every file and note from the source with its identifier, parent key, size and hash, plus every exclusion with its reason. The full validation set for records is in our data migration guide; these are the checks specific to files and notes.
- Counts: files and notes in the source equal files and notes loaded plus files and notes excluded by a recorded rule, per source.
- Counts per parent: the number of notes and files on each parent record matches the source, so a correct total cannot hide files attached to the wrong record.
- Bytes: total bytes per source and per parent table match the manifest.
- Hashes: the hash of each file in its destination matches the hash recorded at extraction, which proves the content is unchanged.
- Orphans: no pointer record without a file, and no file in the destination without a pointer record or document location.
- Opening: a sample chosen by the business opens from the record in the app, including the oldest and largest files.
- Evidence: the manifest, results and exclusion list are kept with the retention policy, because they are what an auditor will ask for.
Is Dataverse the right home for documents with a long retention obligation?
Dataverse is the right home for the records and the references; it is rarely the right home for a decade of document bytes. On Dynamics 365 and Power Platform the practical answer is almost always a split, with SharePoint or Azure Blob Storage holding the files under proper retention controls. How add-on capacity fits into licence decisions is covered in our guide to Dynamics 365 licensing costs and renewals.
We recommend the right solution - whether that's Microsoft Dynamics 365, Power Platform, or a custom-built CRM. Some businesses need the Microsoft ecosystem. Others need full control without licensing. We deliver both. Where storage and per-user licensing together no longer fit a document heavy process, a custom-built CRM on React, Node.js, PostgreSQL or .NET gives you direct control over where files are stored and what that storage costs.
Should long-retention documents sit on Dynamics 365 and Power Platform or a custom build?
Can afford licensing and want the Microsoft ecosystem
Dynamics 365
Microsoft 365, Teams and Outlook integration, a mature partner ecosystem, Copilot, and apps for sales, service and field operations that are configured rather than built.
Need full control and zero licensing
Custom CRM
A CRM built on React, Node.js, PostgreSQL or .NET that you own outright: your data model, your hosting, no per-user subscription, and features shaped exactly to your process.
Not sure which fits
We help you decide
A short discovery weighs licensing budget, process complexity, integrations and long-term ownership, then recommends one path. We deliver both, so the recommendation has no reason to lean.
How does Solzet help migrate attachments and notes into Dataverse?
We start with the inventory: every source, its file counts and bytes by type, year and parent, duplicates and noise, and the retention classes your records manager needs. From that we put the storage estimate and design options in writing, agree the retention and legal hold policy, then build the manifest, run the loads source by source with configured tools wherever they fit, and deliver the reconciliation evidence before the old sources are switched off.
The work is done by senior consultants and full-stack developers delivering remotely from Yerevan, Armenia, with 8+ years of Dynamics 365 Customer Engagement and Power Platform work, directly for your team or white-label for Microsoft partners. If capacity is already under pressure, our storage capacity cleanup guide is the place to start.
What do people ask us?
Do attachments count against Dataverse file capacity or database capacity?
In the current storage model, files attached to notes, email attachments and file and image columns count against file capacity, while the note text and email metadata are rows in database capacity. Microsoft has changed which tables count toward which bucket over time, so confirm it in the per table breakdown on the capacity page in the Power Platform admin center.
Should we migrate attachments into Dataverse notes or into SharePoint?
For small volumes that users open daily, notes are simple. For large volumes or long retention, store the files in SharePoint through the document management integration, or in Azure Blob Storage with a pointer record, and keep the notes in Dataverse with a link. Check current Microsoft capacity add-on pricing and estimate from a real file inventory before deciding.
How do we apply a legal hold to migrated documents?
Put documents that may need a hold where a hold is enforced by the platform. SharePoint content can be preserved with Microsoft Purview eDiscovery holds and retention policies, and Azure Blob Storage supports legal holds on immutable storage. Dataverse rows have no comparable native hold, so a hold there depends on process and on excluding records from deletion jobs. Involve your records manager and legal team.
How do we estimate Dataverse storage before migrating attachments?
Inventory the real files in every source by count, bytes, type, year and parent, remove duplicates by hash and noise such as signature images, split by retention class, and project growth over the retention period. Then load a representative sample into a sandbox, compare per table capacity before and after, and price each design against current Microsoft add-on pricing.
Can we migrate attachments without a developer?
Largely, yes. SharePoint Migration Manager and AzCopy move files, dataflows and imports load notes and pointer records from a manifest, and data integration tools are configured rather than coded. Power Automate suits low volumes. Code is usually needed to extract binary content from a legacy CRM database, to open Blob files securely from a record, and for very large volumes.
How do we prove no attachment was lost in the migration?
Build a manifest before the load with every file's identifier, parent, size and hash, plus every exclusion and its reason. After the load, reconcile counts per source and per parent, total bytes, file hashes in the destination, orphans in both directions, and a business chosen sample that opens from the record. Keep the results as evidence.
How do we migrate Salesforce files and notes into Dataverse?
Extract Attachment, Note, ContentNote and ContentVersion content together with the ContentDocumentLink rows that say which record each file belongs to. Load notes into Dataverse on the Salesforce Id after their parent records exist, and send files to SharePoint or Blob Storage with pointer records, or into notes for small volumes. Our Salesforce data migration guide covers the full load order.
Where should you go next?
Dataverse storage capacity cleanup
Which bucket is full, what to delete safely, what never to delete, and moving existing attachments out of Dataverse.
Data migration guide to Dynamics 365
Salesforce and multi-source migrations: mapping, load order, created dates, files and full validation.
Dataverse bulk import strategy
Bulk messages, switching off per row logic, parallelism and throttling for high-volume loads.
GDPR retention, erasure and DSAR in Dynamics 365
Retention periods, erasure and subject access requests handled in the platform.
Dynamics 365 licensing costs and renewals
Rightsizing seats and add-ons, and preparing a Microsoft renewal with usage evidence.
Custom CRM Development
Applications on React, Node.js, PostgreSQL and .NET for organizations that need full control without Microsoft licensing.
Which solution is right for your business?
Tell us what you need. A senior consultant replies within one business day with a recommendation - Dynamics 365, Power Platform, or a custom-built CRM - not a sales script.