Azure Deployment Stacks unleashed
In the context of Microsoft Azure, most of the time, we use resource groups to put together resources that have same lifecycle (creation and deletion). However when you need to manage the lifecycle of resources that are spread across different scopes (management groups, subscriptions, resource groups) it becomes unnecessarily complex, at least until now.
In this post we will try to better understand how a Deployment Stack works and see how to move from deployments to deployment stacks.
Welcome Deployment Stacks
A “deployment stack” is a new resource type that groups together the resources contained into a deployment giving a clear view of your unit of deployment and allowing to define once (at the stack level) how to prevent undesired changes and which actions to be taken (aka lifecycle policies) on the resources that belongs to the stack once the stack is changed.
The following image tries to recap the 3 main concepts covered by a deployment stack: deployment, lifecycle and protection.
🏛️ Actually by creating a deployment stack you initiate a deployment therefore we can say it will eventually replace the “az deployment” command. At the time of writing the main missing functionality is the “what-if” support. It is advisable to review the list of known-issues before embarking on the exploration of deployment stacks.
🔒By defining a protection at stack level you can avoid a to create a multitude of locks (e.g. CanNotDelete) at resource level.
🛟 The stack lifecycle define what actions to be taken to the resources that are not anymore part of the stack meaning it will be easier to clean up an application that spread across different resource groups/subscriptions.
Create a deployment stack
To create a new stack is pretty straightforward. Get a bicep file and execute the “az stack sub create”command, to trigger your deployment. The minimal command looks as the following:
⚡az stack sub create —name “hallostack” —template-file orchestration.bicep —location westeurope —deny-settings-mode “DenyDelete”
Make sure you define the protection policy you want to enforce (—deny-settings-mode) and the deployment parameters (if any). When you create a stack for the first time it is not necessary to specify the lifecycle flags because there are no changes to be taken care of.
One of the main benefits of the Deployment Stack is visibility. Once the stack deployment is completed you get a clear view of which resources has been created, deleted and detached. Particularly useful is the column which indicates which type of lock is applied to the Azure resources. This view is available via the Azure portal or, for example, by running the “az stack sub show” command.
On this GitHub repo you can find a set of simple bicep files I used to play with the deployment stacks.
Modify a deployment stack (add, modify, delete resources)
Should you make modifications to your Bicep file, such as introducing a new resource, this addition will undergo deployment and become part of the stack. Meanwhile, any resources that no longer exist within the deployment will be extracted from the stack, aligning with the stipulated lifecycle policy.
Basically the stack compares the current deployment with the deployment-1 and applies the lifecycle policies to the differences.(A-B).
There are 3 types of lifecycle policies that can be defined on the Stack:
- detach both resources and RGs ( default behavior). Detach means the resources will not be listed anymore in the stack therefore no protection policy is applied. The resources are still present in the Azure environment.
- delete both resources and RGs ( —delete-all true ). Resources and RGs will be deleted from the stack and from the Azure environment as part of the “stack create” command.
- delete all resources, detach the RGs ( —delete-resources true, —delete-resource-groups false).
💡Note: the list of the detached and deleted resources does not represent the evolution over time but is limited to the differences introduced by the the latest update. Suppose your first deployment creates 4 storage accounts (A,B,C,D), then you update the stack in two different steps first to remove the storage account C and then the storage account D. The stack overview will look as the following: the deletion of C is not persisted.
💡Note: Deployment stack do not work in append mode, if you run two completely different bicep files targeting the same stack, all the resources that belongs to the first bicep will be detached or deleted depending on the defined lifecycle.
💡Note : You cannot, with a single “az stack sub create” command delete the resource A and detach the resource B. All the resources that belongs to a given stack are subjected to the same lifecycle policy..
Delete a deployment stack
When you delete a deployment stack you can specify which lifecycle policy to be applied on the resources managed by the stack. This is extremely useful because you can, with a single command, delete an application that is spread over different RGs / subscriptions.
💡Note : In case you have a collection of resources contained within distinct deployment stacks , each governed by a deny delete policy, a conflict will arise when attempting to initiate deletion.
💡Note : If you create a stack by specifying a ‘delete-all’ policy and you delete it using a command without any flag like “az stack sub delete —name stackname”, the default behavior will be applied meaning resources and RGs will be detached a still available in the Azure environment.
From Deployments to Deployment Stacks
Moving to deployment stack is pretty straightforward because, except for the lifecycle and the protection flags, the parameters are named the same. The bigger impact it might be on the naming convention of your deployments and the way you handle the output parameters; let’s see why.
The Deployment Stack uses a specific naming convention for the deployment that involves appending a timestamp to the stack name. The resulting deployment name format initiated by the stack takes the form of ‘{stack-name}-{timestamp}-{uniqueid}’.
When utilizing nested Bicep modules and your linked deployments are named as : ‘name: take(’${deployment().name}-something’, 64)’, the complexity and number of layers in your deployment will impact the growth of the nested deployment names.
The timestamp and unique ID introduced by the stack will add to the length of the deployment name leading to two possible problems:
- Exceeding the maximum permissible length of 64 characters if the ‘take 64’ function is not utilized.
- The possibility of duplicate deployment names if the ‘take 64’ function is applied.
💡Note : To avoid this problem you may consider to rework your deployment naming or use the uniquestring function. For a better backward compatibility it would be nice to introduce an optional ‘—deployment-name’.
The JSON output returned by a deployment stack is different from what we get from a standard deployment. More in details the output parameters are not wrapped anymore in the ‘properties’ object.
- here how you can capture the outputs for a deployment : ”($deployment | ConvertFrom-Json).properties.outputs.alphaName.value”
- here how it looks for a deployment stack : ”($stack | ConvertFrom-Json).outputs.alphaName.value”
Conclusion
The advent of Deployment Stacks marks a significant step towards a better and simpler Azure resource management. Deployment Stacks deliver a better lifecycle governance, and simplified resource protection. We are ready to say goodbye to Deployments in favor of Deployment Stacks, how about you?
Photo by Hugo Chacon on Unsplash




