mcpField
D
deployment-stacks-2025

deployment-stacks-2025

github.com/josiahsiegel云与 DevOps59153 次安装19 周前更新
代码仓库

关于 deployment-stacks-2025

deployment-stacks-2025 是介绍 Azure Deployment Stacks 的参考技能,涵盖资源生命周期管理、保护、清理策略和运维操作,面向在资源组、订阅或管理组范围管理部署的 Azure 基础设施团队。

主要功能

  1. 01统一管理堆栈资源
  2. 02通过拒绝设置保护资源
  3. 03资源脱离管理时的处理策略
  4. 04资源组、订阅和管理组范围
  5. 05Azure CLI 与 Bicep 示例

适用场景

  1. 01防止生产资源被修改或删除
  2. 02清理从模板中移除的资源
  3. 03在不同 Azure 范围管理基础设施
  4. 04规划从 Azure Blueprints 迁移

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/josiahsiegel/claude-plugin-marketplace --skill deployment-stacks-2025

安装后在 Claude Code 中输入/deployment-stacks-2025确认已加载。

附带文件references/cicd-monitoring.md

详细介绍

Azure Deployment Stacks - 2025 GA 功能

Azure Deployment Stacks 的完整知识库。它是 Azure Blueprints 的后继产品(2024 年 GA,2025 年最佳实践)。

概述

Azure Deployment Stacks 是一种资源类型,可将一组 Azure 资源作为一个原子单元进行管理。它提供统一的生命周期管理、资源保护和自动清理功能。

主要功能

1. 统一资源管理
  • 将多个资源作为一个实体进行管理
  • 对整个堆栈执行更新、导出和删除操作
  • 在一个位置跟踪所有受管理的资源
  • 在不同环境中保持部署一致
2. 拒绝设置(资源保护)

防止未经授权修改受管理的资源:

  • None:不设限制(默认)
  • DenyDelete:防止删除资源
  • DenyWriteAndDelete:防止更新和删除资源
3. ActionOnUnmanage(清理策略)

控制模板中不再包含的资源会发生什么:

  • detachAll:从堆栈管理中移除,但保留资源
  • deleteAll:删除模板中不再包含的资源
  • deleteResources:删除不受管理的资源,但保留资源组
4. 灵活的范围

可在以下范围部署堆栈:

  • 资源组范围
  • 订阅范围
  • 管理组范围
5. 替代 Azure Blueprints

Azure Blueprints 将于 2026 年 7 月弃用。Deployment Stacks 是推荐的替代方案。

先决条件

Azure CLI 版本
# Requires Azure CLI 2.61.0 or later
az version

# Upgrade if needed
az upgrade
Azure PowerShell 版本
# Requires Azure PowerShell 12.0.0 or later
Get-InstalledModule -Name Az
Update-Module -Name Az

创建 Deployment Stacks

订阅范围的堆栈
# 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
资源组范围的堆栈
# 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"
管理组范围的堆栈
# 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

Deployment Stack 的 Bicep 模板

生产环境堆栈模板
// 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 模块
// 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

管理 Deployment Stacks

更新堆栈
# 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>
查看堆栈详情
# 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 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 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

拒绝设置详解

DenyDelete 模式

防止删除,但允许更新:

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>

适用场景:

  • 保护生产数据库
  • 防止意外删除资源
  • 允许更新配置
DenyWriteAndDelete 模式

同时防止更新和删除:

az stack sub create \
  --name MyStack \
  --location eastus \
  --template-file main.bicep \
  --deny-settings-mode DenyWriteAndDelete \
  --deny-settings-excluded-principals <break-glass-principal-id>

适用场景:

  • 不可变基础设施
  • 合规要求
  • 关键生产工作负载
排除的主体

允许特定身份绕过拒绝设置:

# 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 策略

detachAll

资源会从堆栈管理中移除,但不会被删除:

az stack sub create \
  --name MyStack \
  --location eastus \
  --template-file main.bicep \
  --action-on-unmanage detachAll

适用场景:

  • 测试部署变更
  • 将资源迁移到另一个堆栈
  • 临时管理堆栈
deleteAll

删除所有不受管理的资源:

az stack sub create \
  --name MyStack \
  --location eastus \
  --template-file main.bicep \
  --action-on-unmanage deleteAll

适用场景:

  • 临时环境(开发、测试)
  • 全新部署
  • 严格执行基础设施即代码
deleteResources

删除资源,但保留资源组:

az stack sub create \
  --name MyStack \
  --location eastus \
  --template-file main.bicep \
  --action-on-unmanage deleteResources

Deployment Stacks 的 RBAC

内置角色

Azure Deployment Stack Contributor

  • 管理 Deployment Stacks
  • 无法创建或删除拒绝分配

Azure Deployment Stack Owner

  • 完整管理堆栈
  • 可以创建和删除拒绝分配
分配角色
# 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、监控与审计

Deployment Stacks 的详细 GitHub Actions 和 Azure DevOps CI/CD 示例,以及监控、审计查询、部署历史检查和运维可观测性内容,请参阅 references/cicd-monitoring.md。在将堆栈部署用于生产环境或审计变更时,请加载该参考文档。

从 Azure Blueprints 迁移

评估
  1. 清点 Blueprints:列出所有蓝图和分配
  2. 记录参数:导出参数和配置
  3. 规划转换:将蓝图映射到 Deployment Stacks
  4. 在开发环境中测试:验证转换后的模板
转换步骤
# 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

最佳实践

✓ 所有新基础设施都使用 Deployment Stacks ✓ 部署前始终运行 what-if 分析 ✓ 生产环境的堆栈使用 DenyWriteAndDelete ✓ 将应急恢复主体排除在拒绝设置之外 ✓ 为堆栈添加 Environment、CostCenter、Owner 标签 ✓ 对临时环境使用 deleteAll ✓ 在迁移场景中使用 detachAll ✓ 为堆栈部署实施 CI/CD 管道 ✓ 通过活动日志监控堆栈操作 ✓ 记录堆栈架构和依赖关系

故障排除

堆栈创建失败
# 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
拒绝设置阻止操作
# 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>
资源未删除
# 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

参考资料

Deployment Stacks 代表着 Azure 基础设施生命周期管理的未来!

评论

支持 Markdown