Azure infrastructure lifecycle management through deployment stacks
Deployment stacks (Preview)

Azure infrastructure lifecycle management through deployment stacks

What are deployment stacks?

Azure infrastructure engineers, who had used Terraform, was always having complain related to lifecycle management of Azure infrastructure deployment using bicep or ARM template. There were no such cloud native way, through which you could maintain a group of deployments as well as tearing down the infra on-demand basis. Here comes the awesome deployment stack, which gives you the liberty to control the lifecycle of different resources with ease under the same umbrella like resource group.

A deployment stack is a native Azure resource type that enables you to perform operations on a resource collection as an atomic unit. Deployment stacks are defined in ARM as the type Microsoft.Resources/deploymentStacks.

Because the deployment stack is a native Azure resource, you can perform all typical Azure Resource Manager (ARM) operations on the resource.

Azure resources created using a deployment stack are managed by the deployment stack itself, which means that to update a resources, you update the deployment stack rather that individual resources, and clean up the environment (resources) can (should) also be done by updating the deployment stack.

Note: This feature is still in public preview, but without a doubt, it will be a popular one.


Microsoft Documentation

Its highly recommended to go through the Microsoft Documentation before staring your journey. Refer the documentation.


Document Purpose

In this small documentation, we'll try to figure out how to leverage deployment stack with Bicep as well as ARM Templates.


Document Scope

Deployment stack can be used through Portal, Azure CLI & Azure PowerShell. In this document we'll focus on the PowerShell part.

You can easily get all the commands available till date using the below cmdlet

Get-Command -Module 'Az.Resources' | Where-Object { $_.Name.tolower().contains('deploymentstack') }        
Article content
Available cmdlets till August end



Working through Bicep

  • Create a Bicep file

Let's create a Bicep file same as documentation and name it as main.bicep


param resourceGroupLocation string = resourceGroup().locatio
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: resourceGroupLocation
  kind: 'StorageV2'
  sku: {
    name: 'Standard_LRS'
  }
}


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

  • Create a Deployment Stack

New-AzResourceGroupDeploymentStack 
  -Name "demoDeploymentStack" `
  -ResourceGroupName "rg-deployment-stack" `
  -TemplateFile "./main.bicep" `
  -DenySettingsMode "none"`
  -Verbose         

Deployment stack should be created in portal

No alt text provided for this image
No alt text provided for this image
Resources managed under deployment stack

To verify the deployment you can easily use the below command

  Get-AzResourceGroupDeploymentStack 
  -ResourceGroupName "rg-deployment-stack" `
  -Name "demoDeploymentStack"`        

  • Deployment Stack creation with parameterized bicep file

Let's modify the bicep file to have resource group location as input parameter

param resourceGroupLocation string 
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: resourceGroupLocation
  kind: 'StorageV2'
  sku: {
    name: 'Standard_LRS'
  }
}

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

we can easily pass the parameter object like we use for normal deployment cmdlets

New-AzResourceGroupDeploymentStack `
   -Name "demoDeploymentStack" `
   -ResourceGroupName "rg-deployment-stack" `
   -TemplateFile ".\main.bicep" `
   -DenySettingsMode "none" -Verbose -TemplateParameterObject @{ resourceGroupLocation = 'eastus' }        

  • Update the deployment stack

Let's update the main.bicep with one more subnet

param resourceGroupLocation string = resourceGroup().locatio
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: resourceGroupLocation
  kind: 'StorageV2'
  sku: {
    name: 'Standard_LRS'
  }
}


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

Cmdlet to update the deployment stack

 Set-AzResourceGroupDeploymentStack 
  -Name "demoDeploymentStack" `
  -ResourceGroupName "rg-deployment-stack" `
  -TemplateFile ".\main.bicep" `
  -DenySettingsMode "none" -Verbose`        
No alt text provided for this image
New subnet has been created

  • Remove the deployment stack

If you only want to detach the resources and remove the deployment stack, run the below command

Remove-AzResourceGroupDeploymentStack 
  -Name "demoDeploymentStack" `
  -ResourceGroupName "rg-deployment-stack"`        

If you want to delete the deployment stack as well as the resources, use the below command

Remove-AzResourceGroupDeploymentStack 
  -Name "demoDeploymentStack" `
  -ResourceGroupName "rg-deployment-stack" `
  -DeleteResources`        

The following parameters can be used to control between detach and delete.

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


Before moving to the usage of ARM template, let's have couple of experiments

  • Experiment 1# What happens if we remove few components from the backend template and update the Deployment Stack

Lets remove the storage account from our bicep template and update the deployment stack

param resourceGroupLocation string = resourceGroup().location

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



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

Update the deployment stack now

 Set-AzResourceGroupDeploymentStack `
  -Name "demoDeploymentStack" `
  -ResourceGroupName "rg-deployment-stack" `
  -TemplateFile ".\main.bicep" `
  -DenySettingsMode "none" -Verbose        

Let's check the portal

Article content
Managed and detached resources are automatically segregated

  • Experiment 2# What happens if we make some manual changes (Not Recommended) in portal and update the Deployment StackLet's use our base Bicep template and deploy the storage account.

param resourceGroupLocation 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: resourceGroupLocation
  kind: 'StorageV2'
  sku: {
    name: 'Standard_LRS'
  }
}

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

New-AzResourceGroupDeploymentStack `
  -Name "demoDeploymentStack" `
  -ResourceGroupName "rg-deployment-stack" `
  -TemplateFile ".\main.bicep" `
  -DenySettingsMode "none" -Verbose        

let's do some modification now in the storage account properties and update the deployment stack to check whether or not deployment stack is reverting the changes.

Article content
Modified the blob anonymous access level and the minimum TLS version to be used

Lets update the deployment stack with previous Bicep file and check what happens to the manual changes.

Interestingly, no changes are made to the properties updated manually.


Working through ARM Template

Deployment stack using ARM template can only be done through Template Specs, don't worry if you are new in Template Specs, let's discuss the template specs in brief.

  • What is Azure Resource Manager template specs

Azure Template Specs take the power of Azure Resource Manager templates a step further by allowing you to package and version your templates along with other artifacts, such as parameter files and policies, into a single shareable and reusable entity. Think of it as a comprehensive blueprint that encapsulates not only the infrastructure definition but also any associated scripts, extensions, and documentation required for a successful deployment.

  • Create template spec with a single ARM Template

Let's create a simple ARM template and store it as singleTemplate.json

{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "storageAccountType": {
      "type": "string",
      "defaultValue": "Standard_LRS",
      "allowedValues": [
        "Standard_LRS",
        "Standard_GRS",
        "Standard_ZRS",
        "Premium_LRS"
      ]
    }
  },
  "resources": [
    {
      "type": "Microsoft.Storage/storageAccounts",
      "apiVersion": "2019-06-01",
      "name": "[concat('sa', uniquestring(resourceGroup().id))]",
      "location": "[resourceGroup().location]",
      "kind": "StorageV2",
      "sku": {
        "name": "[parameters('storageAccountType')]"
      }
    }
  ]
}        

Through Azure PowerShell the template specs can be deployed

New-AzTemplateSpec `
   -Name TemplateSpecSingleARM `
   -Version "1.0" `
   -ResourceGroupName 'rg-deployment-stack' `
   -Location eastus `
   -TemplateFile '.\singleTemplate.json' -Verbose        

We can check the template specs in the portal

Article content
Sample Template Specs with single ARM Template

  • Create template spec with multiple(linked) ARM Template

Let's first create a master template (lets name it azuredeploy.json) which refers to another ARM Template

{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "location": {
      "type": "string",
      "defaultValue": "westus2",
      "metadata":{
        "description": "Specify the location for the resources."
      }
    },
    "storageAccountType": {
      "type": "string",
      "defaultValue": "Standard_LRS",
      "metadata":{
        "description": "Specify the storage account type."
      }
    }
  },
  "variables": {
    "appServicePlanName": "[format('plan{0}', uniquestring(resourceGroup().id))]"
  },
  "resources": [
    {
      "type": "Microsoft.Web/serverfarms",
      "apiVersion": "2022-09-01",
      "name": "[variables('appServicePlanName')]",
      "location": "[parameters('location')]",
      "sku": {
        "name": "B1",
        "tier": "Basic",
        "size": "B1",
        "family": "B",
        "capacity": 1
      },
      "kind": "linux",
      "properties": {
        "perSiteScaling": false,
        "reserved": true,
        "targetWorkerCount": 0,
        "targetWorkerSizeId": 0
      }
    },
    {
      "type": "Microsoft.Resources/deployments",
      "apiVersion": "2022-09-01",
      "name": "createStorage",
      "properties": {
        "mode": "Incremental",
        "templateLink": {
          "relativePath": "artifacts/linkedTemplate.json"
        },
        "parameters": {
          "storageAccountType": {
            "value": "[parameters('storageAccountType')]"
          }
        }
      }
    }
  ]
}        

In the same location, lets create a new folder named "artifacts" and inside that lets create a simple template (contents mentioned below) named "linkedTemplate.json"

Let's create the Template Specs

New-AzTemplateSpec `
   -Name TemplateSpecMultipleARM `
   -Version "1.0" `
   -ResourceGroupName 'rg-deployment-stack' `
   -Location eastus `
   -TemplateFile '.\azuredeploy.json' -Verbose        

We can check the Template Specs in portal

Article content
In the linked template relative path can be seen

Before moving back to the deployment stack, let's quickly explore the versioning feature of Template Specs.

Lets deploy one more version of the Template Spec using single ARM template

New-AzTemplateSpec `
   -Name TemplateSpecSingleARM `
   -Version "1.1" `
   -ResourceGroupName 'rg-deployment-stack' `
   -Location eastus `
   -TemplateFile '.\singleTemplate.json' -Verbose        

In the portal, we'll be able to see both the versions and Azure gives us opportunity to deploy any versions. By default, latest version will be selected

Article content
BY default, latest version will be selected
Article content
We can deploy any version

Coming back to Deployment Stack, lets try to deploy Deployment Stack using template specs

  • Retrieve Template Specs Info

$templateSpecId = (Get-AzTemplateSpec `
   -Name TemplateSpecMultipleARM `
   -Version "1.0" `
   -ResourceGroupName 'rg-deployment-stack' ).Versions.id        

Let's deploy the Deployment Stack

New-AzResourceGroupDeploymentStack `
  -Name 'demoDeploymentStackTemplateSpec' `
  -ResourceGroupName 'rg-deployment-stack' `
  -TemplateSpecId $templateSpecId `
  -DenySettingsMode none        

Hope you find this blog interesting and exciting.

To view or add a comment, sign in

Others also viewed

Explore content categories