Excellent — here’s Section 9 of your full article, expanding it into a complete, production-style migration guide.
This section shows how to run FileMigrationServiceV2 inside a console app with a progress bar, job summary, and estimated completion time.
Running the Migration with Progress Bar and Job Summary (Console App Example)
Overview
This section integrates the migration logic into a simple C# console application, allowing you to:
- Display a real-time progress bar
- Show file-by-file migration updates
- Estimate total time and remaining duration
- Output a summary report once migration is finished
This version is perfect for running migrations from your local machine or as a scheduled job in an Azure VM.
1️⃣ Program Structure
We’ll create a simple console entry point (Program.cs) that:
- Authenticates both SharePoint contexts
- Retrieves all files to be migrated
- Iterates through them using
FileMigrationServiceV2 - Displays migration progress in the console
2️⃣ Full Example – Program.cs
using Microsoft.SharePoint.Client;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Security;
using System.Threading;
namespace Contoso.MigrationTool
{
class Program
{
static void Main(string[] args)
{
Console.Title = "Contoso SharePoint Migration Console";
Console.WriteLine("===============================================");
Console.WriteLine(" SHAREPOINT MIGRATION TOOL - Version 2.0");
Console.WriteLine(" Preserves versions, authors, timestamps & comments");
Console.WriteLine("===============================================\n");
string sourceUrl = "https://contoso.sharepoint.com/sites/Engineering";
string targetUrl = "https://contoso.sharepoint.com/sites/Archive";
string libraryName = "Project Documents";
string sourceRoot = "/sites/Engineering/Project Documents";
string targetRoot = "/sites/Archive/Project Documents";
Console.Write("Enter your SharePoint username: ");
string username = Console.ReadLine();
SecureString password = GetSecurePassword("Enter your password: ");
var sourceCtx = new ClientContext(sourceUrl)
{
Credentials = new SharePointOnlineCredentials(username, password)
};
var targetCtx = new ClientContext(targetUrl)
{
Credentials = new SharePointOnlineCredentials(username, password)
};
// Load all items
var list = sourceCtx.Web.Lists.GetByTitle(libraryName);
var query = new CamlQuery { ViewXml = "<View Scope='RecursiveAll'><RowLimit>5000</RowLimit></View>" };
var items = list.GetItems(query);
sourceCtx.Load(items, each => each.Include(
item => item["FileRef"],
item => item.File,
item => item.FileSystemObjectType));
sourceCtx.ExecuteQuery();
var files = items.Where(i => i.FileSystemObjectType == FileSystemObjectType.File).ToList();
int total = files.Count;
Console.WriteLine($"\nFound {total} files to migrate.\n");
Stopwatch timer = Stopwatch.StartNew();
int index = 0;
foreach (var item in files)
{
index++;
string fileUrl = item.File.ServerRelativeUrl;
string relativeSubFolder = Path.GetDirectoryName(fileUrl.Replace(sourceRoot, "")).Replace("\\", "/");
string finalTargetFolder = targetRoot.TrimEnd('/') + relativeSubFolder;
DrawProgressBar(index, total, fileUrl);
try
{
FileMigrationServiceV2.MigrateFileWithVersions(
sourceCtx,
targetCtx,
fileUrl,
finalTargetFolder,
publishAfterMigration: true
);
}
catch (Exception ex)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"Error migrating {fileUrl}: {ex.Message}");
Console.ResetColor();
}
// Add short delay to avoid throttling
Thread.Sleep(100);
}
timer.Stop();
Console.WriteLine("\n===============================================");
Console.WriteLine($"Migration complete! {total} files processed.");
Console.WriteLine($"Total time: {timer.Elapsed:hh\\:mm\\:ss}");
Console.WriteLine($"Log saved at: {Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "MigrationLog.csv")}");
Console.WriteLine("===============================================");
}
private static SecureString GetSecurePassword(string prompt)
{
Console.Write(prompt);
SecureString pass = new SecureString();
while (true)
{
var key = Console.ReadKey(intercept: true);
if (key.Key == ConsoleKey.Enter)
{
Console.WriteLine();
break;
}
else if (key.Key == ConsoleKey.Backspace && pass.Length > 0)
{
pass.RemoveAt(pass.Length - 1);
Console.Write("\b \b");
}
else if (!char.IsControl(key.KeyChar))
{
pass.AppendChar(key.KeyChar);
Console.Write("*");
}
}
pass.MakeReadOnly();
return pass;
}
private static void DrawProgressBar(int current, int total, string fileName)
{
int barWidth = 50;
double progress = (double)current / total;
int filled = (int)(progress * barWidth);
Console.CursorLeft = 0;
Console.Write("[");
Console.Write(new string('=', filled));
Console.Write(new string(' ', barWidth - filled));
Console.Write($"] {current}/{total} files ({progress * 100:0.0}%)");
Console.WriteLine();
Console.WriteLine($"Migrating: {Path.GetFileName(fileName)}");
}
}
}
3️⃣ Example Console Output
===============================================
SHAREPOINT MIGRATION TOOL - Version 2.0
Preserves versions, authors, timestamps & comments
===============================================
Found 45 files to migrate.
[========== ] 10/45 files (22.2%)
Migrating: Proposal_v1.docx
Version migrated: 2024-07-05 09:13
Version migrated: 2024-08-01 17:45
Current version migrated and published.
...
[========================================] 45/45 files (100%)
Migration complete! 45 files processed.
Total time: 00:18:42
Log saved at: C:\MigrationTool\MigrationLog.csv
===============================================
4️⃣ Key Features in the Console App
| Component | Description |
|---|---|
| Progress Bar | Displays migration percentage in real time. |
| Throttling Delay | Adds a 100ms delay per file to reduce SharePoint Online throttling risk. |
| Job Timer | Measures total migration duration. |
| Error Handling | Displays failed files in red for visibility. |
| CSV Log | Automatically created from FileMigrationServiceV2 for audit compliance. |
5️⃣ Optional Enhancements
| Feature | Implementation Idea |
|---|---|
| Parallel Migration | Use Parallel.ForEach() with throttling to process files in batches. |
| Azure Storage Logs | Store logs in an Azure Blob container instead of local CSV. |
| Real-time UI Dashboard | Feed logs into a WPF or MAUI app showing charts and job metrics. |
| Incremental Migration | Compare Modified timestamps before copying to skip unchanged files. |
| E-mail Summary | Send the CSV log automatically to admins when migration finishes. |
6️⃣ Full Project Layout
📂 Contoso.MigrationTool
┣ 📜 Program.cs
┣ 📜 FileMigrationServiceV2.cs
┣ 📜 MigrationLog.csv
┗ 📜 app.config
7️⃣ Best Practice Checklist
| Area | Recommendation |
|---|---|
| Authentication | Always use modern credentials or MSAL for token-based login. |
| Permissions | Ensure Full Control on both source and target libraries. |
| Logging | Keep every run’s CSV as a permanent audit record. |
| Error Recovery | Implement retry logic for transient SharePoint exceptions. |
| Testing | Start with small libraries before scaling up full migrations. |
8️⃣ References
- Perform basic CSOM operations in SharePoint
- Manage file versions with CSOM
- Avoid throttling in SharePoint Online
Conclusion
This console-based approach turns your migration utility into a transparent, traceable, and user-friendly process.
You can now run large SharePoint migrations with:
- Real-time feedback
- Precise progress tracking
- Auditable CSV logs
- Safe version and metadata preservation
