CV-PstCopy Powershell Script

5 minute read

In a previous post, PST Migration from Enterprise Vault with TransVault, I wrote about a past project in which the customer was migrating from Enterprise Vault to Commvault. They were managing the export process via a software called TransVault. The output was all in one folder with a specific naming structure depending on export status but there was commonality. The format, if export was successful was as such Recipients.UserName@domain.com_Number.pst. The format of the PST file must be like this for the script to work.

Commvault needs to know which user/mailbox is associated to each PST. One of the ways to do this is based on folder structure. In the format of \userName\pstName.pst. The name of the parent folder which matches the user’s username is used to associate the PST to the correct mailbox.

To automate this I created a vbscript back then as the number of PST’s and users was in the thousands. Well, it’s 2019 and vbscript is not really applicable anymore. I rewrote the script in PowerShell and added some features.

It will now create a checksum and compare original and target to ensure still the same. It logs output. It is recursive. The PST files can be moved rather than copied with the -move option. If copied, when PST files are successfully copied the source file is changed to indicate as such and renamed to remove the PST extension which helps in subsequent incremental runs of the script.

Script Instructions (CV-PstCopy.ps1)

  • Run As Administrator!
  • Scripts purpose is to move PST’s from source folder to target folder structure necessary for Commvault PST archive user association”
  • If PST file owner set correctly then script is not necessary
  • Currently specific to only copying PSTs from TransVault migration output from Enterprise Vault.
  • Creates log file if doesn’t exist in same directory as script
  • Creates hashsum for file prior and after copying and compares to determine if mismatch
  • Renames source files after copy to indicate they were copied. Not applicable with -move option
  • Source and target directory must exist
  • -source = Source directory where PST’s are located.
  • -target = Target main directory where PST’s are to be moved to
  • -move = is an optional parameter that moves rather than copies PST’s so faster and saves space.

Example Usage:

To copy files -

.\CV-PstCopy.ps1 -source C:\scripts\test\srcdir -target C:\scripts\test\trgtdir

To move files -

.\CV-PstCopy.ps1 -source C:\scripts\test\srcdir -target C:\scripts\test\trgtdir -move

The script is also available via the following Github Repository

Script Contents (CV-PstCopy.ps1)


#Start Script ----------------

#user input values
param  ( [String] $source, [String] $target, [switch] $move)

#for creating uniqueness
$timestamp = Get-Date -Format o

#Set log file. Can be changed!
$Logfile = ".\CV-PstCopy.log"

#Simple log writer
Function LogWrite {

   Param ([string]$logstring)

   Add-content $Logfile -value $logstring

} #End Function LogWrite

#Add header to log
Function AddHeader {

    for ($zz=1; $zz -le 100; $zz++) {
    Add-Content -NoNewline $Logfile -value "-"
    }

    Add-Content $Logfile -value "-"
    Add-Content  $Logfile -value $timestamp

    for ($zz=1; $zz -le 100; $zz++) {
    Add-Content -NoNewline $Logfile -value "-"
    }

    Add-Content $Logfile -value "-"

} #End Function AddHeader

AddHeader

#Check for -source and -target input values as both required
Function CheckInputs {

    If ($source -ne "") {
        $global:path0 = $source
        }

    else {
        Write-Output "Oops! You forgot to specify your source directory!"
        LogWrite "Oops! You forgot to specify your source directory!"
        Exit
        }

    If ($source -ne "") {
        $global:path1 = $target
        }

    else {
        Write-Output "Oops! You forgot to specify your target directory!"
        LogWrite "Oops! You forgot to specify your target directory!"
        Exit
        }

} #End Function CheckInputs

CheckInputs

#Verify paths exist
Function GetMyPaths {


    If (Test-Path $Path0) {
        #The source directory exists. Move along.
        }

    Else {
        #md $Path0
        Write-Output "Oops! This source directory doesn't exist! Please, specify a valid directory."
        LogWrite "Oops! This source directory doesn't exist! Please, specify a valid directory."
        Exit
        }

    If (Test-Path $Path1) {
        #The target directory exists. Move along.
        }

    Else {
        #md $Path1
        Write-Output "Oops! This target directory doesn't exist! Please, specify a valid directory."
        LogWrite "Oops! This target directory doesn't exist! Please, specify a valid directory."
        Exit
        }

} #End Function GetMyPaths

#Copy or Move files into folder structure necessary for Commvault archive
Function CopyFiles {

    #Get All PST Files
    $myarray = Get-ChildItem -Path $path0 -Recurse | where {$_.extension -eq ".pst"}
    $mycount = $myarray.Length
    Write-Output "We found $mycount PST files to copy"
    LogWrite "We found $mycount PST files to copy"

    for ($m=0; $m -lt $myarray.length; $m++) {

        $y = $myarray[$m] | Select-Object Name
        $PstFile = $myarray[$m] | Select-Object FullName


        #Only select files that match regular expression
        If ($y -match "Recipients(\.\w{1,})") {

            #Generate hash on original PST file for comparison
            $myname = $PstFile.FullName
            $orighash = Get-FileHash $myName

            #Create User Folders necessary to import PST files into Commvault
            $x = $Matches.0
            $z = $x -replace "Recipients.", ""
            $UserFolder = $path1+"\"+$z

            #Make target PST file names. I'm sure this could be done better!!!
            $k = $path1+"\"+$z+"\"+$y.Name
            $g = $k -replace "@{Name=", ""

            #Randomize Name to ensure all files get copied
            $CopiedPstFile = $g+"_Commvault_"+"$(get-date -format yyyy-MM-ddTHH-mm-ss-ff)"+".pst"

            #Check if user folders exists
            if (Test-Path $UserFolder) {

                #Copy or Move PST files
                #If user provides -move switch then move files
                if ($move) {
                    Move-Item $PstFile.FullName -Destination $CopiedPstFile
                    }

                #if no -move switch then copy files and rename source files to indicate they were copied
                else {
                    Copy-Item $PstFile.FullName -Destination $CopiedPstFile -Recurse
                    $ee = $myname+"_CopiedbyCommvault"
                    Rename-Item $PstFile.FullName -NewName $ee
                    }

                #Generate hash on copied PST file for comparison
                $newhash = Get-FileHash $CopiedPstFile

                If ($newhash.hash -ne $orighash.hash) {
                    Logwrite "Something went wrong with copy of $PstFile"
                    }

                Else {
                    LogWrite "$orighash copied to $newhash"
                    }
            }

            else {
                #make user folders
                md $UserFolder | Out-Null

                #Copy or Move PST files
                #If user provides -move switch then move files
                if ($move) {
                    Move-Item $PstFile.FullName -Destination $CopiedPstFile
                    }

                #if no -move switch then copy files and rename source files to indicate they were copied
                else {
                    Copy-Item $PstFile.FullName -Destination $CopiedPstFile -Recurse
                    $ee = $myname+"_CopiedbyCommvault"
                    Rename-Item $PstFile.FullName -NewName $ee
                    }

                #Generate hash on original PST file for comparison
                $newhash = Get-FileHash $CopiedPstFile

                If ($newhash.hash -ne $orighash.hash) {
                    Logwrite "Something went wrong with copy of $PstFile"
                    Write-Output "Something went wrong with copy of $PstFile"
                    }

                Else {
                    LogWrite "$orighash copied to $newhash"
                    }

            }
        }

    }

}#End Function CopyFiles

GetMyPaths
CopyFiles

#End Script ----------------