Insight Tech APAC Blog Logo

Addressing configuration drift using Deployment Stacks

stephentulp
December 20, 2023

11 minutes to read

Bicep Advent Calendar

Introduction

In this blog post, we will look at one of the things that I am looking forward to in 2024, a preview capability called deployment stacks, which is the next evolution of Azure Blueprints that never really took off and was preview for way too long before it was eventually deprecated. Deployment Stacks isn’t specifically related to Bicep but can also be used for ARM JSON templates and Template Specs as well.

Deployment stacks are currently in preview and are not recommended for production use at this stage.

What are Deployment Stacks?

Deployment stacks are a type of Azure resource that allow you to manage a set of Azure resources as a single 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.

Why use deployment stacks?

Deployment stacks provide the following benefits:

  • Simplify provisioning and management of resources across different scopes, that are treated like a single deployment.
  • 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 the deployment stacks.
  • Define specific actions and Service Principals that can perform changes on the stack.
  • Management group scoped deployment stacks to deploy resources to multiple subscriptions.

Known issues

As this is a preview features there are some known issues and limitations, for me a lot of these are the main reason I haven’t invested more time in deployment stacks at this stage because my main uses cases touches one or more of these.

  • Deleting resource groups currently bypasses deny assignments.
  • Implicitly created resources aren’t managed by the stack. Therefore, no deny assignments or cleanup is possible.
  • THe What-if command isn’t available yet.
  • Management group scoped deployment stacks can only deploy the template to subscription.
  • If you create or modify a deployment stack in the Azure portal, deny settings will be overwritten.
  • Management group deployment stacks are not yet available in the Azure portal.

Deployment Stack Examples

We will look at a few of the common scenarios that you might use deployment stacks for.

  • Create a deployment stack
  • List a deployment stack
  • Update a deployment stack
  • Delete a deployment stack

We have an example Main.bicep file that we will use for the examples below.

param location string = resourceGroup().location
param storageAccountName string = 'store${uniqueString(resourceGroup().id)}'
param vnetName string = 'vnet${uniqueString(resourceGroup().id)}'

resource storageAccount 'Microsoft.Storage/storageAccounts@2022-09-01' = {
  name: storageAccountName
  location: location
  kind: 'StorageV2'
  sku: {
    name: 'Standard_LRS'
  }
}

resource virtualNetwork 'Microsoft.Network/virtualNetworks@2022-11-01' = {
  name: vnetName
  location: location
  properties: {
    addressSpace: {
      addressPrefixes: [
        '10.0.0.0/16'
      ]
    }
    subnets: [
      {
        name: 'Subnet-1'
        properties: {
          addressPrefix: '10.0.0.0/24'
        }
      }
    ]
  }
}

Create a deployment stack

Deployment Stacks can be created at the Resource Group, Subscription or Management Group level. In this example, we will create a deployment stack at the Resource Group level.

New-AzResourceGroupDeploymentStack `
  -Name "exampleStack" `
  -ResourceGroupName "exampleRg" `
  -TemplateFile "./main.bicep" `
  -DenySettingsMode "none"

The DenySettingsMode switch assigns a specific type of permissions to the managed resources, which prevents their deletion by unauthorized security principals

List a deployment stack

To list the example deployed deployment stack:

Get-AzResourceGroupDeploymentStack `
  -ResourceGroupName "exampleRg" `
  -Name "exampleStack"

The output shows two managed resources - one storage account and one virtual network:

Id                          : /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/exampleRg/providers/Microsoft.Resources/deploymentStacks/exampleStack
Name                        : exampleStack
ProvisioningState           : succeeded
ResourcesCleanupAction      : detach
ResourceGroupsCleanupAction : detach
DenySettingsMode            : none
CreationTime(UTC)           : 18/12/2023 8:55:48 PM
DeploymentId                : /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/exampleRg/providers/Microsoft.Resources/deployments/exampleStack-2023-18-12-20-55-48-38d09
Resources                   : /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/exampleRg/providers/Microsoft.Network/virtualNetworks/vnetzu6pnx54hqubm
                              /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/exampleRg/providers/Microsoft.Storage/storageAccounts/storezu6pnx54hqubm

Update a deployment stack

To update a deployment stack, make the necessary modifications to the underlying Bicep file, and then rerun the command for creating the deployment stack or use the set command in Azure PowerShell.

  • Update a property of a managed resource.
  • Add a resource to the stack.
  • Detach a managed resource.
  • Attach an existing resource to the stack.
  • Delete a managed resource.

Update a property of a managed resource

We will now update the Sku to be Standard_GRS instead of Standard_LRS for the storage account.

resource storageAccount 'Microsoft.Storage/storageAccounts@2022-09-01' = {
  name: storageAccountName
  location: location
  kind: 'StorageV2'
  sku: {
    name: 'Standard_GRS'
  }
}

We can update the deployment stack.

Set-AzResourceGroupDeploymentStack `
  -Name "exampleStack" `
  -ResourceGroupName "exampleRg" `
  -TemplateFile "./main.bicep" `
  -DenySettingsMode "none"

If we list the stack now we should see the changes.

Add a resource to the stack

We are going to add another storage account to the stack.

resource storageAccount1 'Microsoft.Storage/storageAccounts@2022-09-01' = {
  name: '1${storageAccountName}'
  location: location
  kind: 'StorageV2'
  sku: {
    name: 'Standard_LRS'
  }
}

We can update the deployment stack.

Set-AzResourceGroupDeploymentStack `
  -Name "exampleStack" `
  -ResourceGroupName "exampleRg" `
  -TemplateFile "./main.bicep" `
  -DenySettingsMode "none"

If we list the stack now we should see the changes. This will include the new storage account in addition to the two existing resources.

(Get-AzResourceGroupDeploymentStack -Name "exampleStack" -ResourceGroupName "exampleRg").Resources

Detach a managed resource

Now lets remove that StorageAccount1 resource from the stack. Remove the resource from the Bicep file and then update the deployment stack.

Set-AzResourceGroupDeploymentStack `
  -Name "exampleStack" `
  -ResourceGroupName "exampleRg" `
  -TemplateFile "./main.bicep" `
  -DenySettingsMode "none"

If we verify the stack again we should see that the StorageAccount1 resource has been detached but not deleted.

(Get-AzResourceGroupDeploymentStack -Name "exampleStack" -ResourceGroupName "exampleRg").Resources

There are 2 managed resources in the stack. However, the detached resource is still listed in the resource group. We can see all the resources in the resource group by running the following command, which shows 3 resources, even though there are only 2 in the stack.

Get-azResource -ResourceGroupName "exampleRg"

Attach an existing resource to the stack

We now have one stack with 2 managed resources and one detached resource. We can attach the detached resource back into the stack, we need to add the storage account back into the Bicep file and then update the deployment stack.

Set-AzResourceGroupDeploymentStack `
  -Name "exampleStack" `
  -ResourceGroupName "exampleRg" `
  -TemplateFile "./main.bicep" `
  -DenySettingsMode "none"

If we verify the stack again we should see that the StorageAccount1 resource has been added back into the stack.

(Get-AzResourceGroupDeploymentStack -Name "exampleStack" -ResourceGroupName "exampleRg").Resources

Delete a managed resource

We can delete a managed resource from the stack by removing it from the Bicep file and then updating the deployment stack. We will know remove the storageAccount1 resource from the Bicep file.

Set-AzResourceGroupDeploymentStack `
  -Name "exampleStack" `
  -ResourceGroupName "exampleRg" `
  -TemplateFile "./main.bicep" `
  -DenySettingsMode "none" `
  -DeleteResources

We have used the DeleteResources switch, there are two other switches available: DeleteAll and DeleteResourceGroups. Details on the switches are below:

  • DeleteResources - Deletes all resources that are not managed by the stack.
  • DeleteAll - Deletes all resources in the stack, including those that are not managed by the stack.
  • DeleteResourceGroups - Deletes all resources in the stack and the resource group.

Configure deny settings

When creating a deployment stack, it is possible to assign a specific type of permissions to the managed resources, which prevents their deletion by unauthorized security principals. These settings are refereed as deny settings.

  • 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 inherited by child Azure management scopes.
  • DenySettingsExcludedActions: List of RBAC operations that are excluded from the deny settings. You can have up to 200 actions per stack.
  • DenySettingsExcludedPrincipals: List of Microsoft Entra Service Principals that are excluded from the lock. You can have up to five principals per stack.

Conclusion

Whilst still in early days and not recommended for production use, I am looking forward to seeing how deployment stacks evolve and how they can be used to manage resources in Azure. I am hoping that they will be able to be used to manage resources in Azure in a similar way to how Terraform does things and be able to manage configuration drift of infrastructure deployments.

Further Reading