2

I'm currently creating a pipeline for Azure DevOps to validate and apply a Terraform configuration to different subscription.

My terraform configuration uses modules, those are "hosted" in other repositories in the same Azure DevOps Project as the terraform configuration.

Sadly, when I try to perform terraform init to fetch those modules, the pipeline task "hang" there waiting for credentials input.

As recommanded in the Pipeline Documentation on Running Git Commands in a script I tried to add a checkout step with the persistCredentials:true attribute.

From what I can see in the log of the task (see bellow), the credentials information are added specifically to the current repo and are not usable for other repos.

The command performed when adding persistCredentials:true

2018-10-22T14:06:54.4347764Z ##[command]git config http.https://my-org@dev.azure.com/my-org/my-project/_git/my-repo.extraheader "AUTHORIZATION: bearer ***"

The output of terraform init task

2018-10-22T14:09:24.1711473Z terraform init -input=false
2018-10-22T14:09:24.2761016Z Initializing modules...
2018-10-22T14:09:24.2783199Z - module.my-module
2018-10-22T14:09:24.2786455Z   Getting source "git::https://my-org@dev.azure.com/my-org/my-project/_git/my-module-repo?ref=1.0.2"

How can I setup the git credentials to work for other repositories ?

4

5 回答 5

2

我有同样的问题,我最终做的是SYSTEM_ACCESSTOKEN在 terraform 配置中进行标记。我在 Azure DevOps 中使用了 Tokenzization 任务,其中__前缀和后缀用于识别并用实际变量替换标记(它是可定制的,但我发现双下划线最好不要干扰我拥有的任何代码)

- task: qetza.replacetokens.replacetokens-task.replacetokens@3
    displayName: 'Replace tokens'
    inputs:
      targetFiles: |
       **/*.tfvars
       **/*.tf
      tokenPrefix: '__'
      tokenSuffix: '__'

find $(Build.SourcesDirectory)/ -type f -name 'main.tf' -exec sed -i 's~__SYSTEM_ACCESSTOKEN__~$(System.AccessToken)~g' {} \;如果您无法将自定义扩展安装到您的 DevOps 组织,类似的方法也可以使用。

我的 terraform main.tf 看起来像这样:

module "app" {
  source = "git::https://token:__SYSTEM_ACCESSTOKEN__@dev.azure.com/actualOrgName/actualProjectName/_git/TerraformModules//azure/app-service?ref=__app-service-module-ver__"
  ....
}

它并不漂亮,但它完成了工作。模块源(在撰写本文时)不支持来自 terraform 的变量输入。所以我们可以做的是使用 Terrafile,它是一个开源项目,通过在代码旁边保留一个简单的 YAML 文件来帮助跟上您可能使用的模块和同一模块的不同版本。似乎它不再被积极维护,但它只是工作:https ://github.com/coretech/terrafile 我的 Terrafile 示例:

app:
    source:  "https://token:__SYSTEM_ACCESSTOKEN__@dev.azure.com/actualOrgName/actualProjectName/_git/TerraformModules"
    version: "feature/handle-twitter"
app-stable:
    source:  "https://token:__SYSTEM_ACCESSTOKEN__@dev.azure.com/actualOrgName/actualProjectName/_git/TerraformModules"
    version: "1.0.5"

Terrafile 默认下载你的模块到 ./vendor 目录,这样你就可以将你的模块源指向类似的东西:

module "app" {
  source = "./vendor/modules/app-stable/azure/app_service"
  ....
}

现在你只需要弄清楚如何terrafile在 Terrafile 所在的目录中执行命令。我的 azure.pipelines.yml 示例:

- script: curl -L https://github.com/coretech/terrafile/releases/download/v0.6/terrafile_0.6_Linux_x86_64.tar.gz | tar xz -C $(Agent.ToolsDirectory)
  displayName: Install Terrafile

- script: |
    cd $(Build.Repository.LocalPath)
    $(Agent.ToolsDirectory)/terrafile
  displayName: Download required modules
于 2020-05-09T07:59:21.233 回答
1

你基本上有两种方法可以做到这一点。

先决条件

确保您阅读并根据需要应用“在脚本中运行 Git 命令”文档中的启用脚本以运行 Git 命令部分。

解决方案 #1:在管道运行时动态插入System.AccessToken(或 PAT,但我不推荐)

您可以通过以下方式做到这一点:

  • __SYSTEM_ACCESSTOKEN__在您的代码中插入替换标记(如Nilsas建议的那样)并使用一些标记替换代码或qetza.replacetokens.replacetokens-task.replacetokens插入值的任务。terraform此解决方案的缺点是您在本地运行时还必须替换令牌。
  • 使用一些代码将所有git::https://dev.azure.com文本替换为git::https://YOUR_ACCESS_TOKEN@dev.azure.com.

我通过使用以下 bash 任务脚本来使用第二种方法(它搜索terragrunt文件,但您可以适应terraform文件而无需太多更改):

- bash: |
    find $(Build.SourcesDirectory)/ -type f -name 'terragrunt.hcl' -exec sed -i 's~git::https://dev.azure.com~git::https://$(System.AccessToken)@dev.azure.com~g' {} \;

Abu Belai提供了一个 PowerShell 脚本来做类似的事情。

terraform但是,如果您的模块gitrepo 中的模块在另一个 repo 中调用自己的模块,这种类型的解决方案将不起作用git,这就是我们的情况。

解决方案 #2:在模块reposextraheader的 url 中全局添加访问令牌terraformgit

这样,由您的代码直接调用或由被调用模块的代码间接调用的所有模块的存储库都将能够使用您的访问令牌。我通过在您的terraform/terragrunt调用之前添加以下步骤来做到这一点:

- bash: |
    git config --global http.https://dev.azure.com/<your-org>/<your-first-repo-project>/_git/<your-first-repo>.extraheader "AUTHORIZATION: bearer $(System.AccessToken)"
    git config --global http.https://dev.azure.com/<your-org>/<your-second-repo-project>/_git/<your-second-repo>.extraheader "AUTHORIZATION: bearer $(System.AccessToken)"

您将需要extraheader为每个调用的git存储库设置 。

请注意,如果您的管道在同一个工作人员上设置了多次,您可能需要在调用extraheader后取消设置。这是因为可能会与多重声明混淆。您可以通过添加以下步骤来执行此操作:terraformextraheadergitextraheader

- bash: |
    git config --global --unset-all http.https://dev.azure.com/<your-org>/<your-first-repo-project>/_git/<your-first-repo>.extraheader
    git config --global --unset-all http.https://dev.azure.com/<your-org>/<your-second-repo-project>/_git/<your-second-repo>.extraheader 
于 2021-01-18T09:47:11.217 回答
1

我做了这个

_ado_token.ps1

# used in Azure DevOps to allow terrform to auth with Azure DevOps GIT repos
$tfmodules = Get-ChildItem $PSScriptRoot -Recurse -Filter "*.tf"
foreach ($tfmodule in $tfmodules) {
    $content = [System.IO.File]::ReadAllText($tfmodule.FullName).Replace("git::https://myorg@","git::https://" + $env:SYSTEM_ACCESSTOKEN +"@")
    [System.IO.File]::WriteAllText($tfmodule.FullName, $content)
}

天蓝色管道.yml

- task: PowerShell@2
  env: 
    SYSTEM_ACCESSTOKEN: $(System.AccessToken)
  inputs:
    filePath: '_ado_token.ps1'
    pwsh: true
  displayName: '_ado_token.ps1'
于 2020-06-08T14:17:15.550 回答
0

我通过创建一个运行内联 powershell 脚本的管道模板解决了这个问题。然后,当使用来自不同 Repo 的任何 terraform 模块时,我将模板作为 Pipeline 模板拉入“资源”。该脚本将对所有 .tf 文件进行递归搜索。然后使用正则表达式更新所有模块源 url。

我选择 REGEX 而不是对模块 url 进行标记,因为这将确保可以在开发机器上拉入模块,而无需对源代码进行任何更改。

parameters:
- name: terraform_directory
  type: string

steps:
  - task: PowerShell@2
    displayName: Tokenize TF-Module Sources
    env:
      SYSTEM_ACCESSTOKEN: $(System.AccessToken)
    inputs:
      targetType: 'inline'
      
      script: |
        $regex = "https://*(.+)dev.azure.com"
        $tokenized_url = "https://token:$($env:SYSTEM_ACCESSTOKEN)@dev.azure.com"

        Write-Host "Recursive Search in ${{ parameters.terraform_directory }}"
        $tffiles = Get-ChildItem -Path "${{ parameters.terraform_directory }}" -Filter "*main.tf" -Recurse -Force

        Write-Host "Found $($tffiles.Count) files ending with 'main.tf'"
        if ($tffiles) { Write-Host $tffiles }

        $tffiles | % {
          Write-Host "Updating file $($_.FullName)"
          $content = Get-Content $_.FullName

          Write-Host "Replace Strings: $($content | Select-String -Pattern $regex)"
          
          $content -replace $regex, $tokenized_url | Set-Content $_.FullName -Force

          Write-Host "Updated content"
          Write-Host (Get-Content $_.FullName)
        }
于 2022-01-10T14:26:08.310 回答
-2

我不认为你可以。通常,您创建另一个构建并链接到该构建中的工件以在当前定义中使用它。这样你就不需要连接到不同的 Git 存储库

于 2018-10-22T15:00:14.260 回答