解鎖 Azure DevOps 變量以進行自動化

如果您已经将Azure DevOps Pipeline作为CI/CD流水线解决方案构建起来,那么您肯定会遇到需要动态管理构建和发布中的配置值的情况。无论是为PowerShell脚本提供构建版本,将动态参数传递给构建任务,还是在构建和发布中使用字符串,您都需要变量。

如果您曾经问过以下类似的问题:

  • 我如何在PowerShell脚本中使用Azure DevOps构建管道变量?
  • 如何在构建和发布之间共享变量?
  • 预定义、用户定义和机密变量有什么区别?
  • 变量组是如何工作的?

…那么您运气真好!在本文中,我们将回答这些问题以及更多。

通过本文,您将了解Azure DevOps构建变量在Azure Pipelines中的工作原理!

Azure DevOps Pipeline变量是什么?

在我们深入介绍变量的具体内容之前,变量是什么以及它们如何帮助您构建和自动化高效的构建和发布流水线?

变量允许您将数据传递到流水线的各个部分。变量非常适合存储在流水线工作流程中可能发生变化的文本和数字。在流水线中,您几乎可以在任何地方设置和读取变量,而不是在脚本和YAML定义中硬编码值。

註:本文將僅關注YAML管線。我們不會涵蓋任何關於傳統經典管線的資訊。此外,除了一些小例外,您將不會學習如何通過網頁界面使用變數。我們將嚴格遵守YAML的規範。

變數在運行時被引用和定義(請參閱用戶定義變數)。當管線啟動一個工作時,各種流程會管理這些變數並將其值傳遞給系統的其他部分。這個系統提供了一種在不擔心更改構建定義和腳本的情況下動態運行管線工作的方式。

如果您對變數的概念還不太了解,不用擔心。本文的其餘部分將教您所需的一切。

變數環境

在深入研究變數本身之前,首先重要的是了解Azure管線的變數環境。本文中將多次提到這個詞彙。

在管線中,有兩個被非正式稱為環境的地方,您可以與變數交互。您可以在稱為管線環境的YAML構建定義中使用變數,也可以在通過任務執行的腳本中使用腳本環境。

管道环境

当您在YAML构建定义中定义或读取构建变量时,这被称为管道环境。例如,您可以在下面的示例中看到在YAML构建定义中定义了一个名为foo的变量,并将其设置为bar。在此上下文中,该变量是在管道环境中定义的。

variables:
  foo: 'bar'

脚本环境

您还可以在YAML定义本身或脚本中的代码中使用变量。当您没有已创建的现有脚本时,您可以在YAML定义中定义和读取变量,如下所示。您将在稍后学习如何在此上下文中使用这些变量的语法。

steps:
 - bash: ## 在此处设置和获取变量

您还可以通过将相同的语法添加到Bash脚本中并执行它来保留在脚本环境中。这是相同的一般概念。

环境变量

在脚本环境中,当一个管道变量可用时,它是通过创建一个环境变量来实现的。然后,可以通过所选语言的典型方法访问这些环境变量。

作为环境变量公开的管道变量将始终大写,并将任何点替换为下划线。例如,您将看到下面如何通过每种脚本语言访问foo管道变量。

variables:
  foo: 'bar'
  • 批次 %FOO%
  • PowerShell $env:FOO
  • Bash 腳本 – $FOO

管道「執行階段」

當一個管道「運行」時,它並不只是「運行」。與其中包含的 階段 一樣,管道在執行時也會經歷各種階段。由於在 Microsoft 文件中缺乏官方術語,我將其稱為「執行階段」。

當觸發一個管道時,它會經歷三個大致的階段 – 排隊編譯運行時。理解這些上下文很重要,因為如果你在導航 Microsoft 文件時,你會看到對這些術語的引用。

排隊時間

當一個管道在排隊中被觸發時,它會經歷的第一階段是排隊。在這個階段中,管道尚未開始,但已排隊,並在代理可用時準備運行。在定義變量時,你可以通過不在 YAML 文件中定義它們來使它們在排隊時間可用。

你可以在管道最初排隊時定義在排隊時間可用的變量,如下所示。當添加此變量後,它將作為全局變量在管道中可用,並可以被 YAML 文件中相同變量名覆蓋。

Run Pipeline Azure DevOps option

編譯

最後,當一個流水線處理一個YAML檔案並進入需要腳本執行的步驟時,該流水線處於編譯“階段”。在這個上下文中,代理程式正在執行在腳本步驟中定義的程式碼。

執行時期

下一個階段是執行時期。這是處理YAML檔案時的階段。在此階段中,每個階段、工作和步驟都在處理中,但並執行任何腳本。

變數擴展

另一個重要的主題是理解變數擴展。簡單來說,變數擴展是指變數返回一個靜態值。該變數展開以顯示它所包含的值。當讀取一個變數時,這是自動完成的,但是該擴展可能在流水線運行期間的不同時間進行,這可能會讓你感到困惑。

當你開始理解變數語法時,變數擴展和編譯與執行時期之間的概念將經常出現。

正如你在上面學到的,流水線在運行時涵蓋了不同的“階段”。當你進行變數擴展的故障排除時,你需要注意這些階段。

你可以在下面看到一個示例。這些“階段”的概念與變數環境密切相關。

steps:
  # 在腳本和下一個腳本中將FOO設置為“some value”
  - bash: |
      FOO="runtime value"
      echo "##vso[task.setvariable variable=FOO]$FOO"

  # 在“編譯時期”使用$()語法,流水線擴展該變數
  - bash: |
      echo "$(FOO)"

  # 在“執行時期”腳本上下文中使用環境變數,bash擴展該變數
  - bash: |
      echo "$FOO"

在流水线运行开始时,变量会被展开一次,然后在每个步骤开始时再次展开。下面是一个简单示例来说明这种行为。

jobs:
- job: build
  variables:
  - foo: bar 
  steps:
    - bash: |
        echo $(foo)            # This will be bar
        echo '##vso[task.setvariable variable=foo]baz'
        echo $(foo)            # This will also be bar, $(foo) expands before the step
    - bash: echo $(foo)        # 这将是 baz。变量在步骤之前被展开

变量语法

正如你所学到的,你可以在两个“环境”(流水线和脚本环境)中设置或读取变量。在每个环境中,引用变量的方式略有不同。你需要注意一些细微的差别。

流水线变量

流水线变量是在YAML构建定义中引用的,可以通过三种不同的语法方法引用 – 宏、模板表达式和运行时表达式。

宏语法

最常见的语法是宏语法。宏语法引用变量的值的形式为$(foo)。括号表示在运行时计算的表达式。

当Azure Pipelines处理定义为宏表达式的变量时,它将用变量的内容替换表达式。在使用宏语法定义变量时,它们遵循的模式是<variable name>: $(<variable value>)例如foo: $(bar)

如果尝试使用宏语法引用一个不存在的变量,那么该变量将简单地不存在。这种行为在不同的语法类型之间有一些差异。

模板表达式语法

另一種變數語法稱為模板表達式。以這種方式定義管道變數的形式為${{ variables.foo }}:${{ variables.bar }}。正如您所看到的,這種形式比宏語法更冗長。

模板表達式語法還具有一個附加功能。使用此語法,您還可以擴展模板參數。如果引用了使用模板表達式語法定義的變數,則管道將返回空字符串,而不是使用宏語法返回空值。

模板表達式變數在編譯時進行處理,然後在運行時被覆蓋(如果定義了)。

運行時表達式語法

作為語法類型,建議的運行時表達式變數僅在運行時展開。這些類型的變數通過格式$[variables.foo]表示。與模板表達式語法變數一樣,如果未替換,這些類型的變數將返回空字符串。

與宏語法類似,運行時表達式語法要求在定義的左側使用變數名,例如foo: $[variables.bar]

腳本變量

在腳本內部使用變量與管道變量不同。在任務腳本中定義和引用暴露的管道變量有兩種方法:使用日誌命令語法或環境變量。

日誌命令

在腳本中定義和引用管道變量的一種方法是使用日誌命令語法。這種語法有點複雜,但在某些情況下是必要的。在腳本中,以這種方式定義的變量必須定義為字符串。

使用日誌命令語法設置一個名為foo值為bar的變量,看起來像下面這樣。

"##vso[task.setvariable variable=foo;]bar"

I could not find a way to get the value of variables using logging commands. If this exists, let me know!

環境變量

當管道變量在腳本中轉換為環境變量時,變量名稍有變化。你會發現變量名變成大寫,點號變成下劃線。你會發現許多預定義的或系統變量中有點號。

例如,如果定義了一個名為[foo.bar](<http://foo.bar>)的管道變量,你可以通過腳本的本地環境變量引用方法來引用該變量,例如PowerShell中的$env:FOO_BAR或Bash中的$FOO_BAR

我們在上面的腳本環境一節中介紹了更多關於環境變量的內容。

變量作用域

A pipeline has various stages, tasks and jobs running. Many areas have predefined variable scopes. A scope is namespace where when a variable is defined, its value can be referenced.

在層次結構中,基本上有三個不同的變量作用域。它們是在以下層級定義的變量:

  • 在根層級定義的變量,使變量對管道中的所有作業都可用
  • 在階段層級定義的變量,使變量對特定階段可用
  • 在作業層級定義的變量,使變量對特定作業可用

在“較低”級別上定義的變量(例如工作)將覆蓋在階段和根級別定義的同一變量。在階段級別定義的變量將覆蓋在根級別定義的變量,但會被在工作級別定義的變量所覆蓋。下面是一個使用每個範圍的示例YAML構建定義。

以下是一個全局可用於所有階段和工作的示例:

variables:
  global_variable: value  # 這是一個對所有階段和工作都可用的全局變量

stages:
- stage: Build
  variables:
    stage_variable1: value3 # 在Build階段和所有工作中可用
  jobs:
  - job: BuildJob
    variables:
      job_variable1: value1    # 這只在BuildJob中可用
    steps:
    - bash: echo $(stage_variable1) ## 正常工作
    - bash: echo $(global_variable) ## 正常工作
    - bash: echo $(job_variable1) ## 正常工作

變量優先級

有時您會看到同一名稱的變量在不同範圍內設置。當發生這種情況時,該變量的值將根據特定的順序進行覆蓋,優先權給最接近的“操作”。

下面您將看到變量的覆蓋順序,從在工作中設置的變量開始。這將具有最高的優先級。

  1. 在工作級別設置的變量(在YAML文件中設置)
  2. 在階段級別設置的變量(在YAML文件中設置)
  3. 在流水線級別(全局)設置的變量(在YAML文件中設置)
  4. 在排隊時設置的變量
  5. 在流水線設置UI中設置的流水線變量

例如,請查看下面的YAML定義。在這個例子中,同一個變數在許多不同的地方被設置,但最終以作業中定義的值結束。隨著每個操作,變數的值在管道進入作業時被覆蓋。

variables:
  foo: 'global variable'

stages:
- stage: build
  variables:
   - name: foo
     value: 'defined at stage level'

  jobs:
  - job: compile
    variables:
    - name: foo
      value: 'defined at job level'
    steps:
      - bash: echo $(foo) # 這將在作業層級上確定

變數類型

在本文中,您已經了解了變數是什麼,它們的外觀,它們可以在哪些上下文中執行等等。但是我們尚未涵蓋的是,並非所有的變數都是相同的。有些變數在管道啟動時已經存在並且無法更改,而其他變數則可以隨意創建、更改和刪除。

有四種一般類型的變數 – 預定義或系統變數、用戶定義變數、輸出變數和機密變數。讓我們逐一介紹每一種變數並了解每種類型的變數。

預定義變數

在所有的構建和發佈中,你會發現許多默認存在的不同變數。這些變數被稱為預定義系統變數。預定義變數都是只讀的,像其他類型的變數一樣,它們表示簡單的字符串和數字。

Azure管道由許多組件組成,包括執行構建的軟件代理、運行部署時生成的作業和其他各種信息。為了表示所有這些領域,預定義系統變數被非正式地分為五個明確的類別:

  • 代理
  • 構建
  • 管道
  • 部署工作
  • 系統

這五個類別中都有數十個變數。在本文中,您不會了解所有這些變數。如果您想獲得所有預定義變數的列表,請查看微軟文件

使用者定義變數

當您在YAML定義或腳本中創建一個變數時,您正在創建一個使用者定義變數使用者定義變數只是您作為使用者在管線中定義和使用的所有變數。您可以為這些變數使用幾乎任何名稱,但有幾個例外。

您不能定義以詞語endpointinputsecretsecurefile開頭的變數。這些標籤是禁止使用的,因為它們保留用於系統使用,且不區分大小寫。

此外,您定義的任何變數都必須只包含字母、數字、點或底線字符。如果您嘗試定義不符合此格式的變數,您的YAML建置定義將無法工作。

輸出變數

A build definition contains one or more tasks. Sometimes a task sends a variable out to be made available to downstream steps and jobs within the same stage. These types of variables are called output variables.

輸出變數用於在管線的組件之間共享信息。例如,如果一個任務從數據庫查詢一個值,並且後續任務需要返回結果,則可以使用輸出變數。然後,您就不必每次都查詢數據庫。而是可以直接引用變數。

注意:输出变量的作用范围是特定的阶段。不要期望在“构建”阶段和“测试”阶段都可以使用输出变量。

保密变量

最后一种类型的变量是保密变量。从技术上讲,这并不是它自己独立的类型,因为它可以是系统定义或用户定义的变量。但是保密变量需要有自己的类别,因为它们与其他变量的处理方式不同。

A secret variable is a standard variable that’s encrypted. Secret variables typically contain sensitive information like API keys, passwords, etc. These variables are encrypted at rest with a 2048-bit RSA key and are available on the agent for all tasks and scripts to use.

– 不要在YAML文件中定义保密变量
– 不要将保密内容作为输出变量或日志信息返回

应该在流水线编辑器中定义保密变量。这将在全局级别将保密变量限定在任务中使用。

日志中的保密值被掩码处理,但不是完全隐藏。这就是为什么不应该将它们包含在YAML文件中的重要原因。您还应该知道不要将任何“结构化”数据作为保密内容。例如,如果将{ "foo": "bar" }设置为保密内容,那么bar将不会在日志中被掩码处理。

保密内容不会自动解密并映射到环境变量。如果您定义了一个保密变量,不要期望在PowerShell脚本中可以通过$env:FOO来使用它。

变量组

最后,我们来到变量组。变量组,顾名思义,是可以作为一个整体引用的变量“组”。变量组的主要目的是存储您希望在多个流水线中共享的值。

與變數不同,變數組在YAML文件中沒有定義。相反,它們在UI中的Library頁面下的Pipelines中定義。

使用變數組來存儲您想要控制並在多個流程中使用的值。您還可以使用變數組來存儲需要傳入YAML流程的機密和其他值。變數組在Library頁面下的Pipelines中定義和管理,如下所示。

Viewing variable groups

在流程庫中定義後,您可以使用以下語法在YAML文件中訪問該變數組。

variables:
- group: group1

默認情況下,不是所有流程都可以使用變數組。這個設置在創建組時提供。必須授權流程使用變數組。

一旦在YAML文件中使變數組可以訪問,您可以像訪問其他變數一樣訪問組內的變數。在引用組內的變數時,不使用變數組的名稱。

例如,如果您定義了一個名為group1的變數組,其中包含一個名為foo的變數,則您可以像訪問其他變數一樣引用foo變數,例如$(foo)

在變數組中定義的機密變數無法直接通過腳本訪問。相反,它們必須作為任務的參數傳遞。

如果對變數組中的變數進行更改,該更改將自動對允許使用該組的所有流程生效。

總結

你現在應該對於 Azure Pipelines 的變數有了扎實的了解。在這篇文章中,你學到了幾乎所有關於變數的概念!現在,拿出這些知識,應用於你的 Azure DevOps Pipelines,自動化所有的事情吧!

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