#PowerShell, #PowerWiseScripting, #ProjectWise, PWPS_DAB

HowTo:Mirror Project Folders between Datasources

Thanks to Buddy for this post.  Buddy developed a script which would mirror folders and documents from one project to another.  In this post, I am going to take that idea and mirror the folders of a Work Area in one datasource to a corresponding Work Area in a second datasource. This is the precursor to mirroring the documents, which will be in a subsequent post.  We will take advantage of the ProjectWise session capabilities. This will allow us to be logged into both datasources simultaneously, and switch back and forth.

We will be using the following cmdlets to accomplish this task. All of the ProjectWise related cmdlets are available using the PWPS_DAB module. At the time of this post, I am using version Take a look at the help for each of the cmdlets to become familiar with their functionality, available parameters, etc.

  • New-PWLogin
  • Get-SecureStringFromEncryptedFile
  • Get-PWSessions
  • Get-PWCurrentDatasource
  • Set-PWSession
  • Show-PWFolderBrowserDialog
  • Get-PWFolders
  • New-PWFolder
  • Remove-PWFolder
  • Undo-PWLogin


The Get-PWSessions cmdlet returns all active ProjectWise datasources within the active PowerShell session. If  you are not logged into a datasource, you will receive the following error message.


Log In to ProjectWise Datasources

Source Datasource

I will be logging into the source datasource using the New-PWLogin cmdlet and providing the datasource name, user name and entering the password when prompted.

$SourceDatasource = 'BMF-WS2016-PWDI:ProjectWise'

$Splat_LoginSource = @{
    DatasourceName = $SourceDatasource
    UserName = 'pwadmin'
    Password = (Read-Host -Prompt "Enter password:" -AsSecureString)
New-PWLogin @Splat_LoginSource


I will be logging into the target datasource in the same manner, except I will read the password from an encrypted file.

$TargetDatasource = 'BMF-WS2016-PWDI:PowerShell'

$Password = Get-SecureStringFromEncryptedFile -FileName 'd:\PowerShell\mysecretpassword.txt'
$Splat_LoginTarget = @{
    DatasourceName = $TargetDatasource 
    UserName = 'pwadmin' 
    Password = $Password
New-PWLogin @Splat_LoginTarget

Set Session

Now, we can use the Get-PWSessions to see that we are logged into both datasources.


We can use the Get-PWCurrentDatasource to determine which one is active. Then we will use the Set-PWSession to set the one we want active.  By default the last login will be active. So, in this instance we are logged into the Target datasource. We want to be in the Source datasource.


Get Work Area Folders

Here we will use the Show-PWFolderBrowserDialog cmdlet to select the Work Area folder within the Source datasource.  This is the folder structure we will be mirroring in the target datasource. We will be filtering the results to only return the FullPath of the selected Work Area folder.

Source Datasource

Select the Work Area folder and then the subfolders for the Source datasource.

$SourceFolderRoot = Show-PWFolderBrowserDialog | 
Select-Object -ExpandProperty FullPath



We will obtain the folder objects for the subfolders of the Source as we will need additional metadata for any folders created within the target. We will also use the -PopulatePaths parameter so that the FullPaths of the folders will be returned.

# Get all subfolders for source.
$SourceFolders = Get-PWFolders -FolderPath $SourceFolderRoot -PopulatePaths


Target Datasource

Now, we will switch to the Target datasource and select the corresponding folder.

# Set the Target datasource active.
Set-PWSession $TargetDatasource

$DestinationFolderRoot = Show-PWFolderBrowserDialog | 
Select-Object -ExpandProperty FullPath


Next, we will get all of the subfolders for the Target Work Area. Again, we will include the -PopulatePaths switch parameter. Here we will only need the full path, so we with filter the results.

# Get all subfolders for Target.
<# Explicitly set the type to a string array. Otherwise, you may 
encounter errors if there is only one folder fullpath returned. #>
[string[]] $DestinationFolders = Get-PWFolders -FolderPath $DestinationFolderRoot -PopulatePaths | 
Select-Object -ExpandProperty FullPath


Mirror Folders

The first step in the mirroring process is verify and create any missing subfolders in the Target to match what is in the Source Work Area. To accomplish this we will loop through each of the Source subfolders, determine if there is a corresponding folder in the Target, if one does not exist, we will create it.

Note: For the sake of brevity, we are going to assume the Target datasource contains the same Environment(s) as the Source datasource. 

# Iterate through all subfolders in SourceFolders. 
# Replicate folders that do not exist.
foreach ($SourceFolder in $SourceFolders) {
    Write-Host "Source Folder $($SourceFolder.FullPath)" -ForegroundColor Cyan

    # Remove the Source Work Area folder name from the fullpath.
    $SourceFolderDiffPath = $($SourceFolder.FullPath).Substring($SourceFolderRoot.Length)
    # Add the Target Work Area folder name.
    $tempDestination = "$($DestinationFolderRoot)$SourceFolderDiffPath"
    <# We can search the DestinationFolders for the new path in the tempDestination variable.
        If it is found, nothing to do. If it is not, create the folder. #>
    if ($DestinationFolders.Contains($tempDestination)) {
        Write-Host "Destination folder '$tempDestination' exists." -ForegroundColor DarkGreen
    } else { 
        Write-Host "Creating Folder '$tempDestination'." -ForegroundColor Red
        $newPWFolder = New-PWFolder $tempDestination -Environment $SourceFolder.Environment
        # Add the new folder path to the Destination folders for future comparisons.
        $DestinationFolders += $tempDestination
} # end foreach ($SourceFolder in $SourceFolders...

The following shows some of the output to the console.


The following shows an updated list of Destination folders. All of the Source folders have been created.


Now we need to clean up any folders that are not needed within the Target Work Area.

# Cleanup any folders no longer needed in the Target.
foreach ($DestinationFolder in $DestinationFolders) {
    Write-Host "Destination Folder $DestinationFolder" -ForegroundColor Cyan

    # This time, we will remove the Target Work Area folder name from the fullpath.
    $DestinationFolderDiffPath = $DestinationFolder.Substring($DestinationFolderRoot.Length)
    # Add the Source Work Area folder name.
    $tempSource = "$($SourceFolderRoot)$DestinationFolderDiffPath"

    <# We can search the SourceFolders for each of the folders in the DestinationFolders.
       If it is found, nothing to do. If it is not, remove the folder from the Target. #>
    if ($($SourceFolders.FullPath).Contains($tempSource)) {
        Write-Host "Source folder '$tempSource' exists." -ForegroundColor DarkGreen
    } else {
        Write-Host "Mirror - removing Folder '$DestinationFolder'." -ForegroundColor Red
        Remove-PWFolder -FolderPath $DestinationFolder -RemoveDocuments -RemoveFolders -ProceedWithDelete
} # end foreach ($DestinationFolder in $DestinationFolders...

The following shows the end result. Notice only the folders contained in the Source are now listed. All unnecessary folders have been removed.


Log Out of Your Datasources

Be sure to log out of each of the ProjectWise datasources.

You can use the Get-PWCurrentDatasource cmdlet to determine which datasource is active. Then log out. Switch sessions and then log out of the other datasource.


Set-PWSession -Datasource $SourceDatasource



In this post, we mirrored a set of folders based on a Work Area within a Source datasource to a selected Work Area within a Target datasource. This same process could be used to mirror folders from a ProjectWise datasource to a Windows folder. Ensure you add any error handling as needed.

Experiment with it and have fun. And please, let me know if there is a topic you would like to see a post for. Or share a solution you have developed.

Hopefully, you find this useful. Please let me know if you have any questions or comments.  If you like this post, please click the Like button at the bottom of the page. And thank you for checking out my blog.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.