question

leeroberts-8039 avatar image
0 Votes"
leeroberts-8039 asked IanXue-MSFT commented

move multiple user folders

Hi,

I have to copy the contents of multiple user folders to new folders on a different drive. The issue is the folder names will be different. E.G Adam.smith will now be SmithA.

I have a CVS file with the old folder names (Adam.Smith) and also the new folder names (SmithA).

Example would be c:\temp\Adam.smith that will need the contents copied to g:\users\SmithA.

Is this possible using powershell?

windows-server-powershell
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

IanXue-MSFT avatar image
0 Votes"
IanXue-MSFT answered IanXue-MSFT commented

Hi,

Have you set the $sourcePath and the $targetPath? Please see if this works.

 $sourcepath = 'c:\temp'
 $targetpath = 'g:\users'
 $mapFile = 'C:\temp\UserNames.csv'
 Import-Csv $mapFile -Header 'OriginalName', 'NewName' | ForEach-Object {
     Copy-Item -Path (Join-Path $sourcePath $_.OriginalName) -Destination (Join-Path $targetPath $_.NewName) -Recurse }

In your csv file it should be like this

 "Adam.smith","SmithA"

Best Regards,
Ian Xue
============================================
If the Answer is helpful, please click "Accept Answer" and upvote it.
Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

· 2
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Hi,

That now is coping thank you.

I was just wondering if there is a way to have it copy the contents of the Original folder into the new folder instead of just coping the root folder?

0 Votes 0 ·

Yes, there is.

 $sourcepath = 'c:\temp'
 $targetpath = 'g:\users'
 $mapFile = 'C:\temp\UserNames.csv'
 Import-Csv $mapFile -Header 'OriginalName', 'NewName' | ForEach-Object {
    $src = Join-Path $sourcePath $_.OriginalName
    $dest = Join-Path $targetPath $_.NewName
    if(-not (Test-Path $dest)){ New-Item -Path $dest -ItemType Directory }
    Get-ChildItem -Path $src | Copy-Item -Destination $dest -Recurse }


0 Votes 0 ·
cooldadtx avatar image
0 Votes"
cooldadtx answered

Yes. Read the CSV file, I assume that field 1 is the old name and field 2 is the new name, and then enumerate the lines using Move-Item to move source to destination. Alternatively first test with -WhatIf or Copy-Item.

Test 1, Test1
Test 2, Test2
Test 3, Test3


Note that I'm assuming you're running a new enough version of PS (at least 5.1+) such that Import-Csv is available to easily parse the CSV. If for some reason you cannot do that then use Get-Content to load the file contents and then manually split the line instead.

# Import the CSV file, assuming no header in the file so create one manually
$mappings = Import-Csv $mapFile -Header 'OriginalName', 'NewName'

# Copy each source folder into the destination folder by combining the names with the source and target paths
$mappings | foreach-object { Copy-Item -Path ([System.IO.Path]::Combine($sourcePath, $_.OriginalName)) -Destination ([System.IO.Path]::Combine($targetPath, $_.NewName)) -Recurse }


Here I'm using the Copy-Item for several reasons.
1. Move-Item is documented as not working if you are moving across drives. Given your post it sounds like this is true so Move-Item won't work.
2. Move-Item does not allow rename while moving. You would therefore need to first move the items then use Rename-Item to rename each folder.
3. For testing/recovery purposes if something goes wrong you can blow away the target structure and run your command again. Only after you've verified everything copied correctly (perhaps by counting files) should you then delete the source. Move-Item would not allow this. If something went wrong you'd have to move all the directories back and try again, potentially.


5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

leeroberts-8039 avatar image
0 Votes"
leeroberts-8039 answered cooldadtx commented

HI Coodadtx,

Thank you for getting back to me.

When i run the above script i'm getting the below error

Import-Csv : Cannot validate argument on parameter 'Delimiter'. The argument is null. Provide a valid value for the argument, and then try running the command again.
At line:1 char:54
+ $mappings = Import-Csv -Path "C:\temp\UserNames.csv" $mapFile -Header ...
+ ~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Import-Csv], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.ImportCsvCommand


Copy-Item : An item with the specified name \\FILE-03\Home\Lee already exists.
At line:6 char:30
+ ... ch-object { Copy-Item -Path ([System.IO.Path]::Combine($sourcePath, $ ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ResourceExists: (\\FILE-03\Home\Lee:String) [Copy-Item], IOException
+ FullyQualifiedErrorId : DirectoryExist,Microsoft.PowerShell.Commands.CopyItemCommand


Copy-Item : An item with the specified name \\FILE-03\Home\Lee\User.1 already exists.
At line:6 char:30
+ ... ch-object { Copy-Item -Path ([System.IO.Path]::Combine($sourcePath, $ ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ResourceExists: (\\FILE-03\Home\Lee\User.1:String) [Copy-Item], IOException
+ FullyQualifiedErrorId : DirectoryExist,Microsoft.PowerShell.Commands.CopyItemCommand

· 3
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

You specified duplicate path parameters

 $mapFile = "C:\temp\UserNames.csv" 
 $mappings = Import-Csv -Path $mapFile -Header 'OriginalName', 'NewName'



0 Votes 0 ·

Hi,
I have changed the script to the below:
$mapFile = "C:\temp\UserNames.csv"
$mappings = Import-Csv -Path $mapFile -Header 'Archive', 'SamAccountName'

 $mappings | foreach-object { Copy-Item -Path ([System.IO.Path]::Combine($sourcePath, $_.Archive)) -Destination ([System.IO.Path]::Combine($targetPath, $_.SamAccountName)) -Recurse -WhatIf }

But I still get the below error:
Copy-Item : Cannot bind argument to parameter 'Path' because it is an empty string.
At C:\Users\adm_robertsl\Desktop\Scripts\Copy Data.ps1:4 char:46
+ ... -Item -Path ([System.IO.Path]::Combine($sourcePath, $_.Archive)) -Des ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Copy-Item], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorEmptyStringNotAllowed,Microsoft.PowerShell.Commands.CopyItemCommand

0 Votes 0 ·

Does it work for some folders? If so then you have a row that has no Archive field set. Perhaps it is an empty row in the CSV?

For debugging purposes consider breaking up the foreach-object logic. Alternatively step through the code in a debugger like Visual Studio Code.

$mappings | foreach-object {
   $itemSourcePath = [System.IO.Path]::Combine($sourcePath, $_.Archive)
   $itemTargetPath = [System.IO.Path]::Combine($targetPath, $_.SamAccountName)
   Write-Host "Source = $itemSourcePath"
   Write-Host "Destination = $itemTargetPath"
   Copy-Item -Path $itemSourcePath -Destination $itemTargetPath -Recurse -WhatIf
}


If you see blank source/destinations then your CSV is not complete and you'll need to account for the differences.

0 Votes 0 ·