针对其他存储库运行git命令

时间:2018-10-22 14:42:24

标签: azure-devops terraform

我目前正在为Azure DevOps创建管道,以验证Terraform配置并将其应用于其他订阅。

我的terraform配置使用模块,这些模块与terraform配置“托管”在同一Azure DevOps项目的其他存储库中。

可悲的是,当我尝试执行terraform init来获取那些模块时,管道任务“挂起”在那里,等待凭据输入。

根据Pipeline Documentation on Running Git Commands in a script的要求,我尝试使用checkout属性添加一个persistCredentials:true步骤。

根据我在任务日志中看到的信息(请参见下面的信息),凭据信息专门添加到当前存储库中,不能用于其他存储库。

添加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 ***"

terraform初始化任务的输出

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"

如何设置git凭据以用于其他存储库?

5 个答案:

答案 0 :(得分:1)

我遇到了同样的问题,我最终要做的是在Terraform配置中标记SYSTEM_ACCESSTOKEN。我在Azure DevOps中使用了令牌化任务,其中__前缀和后缀用于标识和替换具有实际变量的令牌(它是可自定义的,但我发现最好使用双下划线来不干扰任何我拥有的代码)

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

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

我的Terramain 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

答案 1 :(得分:1)

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

先决条件

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

解决方案 #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 模块 git 存储库中的模块将自己称为另一个 git 存储库中的模块(我们就是这种情况),则此类解决方案不起作用。

解决方案 #2:在您的 extraheader 模块 terraform 存储库的 url git 中全局添加访问令牌

这样,由您的代码直接调用或由被调用模块的代码间接调用的所有模块的存储库都将能够使用您的访问令牌。为此,我在您的 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,您可能需要在 terraform 调用后取消设置 extraheader。这是因为 git 可能会与多个 extraheader 声明混淆。您可以通过添加以下步骤来完成此操作:

- 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 

答案 2 :(得分:0)

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

答案 3 :(得分:0)

问题的主要症结在于,下载模块的Terraform git操作不在管道正在构建的存储库的上下文中发生。为了使Terraform git操作通过Azure Repos进行身份验证,您必须在全局.gitconfig中使用管道的访问令牌显式设置一个extraheader

尽管令牌仅在构建期间有效,但我不希望extraheader配置污染(本地)构建代理用户的全局.gitconfig,并且可能影响后续构建。为了使配置瞬态,我在.gitconfig中创建了一个临时$env:AGENT_TEMPDIRECTORY(在构建结束时由代理程序对其进行了清理),然后在全局变量中使用[include]对其进行引用。 .gitconfig。然后,include会在Terraform操作完成后取消设置。

在Terraform操作之前,我在管道中具有以下PowerShell任务:

# Build a temp .gitconfig 
$tempGitConfig = Join-Path $env:AGENT_TEMPDIRECTORY ".gitconfig.$($env:BUILD_BUILDID)"

# Set an extraheader for the module sources that uses the pipeline's access token
git config --file $tempGitConfig `
             http.https://my.azd.com/project.extraheader `
             "AUTHORIZATION: bearer $($env:SYSTEM_ACCESSTOKEN)"

# Include temp .gitconfig in global user .gitconfig
git config --global include.path $tempGitConfig

然后在Terraform操作之后,另一个PowerShell步骤取消设置include.path

# Cleanup 
git config --global --unset include.path "\.gitconfig\.$($env:BUILD_BUILDID)"

如果您要反复执行此操作,则可以全部打包到自定义构建任务中。

答案 4 :(得分:0)

我做到了

_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)
}

azure-pipelines.yml

- task: PowerShell@2
  env: 
    SYSTEM_ACCESSTOKEN: $(System.AccessToken)
  inputs:
    filePath: '_ado_token.ps1'
    pwsh: true
  displayName: '_ado_token.ps1'