I've been working on some pretty complicated infrastructure deployment pipelines using my release management tool of choice (of course): VSTS Release Management. In this particular scenario, we're deploying a set of VMs to a region. We then want to deploy exactly the same setup but in a different region. Conceptually, this is like duplicating infrastructure between different datacenters.
Here's what the DEV environment in a release could look like:
If we're duplicating this to 5 regions, we'd need to clone the environment another 4 times. However, that would mean that any updates to any tasks would need to be duplicated over all 5 regions. It's easy to forget to update or to fat-finger a copy - isn't there a better way to maintain sets of tasks? I'm glad you asked…
DRY - Don't Repeat Yourself
DRY (Don't Repeat Yourself) is a common coding practice - any time you find yourself copying code, you should extract it into a function so that you only have to maintain that logic in a single place. We can do the same thing in a release (or build) using Task Groups. Task Groups are like functions that you can call from releases (or builds) from many places - but maintain in a single place. Just like functions, they have parameters that you can set when you "call" them. Click the selector (checkmark icon to the right of each task) to select all the tasks you want to group, right-click and select "Create task group":
A popup asks for the name of the Task Group and bubbles up all the parameters that are used in the tasks within the group. You can update the defaults and descriptions and click Create (helpful hint: make variables for all the values so that the variable becomes the default rather than a hard-coded value - this will make it easier to re-use the Task Group when you clone environments later):
So far, so good:
However, there's a snag: looking at the parameters section, you'll notice that we don't have any parameter for the Azure Service Endpoint. Let's open the tasks and update the value in the dropdown to $(AzureSubscription):
Now you can see that the parameter is bubble up and surfaced as a parameter on the Task Group - it even has the dropdown with the Service Endpoints. Nice!
Consuming the Task Group
Open up the release again. You'll see that you now have a new parameter on the Task Group: the AzureSubscription. We'll select the DEV sub from the dropdown.
Also note how the phase is now a single "task" (which is just a call to the Task Group). Under the hood, when the release is created, Release Management deletes the task group and replaces it with the tasks from the Task Group - so any values that are likely to change or be calculated on the fly should be variables.
Let's now clone the DEV environment to UAT-WESTUS and to UAT-EASTUS.
If we edit the UAT-WESTUS, we can edit the service endpoint (and any other parameters) that we need to for this environment:
Excellent! Now we can update the Task Group in a single place even if we're using it in dozens of environments. Of course you'd need to update the other parameter values to have environment-specific values (Scopes) in the Variables section.
Task Groups are a great way to keep your releases (or builds) DRY - even allowing you to parameterize the Azure Service Endpoint so that you can duplicate infrastructure across different subscriptions or regions in Azure.