Skip to main content

Real-world Azure migration case study covering planning, RoboCopy strategies, permissions, validation, and lessons learned

How We Migrated 15TB from QNAP NAS to Azure Files: A Real-World Case Study

The planning decisions, the RoboCopy commands that preserved full fidelity, the ACL problem we did not see coming, the Data Box trade-off we almost got wrong, and what the whole thing cost at the end.

By Francis Avorgbedor | Azure Engineer  ·  July 8, 2026  ·  18 min read  ·  Azure Files · Storage Migration · QNAP
FA
Francis Avorgbedor
Azure Engineer  ·  SEVENAI  ·  Azure Field Notes
Writing from production Azure deployments. This case study is drawn from a real engagement — a professional services firm migrating 15TB of file share data from QNAP NAS to Azure Files over a six-week project in Q1 2026.
15TB
Total data migrated across 8 SMB shares
6 wks
End-to-end project duration including planning
4hrs
Total user-facing downtime during cutover weekend
$474
Estimated monthly Azure Files cost post-migration

When I first sat down with the client — a 180-person professional services firm running a QNAP TS-h1290FX NAS with 15TB of active file share data — the IT manager's opening question was exactly what I expected: "Can we just drag and drop it?" The honest answer is no, and explaining why not without losing the room is one of the more diplomatically demanding parts of any NAS migration engagement. The technical reality is that a 15TB migration involving eight active SMB shares, Active Directory-joined users, NTFS ACLs with years of accumulated permission complexity, and a business that cannot tolerate more than a weekend of cutover disruption requires a structured approach with several non-obvious decision points along the way.

This post is the complete account of how we approached and executed that migration — what we decided, why we decided it, what went wrong, and what the final outcome looked like. I am sharing it because the public literature on NAS-to-Azure-Files migration is almost entirely documentation written from a feature perspective rather than a project perspective. What it does not cover is what you actually encounter when you run the process against a real business environment with legacy permissions, inconsistent share structures, and users who will ring the IT manager if their mapped drives are not back by Monday morning.

The environment — what we were working with

Before any migration planning, we spent two days on discovery and inventory. This is the step most organisations underestimate. Understanding the source environment in detail is not optional — every decision that follows depends on it.

The QNAP ACL problem — discovered during inventory

The QNAP NAS was not domain-joined. It was using local QNAP users mapped to Active Directory accounts through a QNAP-specific LDAP bridge that the previous IT contractor had configured in 2021. The practical consequence: the file ACLs on the QNAP contained QNAP-local SIDs, not Active Directory SIDs. RoboCopy's /COPY:DATSO flag copies ACLs faithfully — but if those ACLs reference SIDs that do not exist in Active Directory, the migrated files will have broken permission entries that look correct in Explorer but deny access to users who should have it. This was the most significant complication of the entire project, and we discovered it during the inventory phase, not during the migration itself. Finding it early saved us from a permissions catastrophe on cutover weekend.

The three migration method decision — this is where most projects go wrong

Microsoft documents three primary paths for migrating NAS shares to Azure Files. Understanding which path is correct for a given environment is the most consequential decision in the entire project. Get it wrong and you are either rebuilding permissions manually or running a second migration to fix the first one.

MethodBest forPreserves ACLsDowntime requiredSpeedOur choice?
Azure Storage MoverSMB/NFS shares, fully managed, no AFS needed long-term✓ Full fidelityMinimal — continuous syncFast — FileREST API, tested to 100M itemsNot chosen
Azure File Sync + RoboCopyHybrid deployments, cloud tiering, on-prem caching✓ Full fidelity via /COPY:DATSOMinimal — sync then cutoverModerate — depends on link bandwidth✓ Chosen
Azure Data Box + File Sync>10TB, poor WAN bandwidth, offline transfer needed✓ Via RoboCopy to Data BoxOffline shipping window requiredVery fast — no WAN dependency for bulkEvaluated, not chosen

We chose the Azure File Sync with RoboCopy path for three reasons specific to this environment. First, the client had a 500Mbps dedicated internet connection — sufficient to transfer 15TB within the project timeline without needing a physical Data Box device. Second, the business required a hybrid deployment post-migration — branch office users needed local caching of frequently accessed files without pulling everything from Azure over their WAN links. Azure File Sync provides exactly this through cloud tiering. Third, the SID mapping problem we discovered during inventory meant we needed to rebuild ACLs on a Windows Server before the migration anyway — Azure File Sync requires a Windows Server intermediate step, which gave us the right point to resolve the permission issue.

"The goal is to move the SMB file shares on your NAS appliance to a Windows Server, then utilize Azure File Sync for a hybrid cloud deployment. The migration needs to be done in a way that guarantees data integrity and availability — keeping downtime to a minimum."

— Microsoft Azure Files Migration Documentation

The six-phase migration plan

Phase 1Azure infrastructure provisioningDays 1–3
Provisioned the Azure storage accounts, Azure file shares, Storage Sync Service, and Sync Group. Finance, Projects, and HR shares went to a Premium SSD File Storage account (eastuk region) for low-latency access. Shared and Media went to a Standard HDD pay-as-you-go account. Archive shares went to a separate account configured for Cool storage tier. Active Directory domain-joined the storage accounts using Azure AD Connect — this is what enables users to authenticate to Azure Files using their existing AD credentials without entering additional passwords.
Phase 2Windows Server provisioning and SID mappingDays 4–8
Provisioned a Windows Server 2022 VM on-premises (a spare physical host the client already had) and domain-joined it. This is the mandatory intermediate step — Azure File Sync cannot install on QNAP directly. We then ran a SID mapping exercise: for each local QNAP user, identified the corresponding Active Directory account, and used icacls to re-stamp ACLs on files where QNAP-local SIDs appeared. This took three days for Finance and HR shares — the two with the most complex permission structures — and was the longest single task in the project.
Phase 3Initial RoboCopy from QNAP to Windows ServerDays 9–16
Ran the first RoboCopy pass from QNAP to the Windows Server intermediate. This copy runs over the local network — fast, no WAN dependency. We ran two parallel RoboCopy jobs for the two largest shares (Projects and Archive2022) and serialised the smaller ones. Eight days for the initial copy of all 15TB at approximately 300MB/s sustained throughput on a 10GbE local network. During this phase users continued working on QNAP — no disruption.
Phase 4Azure File Sync agent deployment and initial uploadDays 17–28
Installed the Azure File Sync agent on the Windows Server and registered it with the Storage Sync Service. Created sync group endpoints for each share. The initial upload to Azure Files over the 500Mbps WAN link took 11 days for all 15TB — approximately 135GB per day average throughput accounting for business hours throttling and Azure File Sync's built-in network limits. We set up Azure Monitor alerts on sync errors and checked the sync health dashboard daily. Four sync errors appeared during this phase — all related to file names containing characters not permitted in Azure Files. We resolved each by renaming the affected files on the Windows Server.
Phase 5Delta RoboCopy and cutover preparationDays 29–35
Ran a second RoboCopy pass with /MIR to capture changes made to the QNAP during the initial sync period. This delta copy took four hours. Notified users of the Saturday night maintenance window. Prepared DFS-N namespace updates to redirect share paths from QNAP to the Windows Server's Azure File Sync endpoints. Tested the redirect in a staging environment to confirm mapped drives reconnected automatically.
Phase 6Cutover weekendDay 40 — Saturday 22:00
Final RoboCopy delta with /MIR at Saturday 22:00 — 47 minutes to complete. Redirect DFS-N namespace targets from QNAP to Windows Server. Update DFS-N to point to Azure Files share paths. Disconnect QNAP shares at 23:15. Verify all mapped drives reconnect on test machines. Sleep. Verify again at 06:00 Sunday. Total user-facing downtime: 4 hours (the DFS redirect propagation window). By Monday morning all users had reconnected to Azure Files through the Windows Server File Sync cache without knowing the backend had changed.


https://www.blogger.com/blog/post/edit/5026252216649958101/3592537554909180735

The RoboCopy commands that actually worked

The RoboCopy flags matter more than most migration guides acknowledge. The wrong flags produce a copy that looks complete but is missing timestamps, attributes, or ACLs — and you only discover this after the QNAP is decommissioned and a user cannot access a file they could access last week.

Phase 3 — Initial RoboCopy from QNAP to Windows Server (run from Windows Server)# Full-fidelity copy preserving data, attributes, timestamps, security, ownership # /MT:32 — 32 parallel threads for maximum throughput on 10GbE LAN # /NP — no progress percentage (reduces log noise) # /NFL /NDL — no file/dir listing in log (log size management) # /B — backup mode (bypasses ACL restrictions on source) # /MIR — mirror (adds, modifies, and deletes) # /IT — include tweaked files # /COPY:DATSO — Data, Attributes, Timestamps, Security, Owner # /DCOPY:DAT — Directory data, attributes, timestamps # /UNILOG — Unicode log file for non-ASCII filenames
Robocopy `
  \\QNAP\Finance `
  D:\SyncRoot\Finance `
  /MT:32 /NP /NFL /NDL /B /MIR /IT `
  /COPY:DATSO /DCOPY:DAT `
  /UNILOG:C:\MigrationLogs\Finance_Phase3.log `
  /TEE

# /TEE sends log output to both console and log file simultaneously # Run this as Administrator — /B mode requires elevated privileges # For QNAP shares requiring credentials: add /USER:domain\admin /PASS:xxxx
Phase 5 — Delta RoboCopy (changed files only, no deletions yet)# Pre-cutover delta — captures changes since Phase 3 initial copy # Remove /MIR here — we don't want to delete from destination yet # /XO — exclude older files (only copy if source is newer)
Robocopy `
  \\QNAP\Finance `
  D:\SyncRoot\Finance `
  /MT:32 /NP /NFL /NDL /B /IT /XO `
  /COPY:DATSO /DCOPY:DAT `
  /UNILOG:C:\MigrationLogs\Finance_Delta_Phase5.log `
  /TEE

# Review the log before Phase 6 — confirm error count is 0 or near 0
Phase 6 — Final cutover RoboCopy (with /MIR — run during maintenance window)# This is the authoritative final copy — /MIR will delete files on destination # that no longer exist on source. Confirm this is what you want before running. # Run AFTER users have stopped writing to QNAP shares
Robocopy `
  \\QNAP\Finance `
  D:\SyncRoot\Finance `
  /MT:32 /NP /NFL /NDL /B /MIR /IT `
  /COPY:DATSO /DCOPY:DAT `
  /UNILOG:C:\MigrationLogs\Finance_Cutover.log `
  /TEE

# Wait for File Sync to upload final changes to Azure Files # Monitor: Get-StorageSyncServerEndpoint | Select-Object SyncStatus # Only proceed with DFS-N redirect when SyncStatus = Healthy and # all pending file counts are zero

The four problems we actually hit

Day 5 — SID mapping complexity
The QNAP local user SID problem took three times longer than estimated
We estimated two days for SID mapping. It took six. The Finance share had 847 unique ACL entries across its folder tree. The icacls remapping script worked correctly but had to be applied recursively to approximately 1.4 million files and folders. The script ran at roughly 2,000 entries per minute — meaning the full Finance share remapping took 11 hours of script runtime. We did not estimate this correctly. For any QNAP migration involving local users, budget significantly more time for SID resolution than you think you need.
Day 22 — Illegal filename characters
Azure Files rejected 23 files across the Projects share for illegal characters
Azure Files does not allow certain characters in file names that NTFS and QNAP both permit: specifically the colon (:), asterisk (*), and question mark (?). The Projects share had 23 files with these characters — mostly files named with date stamps in the format "Report_2024:03:15.xlsx". Azure File Sync logged these as errors but did not fail the sync job. We identified them via the sync error log and renamed them before cutover. If we had not been monitoring the sync error log, these files would have been silently missing from Azure Files after cutover.
Day 27 — Throttling during business hours
Azure File Sync upload throttling cut our throughput in half during peak hours
Azure File Sync applies network throttling by default during business hours to avoid saturating the WAN link. We knew this. What we did not anticipate was the interaction with the client's existing bandwidth management policies, which further throttled Azure File Sync traffic during peak business hours. The result was that daily upload throughput dropped to approximately 60GB during business hours versus 200GB+ overnight. We resolved this by configuring a custom network bandwidth schedule on the File Sync agent that aligned with the client's existing traffic shaping policies. The final upload timeline extended by four days as a result.
Day 40 — Cutover success
DFS-N redirect worked exactly as planned — zero Monday morning support calls
The DFS Namespace redirect propagated within the expected two-hour window. All eight mapped drives reconnected automatically on user machines without requiring IT intervention. Zero support calls on Monday morning. The Azure File Sync cache on the Windows Server served local reads at LAN speed for frequently accessed files, so users experienced no perceptible latency difference for day-to-day work. The QNAP remained online but disconnected from shares for two weeks as a safety net before being decommissioned.

What it actually cost

ComponentDetailMonthly cost
Azure Files Premium SSDFinance + Projects + HR — 8.1TB provisioned at 0.21 GiB/month$225
Azure Files Standard HDDShared + Media + Backups — 3.1TB used at 0.04 GiB/month$17
Azure Files Cool tierArchive2022 + Archive2023 — 3.8TB at 0.02 GiB/month$11
Storage Sync ServicePer-server registration fee — 1 registered server$5
Azure Private EndpointPrivate endpoint for storage accounts — avoids public internet traversal$9
Azure Backup for FilesDaily snapshots retained 30 days — replacing QNAP's backup function$72
Egress / transactionsEstimated based on observed usage in first month$135
Total estimated monthly Azure cost$474
✓ Cost context — what the client was spending before

The QNAP TS-h1290FX had a hardware replacement cycle of approximately 5 years. Annualised hardware cost: approximately $750/year. Add QNAP support contract ($225/year), on-site backup hardware ($300/year amortised), and IT time managing QNAP firmware updates and storage pool expansion: approximately $1,500/year total.

Azure Files at $474/month is $5,560/year — approximately 3.8x the on-premises cost. The client accepted this because the Azure Files cost includes global accessibility for remote workers, 99.99% SLA, no hardware replacement risk, and Azure Backup replacing a third-party backup appliance. The pure cost comparison is not the right frame for this decision.

The five things I would do differently

  • I would use Azure Storage Mover for the archive shares. Archive2022 and Archive2023 were read-only after migration, needed no cloud tiering, and had low access frequency. Azure Storage Mover is faster than the File Sync path and does not require a Windows Server intermediate for these workloads. Using File Sync for the archive shares added unnecessary complexity without delivering any benefit that Storage Mover would not have provided.
  • I would audit QNAP file naming conventions before starting the RoboCopy. The 23 files with illegal characters caused no catastrophic problems, but finding them during the sync phase rather than before the initial RoboCopy meant they were identified late. A simple PowerShell scan of the QNAP shares for characters that Azure Files does not permit would have flagged them in day one of the discovery phase and let us rename them at source before the first RoboCopy ran.
  • I would have provisioned the Windows Server on Azure rather than on-premises. Using an on-premises Windows Server as the File Sync intermediate required the initial RoboCopy to run over the local network (fine) and the upload to run over the WAN (the bottleneck). Had we provisioned the Windows Server as an Azure VM in the same region as the storage accounts, we could have eliminated the WAN upload bottleneck entirely — the RoboCopy from local Windows Server to Azure VM would still use WAN, but subsequent File Sync activity between the VM and Azure Files would be intra-Azure at effectively unlimited speed.
  • I would have estimated SID mapping time based on file count, not share count. The discovery phase gave us share sizes and file counts. We estimated SID mapping time based on the number of shares (8) rather than the number of files with non-standard ACLs. The Finance share alone had 1.4 million files, many with complex inherited permissions. File count, not share count, is the correct unit for estimating permission remediation work on a QNAP migration.
  • I would have enabled Private Endpoints before registering the File Sync agent. We added Private Endpoints to the storage accounts after the File Sync agent was already configured and syncing. This required a brief sync pause and re-registration of the agent endpoint URLs. The correct sequence is to configure Private Endpoints and confirm connectivity before the File Sync agent is deployed. Azure Files should never traverse the public internet in a production migration — private endpoints should be a pre-requisite, not an afterthought.
Key takeaways
  • Discovery and inventory is not optional — budget two full days minimum. The SID problem, the illegal filename characters, and the QNAP authentication architecture were all discovered during inventory. Each would have been significantly more painful if discovered during the migration itself.
  • Choose your migration method per share, not per project. Archive shares and active shares have completely different requirements. We should have used Storage Mover for archives and File Sync for active shares rather than a single method for all eight shares.
  • The correct RoboCopy flags are /COPY:DATSO /DCOPY:DAT /B /MIR /IT. Any combination that omits Security (/S in DATSO) or uses Backup mode (/B) incorrectly will produce a copy that is missing ACLs or permission data. These are the flags that Microsoft recommends for full-fidelity migration — use them exactly as specified.
  • Monitor the File Sync error log throughout the upload phase. Azure File Sync does not fail a sync job because of individual file errors — it logs them and continues. Files with illegal characters, path length violations, or permission problems are silently skipped. You must actively monitor the error log or you will discover missing files after cutover.
  • DFS-N is the correct cutover mechanism — not DNS changes, not drive remapping. DFS Namespace redirect provides transparent cutover for all users without requiring any client-side changes. Users' mapped drives reconnect automatically. This is the correct approach for any SMB share migration where minimising user disruption is a requirement.
  • Keep the QNAP live for two weeks after cutover — do not decommission immediately. Two weeks of parallel operation gives you a safe fallback if something is discovered post-cutover. The cost of keeping the QNAP powered on for two extra weeks is negligible. The cost of a failed cutover without a fallback is not.

Popular posts from this blog

Learn how to use Azure Chaos Studio to simulate data center outages, test Azure OpenAI failover, and validate AI app resiliency using KQL and CLI workflows

Resiliency Testing Chaos Studio Zone Down Azure OpenAI Failover Testing AI Resiliency: Using Azure Chaos Studio to Simulate Data Center Outages on Your LLM Every multi-region Azure OpenAI architecture diagram has a failover arrow drawn on it. Almost none of them have ever actually been triggered. The arrow is a hypothesis, confirmed only by a real outage — unless you deliberately cause a controlled one first, on your own schedule, with a rollback plan, instead of finding out during an incident that the failover you designed never quite worked the way the diagram promised. The failure signature this guide resolves # The gap this article closes — a real architecture review finding: Design doc, page 4: "In the event of a regional outage, Azure Front Door automatically routes traffic to the secondary Azure OpenAI deployment in West Europe, with an expected failover time under 60 seconds." Verification performed to support this claim: NONE. Last time this path was ac...

Improve AI application performance by reducing latency, optimizing embeddings, and lowering cloud inference costs

Performance Fix Foundry Local 1.2 Linux ARM64 Embeddings Offline ASR The Edge Latency Drop: Fixing Latency Spikes by Offloading Embeddings to Foundry Local 1.2 You are paying a full cloud round trip — network, TLS, queue, throttle risk — to turn a twelve-word search query into a vector. That is the most expensive way possible to do one of the cheapest computations in your stack. Foundry Local 1.2 now runs on Linux ARM64, which means embeddings and speech recognition can happen on a Raspberry Pi, a Jetson, or a Graviton instance — offline, unmetered, and in single-digit milliseconds. The failure signature this guide resolves # Application Insights — the embedding call, not the LLM, is your tail latency: name p50 p95 p99 calls/day POST /embeddings (cloud) 89 ms 412 ms 3,847 ms 1,240,000 POST /chat/completions (cloud) 940 ms 1,720 ms 2,910 ms 38,000 ^^^^^^^^ ...

Learn how to select Azure Files and Blob storage tiers, avoid early deletion fees, model costs, and automate lifecycle management for large file migrations.

Choosing the Right Azure Storage Tier for Large File Migrations The complete decision framework for storage tier selection during large file migrations — Azure Files tiers, Blob tiers, cost modelling, early deletion traps, lifecycle automation, and the 2026 changes that affect every migration running today. By Francis Avorgbedor | Azure Engineer  ·  July 14, 2026  ·  18 min read  ·  Storage Tiers · Cost Optimisation · Migration FA Francis Avorgbedor Azure Engineer  ·  SEVENAI  ·  Azure Field Notes 9 Distinct Azure storage tiers across Files and Blob — most engineers know only 3 15hrs Archive tier rehydration time at standard priority — the delay teams forget to plan for 128KB Minimum billable object size for Cool/Cold/Archive from July 2026 — a 32× trap for small files 70% How much retrieval and transaction fees add to a theoretical Archive storage bill The most expensive mistake I see on large Azure file migrations is not choosing the w...

Find the hidden Windows 10 system files consuming up to 500GB, including hibernation, shadow copies, backups, and WinSxS, with safe cleanup steps.

  The 500GB System File That Eats Your Hard Drive Something on your Windows 10 drive is consuming hundreds of gigabytes and the normal tools cannot find it. This guide identifies every known culprit — from hibernation files and shadow copies to runaway backups and the Windows component store — and tells you exactly what is safe to delete, what to leave alone, and what the commands actually do.

Learn safe methods to reset Azure virtual machines using managed disks while preserving critical workloads

How to Reset an Azure Virtual Machine to Factory Settings Using a Managed Disk Azure does not have a single "factory reset" button. What it does have is something better: the OS Disk Swap — a method that swaps out the corrupted or misconfigured OS disk for a clean Windows Server managed disk without deleting the VM, its NICs, its IP addresses, or any attached data disks. Here is how it works, when to use it, and the exact steps to execute it safely. FA Francis Avorgbedor Azure Engineer July 16, 2026 15 min read Azure VMs · Windows Server · Real-World Fix 3 Methods to achieve a clean Windows Server installation on an existing Azure VM ~15min Typical OS Disk Swap duration — VM retains its NICs, IPs, and data disks throughout 0 Data disks affected by an OS Disk Swap — data disks remain attached and untouched 1 Snapshot of the original OS disk you must take before starting — no exceptions Introduction Why Azure Does Not Have a Simple Factory Reset — and What to Do Instead On a ph...

Determine Windows 11 compatibility, upgrade requirements, costs, and performance expectations on older hardware

Can I Update My Old Computer to Windows 11 — and How Much Will It Cost? Your i7, 16GB RAM, 512GB SSD machine is powerful enough to run Windows 11 comfortably. The TPM 2.0 and Secure Boot wall is a security checkbox, not a performance ceiling. Here are two proven ways to get past it, what each one costs, and what you are trading away by doing so. $0 Cost of the Windows 11 licence if your existing Windows 10 is genuine — the upgrade remains free in 2026 2 Proven methods to bypass TPM 2.0 and Secure Boot — Rufus (easy) and Registry edit (manual) 25H2 Current Windows 11 version — all known bypass methods tested and confirmed working as of July 2026 Oct 2025 Windows 10 end of life — no more security updates. Staying on Windows 10 now carries real risk. First — Check Your BIOS Before Anything Else You Might Not Actually Need a Bypass Before running any bypass, open your BIOS and look at two settings. Many computers that fail the Windows 11 compatibility check have TPM 2.0 present in the hard...

Solve common AKS issues with practical troubleshooting techniques for networking, scaling, upgrades, and workloads

Troubleshooting Guide AKS Kubernetes Real Solutions kubectl Azure Kubernetes Service (AKS) Troubleshooting Guide: Real Solutions to Common Problems CrashLoopBackOff at 2am. Pods stuck Pending with no obvious cause. Nodes going NotReady mid-deployment. DNS resolution silently failing in production. Every AKS engineer encounters these — the difference between engineers who panic and engineers who stay calm is knowing the exact sequence of diagnostic commands to run. This guide gives you that sequence, the root cause analysis for each failure mode, and the fix. 3 commands 90% of AKS problems are diagnosed with the same three kubectl commands: describe pod, logs --previous, and get events — in that order, every time Exit 137 The exit code that tells you everything: container killed by SIGKILL — either the Linux OOM killer (memory limit exceeded) or kubelet after grace period expired 5 min The CrashLoopBackOff ceiling: Kubernetes applies exponential backoff (10s → 20s → 40s → 80s → 160s → 3...

Step-by-step guide to deploying scalable AI chatbots on Azure with OpenAI and App Service

Step-by-Step Guide Azure OpenAI App Service Production Python How to Deploy an AI Chatbot on Azure Using Azure OpenAI and App Service From zero to a production-grade AI chatbot: provision Azure OpenAI, write a streaming Flask API backend, deploy it on Azure App Service with Managed Identity, wire in conversation history and content safety, and instrument it with Application Insights — all with complete code and Terraform IaC. No API keys in environment variables. No hardcoded secrets. No half-finished PoC patterns. 7 phases This guide covers the full deployment lifecycle: architecture design → resource provisioning → backend code → App Service deployment → streaming → security → monitoring Zero keys The chatbot authenticates to Azure OpenAI using Managed Identity and DefaultAzureCredential — no API keys stored in environment variables, Key Vault, or code SSE Server-Sent Events stream GPT tokens to the browser as they generate — the same token-by-token typing effect users expect from pr...