Detach and delete resources from a deployment stack

Completed

Changes continue with the deposits application. The team decided to remove resources from the application. Some of the resources need to continue to exist in Azure, while others are safe to delete. You need to learn more about how Azure handles resources that a deployment stack no longer manages.

In this unit, you learn how to use control how Azure handles resources detached from a deployment stack using the action on unmanage parameter.

Note

The commands in this unit are shown to illustrate concepts. Don't run the commands yet. You'll practice what you learn here soon.

Action on unmanage revisited

With deployment stacks, the action on unmanage parameter is used to control how Azure handles detached resources, resource groups, and management groups. You can set the action on unmanage parameter when creating, modifying, or deleting a deployment stack. All three operations have the ability to set the behavior of the action on unmanage parameter. Keep in mind that the value set last takes precedence.

There are three possible values for the --action-on-unmanage parameter:

  • deleteAll - deletes resources, resource groups, and management groups
  • deleteResources - deletes resources, but detaches resource groups and management groups
  • detachAll - detaches all resources, resource groups, and management groups

There are three possible values for the -ActionOnUnmanage parameter:

  • DeleteAll - deletes resources, resource groups, and management groups
  • DeleteResources - deletes resources, but detaches resource groups and management groups
  • DetachAll - detaches all resources, resource groups, and management groups

Detach a managed resource

A detached resource, also known as an unmanaged resource, is a resource that is no longer tracked or managed by a deployment stack, but the resource still exists in Azure. The default behavior of deployment stacks is to detach resources rather than delete. For example, you might need to keep the resource so that you can use it in another deployment stack in the future, or you might need to manually verify that its data is safe to delete.

There are two values of the action on unmanage parameter that set resources, resource groups, and management groups to detach when the deployment stack no longer manages them.

Deployment stacks can't 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.

  • deleteResources - deletes resources, but detaches resource groups and management groups
  • detachAll - detaches all resources, resource groups, and management groups

Using either deleteResources or detachAll when creating, modifying, or deleting your deployment stacks offers some additional protection against accidental deletion. Consider our scenario from the last unit. We added an existing Log Analytics workspace to our deployment stack. The workspace is used by other applications, not just the deposits application. It needs to persist past the life of the application. By using detachAll as the action on unmanage parameter, needed resources continue to exist in Azure.

  • DeleteResources - deletes resources, but detaches resource groups and management groups
  • DetachAll - detaches all resources, resource groups, and management groups

Using either DeleteResources or DetachAll when creating, modifying, or deleting your deployment stacks offers some additional protection against accidental deletion. Consider our scenario from the last unit. We added an existing Log Analytics workspace to our deployment stack. The workspace is used by other applications, not just the deposits application. It needs to persist past the life of the application. By using DetachAll as the action on unmanage parameter, needed resources continue to exist in Azure.

Let's consider our Bicep file from the last unit. The template file defines an app service plan, a web app, an Azure SQL server and database, a Log Analytics workspace, and an Application Insights instance. Let's say that we need to remove the Log Analytics workspace and Application Insights instance we created in the last unit. We edit our Bicep file, removing the highlighted code that references our web app.

// Parameters
@description('The location for all resources.')
param location string = 'eastus'

@description('The name of the SQL database.')
param sqlDatabaseName string = 'sqldb-${uniqueString(resourceGroup().id)}'

@description('The password of the admin user.')
param sqlServerAdminUserName string

@description('The name of the admin user.')
@secure()
param sqlServerAdminPassword string

@description('The name of the SQL server.')
param sqlServerName string = 'sql-${uniqueString(resourceGroup().id)}'

@description('The name of the web application.')
param webApplicationName string = 'webapp-${uniqueString(resourceGroup().id)}'

// Variables
@description('The name of the Application Insights instance.')
var applicationInsightsName = 'appinsights-deposits'

@description('The name of the app service plan.')
var appServicePlanName = 'plan-deposits'

@description('The name of the Log Analytics Workspace.')
var logAnalyticsWorkspaceName = 'log-deposits'

// Resource - App Service Plan
resource appServicePlan 'Microsoft.Web/serverfarms@2023-12-01' = {
  name: appServicePlanName
  location: location
  sku: {
    name: 'S1'
    capacity: 1
  }
}

// Resource - Web App
resource webApplication 'Microsoft.Web/sites@2023-12-01' = {
  name: webApplicationName
  location: location
  properties: {
    serverFarmId: appServicePlan.id
    siteConfig: {
      appSettings: [
        {
          name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
          value: applicationInsights.properties.InstrumentationKey
        }     
      ]
    }    
  }
}

// Resource - SQL Server
resource sqlServer 'Microsoft.Sql/servers@2021-11-01' ={
  name: sqlServerName
  location: location
  properties: {
    administratorLogin: sqlServerAdminUserName
    administratorLoginPassword: sqlServerAdminPassword
  }
}

// Resource - SQL Database
resource sqlServerDatabase 'Microsoft.Sql/servers/databases@2021-11-01' = {
  parent: sqlServer
  name: sqlDatabaseName
  location: location
  sku: {
    name: 'Standard'
    tier: 'Standard'
  }
}

// Resource - Log Analytics Workspace
resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2023-09-01' = {
  name: logAnalyticsWorkspaceName
  location: location
  properties: {
    retentionInDays: 30
    sku: {
      name: 'PerGB2018'
    }
  }
}

// Resource - Application Insights
resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = {
  name: applicationInsightsName
  location: location
  kind: 'web'
  properties: {
    Application_Type: 'web'
    WorkspaceResourceId: logAnalyticsWorkspace.id
  }
}

To apply the changes, we need to update the deployment stack. To update a deployment stack using Azure CLI, use the az stack group create command.

az stack group create \
    --name stack-deposits \
    --resource-group rg-depositsApplication \
    --template-file ./main.bicep \
    --action-on-unmanage detachAll \
    --deny-settings-mode none

After the update operation is complete, the deployment stack no longer manages the Log Analytics workspace and Application Insights instance. In our command, we used --action-on-unmanage detachAll to specify how Azure handles resources that a deployment stack no longer manages. In this case, the resources are detached from the deployment stack, but they still exist in the resource group.

To apply the changes, we need to update the deployment stack. To update a deployment stack using Azure PowerShell, use the Set-AzResourceGroupDeploymentStack command.

Set-AzResourceGroupDeploymentStack `
    -Name stack-deposits `
    -ResourceGroupName rg-depositsApplication `
    -TemplateFile ./main.bicep `
    -ActionOnUnmanage DetachAll `
    -DenySettingsMode None

After the update operation is complete, the deployment stack no longer manages the Log Analytics workspace and Application Insights instance. In our command, we used -ActionOnUnmanage DetachAll to specify how Azure handles resources that a deployment stack no longer manages. In this case, the resources are detached from the deployment stack, but they still exist in the resource group.

Delete a managed resource

Deployment stacks provide for reliable resource cleanup. When you update or delete a deployment stack, you can also delete the managed resources, resource groups, and management groups. There are two values of the action on unmanage parameter that set resources, resource groups, and management groups to delete when the deployment stack no longer manages them.

  • deleteAll - deletes resources, resource groups, and management groups
  • deleteResources - deletes resources, but detaches resource groups and management groups

Consider our deposits application. Let's say that the development team decides to use an Azure Database for PostgreSQL instead of Azure SQL Database. We need to first update our deployment stack to remove and fully delete the Azure SQL server and database from Azure. To accomplish this behavior, use the deleteAll or deleteResources action on unmanage parameter when updating or deleting the deployment stack.

  • DeleteAll - deletes resources, resource groups, and management groups
  • DeleteResources - deletes resources, but detaches resource groups and management groups

Consider our deposits application. Let's say that the development team decides to use an Azure Database for PostgreSQL instead of Azure SQL Database. We need to first update our deployment stack to remove and fully delete the Azure SQL server and database from Azure. To accomplish this behavior, use the DeleteAll or DeleteResources action on unmanage parameter when updating or deleting the deployment stack.

Let's consider our Bicep file from the previous section. The template file defines an app service plan, a web app, and an Azure SQL server and database. Let's say that we need to remove the Azure SQL server and database. We edit our Bicep file, removing the highlighted code that references our web app.

// Parameters
@description('The location for all resources.')
param location string = 'eastus'

@description('The name of the SQL database.')
param sqlDatabaseName string = 'sqldb-${uniqueString(resourceGroup().id)}'

@description('The password of the admin user.')
param sqlServerAdminUserName string

@description('The name of the admin user.')
@secure()
param sqlServerAdminPassword string

@description('The name of the SQL server.')
param sqlServerName string = 'sql-${uniqueString(resourceGroup().id)}'

@description('The name of the web application.')
param webApplicationName string = 'webapp-${uniqueString(resourceGroup().id)}'

// Variables
@description('The name of the app service plan.')
var appServicePlanName = 'plan-deposits'

// Resource - App Service Plan
resource appServicePlan 'Microsoft.Web/serverfarms@2023-12-01' = {
  name: appServicePlanName
  location: location
  sku: {
    name: 'F1'
    capacity: 1
  }
}

// Resource - Web App
resource webApplication 'Microsoft.Web/sites@2023-12-01' = {
  name: webApplicationName
  location: location
  properties: {
    serverFarmId: appServicePlan.id  
  }
}

// Resource - SQL Server
resource sqlServer 'Microsoft.Sql/servers@2021-11-01' ={
  name: sqlServerName
  location: location
  properties: {
    administratorLogin: sqlServerAdminUserName
    administratorLoginPassword: sqlServerAdminPassword
  }
}

// Resource - SQL Database
resource sqlServerDatabase 'Microsoft.Sql/servers/databases@2021-11-01' = {
  parent: sqlServer
  name: sqlDatabaseName
  location: location
  sku: {
    name: 'Standard'
    tier: 'Standard'
  }
}

We're left with the following code in our file:

// Parameters
@description('The location for all resources.')
param location string = 'eastus'

@description('The name of the web application.')
param webApplicationName string = 'webapp-${uniqueString(resourceGroup().id)}'

// Variables
@description('The name of the app service plan.')
var appServicePlanName = 'plan-deposits'

// Resource - App Service Plan
resource appServicePlan 'Microsoft.Web/serverfarms@2023-12-01' = {
  name: appServicePlanName
  location: location
  sku: {
    name: 'F1'
    capacity: 1
  }
}

// Resource - Web App
resource webApplication 'Microsoft.Web/sites@2023-12-01' = {
  name: webApplicationName
  location: location
  properties: {
    serverFarmId: appServicePlan.id  
  }
}

To apply the changes, we need to update the deployment stack. To update a deployment stack using Azure CLI, use the az stack group create command. This time, we use --action-on-unmanage deleteAll instead of --action-on-unmanage detachAll

az stack group create \
    --name stack-deposits \
    --resource-group rg-depositsApplication \
    --template-file ./main.bicep \
    --action-on-unmanage deleteAll \
    --deny-settings-mode none

After the update operation is complete, the only resources that remain are the app service plan and web app. In our command, we used --action-on-unmanage deleteAll to specify how Azure handles resources that the deployment stack no longer manages. In this case, the resources are deleted from the deployment stack and from Azure.

To apply the changes, we need to update the deployment stack. To update a deployment stack using Azure PowerShell, use the Set-AzResourceGroupDeploymentStack command.

Set-AzResourceGroupDeploymentStack `
    -Name stack-deposits `
    -ResourceGroupName rg-depositsApplication `
    -TemplateFile ./main.bicep `
    -ActionOnUnmanage DeleteAll `
    -DenySettingsMode None

After the update operation is complete, the only resources that remain are the app service plan and web app. In our command, we used -ActionOnUnmanage DeleteAll to specify how Azure handles resources that the deployment stack no longer manages. In this case, the resources are deleted from the deployment stack and from Azure.