Как скрипт может получить доступ к сервисным соединениям? (Azure Devops Pipelines)

Согласно https://docs.microsoft.com/en-us/azure/devops/pipelines/library/service-endpoints существует широкий спектр типов сервисных подключений. Я могу легко управлять набором сервисных подключений на уровне проекта и устанавливать разрешения, чтобы ограничивать, какие пользователи могут просматривать / редактировать их - это все хорошо.

Но я не могу понять, как получить доступ к Service Connection с помощью шага сценария в моем конвейере сборки. Например, допустим, у меня есть служебное соединение, представляющее учетные данные для участника службы Azure. Я хотел бы получить доступ к этим учетным данным в шаге сценария.

Как я могу написать шаг сценария, который использует их?

8 ответов

Я тоже об этом думал. Решение, на котором я остановился, - использовать задачу Azure CLI, а не базовую задачу Script (или Bash). Это якобы для запуска команд Az CLI, но ничто не мешает вам запускать только стандартные сценарии Bash (или PSCore, если вам это нравится).

Если вы изучите переменные среды, присутствующие при запуске этой задачи, вы увидите кучу информации о соединении службы в переменных с префиксом ENDPOINT_DATA_. Это согласуется с тем, что говорил Джош Э. Он включает идентификатор подписки Azure, имя, идентификатор объекта принципала обслуживания и т. Д.

При желании вы также можете разрешить добавление деталей принципа обслуживания в среду. Затем это будет включать ключ 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 Cloud Service Connections. Могут существовать аналогичные методы, которые вы могли бы использовать для других подключений служб, но я не исследовал их.

Поскольку подключение службы включает данные, специально сформированные для подключенной службы ( универсальное подключение службы является исключением, подтверждающим правило...), вы не сможете использовать строго типизированные свойства в своей задаче Bash. Вместо этого вы можете захотеть проверить переменные среды и обработать данные подключения к службе вручную.

Основываясь на обзоре некоторых задач в репозиториях DevOps Azure, выясняется, что подключения служб и их данные заполняются как переменные среды на агенте, выполняющем задачу построения. Сервисные соединения извлекаются с помощью метода, который запускает данный name пропустите следующее регулярное выражение перед получением значения результирующего ключа среды:

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

Извлечение различных данных конечной точки службы заключено в модуль vsts-task-lib/task, что позволяет потребляющим задачам писать код следующим образом:

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 переменная окружения. Существует некоторое указание на то, что задачи сборки не "засеваются" соединениями, которые они не запрашивают специально, поэтому вам может потребоваться создать пользовательскую задачу сборки, в которой в качестве одного из входов будет указано имя подключения службы, которое вы хотите использовать

б) читать нужные значения из переменных, как указано выше в вашем скрипте bash. Имена переменных подключения к сервису могут быть вычислены следующим образом:

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

Возможно, вам придется повторить эту процедуру, чтобы определить схему / структуру данных.

Я обнаружил, что если я использую задачу Kubectl с командой для входа в систему непосредственно перед запуском моей задачи bash, мне не нужно проходить аутентификацию или использовать имя хоста.

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

Я использую то же сервисное соединение в своих скриптах / инструментах, что и для развертываний 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 действительно хорошо разбирается в секретах, поэтому они не отображаются в журналах конвейера.

Как заявляли другие, нет отличного встроенного способа доступа к Service Connections с помощью сценария. Поскольку мне не нравится обходной путь раскрытия учетных данных через долгоживущие переменные среды (в целях безопасности и лени), я написал расширение, которое позволяет использовать общее подключение к службе с настраиваемым сценарием: https://marketplace.visualstudio. com / items?itemName=cloudpup.authenticated-scripts

Он делает это, предоставляя подключение к службе как переменные среды, которые действуют только в течение всего времени существования единственной задачи сценария:

Пример

Задачи, включенные в это расширение, позволяют писать лаконичные конвейеры вроде следующего:

      steps:
- task: AuthenticatedPowerShell@1  
  inputs:
    serviceConnection: 'Testing Authenticated Shell'
    targetType: inline
    script: 'Write-Host "url: $env:AS_SC_URL | username: $env:AS_SC_USERNAME | password: $env:AS_SC_PASSWORD"'

Да, это может быть достигнуто, я использую этот метод все время, сначала вам нужна задача, которая будет выводить учетные данные в переменные среды, затем вы создаете свои собственные переменные из переменных среды, которые выводит задача, я обычно использую AzureCLI:

      # Set Variables.
- task: AzureCLI@2
  name: setVariables
  displayName: Set Output Variables
  continueOnError: false
  inputs:
    azureSubscription: nameOfAzureServiceConnection
    scriptType: ps
    scriptLocation: inlineScript
    addSpnToEnvironment: true # this must be set to true
    inlineScript: |
      Write-Host "##vso[task.setvariable variable=azureClientId;isOutput=true]$($env:servicePrincipalId)"
      Write-Host "##vso[task.setvariable variable=azureClientSecret;isOutput=true]$($env:servicePrincipalKey)"
      Write-Host "##vso[task.setvariable variable=azureTenantId;isOutput=true]$($env:tenantId)"

Затем вы можете использовать эти новые переменные, которые вы установили на шаге ниже, убедитесь, что вы вызываете переменные с именем задачи, поэтому $(taskName.variableName), в приведенном ниже примере используются новые переменные для установки переменных среды в более поздней задаче PowerShell. для использования Terraform для аутентификации:

      - PowerShell: |
     terraform plan -input=false -out=tfplan
 displayName: 'Terraform Plan'
 env:
   ARM_CLIENT_ID: $(setVariables.azureClientId)
   ARM_CLIENT_SECRET: $(setVariables.azureClientSecret)
   ARM_TENANT_ID: $(setVariables.azureTenantId)

ссылка: https://jimferrari.com/2021/11/15/access-azure-service-connection-via-script/

Если вам нужно использовать соединение службы для авторизации для различных служб / ресурсов, вы также можете получить необходимые токены с соединением службы и передать их скриптам, которые не могут использовать соединение службы напрямую, например:

      - task: AzurePowerShell@5
  inputs:
    azureSubscription: 'AzureServiceConnection'
    ScriptType: 'InlineScript'
    Inline: |
      $token = Get-AzAccessToken 
      echo "##vso[task.setvariable variable=accesstoken;]$($token.Token)"
    azurePowerShellVersion: 'LatestVersion'
  
- script: 'echo This is the token: $(accesstoken)'

Соглашаясь с Джошем (AFAIK), я думаю, что на сегодняшний день практически невозможно повторно использовать подключения к сервису с задачей Bash в Azure Pipelines, хотя мне нравится идея иметь многоразовые подключения к сервису!

Однако имейте в виду, что исходный код этой задачи открыт на GitHub, и что отзывы и предложения будут приветствоваться.:)

Другие вопросы по тегам