Deployment stacks (Preview)

An Azure deployment stack is a type of Azure resource that enables the management of a group of Azure resources as an atomic unit. When a Bicep file or an ARM JSON template is submitted to a deployment stack, it defines the resources that are managed by the stack. If a resource that was previously included in the template is removed, it will either be detached or deleted based on the specified actionOnUnmanage behavior of the deployment stack. Similar to other Azure resources, access to the deployment stack can be restricted using Azure role-based access control (Azure RBAC).

To create and update a deployment stack, you can utilize Azure CLI, Azure PowerShell, or the Azure portal along with Bicep files. These Bicep files are transpiled into ARM JSON templates, which are then deployed as a deployment object by the stack. The deployment stack offers additional capabilities beyond the familiar deployment resources, serving as a superset of those capabilities.

Microsoft.Resources/deploymentStacks is the resource type for deployment stacks. It consists of a main template that can perform 1-to-many updates across scopes to the resources it describes, and block any unwanted changes to those resources.

When planning your deployment and determining which resource groups should be part of the same stack, it's important to consider the management lifecycle of those resources, which includes creation, updating, and deletion. For instance, suppose you need to provision some test VMs for various application teams across different resource group scopes. In this case, a deployment stack can be utilized to create these test environments and update the test VM configurations through subsequent updates to the deployment stack. After completing the project, it may be necessary to remove or delete any resources that were created, such as the test VMs. By utilizing a deployment stack, the managed resources can be easily removed by specifying the appropriate delete flag. This streamlined approach saves time during environment cleanup, as it involves a single update to the stack resource rather than individually modifying or removing each test VM across various resource group scopes.

Deployment stacks requires Azure PowerShell version 10.1.0 or later or Azure CLI version 2.50.0 or later.

To create your first deployment stack, work through Quickstart: create deployment stack.

Why use deployment stacks?

Deployment stacks provide the following benefits:

  • Simplified provisioning and management of resources across different scopes as a cohesive entity.
  • Preventing undesired modifications to managed resources through deny settings.
  • Efficient environment cleanup by employing delete flags during deployment stack updates.
  • Utilizing standard templates such as Bicep, ARM templates, or Template specs for your deployment stacks.

Known limitations

  • Implicitly created resources aren't managed by the stack. Therefore, no deny assignments or cleanup is possible.
  • Deny assignments don't support tags.
  • Deny assignments is not supported within the management group scope.
  • Deployment stacks cannot delete Key vault secrets. If you're removing key vault secrets from a template, make sure to also execute the deployment stack update/delete command with detach mode.

Known issues

  • Deleting resource groups currently bypasses deny assignments. When creating a deployment stack in the resource group scope, the Bicep file doesn't contain the definition for the resource group. Despite the deny assignment setting, it's possible to delete the resource group and its contained stack. However, if a lock is active on any resource within the group, the delete operation will fail.
  • What-if isn't available in the preview.
  • A management group-scoped stack is restricted from deploying to another management group. It can only deploy to the management group of the stack itself or to a child subscription.

Create deployment stacks

A deployment stack resource can be created at resource group, subscription, or management group scope. The template passed into a deployment stack defines the resources to be created or updated at the target scope specified for the template deployment.

  • A stack at resource group scope can deploy the template passed-in to the same resource group scope where the deployment stack exists.
  • A stack at subscription scope can deploy the template passed-in to a resource group scope (if specified) or the same subscription scope where the deployment stack exists.
  • A stack at management group scope can deploy the template passed-in to the subscription scope specified.

It's important to note that where a deployment stack exists, so is the deny assignment created with the deny settings capability. For example, by creating a deployment stack at subscription scope that deploys the template to resource group scope and with deny settings mode DenyDelete, you can easily provision managed resources to the specified resource group and block delete attempts to those resources. By using this approach, you also enhance the security of the deployment stack by separating it at the subscription level, as opposed to the resource group level. This separation ensures that the developer teams working with the provisioned resources only have visibility and write access to the resource groups, while the deployment stack remains isolated at a higher level. This minimizes the number of users that can edit a deployment stack and make changes to its deny assignment. For more information, see Protect managed resource against deletion.

The create-stack commands can also be used to update deployment stacks.

To create a deployment stack at the resource group scope:

New-AzResourceGroupDeploymentStack `
  -Name "<deployment-stack-name>" `
  -ResourceGroupName "<resource-group-name>" `
  -TemplateFile "<bicep-file-name>" `
  -DenySettingsMode "none"

To create a deployment stack at the subscription scope:

New-AzSubscriptionDeploymentStack `
  -Name "<deployment-stack-name>" `
  -Location "<location>" `
  -TemplateFile "<bicep-file-name>" `
  -DeploymentResourceGroupName "<resource-group-name>" `
  -DenySettingsMode "none"

The DeploymentResourceGroupName parameter specifies the resource group used to store the managed resources. If the parameter isn't specified, the managed resources are stored in the subscription scope.

To create a deployment stack at the management group scope:

New-AzManagmentGroupDeploymentStack `
  -Name "<deployment-stack-name>" `
  -Location "<location>" `
  -TemplateFile "<bicep-file-name>" `
  -DeploymentSubscriptionId "<subscription-id>" `
  -DenySettingsMode "none"

The deploymentSubscriptionId parameter specifies the subscription used to store the managed resources. If the parameter isn't specified, the managed resources are stored in the management group scope.

List deployment stacks

To list deployment stack resources at the resource group scope:

Get-AzResourceGroupDeploymentStack `
  -ResourceGroupName "<resource-group-name>"

To list deployment stack resources at the subscription scope:

Get-AzSubscriptionDeploymentStack

To list deployment stack resources at the management group scope:

Get-AzManagementGroupDeploymentStack `
  -ManagementGroupId "<management-group-id>"

Update deployment stacks

To update a deployment stack, which may involve adding or deleting a managed resource, you need to make changes to the underlying Bicep files. Once the modifications are made, you have two options to update the deployment stack: run the update command or rerun the create command.

The list of managed resources can be fully controlled through the infrastructure as code (IaC) design pattern.

Use the Set command

To update a deployment stack at the resource group scope:

Set-AzResourceGroupDeploymentStack `
  -Name "<deployment-stack-name>" `
  -ResourceGroupName "<resource-group-name>" `
  -TemplateFile "<bicep-file-name>" `
  -DenySettingsMode "none"

To update a deployment stack at the subscription scope:

Set-AzSubscriptionDeploymentStack `
   -Name "<deployment-stack-name>" `
   -Location "<location>" `
   -TemplateFile "<bicep-file-name>" `
   -DeploymentResourceGroupName "<resource-group-name>" `
  -DenySettingsMode "none"

The DeploymentResourceGroupName parameter specifies the resource group used to store the deployment stack resources. If you don't specify a resource group name, the deployment stack service will create a new resource group for you.

To update a deployment stack at the management group scope:

Set-AzManagmentGroupDeploymentStack `
  -Name "<deployment-stack-name>" `
  -Location "<location>" `
  -TemplateFile "<bicep-file-name>" `
  -DeploymentSubscriptionId "<subscription-id>" `
  -DenySettingsMode "none"

Use the New command

You get a warning similar to the following:

The deployment stack 'myStack' you're trying to create already exists in the current subscription/management group/resource group. Do you want to overwrite it? Detaching: resources, resourceGroups (Y/N)

For more information, see Create deployment stacks.

Control detachment and deletion

A detached resource (or unmanaged resource) refers to a resource that isn't tracked or managed by the deployment stack but still exists within Azure.

To instruct Azure to delete unmanaged resources, update the stack with the create stack command with one of the following delete flags. For more information, see Create deployment stack.

  • DeleteAll: use delete rather than detach for managed resources and resource groups.
  • DeleteResources: use delete rather than detach for managed resources only.
  • DeleteResourceGroups: use delete rather than detach for managed resource groups only. It's invalid to use DeleteResourceGroups by itself. DeleteResourceGroups must be used together with DeleteResources.

For example:

New-AzSubscriptionDeploymentStack `
  -Name "<deployment-stack-name" `
  -TemplateFile "<bicep-file-name>" `
  -DenySettingsMode "none" `
  -DeleteResourceGroups `
  -DeleteResources

Warning

When deleting resource groups with either the DeleteAll or DeleteResourceGroups properties, the managed resource groups and all the resources contained within them will also be deleted.

Delete deployment stacks

If you run the delete commands without the delete flags, the unmanaged resources will be detached but not deleted. To delete the unmanaged resources, use the following switches:

  • DeleteAll: Delete both the resources and the resource groups.
  • DeleteResources: Delete the resources only.
  • DeleteResourceGroups: Delete the resource groups only.

Even if you specify the delete all switch, if there are unmanaged resources within the resource group where the deployment stack is located, both the unmanaged resource and the resource group itself won't be deleted.

To delete deployment stack resources at the resource group scope:

Remove-AzResourceGroupDeploymentStack `
  -name "<deployment-stack-name>" `
  -ResourceGroupName "<resource-group-name>" `
  [-DeleteAll/-DeleteResourceGroups/-DeleteResources]

To delete deployment stack resources at the subscription scope:

Remove-AzSubscriptionDeploymentStack `
  -Name "<deployment-stack-name>" `
  [-DeleteAll/-DeleteResourceGroups/-DeleteResources]

To delete deployment stack resources at the management group scope:

Remove-AzManagementGroupDeploymentStack `
  -Name "<deployment-stack-name>" `
  -ManagementGroupId "<management-group-id>" `
  [-DeleteAll/-DeleteResourceGroups/-DeleteResources]

View managed resources in deployment stack

During public preview, the deployment stack service doesn't yet have an Azure portal graphical user interface (GUI). To view the managed resources inside a deployment stack, use the following Azure Powershell/Azure CLI commands:

To view managed resources at the resource group scope:

(Get-AzResourceGroupDeploymentStack -Name "<deployment-stack-name>" -ResourceGroupName "<resource-group-name>").Resources

To view managed resources at the subscription scope:

(Get-AzSubscriptionDeploymentStack -Name "<deployment-stack-name>").Resources

To view managed resources at the management group scope:

(Get-AzManagementGroupDeploymentStack -Name "<deployment-stack-name>" -ManagementGroupId "<management-group-id>").Resources

Add resources to deployment stack

To add a managed resource, add the resource definition to the underlying Bicep files, and then run the update command or rerun the create command. For more information, see Update deployment stacks.

Delete managed resources from deployment stack

To delete a managed resource, remove the resource definition from the underlying Bicep files, and then run the update command or rerun the create command. For more information, see Update deployment stacks.

Protect managed resources against deletion

When creating a deployment stack, it's possible to assign a specific type of permissions to the managed resources, which prevents their deletion by unauthorized security principals. These settings are referred to as deny settings. You want to store the stack at a parent scope.

The Azure PowerShell includes these parameters to customize the deny assignment:

  • DenySettingsMode: Defines the operations that are prohibited on the managed resources to safeguard against unauthorized security principals attempting to delete or update them. This restriction applies to everyone unless explicitly granted access. The values include: None, DenyDelete, and DenyWriteAndDelete.
  • DenySettingsApplyToChildScopes: Deny settings are applied to nested resources under managed resources.
  • DenySettingsExcludedAction: List of role-based management operations that are excluded from the deny settings. Up to 200 actions are permitted.
  • DenySettingsExcludedPrincipal: List of Microsoft Entra principal IDs excluded from the lock. Up to five principals are permitted.

To apply deny settings at the resource group scope:

New-AzResourceGroupDeploymentStack `
  -Name "<deployment-stack-name>" `
  -ResourceGroupName "<resource-group-name>" `
  -TemplateFile "<bicep-file-name>" `
  -DenySettingsMode "DenyDelete" `
  -DenySettingsExcludedAction "Microsoft.Compute/virtualMachines/write Microsoft.StorageAccounts/delete" `
  -DenySettingsExcludedPrincipal "<object-id>,<object-id>"

To apply deny settings at the subscription scope:

New-AzSubscriptionDeploymentStack `
  -Name "<deployment-stack-name>" `
  -Location "<location>" `
  -TemplateFile "<bicep-file-name>" `
  -DenySettingsMode "DenyDelete" `
  -DenySettingsExcludedAction "Microsoft.Compute/virtualMachines/write Microsoft.StorageAccounts/delete" `
  -DenySettingsExcludedPrincipal "<object-id>,<object-id>"

Use the DeploymentResourceGroupName parameter to specify the resource group name at which the deployment stack is created. If a scope isn't specified, it uses the scope of the deployment stack.

To apply deny settings at the management group scope:

New-AzManagmentGroupDeploymentStack `
  -Name "<deployment-stack-name>" `
  -Location "<location>" `
  -TemplateFile "<bicep-file-name>" `
  -DenySettingsMode "DenyDelete" `
  -DenySettingsExcludedActions "Microsoft.Compute/virtualMachines/write Microsoft.StorageAccounts/delete" `
  -DenySettingsExcludedPrincipal "<object-id>,<object-id>"

Use the DeploymentSubscriptionId parameter to specify the subscription ID at which the deployment stack is created. If a scope isn't specified, it uses the scope of the deployment stack.

Detach managed resources from deployment stack

By default, deployment stacks detach and don't delete unmanaged resources when they're no longer contained within the stack's management scope. For more information, see Update deployment stacks.

Export templates from deployment stacks

You can export the resources from a deployment stack to a JSON output. You can pipe the output to a file.

To export a deployment stack at the resource group scope:

Save-AzResourceGroupDeploymentStack `
   -Name '<deployment-stack-name>' `
   -ResourceGroupName '<resource-group-name>' `

To export a deployment stack at the subscription scope:

Save-AzSubscriptionDeploymentStack `
  -name '<deployment-stack-name>'

To export a deployment stack at the management group scope:

Save-AzManagmentGroupDeploymentStack `
  -Name '<deployment-stack-name>' `
  -ManagementGroupId '<management-group-id>'

Next steps

To go through a quickstart, see Quickstart: create a deployment stack.