電通総研 テックブログ

電通総研が運営する技術ブログ

GitHub Actions と Salesforce DX CLI を使って Salesforce開発でCI/CDする(まずは認証編)

電通国際情報サービス、Xイノベーション本部デジタルエンゲージメントセンターの堀越です。

Salesforce の開発でも GitHub を使って CI/CD をできるようにしたいのですが、この記事ではそのやり方などについて書きます。

最近の Salesforceの開発では、Salesforce DXというコンセプトのもと、CLIを使って Salesforce上のローコード設定をXMLで取得したり反映したりできます。 XMLとして取り扱えれば、 GitHub などで構成管理したり、環境間の差異がないかの比較をしたりということも可能です。 当然ながらローコードだけでなくApexやVisualforce、LightningコンポーネントといったプログラムコードもCLIで取り扱うことができます。

ここまでは 個々の開発者でCLIとGitを利用すれば実現できるところです。 が、せっかく GitHub で構成管理するのであれば、Pull Request した際に自動テストやデプロイ可否のチェック(いわゆるCI/CD)もやりたくなりますね。

これを踏まえて、ここではSalesforce DX のCLIを日ごろから使っている & プロジェクト構成が GitHub上のリポジトリに登録されていることを前提に、CI/CDを組み込んでみたいと思います。 CI/CD の実行には、GitHub標準装備の GitHub Actions を使いたいと思います。

やりたいことのイメージは次のようなものです。 統合用のブランチにSandboxを対応させて、Pull Request されたときにそのSandboxにデプロイできるかを自動チェックできるようにしたいと思います。 SalesforceでのCI/CD

CLIによる Sandbox への接続

Salesforce DX の CLI では、まず Sandbox に接続(認証)し、それから取得やデプロイなどの操作を行う必要があります。 CLIによる認証にはいくつか方式が提供されていますが、CI/CDで利用可能なのは「JWTベアラートークンフロー」です。

JWTベアラートークンフローでの接続には、以下の準備が必要です。

  1. 秘密鍵とそれとペアになった証明書(自己署名証明書でOK)の用意
  2. Salesforce側の準備
  3. GitHub側の準備

秘密鍵とそれとペアになった証明書の用意

opensslで秘密鍵自己署名証明書を準備します。 自己署名証明書は、Salesforce側の「接続アプリケーション」設定に登録されることになります。 また、秘密鍵GitHub側で利用されることになります。

Salesforce 側の準備

Salesforce側がJWTベアラートークンフローでアクセスを受けるためには、「接続アプリケーション」の設定が必要となります。 「接続アプリケーション」では大きく以下の要素の設定が必要です。

  • JWTベアラートークンフローを有効化する諸設定
  • JWT を検証するための証明書のアップロード
  • 接続アプリケーションで利用するプロファイル(または権限セット)とユーザー

プロファイルまたは権限セットでSandbox内の操作可否を制御する認可の設定をします。 今回は、CI/CDでの利用のため、デプロイやテストを実行するための権限を与えておく必要があります。 また、そのプロファイルまたは権限セットに属する Salesforceユーザーで接続することになりますので、そのためのユーザーも用意しておく必要があります。

今回は、セキュリティを考慮し、デプロイやテストを実行するための最小の権限を持った専用ユーザーと専用プロファイルを用意することにしました。 また、このユーザーはブラウザでのログインを行わないものとし、API呼出専用のユーザーとして用意しておくことにします。

これらの一連の設定方法は、Salesforce DXのドキュメントの以下が参照できます。

https://developer.salesforce.com/docs/atlas.ja-jp.sfdx_dev.meta/sfdx_dev/sfdx_dev_auth_connected_app.htm

プロファイルの許可IPアドレス設定

プロファイルには、ログインが許可できるアクセス元IPアドレス範囲の指定が可能で、GitHubからのアクセスを許可するように設定するようにします。 ただ、つぶさに設定するのは数が多くて現実的ではないので広めのレンジにならざるを得ないものと思います。 その分、クレデンシャル情報はしっかりと管理しておく必要があるでしょう。 なお、GitHubのIPレンジは以下から取得できます。

https://docs.github.com/ja/authentication/keeping-your-account-and-data-secure/about-githubs-ip-addresses

プロファイルに与える権限

プロファイルで与える権限(システム権限)は、以下になります。

  • 「アプリケーションのカスタマイズ」(Customize Application)
  • メタデータ API 関数を使用したメタデータの変更」(Modify Metadata Through Metadata API Function)
  • API の有効化」(API Enabled)
  • API限定ユーザ」(API Only User)
  • 「すべてのデータの編集」(Modify All Data)

必要な権限についての情報は以下を参照しています。

https://developer.salesforce.com/docs/atlas.ja-jp.238.0.api_meta.meta/api_meta/meta_modify_metadata_perm.htm

なお、「すべてのデータの編集」は、全てのオブジェクトに対するCRUD操作を行える権限となります。 デプロイに伴ってApexのテストコードの実行が必要であり、それに伴ってオブジェクトのCRUD操作が必要となることから、この権限を与えておくのが無難でしょう。

GitHub側の準備

JWTベアラートークンフローで認証する際には、CLI コマンドで以下を指定する必要があります。

  • JWT を署名するための秘密鍵のファイル
  • Sandboxへの接続に利用するユーザー名
  • 接続アプリケーションのコンシューマ鍵(OAuthのクライアントID)
  • Salesforce環境のログインURL

認証のコマンドは以下のようなイメージとなります。

sfdx auth:jwt:grant --instanceurl=https://test.salesforce.com/ 
--jwtkeyfile 【秘密鍵ファイル名】 
--username 【接続ユーザー名】 --clientid 【接続アプリケーションのコンシューマ鍵】

ここで、 https://test.salesforce.com/ はログインURLであり、今回はSandboxを利用するためにこちらのURLとなっています。 もし本番環境に接続したい場合は Salesforce の「私のドメイン」で指定したURLを適切に指定する必要があると思われます。

「接続アプリケーションのコンシューマ鍵」は、準備した接続アプリケーション設定から取得します。 また、「接続ユーザー名」も同様にSalesforce 側の準備で作成したものを利用します。

セキュリティの考慮で secrets を使う

コンシューマ鍵や秘密鍵ファイルなどはクレデンシャル情報なのでリポジトリ内に含めるわけにはいきません。 そこで、 GitHub の secrets に登録しておくものとします。 また秘密鍵ファイルはX.509形式ですが、改行が含まれるため、そのまま secrets で扱うことはできません。 そこで、BASE64エンコードして secrets に登録しておき、それを実行時に取り出してBASE64デコード、一時ファイルに書き出して鍵ファイルとするものとします。

今回は secrets を利用しましたが、接続先の Sandbox が増えるようであれば、 Environments 機能を利用するのが良いでしょう。

CLIでの接続設定のイメージ

この接続設定をまとめると、イメージは次のようになります。

CLIでの接続設定のイメージ

CLIでの認証に向けた設定作業

以下、このイメージに沿って設定作業を進めていきます。

キーペアと証明書の準備

最初に、opensslで秘密鍵自己署名証明書を準備します。 この手順は比較的よくある手順であり、詳しくは割愛します。

Salesforce DXの公式ドキュメントに沿った手順としては以下が参照可能です。

https://developer.salesforce.com/docs/atlas.ja-jp.238.0.sfdx_dev.meta/sfdx_dev/sfdx_dev_auth_key_and_cert.htm

また、GitHub の secrets で秘密鍵の内容を保持できるようにするため、秘密鍵BASE64したものを控えておきます。

以下はそのコマンドイメージです。

UNIX系の場合: cat server.key | base64

Windowsの場合: openssl base64 -A -in server.key -out sever_base64.key

Sandbox側の設定

Salesforceの Sandbox に管理者でログインし、一連の設定を実施します。 プロファイルの作成と権限付与、ユーザーの作成はよくある手順ですので、詳細は割愛します。

接続用のプロファイルの準備

「設定」画面にて、「クイック検索」 ボックスに「プロファイル」と入力し、「プロファイル」を選択、設定画面に遷移します。 新しいプロファイルを作り、上述の「プロファイルの設定」で示した権限を与えます。 このプロファイルは、のちに接続アプリケーションに紐づけることになりますので、名前を控えておきます。 (今回は、「sfdx_connect」というプロファイル名を設定することとしました)

仮に他のプロファイルをコピーして作った場合には、アクセス元IPアドレスの許可レンジが自社IPアドレスのままなどになっている場合があるでしょう。 今回のアクセス元は GitHub Actions のサーバになるので、その指定の変更を忘れないようにしましょう。

接続用のユーザーの準備

同様に、「クイック検索」 ボックスに「ユーザ」と入力しユーザー設定画面に遷移します。 ユーザーを新規作成し、作ったプロファイルにユーザーを割り当てます。

作ったユーザーはGitHub Actionsでの接続に利用されますので、ユーザー名を控えておきます。

接続アプリケーションの準備

Salesforce DX の認証を実施するため、接続アプリケーションの設定を行います。

この一連の設定方法は、Salesforce DXのドキュメントの以下を参照します。

https://developer.salesforce.com/docs/atlas.ja-jp.sfdx_dev.meta/sfdx_dev/sfdx_dev_auth_connected_app.htm

基本的にはこの設定手順に沿いますが、「JWTベアラートークンフロー」なので「(JWTのみ)」と書いてある項目も設定が必要です。

  1. 「設定」画面にて、「クイック検索」 ボックスに「アプリケーションマネージャ」と入力し、「アプリケーションマネージャ」を選択、設定画面に遷移します。
  2. 「新規接続アプリケーション」をクリックします。
  3. 接続アプリケーションの「基本情報」を入力します。

    1. 「接続アプリケーション名」: ここでは「sfdx」などとします。
    2. API参照名」: ここでは「sfdx」などとします。
    3. 「取引先責任者 メール」: ここでは、開発チームが利用することになるため、開発者のメールアドレスを設定することでよいでしょう。
    4. 上記以外は空欄でOKです 新規接続アプリケーション
  4. 「OAuth 設定の有効化」を選択します。

  5. コールバック URL に「http://localhost:1717/OauthRedirect」と入力します。JWTベアラートークンフローでは使われない設定なのですが、必須項目なので設定する必要があります。
  6. 「デジタル署名を使用」をチェックします。
  7. 「ファイルを選択」をクリックし、用意した証明書ファイルをアップロードします。
  8. 次の OAuth 範囲を追加します。
  9. API を使用してユーザデータを管理 (api)
  10. Web ブラウザを使用してユーザデータを管理 (web)
  11. いつでも要求を実行 (refresh_token, offline_access) OAuth設定
  12. 残りはデフォルトとし、「保存」をクリックします。

作成した接続アプリケーションに対し、セキュリティポリシーを設定します。 CI/CDで利用する場合には、接続のたびに認証(sfdx auth:jwt:grant コマンド)を行うため、セッション時間などは短くしておいて問題ないという考え方になります。 また、許可するユーザーもユーザー自身が登録するフローは利用できないので、事前に管理者がプロファイルとして許可したユーザーのみがアクセス可能であるように設定します。

  1. 作成した接続アプリケーションの画面から、「Manage」をクリックします。
  2. 「ポリシーを編集」 をクリックします。
  3. 「OAuth ポリシー」セクションの「更新トークンポリシー」項目で「次の時間が経過したら更新トークンを期限切れにする:」をクリックし、90 日以下を入力します。(ここでは1時間)
  4. 「セッションポリシー」セクションで「タイムアウト値」を 15 分に設定します。
  5. 「OAuth ポリシー」セクションで、「許可されているユーザ」に「管理者が承認したユーザは事前承認済み」を選択し、「OK」をクリックします。 ポリシーの設定
  6. 「保存」をクリックします。

接続アプリケーションに、作成したプロファイルを関連付けます。

  1. 「プロファイルを管理する」をクリックし、接続用に準備したプロファイルを選択します。

続いて、「コンシューマ鍵」を取得します。 これは、CLIでの認証時に指定されるもので、GitHub の secrets に登録されるものとなります。

  1. 作成した接続アプリケーションの画面から、「コンシューマの詳細を管理」をクリックします。
  2. 場合によってはID検証が実行されますので、その場合は管理者の認証を行います。
  3. 「顧客の詳細」の「コンシューマ鍵」の内容をコピーし、控えておきます。

GitHubリポジトリ側の設定

GitHubにログインし、リポジトリの画面から設定を行います。

Secretsの設定

Salesforce への接続情報を secrets に設定します。

  1. 接続するリポジトリの「Settings」タブを選択します。
  2. 左側のメニューの「Security」-「Secrets」-「Actions」を選択します。
  3. 「New repository secret」ボタンをクリックし、secrets の登録画面に遷移します。

secrets の登録画面で、以下の3つを登録します。

  • ORG_USER: Salesforce で作成したユーザーのユーザー名
  • CLIENT_ID: 接続アプリケーションのコンシューマ鍵
  • PRIVATE_KEY_BASE64ED: 秘密鍵BASE64したもの

GitHub Actions ワークフローの作成

次の GitHub Actions ワークフローを作り、統合用ブランチの .github/workflowsディレクトリに、deploy-to-sandbox.yml などの名前で作成、プッシュします。 ここでは、統合用ブランチの名前は「integration」としています。

name: salesforce ci
on: 
  pull_request:
    branches:
      - integration

jobs:
  deploy_to_sandbox:
    runs-on: ubuntu-latest
    steps:
      - name: Check out code
        uses: actions/checkout@v3

      - name: Make temporary directory
        run: |
          mkdir ./temp

      # Install Salesforce CLI
      - name: Install Salesforce CLI
        run: |
          wget https://developer.salesforce.com/media/salesforce-cli/sfdx/channels/stable/sfdx-linux-x64.tar.xz
          mkdir ./temp/sfdx-cli
          tar xJf sfdx-linux-x64.tar.xz -C ./temp/sfdx-cli --strip-components 1
          echo "./temp/sfdx-cli/bin" >> $GITHUB_PATH
      - name: Display sfdx version
        run: sfdx version

      # Save into private-key
      - name: Save into private-key
        run: echo -n ${{ secrets.PRIVATE_KEY_BASE64ED }} | base64 -d > ./temp/server.key

      # Authenticate to access the sandbox
      - name: Authenticate to access the sandbox
        run: sfdx force:auth:jwt:grant --instanceurl=https://test.salesforce.com/ --jwtkeyfile ./temp/server.key --username ${{ secrets.ORG_USER }} --clientid ${{ secrets.CLIENT_ID }}

ワークフローの流れとしては以下のようになっています。

  • Pull Request で GitHub Actions が実行されるようにしています。
  • チェックアウトののち、Salesforce DX CLIの最新版をwgetで取得して一時ディレクトリにインストール、パスを通しています。
  • secrets に登録しておいた秘密鍵BASE64デコードして一時ファイルに書き出しています。
  • 最後に、これらを使って sfdx force:auth:jwt:grant コマンドでSalesforceに接続/認証しにいきます。

GitHub Actions ワークフローの接続検証

この状態で何か統合用ブランチに対してPull Request すれば、このワークフローが実行されるでしょう。

Actionsタブからワークフローの「salesforce ci」を見つけ、ワークフローの実行結果を確認します。 「Authenticate to access the sandbox」の部分が、以下のように表示されていれば成功です。

CLI認証後の操作

認証さえできてしまえば、あとは通常のCLI操作と変わりません。 デプロイやそのための事前チェックであれば sfdx force:source:deploy コマンドを利用することになるでしょう。

接続時のトラブルシューティングのヒント

Salesforce側のID検証履歴を確認

接続アプリケーションであっても、原理としてはSalesforceユーザーを介してSalesforceにログインすることになります。 このため、Salesforce側の「ID検証履歴」を見ることで、ある程度どういう状況であるかを確認できます。

https://help.salesforce.com/s/articleView?id=sf.security_verification_history.htm&type=5

「設定」画面にて、「クイック検索」 ボックスに「検証履歴」と入力し、「ID 検証履歴」を選択、履歴表示画面に遷移します。 CLIで利用するユーザーによるログインもこの履歴にエントリーされます。

もし、この履歴にログがない場合、以下のような状況であると思われます。

  • 接続処理に至る前の GitHub Actions の処理で失敗している
  • その Sandbox には指定のユーザーが存在していない
  • 接続したいSandbox組織とログインURLが合っていない(SandboxなのにログインURLに login.salesforce.com が指定されている、あるいはその逆など)

接続に失敗した状況が履歴に記載されているようであれば、その内容をもとにして対応します。 典型的には以下のような原因が考えられるでしょう。

  • ユーザーに適切なプロファイルが割り当たっていない
  • プロファイルに設定された権限が不正
  • アクセス元のIPアドレスで弾かれている

余談

今回は、Salesforce CLIwget で取得してインストールしました。 実際には、以下のにホスティングされている Action を利用することも可能でしょう。

https://github.com/forcedotcom/salesforcedx-actions

ただ、(Unofficial)という記述があるので、今回については手動でインストールしています。 実行速度などを考慮するとこちらを使ってみてもいいかもしれません。

執筆:@horikoshi.yuji、レビュー:@hashizume.hidekiShodoで執筆されました