1

I am trying to copy a List workflow from one site to another (same site collection). Unfortunately, for various reasons I can't save the workflow as a template.

I have a PowerShell script that does almost everything, but is missing one component.

The below script will get the workflow from a source site, get the definition, then replace all the list GUIDs for those in the new site (sites have the same structure).

It will then create a new workflow in the target site, associate it to the correct list, and publish it. The workflow performs perfectly.

There is only one problem - if I go into SharePoint Designer and connect to the new site, if I Publish the workflow again (or make a change and publish it), a second subscription is created on the site.

Before Publishing: BeforePublishing

After Publishing: AfterPublishing

Can anyone see a way around this? Script pasted below:

function Copy-Workflow {
    param(
    [Parameter(Mandatory=$true)][string]$TargetAKPA,
    [Parameter(Mandatory=$true)][string]$SourceAKPA,
    [Parameter(Mandatory=$true)][string]$Workflow,
    [string][ValidateSet("PROD","DEV","TEST","SANDPIT")]$Environment
    )

    switch ($Environment){
        'PROD'{
            $rootURL = "https://xxx.sharepoint.com/sites/db"
            $sourceSite = $SourceAKPA
        }
        'TEST'{
            $rootURL = "https://xxx.sharepoint.com/sites/db-test"
            $sourceSite = $SourceAKPA
        }
        'DEV'{
            $rootURL = "https://xxx.sharepoint.com/sites/db-dev"
            $sourceSite = $SourceAKPA
        }
        'SANDPIT'{
            $rootURL = "https://xxx.sharepoint.com/sites/db-sandpit"
            $sourceSite = $SourceAKPA
        }
    }    
    $errors = $false
    $sourceSiteURL = "$rootURL/$sourceSite"

    Connect-PnPOnline -Url $sourceSiteURL -Credentials dbadmin

        $SourceTable = Get-PnPList
        $SourceWorkflowDefn = Get-PnPWorkflowDefinition -PublishedOnly
        $SourceWorkflowSub = Get-PnPWorkflowSubscription 

    Disconnect-PnPOnline

    Connect-PnPOnline -Url "$rooturl/$targetAKPA" -Credentials dbadmin 

        $DestTable = Get-PnPList
        $DestWorkflowDefn = Get-PnPWorkflowDefinition -PublishedOnly
        $DestWorkflowSub = Get-PnPWorkflowSubscription

        $sourceDefn = $SourceWorkflowDefn | Where-Object{$_.DisplayName -eq $Workflow}
        $sourceSubs = $SourceWorkflowSub | Where-Object{$_.Name -eq $Workflow}
        if($sourceDefn -ne $null){
            $sourceXAML = $sourceDefn.Xaml


            foreach ($id in $SourceTable){

                if($sourceXAML -match $id.id.Guid){
                    $sourceIDName = $SourceTable | Where-Object{$_.ID.Guid -eq $id.id.Guid} | Select-Object Title
                    $destIDNumber = $DestTable | Where-Object{$_.Title -eq $sourceIDName.Title} | Select-Object Id

                    $sourceXAML = $sourceXAML.Replace($Id.id.guid,$destIDNumber.id.Guid)

                }

            }
        } else {
        Write-Host "Workflow $workflow not found in source site" -ForegroundColor Red
        $errors = $true
        }

        if ($errors -ne $true){
            $destDefn = $DestWorkflowDefn | Where-Object{$_.DisplayName -eq $Workflow}
            if($destDefn -ne $null){
                #$destXAML = $sourceXAML
                #$destDefn.Xaml = $destXAML
                Write-Host "Removing old definition"-ForegroundColor Yellow
                Remove-PnPWorkflowDefinition -Identity $destDefn

            }
            #Write-Host "Workflow $workflow not found in destination site" -ForegroundColor Red
            # Load the WorkflowServicesManager client library (please provide the proper file path …)
            Add-Type -Path (Resolve-Path "$env:CommonProgramFiles\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.dll")
            Add-Type -Path (Resolve-Path "$env:CommonProgramFiles\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.Runtime.dll")
            Add-Type -Path (Resolve-Path "$env:CommonProgramFiles\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.WorkflowServices.dll")

            # Set the target SharePoint Site
            $SiteURL =  "$rooturl/$targetAKPA"

            # Set up credentials

            $Creds = Get-PnPStoredCredential -Name DBAdmin -Type PSCredential

            # Create Context

            $ClientContext = New-Object Microsoft.SharePoint.Client.ClientContext($SiteUrl)
            $credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($creds.UserName, $creds.Password)
            $ClientContext.Credentials = $credentials
            $spWeb = $ClientContext.Web
            $ClientContext.ExecuteQuery()

            # Create a WorkflowServicesManager instance and retrieve WorkflowService related objects

            $WorkflowServicesManager = New-Object Microsoft.SharePoint.Client.WorkflowServices.WorkflowServicesManager($ClientContext,$spWeb)
            $WorkflowSubscriptionService = $WorkflowServicesManager.GetWorkflowSubscriptionService()
            $WorkflowInstanceService = $WorkflowServicesManager.GetWorkflowInstanceService()
            $ClientContext.Load($WorkflowServicesManager)
            $ClientContext.Load($WorkflowSubscriptionService)
            $ClientContext.Load($WorkflowInstanceService)
            $ClientContext.ExecuteQuery()

            # Create a WorkflowServicesManager instance

            # Get a reference to the Workflow Deployment Service

            $wfDeploymentService = $WorkflowServicesManager.GetWorkflowDeploymentService()

            # Load the Workflow XAML

            $xaml = $sourceXAML

            # Prepare the Workflow Definition object
            $wfDefinition = New-Object Microsoft.SharePoint.Client.WorkflowServices.WorkflowDefinition -ArgumentList $ClientContext
            $wfDefinition.DisplayName = $workflow
            $wfDefinition.Description = $sourceDefn.Description
            $wfDefinition.Xaml = $xaml.ToString()
            $wfDefinition.RestrictToType = $sourceDefn.RestrictToType
            # Define scope

            $scopeName = $SourceTable | Where-Object {$_.Id.Guid -eq $sourceDefn.RestrictToScope.Trim("{","}")}
            $destScope = $DestTable | Where-Object{$_.Title -eq $scopeName.Title}

            $wfDefinition.RestrictToScope = $destScope.id.Guid
            # Set SPD launch flags

            if($sourceDefn.Properties."SPDConfig.StartOnCreate" -eq $true){
                $wfDefinition.setProperty("SPDConfig.StartOnCreate", $true)
            }
            if($sourceDefn.Properties."SPDConfig.StartOnChange" -eq $true){
                $wfDefinition.setProperty("SPDConfig.StartOnChange", $true)
            }
            if($sourceDefn.Properties."SPDConfig.StartManually" -eq $true){
                $wfDefinition.setProperty("SPDConfig.StartManually", $true)
            }                        

            # Save and publish the Workflow Definition object
            $definitionId = $wfDeploymentService.SaveDefinition($wfDefinition)
            $ClientContext.Load($wfDefinition)
            $ClientContext.ExecuteQuery()

            # Publish the Workflow Definition
            $wfDeploymentService.PublishDefinition($definitionId.Value)

            # Retrieve IDs of targets (list/library, history, and tasks)
            $targetLibrary = $spWeb.Lists.GetByTitle($scopeName.Title)
            $ClientContext.Load($targetLibrary)

            # Get History List Name
            $sourceHistoryListID = $sourcedefn.Properties.HistoryListId -Replace "[{}]",""
            $historyListName = $SourceTable | Where-Object {$_.Id.Guid -eq $sourceHistoryListID}

            $historyList = $spWeb.Lists.GetByTitle($historyListName.Title)
            $ClientContext.Load($historyList)

            # Get Task List Name
            $sourceTaskListID = $sourcedefn.Properties.TaskListId -Replace "[{}]",""
            $taskListName = $SourceTable | Where-Object {$_.Id.Guid -eq $sourceTaskListID}
            $tasksList = $spWeb.Lists.GetByTitle($taskListName.Title)
            $ClientContext.Load($tasksList)

            $ClientContext.ExecuteQuery()

            # Associate the Workflow Definition to a target list/library
            $wfSubscriptionService = $WorkflowServicesManager.GetWorkflowSubscriptionService()
            $wfSubscription = New-Object Microsoft.SharePoint.Client.WorkflowServices.WorkflowSubscription -ArgumentList $ClientContext

            # Configure the Workflow Subscription
            $wfSubscription.DefinitionId = $definitionId.Value
            $wfSubscription.Name = $wfDefinition.DisplayName
            $wfSubscription.Enabled = $true

            $wfSubscription.EventTypes = $sourceSubs.EventTypes

            $wfSubscription.EventSourceId = $targetLibrary.Id.ToString()
            $wfSubscription.SetProperty(“TaskListId”, $tasksList.Id.ToString())
            $wfSubscription.SetProperty(“HistoryListId”, $historyList.Id.ToString())


            # Publish the Workflow Subscription
            $wfSubscriptionService.PublishSubscriptionForList($wfSubscription, $targetLibrary.Id.Guid)
            $ClientContext.ExecuteQuery()
            $ClientContext.Dispose()

        }
    Disconnect-PnPOnline 
    if($errors -ne $true){
        Write-Host "Updated $workflow on site $TargetAKPA" -ForegroundColor Cyan
    } else {
        Write-Host "Errors encountered" -ForegroundColor Red
    }
}

Edit with additional information:

There is only one workflow created through this process - what is happening is a second subscription to the same workflow gets created after the workflow is published in SPD (even if no changes are made to the workflow).

Largely speaking, the two subscriptions are similar, most differences seem to be in the PropertyDefinitions section.

Original subscription:

PropertyDefinitions                : {[StatusColumnCreated, 1], [Microsoft.SharePoint.ActivationProperties.ListId, dd350e1f-3c95-4dc9-aec0-ddb1dad38820], [WSEventContextKeys,
                                 StatusColumnCreated;#WorkflowService
                                 CreatedBySPD;#WorkflowService
                                 CurrentWebUri;#WorkflowService
                                 HistoryListId;#WorkflowService
                                 SharePointWorkflowContext.ActivationProperties.SiteId;#WorkflowService
                                 TaskListId;#WorkflowService
                                 Microsoft.SharePoint.ActivationProperties.ParentContentTypeId;#Instance
                                 SharePointWorkflowContext.ActivationProperties.WebId;#WorkflowService
                                 Microsoft.SharePoint.ActivationProperties.ListId;#WorkflowService
                                 Microsoft.SharePoint.ActivationProperties.ListName;#WorkflowService
                                 Microsoft.SharePoint.ActivationProperties.CurrentItemUrl;#Instance
                                 Microsoft.SharePoint.ActivationProperties.ItemId;#Instance
                                 Microsoft.SharePoint.ActivationProperties.ItemGuid;#Instance
                                 Microsoft.SharePoint.ActivationProperties.UniqueId;#Instance
                                 StatusFieldName;#WorkflowService
                                 Microsoft.SharePoint.ActivationProperties.RelatedItems;#Instance

Extra subscription:

PropertyDefinitions                : {[StatusColumnCreated, 1], [vti_folderitemcount, 0], [SharePointWorkflowContext.Subscription.DisplayName, Document Management], [vti_privatelistexempt,true]...}

Expanding PropertyDefinitions and removing duplicates gives the following:

Original subscription:

_EditMenuTableEnd                                                                                   77
_EditMenuTableStart                                                                                 WorkflowAssociation_fa9055536d3c46b2856dbd9822313a12
_EditMenuTableStart2                                                                                77
BaseName                                                                                            WorkflowAssociation_fa9055536d3c46b2856dbd9822313a12
CheckedOutTitle                                                                                     77;#
CheckedOutUserId                                                                                    77;#
GUID                                                                                                {63FFC6AD-F509-4F9F-81F9-7F21D6024C9E}
HistoryListId                                                                                       d7a431e9-26a5-492f-ab6f-77a67b74b2e9
LinkFilename                                                                                        WorkflowAssociation_fa9055536d3c46b2856dbd9822313a12
LinkFilename2                                                                                       WorkflowAssociation_fa9055536d3c46b2856dbd9822313a12
LinkFilenameNoMenu                                                                                  WorkflowAssociation_fa9055536d3c46b2856dbd9822313a12
Order                                                                                               7700
ParentLeafName                                                                                      77;#
ParentVersionString                                                                                 77;#
SelectFilename                                                                                      77
SelectTitle                                                                                         77
ServerUrl                                                                                           /sites/db-dev/4VPPXH/wfsvc/65c55a64966b4de6aef8f2b140b912e3/WorkflowAssociation_fa9055536d3c...
SharePointWorkflowContext.Subscription.FullName                                                     /sites/db-dev/4VPPXH/wfsvc/65c55a64966b4de6aef8f2b140b912e3/WorkflowAssociation_fa9055536d3c...
SharePointWorkflowContext.Subscription.Id                                                           2c6a4ddb-60f1-4b76-ac35-d34ea8c2d356
SharePointWorkflowContext.Subscription.ListItemId                                                   77
SharePointWorkflowContext.Subscription.Name                                                         WorkflowAssociation_fa9055536d3c46b2856dbd9822313a12
SortBehavior                                                                                        77;#0
StatusFieldName                                                                                     Document_x0020_Management
SyncClientId                                                                                        77;#
TaskListId                                                                                          d0525df0-3067-4d40-8e77-f654f5a5d1bc
UniqueId                                                                                            abed778d-2c97-4e54-8528-458212af0c21
VirusStatus                                                                                         77;#0

New subscription:

_EditMenuTableEnd                                                                                   78
_EditMenuTableStart                                                                                 WorkflowAssociation_b34018da8e344a188bf56eb3a98b21b1
_EditMenuTableStart2                                                                                78
BaseName                                                                                            WorkflowAssociation_b34018da8e344a188bf56eb3a98b21b1
CheckedOutTitle                                                                                     78;#
CheckedOutUserId                                                                                    78;#
CreatedBySPD                                                                                        1
CurrentWebUri                                                                                       https://xxx.sharepoint.com/sites/db-dev/4VPPXH
GUID                                                                                                {2FDB648B-7A0A-4724-828E-CCACA3640E4A}
HistoryListId                                                                                       D7A431E9-26A5-492F-AB6F-77A67B74B2E9
LinkFilename                                                                                        WorkflowAssociation_b34018da8e344a188bf56eb3a98b21b1
LinkFilename2                                                                                       WorkflowAssociation_b34018da8e344a188bf56eb3a98b21b1
LinkFilenameNoMenu                                                                                  WorkflowAssociation_b34018da8e344a188bf56eb3a98b21b1
Microsoft.SharePoint.ActivationProperties.ParentContentTypeId   
Order                                                                                               7800
ParentLeafName                                                                                      78;#
ParentVersionString                                                                                 78;#
SelectFilename                                                                                      78
SelectTitle                                                                                         78
ServerUrl                                                                                           /sites/db-dev/4VPPXH/wfsvc/65c55a64966b4de6aef8f2b140b912e3/WorkflowAssociation_b34018da8e34...
SharePointWorkflowContext.ActivationProperties.SiteId                                               41DC94B3-DF7B-4133-90FB-487A8F799C70
SharePointWorkflowContext.ActivationProperties.WebId                                                364E3D25-3095-401F-9243-C38D7E539816
SharePointWorkflowContext.Subscription.FullName                                                     /sites/db-dev/4VPPXH/wfsvc/65c55a64966b4de6aef8f2b140b912e3/WorkflowAssociation_b34018da8e34...
SharePointWorkflowContext.Subscription.Id                                                           13780a63-5869-41a2-8c44-feb04e662243
SharePointWorkflowContext.Subscription.ListItemId                                                   78
SharePointWorkflowContext.Subscription.Name                                                         WorkflowAssociation_b34018da8e344a188bf56eb3a98b21b1
SortBehavior                                                                                        78;#0
StatusFieldName                                                                                     Document_x0020_Management_x0028_1_x0029_
SyncClientId                                                                                        78;#
TaskListId                                                                                          D0525DF0-3067-4D40-8E77-F654F5A5D1BC
UniqueId                                                                                            20438985-31d6-4d5b-8fdb-a62bcbeec60f
VirusStatus                                                                                         78;#0
6
  • 1
    Nice work. Would love to see the PS code and the (sterilized to remove company names) initial, translated (before import), after SPD saved versions of the workflow. It could be as simple as a invalid GUID transform, or an unexpected revision in SPD (it recompiles before publishing).
    – ghangas
    Oct 10, 2017 at 22:10
  • We are using actions from the Plumsail Workflow Action pack, and have never managed to get these to be included in the site template. Currently we use Sharegate to copy these workflows to our new sites, however this takes some time, when all we really need to do is get the workflow over. The code above is much faster than using Sharegate, if only the workflows would publish properly! I could put a business process in place so that people know to delete the existing subscription before republishing, but I would rather do it properly!
    – Gizmo3k
    Oct 11, 2017 at 7:44
  • 1
    After the edit and republish when the duplicate exists, What is different between the 2 workflows? You can grab that by stepping through the PS first 30 lines. There should be 2 nearly identical workflows in $SourceWorkflowDefn. Review them and see what the difference is. Review the xaml and guids. If not different then it may just be a duplicate subscription in which case review $DestWorkflowSub.
    – ghangas
    Oct 11, 2017 at 15:46
  • @ghangas Information updated, there is only one workflow definition - it's just a second subscription is getting created.
    – Gizmo3k
    Oct 13, 2017 at 8:15
  • 1
    Great! Credit to github.com/DarkGenius/sp-powershell/blob/master/workflows/… Try appending to the respective section: # Configure the Workflow Subscription ... $wfSubscription.SetProperty("SharePointWorkflowContext.ActivationProperties.SiteId", $spWeb.Site.ID.ToString()) $wfSubscription.SetProperty("SharePointWorkflowContext.ActivationProperties.WebId", $spWeb.ID.ToString()) Also, verify the StatusFieldName
    – ghangas
    Oct 13, 2017 at 17:33

1 Answer 1

0

Credit to https://github.com/DarkGenius/sp-powershell/blob/master/workflows/deploy_workflow.ps1 Try appending to the section:

 # Configure the Workflow Subscription
            ...
            $wfSubscription.SetProperty("SharePointWorkflowContext.ActivationProperties.SiteId", $spWeb.Site.ID.ToString())
            $wfSubscription.SetProperty("SharePointWorkflowContext.ActivationProperties.WebId", $spWeb.ID.ToString())       

Also, verify the StatusFieldName

1
  • Unfortunately, I am still getting the second subscription on publishing, even with the new lines added.
    – Gizmo3k
    Oct 16, 2017 at 10:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.