Migrate Users Home Drives to OneDrive for Business with PowerShell

We have Office 365 now for 3 years almost and one the things on my to-do list was migrating the users home folders to OneDrive for Business. I tried it last year, asking everyone to copy or move the files themself but that didn’t work out as planned.

So this time I had to change my tactics, clearly, the users (150+) weren’t going to move the files themself. Now I know OneDrive has the ability to move the Known Folders (Desktop, Documents, and Pictures), but that are not the personal files that are stored on the file server. So to move the files we are going to use PowerShell.

Server storage is expensive, so it’s a waste of money to buy storage for the home folders when every user gets unlimited storage in the cloud. Another advantage for the users is that they can access their files from every location of course.

Preparing the home folder move

Before we can move the files we need to prepare some things. There are some GPO settings we can configure, review the OneDrive admin settings, we need to inform our users and we have to deploy the script.

Another potential issue are invalid filenames or to longs paths. Just like SharePoint has OneDrive for Business some file limitations:

  • Invalid file or folder names
  • Invalid characters ( ” * : < > ? / \ |)
  • Max 15Gb file size
  • 400 character path limit

Read the full list of limitations here.

Checking OneDrive for Business Admin Center

It’s always good to check the Office 365 Admin Center before you are running out this kind of migrations. New settings are added regularly and you might want to limit for example the sharing options for OneDrive for Business.

Make sure the settings are in line with your business security policies.

OneDrive for Business Known Folder Move

Moving the known folders (Desktop, Documents, and Pictures) to OneDrive is something you really should enable. It only requires two GPO settings to set it up and it will roll out by itself. Your users can work and store the files as they are used to do, but they are safely backupped in the cloud.

When we switch from a full Citrix environment to standalone, one of my concerns was keeping the user’s data safe. Of course, they store the work-related files on the network drivers and now on SharePoint. But we all know users like to keep work-related files on there desktop or they may save them unknowingly in their documents folder.

And yes, we need to educate our users, but by settings up known folders, we have one problem less to worry about.

Get your Office 365 Tenant ID

Before we can configure the GPO’s we need to get our tenant ID. With PowerShell we can get it with only one command:

# Connect to the Azure AD
connect-azuread

# result:
Account                Environment Tenant
-------                ----------- ------
johndoe@lazyadmin.nl   AzureCloud  <your tenant ID>

# Optional - Get your tenant details
get-azureadtenantdetail

After you successfully connected to Azure AD you will get the Tenant ID in the results. If you don’t get it you can always use the cmdlet Get-AzureAdTenantDetail where the objectid is your tenant id.

Configuring the GPO’s

There are a couple of GPO’s we need to configure. Keep in mind that if you enable this GPO for your entire organization at once you will have a huge network traffic spike due to the OneDrive synchronization. So you might want to use a temporary security group to target the GPO.

Create a new GPO and navigate to the following policies:

Computer configuration\Policies\Administrative Templates\OneDrive


If you can’t find the OneDrive Group Policies, then you may need to copy the adml and admx files first:

  1. Copy the .adml and .admx file from %localappdata%\Microsoft\OneDrive\BuildNumber\adm\  on a workstation
  2. Paste the .admx in the central store of the domain controller: \\domain\sysvol\domain\Policies\PolicyDefinitions
  3. Paste the .adml in the correct language folder in the central store
Migrate user home folder to OneDrive


If you haven’t done already, first enable the policy Silently configure OneDrive using the primary Windows account. This will save the user an extra login when they set up OneDrive the first-time.

Also, make sure you limit the upload bandwidth, this way one user can’t slow down the whole internet connection. Set the policy Set the maximum percentage of upload bandwidth that OneDrive.exe uses to something like 70.

To move the known folder, enable the policy Silently redirect Windows Known folder to OneDrive and enter the tenant ID. You can enable a user notification if you like.

It’s also recommended to enable the Prompt user to move Windows Known folder to OneDrive policy to support any legacy OneDrive clients.

Moving the users home folder

The last step, and this what we were planned to do, is to move the user home folder data to their OneDrive for Business account. In my case are the files stored on the file servers. So every user has a network drive mapped to their folder.

A few things we need to tackle in our PowerShell script is to find the users OneDrive folder, check if it is connected and leave an indication behind in the home folder so we know it’s copied. We are not going to move the files, just copy it. I plan to move the files to our archive server later so we always have a backup.

PowerShell and OneDrive

To check if OneDrive is connected we need to import a DLL file to readout the OneDrive status. Download the OneDriveLib.dll from this GitHub repository. Place it in a location every user can access, I recommend the netlogon but any other network share can work as well.

First, we import the DLL file:

Import-Module \\contoso.local\netlogon\OnedriveLib.dll

Next, we need to get the path to the users OneDrive folder. The default location for this folder is the users profile folder. If you have multiple OneDrive folders you can user the USERPROFILE variable, otherwise, I would recommend using the OneDriveCommercial variable.

#Find OneDrive folder
$path = $env:OneDriveCommercial

# Alternative:
# $path = $env:USERPROFILE + '\OneDrive - LazyAdmin'

To check if we have the right folder and that OneDrive is connected we can get the OneDrive status:

#Get OneDrive status
$ODStatus = Get-ODStatus -ByPath $path

# or in short:
$ODStatus = Get-ODStatus -ByPath $env:OneDriveCommercial

If it’s connected it will return either ‘UpToDate’ or ‘Syncing’. So we check the result and use robocopy to copy the files.

if ($ODStatus -eq 'UpToDate' -or $ODStatus -eq 'Syncing') {
    #OneDrive is connected
    write-host 'OneDrive connected and found'
    robocopy $env:HOMESHARE $env:OneDriveCommercial /E /SEC
    new-item $env:HOMESHARE -name '_FILES COPIED TO ONEDRIVE.txt' -ItemType 'file' -Value 'Files Copied'
}

As you can see on the last line we create a new item, a text file. We run a PowerShell script, later on, to check if we migrated every home drive folder before we archive them.

Conclusion

Run the PowerShell in the logged-on user’s context mode, you can use PDQ Deploy for this, a GPO (logon script) or SCCM. If you have many users, make sure you spread the deployment to limit the impact on your network.

With PowerShell make sure you check the available environment variables. In my first attempt to make this script, I tried using the drive letter of the users home folder. That didn’t work out. While looking for an alternative I noticed that we can use $env:HomeDrive which will return the drive letter or $env:HomeShare which returns the actual path.

28 thoughts on “Migrate Users Home Drives to OneDrive for Business with PowerShell”

  1. Redirect the users My Documents to look at their corporate server home drive first and let that complete. Then add the OneDrive GPO to backup the local My Documents you will find it moves all the files from the corporate server to OneDrive and also sync a copy to the local PC, thus doing the data move for you.
    I already had the My Documents redirect in place and when I first setup a OneDrive Backup I almost had a heart attack as I saw files disappearing. They then re-appeared locally and in OneDrive and all was good with the world.

  2. Would this work on GPO redirected folders? My users have their MyDocuments and Desktop redirected to a network share “M:” Every time we try to automate a migration we screw up the folder redirection and have lost data that was local but not on the server. The newest wrinkle in the effort is that the server that hosted the redirected folders died and I’m hoping to skip anything that would require that to be in place.

  3. Hello There,
    Cannot see this GPO option anymore
    “Set the maximum percentage of upload bandwidth that OneDrive.exe uses”
    Seems the template has been changed lately?

  4. Hello, nice tutorial. But when applying this method, the homefolder path and drive in Active Directory (under tab Profile) is not emptied/deleted. Thus the homedrive and homedirectory will persist to exist ? Also, when data is copied from server to the cloud, it still remains on the server correct ?
    I suggest to

    1. delete the source files after copy by adding the switch /MOVE to the robocopy command “robocopy $env:HOMESHARE $env:OneDriveCommercial /E /SEC /MOVE”

    2. remove the homefolder path and drive “Get-ADUser -Identity IDENTITY | Set-ADUser -Homedirectory $null -HomeDrive $null”

    Regards,
    William.

    • Hi William,

      Thanks for sharing the two clean up parts. I indeed didn’t remove the files in the first run, always like to copy and check first. If no-one complains, then I can safely remove everything later on.

  5. Hi

    I get the below error;

    PS C:\Windows\System32> Unblock-File -Path \\my_domain\netlogon\OnedriveLib.dll
    PS C:\Windows\System32> Import-Module \\my_domain\netlogon\OnedriveLib.dll
    Import-Module : Could not load file or assembly ‘file://\\my_domain\netlogon\OnedriveLib.dll’ or one of its dependencies. Operation is not supported. (Exception from HRESULT: 0x80131515)
    At line:1 char:1
    + Import-Module \\my_domain\netlogon\OnedriveLib.dll
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [Import-Module], FileLoadException
    + FullyQualifiedErrorId : System.IO.FileLoadException,Microsoft.PowerShell.Commands.ImportModuleCommand

    • Is the file available on \\my_domain\netlogon\OnedriveLib.dll ? Try opening it with your explorer. You can also try to use a direct path. \\server-name\netlogon or copy the OnedriveLib.dll to c:\temp and import it from there.

      • Hi

        The file is accessible from the domain-joined computer. What I did find out is, when I installed Powershell7, the error disappeared and my script runs fine.

        I’m now stuck with getting the script to run on user login. I have even delayed the running by 5 minutes, to allow for all the login processes to finish. This is how i make sure PowerShell 7 is invoked to run the script – pwsh \\MYDOMAIN\NETLOGON\automateOneDrive.ps1

        If I run the above manually, the script completes successfully. Under %temp% I do not see any log to analyse.

        I hope the above is helpful.

      • Hi

        Resolved my issue.

        1. Created a GPO to install Powershell 7 – Could not get it running using Powershell 5.
        2. In order to deploy the script – Had to create a batch script that executes the powershell script. Found that the normal way of deploying scripts no longer apply in AD. But found a way around that.

        Thanks so much for your script.

        • hi

          Batch file contents
          ==============================================
          pwsh -WindowStyle hidden -ExecutionPolicy Bypass -File \\mydomain\NETLOGON\OneDriveAutomated.ps1
          ==============================================

          Modified Powershell Script

          ==============================================
          Import-Module \\mydomain\netlogon\OnedriveLib.dll

          #Find OneDrive folder
          $path = $env:OneDriveCommercial

          #Get OneDrive status
          $ODStatus = Get-ODStatus -ByPath $path

          $SourceRoot = “\\fileserver\mydocumentsfiles$\”+$env:username
          $UsersMigrated = “\\fileserver\mydocumentsfiles$\OneDriveMigration\”
          $DestinationRoot = $path

          if ($ODStatus -eq ‘UpToDate’ -or $ODStatus -eq ‘Syncing’ -or $ODStatus -eq ‘OnDemandOrUnknown’) {
          #OneDrive is connected
          write-host ‘OneDrive connected and found’
          robocopy $SourceRoot $DestinationRoot /E /SEC
          new-item $SourceRoot -name ‘_FILES COPIED TO ONEDRIVE.txt’ -ItemType ‘file’ -Value ‘Files Copied’
          new-item $UsersMigrated -name $env:username’.txt’ -ItemType ‘file’ -Value ‘Files Copied’
          }
          ==============================================

          I then thought, how can I force users who are not yet making use of OneDrive, to open it for them to register. Reason being, in our environment, when a user searches for one drive and click on it. It runs an update and disappears. You have to search on drive again and run it for the registration/login window to open. Thus, I created the following batch script to run OneDrive on user login.

          ==============================================
          powershell -WindowStyle hidden %localappdata%\Microsoft\OneDrive\OneDrive.exe
          ==============================================

          I hope the above will be helpful to someone else. Thanks once again for your DLL. It works great.

          Regards,
          Mervin

  6. This is awesome, but I’m getting stuck on deployment. With PDQ Deploy, it errors everytime with “PowerShell is running in Administrator mode. OneDrive status cannot be checked in elevated privileges”. Not sure how to get it to run any other way.

    I tried using invoke-command with a script block and got “Non-Interactive mode detected. OneDrive Status can only be checked interactively”.

    The script works perfectly on my own machine when i run it in my user context. Any tips on how to get PDQ Deploy to work with this?

    • You will have to change the package in PDQ Deploy. Open the package, select the options tab and set run as to Logged on User. This way the script will run in the logged on users context, allowing it to find the OneDrive installation.

  7. Hi

    Everything works but it does not dump the log file for the robocopy.

    power shell hangs on

    new-item $env:HOMESHARE -name ‘_FILES COPIED TO ONEDRIVE.txt’ -ItemType ‘file’ -Value ‘Files Copied
    >> }

    • Sorry, I see that there was a ‘ missing. The line should be:

      new-item $env:HOMESHARE -name '_FILES COPIED TO ONEDRIVE.txt' -ItemType 'file' -Value 'Files Copied'

  8. Hey Rudy,

    Thanks for the guide!
    I think there is a mistake in you if-part. It should be

    if (($ODStatus -eq ‘UpToDate’) -or ($ODStatus -eq ‘Syncing’)) {
    }

    and not

    if ($ODStatus = ‘UpToDate’ -or ‘Syncing’) {
    }

    Your way is always $true.

    • You are totally correct. I also program in other languages where we use = or == in if statements. So this is a mistake I tend to make a lot when switching back to PowerShell.

  9. just curious why this policy wouldn’t be applied at the USER GPO level – why the computer? When you have users that could login from various workstations, some may be on folder redirection still, others may not – anyone have advice or experience on this?

  10. Doesn’t seem to be working for me however when i try to just powershell: get-ODStatus

    i get :

    get-ODStatus : The term ‘get-ODStatus’ is not recognized as the name of a cmdlet, function, script file, or operable
    program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
    At line:1 char:1
    + get-ODStatus
    + ~~~~~~~~~~~~
    + CategoryInfo : ObjectNotFound: (get-ODSt

  11. Ah yes, I placed it locally for my tests and thought it would not make a difference.
    Changing it back to a share worked like a charm.

    Thank you so much. Very helpful guide!

  12. Hi

    Great write up but I can’t seem to get the module to work.
    It keeps getting me this error
    ‘Get-ODStatus’ is not recognized as the name of a cmdlet

    • Did you download the OneDriveLib.dll file and placed it in the same folder as your script or on a network share? You should import it with Import-Module \\\OnedriveLib.dll

Leave a Comment

0 Shares
Tweet
Pin
Share
Share