If you haven't already, please check out the previous parts of this series.
Part 2 - Deploying Bicep with AZ CLI
Part 5 - Deploying Bicep with GitHub Actions
Part 6 - You're reading it now!
Extra Credit - My Advanced Bicep Guide
Deployment Scopes
First, let's go over Deployment Scopes. As discussed in Part 1, a Bicep file can use 1 of 4 deployment scopes: resourceGroup, subscription, managementGroup, or tenant. Each scope can only deploy a certain set of resource types.
A Bicep file using targetScope: resourceGroup can only deploy resources to the chosen Resource Group. A Bicep file using targetScope: subscription can only deploy resources to the chosen Subscription. Same thing goes for the targetScopes of managementGroup and tenant. However, you can get around this limitation by using Bicep Modules. That's because Bicep Modules can have their own unique scope which can be different than the Bicep file's targetScope. For example, this means you can have a Bicep file using targetScope: resourceGroup that does the following:
Deploys resources to the chosen Resource Group
Deploys resources to a different Resource Group
Deploys a module with a scope of Subscription
Deploys a module with a scope of Tenant
The diagrams below highlight the different Module Scopes that are supported by the 4 Bicep file targetScopes. I also show the bicep functions that can be used to define your module scopes.
Deployment Stacks
Deployment Stacks are an alternative way to deploy your Bicep file. They allow you to control all of the resources in your Bicep file as one cohesive unit. Deployment Stacks support Bicep files that use targetScope: resourceGroup, subscription, or managementGroup. But, Bicep files using targetScope: tenant are not supported by Deployment Stacks. Deployment Stacks have a few unique features versus normal Bicep deployments, let's highlight them below.
Resource Management
A Deployment Stack will track the exact set of Azure resources that are deployed as part of the Stack. After the initial deployment of the stack, you can easily add or remove resources from the Stack. When removing resources from a Stack, you are given multiple options:
deleteAll: this deletes ALL resources that were removed from the Stack, including any RGs and MGs that are managed by the Stack
deleteResources: this deletes only the resources that were removed from the Stack, but keeps any RGs and MGs that are managed by the Stack
detachAll: this option "detaches" ALL resources that were removed from the Stack, including RGs and MGs that are managed by the Stack. To "detach" a resource means it will be detached/disconnected from the Deployment Stack itself, but the resource will still exist in Azure and won't be deleted
Another benefit of Stacks is that it is easier to delete all resources associated with the Stack after you are done with it. Simply delete the Stack and choose the option to delete all the resources, and it will be done in one simple operation, no matter where the resources are located (resource group, subscription, management group, tenant).
Now, let's compare this to normal Bicep deployments. Normal Bicep deployments that use targetScope: resourceGroup have an option called "mode" which can be used to track the deployed resources. "Mode" has 2 options:
Incremental: this simply deploys whatever is defined in your Bicep file, and it doesn't care what other resources may exist in the Resource Group.
Complete: this uses the Bicep file as the ultimate source of truth. If the Resource Group contains resources that don't appear in the Bicep file, then those resources are deleted from Azure.
Unfortunately for normal Bicep deployments these are your only options. On top of that, complete mode is only supported for the resourceGroup scope. Also, Complete mode doesn't work for any resources deployed via modules. Lastly, if you want to delete all of the resources from a normal Bicep deployment that are spread across multiple scopes, then you would need to do that manually.
Deny Settings
Another benefit of Deployment Stacks is that you can define "Deny" settings for the resources deployed by the Stack. Deny settings support 3 different modes:
none: resources deployed by the Stack will not have any Deny settings, and as long as you have the RBAC permissions you can delete or update the resources
denyDelete: resources deployed by the Stack can not be deleted
denyWriteAndDelete: resources deployed by the Stack can not be updated or deleted
Some Azure resources have "child" scopes. For example, a SQL Server would be considered the parent, and a SQL Database would be considered the child. With Deployment Stacks you can optionally specify that "child" resources must inherit the Deny settings applied to the parent. If we use the SQL example again, that would mean any SQL Databases (child) would inherit the Deny settings applied on the SQL Server (parent).
Lastly, Deployment Stacks allow you to make 2 different types of exclusions for Deny Settings. One, you can exclude up to 200 specific RBAC management actions. For example, say you wanted to completely lock down an Azure Compute Gallery from all changes, but you still wanted to allow users to share the Compute Gallery. In this case, you would exclude the RBAC action for Microsoft.Compute/galleries/share/action. Two, you can exclude up to 5 specific users or principals. The Deny Settings would not apply to these specified users.
Again, let's compare this to normal Bicep deployments. When it comes to Deny Settings, there are no good options in my opinion. You could create Azure Blueprints, which can include Locks for Do-Not-Delete or Read-Only. However, Azure Blueprints are being deprecated, so I can't recommend using them. You could also use Azure Policy which has a 'denyAction' effect. However, at the time of this writing the effect only supports 1 type of action and that is 'delete'.
Deployment Stack Scope
One final feature of Deployment Stacks that I will discuss is that you can deploy them at a different scope than the Bicep File. There are 2 options when using this feature:
Deployment Stack at the Management Group scope, with a Bicep file at the Subscription scope
Deployment Stack at the Subscription scope, with a Bicep file at the Resource Group scope
But, why would you want to do this? This would be useful in situations where you want to strictly control permissions between different teams. For example, this would allow you, as the administrator, to have control over the Deployment Stack because it lives in a higher scope that only you have access to. The end users would have access to just the resources that are deployed to the lower scope, and they would not be able to modify the Deployment Stack. This is especially important when using Deny Settings on your Deployment Stack. Because what good is a Deny Setting if a user can just remove the setting themselves?
Creating Deployment Stacks
I made some examples showing how to create Deployment Stacks:
Official Microsoft documentation:
Using the Az PowerShell Module (version 12.0.0 or later):
Using the Az CLI (version 2.61.0 or later):
Using GitHub Actions:
That covers everything that I wanted to discuss on Scopes & Stacks. I hope you got some useful information out of this post. This is also the end of my Bicep deployment series, thank you for reading!
My Bicep Deployment Series:
Part 2 - Deploying Bicep with AZ CLI
Part 5 - Deploying Bicep with GitHub Actions
Part 6 - You're reading it now!
Extra Credit - My Advanced Bicep Guide
Comments