The PowerShell script below was design to move Documents, Music, Videos, Pictures, Favorites and Desktop to a sub-folder in a connected OneDrive. In theory the script does not depend on OneDrive and could be adjusted to any other destination.
While it normally is wise to engage GPOs to adjust those paths to internal server resources, this is not possible easily while using OneDrive. The script therefor works better here.
What it does
- is the current path per folder accessible
- does the target path exist
- YES: adjust the registry respective folder targets to the target path – FINISHED
- NO: create the target folders – see 3.
- is the source path on the same volume / partition – like C:
- YES: see below – 4.
- NO: check if there is enough free space for the amount of data needed to be moved
- YES: see below – 4.
- ALMOST: YELLOW warning – see below 4.
- NO: RED error – you could still proceed or simply close the script
- move the data to the new target folder
- remove the old folder – if not possible rename it
The script retains the special icons for the folders and engages the Windows API to adjust the folder paths.
What you need to do
- Adjust the target-path in the top of the script
- If desired, adjust the minimum free space value (2 GB by default) for the warning in regards to the free space – this only matters if the source and target volume / partition aren’t the same
To start the script, either right click and say run with PowerShell or run it directly in a PowerShell. This script will need to execute in the user-context and does NOT need administrative rights.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 | Clear $NewRootPath = "$env:USERPROFILE\OneDrive - MYDOMAIN\User Profile" $regUSF = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders" $regSF = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders" $Folders = "Personal,My Pictures,My Music,My Video,Favorites,Desktop" $Folders = $Folders.Split(",") $FolderNames = "Documents,Pictures,Music,Videos,Favorites,Desktop" $FolderNames = $FolderNames.Split(",") $AddSize = 2000000000 #free space in Bytes to add if files are on a different volume (2000000000 = about 2 GB) $ArrayPosition = -1 Function Move-KnownFolderPath { Param ( [Parameter(Mandatory = $true)] [ValidateSet('Desktop', 'Documents', 'Downloads', 'Favorites', 'Music', 'Pictures', 'Videos')] [string]$KnownFolder, [Parameter(Mandatory = $true)] [string]$Path ) # Known Folder GUIDs $KnownFolders = @{ 'Desktop' = @('B4BFCC3A-DB2C-424C-B029-7FE99A87C641'); 'Documents' = @('FDD39AD0-238F-46AF-ADB4-6C85480369C7','f42ee2d3-909f-4907-8871-4c22fc0bf756'); 'Downloads' = @('374DE290-123F-4565-9164-39C4925E467B','7d83ee9b-2244-4e70-b1f5-5393042af1e4'); #not in use - just in case you want to use it.. 'Favorites' = '1777F761-68AD-4D8A-87BD-30B759FA33DD'; 'Music' = @('4BD8D571-6D19-48D3-BE97-422220080E43','a0c69a99-21c8-4671-8703-7934162fcf1d'); 'Pictures' = @('33E28130-4E1E-4676-835A-98395C3BC3BB','0ddd015d-b06c-45d5-8c4c-f59713854639'); 'Videos' = @('18989B1D-99B5-455B-841C-AB7C74E4DDFC','35286a68-3c57-41a1-bbb1-0eae73d76c95'); } $FolderGUID = ([System.Management.Automation.PSTypeName]'KnownFolders').Type #create the Type entry if it does not yet exist / relates to the Registry eventually If (-not $FolderGUID) { $KnownFolderGUID = @' [DllImport("shell32.dll")] public extern static int SHSetKnownFolderPath(ref Guid folderId, uint flags, IntPtr token, [MarshalAs(UnmanagedType.LPWStr)] string path); '@ $FolderGUID = Add-Type -MemberDefinition $KnownFolderGUID -Name 'KnownFolders' -Namespace 'SHSetKnownFolderPath' -PassThru } If(!(Test-Path $Path)){ #in case folder does yet exist, we create it Try { New-Item -Path $Path -Type Directory -Force -ErrorAction SilentlyContinue } Catch {} } #set the new folder path ForEach ($TmpGUID in $KnownFolders[$KnownFolder]) { $tmp = $FolderGUID::SHSetKnownFolderPath([ref]$TmpGUID, 0, 0, $Path) } Attrib +r $Path #needed to retain the icon } ForEach ($Folder In $Folders) { $ArrayPosition += 1 Write-Host "Working on folder"$FolderNames[$ArrayPosition] -BackgroundColor DarkGreen $SourcePath = (Get-ItemProperty -Path $regUSF -Name $Folder).$Folder If ($SourcePath.Length -gt 0) { Write-Host "..checking source path: $SourcePath" If ((Test-Path -Path $SourcePath)){ Write-Host "....path is accessible" $CompSource = Get-Item -Path $SourcePath $CompareResult = $false If ((Test-Path -Path ($NewRootPath + "\" + $CompSource.Name))){ $CompTarget = Get-Item -Path ($NewRootPath + "\" + $CompSource.Name) Write-Host "..Comparing Source Path: "$CompSource.FullName Write-Host "....with Target Path: "$CompTarget.FullName If ($CompSource.FullName.ToLower() -eq $CompTarget.FullName.ToLower()){ $CompareResult = $true } Else { $CompareResult = $false } } Else { Write-Host "..Target Path does not exist" } If ($CompareResult -eq $true){ Write-Host "..Source and Target path are identical: $CompTarget" -ForegroundColor Yellow Write-Host "....Not applying any changes!" -ForegroundColor Yellow Pause } Else { $SourceFolder = Get-Item -Path $SourcePath Write-Host "..accessing source folder $SourceFolder" -ForegroundColor Green If (!(Test-Path -Path ($NewRootPath + '\' + $SourceFolder.Name))){ Move-KnownFolderPath -KnownFolder ("" + $FolderNames[$ArrayPosition] + "") -Path ($NewRootPath + '\' + $FolderNames[$ArrayPosition]) $TargetPath = Get-Item -Path ("$NewRootPath\" + $FolderNames[$ArrayPosition]) Write-Host "..created new folder $TargetPath" #compare source size with target free space here Write-Host "..Comparing Source and Target drive letters" If ($NewRootPath.SubString(0,2).ToLower() -ne $SourcePath.SubString(0,2).ToLower()) { #source drives are different.. we need to obey the SourceSize against the free space on the target Write-Host "....Source and Target drive letters are different" Write-Host "......Please wait while calculating the source size" $SourceSize = (Get-ChildItem -Path $SourcePath -Recurse | Measure-Object -Property Length -Sum -ErrorAction SilentlyContinue).Sum $TargetFree = Get-PSDrive $NewRootPath.SubString(0,1) | Select-Object Free If ($SourceSize -gt $TargetFree.Free) { Write-Host "......The Source size of $SourceSize Bytes is bigger then the free space on the Target of $TargetFree.Free Bytes" -ForegroundColor Red } ElseIf (($SourceSize + $AddSize) -gt $TargetFree.Free) { Write-Host "......The Source size of $SourceSize Bytes is close to the free space on the Target of $TargetFree.Free Bytes" -ForegroundColor Yellow } Else { Write-Host "......The Source size of $TargetFree.Free Bytes is sufficient to hold the data from the Source of $SourceSize Bytes" -ForegroundColor Green } Pause } Else { Write-Host "....Source and Targets are on the same drive, all good" } Write-Host "..Moving data from: $SourcePath" Write-Host "....to folder: $TargetPath" Write-Host "....This can take several minutes..." Move-Item -Path ("" + $SourcePath + "\*") -Destination $TargetPath -Force -ErrorAction SilentlyContinue Write-Host "....Done moving data" } Else { Write-Host "..Warning - Target Path exists already: $CompTarget" -ForegroundColor Yellow Write-Host "....Files will not be moved, registry still will be adjusted to the target path!" -ForegroundColor Yellow Pause Move-KnownFolderPath -KnownFolder ("" + $FolderNames[$ArrayPosition] + "") -Path ($NewRootPath + '\' + $FolderNames[$ArrayPosition]) Write-Host "....Finished adjusting registry" } Write-Host "..Trying to remove $SourcePath" Pause Try { Attrib -r -s $SourceFolder.FullName Remove-Item -Path $SourceFolder.FullName -Force -Confirm:$false -Recurse -ErrorAction SilentlyContinue } Catch {} Try { Attrib -r -s ($SourceFolder + "\desktop.ini") Remove-Item -Path ($SourceFolder + "\desktop.ini") -Force -Confirm:$false -ErrorAction SilentlyContinue } Catch {} Try { Rename-Item -Path $SourceFolder -NewName ($SourceFolder.Name + ".old") -Force -ErrorAction SilentlyContinue } Catch {} } } Else { Write-Host "....SourceFolder Path invalid: $SourcePath" -ForegroundColor Red } } Write-Host "..Done processing"$FolderNames[$ArrayPosition] -BackgroundColor Blue Pause } Write-Host "Script finished processing" -ForegroundColor Green Pause |
Please be advised – the script will by default not try to move e.g. DOWNLOADS.
You can adjust this, while adding the folder to the two parameter, see sample below.
1 2 3 4 | $Folders = "Personal,My Pictures,My Music,My Video,Favorites,Desktop<strong>,Downloads</strong>" $Folders = $Folders.Split(",") $FolderNames = "Documents,Pictures,Music,Videos,Favorites,Desktop<strong>,Downloads</strong>" $FolderNames = $FolderNames.Split(",") |
If you want more folder, the script would need some special adjustments. It can be used as a base script, if you want.