脚本如何访问服务连接? (Azure Devops管道)

时间:2019-07-27 16:38:08

标签: azure azure-devops

根据https://docs.microsoft.com/en-us/azure/devops/pipelines/library/service-endpoints,有很多服务连接类型。我可以轻松地在项目级别管理一组服务连接,并设置权限以限制哪些用户可以查看/编辑它们-一切都很好。

但是我不知道如何在构建管道中使用脚本步骤访问服务连接。例如,假设我有一个服务连接,代表一个Azure服务主体的凭据。我想在脚本步骤中访问这些凭据。

我如何编写一个脚本来利用它们?

5 个答案:

答案 0 :(得分:3)

我在脚本/工具中使用与 ARM 部署相同的服务连接。

为了导出变量,我创建了以下模板。

parameters:
- name: azureSubscription
  type: string
- name: exportAsOutput
  type: boolean
  default: false
  
steps:  
- task: AzureCLI@2
  name: exported_azure_credentials
  displayName: 'Export Azure Credentials'
  inputs:
    azureSubscription: '${{ parameters.azureSubscription }}'
    scriptType: pscore
    scriptLocation: inlineScript
    addSpnToEnvironment: true
    ${{ if eq(parameters.exportAsOutput, true) }}:
      inlineScript: |
        Write-Host "##vso[task.setvariable variable=AZURE_TENANT_ID]$env:tenantId"
        Write-Host "##vso[task.setvariable variable=AZURE_TENANT_ID;isOutput=true]$env:tenantId"
        Write-Host "##vso[task.setvariable variable=AZURE_CLIENT_ID]$env:servicePrincipalId"
        Write-Host "##vso[task.setvariable variable=AZURE_CLIENT_ID;isOutput=true]$env:servicePrincipalId"
        Write-Host "##vso[task.setvariable variable=AZURE_CLIENT_SECRET]$env:servicePrincipalKey"
        Write-Host "##vso[task.setvariable variable=AZURE_CLIENT_SECRET;isOutput=true]$env:servicePrincipalKey"
    ${{ if eq(parameters.exportAsOutput, false) }}:
      inlineScript: |
        Write-Host "##vso[task.setvariable variable=AZURE_TENANT_ID]$env:tenantId"
        Write-Host "##vso[task.setvariable variable=AZURE_CLIENT_ID]$env:servicePrincipalId"
        Write-Host "##vso[task.setvariable variable=AZURE_CLIENT_SECRET]$env:servicePrincipalKey"

DevOps 在处理机密方面非常聪明,因此它们不会出现在管道日志中。

答案 1 :(得分:2)

我也一直在想这个。我确定的解决方案是使用“ Azure CLI”任务,而不是基本的“脚本”(或“重击”)任务。表面上看,这是用于运行Az CLI命令的,但是没有什么可以阻止您仅运行标准的Bash脚本(或者PSCore)。

如果检查运行此任务时出现的环境变量,则会在前缀为“ ENDPOINT_DATA_”的变量中看到大量有关服务连接的信息。这与Josh E所说的一致。它包括Azure订阅ID,名称,服务主体对象ID等。

(可选)您也可以启用将服务原理详细信息添加到环境中。然后,它将包含SPN密钥,TenantID等作为秘密环境变量。

这是任务的样子:

- task: AzureCLI@2
  displayName: 'Azure CLI'
  inputs:
    scriptType: bash
    scriptLocation: inlineScript
    azureSubscription: '<Service Connection Name>'
    inlineScript: |
      env | sort

- task: AzureCLI@2
  displayName: 'Azure CLI, with SPN info'
  inputs:
    scriptType: bash
    scriptLocation: inlineScript
    azureSubscription: '<Service Connection Name>'
    addSpnToEnvironment: true
    inlineScript: |
      env | sort

当然,这仅适用于Azure云服务连接。可能会有类似的技术可用于其他服务连接,但我尚未对其进行调查。

答案 2 :(得分:1)

由于服务连接涉及专门针对所连接服务的数据(Generic Service Connection是证明规则的例外...),因此您将无法在Bash中使用强类型属性任务。相反,您可能需要检查环境变量并手动处理服务连接数据。

根据对Azure DevOps存储库中某些tasks的调查,看来服务连接及其数据已作为环境变量填充到运行构建任务的代理上。服务连接是通过以下方法检索的,该方法通过以下正则表达式运行给定的name字符串,然后检索结果环境键的值:

process.env[name.replace(/\./g, '_').toUpperCase()];

对各种服务端点数据的检索都包装在vsts-task-lib/task module中,从而允许执行繁琐的任务来编写代码,如下所示:

taskLib.getEndpointAuthorization('SYSTEMVSSCONNECTION', false);

taskLib.getEndpointDataParameter('MYSERVICECONNECTION', 'SOME_PARAMETER_NAME', false);

taskLib.getEndpointUrl('MYSERVICECONNECTION', false) // <-- last param indicates required or not

因此,如果您希望在bash脚本中访问服务连接而不进行任何其他自定义,我建议您:

a)通过迭代和编写环境变量,设置system.debug环境变量,验证构建脚本任务中服务连接信息的可用性。有迹象表明,构建任务没有被没有特别请求的连接“播种”,因此您可能需要创建一个自定义构建任务,其输入之一就是您要使用的服务连接名称

b)从bash脚本中上面概述的变量中读取所需的值。服务连接变量名称的计算方式类似于this

   var dataParam = getVariable('ENDPOINT_DATA_' + id + '_' + key.toUpperCase());  

您可能需要对此进行迭代以确定数据架构/结构。

答案 3 :(得分:0)

与AFAIK的乔希(Josh)达成共识,尽管我喜欢具有可重用的服务连接的想法,但直到今天,在Azure管道中重用服务连接和Bash任务的可能性还是很小的!

但是,请注意,此任务是在GitHub上开源的,欢迎您提供反馈和意见。 :)

答案 4 :(得分:0)

我发现,如果我在运行bash Task之前将Kubectl任务与命令一起使用,则无需身份验证或使用主机名。

KUBERNETESNODE和SERVICEPROTOCOL是我设置的优先级的管道变量。

      - task: Kubernetes@1
        displayName: 'Kubernetes Login'
        # This is needed to run kubectl command from bash.
        inputs:
          connectionType: 'Kubernetes Service Connection'
          kubernetesServiceEndpoint: '<Service Connection Name>'
          command: 'login'

      - task: Bash@3
        displayName: 'Run Component Test'        
        inputs:
          targetType: 'inline'
          script: |
            #Get the Node Port
            nodePort=`kubectl get --namespace $(Build.BuildId) svc <service name> -o=jsonpath='{.spec.ports[0].nodePort}'`
            #Run Newman test
            newman run postman/Service.postman_collection.json --global-var host=$KUBERNETESNODE --global-var protocol=$SERVICEPROTOCOL --global-var port=$nodePort -r junit