如何测试链接的ARM模板?

时间:2020-01-11 19:16:57

标签: azure azure-devops azure-resource-manager arm-template

我的ARM模板太大,因此我想使用链接的模板。我知道模板必须位于ARM可以访问的位置。但是在将它们上传到目标位置之前,我应该能够以某种方式对其进行测试。否则,存在使用无效模板覆盖以前工作的模板的风险。那我该如何还原?

你如何做到的?

2 个答案:

答案 0 :(得分:2)

这是对我有用的。我有一个管道,用于从Azure Devops存储库中部署模板。管道将其部署到开发环境中,因此,打破世界末日并没有结束。您也可以在本地进行测试,然后再选择是否将其推送到仓库(在某些情况下,这可能真的很难做到,因为模板在“实际”开发环境中对内容有很多依赖性,而在其他订阅上进行测试是由于引用了现有资源\密钥库\等而无法执行)。我在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) }}

让我解释一下这是怎么回事:

  1. Update-DeploymentPrereq-用于检查目标资源组是否存在并设置了正确的标记。如果没有创建并标记它。标签为:Version-表示最后成功部署的版本; FailedVersion-这实际上意味着上次部署失败,并且包含启动该部署的内部版本ID(成功部署后将被删除); InProgress-包含开始部署到资源组的内部版本的内部版本;部署结束后(无论是否失败),它将被重置,因此它仅在构建运行时才有值。
  2. Update-Prereq-此文件将所有模板\工件上传到专用存储帐户(稍后会详细介绍)
  3. Update-DeploymentConcurrency-通过检查InProgress标记的值来检查此资源组上是否已在运行部署。如果该值与not running(或您对其进行测试的任何字符串- it sets the InProgress`标记)匹配到作业的构建ID,否则-使用非零退出代码退出脚本,从而有效地破坏了构建(从而防止碰撞)。
  4. 部署应该很明显

现在让我们谈谈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,该ID使用GitVersion映射到特定的提交(因此,您始终可以确定已部署的版本的代码或无法部署到特定的环境的代码)。并输出生成的容器名称,因此AzureResourceGroupDeployment@2可以找出它并使用该特定容器来查找模板。

还有其他构建步骤,可控制失败\成功标记,清理旧版本\旧容器。如果您需要我的特定实现,我可能可以将其转变为博客文章

从本质上讲,一切都归结为:

  1. 防止并行执行
  2. 每个资源组\提交组合都有一组单独的模板,因此它们永远不会重叠
  3. 能够确定将哪个版本部署到环境中
  4. 能够毫不费力地将其回溯到提交ID。

这可能不是最佳方法,但这是我在需要时可以想到的最佳方法。

ps。对于本地测试,您只需使用Update-Prereq函数,因为您不需要所有检查,只需模板的唯一URL即可。

答案 1 :(得分:0)

所以我想出了这个脚本。每个开发人员都有一个预先创建的资源组,他将在其中部署资源和预先创建的Blob容器来存储当前正在开发的模板。在部署模板之前,我使用azcopy将本地文件夹与Blob容器同步。不幸的是,有时候Test-AzResourceGroupDeployment在失败的情况下无法为您提供足够的详细信息,因此我无法根据其返回值来决定是否执行部署。目前,这似乎很好。但是它已经是第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
}