Claude-skill-registry deployment-stacks-2025
Azure Deployment Stacks GA 2025 features for unified resource management, deny settings, and lifecycle management
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/deployment-stacks-2025" ~/.claude/skills/majiayu000-claude-skill-registry-deployment-stacks-2025 && rm -rf "$T"
skills/data/deployment-stacks-2025/SKILL.mdAzure Deployment Stacks - 2025 GA Features
Complete knowledge base for Azure Deployment Stacks, the successor to Azure Blueprints (GA 2024, best practices 2025).
Overview
Azure Deployment Stacks is a resource type for managing a collection of Azure resources as a single, atomic unit. It provides unified lifecycle management, resource protection, and automatic cleanup capabilities.
Key Features
1. Unified Resource Management
- Manage multiple resources as a single entity
- Update, export, and delete operations on the entire stack
- Track all managed resources in one place
- Consistent deployment across environments
2. Deny Settings (Resource Protection)
Prevent unauthorized modifications to managed resources:
- None: No restrictions (default)
- DenyDelete: Prevent resource deletion
- DenyWriteAndDelete: Prevent updates and deletions
3. ActionOnUnmanage (Cleanup Policies)
Control what happens to resources no longer in template:
- detachAll: Remove from stack management, keep resources
- deleteAll: Delete resources not in template
- deleteResources: Delete unmanaged resources, keep resource groups
4. Scope Flexibility
Deploy stacks at:
- Resource group scope
- Subscription scope
- Management group scope
5. Replaces Azure Blueprints
Azure Blueprints will be deprecated in July 2026. Deployment Stacks is the recommended replacement.
Prerequisites
Azure CLI Version
# Requires Azure CLI 2.61.0 or later az version # Upgrade if needed az upgrade
Azure PowerShell Version
# Requires Azure PowerShell 12.0.0 or later Get-InstalledModule -Name Az Update-Module -Name Az
Creating Deployment Stacks
Subscription Scope Stack
# Create deployment stack at subscription level az stack sub create \ --name MyProductionStack \ --location eastus \ --template-file main.bicep \ --parameters @parameters.json \ --deny-settings-mode DenyWriteAndDelete \ --deny-settings-excluded-principals <devops-service-principal-id> <admin-group-id> \ --action-on-unmanage deleteAll \ --description "Production infrastructure managed by deployment stack" \ --tags Environment=Production ManagedBy=DeploymentStack CostCenter=Engineering # What-if analysis before deployment az stack sub what-if \ --name MyProductionStack \ --location eastus \ --template-file main.bicep \ --parameters @parameters.json # Create with confirmation prompt disabled az stack sub create \ --name MyDevStack \ --location eastus \ --template-file main.bicep \ --deny-settings-mode None \ --action-on-unmanage detachAll \ --yes
Resource Group Scope Stack
# Create resource group az group create \ --name MyRG \ --location eastus \ --tags Environment=Production # Create deployment stack az stack group create \ --name MyAppStack \ --resource-group MyRG \ --template-file main.bicep \ --parameters environment=production \ --deny-settings-mode DenyDelete \ --action-on-unmanage deleteAll \ --description "Application infrastructure stack"
Management Group Scope Stack
# Create stack at management group level az stack mg create \ --name MyEnterpriseStack \ --management-group-id MyMgmtGroup \ --location eastus \ --template-file main.bicep \ --deny-settings-mode DenyWriteAndDelete \ --action-on-unmanage detachAll
Bicep Template for Deployment Stack
Production Stack Template
// main.bicep targetScope = 'subscription' @description('Environment name') @allowed([ 'dev' 'staging' 'production' ]) param environment string = 'production' @description('Primary location') param location string = 'eastus' @description('Secondary location for geo-replication') param secondaryLocation string = 'westus' // Resource naming var namingPrefix = 'myapp-${environment}' // Resource Group for core infrastructure resource coreRG 'Microsoft.Resources/resourceGroups@2024-03-01' = { name: '${namingPrefix}-core-rg' location: location tags: { Environment: environment ManagedBy: 'DeploymentStack' Purpose: 'Core Infrastructure' } } // Resource Group for data services resource dataRG 'Microsoft.Resources/resourceGroups@2024-03-01' = { name: '${namingPrefix}-data-rg' location: location tags: { Environment: environment ManagedBy: 'DeploymentStack' Purpose: 'Data Services' } } // Log Analytics Workspace module logAnalytics 'modules/log-analytics.bicep' = { name: 'logAnalyticsDeploy' scope: coreRG params: { name: '${namingPrefix}-logs' location: location retentionInDays: environment == 'production' ? 90 : 30 } } // AKS Automatic Cluster module aksCluster 'modules/aks-automatic.bicep' = { name: 'aksClusterDeploy' scope: coreRG params: { name: '${namingPrefix}-aks' location: location kubernetesVersion: '1.34' workspaceId: logAnalytics.outputs.workspaceId enableZoneRedundancy: environment == 'production' } } // Container Apps Environment module containerEnv 'modules/container-env.bicep' = { name: 'containerEnvDeploy' scope: coreRG params: { name: '${namingPrefix}-containerenv' location: location workspaceId: logAnalytics.outputs.workspaceId zoneRedundant: environment == 'production' } } // Azure OpenAI module openAI 'modules/openai.bicep' = { name: 'openAIDeploy' scope: dataRG params: { name: '${namingPrefix}-openai' location: location deployGPT5: environment == 'production' } } // Cosmos DB with geo-replication module cosmosDB 'modules/cosmos-db.bicep' = { name: 'cosmosDBDeploy' scope: dataRG params: { name: '${namingPrefix}-cosmos' primaryLocation: location secondaryLocation: secondaryLocation enableAutomaticFailover: environment == 'production' } } // Key Vault module keyVault 'modules/key-vault.bicep' = { name: 'keyVaultDeploy' scope: coreRG params: { name: '${namingPrefix}-kv' location: location enablePurgeProtection: environment == 'production' } } // Outputs output aksClusterName string = aksCluster.outputs.clusterName output containerEnvId string = containerEnv.outputs.environmentId output openAIEndpoint string = openAI.outputs.endpoint output cosmosDBEndpoint string = cosmosDB.outputs.endpoint output keyVaultUri string = keyVault.outputs.vaultUri
AKS Automatic Module
// modules/aks-automatic.bicep @description('Cluster name') param name string @description('Location') param location string @description('Kubernetes version') param kubernetesVersion string = '1.34' @description('Log Analytics workspace ID') param workspaceId string @description('Enable zone redundancy') param enableZoneRedundancy bool = true resource aksCluster 'Microsoft.ContainerService/managedClusters@2025-01-01' = { name: name location: location sku: { name: 'Automatic' tier: 'Standard' } identity: { type: 'SystemAssigned' } properties: { kubernetesVersion: kubernetesVersion dnsPrefix: '${name}-dns' enableRBAC: true aadProfile: { managed: true enableAzureRBAC: true } networkProfile: { networkPlugin: 'azure' networkPluginMode: 'overlay' networkDataplane: 'cilium' serviceCidr: '10.0.0.0/16' dnsServiceIP: '10.0.0.10' } autoScalerProfile: { 'balance-similar-node-groups': 'true' expander: 'least-waste' } autoUpgradeProfile: { upgradeChannel: 'stable' nodeOSUpgradeChannel: 'NodeImage' } securityProfile: { defender: { securityMonitoring: { enabled: true } } workloadIdentity: { enabled: true } } oidcIssuerProfile: { enabled: true } addonProfiles: { omsagent: { enabled: true config: { logAnalyticsWorkspaceResourceID: workspaceId } } azurePolicy: { enabled: true } } } zones: enableZoneRedundancy ? ['1', '2', '3'] : null } output clusterName string = aksCluster.name output clusterId string = aksCluster.id output oidcIssuerUrl string = aksCluster.properties.oidcIssuerProfile.issuerUrl output kubeletIdentity string = aksCluster.properties.identityProfile.kubeletidentity.objectId
Managing Deployment Stacks
Update Stack
# Update with new template version az stack sub update \ --name MyProductionStack \ --template-file main.bicep \ --parameters @parameters.json \ --action-on-unmanage deleteAll # Update deny settings az stack sub update \ --name MyProductionStack \ --deny-settings-mode DenyWriteAndDelete \ --deny-settings-excluded-principals <new-principal-id>
View Stack Details
# Show stack information az stack sub show \ --name MyProductionStack \ --output json # List all stacks in subscription az stack sub list --output table # List stacks in resource group az stack group list \ --resource-group MyRG \ --output table
Export Stack Template
# Export template from deployed stack az stack sub export \ --name MyProductionStack \ --output-file exported-stack.json # Export and save parameters az stack sub show \ --name MyProductionStack \ --query "parameters" \ --output json > parameters-backup.json
Delete Stack
# Delete stack and all managed resources az stack sub delete \ --name MyProductionStack \ --action-on-unmanage deleteAll \ --yes # Delete stack but keep resources az stack sub delete \ --name MyProductionStack \ --action-on-unmanage detachAll \ --yes # Delete with confirmation prompt az stack sub delete --name MyProductionStack
Deny Settings in Detail
DenyDelete Mode
Prevents deletion but allows updates:
az stack sub create \ --name MyStack \ --location eastus \ --template-file main.bicep \ --deny-settings-mode DenyDelete \ --deny-settings-excluded-principals \ <emergency-access-principal-id> \ <devops-service-principal-id>
Use cases:
- Protect production databases
- Prevent accidental resource deletion
- Allow configuration updates
DenyWriteAndDelete Mode
Prevents both updates and deletions:
az stack sub create \ --name MyStack \ --location eastus \ --template-file main.bicep \ --deny-settings-mode DenyWriteAndDelete \ --deny-settings-excluded-principals <break-glass-principal-id>
Use cases:
- Immutable infrastructure
- Compliance requirements
- Critical production workloads
Excluded Principals
Bypass deny settings for specific identities:
# Get principal IDs SERVICE_PRINCIPAL_ID=$(az ad sp show --id <app-id> --query id -o tsv) ADMIN_GROUP_ID=$(az ad group show --group "Cloud Admins" --query id -o tsv) # Apply with exclusions az stack sub create \ --name MyStack \ --location eastus \ --template-file main.bicep \ --deny-settings-mode DenyWriteAndDelete \ --deny-settings-excluded-principals $SERVICE_PRINCIPAL_ID $ADMIN_GROUP_ID
ActionOnUnmanage Policies
detachAll
Resources are removed from stack management but not deleted:
az stack sub create \ --name MyStack \ --location eastus \ --template-file main.bicep \ --action-on-unmanage detachAll
Use when:
- Testing deployment changes
- Migrating resources to another stack
- Temporary stack management
deleteAll
All unmanaged resources are deleted:
az stack sub create \ --name MyStack \ --location eastus \ --template-file main.bicep \ --action-on-unmanage deleteAll
Use when:
- Ephemeral environments (dev, test)
- Clean slate deployments
- Strict infrastructure-as-code enforcement
deleteResources
Delete resources but keep resource groups:
az stack sub create \ --name MyStack \ --location eastus \ --template-file main.bicep \ --action-on-unmanage deleteResources
RBAC for Deployment Stacks
Built-in Roles
Azure Deployment Stack Contributor
- Manage deployment stacks
- Cannot create or delete deny-assignments
Azure Deployment Stack Owner
- Full stack management
- Can create and delete deny-assignments
Assign Roles
# Assign Stack Contributor role az role assignment create \ --assignee <user-or-service-principal-id> \ --role "Azure Deployment Stack Contributor" \ --scope /subscriptions/<subscription-id> # Assign Stack Owner role az role assignment create \ --assignee <admin-principal-id> \ --role "Azure Deployment Stack Owner" \ --scope /subscriptions/<subscription-id>
CI/CD Integration
GitHub Actions
name: Deploy Deployment Stack on: push: branches: [main] workflow_dispatch: permissions: id-token: write contents: read jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Azure Login uses: azure/login@v2 with: client-id: ${{ secrets.AZURE_CLIENT_ID }} tenant-id: ${{ secrets.AZURE_TENANT_ID }} subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - name: What-if Analysis run: | az stack sub what-if \ --name MyProductionStack \ --location eastus \ --template-file main.bicep \ --parameters @parameters.json - name: Deploy Stack run: | az stack sub create \ --name MyProductionStack \ --location eastus \ --template-file main.bicep \ --parameters @parameters.json \ --deny-settings-mode DenyWriteAndDelete \ --deny-settings-excluded-principals ${{ secrets.DEVOPS_PRINCIPAL_ID }} \ --action-on-unmanage deleteAll \ --yes
Azure DevOps Pipeline
trigger: branches: include: - main pool: vmImage: 'ubuntu-latest' variables: azureSubscription: 'MyAzureConnection' stackName: 'MyProductionStack' location: 'eastus' steps: - task: AzureCLI@2 displayName: 'What-if Analysis' inputs: azureSubscription: $(azureSubscription) scriptType: 'bash' scriptLocation: 'inlineScript' inlineScript: | az stack sub what-if \ --name $(stackName) \ --location $(location) \ --template-file main.bicep \ --parameters @parameters.json - task: AzureCLI@2 displayName: 'Deploy Stack' inputs: azureSubscription: $(azureSubscription) scriptType: 'bash' scriptLocation: 'inlineScript' inlineScript: | az stack sub create \ --name $(stackName) \ --location $(location) \ --template-file main.bicep \ --parameters @parameters.json \ --deny-settings-mode DenyWriteAndDelete \ --action-on-unmanage deleteAll \ --yes
Monitoring and Auditing
View Stack Events
# Get deployment operations az stack sub show \ --name MyProductionStack \ --query "deploymentId" \ --output tsv | \ xargs -I {} az deployment sub show --name {} # List managed resources az stack sub show \ --name MyProductionStack \ --query "resources[].id" \ --output table
Activity Logs
# Query stack operations az monitor activity-log list \ --resource-group MyRG \ --namespace Microsoft.Resources \ --start-time 2025-01-01T00:00:00Z \ --query "[?contains(authorization.action, 'Microsoft.Resources/deploymentStacks')]" \ --output table
Migration from Azure Blueprints
Assessment
- Inventory Blueprints: List all blueprints and assignments
- Document Parameters: Export parameters and configurations
- Plan Conversion: Map blueprints to deployment stacks
- Test in Dev: Validate converted templates
Conversion Steps
# 1. Export Blueprint as ARM template # (Use Azure Portal or PowerShell) # 2. Convert ARM to Bicep az bicep decompile --file blueprint-template.json # 3. Create Deployment Stack az stack sub create \ --name ConvertedFromBlueprint \ --location eastus \ --template-file converted.bicep \ --parameters @blueprint-parameters.json \ --deny-settings-mode DenyWriteAndDelete \ --action-on-unmanage detachAll # 4. Validate resources az stack sub show --name ConvertedFromBlueprint # 5. Delete Blueprint assignment (after validation) # Remove-AzBlueprintAssignment -Name MyBlueprintAssignment
Best Practices
✓ Use Deployment Stacks for all new infrastructure ✓ Always run what-if analysis before deployment ✓ Use DenyWriteAndDelete for production stacks ✓ Exclude break-glass principals from deny settings ✓ Tag stacks with Environment, CostCenter, Owner ✓ Use deleteAll for ephemeral environments ✓ Use detachAll for migration scenarios ✓ Implement CI/CD pipelines for stack deployment ✓ Monitor stack operations via activity logs ✓ Document stack architecture and dependencies
Troubleshooting
Stack Creation Fails
# Check deployment errors az stack sub show \ --name MyStack \ --query "error" \ --output json # Validate template az deployment sub validate \ --location eastus \ --template-file main.bicep \ --parameters @parameters.json
Deny Settings Blocking Operations
# Check deny assignments az role assignment list \ --scope /subscriptions/<subscription-id> \ --include-inherited \ --query "[?type=='Microsoft.Authorization/denyAssignments']" # Add principal to exclusions az stack sub update \ --name MyStack \ --deny-settings-excluded-principals <new-principal-id>
Resources Not Deleted
# Check action-on-unmanage setting az stack sub show \ --name MyStack \ --query "actionOnUnmanage" \ --output tsv # Update to deleteAll az stack sub update \ --name MyStack \ --action-on-unmanage deleteAll
References
Deployment Stacks represents the future of Azure infrastructure lifecycle management!