Skip to content

Instantly share code, notes, and snippets.

@aaronparker
Last active July 13, 2023 20:54
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save aaronparker/cf124f13bb58d95342707527900d307b to your computer and use it in GitHub Desktop.
Save aaronparker/cf124f13bb58d95342707527900d307b to your computer and use it in GitHub Desktop.
Redirects select folders into the OneDrive folder.
<#
.SYNOPSIS
Sets a known folder's path using SHSetKnownFolderPath.
.PARAMETER KnownFolder
The known folder whose path to set.
.PARAMETER Path
The target path to redirect the folder to.
.NOTES
Forked from: https://gist.github.com/semenko/49a28675e4aae5c8be49b83960877ac5
#>
Function Set-KnownFolderPath {
Param (
[Parameter(Mandatory = $true)]
[ValidateSet('AddNewPrograms', 'AdminTools', 'AppUpdates', 'CDBurning', 'ChangeRemovePrograms', 'CommonAdminTools', 'CommonOEMLinks', 'CommonPrograms', 'CommonStartMenu', 'CommonStartup', 'CommonTemplates', 'ComputerFolder', 'ConflictFolder', 'ConnectionsFolder', 'Contacts', 'ControlPanelFolder', 'Cookies', 'Desktop', 'Documents', 'Downloads', 'Favorites', 'Fonts', 'Games', 'GameTasks', 'History', 'InternetCache', 'InternetFolder', 'Links', 'LocalAppData', 'LocalAppDataLow', 'LocalizedResourcesDir', 'Music', 'NetHood', 'NetworkFolder', 'OriginalImages', 'PhotoAlbums', 'Pictures', 'Playlists', 'PrintersFolder', 'PrintHood', 'Profile', 'ProgramData', 'ProgramFiles', 'ProgramFilesX64', 'ProgramFilesX86', 'ProgramFilesCommon', 'ProgramFilesCommonX64', 'ProgramFilesCommonX86', 'Programs', 'Public', 'PublicDesktop', 'PublicDocuments', 'PublicDownloads', 'PublicGameTasks', 'PublicMusic', 'PublicPictures', 'PublicVideos', 'QuickLaunch', 'Recent', 'RecycleBinFolder', 'ResourceDir', 'RoamingAppData', 'SampleMusic', 'SamplePictures', 'SamplePlaylists', 'SampleVideos', 'SavedGames', 'SavedSearches', 'SEARCH_CSC', 'SEARCH_MAPI', 'SearchHome', 'SendTo', 'SidebarDefaultParts', 'SidebarParts', 'StartMenu', 'Startup', 'SyncManagerFolder', 'SyncResultsFolder', 'SyncSetupFolder', 'System', 'SystemX86', 'Templates', 'TreeProperties', 'UserProfiles', 'UsersFiles', 'Videos', 'Windows')]
[string]$KnownFolder,
[Parameter(Mandatory = $true)]
[string]$Path
)
# Define known folder GUIDs
$KnownFolders = @{
'AddNewPrograms' = 'de61d971-5ebc-4f02-a3a9-6c82895e5c04';
'AdminTools' = '724EF170-A42D-4FEF-9F26-B60E846FBA4F';
'AppUpdates' = 'a305ce99-f527-492b-8b1a-7e76fa98d6e4';
'CDBurning' = '9E52AB10-F80D-49DF-ACB8-4330F5687855';
'ChangeRemovePrograms' = 'df7266ac-9274-4867-8d55-3bd661de872d';
'CommonAdminTools' = 'D0384E7D-BAC3-4797-8F14-CBA229B392B5';
'CommonOEMLinks' = 'C1BAE2D0-10DF-4334-BEDD-7AA20B227A9D';
'CommonPrograms' = '0139D44E-6AFE-49F2-8690-3DAFCAE6FFB8';
'CommonStartMenu' = 'A4115719-D62E-491D-AA7C-E74B8BE3B067';
'CommonStartup' = '82A5EA35-D9CD-47C5-9629-E15D2F714E6E';
'CommonTemplates' = 'B94237E7-57AC-4347-9151-B08C6C32D1F7';
'ComputerFolder' = '0AC0837C-BBF8-452A-850D-79D08E667CA7';
'ConflictFolder' = '4bfefb45-347d-4006-a5be-ac0cb0567192';
'ConnectionsFolder' = '6F0CD92B-2E97-45D1-88FF-B0D186B8DEDD';
'Contacts' = '56784854-C6CB-462b-8169-88E350ACB882';
'ControlPanelFolder' = '82A74AEB-AEB4-465C-A014-D097EE346D63';
'Cookies' = '2B0F765D-C0E9-4171-908E-08A611B84FF6';
'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');
'Favorites' = '1777F761-68AD-4D8A-87BD-30B759FA33DD';
'Fonts' = 'FD228CB7-AE11-4AE3-864C-16F3910AB8FE';
'Games' = 'CAC52C1A-B53D-4edc-92D7-6B2E8AC19434';
'GameTasks' = '054FAE61-4DD8-4787-80B6-090220C4B700';
'History' = 'D9DC8A3B-B784-432E-A781-5A1130A75963';
'InternetCache' = '352481E8-33BE-4251-BA85-6007CAEDCF9D';
'InternetFolder' = '4D9F7874-4E0C-4904-967B-40B0D20C3E4B';
'Links' = 'bfb9d5e0-c6a9-404c-b2b2-ae6db6af4968';
'LocalAppData' = 'F1B32785-6FBA-4FCF-9D55-7B8E7F157091';
'LocalAppDataLow' = 'A520A1A4-1780-4FF6-BD18-167343C5AF16';
'LocalizedResourcesDir' = '2A00375E-224C-49DE-B8D1-440DF7EF3DDC';
'Music' = @('4BD8D571-6D19-48D3-BE97-422220080E43','a0c69a99-21c8-4671-8703-7934162fcf1d');
'NetHood' = 'C5ABBF53-E17F-4121-8900-86626FC2C973';
'NetworkFolder' = 'D20BEEC4-5CA8-4905-AE3B-BF251EA09B53';
'OriginalImages' = '2C36C0AA-5812-4b87-BFD0-4CD0DFB19B39';
'PhotoAlbums' = '69D2CF90-FC33-4FB7-9A0C-EBB0F0FCB43C';
'Pictures' = @('33E28130-4E1E-4676-835A-98395C3BC3BB','0ddd015d-b06c-45d5-8c4c-f59713854639');
'Playlists' = 'DE92C1C7-837F-4F69-A3BB-86E631204A23';
'PrintersFolder' = '76FC4E2D-D6AD-4519-A663-37BD56068185';
'PrintHood' = '9274BD8D-CFD1-41C3-B35E-B13F55A758F4';
'Profile' = '5E6C858F-0E22-4760-9AFE-EA3317B67173';
'ProgramData' = '62AB5D82-FDC1-4DC3-A9DD-070D1D495D97';
'ProgramFiles' = '905e63b6-c1bf-494e-b29c-65b732d3d21a';
'ProgramFilesX64' = '6D809377-6AF0-444b-8957-A3773F02200E';
'ProgramFilesX86' = '7C5A40EF-A0FB-4BFC-874A-C0F2E0B9FA8E';
'ProgramFilesCommon' = 'F7F1ED05-9F6D-47A2-AAAE-29D317C6F066';
'ProgramFilesCommonX64' = '6365D5A7-0F0D-45E5-87F6-0DA56B6A4F7D';
'ProgramFilesCommonX86' = 'DE974D24-D9C6-4D3E-BF91-F4455120B917';
'Programs' = 'A77F5D77-2E2B-44C3-A6A2-ABA601054A51';
'Public' = 'DFDF76A2-C82A-4D63-906A-5644AC457385';
'PublicDesktop' = 'C4AA340D-F20F-4863-AFEF-F87EF2E6BA25';
'PublicDocuments' = 'ED4824AF-DCE4-45A8-81E2-FC7965083634';
'PublicDownloads' = '3D644C9B-1FB8-4f30-9B45-F670235F79C0';
'PublicGameTasks' = 'DEBF2536-E1A8-4c59-B6A2-414586476AEA';
'PublicMusic' = '3214FAB5-9757-4298-BB61-92A9DEAA44FF';
'PublicPictures' = 'B6EBFB86-6907-413C-9AF7-4FC2ABF07CC5';
'PublicVideos' = '2400183A-6185-49FB-A2D8-4A392A602BA3';
'QuickLaunch' = '52a4f021-7b75-48a9-9f6b-4b87a210bc8f';
'Recent' = 'AE50C081-EBD2-438A-8655-8A092E34987A';
'RecycleBinFolder' = 'B7534046-3ECB-4C18-BE4E-64CD4CB7D6AC';
'ResourceDir' = '8AD10C31-2ADB-4296-A8F7-E4701232C972';
'RoamingAppData' = '3EB685DB-65F9-4CF6-A03A-E3EF65729F3D';
'SampleMusic' = 'B250C668-F57D-4EE1-A63C-290EE7D1AA1F';
'SamplePictures' = 'C4900540-2379-4C75-844B-64E6FAF8716B';
'SamplePlaylists' = '15CA69B3-30EE-49C1-ACE1-6B5EC372AFB5';
'SampleVideos' = '859EAD94-2E85-48AD-A71A-0969CB56A6CD';
'SavedGames' = '4C5C32FF-BB9D-43b0-B5B4-2D72E54EAAA4';
'SavedSearches' = '7d1d3a04-debb-4115-95cf-2f29da2920da';
'SEARCH_CSC' = 'ee32e446-31ca-4aba-814f-a5ebd2fd6d5e';
'SEARCH_MAPI' = '98ec0e18-2098-4d44-8644-66979315a281';
'SearchHome' = '190337d1-b8ca-4121-a639-6d472d16972a';
'SendTo' = '8983036C-27C0-404B-8F08-102D10DCFD74';
'SidebarDefaultParts' = '7B396E54-9EC5-4300-BE0A-2482EBAE1A26';
'SidebarParts' = 'A75D362E-50FC-4fb7-AC2C-A8BEAA314493';
'StartMenu' = '625B53C3-AB48-4EC1-BA1F-A1EF4146FC19';
'Startup' = 'B97D20BB-F46A-4C97-BA10-5E3608430854';
'SyncManagerFolder' = '43668BF8-C14E-49B2-97C9-747784D784B7';
'SyncResultsFolder' = '289a9a43-be44-4057-a41b-587a76d7e7f9';
'SyncSetupFolder' = '0F214138-B1D3-4a90-BBA9-27CBC0C5389A';
'System' = '1AC14E77-02E7-4E5D-B744-2EB1AE5198B7';
'SystemX86' = 'D65231B0-B2F1-4857-A4CE-A8E7C6EA7D27';
'Templates' = 'A63293E8-664E-48DB-A079-DF759E0509F7';
'TreeProperties' = '5b3749ad-b49f-49c1-83eb-15370fbd4882';
'UserProfiles' = '0762D272-C50A-4BB0-A382-697DCD729B80';
'UsersFiles' = 'f3ce0f7c-4901-4acc-8648-d5d44b04ef8f';
'Videos' = @('18989B1D-99B5-455B-841C-AB7C74E4DDFC','35286a68-3c57-41a1-bbb1-0eae73d76c95');
'Windows' = 'F38BF404-1D43-42F2-9305-67DE0B28FC23';
}
# Define SHSetKnownFolderPath if it hasn't been defined already
$Type = ([System.Management.Automation.PSTypeName]'KnownFolders').Type
If (-not $Type) {
$Signature = @'
[DllImport("shell32.dll")]
public extern static int SHSetKnownFolderPath(ref Guid folderId, uint flags, IntPtr token, [MarshalAs(UnmanagedType.LPWStr)] string path);
'@
$Type = Add-Type -MemberDefinition $Signature -Name 'KnownFolders' -Namespace 'SHSetKnownFolderPath' -PassThru
}
# Make path, if doesn't exist
If (!(Test-Path $Path -PathType Container)) {
New-Item -Path $Path -Type Directory -Force
}
# Validate the path
If (Test-Path $Path -PathType Container) {
# Call SHSetKnownFolderPath
# return $Type::SHSetKnownFolderPath([ref]$KnownFolders[$KnownFolder], 0, 0, $Path)
ForEach ($guid in $KnownFolders[$KnownFolder]) {
$result = $Type::SHSetKnownFolderPath([ref]$guid, 0, 0, $Path)
If ($result -ne 0) {
$errormsg = "Error redirecting $($KnownFolder). Return code $($result) = $((New-Object System.ComponentModel.Win32Exception($result)).message)"
Throw $errormsg
}
}
} Else {
Throw New-Object System.IO.DirectoryNotFoundException "Could not find part of the path $Path."
}
# Fix up permissions, if we're still here
Attrib +r $Path
Return $Path
}
<#
.SYNOPSIS
Gets a known folder's path using GetFolderPath.
.PARAMETER KnownFolder
The known folder whose path to get.
.NOTES
https://stackoverflow.com/questions/16658015/how-can-i-use-powershell-to-call-shgetknownfolderpath
#>
Function Get-KnownFolderPath {
Param (
[Parameter(Mandatory = $true)]
[ValidateSet('AdminTools','ApplicationData','CDBurning','CommonAdminTools','CommonApplicationData','CommonDesktopDirectory','CommonDocuments','CommonMusic','CommonOemLinks','CommonPictures','CommonProgramFiles','CommonProgramFilesX86','CommonPrograms','CommonStartMenu','CommonStartup','CommonTemplates','CommonVideos','Cookies','Desktop','DesktopDirectory','Favorites','Fonts','History','InternetCache','LocalApplicationData','LocalizedResources','MyComputer','MyDocuments','MyMusic','MyPictures','MyVideos','NetworkShortcuts','Personal','PrinterShortcuts','ProgramFiles','ProgramFilesX86','Programs','Recent','Resources','SendTo','StartMenu','Startup','System','SystemX86','Templates','UserProfile','Windows')]
[string]$KnownFolder
)
Return [Environment]::GetFolderPath($KnownFolder)
}
<#
.SYNOPSIS
Moves contents of a folder with output to a log.
Uses Robocopy to ensure data integrity and all moves are logged for auditing.
Means we don't need to re-write functionality in PowerShell.
.PARAMETER Source
The source folder.
.PARAMETER Destination
The destination log.
.PARAMETER Log
The log file to store progress/output
#>
Function Move-Files {
Param (
[Parameter(Mandatory = $true)]
[string]$Source,
[Parameter(Mandatory = $true)]
[string]$Destination,
[Parameter(Mandatory = $true)]
[string]$Log
)
If (!(Test-Path (Split-Path $Log))) { New-Item -Path (Split-Path $Log) -ItemType Container }
Robocopy.exe $Source $Destination /E /MOV /XJ /R:1 /W:1 /NP /LOG+:$Log
}
<#
.SYNOPSIS
Function exists to reduce code required to redirect each folder.
#>
Function Redirect-Folder {
Param (
$SyncFolder,
$GetFolder,
$SetFolder,
$Target
)
# Get current Known folder path
$Folder = Get-KnownFolderPath -KnownFolder $GetFolder
# If paths don't match, redirect the folder
If ($Folder -ne "$SyncFolder\$Target") {
# Redirect the folder
Set-KnownFolderPath -KnownFolder $SetFolder -Path "$SyncFolder\$Target"
# Move files/folders into the redirected folder
Move-Files -Source $Folder -Destination "$SyncFolder\$Target" -Log "$env:LocalAppData\RedirectLogs\Robocopy$Target.log"
# Hide the source folder (rather than delete it)
Attrib +h $Folder
}
}
# Get OneDrive sync folder
$OneDriveFolder = Get-ItemPropertyValue -Path 'HKCU:\Software\Microsoft\OneDrive\Accounts\Business1' -Name 'UserFolder'
# Redirect select folders
If (Test-Path $OneDriveFolder) {
Redirect-Folder -SyncFolder $OneDriveFolder -GetFolder 'Desktop' -SetFolder 'Desktop' -Target 'Desktop'
Redirect-Folder -SyncFolder $OneDriveFolder -GetFolder 'MyDocuments' -SetFolder 'Documents' -Target 'Documents'
Redirect-Folder -SyncFolder $OneDriveFolder -GetFolder 'MyPictures' -SetFolder 'Pictures' -Target 'Pictures'
}
@nelaida
Copy link

nelaida commented Aug 15, 2019

Hi this really good. We are looking for script to migrate our users "Documents" from MS sync -folder redirect (CSC cache) to MS onedrive for business. The issue we have is that we need some sort of script to break csc and set the %USERPROFILE%\Documents to default.

@danielpetrovic
Copy link

danielpetrovic commented Nov 13, 2019

Thanks, this script helps a lot. I have two questions:

Why hide the original folder and not remove it? I like my folders clean + it prevents any apps somehow finding the old folder and saving data there.

Also could you possibly add the other user folders in the profile? 3D Objects, Contacts, Downloads, Favorites, Links, Saved Games, Searches and Videos. Or is that not advicable to do? I tried adding them to your script, it did move them but it ran errors during the process.

@aaronparker
Copy link
Author

Source folder is hidden to avoid data loss in the event that files from the source didn't copy. You can update the script to delete though.

I'd have to test those other folders to see the error.

@wumbowarrior
Copy link

I am trying to modify this so that it will run under a SYSTEM context. Right now it requires a user context which can't be run silently using task scheduler. However, when I do I am getting an error on line 132 like so:

Error redirecting {374DE290-123F-4565-9164-39C4925E467B}. Return code
-2147024894 = The system cannot find the file specified

The strange thing is that I can confirm that the GUID is located in that path, so I don't know why it's not finding the file? Can someone break down what is happening in this line because I don't understand the syntax...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment