Keeping projects in sync


We currently have 8 projects that are effectively clones of each other just using variables to distinguish which nuget packages to select.

A problem we have is keeping the 8 projects aligned , when we add a step from a template into a project and test we then have the long process of making sure the remaining 8 (and growing) projects have the same steps in the same order…

Now I’ve wrote some powershell to clone the steps from 1 project into another , I’m wondering if anyone could advise if this would work as i intend , all of the steps are from step templates and contain NO project specific information

it backups the existing steps of the project you are cloning to and exports these
it then removes all the steps from the target project
it then adds all the steps / actions from the clone project into the target project and updates the process

I have tested and from what i can see it works and i can update the step template versions etc




$copyfromproject = Read-Host “please enter the master project to copy from”
$copytoproject = Read-Host “please enter the project to copy to”

$fromprojectId = ($repositry.Projects.FindByName("$copyfromproject")).Id
$toprojectId = ($repositry.Projects.FindByName("$copytoproject")).Id

$fromproject = $repositry.Projects.Get($fromprojectId)
$fromprocess = $repositry.DeploymentProcesses.Get($fromproject.DeploymentProcessId)

$toproject = $repositry.Projects.Get($toprojectId)
$toprocess = $repositry.DeploymentProcesses.Get($toproject.DeploymentProcessId)

$backup = $repositry.DeploymentProcesses.Get($toproject.DeploymentProcessId)
$backup | Export-Clixml "c:\test$copytoproject.xml"
foreach($step in $($toprocess.Steps))

$toproject = $repositry.Projects.Get($toprojectId)
$toprocess = $repositry.DeploymentProcesses.Get($toproject.DeploymentProcessId)

foreach($steps in $fromprocess.Steps)
$step = New-Object Octopus.Client.Model.DeploymentStepResource
$step.Name = $steps.Name
$step.Condition = $steps.Condition
$step.StartTrigger = $steps.StartTrigger
$step.RequiresPackagesToBeAcquired = $steps.RequiresPackagesToBeAcquired

    foreach($action in $steps.Actions)
            $keys = $action.Properties
            $scriptAction = New-Object Octopus.Client.Model.DeploymentActionResource
            $scriptAction.ActionType = $action.ActionType
            $scriptAction.Name = $
            foreach($key in $keys.GetEnumerator())
                    $scriptAction.Properties.Add("$($key.Key)", "$($key.value)")


Hi Kris

Thanks for reaching out!

I haven’t run your code, but it looks like it should work fairly well.

A few things to consider though:

  • the SensitiveProperties field was removed in Octopus 3.3.0, so you wont need to copy that across
  • where you’re copying across the properties of the step (line 34), it wont copy correctly - you’ll need to do it the same way your doing for the action properties.

You might want to consider a pure JSON manipulation approach - that way this will continue to work in future if we add/remove fields. All you’ll need to do is blank out the step id’s and the action id’s. Take a look at how the BlueFin extension implemented clone step which is similar to what you’re doing here.

That said, I believe that composite step templates would be what you’re looking for here. We’ve committed to implementing all of our UserVoice suggestions with more than 200 votes, so this one will definitely be on the agenda this year (sorry I cant give you a better idea of when at this point).

Hope that helps!


Hi Matt,

thanks for your input :slight_smile:
I’ve changed the properties now to loop through as you suggested … and removed the sensitive properties , Which we thought has gone when testing on another instance as the property wasn’t showing…

As a temporary tool we’re happy to run like this as we won’t be upgrading for a while and hopefully the composite template bit will be implemented when we do :slight_smile:

            $keys = $steps.Properties
            foreach($key in $keys.GetEnumerator())
                    $step.Properties.Add("$($key.Key)", "$($key.value)")

Hi Kris

Glad its working for you.

Keep an eye out on our blog - I suspect that the composite templates will get a nice blog post to announce it.

Happy deployments!