Azure customers find it extremely difficult to manage the lifecycle of a collection of resources – while it’s easy to deploy resources together as a group, after the deployment finishes there is no single way to relate those resources together and manage their lifecycle. Infrastructure deployed in Azure may span across multiple resource groups, subscriptions and even tenants. Deployment Stacks will make it easy to manage the lifecycle of a collection resources that work together to create a solution.
A "deploymentStack" is a grouping concept that allows for lifecycle operations to be performed on the defined group of resources. While it is very similar to the traditional Microsoft.Resources/deployments resource, Microsoft.Resources/deploymentStacks is a reusable resource type that can help you manage the resources your deployment creates. Any resource created using a deploymentStack is managed by it, and subsequent updates to that deploymentStack, combined with the newest iteration's UpdateBehavior
, will allow you to control the lifecycle of the resources managed by the deploymentStack . When a deploymentStack is updated, all previously managed resources are replaced with the resources provisioned by the latest template update. The UpdateBehavior property of the deploymentStack currently supports the following behaviors:
DetachResources
: Remove previously managed resources from the list of the stack's managedResources, but keep them in Azure.PurgeResources
: Remove previously managed resources from the list of the stack's managedResources, and also delete them so that they no longer exist in Azure.
There are the known limitations with the private preview release 2021-05-01-preview
:
- It is not recommended to use deploymentStacks in production environment since it is still in preview stages and can introduce breaking changes in the future.
- Locking the resources managed by the deploymentStack is not available in the private preview. In the future, locking will allow you to prevent changes or deletion to any managed resource.
- What-if is not available in the private preview. What-if allows for evaluating changes before deploying.
- A deploymentStack currently does not manage resourceGroups, subscriptionAliases, or managementGroups that are created by the stack.
- DeploymentStacks are currently limited to resource group or subscription scope for the private preview.
- A deploymentStack does not gurantee the protection of
secureString
andsecureObject
parameters, as this release returns them back when requested. - DeploymentStacks can currently only be created, updated, retrieved, and deleted through PowerShell and the REST API. CLI support is coming soon.
- You cannot currently create deploymentStacks using Bicep but you can use the
bicep build
command to author the template file for a deploymentStack update. -PurgeResources
forRemove-AzSubscriptionDeploymentStack
is experimental and may not cleanup resources properly
Use the following steps to install the deployment stacks PowerShell cmdlets:
-
Install the latest Azure Az PowerShell module. See Install the Azure Az PowerShell module.
-
Open PowerShell 7 window as administrator.
-
Run the following command to set up a bypass for local signing policy.
Set-ExecutionPolicy Bypass -Scope Process
-
Download the deployment stacks package, expand the package and then run the installation ps1 file and follow the instructions.
./AzDeploymentStacksPrivatePreview.ps1
To uninstall the module, run the same ps1 file and choose the Uninstall module option.
-
Set the current subscription context to the subscription for the private preview:
Connect-AzAccount Set-AzContext -subscription "<subscription-id>"
You can use any ARM template to create a deployment stack. The following template is an example. This template creates two storage accounts.
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"namePrefix": {
"type": "string",
"minLength": 3,
"maxLength": 11
},
"location":{
"type": "string",
"defaultValue": "[resourceGroup().location]"
}
},
"variables": {
"storageName1": "[concat(parameters('namePrefix'), uniqueString(resourceGroup().id), 'a')]",
"storageName2": "[concat(parameters('namePrefix'), uniqueString(resourceGroup().id), 'b')]"
},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2019-04-01",
"name": "[variables('storageName1')]",
"location": "[parameters('location')]",
"sku": {
"name": "Standard_LRS"
},
"kind": "StorageV2",
"properties": {}
},
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2019-04-01",
"name": "[variables('storageName2')]",
"location": "[parameters('location')]",
"sku": {
"name": "Standard_LRS"
},
"kind": "StorageV2",
"properties": {}
}
]
}
A parameter file sample:
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"storagePrefix": {
"value": "devstore"
}
}
}
To create a stack:
New-AzResourceGroup `
-Name myRgStackRg `
-Location eastus2
New-AzResourceGroupDeploymentStack `
-Name myRgStack `
-ResourceGroupName myRgStackRg `
-TemplateFile azuredeploy.json `
-ParameterFile azuredeploy.parameters.json
It takes a few moments to create a stack. Once completed, you can run the following cmdlet to get the stack:
Get-AzResourceGroupDeploymentStack `
-ResourceGroupName myRgStackRg `
-Name myRgStack
The output is similar to:
Id : /subscriptions/<sub-id>/resourceGroups/myRgStackRg/providers/Microsoft.Resources/deploymentStacks/myRgStack
Name : myRgStack
ProvisioningState : succeeded
UpdateBehavior : detachResources
CreationTime(UTC) : 8/19/2021 4:43:21 PM
ManagedResources : {'/subscriptions/<sub-id>/resourceGroups/myRgStackRg/providers/Microsoft.Storage/storageAccounts/devstorett73cak7aqhwka',
'/subscriptions/<sub-id>/resourceGroups/myRgStackRg/providers/Microsoft.Storage/storageAccounts/devstorett73cak7aqhwkb'}
DeploymentId : /subscriptions/<sub-id>/resourceGroups/myRgStackRg/providers/Microsoft.Resources/deployments/myRgStack-2021-08-19-16-43-21-174c4
SnapshotId : /subscriptions/<sub-id>/resourceGroups/myRgStackRg/providers/Microsoft.Resources/deploymentStacks/myRgStack/snapshots/2021-08-19-16-43-21-174c4
The two resources are listed under ManagedResources
.
ProvisioningState
shows the status. If the deployment failed, the output shows the error. For example:
Id : /subscriptions/<sub-id>/resourceGroups/myRgStackRg/providers/Microsoft.Resources/deploymentStacks/myRgStack
Name : myRgStack
ProvisioningState : failed
UpdateBehavior : detachResources
CreationTime(UTC) : 8/12/2021 3:33:43 PM
Error : LocationNotAvailableForResourceType - The provided location 'eastus2' is not available for resource type 'Providers.Test/statefulResources'. List of available regions for the resource type is 'westus,westus2,eastus,c
entralus,northcentralus,southcentralus,westcentralus,westeurope,northeurope,eastasia,southeastasia,westindia,southindia,centralindia,canadacentral,canadaeast,uksouth,ukwest,francecentral,australiacentral,centraluseua
p,eastus2euap'.
The following ARM template creates two resource groups with two storage accounts resources in each group:
{
"$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"namePrefix": {
"type": "string",
"minLength": 3,
"maxLength": 11
},
"location": {
"type": "string",
"defaultValue": "[deployment().location]"
}
},
"variables": {
"rgName1": "[concat(parameters('namePrefix'), 'rg1')]",
"rgName2": "[concat(parameters('namePrefix'), 'rg2')]",
"storageNameA": "[concat(parameters('namePrefix'), uniqueString(subscription().id), 'a')]",
"storageNameB": "[concat(parameters('namePrefix'), uniqueString(subscription().id), 'b')]",
"storageNameC": "[concat(parameters('namePrefix'), uniqueString(subscription().id), 'c')]",
"storageNameD": "[concat(parameters('namePrefix'), uniqueString(subscription().id), 'd')]"
},
"resources": [
{
"type": "Microsoft.Resources/resourceGroups",
"apiVersion": "2020-06-01",
"name": "[variables('rgName1')]",
"location": "[parameters('location')]",
"properties": {}
},
{
"type": "Microsoft.Resources/deployments",
"apiVersion": "2020-06-01",
"name": "stackDeployment",
"resourceGroup": "[variables('rgName1')]",
"dependsOn": [
"[resourceId('Microsoft.Resources/resourceGroups/', variables('rgName1'))]"
],
"properties": {
"mode": "Incremental",
"template": {
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {},
"variables": {},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2019-04-01",
"name": "[variables('storageNameA')]",
"location": "[parameters('location')]",
"sku": {
"name": "Standard_LRS"
},
"kind": "StorageV2",
"properties": {}
},
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2019-04-01",
"name": "[variables('storageNameB')]",
"location": "[parameters('location')]",
"sku": {
"name": "Standard_LRS"
},
"kind": "StorageV2",
"properties": {}
}
],
"outputs": {}
}
}
},
{
"type": "Microsoft.Resources/resourceGroups",
"apiVersion": "2020-06-01",
"name": "[variables('rgName2')]",
"location": "[parameters('location')]",
"properties": {}
},
{
"type": "Microsoft.Resources/deployments",
"apiVersion": "2020-06-01",
"name": "stackDeployment",
"resourceGroup": "[variables('rgName2')]",
"dependsOn": [
"[resourceId('Microsoft.Resources/resourceGroups/', variables('rgName2'))]"
],
"properties": {
"mode": "Incremental",
"template": {
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {},
"variables": {},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2019-04-01",
"name": "[variables('storageNameC')]",
"location": "[parameters('location')]",
"sku": {
"name": "Standard_LRS"
},
"kind": "StorageV2",
"properties": {}
},
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2019-04-01",
"name": "[variables('storageNameD')]",
"location": "[parameters('location')]",
"sku": {
"name": "Standard_LRS"
},
"kind": "StorageV2",
"properties": {}
}
],
"outputs": {}
}
}
}
],
"outputs": {}
}
Deploy the stack with the following cmdlets:
New-AzSubscriptionDeploymentStack `
-Name mySubStack `
-Location eastus2 `
-TemplateFile azuredeploy.json `
-ParameterFile azuredeploy.parameters.json
It takes a few moments to create a stack. Once completed, use the following cmdlet to get the stack:
Get-AzSubscriptionDeploymentStack `
-Name mySubStack
The output is similar to:
Id : /subscriptions/<sub-id>/providers/Microsoft.Resources/deploymentStacks/
mySubStack
Name : mySubStack
ProvisioningState : succeeded
UpdateBehavior : detachResources
Location : eastus2
CreationTime(UTC) : 8/19/2021 5:57:28 PM
ManagedResources : {'/subscriptions/<sub-id>/resourceGroups/mySubStackrg1',
'/subscriptions/<sub-id>/resourceGroups/mySubStackrg1/providers/Microsoft.Storage/storageAccounts/stksubiyrmpdcyfhgl6a',
'/subscriptions/a1bfa635-f2bf-42f1-86b5-848c674fc31/resourceGroups/mySubStackrg1/providers/Microsoft.Storage/storageAccounts/stksubiyrmpdcyfhgl6b',
'/subscriptions/<sub-id>/resourceGroups/mySubStackrg2'…}
DeploymentId : /subscriptions/<sub-id>/providers/Microsoft.Resources/deployments/StorageSubStack-2021-08-19-18-22-33-d4216
SnapshotId : /subscriptions/<sub-id>/providers/Microsoft.Resources/deploymentStacks/mySubStack/snapshots/2021-08-19-18-22-33-d4216
The two resource groups and the resources are listed under ManagedResources
.
Modify the original template to remove an existing resource or add a new resource, and then use Set-AzResourceGroupDeploymentStack
or Set-AzSubscriptionDeploymentStack
to update the stack. When you remove a resource from a stack, you have two options with the UpdateBehavior
switch:
- DetachResources: remove the resource from the stack, but keep the resource in Azure.
- PurgeResources: remove the resource from the stack, and remove the resource from Azure.
To use the detachResources option:
Set-AzResourceGroupDeploymentStack `
-Name myRgStack `
-ResourceGroupName myRgStackRg `
-TemplateFile stack.json `
-ParameterFile azuredeploy.parameters.json `
-UpdateBehavior DetachResources
To use the purgeResources option:
Set-AzResourceGroupDeploymentStack `
-Name myRgStack `
-ResourceGroupName myRgStackRg `
-TemplateFile stack.json `
-ParameterFile azuredeploy.parameters.json `
-UpdateBehavior PurgeResources
After updating the stack, use Get-AzResourceGroupDeploymentStack
to list the resources in the stack.
To use the detachResources option:
Set-AzSubscriptionDeploymentStack `
-Name stack `
-TemplateFile azuredeploy.json `
-ParameterFile azuredeploy.parameters.json `
-UpdateBehavior DetachResources `
-Location eastus
To use the purgeResources option:
Set-AzSubscriptionDeploymentStack `
-Name stack `
-TemplateFile azuredeploy.json `
-ParameterFile azuredeploy.parameters.json `
-UpdateBehavior PurgeResources `
-Location eastus
The Location
parameter specifies where the stack is saved.
After updating the stack, use Get-AzSubscriptionDeploymentStack
to list the resources in the stack.
Snapshots provide a way to view the history of updates to the stack. A snapshot is read-only and is created whenever any template resource is successfully deployed. Note that not all resources need to be successfully deployed for a snapshot to be created. Snapshots are primarily used for viewing history for diagnostics or troubleshooting. In the future, a snapshot can be applied to roll back a deploymentStack to a previous state if there is an error.
Since it contains more information about the state of the deploymentStack, the latest snapshot of a stack cannot be deleted.
Use the following cmdlet to list the snapshots of a stack:
Get-AzResourceGroupDeploymentStackSnapshot `
-ResourceGroupName myRgStackRG `
-StackName myRgStack
The following output shows two snapshots:
Id : /subscriptions/<sub-id>/resourceGroups/myRgStackRg/providers/Microsoft.Resources/deploymentStacks/myRgStack/snapshots/2021-08-19-16-43-21-174c4
Name : 2021-08-19-16-43-21-174c4
ProvisioningState : succeeded
UpdateBehavior : detachResources
CreationTime(UTC) : 8/19/2021 4:43:21 PM
ManagedResources : {'/subscriptions/<sub-id>/resourceGroups/myRgStackRg/providers/Microsoft.Storage/storageAccounts/devstorett73cak7aqhwka',
'/subscriptions/<sub-id>/resourceGroups/myRgStackRg/providers/Microsoft.Storage/storageAccounts/devstorett73cak7aqhwkb'}
DeploymentId : /subscriptions/<sub-id>/resourceGroups/myRgStackRg/providers/Microsoft.Resources/deployments/myRgStack-2021-08-19-16-43-21-174c4
Id : /subscriptions/<sub-id>/resourceGroups/myRgStackRg/providers/Microsoft.Resources/deploymentStacks/myRgStack/snapshots/2021-08-19-18-42-15-e871d
Name : 2021-08-19-18-42-15-e871d
ProvisioningState : succeeded
UpdateBehavior : detachResources
CreationTime(UTC) : 8/19/2021 6:42:15 PM
ManagedResources : '/subscriptions/<sub-id>/resourceGroups/myRgStackRg/providers/Microsoft.Storage/storageAccounts/devstorett73cak7aqhwka'
DeploymentId : /subscriptions/<sub-id>/resourceGroups/myRgStackRg/providers/Microsoft.Resources/deployments/myRgStack-2021-08-19-18-42-15-e871d
To remove a snapshot from a stack:
Remove-AzResourceGroupDeploymentStackSnapshot `
-ResourceId /subscriptions/<subscription-id>/resourceGroups/myRgStackRg/providers/Microsoft.Resources/deploymentStacks/myRgStack/snapshots/2021-07-14-16-25-47-71d60
name : 2021-08-19-16-43-21-174c4
Remove-AzResourceGroupDeploymentStackSnapshot `
-Name 2021-08-19-16-43-21-174c4 `
-StackName myRgStack `
-ResourceGroupName myRgStackRg
Use the following cmdlet to list the snapshots of a stack:
Get-AzSubscriptionDeploymentStackSnapshot `
-StackName mySubStack
To remove a snapshot from a stack:
Remove-AzSubscriptionDeploymentStackSnapshot `
-Name 2021-08-19-16-43-21-174c4 `
-StackName myRgStack
Remove a deployment stack also remove the associated snapshots.
Remove-AzResourceGroupDeploymentStack `
-ResourceGroupName myRgStackRG `
-Name myRgStack
If you also want to delete the managed resources of the stack, use the -PurgeResources
switch:
Remove-AzResourceGroupDeploymentStack `
-ResourceGroupName myRgStackRG `
-Name myRgStack `
-PurgeResources
If you delete a resource group that contains a stack, it deletes the stack and also detaches the resources that are contained in other resource groups.
Remove-AzSubscriptionDeploymentStack `
-Name mySubStack
If you also want to delete the managed resources of the stack, use the -PurgeResources
switch:
Remove-AzSubscriptionDeploymentStack `
-Name mySubStack `
-PurgeResources
The Azure PowerShell cmdlet samples provided in this article use local templates. You can also use remote templates and template specs.
To use a remote template, use the -TemplateUri
switch; to use a remote parameter file, use the -ParameterUri
:
New-AzResourceGroupDeploymentStack `
-Name myRgStack `
-ResourceGroupName myRgStackRg `
-TemplateUri https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/quickstarts/microsoft.storage/storage-account-create/azuredeploy.json `
-ParameterUri https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/quickstarts/microsoft.storage/storage-account-create/azuredeploy.parameters.json
To use a template spec, use the -TemplateSpecId
switch:
New-AzResourceGroupDeploymentStack `
-Name myRgStack `
-ResourceGroupName myRgStackRg `
-TemplateSpecId <template-spec-id> `
- For a step-by-step tutorial that guides you through the process of creating a template, see Tutorial: Create and deploy your first ARM template.
- To learn about ARM templates through a guided set of modules on Microsoft Learn, see Deploy and manage resources in Azure by using ARM templates.
- For information about the properties in template files, see Understand the structure and syntax of ARM templates.
- To learn about exporting templates, see Quickstart: Create and deploy ARM templates by using the Azure portal.
- For answers to common questions, see Frequently asked questions about ARM templates.
This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.
When you submit a pull request, a CLA bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA.
This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact opencode@microsoft.com with any additional questions or comments.
This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft trademarks or logos is subject to and must follow Microsoft's Trademark & Brand Guidelines. Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. Any use of third-party trademarks or logos are subject to those third-party's policies.