如何测试链接的 ARM 模板?
How to test linked ARM templates?
我的 ARM 模板太大了,所以我想使用链接模板。我知道模板需要放在 ARM 可以访问的地方。但是在将它们上传到目标位置之前,我应该能够以某种方式测试它们。否则,我可能会用无效的模板覆盖以前工作的模板。那我该如何恢复?
你是怎么做到的?
以下是适合我的方法。我有一个从 Azure Devops 存储库部署模板的管道。管道将其部署到开发环境,因此破坏某些东西并不是世界末日。如果这是一个选项,您也可以在推送到回购之前在本地进行测试(在某些情况下可能真的很难做到,模板对 "real" 开发环境中的东西有很多依赖性,并在其他环境中进行测试由于对现有 resources\key vaults\etc 的引用,订阅是不可能的。我在 azure devops 中使用以下代码片段到 运行 powershell 脚本,然后是部署:
- task: AzurePowerShell@3
displayName: UpdatePrereq
inputs:
azureSubscription: ${{ parameters.azureSubscription }}
ScriptType: InlineScript
Inline: |
${{ format('. $(Build.Repository.LocalPath)\scripts\_helpers.ps1
Update-DeploymentPrereq -resourceGroup {1} -location {3}
Update-Prereq -pathSuffix {0} -pathBase $(Build.Repository.LocalPath) -resourceGroup {1} -buildId $(Build.BuildNumber) -paramFile {2}
Update-DeploymentConcurrency -resourceGroup {1} -buildId $(Build.BuildNumber)',
parameters.buildDir, parameters.resourceGroupName, parameters.paramFile, parameters.location ) }}
azurePowerShellVersion: LatestVersion
- task: AzureResourceGroupDeployment@2
displayName: DeploySolution
inputs:
azureSubscription: ${{ parameters.azureSubscription }}
resourceGroupName: ${{ parameters.resourceGroupName }}
location: ${{ parameters.location }}
templateLocation: 'URL of the file'
csmFileLink: "https://xxx.blob.core.windows.net/$(containerName)/azuredeploy.json"
csmParametersFileLink: ${{ format('https://xxx.blob.core.windows.net/$(containerName)/param.{0}.json', parameters.paramFile) }}
让我解释一下这里发生的事情:
- Update-DeploymentPrereq - 用于检查目标资源组是否存在以及是否设置了正确的标签。如果不是,则创建并标记它。标签是:
Version
- 表示最后成功部署的版本; FailedVersion
- 这实际上意味着最后一次部署失败,它包含启动部署的构建 ID(这将在成功部署后删除); InProgress
- 包含开始部署到资源组的构建的构建 ID;这将在部署结束后重置(无论是否失败),因此它只有在构建 运行ning. 时才有价值
- Update-Prereq - 此更新将所有 templates\artifacts 上传到专用存储帐户(稍后详细介绍)
- Update-DeploymentConcurrency - 通过检查
InProgress
标记的值检查此资源组上是否已经有部署 运行ning。如果该值匹配 not running
(或您将其编码为 test- it sets the
InProgress` 标记的任何字符串与作业的构建 ID 相匹配,否则 - 使用非零退出代码退出脚本,有效地破坏构建(从而防止碰撞)。
- 部署应该很明显
现在让我们谈谈 Update-Prereq
,因为它对于所有这些工作都是必不可少的。它通过执行以下操作为每个构建生成一个随机容器名称:
Function Get-StringHash ([String]$String, $HashName = "MD5") {
$StringBuilder = New-Object System.Text.StringBuilder
[System.Security.Cryptography.HashAlgorithm]::Create($HashName).ComputeHash([System.Text.Encoding]::UTF8.GetBytes($String))|
ForEach-Object { [Void]$StringBuilder.Append($_.ToString("x2"))
}
$StringBuilder.ToString().Substring(0, 24)
}
...
Function Update-Prereq {
...
$containerName = Get-StringHash ( $resourceGroup + $buildId )
New-AzureStorageContainer -Name $containerName -Context $storageContext -Permission Blob
Write-Host "##vso[task.setvariable variable=containerName]$containerName"
...
}
这会产生一个 24 个字符长的字符串,它是确定性的(对于提供给 Get-StringHash
函数的相同输入,它总是相同的,这意味着当您 运行 从相同的构建提交到同一个资源组 - 它总是上传到同一个容器,但是如果你 运行 它用于另一个提交或另一个资源组 - 它会生成一个新的容器名称),从而避免你正在谈论的冲突。前面提到的标签泄露了构建 ID,它 maps 到使用 GitVersion 的特定提交(所以你总是可以弄清楚什么版本的代码是 deployed\or 未能部署到特定环境)。并输出生成的容器名称,因此 AzureResourceGroupDeployment@2
能够找出并使用该特定容器找到模板。
还有其他构建 steps,控制 failure\success 标记,清理旧的 builds\old 容器。如果您需要我的具体实现,我可能会把它变成一个博客 post
基本上这一切都归结为:
- 防止并行执行
- 每个资源 group\commit 组合都有一组单独的模板,因此它们永远不会重叠
- 能够判断将哪个构建部署到环境中
- 能够毫不费力地将它回溯到提交 ID。
这可能不是最佳方式,但这是我在需要时能想到的最佳方式。
ps。对于本地测试,你可以只使用 Update-Prereq
函数,因为你不需要所有的检查,只需要唯一的 url 为你的模板
所以我想到了这个脚本。每个开发人员都有预先创建的资源组,他将在其中部署资源和预先创建的 Blob 容器来存储当前正在开发的模板。在部署模板之前,我使用 azcopy
将我的本地文件夹与 Blob 容器同步。不幸的是,有时 Test-AzResourceGroupDeployment
不会在失败的情况下为您提供足够的详细信息,因此我无法根据它的 return 值来决定是否执行部署。现在这似乎工作正常。但它已经是第 3 或第 4 个版本,将来可能会改变。一种想法是合并 ARM-TTK 进行模板测试。
$currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
$resourceGroup = $currentUser.Substring($currentUser.IndexOf('\') + 1) + "-testing"
$containerName = $currentUser.Substring($currentUser.IndexOf('\') + 1) + "-testing"
$storageAccountName = "team_shared_account_here";
$containerUrl = "https://${storageAccountName}.blob.core.windows.net/${containerName}"
Write-Host "Current user: <${currentUser}>" -ForegroundColor Green
Write-Host "Deployment will use templates from <${containerName}> container" -ForegroundColor Green
Write-Host "Resources will be deployed to <${resourceGroup}> resource group" -ForegroundColor Green
Write-Host
Write-Host "Syncing templates..." -ForegroundColor Green
.\azcopy.exe sync '.' $containerUrl --include-pattern "*.json" --delete-destination true
$toDeploy = "app1", "app2"
foreach ($template in $toDeploy) {
$templateUri = "${containerUrl}/${template}.json"
$templateParameterUri = "${containerUrl}/${template}.parameters.DEV.json"
Write-Host "`nDeploying: ${templateUri}" -ForegroundColor Green
Write-Host "Paramaters: ${templateParameterUri}" -ForegroundColor Green
Test-AzResourceGroupDeployment -ResourceGroupName $resourceGroup `
-TemplateUri $templateUri `
-TemplateParameterUri $templateParameterUri `
-Mode Incremental `
-Verbose
New-AzResourceGroupDeployment -ResourceGroupName $resourceGroup `
-TemplateUri $templateUri `
-TemplateParameterUri $templateParameterUri `
-Mode Incremental `
-DeploymentDebugLogLevel All `
-Verbose
}
我的 ARM 模板太大了,所以我想使用链接模板。我知道模板需要放在 ARM 可以访问的地方。但是在将它们上传到目标位置之前,我应该能够以某种方式测试它们。否则,我可能会用无效的模板覆盖以前工作的模板。那我该如何恢复?
你是怎么做到的?
以下是适合我的方法。我有一个从 Azure Devops 存储库部署模板的管道。管道将其部署到开发环境,因此破坏某些东西并不是世界末日。如果这是一个选项,您也可以在推送到回购之前在本地进行测试(在某些情况下可能真的很难做到,模板对 "real" 开发环境中的东西有很多依赖性,并在其他环境中进行测试由于对现有 resources\key vaults\etc 的引用,订阅是不可能的。我在 azure devops 中使用以下代码片段到 运行 powershell 脚本,然后是部署:
- task: AzurePowerShell@3
displayName: UpdatePrereq
inputs:
azureSubscription: ${{ parameters.azureSubscription }}
ScriptType: InlineScript
Inline: |
${{ format('. $(Build.Repository.LocalPath)\scripts\_helpers.ps1
Update-DeploymentPrereq -resourceGroup {1} -location {3}
Update-Prereq -pathSuffix {0} -pathBase $(Build.Repository.LocalPath) -resourceGroup {1} -buildId $(Build.BuildNumber) -paramFile {2}
Update-DeploymentConcurrency -resourceGroup {1} -buildId $(Build.BuildNumber)',
parameters.buildDir, parameters.resourceGroupName, parameters.paramFile, parameters.location ) }}
azurePowerShellVersion: LatestVersion
- task: AzureResourceGroupDeployment@2
displayName: DeploySolution
inputs:
azureSubscription: ${{ parameters.azureSubscription }}
resourceGroupName: ${{ parameters.resourceGroupName }}
location: ${{ parameters.location }}
templateLocation: 'URL of the file'
csmFileLink: "https://xxx.blob.core.windows.net/$(containerName)/azuredeploy.json"
csmParametersFileLink: ${{ format('https://xxx.blob.core.windows.net/$(containerName)/param.{0}.json', parameters.paramFile) }}
让我解释一下这里发生的事情:
- Update-DeploymentPrereq - 用于检查目标资源组是否存在以及是否设置了正确的标签。如果不是,则创建并标记它。标签是:
Version
- 表示最后成功部署的版本;FailedVersion
- 这实际上意味着最后一次部署失败,它包含启动部署的构建 ID(这将在成功部署后删除);InProgress
- 包含开始部署到资源组的构建的构建 ID;这将在部署结束后重置(无论是否失败),因此它只有在构建 运行ning. 时才有价值
- Update-Prereq - 此更新将所有 templates\artifacts 上传到专用存储帐户(稍后详细介绍)
- Update-DeploymentConcurrency - 通过检查
InProgress
标记的值检查此资源组上是否已经有部署 运行ning。如果该值匹配not running
(或您将其编码为 test- it sets the
InProgress` 标记的任何字符串与作业的构建 ID 相匹配,否则 - 使用非零退出代码退出脚本,有效地破坏构建(从而防止碰撞)。 - 部署应该很明显
现在让我们谈谈 Update-Prereq
,因为它对于所有这些工作都是必不可少的。它通过执行以下操作为每个构建生成一个随机容器名称:
Function Get-StringHash ([String]$String, $HashName = "MD5") {
$StringBuilder = New-Object System.Text.StringBuilder
[System.Security.Cryptography.HashAlgorithm]::Create($HashName).ComputeHash([System.Text.Encoding]::UTF8.GetBytes($String))|
ForEach-Object { [Void]$StringBuilder.Append($_.ToString("x2"))
}
$StringBuilder.ToString().Substring(0, 24)
}
...
Function Update-Prereq {
...
$containerName = Get-StringHash ( $resourceGroup + $buildId )
New-AzureStorageContainer -Name $containerName -Context $storageContext -Permission Blob
Write-Host "##vso[task.setvariable variable=containerName]$containerName"
...
}
这会产生一个 24 个字符长的字符串,它是确定性的(对于提供给 Get-StringHash
函数的相同输入,它总是相同的,这意味着当您 运行 从相同的构建提交到同一个资源组 - 它总是上传到同一个容器,但是如果你 运行 它用于另一个提交或另一个资源组 - 它会生成一个新的容器名称),从而避免你正在谈论的冲突。前面提到的标签泄露了构建 ID,它 maps 到使用 GitVersion 的特定提交(所以你总是可以弄清楚什么版本的代码是 deployed\or 未能部署到特定环境)。并输出生成的容器名称,因此 AzureResourceGroupDeployment@2
能够找出并使用该特定容器找到模板。
还有其他构建 steps,控制 failure\success 标记,清理旧的 builds\old 容器。如果您需要我的具体实现,我可能会把它变成一个博客 post
基本上这一切都归结为:
- 防止并行执行
- 每个资源 group\commit 组合都有一组单独的模板,因此它们永远不会重叠
- 能够判断将哪个构建部署到环境中
- 能够毫不费力地将它回溯到提交 ID。
这可能不是最佳方式,但这是我在需要时能想到的最佳方式。
ps。对于本地测试,你可以只使用 Update-Prereq
函数,因为你不需要所有的检查,只需要唯一的 url 为你的模板
所以我想到了这个脚本。每个开发人员都有预先创建的资源组,他将在其中部署资源和预先创建的 Blob 容器来存储当前正在开发的模板。在部署模板之前,我使用 azcopy
将我的本地文件夹与 Blob 容器同步。不幸的是,有时 Test-AzResourceGroupDeployment
不会在失败的情况下为您提供足够的详细信息,因此我无法根据它的 return 值来决定是否执行部署。现在这似乎工作正常。但它已经是第 3 或第 4 个版本,将来可能会改变。一种想法是合并 ARM-TTK 进行模板测试。
$currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
$resourceGroup = $currentUser.Substring($currentUser.IndexOf('\') + 1) + "-testing"
$containerName = $currentUser.Substring($currentUser.IndexOf('\') + 1) + "-testing"
$storageAccountName = "team_shared_account_here";
$containerUrl = "https://${storageAccountName}.blob.core.windows.net/${containerName}"
Write-Host "Current user: <${currentUser}>" -ForegroundColor Green
Write-Host "Deployment will use templates from <${containerName}> container" -ForegroundColor Green
Write-Host "Resources will be deployed to <${resourceGroup}> resource group" -ForegroundColor Green
Write-Host
Write-Host "Syncing templates..." -ForegroundColor Green
.\azcopy.exe sync '.' $containerUrl --include-pattern "*.json" --delete-destination true
$toDeploy = "app1", "app2"
foreach ($template in $toDeploy) {
$templateUri = "${containerUrl}/${template}.json"
$templateParameterUri = "${containerUrl}/${template}.parameters.DEV.json"
Write-Host "`nDeploying: ${templateUri}" -ForegroundColor Green
Write-Host "Paramaters: ${templateParameterUri}" -ForegroundColor Green
Test-AzResourceGroupDeployment -ResourceGroupName $resourceGroup `
-TemplateUri $templateUri `
-TemplateParameterUri $templateParameterUri `
-Mode Incremental `
-Verbose
New-AzResourceGroupDeployment -ResourceGroupName $resourceGroup `
-TemplateUri $templateUri `
-TemplateParameterUri $templateParameterUri `
-Mode Incremental `
-DeploymentDebugLogLevel All `
-Verbose
}