Salesforce CLI で CircleCI から Salesforce 組織にログインしてみた

今回は Salesforce CLI を使って CI/CD ツールである CircleCI のジョブから Salesforce の組織にログインしてみました。CircleCI のような CI/CD ツールから Salesforce の組織を操作できるようになると、Salesforce 開発におけるテストやパッケージの作成、インストールを自動化できるようになり、開発プロセスを効率化する事ができるようになります。とは言え、今回はログインするところまでやってみます。

## Salesforce で接続アプリケーションを作成する
CircleCI のような外部のサービスから Salesforce 組織にアクセスするには、Salesforce 側で接続アプリケーションを用意します。接続アプリケーションを作成する事で、アクセスの暗号化や適切な権限の付与が可能になります。

### ローカルで自己署名証明書を作成
まずは、そのために必要な電子証明書を作成します。今回は自己署名証明書で実施します。

```bash
# プライベートキーを生成し、server.key ファイルに出力
$ openssl genrsa -des3 -passout pass:MySecretPassword -out server.pass.key 2048
$ openssl rsa -passin pass:MySecretPassword -in server.pass.key -out server.key
$ rm server.pass.key

# server.key から証明書署名要求(CSR)を作成し、 server.csr に出力
# プロンプトが出るので質問に沿って値を入力
$ openssl req -new -key server.key -out server.csr

# 自己署名証明書(Self-signed certificate) を作成し、 server.crt に出力
$ openssl x509 -req -sha256 -days 365 -in server.csr -signkey server.key -out server.crt

# 証明書署名要求(CSR)は必要ないので削除
$ rm server.csr
```

### Salesforce で接続アプリケーションを作成
次に Salesforce 組織にログインして、接続アプリケーションを作成します。
`設定 > アプリケーション > アプリケーションマネージャ > 新規接続アプリケ−ション` から、以下の要領で必要な項目をすべて埋めていきます。この時、先程作成した**自己署名証明書のアップロードを忘れないでください**。

![スクリーンショット_2020-12-28_9_41_22-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26378/e38ad147-6014-e96a-62ad-121484d248c7.png)

保存したら、 `Manage > ポリシーを編集` を開き、 `許可されているユーザー`の値を変更して保存します。

![スクリーンショット_2020-12-28_12_12_03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26378/0eb9bcac-870f-72da-8e07-f9c057ace99f.png)

### Salesforce で権限セットを作成
Salesforce 組織上では、ブラウザ上からの操作やシステム間通信による操作に関わらず、すべての操作がユーザーに紐づいています。そのため、今回も CircleCI からのアクセスを誰の操作として扱うのかを明確にするためにユーザーが必要になります。このような場合は、専用のユーザーを作成する事が多いのですが、今回は簡便のため、システム管理者(自分のユーザー)を使います。そして、このユーザーに先程作成した接続アプリケーションを介した Salesforce 組織へのアクセスを許可するための権限セットを作成していきます。

`設定 > ユーザ > 権限セット` を開き `新規` から、CircleCI 接続アプリケーション権限 を作成します。

![スクリーンショット_2020-12-28_9_34_42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26378/0d0cce72-bbed-4865-bd82-fc0981d9d678.png)

その権限セットの `割り当てられた接続アプリケーション` として、 `CircleCI 接続アプリケーション` を割り当てます。

![スクリーンショット_2020-12-28_12_34_16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26378/1ea1dc6a-a637-674e-2b4a-1a1c17c08fbd.png)

そして、この権限セットを自分のユーザーに割り当てれば完了です。
![スクリーンショット_2020-12-28_12_35_52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26378/b85882b2-d51d-b6d8-fdfe-eacd7674455e.png)


###　CLI から接続アプリケーションの動作確認
ここまでできたら、念の為 Salesforce CLI からログインできるか確認してみましょう。

```bash
# Salesforce CLI からログインできるか確認
# -i には作成した接続アプリケーションで発行されたコンシューマ鍵を指定する
# -f には先程作成した server.key を指定する
$ sfdx auth:jwt:grant -i 3MVG95m... -f server.key -u admin@example.com
Successfully authorized admin@example.com with org ID 00DXXXXXXXXXXXX
```

## CircleCI から Salesforce 組織にログインする
Salesforce 組織の準備ができたので、次は CircleCI のジョブから Salesforce 組織にログインできるように SFDX プロジェクトと CircleCI を設定していきます。ここでは、プロジェクト名とリポジトリ名を `sdfx-cicleci-integration` にしています。

### ローカルで SFDX プロジェクトを設定
SFDX プロジェクト内に .circleci フォルダを作成し、その下に config.yml ファイルを作成します。config.yml には CircleCI で実行するジョブを記述しておきます。このジョブでは、リポジトリからソースコードを取得、Salesforce CLI をインストール、Salesforce 組織(Dev Hub) にログイン、ログイン済みの組織の一覧を表示します。

```yml:.circleci/config.yml
jobs:
  build:
    machine: true
    working_directory: ~/project
    steps:
      - checkout
      - run:
          name: Setup Salesforce CLI
          command: |
            mkdir sfdx
            wget -qO- https://developer.salesforce.com/media/salesforce-cli/sfdx-linux-amd64.tar.xz | tar xJ -C sfdx --strip-components 1
            ./sfdx/install
      - run:
          name: Login to Dev Hub
          command: |
            openssl enc -nosalt -aes-256-cbc -d -in .circleci/server.key.enc -out .circleci/server.key -base64 -K $DECRYPTION_KEY -iv $DECRYPTION_IV
            sfdx force:auth:jwt:grant -i $DEVHUB_CONSUMER_KEY -f .circleci/server.key -u $DEVHUB_USER_NAME -d -a DevHub
      - run:
          name: List Connected Orgs
          command: sfdx force:org:list
```

同時に、Salesforce 組織への認証に必要な server.key を暗号化した上で .circleci フォルダに入れておきます。生成されたキー(key)と初期化ベクトル(iv)は、ジョブの実行時に server.key を復号化するために使うので残しておきましょう。

```bash
# キー(key)と初期化ベクトル(iv)を生成
$ openssl enc -aes-256-cbc -k MySecretPassPhrase -P -md sha1 -nosalt
key=62102...
iv =EB565...

# server.key を暗号化し、server.key.enc に書き出す
$ openssl enc -nosalt -aes-256-cbc -in server.key -out server.key.enc -base64 -K 62102... -iv EB565...

# server.key.enc を .circleci 配下に移動
$ mv server.key.enc path/to/your/project/.circleci/
```

最終的に SFDX プロジェクト内の .circleci フォルダのは以下のようになっている想定です。ここまで出来たら GitHub のリモートリポジトリにプッシュしておきます。
![スクリーンショット_2020-12-28_16_41_57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26378/a947c62f-d9f4-d869-3c6b-e7786741ac7e.png)

### CircleCI で環境変数を設定
CircleCI にログインし、`Project > sdfx-cicleci-integration > Set Up Project` を開き、`Use Existing Config > Start Building` します。
![スクリーンショット_2020-12-28_17_01_09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26378/4563a781-730e-b0b9-6f18-b7660b61c863.png)

この時に起動したジョブは必ず失敗(Fail)するので、気にぜずに `Project Settings > Environment Variable` から `Add Environment Variable` し、以下の通り環境変数を追加します。

![スクリーンショット_2020-12-28_11_15_11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26378/93f79a4e-5e14-ca71-b76e-af21cb84ff56.png)

| Name | Value |
|:--|:--|
| DECRYPTION_IV  | server.key を暗号化した時に使った初期化ベクトル(iv)  |
| DECRYPTION_KEY  | server.key を暗号化した時に使ったキー(Key)  |
| DEVHUB_CONSUMER_KEY  | Salesforce 組織で作成した接続アプリケーションから発行されたコンシューマ鍵  |
| DEVHUB_USER_NAME  | Salesforce 組織で接続アプリケーションを介してログインするユーザ名  |

### CircleCI で動作確認
ここまでの設定で CircleCI を動かす準備が整いました。先程失敗(Fail)したジョブを開いて `Rerun` してみます。全てうまく設定できていればジョブが成功し、ログインした組織の情報が表示されるはずです。以降は、リポジトリに変更を加える度にジョブが自動で実行されるようになります。
![スクリーンショット_2020-12-28_17_17_44-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26378/7d72e355-abec-cd3f-1c94-3811b6d96134.png)


## おまけ： README.md にバッジを表示する
![スクリーンショット 2020-12-28 17.30.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26378/ce91bb6e-003b-830e-cd72-e97caa4a06b1.png)
GutHub のリポジトリの README.md に CircleCI のバッジが出ているとカッコいいですよね。このバッジを表示するには、 CircleCI の API TOKEN を発行しておく必要があります。対象のプロジェクトの `Project Settings > API Permissions` から `Add API Token` します。 STATUS スコープを選択し分かりやすいラベルを付けて保存すれば、外部からパイプラインの状態を確認できるトークンが発行されます。
![スクリーンショット 2020-12-28 17.26.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26378/d04be287-95f7-7833-7285-a80fcd75fc63.png)

あとは、README ファイルに以下のフォーマットで記述すればバッジが表示されるようになります。

```markdown
[![CircleCI](https://circleci.com/gh/<USERNAME>/<PROJECT_NAME>.svg?style=shield&circle-token=<STATUS_API_TOKEN>)](https://circleci.com/gh/<USERNAME>/<PROJECT_NAME>)

↓今回の例での設定はこんな感じ
[![CircleCI](https://circleci.com/gh/hrk623/sfdx-circleci-integration.svg?style=shield&circle-token=690e2...)](https://circleci.com/gh/hrk623/sfdx-circleci-integration)
```

## 参考
- [CircleCI を使用した継続的インテグレーション | Salesforce DX 開発者ガイド](https://developer.salesforce.com/docs/atlas.ja-jp.228.0.sfdx_dev.meta/sfdx_dev/sfdx_dev_ci_circle.htm)
- [forcedotcom/sfdx-circleci/.circleci/config.yml | GitHub](https://github.com/forcedotcom/sfdx-circleci/blob/master/.circleci/config.yml)
- [Salesforce アプリケーションの継続的インテグレーション | CircleCI ブログ](https://circleci.com/ja/blog/continuous-integration-for-salesforce-applications/)