🐈

Copilot で Azure Pipelines を効率化する

2024/07/06に公開

Copilot で Azure Pipelines を効率化する

Copilot を使って Azure Pipelines でどのようなものができるかを試してみた結果です。ぜひ使い方の参考にしてください🐈

パイプライン定義の生成

Prompt:

Azure Pipelines YAML を生成してください。YAML の構成は以下のとおりです。

条件:
ステージは、ビルド、ステージング、プロダクションの流れ
プロダクションへのステージ開始には管理者の承認操作が必要

YAML:
1. .net8.0 の web アプリをビルドする
2. web アプリのテストを実行する
3. ビルドしたものを Azure Web Apps のステージングスロットへデプロイ
4. プロダクジョンへのデプロイ方法は、ステージングスロットとのスワップをする

Copilot:

trigger:
- main

pool:
  vmImage: 'ubuntu-latest'

jobs:
- job: BuildAndDeploy
  steps:
  - task: UseDotNet@2
    inputs:
      packageType: 'sdk'
      version: '8.x'
      installationPath: $(Agent.ToolsDirectory)/dotnet
  - script: dotnet build --configuration Release
    displayName: 'Build .NET app'
  - script: dotnet test
    displayName: 'Run tests'
  - task: AzureRmWebAppDeployment@4
    inputs:
      ConnectionType: 'AzureRM'
      azureSubscription: 'YourAzureSubscription'
      appType: 'webApp'
      WebAppName: 'YourWebAppName'
      packageForLinux: '$(Build.ArtifactStagingDirectory)/**/*.zip'
      deploymentSlot: 'staging'

だいたいあっていますね。これが実際に正常動作するかは検証が必要ですが、おそらく dotnet publish ステップが不足しているように見えます。ここでは直しませんが、実際にパイプラインを実行すると AzureRmWebAppDeployment@4 タスクで zip ファイルが見つからないエラーが発生するのではないでしょうか。
しかし条件は完全にスルーされており、「4. プロダクジョンへのデプロイ方法は、ステージングスロットとのスワップをする」もないですね。こちらは追加でプロンプトを書いてみます。

Additional Prompt:

YAML 定義で Web app のステージングスロットをスワップするためのタスクを追加してください

Copilot:

trigger:
- main

pool:
  vmImage: 'ubuntu-latest'

jobs:
- job: BuildAndDeploy
  steps:
  - task: UseDotNet@2
    inputs:
      packageType: 'sdk'
      version: '8.x'
      installationPath: $(Agent.ToolsDirectory)/dotnet
  - script: dotnet build --configuration Release
    displayName: 'Build .NET app'
  - script: dotnet test
    displayName: 'Run tests'
  - task: AzureRmWebAppDeployment@4
    inputs:
      ConnectionType: 'AzureRM'
      azureSubscription: 'YourAzureSubscription'
      appType: 'webApp'
      WebAppName: 'YourWebAppName'
      packageForLinux: '$(Build.ArtifactStagingDirectory)/**/*.zip'
      deploymentSlot: 'staging'
  - task: AzureAppServiceManage@0
    inputs:
      azureSubscription: 'YourAzureSubscription'
      WebAppName: 'YourWebAppName'
      ResourceGroupName: 'YourResourceGroupName'
      SlotName: 'staging'
      Action: 'Swap Slots'

パイプライン YAML は大体 OK ですね〜!次は上記を使ってシナリオの条件を追加してみます。

Prompt:

以下の Azure Pipelines YAML 定義をビルド、デプロイ、スワップの3つのステージに分割してください。分割後のYAMLも表示してください

```yaml
trigger:
- main

pool:
  vmImage: 'ubuntu-latest'

jobs:
- job: BuildAndDeploy
  steps:
  - task: UseDotNet@2
    inputs:
      packageType: 'sdk'
      version: '8.x'
      installationPath: $(Agent.ToolsDirectory)/dotnet
  - script: dotnet build --configuration Release
    displayName: 'Build .NET app'
  - script: dotnet test
    displayName: 'Run tests'
  - task: AzureRmWebAppDeployment@4
    inputs:
      ConnectionType: 'AzureRM'
      azureSubscription: 'YourAzureSubscription'
      appType: 'webApp'
      WebAppName: 'YourWebAppName'
      packageForLinux: '$(Build.ArtifactStagingDirectory)/**/*.zip'
      deploymentSlot: 'staging'
  - task: AzureAppServiceManage@0
    inputs:
      azureSubscription: 'YourAzureSubscription'
      WebAppName: 'YourWebAppName'
      ResourceGroupName: 'YourResourceGroupName'
      SlotName: 'staging'
      Action: 'Swap Slots'
```

Copilot:

trigger:
- main

pool:
  vmImage: 'ubuntu-latest'

jobs:
- job: Build
  steps:
  - task: UseDotNet@2
    inputs:
      packageType: 'sdk'
      version: '8.x'
      installationPath: $(Agent.ToolsDirectory)/dotnet
  - script: dotnet build --configuration Release
    displayName: 'Build .NET app'
  - script: dotnet test
    displayName: 'Run tests'

- job: Deploy
  steps:
  - task: AzureRmWebAppDeployment@4
    inputs:
      ConnectionType: 'AzureRM'
      azureSubscription: 'YourAzureSubscription'
      appType: 'webApp'
      WebAppName: 'YourWebAppName'
      packageForLinux: '$(Build.ArtifactStagingDirectory)/**/*.zip'
      deploymentSlot: 'staging'

- job: SwapSlots
  steps:
  - task: AzureAppServiceManage@0
    inputs:
      azureSubscription: 'YourAzureSubscription'
      WebAppName: 'YourWebAppName'
      ResourceGroupName: 'YourResourceGroupName'
      SlotName: 'staging'
      Action: 'Swap Slots'

いいですね!最後に2つほど細かく設定してもらいます。

Additional Prompt:

デプロイのジョブは、environments を使ったデプロイメントジョブに変更してくれますか

Copilot:

trigger:
- main

pool:
  vmImage: 'ubuntu-latest'

jobs:
- job: Build
  steps:
  - task: UseDotNet@2
    inputs:
      packageType: 'sdk'
      version: '8.x'
      installationPath: $(Agent.ToolsDirectory)/dotnet
  - script: dotnet build --configuration Release
    displayName: 'Build .NET app'
  - script: dotnet test
    displayName: 'Run tests'

- deployment: Deploy
  environment: 'YourEnvironmentName'
  strategy:
    runOnce:
      deploy:
        steps:
        - task: AzureRmWebAppDeployment@4
          inputs:
            ConnectionType: 'AzureRM'
            azureSubscription: 'YourAzureSubscription'
            appType: 'webApp'
            WebAppName: 'YourWebAppName'
            packageForLinux: '$(Build.ArtifactStagingDirectory)/**/*.zip'
            deploymentSlot: 'staging'

- job: SwapSlots
  steps:
  - task: AzureAppServiceManage@0
    inputs:
      azureSubscription: 'YourAzureSubscription'
      WebAppName: 'YourWebAppName'
      ResourceGroupName: 'YourResourceGroupName'
      SlotName: 'staging'
      Action: 'Swap Slots'

Additional Prompt:

jobs を stages に変換してください

Copilot:

trigger:
- main

pool:
  vmImage: 'ubuntu-latest'

stages:
- stage: Build
  jobs:
  - job: Build
    steps:
    - task: UseDotNet@2
      inputs:
        packageType: 'sdk'
        version: '8.x'
        installationPath: $(Agent.ToolsDirectory)/dotnet
    - script: dotnet build --configuration Release
      displayName: 'Build .NET app'
    - script: dotnet test
      displayName: 'Run tests'

- stage: Deploy
  jobs:
  - deployment: Deploy
    environment: 'YourEnvironmentName'
    strategy:
      runOnce:
        deploy:
          steps:
          - task: AzureRmWebAppDeployment@4
            inputs:
              ConnectionType: 'AzureRM'
              azureSubscription: 'YourAzureSubscription'
              appType: 'webApp'
              WebAppName: 'YourWebAppName'
              packageForLinux: '$(Build.ArtifactStagingDirectory)/**/*.zip'
              deploymentSlot: 'staging'

- stage: SwapSlots
  jobs:
  - job: SwapSlots
    steps:
    - task: AzureAppServiceManage@0
      inputs:
        azureSubscription: 'YourAzureSubscription'
        WebAppName: 'YourWebAppName'
        ResourceGroupName: 'YourResourceGroupName'
        SlotName: 'staging'
        Action: 'Swap Slots'

よくなりましたね!一点だけ補足があるとすれば "- deployment: Deploy" は利用できない制限文字のため、"- deployment: StageDeploy" などに変更する必要があります。これはパイプライン実行時にエラーとして気がつくことができますね。

このように、それなりのパイプライン定義を作ってもらうことができましたが、実際の作業ではトライアンドエラーを繰り返しながらパイプラインを実行したり、ドキュメントを見て自分で修正したり、さらに Copilot に詳しく書いてもらう必要があります。

パイプラインエラーの解析

とりあえず期待する結果とエラーログをそのまま貼り付けてみます。

Prompt:

Azure Pipelines の AzurePowerShell タスクで発生した次のエラーの原因と対処方法を教えて下さい
---
Retrieving subscriptions for the selection...
[Announcements]
With the new Azure PowerShell login experience, you can select the subscription you want to use more easily. Learn more about it and its configuration at https://go.microsoft.com/fwlink/?linkid=2271909.

If you encounter any problem, please open an issue at: https://aka.ms/azpsissue

Set-AzContext -SubscriptionId 092a8b61-13d5-4eb7-a4cb-041b4600a31a -TenantId 85cdabbe-3476-426f-8c4e-072e00cf8d34
Set-AzContext: /home/vsts/work/_tasks/AzurePowerShell_72a1931b-effb-4d2e-8fd8-f8472a07cb62/5.241.4/InitializeAz.ps1:104
Line |
104 |  …     $null = Set-AzContext -SubscriptionId $SubscriptionId @additional …
    |                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    | Please provide a valid tenant or a valid subscription.
##[error]PowerShell exited with code '1'.

Copilot:

このエラーは、`Set-AzContext` コマンドが有効なテナントまたはサブスクリプションを提供していない場合に発生します。具体的には、指定された Azure Resource Manager サービス接続で認証されていないためです。

以下の手順で問題を解決できます。

1. **Azure サブスクリプションに接続**: Azure サブスクリプションに接続してください。これには、Azure PowerShell モジュールを使用して接続するか、Azure サービス接続を構成することが含まれます。

2. **テナント ID を確認**: `Set-AzContext` コマンドで使用するテナント ID が正しいことを確認してください。テナント ID は Azure Active Directory テナントの一意の識別子です。

3. **サブスクリプション ID を確認**: 同様に、使用するサブスクリプション ID も正しいことを確認してください。サブスクリプション ID は Azure サブスクリプションの一意の識別子です。

4. **Azure PowerShell バージョンを確認**: `AzurePowerShell` タスクでは、既定では Azure PowerShell v5 が使用されます。必要に応じて、最新バージョンの PowerShell を使用するように設定できます¹。

この問題の実際の原因としては Azure サブスクリプションが削除されたことによる影響でした。 Copilot はトラブルシューティングの方法を教えてくれるのでよいですね。
エラーの内容によっては一発で解決することは難しいかもしれませんが、初動の対応が全くわからない場合や、トラブルシューティングを進めていくうえで発生した不明な点などを聞いてみるのがよいと思いました。

パイプライン YAML 構文エラーの解析

パイプライン YAML 構文のエラーになっている状態のため Copilot に聞いてみます。

Prompt:

以下のパイプラインYAMLで構文エラーが発生しています。どのように修正すればよいですか?

エラー /azure-pipelines.yml (Line: 4, Col: 11): Unexpected value ''

```yaml
trigger:
- main

variables:
name: aaa

pool:
  vmImage: ubuntu-latest

steps:
- bash: echo aaa
```

Copilot:

このエラーは、YAML パイプラインの構文に問題があることを示しています。具体的には、`variables` セクションの定義が正しくありません。`variables` セクションは、パイプラインの直下ではなく、別のテンプレートで定義する必要があります。

以下の修正を行ってみてください:

1. `variables` セクションを削除します。パイプラインの直下には `variables` は不要です。
2. もしくは、`variables` セクションを別のテンプレートに移動させてください。

修正後のYAMLは次のようになります:

```yaml
trigger:
- main

pool:
  vmImage: ubuntu-latest

steps:
- bash: echo aaa
```

ある意味では正しい回答なのですが、今回は variables を使いたい目的があったためそのように追加で聞いてみます。

Additional Prompt:

variables は利用する必要があります。 variables を定義に記述した形で修正してください

Copilot:

trigger:
- main

variables:
  name: aaa

pool:
  vmImage: ubuntu-latest

steps:
- bash: echo ${{ variables.name }}

これは求めている結果ですね!variables.name のインデントが間違えていたことが原因でした。
※ bash スクリプトが期待せずに変更されていますが、これは問題の本質ではないため気にしないことにします。

パイプラインの設定

ここでは、パイプライン定義以外の部分に関する問題について聞いてみます。

Prompt:

Azure Pipelines を使っています。あるパイプラインではセキュリティ求められているため、特定のユーザーのみ参照・実行できるようにする必要があります。具体的な設定方法を教えて下さい

Copilot:

Azure Pipelines でパイプラインのアクセス許可を制御するためには、以下の手順を試してみてください。

1. **YAML パイプラインを使用する**:
  - YAML パイプラインは、Azure Pipelines に最適なセキュリティを提供します。クラシック ビルドおよびリリース パイプラインとは異なり、YAML パイプラインでは次のようなセキュリティ機能があります:
    - **コードをレビューできます**: 悪意のあるアクターが pull request を使って変更のマージを強制的に行うことでパイプラインに悪意のあるステップを導入するのを防ぐことができます。ブランチ ポリシーを使うと、これを簡単に設定できます。
    - **リソース アクセス管理を提供します**: リソース所有者は、YAML パイプラインがリソースにアクセスできるかどうかを決定します。このセキュリティ機能は、別のリポジトリの盗み出しのような攻撃を制御します。
    - **承認とチェック**: パイプラインの実行ごとにアクセス制御が提供されます。
    - **ランタイム パラメーターをサポート**: 変数に関連するセキュリティの問題のホストを回避するのに役立ちます。

2. **リソース アクセスを制御する**:
  - プロジェクトから「ライブラリ」を選択し、ユーザーやグループのアクセス許可を設定します。特定の機能やタスクへのアクセスを制御するために、ユーザーとグループのアクセス許可を設定してビルド パイプラインをセキュリティで保護できます¹².

3. **セキュア ファイルを使用する**:
  - セキュア ファイルは暗号化され、タスクから使うときにのみ使用できます。承認とチェックを追加し、パイプラインのアクセス許可を設定できます⁴.

4. **パイプラインの監視とアラート設定**:
  - パイプラインの活動を監視し、不審な動作に対してアラートを設定します。
  - 定期的にアクセス許可を見直して、エクスポージャーを最小限に抑えましょう。

これらの手順を試して、パイプラインのセキュリティを向上させてください。もしさらなる質問があればお知らせください! 😊

Source: Conversation with Copilot, 7/5/2024
(1) Azure Pipelines でセキュリティを管理する - Azure Pipelines. https://learn.microsoft.com/ja-jp/azure/devops/pipelines/policies/permissions?view=azure-devops.
(2) Azure Pipelines でビルド パイプラインのアクセス許可を設定する - Azure Pipelines. https://learn.microsoft.com/ja-jp/azure/devops/pipelines/policies/pipeline-permissions?view=azure-devops.
(3) パイプラインのリソースについて - Azure Pipelines | Microsoft Learn. https://learn.microsoft.com/ja-jp/azure/devops/pipelines/process/about-resources?view=azure-devops.
(4) Azure Pipelines でリリース パイプラインのアクセス許可を設定する - Azure Pipelines. https://learn.microsoft.com/ja-jp/azure/devops/pipelines/policies/release-pipeline-permissions?view=azure-devops.

包括的な回答になっていますね。文章自体はこんなことができますよーくらいですが、具体的な手順や権限についてはリンク先のドキュメントにあるので「パイプラインの権限設定に関するドキュメントを探してほしい」みたいな聞き方でもよさそうです。

Azure CLI / REST API の生成

Azure DevOps 関連の Azure CLI と REST API についての実装方法を聞いてみます。

Prompt:

Azure CLI を使って Azure DevOps のパイプライン定義一覧を取得するための方法が知りたい

Copilot:

Azure CLIを使用してAzure DevOpsのパイプライン定義一覧を取得するには、`az pipelines list` コマンドを使用します。このコマンドは、指定されたAzure DevOps組織またはプロジェクト内のすべてのパイプラインの一覧を表示します。以下に、パイプライン一覧を取得するための基本的なコマンド例を示します。

```azurecli
az pipelines list --organization https://dev.azure.com/YourOrganization --project YourProject
```

このコマンドでは、`--organization` パラメーターにAzure DevOpsの組織URLを、`--project` パラメーターにプロジェクト名を指定します。結果はデフォルトでJSON形式で出力されますが、`--output` パラメーターを使用して出力形式を変更することができます¹。

Azure CLI では完璧な回答ですね。続いて REST API でも聞いてみます。

Prompt:

Azure DevOps REST API を使って Azure DevOps のパイプライン定義一覧を取得するための方法が知りたい

Copilot:

Azure DevOps のパイプライン定義一覧を取得するためには、Azure DevOps Services の REST API を使用できます。以下の手順で取得できます:

1. **REST API リクエストの構築**:
  - リクエスト URI を作成します。URI の形式は次のとおりです:
    ```
    VERB https://{instance}[/{team-project}]/_apis[/{area}]/{resource}?api-version={version}
    ```
    - `instance`: リクエストを送信する Azure DevOps Services の組織または TFS サーバーです。
      - Azure DevOps Services: `dev.azure.com/{organization}`
      - TFS: `{server:port}/tfs/{collection}` (デフォルトのポートは 8080 で、コレクションの値は `DefaultCollection` ですが、任意のコレクションを使用できます)
    - `resource path`: リソースパスは `_apis/{area}/{resource}` の形式です。例えば `_apis/wit/workitems` です。
    - `api-version`: API リクエストごとに `api-version` を含める必要があります。API バージョンは次の形式です:
      - `api-version=1.0`
      - `api-version=1.2-preview`
      - `api-version=2.0-preview.1`
  - 必要な HTTP リクエストメッセージヘッダーフィールドを指定します。Azure REST API は GET、HEAD、PUT、POST、および PATCH メソッドをサポートしています。
  - 必要に応じて追加のヘッダーフィールドを指定します(例:認証情報を含む Authorization ヘッダー)。
  - POST や PUT の場合、リクエストヘッダーの Content-type で MIME エンコードタイプを指定します(例:`application/json`)。

2. **API リクエストを送信**:
  - 上記で構築したリクエストを送信します。
  - 応答を処理します。

3. **REST クライアントを使用**:
  - REST クライアントを使用して API リクエストを行うこともできます。

詳細な情報は [Azure DevOps Services REST API リファレンス](https://learn.microsoft.com/en-us/rest/api/azure/devops/?view=azure-devops-rest-7.2) を参照してください。¹

汎用的に回答をくれていますね。わかりづらいので追加で聞いてみます。

Additional Prompt:

今回取得したい Azure DevOps の組織は dev.azure.com/admin-sea で、プロジェクトは a です。この情報からパイプラインの一覧を取得するための REST API を教えて下さい

Copilot:

Azure DevOps のパイプライン定義一覧を取得するためには、以下の REST API リクエストを使用できます:

```plaintext
GET https://dev.azure.com/admin-sea/_apis/pipelines?api-version=7.2
```

このリクエストは、`admin-sea` 組織のプロジェクト `a` 内のすべてのパイプラインを一覧表示します。必要に応じて認証情報を含むヘッダーを追加してリクエストを送信してください。¹²

短くなってわかりやすくなりました!ただし回答くれたコマンドは実際には間違っており、組織名の隣のプロジェクト名が抜けています。また、 API バージョン 7.2 はプレビューなので 7.2-preview にするか 7.1 に変更する必要があります。
このあたりは REST API ドキュメントのリンクも乗せてくれているので、 REST API の実行で期待する結果が返ってこない場合にはドキュメントも一緒に確認したほうが確実ですね。

終わりに

いくつかのシナリオで Copilot が使えそうなことが確認できました!
Copilot は必ずしも正しい結果を返すわけではないため、期待する結果が得られなかった場合には更に詳しく背景を伝えて Copilot に質問してみるか、もしくは回答くれた参考ドキュメントを見るとより効率化ができそうですね。

ぜひ Copilot と Azure Pipelines を楽しく利用してみてください🐈

Microsoft (有志)

Discussion