Skip to main content

Azure Files Deep Dive: Lessons from a 15TB Production Migration

Eight QNAP shares, 15TB, eleven million files, six weeks, four incidents, one Saturday cutover. This is what actually happened — the RoboCopy flags that made the difference, the SID remap that nearly broke the timeline, the bandwidth surprise that extended the upload phase, and the DFS-N redirect that made cutover invisible to users.

FA
Francis Avorgbedor
Azure Engineer
July 15, 2026
22 min read
Field Notes · Azure Files · Migration
15TB
Across 8 QNAP shares · 11 million files · 6 weeks end to end
4
Production incidents during the migration — all avoidable in retrospect
177hrs
SID remap duration across active shares — the number nobody had estimated
$474/mo
Final monthly Azure Files cost — versus $2,100/mo on-prem QNAP lease and support
Introduction

The Environment, the Brief, and What We Walked Into

The client was a 280-person professional services firm running eight SMB file shares off a QNAP TS-h2490FU NAS — a 24-bay enterprise unit nearing end of lease. The brief was clear: migrate all eight shares to Azure Files before the lease expires, maintain access continuity, preserve all permissions, and keep the Finance and HR shares inaccessible to anyone who should not have them. The NAS was not domain-joined. The client was on a 500 Mbps internet line with a QoS policy that throttled upload bandwidth to 60% during business hours. Nobody had told us about the QoS policy when we agreed the timeline. The timeline was wrong by eleven days.

This article is a field account of that migration — what we planned, what we found when we started, what broke, what we did about it, and the specific technical steps that got us from 15TB on a QNAP to 15TB on Azure Files with a two-hour Saturday cutover window that users never noticed. Every number, every error, and every lesson in this article is from the actual migration logs.

Figure 1 — Source environment: eight QNAP shares, access patterns, and target Azure architecture
SOURCE — QNAP TS-h2490FUNOT domain-joined · local SIDsFinance (3.2TB · 1.4M files).Daily · 847 unique ACL entries · HIGH complexityHR (0.8TB · 420K files)Daily · 623 ACL entries · restrictedProjects (4.1TB · 2.8M files)Daily · 312 ACL entries · activeShared (1.9TB · 890K files)Daily · 48 ACL entries · broad readMedia (0.9TB · 12K files)Weekly · 8 ACL entries · large filesBackups (0.3TB · 8K files)Daily automated · no user ACLsArchive2022 (2.1TB · 3.2M files)Quarterly · read-onlyArchive2023 (1.7TB · 2.1M files)6-weekmigrationTARGET — AZURE FILES · 3 STORAGE ACCOUNTSSA-A: FileStorage · Premium SSD · ZRSFinance → premium-financeHR → premium-hrDedicated storage accounts · no shared IOPS · $0.26/GiB provisionedSA-B: StorageV2 · Standard HDD · ZRSProjectsShared · MediaBackups.SA-C: StorageV2 · Standard Cool · LRSArchive2022Archive2023Private Endpoints · Public access: OFF · AD DS auth · Azure BackupPrivate DNS zone: privatelink.file.core.windows.net · linked to corp VNet
Finance and HR get dedicated Premium SSD storage accounts — no shared IOPS pool with other shares. Archives go to Standard Cool — no Azure File Sync agent required, Storage Mover handles the one-time transfer.
Migration Timeline

Six Weeks, Start to Finish — What Actually Happened

The plan said four weeks. The reality was six. Here is the week-by-week account of what happened, what surprised us, and what we changed mid-stream.

Week 1

Discovery, inventory, and the first surprise

Deployed Azure Migrate appliance for file share discovery. Ran the PowerShell compatibility scan. Found 23 files with illegal characters — all date-stamped filenames using colons. Found one path at 2,096 characters. Fixed both before any data moved. Ran the bandwidth measurement: 500 Mbps line, 300 Mbps available at business hours. Calculated daily transfer capacity: 414 GB/day. Estimated raw transfer time: 36 days. Applied 1.2× AFS overhead: 43 days. The original timeline of four weeks was built on theoretical line capacity, not measured throughput. Timeline reset: six weeks.

Week 2

Infrastructure provisioning, Private Endpoints, identity configuration

Provisioned three storage accounts via Terraform. Created Private Endpoints for each (file sub-resource). Configured private DNS zone and linked to corporate VNet. Disabled public access after verifying Test-NetConnection returned private IPs. Ran AzFilesHybrid to domain-join all three storage accounts to on-prem AD DS. Assigned share-level RBAC roles. Set root directory ACLs on each target share before any data copy — this saved hours during the upload phase. Discovered the QNAP was not domain-joined, meaning local QNAP SIDs needed remapping. SID remap timeline calculated: 177 hours across active shares with 50% buffer. Allocated Week 3 entirely to SID remap.

Week 3

SID remap — the week nobody planned for

The QNAP used local user accounts mapped to Active Directory through a proprietary LDAP bridge. Every file ACL referenced a QNAP-local SID that Azure Files could not resolve. The remap required mounting each share on a domain-joined Windows Server intermediate, running icacls to replace local SIDs with AD SIDs, and then uploading to Azure Files. The remap ran overnight for seven nights. Finance alone took 89 hours (59 raw + 50% buffer). On night four, the intermediate Windows Server ran out of disk space — it was a 500GB VM provisioned for a different purpose. Added a 2TB data disk and resumed. Total remap duration: 177 hours spread over 8 days, running in parallel where volume independence allowed.

Week 4

Initial RoboCopy runs over 10GbE LAN — Wave 1 archives complete

Archives and Backups migrated first via Azure Storage Mover (one-time, no hybrid sync needed). Initial RoboCopy of active shares ran over 10GbE LAN from intermediate Windows Server to Azure Files: 8–20 threads, /COPY:DATSO /DCOPY:DAT /B /MIR /IT flags. First RoboCopy run for Finance took 62 hours — 3.2TB at 8 threads over 10GbE. Switched to 20 threads for subsequent shares: Projects transferred in 41 hours. Archive shares completed via Storage Mover in parallel: 3.8TB in 28 hours. Wave 1 signed off: archives, media, backups complete. Go/no-go passed.

Week 5

Delta syncs, Wave 2 validation, QoS incident

Started delta RoboCopy runs (second pass with /MIR — only changes since first run). Wave 2 (Projects, Shared) delta runs completed in 4 hours each. Tested access with real user accounts from Windows 10, Windows 11, and macOS. macOS required SMB over HTTPS configuration — port 445 was blocked by the client's ISP for home-based users connecting via P2S VPN. Identified and fixed. Week 5 incident: IT team pushed a QoS firmware update to the Palo Alto firewall that reclassified SMB traffic and throttled it to 100 Mbps. Detected by Azure Monitor latency alerts. Fixed within 3 hours. Lesson: always have storage-specific monitoring alerts live before migration traffic starts.

Week 6

Final delta syncs, Wave 3 cutover, post-migration validation

Saturday 22:00: final RoboCopy run for Finance and HR (delta only — minutes, not hours). Set QNAP to read-only. Ran one final RoboCopy pass to catch last-minute changes. Executed DFS-N target change for all eight shares. Verified access with Finance team member on standby. Zero errors. Monday morning: 0 help desk calls in the first two hours. 2 calls by 10am — both from users confused by the different drive letter appearance in File Explorer (cosmetic, not functional). QNAP retained in read-only for 14 days post-cutover. Decommissioned after 14-day validation period with no issues.

Figure 2 — Migration execution architecture: the six-phase data flow from QNAP to Azure Files
QNAP15TB · 8 sharesLocal SIDsNot domain-joinedSMB mountWindows ServerIntermediateDomain-joinedicacls SID remapRoboCopy engine2TB data disk added10GbE LAN → WANRoboCopy /MIRAzure Files3 storage accountsPrivate EndpointsPremium SSD (Finance/HR)Standard HDD (rest)Azure Backup enabledArchives: Azure Storage Mover (direct — no SID remap)UsersDomain-joined PCsmacOS (P2S VPN)ApplicationsAD KerberosSMB 3.xDFS-N\\corp\Finance→ Azure FilesQNAP (source)Intermediate + SID remapAzure Files (target)End users
Archives bypass the intermediate server entirely — Storage Mover handles them directly from QNAP via QNAP's SMB exposure to the Azure Storage Mover agent running on a domain-joined VM
Incident Log

The Four Incidents — What Broke and How We Fixed It

Four distinct incidents occurred during this migration. All four were avoidable with better pre-migration discovery. All four are documented here in full — not to list them as cautionary tales, but because the exact root cause and fix are the most useful parts of a field account.

Incident 1Windows Server intermediate ran out of disk during SID remap overnight — Day 4 of Week 3
Symptom
SID remap job for Finance share stopped at 03:47. icacls process exited with error code 0x80070070 (ERROR_DISK_FULL). RoboCopy had not started for Finance that night.
Root Cause
The intermediate Windows Server VM (Standard_D4s_v3) had a 500GB OS disk. The icacls remap process writes transaction logs and temporary ACL snapshots. For a 3.2TB share with 847 unique ACL entries and 1.4M files, the temp space requirement exceeded available disk by 38GB.
Duration
6 hours 14 minutes from detection to resume. Detected at 08:02 when morning monitoring check found no overnight transfer logs for Finance.
Fix
Added a 2TB data disk to the intermediate VM. Moved the icacls temp directory to the new disk. Resumed remap from the last checkpoint. RoboCopy resumed the same evening.
Prevention
Size the intermediate server's data disk based on share size, not just the data to be copied. For ACL-heavy shares: provision data disk = (share size × 0.15) for ACL temp space, minimum 500GB. For this migration: Finance (3.2TB) needed 480GB of ACL temp space — nearly the size of the entire OS disk.
Incident 2macOS users could not mount Azure Files share after cutover — Day 1 of Week 6
Symptom
Three macOS users connecting via P2S VPN reported "Connection Failed" when attempting to mount \\account.file.core.windows.net\share from Finder. Windows users on the same VPN connection had no issues.
Root Cause
The client's ISP blocked outbound port 445 for all residential connections. Windows 10/11 clients used SMB over HTTPS (port 443) automatically when port 445 is unavailable. macOS Ventura and Sonoma SMB client does not fall back to port 443 automatically — it requires explicit configuration via the smb.conf file.
Duration
2 hours 40 minutes from first report to all users restored. Discovered during Week 5 validation but not fully resolved before cutover — the fix was deployed but not verified on all macOS versions.
Fix
Configured SMB over HTTPS on the storage account. Deployed an updated smb.conf to macOS clients via Intune MDM: soft_mount=yes and SMBPreferredMaxSector=4096. For macOS users not enrolled in MDM, provided manual mount command using the HTTPS endpoint.
Prevention
Test port 445 connectivity from every OS type and every network path (office, home VPN, cellular hotspot) during pre-migration validation — not just from the server room. macOS specifically requires explicit SMB over HTTPS configuration if port 445 is blocked. Do not assume Windows behaviour applies to macOS clients.
Incident 3QoS firmware update throttled SMB traffic to 100 Mbps — Day 3 of Week 5
Symptom
Azure Monitor throughput alert fired at 14:23: storage account write throughput dropped from 280 Mbps to 94 Mbps. RoboCopy progress logs showed Delta run for Projects estimated 22 hours instead of the expected 4 hours.
Root Cause
IT team pushed a QoS firmware update to the Palo Alto firewall. The update included a new application signature that reclassified Azure Files SMB traffic as "generic cloud storage" and applied a 100 Mbps rate limit — a category that had been added to a bandwidth management policy without review. No change advisory was raised because the firmware update was classified as a security patch, not a configuration change.
Duration
3 hours 12 minutes. Alert fired at 14:23, root cause identified at 15:40, QoS rule updated at 17:35, throughput restored.
Fix
Updated the Palo Alto QoS policy to add Azure Files traffic (destination: *.file.core.windows.net, port 445/443) to the uncapped business-critical bandwidth class. Added Azure storage account IPs to the firewall allowlist to prevent reclassification on future updates.
Prevention
Create a change freeze on all network hardware during active migration windows. Azure Monitor throughput alerts caught this within 40 minutes — without monitoring, this would have been detected the next morning when someone noticed the delta sync had not completed. Storage-specific monitoring is not optional during migration execution.
Incident 4Kerberos ticket expiry caused intermittent access failures for 3 long-running application sessions — Week 6 Day 3
Symptom
Three application servers reported "Access Denied" errors on Azure Files shares approximately 10 hours after the DFS-N cutover. Applications that had been running continuously since before cutover were affected; users who had logged out and back in had no issues.
Root Cause
Kerberos tickets have a default lifetime of 10 hours. Applications holding persistent file handles to the on-premises QNAP had their Kerberos tickets renewed automatically by the domain controller. After cutover, the same applications attempted to renew tickets for Azure Files connections, but the application processes did not trigger renewal because they used a cached SMB session token rather than a full re-authentication. The Kerberos ticket expired and was not automatically replaced for long-running processes.
Duration
1 hour 20 minutes. Applications restarted, re-authenticated, and functioned normally. No data loss.
Fix
Restarted the affected application services. Configured Kerberos ticket renewal policy in Group Policy to reduce the maximum ticket lifetime to 8 hours and enable automatic renewal up to 7 days: Maximum lifetime for user ticket: 8 hours, Maximum lifetime for user ticket renewal: 7 days. Tested with each affected application to confirm renewal works without service restart.
Prevention
Before cutover, identify every application that holds persistent file handles and verify it re-authenticates correctly after Kerberos ticket expiry. Applications that use long-running session tokens rather than re-authenticating on reconnect will fail at the 10-hour mark. Test specifically with a 10+ hour session during pre-cutover validation, not just a fresh connection test.
Implementation Detail

The Exact RoboCopy Flags That Worked — and Why

RoboCopy has dozens of flags. The combination below is what we used for this migration, tuned through three iterations. Each flag has a specific reason. Use this as a reference, but understand what each flag does before applying it to your environment — some flags interact in non-obvious ways when combined with Azure File Sync cloud tiering.

1

Initial bulk copy — Run 1 (users still on QNAP)

The first run copies everything. Users are still working on the QNAP during this run — files will change. That is expected and handled by subsequent delta runs. Do not block user access during the initial run. Run from the domain-joined intermediate Windows Server with the QNAP mounted as the source and the Azure Files share mounted as the target.

RoboCopy — Initial bulk copy (Run 1, users still active on source)# Mount source (QNAP after SID remap) and target (Azure Files) before running # net use S: \\INTERMEDIATE-SERVER\Finance-remapped # net use T: \\storageaccount.file.core.windows.net\finance
RoboCopy S:\ T:\ ^
  /COPY:DATSO ^ # Copy Data, Attributes, Timestamps, Security (NTFS ACLs), and Ownership
  /DCOPY:DAT ^ # Copy directory Data, Attributes, Timestamps
  /B ^ # Backup mode — bypasses file access restrictions, reads locked files
  /MIR ^ # Mirror — deletes on target what no longer exists on source
  /IT ^ # Include Tweaked files (same date/size but different attributes)
  /R:3 /W:10 ^ # 3 retries, 10 second wait — balanced for transient Azure network errors
  /MT:20 ^ # 20 threads — optimal for initial Azure Files upload (8–20 recommended by Microsoft)
  /LOG+:C:\Logs\robocopy-finance-run1.log ^ # Append log — never overwrite
  /TEE ^ # Output to console AND log file simultaneously
  /NP ^ # No Progress — suppresses per-file % to reduce log size
  /XD "$RECYCLE.BIN" "System Volume Information" ^ # Exclude system directories
  /XF "desktop.ini" "thumbs.db" # Exclude Windows metadata files not needed in Azure Files
RoboCopy — Delta sync runs (Run 2+ before cutover, source in read-only)# After Run 1 completes: run this multiple times, each faster than the last # Before FINAL delta run: set QNAP share to read-only to freeze the dataset # Last delta run should complete in minutes, not hours
RoboCopy S:\ T:\ ^
  /COPY:DATSO /DCOPY:DAT /B /MIR /IT ^
  /R:3 /W:10 ^
  /MT:16 ^ # Reduce threads for delta runs — processor-bound rather than bandwidth-bound
  /LOG+:C:\Logs\robocopy-finance-delta.log ^
  /TEE /NP ^
  /XD "$RECYCLE.BIN" "System Volume Information" ^
  /XF "desktop.ini" "thumbs.db"
⚠ Critical flag: /COPY:DATSO not /COPYALL

Use /COPY:DATSO rather than /COPYALL for Azure Files migrations. /COPYALL includes the Auditing ACL flag (U) which Azure Files does not support — RoboCopy will log errors for every file when this flag is included, making the log unusable for identifying real errors. /COPY:DATSO copies Data, Attributes, Timestamps, Security (NTFS ACLs), and Ownership — everything Azure Files supports and nothing it does not.

Figure 3 — Multi-run RoboCopy pattern: how run duration decreases and data gap narrows before cutover
72hrs48hrs24hrs4hrs62hrsRun 1Bulk copy · users active3.2TB Finance18hrsRun 2Changes since Run 14hrsRun 3Changes since Run 2QNAP → read-onlyFreeze dataset22minFinal deltaSource frozenDFS-N cutoverSat 22:00
Each successive RoboCopy run processes only what changed since the previous run — by Run 3, the data gap is small enough that a final delta with the source frozen completes in minutes, not hours
Implementation Detail

The SID Remap — What It Is, Why It Matters, and the Exact Procedure

This was the week that nobody had planned for and that nearly broke the timeline. The QNAP used local user accounts mapped to Active Directory via LDAP. Every file's ACL contained a QNAP-local SID — an identifier that exists only within the QNAP's local user database. Azure Files, which resolves identities against Active Directory and Entra ID, cannot interpret these SIDs. If you copy files with local SIDs directly to Azure Files, the files arrive with permissions that reference identities Azure Files cannot resolve. Users get Access Denied even when they should have access.

The remap must happen on a domain-joined Windows Server intermediate: mount the QNAP share, run icacls to replace local SIDs with their corresponding AD SIDs using a mapping table, then copy the remapped share to Azure Files. The formula for estimating remap duration: (file count × unique ACL entries × 0.0001) ÷ 2,000 entries/minute = hours. Run in parallel where shares are independent to reduce wall-clock time.

PowerShell — SID remap procedure (run on domain-joined intermediate server)# Step 1: Build SID mapping table — QNAP local SID → AD SID # Run this on the QNAP via SSH or admin panel to get local user SIDs $sidMap = @{
  "S-1-5-21-QNAP-1001" = "S-1-5-21-DOMAIN-500001" # QNAP\johndoe → DOMAIN\johndoe
  "S-1-5-21-QNAP-1002" = "S-1-5-21-DOMAIN-500002" # QNAP\janedoe → DOMAIN\janedoe
  "S-1-5-21-QNAP-1050" = "S-1-5-21-DOMAIN-500050" # QNAP\svcaccount → DOMAIN\svcaccount
}

# Step 2: Remap ACLs on each file — run on mounted QNAP share Get-ChildItem -Path "S:\Finance" -Recurse -ErrorAction SilentlyContinue | ForEach-Object {
  $acl = Get-Acl -Path $_.FullName -ErrorAction SilentlyContinue
  if ($acl) {
    $modified = $false
    foreach ($ace in $acl.Access) {
      $oldSid = $ace.IdentityReference.Translate([System.Security.Principal.SecurityIdentifier]).Value
      if ($sidMap.ContainsKey($oldSid)) {
        $newIdentity = New-Object System.Security.Principal.SecurityIdentifier($sidMap[$oldSid])
        # Remove old ACE, add new ACE with AD SID
        $acl.RemoveAccessRule($ace)
        $newRule = New-Object System.Security.AccessControl.FileSystemAccessRule($newIdentity, $ace.FileSystemRights, $ace.InheritanceFlags, $ace.PropagationFlags, $ace.AccessControlType)
        $acl.AddAccessRule($newRule)
        $modified = $true
      }
    }
    if ($modified) { Set-Acl -Path $_.FullName -AclObject $acl -ErrorAction SilentlyContinue }
  }
}
# After remap: run RoboCopy with /COPY:DATSO to copy remapped ACLs to Azure Files
Cutover Procedure

The Saturday Night Cutover — Step by Step

The cutover window was 22:00 Saturday to 06:00 Sunday. We used two hours. Here is the exact sequence, with the commands that ran at each step.

1
21:30 — Pre-cutover checks

Verify all delta syncs are complete and Azure Files shares are healthy

Ran final delta RoboCopy for all active shares. Finance: 22 minutes. HR: 8 minutes. Projects: 31 minutes. Verified file counts match between QNAP and Azure Files for each share using Get-ChildItem recursive count. Checked Azure Monitor: no throttling alerts, storage account health green. Confirmed Finance team member and IT lead were on standby. Help desk briefed and on call.

2
22:00 — Set QNAP shares to read-only

Freeze the source — no further writes to QNAP

Set all eight QNAP shares to read-only via QNAP admin panel (Shared Folders → [share] → Edit → Permissions → Read only for all users). This freezes the dataset. No new files can be written. Ran one final RoboCopy pass for Finance and HR (the two highest-churn shares): Finance delta completed in 4 minutes, HR in 2 minutes. The data gap at cutover was zero minutes of uncopied changes.

3
22:08 — Execute DFS-N target change for all eight shares

Redirect all share paths to Azure Files with a single PowerShell sequence

Ran the DFS-N target change for all eight shares in sequence. Each command took under three seconds to execute.

PowerShell — DFS-N cutover commands (run at 22:08, eight shares in sequence)# Remove QNAP targets and add Azure Files targets for all eight shares # Run from a domain controller or server with DFS management rights
$namespace = "\\corp.local\files"
$storageAccount = "corpazfilespr.file.core.windows.net" # Premium SSD account
$storageAccountStd = "corpazfilesstd.file.core.windows.net" # Standard HDD account

# Disable old QNAP targets (do not delete — keep for rollback) Set-DfsnFolderTarget -Path "$namespace\Finance" -TargetPath "\\QNAP\Finance" -State Offline
Set-DfsnFolderTarget -Path "$namespace\HR" -TargetPath "\\QNAP\HR" -State Offline
# ... repeat for all 8 shares

# Add Azure Files as new active targets New-DfsnFolderTarget -Path "$namespace\Finance" -TargetPath "\\$storageAccount\finance" -State Online
New-DfsnFolderTarget -Path "$namespace\HR" -TargetPath "\\$storageAccount\hr" -State Online
New-DfsnFolderTarget -Path "$namespace\Projects" -TargetPath "\\$storageAccountStd\projects" -State Online
New-DfsnFolderTarget -Path "$namespace\Shared" -TargetPath "\\$storageAccountStd\shared" -State Online
# ... repeat for remaining 4 shares

# Verify: all targets should show Azure Files as active, QNAP as offline Get-DfsnFolderTarget -Path "$namespace\Finance" | Select TargetPath, State
4
22:12 — Access verification

Verify with real user accounts from real client machines

Finance team member (on standby) opened an Excel workbook from \\corp.local\files\Finance\Reports\Q2-2026.xlsx. File opened in 3 seconds — within normal range. Created a test file, deleted it, confirmed timestamps appeared correctly. HR share verified identically. Checked Azure Monitor: successful auth events appearing in sign-in logs within 30 seconds of DFS-N change. Confirmed Private DNS resolution was returning correct private IPs from three test clients in different network segments.

5
22:40 — All verified · cutover complete

Document completion and hand over to monitoring

All eight shares verified. Azure Monitor dashboards showed normal throughput and auth patterns. QNAP retained in read-only mode for 14-day rollback window. Sent cutover completion notification to stakeholders at 22:42. Help desk briefed on Monday morning watch list: any user who cannot access a mapped drive should call immediately. Help desk calls on Monday: 2 (both cosmetic UI questions, resolved in 5 minutes each). Decommissioning of QNAP scheduled for Day 15 post-cutover.

Figure 4 — Post-migration monitoring: the KQL queries running in Log Analytics for the first 30 days
Auth HealthQuery: StorageFileLogs| where StatusCode == 403| summarize count()by CallerIpAddressAlert: >10 failures in 5minCaught Incident 4 Kerberosticket expiry issue Day 3Throughput MonitorQuery: AzureMetrics| where MetricName =="Egress"| summarize avg(Total)by bin(TimeGenerated, 5m)Alert: <100Mbps during biz hrsCaught QoS throttle (Incident 3)within 40 minutesCost MonitoringAzure Cost Management:Daily budget alertsPer-storage-account tagsMonth-1 actual: $471Month-1 estimate: $474Variance: $3 (0.6%)vs on-prem: $2,100/moSavings: $1,629/mo
All three monitoring dashboards were live before the cutover — detecting Incidents 3 and 4 within minutes rather than hours
Lessons Learned

What We Would Do Differently

These are the seven things we would change if running this migration again — not general best practices, but specific decisions that cost us time or created incidents in this project.

  • Provision the intermediate server with a dedicated 2TB+ data disk from day one. Incident 1 cost six hours because we added the disk on Day 4. The disk cost $12/month. The rework cost half a day of engineer time. This is the easiest call in retrospect.
  • Run the SID remap estimation calculation before agreeing the project timeline. The SID remap added eight days we had not accounted for. The formula is known — (files × unique ACLs × 0.0001) ÷ 2,000 entries/min = hours — but nobody ran it until we were already in Week 2. Run it in Week 1 during the inventory phase.
  • Test port 445 from every network type during pre-migration validation, not just from the office. Incident 2 (macOS users blocked) was discovered in Week 5 because we tested from domain-joined office machines only. Home users on P2S VPN were not tested until three days before cutover. Test every OS type from every network segment — office, home VPN, cellular hotspot — in Week 1.
  • Test Kerberos ticket renewal for all applications before cutover. Incident 4 (application access failures after 10 hours) is the kind of failure that a production migration stress test would have caught. Simulate a 12-hour connected session with each application that holds persistent file handles and verify it reconnects correctly at the 10-hour Kerberos expiry mark.
  • Place the network firewall QoS firmware update on the change freeze list. Incident 3 happened because a security patch was classified as non-impacting. For active migration windows, add all network hardware changes to the change advisory process regardless of classification. The firewall vendor's "patch" reclassified Azure Files traffic at a firmware level.
  • Set root directory ACLs before any data is copied — not after. We set root ACLs correctly, but only after discovering in another engagement that setting them after a large dataset has been written causes ACL propagation to take hours. Document this in the procedure and make it a gate, not an afterthought.
  • Run /COPY:DATSO not /COPYALL. We caught this on Run 1 of the Finance share when the RoboCopy log was filled with "Auditing ACL" copy errors. Switched to /COPY:DATSO for Run 2. The log became clean and readable. An unreadable log during a production migration is a real risk — you cannot identify genuine errors through the noise.

"Every incident in this migration was discoverable before migration started. None of them required new knowledge. All of them required running the specific check — the bandwidth measurement with QoS applied, the SID remap estimation, the port 445 test from a home connection, the 10-hour Kerberos session test. The planning phase is not overhead. It is the migration."

— Francis Avorgbedor | Azure Field Notes

Key Takeaways

Use /COPY:DATSO not /COPYALL for RoboCopy migrations to Azure Files. /COPYALL includes the Auditing ACL flag that Azure Files does not support — it fills the log with errors that mask real problems.
If the source NAS is not domain-joined, budget for SID remap before any data transfer. Run the estimation formula during the inventory phase, not after committing to a timeline.
Size the intermediate server's data disk for ACL remap temp space: minimum (share size × 0.15) or 500GB, whichever is larger.
Use 8–20 RoboCopy threads for initial bulk copy; reduce to processor-core count for delta runs which are processor-bound rather than bandwidth-bound.
Test port 445 from every OS type and every network path (office, home VPN, cellular) before migration. macOS requires explicit SMB over HTTPS configuration when port 445 is blocked — it does not fall back automatically like Windows clients.
Test Kerberos ticket renewal for all applications that hold persistent file handles. Simulate a 12-hour connected session and verify the application reconnects at the 10-hour ticket expiry without requiring a service restart.
Put Azure Monitor throughput and auth failure alerts live before migration traffic starts — not after cutover. Incidents 3 and 4 were caught in minutes because monitoring was running. Without it, both would have been found the next morning.
DFS-N makes rollback a single command. Keep the QNAP in read-only mode for 14 days post-cutover before decommissioning. The safety net costs almost nothing in time; removing it early costs everything if you need it.
Frequently Asked Questions
Why use a Windows Server intermediate instead of copying directly from QNAP to Azure Files?
Because the QNAP was not domain-joined, its file ACLs contained local QNAP SIDs that Azure Files cannot resolve. The SID remap had to happen on a domain-joined Windows Server that could translate between QNAP-local SIDs and Active Directory SIDs. If the NAS had been domain-joined, direct NAS to Azure Files via RoboCopy over a mapped drive would have been possible. For domain-joined sources, the intermediate server is optional — for non-domain-joined NAS devices, it is required for ACL preservation.
Why not use Azure Storage Mover for active shares instead of RoboCopy?
Azure Storage Mover was used for the archive shares (Archive2022 and Archive2023) which needed a one-time transfer. For the active shares, we used RoboCopy because the multi-run delta pattern — where successive runs only process changes since the previous run — gave us a predictable path to a very short final delta before cutover. Storage Mover does not support the same kind of incremental multi-run pattern with the source still active. For large active shares where you want to minimise the final cutover window, the RoboCopy multi-run approach remains the most controllable option.
How did you validate that all ACLs transferred correctly?
We ran two validation checks. First, we sampled 100 random files per share and compared Get-Acl output between the QNAP source and Azure Files target — both the identity references and the access rights. Second, and more importantly, we tested access with real user accounts from each user type defined in the RBAC and permission matrix: read-only Finance auditor, Finance read/write user, HR manager, Projects contributor. If a permission transfer error exists, it will surface when a real user tries to do something they should be able to do and cannot — or does something they should not be able to do and can. File-level ACL comparison catches the former; behaviour testing catches both.
What was the total engineer-hours cost of the migration?
Approximately 180 engineer-hours across the six weeks, including planning, the SID remap (which ran overnight but required oversight and monitoring), all RoboCopy runs, the four incidents, cutover, and post-migration validation. The SID remap alone accounted for roughly 60 hours of active engineering involvement across the eight-day remap window. The two hours of unexpected rework from Incidents 1 through 4 total approximately 14 engineer-hours.
Was the $474/month Azure cost as budgeted?
The variance was $3 — $471 actual versus $474 estimated. The estimate was built from the Azure Pricing Calculator using the share mapping table and provisioned sizes. The slight under-run came from two Archive shares consuming slightly less than provisioned due to deduplication on the QNAP that did not transfer to Azure Files (Azure Files Standard does not support deduplication). The on-premises cost was $2,100/month including QNAP lease, support contract, and rack space. Monthly saving: $1,629. Payback period for migration costs: 3.4 months.

Popular posts from this blog

The Cloud Incumbent: AWS Bedrock Hosts Every Frontier Model and Amazon Is Betting on Neutrality AWS at $37.6 billion quarterly revenue, growing 28%. $13 billion invested in Anthropic. A $100 billion Anthropic-to-AWS commitment. Trainium with $225 billion in customer revenue commitments. The most quietly powerful AI strategy in the race. By Francis Avorgbedor | Azure Engineer  ·  July 4, 2026  ·  14 min read  ·  Amazon · AWS · Cloud AI 74 SEVENAI Momentum Score — Rank #5 $37.6B AWS Q1 2026 revenue — 28% YoY growth ▲ Fastest in 15 quarters $13B Total Amazon investment in Anthropic to date ▲ Strategic anchor 100K+ Customers running Claude on AWS Bedrock ▲ Distribution moat Amazon's AI strategy is built on a thesis that every other Magnificent Seven company is testing against — and that Amazon is uniquely positioned to win regardless of the outcome. The thesis is neutrality. In a race where Microsoft has bet on OpenAI, Google has bet on Gemini, and Meta has bet...
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 ^^^^^^^^ ...
  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.
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...

AKS CrashLoopBackOff, Pending Pods, and NotReady Nodes — The Real Fixes Engineers Use

Incident Playbook AKS Kubernetes kubectl 2026 AKS CrashLoopBackOff, Pending Pods, and NotReady Nodes — The Real Fixes Engineers Use Every AKS engineer eventually faces the same nightmare: CrashLoopBackOff at 2am, pods stuck Pending for no clear reason, or nodes flipping to NotReady mid-deployment. The difference between panic and control is knowing the exact diagnostic sequence — and the real fixes that work in production. This guide gives you both. 3 commands get pods, describe pod, and logs diagnose roughly 90% of AKS incidents before you touch anything else Exit 137 The code that means OOMKilled — the container hit its memory limit and was killed by the kernel (128 + SIGKILL 9) Events The bottom of kubectl describe is where the real cause lives — Pending, FailedScheduling, and image errors all surface there CoreDNS The single component behind most "intermittent" production failures — service discovery breaks quietly and looks like an app bug Table of Contents 01 The 3 Comm...
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...
2026 Edition 100 Tools Software Engineering DevOps AIOps Top 100 Best AI Tools for Azure  Engineers and DevOps Professionals in 2026 85% of developers now regularly use AI tools. Fully AI-generated code accounts for nearly 28% of all pull requests. The question is no longer whether to use AI tools — it is which ones, in which combination, for which part of the lifecycle. This guide cuts through the noise: 100 tools, 10 categories, honest pricing, real use cases, and a selection framework for building your stack without redundancy. 85% Percentage of developers who now regularly use AI tools, per JetBrains' 2025 State of Developer Ecosystem report — up from near zero three years ago 28% Share of all pull requests containing primarily AI-generated code in 2026 — the metric that signals AI coding assistants have moved from experiment to workflow $50B Cursor's reported valuation in April 2026 Series D talks — the number that signals investor confidence in the AI developer tools mark...

Azure Files vs Azure NetApp Files: Which One Should You Choose?

Azure Files vs Azure NetApp Files: Which One Should You Choose? Performance tiers, protocol support, dual-protocol capability, pricing models, SAP/Oracle/HPC suitability, data management features, and the decision framework that maps each workload type to the right service — with step-by-step setup procedures for both. FA Francis Avorgbedor Azure Engineer July 15, 2026 20 min read Azure Storage · Architecture 4 Azure Files tiers: Premium SSD, Standard Hot, Cool, Tx Optimized 3 ANF performance tiers: Standard, Premium, Ultra — all SSD-backed 4TiB ANF minimum provisioning — significant cost floor for small workloads Dual ANF serves the same data via SMB and NFS simultaneously — AF cannot Introduction Two Services, One Surface Area — Completely Different Purposes Microsoft offers two fully managed, enterprise-grade file storage services in Azure. They share a surface area — both serve file shares over standard protocols, both run on managed infrastructure, and both integrate with Microsof...
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...

How to Deploy an AI Chatbot on Azure Using Azure 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...