為 ARM 模板構建真實世界的 Azure DevOps 流水線

在線搜索時,您會找到各種關於Azure DevOps的博客文章,文檔和教程。所有這些都是有價值的資源,但很少有一個能夠引導您完成真實世界的情景。許多教程都只是簡單地介紹了安全方面,將密碼以明文形式呈現,或者最終生成的產品實際上沒有任何功能。讓我們改變這一點。

在這篇文章/教程中,您將從頭到尾學習如何構建一個真實的Azure DevOps發布管道,以自動化基礎架構。具體而言,您將學習如何使用Azure DevOps構建一個連續部署管道以提供Azure虛擬機器。

通過完成這個項目,您將擁有一個完全功能的Azure管道。從單個GitHub存儲庫提交,它將:

  • 構建臨時的Azure資源組
  • 通過ARM模板提供Azure虛擬機器
  • 在CI/CD管道中設置該ARM模板
  • 在模板更改時,啟動模板驗證測試
  • 將ARM模板部署到Azure
  • 測試部署的基礎架構
  • 拆除所有Azure資源

立即開始吧!

項目概述

本項目將分為六個主要部分。它們是:

Azure資源準備

在本節中,您將學習如何在Azure中設置所有先決資源。在這裡,您將:

  • 為管道中的各種任務建立 Azure 服務主體
  • 設置 Azure 金鑰保管庫以供管道使用
  • 為 ARM 部署和管道使用設置適當的存取權限

Azure DevOps 準備工作

完成 Azure 資源的所有設置後,現在是為管道準備 Azure DevOps 的時候。在這一部分,您將:

  • 在 Azure DevOps 組織中安裝 Pester 測試運行器建置任務
  • 建立服務連接以提供 Azure DevOps 所需的資源訪問權限
  • 建立 Azure DevOps 變數群組,將金鑰保管庫連接至存取 Azure 金鑰保管庫的密鑰

腳本/模板概述

這個專案有各種相關的工具,包括用於建立伺服器的 ARM 樣板和 Pester 測試。在這一節中,我們將簡要介紹樣板的佈署項目以及 Pester 測試在流程中的具體內容。

流程建立

在這一節中,真正的樂趣開始了。您將開始設置實際的流程。在這裡,您將學習如何通過一個單一的 YAML 文件來設置整個協調流程。

您將使用多階段流程使用者介面來建立流程。截至本文撰寫時,此功能處於預覽階段。

流程演示

流程建立完成後,您需要看它運行!在這一節中,您將學習如何觸發流程並觀察奇蹟的發生。

清理工作

最後,由於這只是一個示範,您將獲得一個拆除教程中所建立的所有內容的腳本。

聽起來很多嗎?是的!但別擔心,您將逐步學習,一次攻克一個任務。

如果您想要一個包含用於建立此流程的所有 Azure CLI 命令的腳本,您可以在 ServerAutomationDemo GitHub 存儲庫中找到它,文件名為demo.ps1

先決條件

你將學到很多東西,但也需要一些前提條件。如果你計劃跟隨教程,請確保擁有以下內容:

ARM deployments allowed to access the key vault
  • Cloud Shell 或 PowerShell 6+(如果在本地運行) – 示例可能在 Windows PowerShell 中運行,但未經過測試。所有示例均在本地的 PowerShell 控制台中執行,但 Cloud Shell 也同樣適用。你將自動化構建流程。
  • 安裝 Azure CLI(如果在本地運行) – 你將在本文中學習如何使用 Azure CLI 執行任務。但是,相同的操作也可以通過 Azure Portal、PowerShell 或 Azure SDK 執行。

警告:你即將執行的操作需要真實貨幣支付,除非你有一些 Azure 學分。在 Azure 中,你將啟用最消耗費用的資源是一個虛擬機,但僅在暫時使用。

開始之前

在本教程中,你將進行大量配置。在開始之前,請確保準備好以下項目。

  • 將部署資源的 Azure 訂閱名稱 – 示例將使用 Adam the Automator
  • 訂閱的ID
  • Azure AD租戶ID
  • DevOps組織名稱 – 範例將使用 adbertram.
  • 您放置資源的區域 – 範例將使用 eastus.
  • 將臨時金鑰庫放入的Azure資源組的名稱 – 範例將使用 ServerAutomationDemo.
  • A password to assign to the local administrator account on a deployed VM – the examples will use “I like azure.”.
  • GitHub存儲庫的URL – 範例將使用 https://github.com/adbertram/ServerAutomationDemo.

使用Azure CLI登錄

請準備在本文中使用Azure CLI進行大量工作。我喜歡Azure PowerShell cmdlets,但Azure CLI目前能夠執行更多DevOps任務。

您的第一個任務是進入PowerShell 6+控制台。一旦在控制台中,使用命令 az login 認證到Azure。這個命令將打開一個瀏覽器窗口並提示您輸入您的帳戶。

一旦認證,請確定將您的訂閱設定為默認。將其設定為默認將防止您必須不斷指定它。

az login
az account set --subscription 'Adam the Automator'

準備Azure資源

一旦使用 Azure CLI 登錄,就可以開始進行業務。Azure Pipeline 有很多不同的依賴項和各種開關。在這個第一部分中,您將學習如何進行一些設置,為您的管道準備環境。

安裝 Azure CLI DevOps 擴展

您需要一種使用 Azure CLI 構建各種 Azure DevOps 組件的方式。默認情況下,它不包含該功能。要從 Azure CLI 管理 Azure DevOps,您需要安裝 DevOps 擴展

幸運的是,安裝這個擴展只需一行命令,如下所示。

az extension add --name azure-devops

安裝完成後,將組織設置為默認組織,以防止重複指定。

az devops configure --defaults organization=https://dev.azure.com/adbertram

創建資源組

儘管管道將創建一個臨時資源組,但您還應該為此演示中的任何資源創建一個資源組。具體來說,這個資源組是您將創建 Azure 金鑰保管庫的地方。

az group create --location "eastus" --name "ServerAutomationDemo"

創建 Azure 服務主體

下一個任務是創建 Azure 服務主體。您需要一個 Azure 服務主體來進行 Azure 金鑰保管庫的身份驗證。您還將使用此服務主體來進行服務連接的身份驗證。按照以下步驟為金鑰保管庫和最終的 ARM 部署創建服務主體。

$spIdUri = "http://ServerAutomationDemo"
$sp = az ad sp create-for-rbac --name $spIdUri | ConvertFrom-Json

此時,將 $sp.appId 的值儲存起來是一個好主意。當您稍後開始構建流水線時,將需要這個值!

您會在示例中看到一些 PowerShell 命令,例如 ConvertFrom-Json。由於 Azure CLI 只返回 JSON 字符串,將其轉換為 PowerShell 對象後,可以更輕鬆地引用屬性。

建立金鑰保管庫

本教程中的流水線需要引用幾個密碼。我們將採用正確的方式,而不是以明文形式儲存密碼。所有敏感信息將儲存在 Azure 金鑰保管庫中。

要創建金鑰保管庫,請使用如下所示的 az keyvault create 命令。此命令將在之前創建的資源組中創建金鑰保管庫。同時注意到 enabled-for-template-deployment 開關。這將更改金鑰保管庫的存取策略,以允許未來的 ARM 部署訪問金鑰保管庫。

az keyvault create --location $region --name "ServerAutomationDemo-KV" --resource-group "ServerAutomationDemo" --enabled-for-template-deployment true
Allowing ARM to access the keyvault

創建金鑰保管庫密碼

一旦创建了密钥保管库,就是创建密钥的时候了。对于此演示,请创建两个名为ServerAutomationDemo-AppPwStandardVmAdminPassword的密钥。AppPw密码是服务主体的密码。VM密码将分配给VM上的本地管理员帐户。

az keyvault secret set --name "ServerAutomationDemo-AppPw" --value $sp.password --vault-name "ServerAutomationDemo-KV"
az keyvault secret set --name StandardVmAdminPassword --value "I like azure." --vault-name "ServerAutomationDemo-KV"

请注意,在此示例中使用了先前定义的PowerShell变量。在这里,您提供了之前获取到的服务主体密码($sp.password)。

允许管道访问密钥保管库

接下来,管道需要权限来访问密钥保管库。稍微放宽密钥保管库的访问策略。为创建的服务主体提供获取列出密钥保管库密钥的权限。

az keyvault set-policy --name "ServerAutomationDemo-KV" --spn $spIdUri --secret-permissions get list

准备Azure DevOps

现在,您已经完成了所有Azure资源的准备工作。是时候在Azure DevOps中做一些准备工作了。

安装Pester扩展

首先要执行的任务是安装PesterRunner Azure DevOps扩展。管道将运行两组Pester测试,以确保VM ARM部署成功。运行Pester测试的最简单方法之一是使用PesterRunner扩展。

使用以下命令安装扩展。

az devops extension install --extension-id PesterRunner --publisher-id Pester

创建Azure DevOps项目

現在是創建專案的時候,將在其中創建流程管道。使用Azure CLI創建Azure DevOps流程管道非常簡單。只需運行以下命令來創建專案並將專案設置為默認值。

az devops project create --name "ServerAutomationDemo"
az devops configure --defaults project=ServerAutomationDemo

創建服務連接

您的流程管道需要驗證兩個服務-ARM和您的GitHub存儲庫。為此,需要創建兩個服務連接。

首先,創建ARM服務端點。下面的命令將提示輸入服務主體密碼。請確保首先在控制台上顯示它並將其複製到剪貼板中。

請確保填入您的訂閱ID,租戶ID並替換下面的訂閱名稱。

## 執行$sp.password並將其複製到剪貼板中
$sp.Password

## 創建服務端點
az devops service-endpoint azurerm create --azure-rm-service-principal-id $sp.appId --azure-rm-subscription-id "YOURSUBSCRIPTIONIDHERE" --azure-rm-subscription-name 'Adam the Automator' --azure-rm-tenant-id $tenantId --name 'ARM'

接下來,為GitHub創建一個服務連接。由於流程管道將通過Git提交觸發,因此它需要能夠讀取存儲庫。

在這一點上,GitHub個人訪問令牌就派上用場了。在下面,您還需要再次粘貼服務主體密碼。對於兩個服務連接,您使用的是同一個服務主體。

$gitHubServiceEndpoint = az devops service-endpoint github create --github-url 'https://github.com/adbertram/ServerAutomationDemo' --name 'GitHub' | ConvertFrom-Json

## 在提示時粘貼GitHub令牌

創建變數組

該流水線將參考金鑰保管庫密碼的兩個密碼。為了安全地實現這一點,您必須創建一個變量組並將其連接到金鑰保管庫。

首先,按照下面的示例創建變量組。

az pipelines variable-group create --name "ServerAutomationDemo" --authorize true --variables foo=bar

注意foo=bar變量嗎?這不會被使用,但是需要一個單一變量來創建變量組。

將變量組連接到金鑰保管庫

此時,您不幸需要轉到 Azure DevOps 開發人員門戶。截至撰寫本文時,Azure CLI 無法將金鑰保管庫與變量組連接。

導航到 Azure DevOps 專案,然後點擊。然後,您應該會看到如下所示的ServerAutomationDemo變量組。點擊ServerAutomationDemo變量組。

Available variable group

進入變量組後,點擊將 Azure 金鑰保管庫的密碼連接為變量。這樣做後,您將收到警告,提示您將刪除所有變量,然後點擊確認。您將在下面看到如何執行此操作。這個操作是可以接受的,因為foo變量一直都是臨時性的。

Linking variable group to pipeline

確認後,選擇ARM服務連接和之前創建的ServerAutomationDemo-KV金鑰保管庫,如下所示。點擊添加

Setting the service connection

現在檢查之前創建的兩個密碼,如下所示,然後點擊確定保存以保存更改。

Selecting keyvault secrets for the pipeline to use

項目文件概述

如果您已經走到這一步,恭喜!您現在已經準備好開始建立流水線了。但是等等…還有更多!

為了使Azure流水線更貼近實際情況,本教程將建立一個包含”單元測試”和”驗收測試”的流水線。這使得教程更有趣,但也需要一些額外的解釋。

在本教程的GitHub存儲庫中,您將找到如下所示的幾個文件。現在是時候從這些文件中克隆此存儲庫或者構建自己的存儲庫了。

GitHub files list
  • azure-pipelines.yml – 最終的YAML流水線
  • connect-azure.ps1 – 用於對Azure訂閱進行身份驗證的PowerShell腳本
  • server.infrastructure.tests.ps1 – 簡單的Pester測試,用於確認VM配置是否正確
  • server.json – 用於部署VM的Azure ARM模板
  • server.parameters.json – 一個Azure ARM參數模板,為ARM模板提供參數值。

請務必在server.parameters.json文件中替換您的訂閱ID和金鑰保管庫名稱。

  • server.templates.tests.ps1 – Pester的”單元測試”,用於確認ARM模板的有效性

您將在稍後看到這些文件如何在流水線中相互配合。

創建流程

假設您已經克隆了我的GitHub存儲庫或者自己建立了一個,現在是時候創建流程了!要這樣做,運行az pipelines create命令。下面的命令將使用提供的GitHub存儲庫作為觸發器創建一個名為ServerAutomationDemo的流程。它將查看master分支並使用之前建立的服務連接。

az pipelines create --name "ServerAutomationDemo" --repository "https://github.com/adbertram/ServerAutomationDemo" --branch master --service-connection $gitHubServiceEndpoint.id --skip-run

根據您的GitHub存儲庫中是否有azure-pipelines.yml文件,您可能會收到以下類似的反饋。無論如何,您的控制台看起來都會類似。請確保準備好您的GitHub個人訪問權杖!

Creating the Azure DevOps pipeline with the Azure CLI

YAML流程評審

此時,您的流程已經準備好運行,但首先了解一下YAML流程是很重要的。請查看azure-pipelines.yml文件。該文件是使用多階段YAML流程功能時的流程。

讓我們分解一下構成這個YAML流程的各個組件。

觸發器

由於您正在構建一個自動運行的CI流程,所以需要一個觸發器。下面的觸發器指示流程在Git主分支檢測到提交時運行。

同時也請注意paths部分。預設情況下,在CI建置中,如果未明確包含或排除檔案或目錄,則當提交任何檔案時,將執行流程。由於該專案完全建立在ARM範本之上,如果您對Pester測試進行微調,則不應該執行流程。

trigger:
  branches:
    include:
      - master
  paths:
    include:
      - server.json
      - server.parameters.json

執行環境

每個建置都需要一個代理程式。每個建置代理程式都需要在虛擬機上運行。在本例中,該虛擬機使用ubuntu-latest虛擬機映像。此映像是在建置原始建立時定義的預設映像。由於此流程的「簡單性」,未對其進行更改。

pool:
  vmImage: "ubuntu-latest"  

變數

接下來,我們有所有變數和變數群組。此流程中的各個任務需要讀取值,例如Azure訂閱ID、租戶ID和服務主體的應用程式ID等。為了不在每個任務中複製靜態值,它們被定義為變數。

還要注意group元素。此元素引用了您之前創建的變數群組。請確保此時替換subscription_idtenant_id的值。

還記得在「創建Azure服務主體」部分中,您被提醒將$sp.appId的值保存在某個地方嗎?這就是您需要它的地方。將該服務主體應用程式ID的值指定給application_id,如下所示。

variables:
    - group: ServerAutomationDemo
    - name: azure_resource_group_name
      value: "ServerProvisionTesting-$(Build.BuildId)"
    - name: subscription_id
      value: "XXXXXXXXXXXXX"
    - name: application_id
      value: "XXXXXXXXXXXXX"
    - name: tenant_id
      value: "XXXXXXXXXXXX"

請注意azure_resource_group_name變數的值。在該值內,您會看到$(Build.BuildId)。這是一個系統變數,代表當前作業的建置 ID。在這個情境中,它被用來確保所建立的臨時資源群組是唯一的。

PowerShell 準備工作

接下來的一組工作會呼叫 PowerShell 程式碼。這個流程範例使用 PowerShell 建立和移除一個用於測試目的的臨時資源群組。在這些部署工作中,您會看到兩個呼叫 PowerShell 程式碼的範例。

第一個工作呼叫一個名為connect-azure.ps1的腳本,該腳本存在於 GitHub 存儲庫中。此工作用於對 Azure 訂閱進行身分驗證,以便後續的 Azure PowerShell 命令能夠執行。

此 Azure PowerShell 連線工作呼叫該腳本,並傳遞金鑰保險庫的密碼值(ServerAutomationDemo-AppPw)以及管線變數subscription_idapplication_idtenant_id

第二個工作執行 PowerShell 程式碼,內嵌表示腳本不存在,而是在流程 YAML 中使用azure_resource_group_name管線變數的值來定義 PowerShell 程式碼。

- task: PowerShell@2
  inputs:
    filePath: "connect-azure.ps1"
    arguments: '-ServicePrincipalPassword "$(ServerAutomationDemo-AppPw)" -SubscriptionId $(subscription_id) -ApplicationId $(application_id) -TenantId $(tenant_id)'
- task: PowerShell@2
  inputs:
    targetType: "inline"
    script: New-AzResourceGroup -Name $(azure_resource_group_name) -Location eastus -Force

Pester 範本測試

接下來是第一個 Pester 測試。在像這樣的 CI/CD 流程中,擁有幾個不同層級的測試非常重要。如果您正在建立一個軟體專案的流程,您可能會建立各種單元測試。

由於此示例管道是建立在單個 ARM VM 部署周圍的基礎上,因此首先的 “單元” 測試將是測試 JSON 模板的有效性。您可以在 server.templates.tests.ps1 檔案中添加任意數量的測試,以測試 ARM 模板本身的有效性。

請注意,下面的管道正在使用各種 系統變量。這些變量引用了文件在它們進入構建代理後的位置。

PesterRunner 任務正在將測試結果發送到 XML 檔案,稍後在管道中將對其進行讀取。

- task: Pester@0
  inputs:
    scriptFolder: "@{Path='$(System.DefaultWorkingDirectory)/server.template.tests.ps1'; Parameters=@{ResourceGroupName='$(azure_resource_group_name)'}}"
    resultsFile: "$(System.DefaultWorkingDirectory)/server.template.tests.XML"
    usePSCore: true
    run32Bit: False

ARM VM 部署

我們現在來到 ARM 部署。由於整個管道都建立在能夠部署 VM 的能力上,因此這一點非常重要!此任務部署 ARM 模板,提供了執行此操作所需的所有必要屬性。

請注意 deploymentOutputs: arm_output 屬性。在下一步中,任務需要連接到已部署的 VM。通過通過 ARM 部署返回 DNS 名稱或 IP 地址來獲取此 VM 的一種很好的方法。 deploymentOutputs 選項創建了一個可以在其他任務中引用的管道變量。

- task: AzureResourceManagerTemplateDeployment@3
  inputs:
    deploymentScope: "Resource Group"
    azureResourceManagerConnection: "ARM"
    subscriptionId: "1427e7fb-a488-4ec5-be44-30ac10ca2e95"
    action: "Create Or Update Resource Group"
    resourceGroupName: $(azure_resource_group_name)
    location: "East US"
    templateLocation: "Linked artifact"
    csmFile: "server.json"
    csmParametersFile: "server.parameters.json"
    deploymentMode: "Incremental"
    deploymentOutputs: "arm_output"

Pester “接受” 測試

VM 部署完成後,您需要使用 “整合” 或 “接受” 測試來確保它已成功部署。此 PesterRunner 任務正在調用 Pester,並運行另一組基礎架構相關測試,以確保 VM 部署成功。

請注意,我們正在通過ArmDeploymentJsonOutput參數傳遞ARM部署的輸出值。Pester測試腳本文件有一個定義的參數,它接受這個值並讀取VM的DNS主機名。

 - task: Pester@0
    inputs:
      scriptFolder: "@{Path='$(System.DefaultWorkingDirectory)/server.infrastructure.tests.ps1'; Parameters=@{ArmDeploymentJsonOutput='$(arm_output)'}}"
      resultsFile: "$(System.DefaultWorkingDirectory)/server.infrastructure.tests.XML"
      usePSCore: true
      run32Bit: False

您可以在下面看到server.infrastructure.tests.ps1 PowerShell腳本的外觀。請注意,它正在讀取VM的DNS主機名,然後運行一個簡單的開放端口檢查。

$ArmDeploymentOutput = $ArmDeploymentJsonOutput | convertfrom-json

## 執行測試
describe 'Network Connnectivity' {
    it 'the VM has RDP/3389 open' {
        Test-Connection -TCPPort 3389 -TargetName $ArmDeploymentOutput.hostname.value -Quiet | should -Be $true
    }
}

“接受” 測試清理

管道部署任何基礎設施的唯一原因是測試ARM模板的有效性。因為這個基礎設施只是暫時的,所以需要清理。在最後一個PowerShell任務中,管道正在刪除之前創建的資源組及其內容。

- task: PowerShell@2
  inputs:
    targetType: "inline"
    script: Get-AzResourceGroup -Name $(azure_resource_group_name) | Remove-AzResourceGroup -Force

Pester測試發布

最後,我們來到最後一組任務。Azure Pipelines有一個名為發布測試結果的任務。此任務在建構代理上讀取XML文件並在Azure DevOps中顯示測試結果。這是一種方便的方式,可以輕鬆查看所有運行的測試結果。

- task: PublishTestResults@2
  inputs:
    testResultsFormat: "NUnit"
    testResultsFiles: "$(System.DefaultWorkingDirectory)/server.infrastructure.tests.XML"
    failTaskOnFailedTests: true

- task: PublishTestResults@2
  inputs:
    testResultsFormat: "NUnit"
    testResultsFiles: "$(System.DefaultWorkingDirectory)/server.template.tests.XML"
    failTaskOnFailedTests: true
The Tests section of a pipeline run

使用Azure DevOps Pipeline

最後,我們準備運行管道並查看其運作情況。在Azure DevOps的Web UI中,確保您在ServerAutomationDemo項目中。在這裡,點擊Pipelines,然後您應該能看到ServerAutomationDemo管道。

運行管道的一種方法是點擊右側的三個點,如下所示。然後,點擊運行管道。這將啟動自動化過程。

Running a pipeline

管道將按照指示順利運行每個任務。最終,您應該看到每個工作執行的任務都有綠色的勾號,如下所示。

Successful job task execution

清理

一旦您在管道中進行了一些調整並完成了所有工作,您應該進行清理。畢竟,這只是一個教程,而不是一個生產任務!

以下是清理本文中創建的所有內容的一些命令。此代碼將刪除服務主體、Azure AD應用程序、資源組以及其中的所有內容和Azure DevOps項目。

$spId = ((az ad sp list --all | ConvertFrom-Json) | ? { '<https://ServerAutomationDemo>' -in $_.serviceprincipalnames }).objectId
az ad sp delete --id $spId

## 刪除資源組
az group delete --name "ServerAutomationDemo" --yes --no-wait

## 刪除項目
$projectId = ((az devops project list | convertfrom-json).value | where { $_.name -eq 'ServerAutomationDemo' }).id
az devops project delete --id $projectId --yes

總結

本教程旨在讓您了解如何構建真實的Azure DevOps基礎設施自動化管道。儘管還有無數其他構建這樣的管道的方法,但您在本教程中學到的技能應該能夠幫助您應對許多不同的配置。

現在去做更多的自動化吧!

Source:
https://adamtheautomator.com/azure-devops/