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.
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