はじめに
こんにちは、PE課(プロセスエンジニアリング課)の江利です。
今回も AWS ではなく Google Cloud と Salesforce のお話です。
対象読者
- Salesforce(Apex) から サービスアカウントを使った認証で Cloud Workflows を呼び出したい方
指定ユーザーでの認証にしてしまうと対象ユーザーの異動や退職などによりアカウントが停止されてしまった場合にワークフローが呼び出せなくなってしまったり。 Apex の実行ユーザーに対応する Google Workspaces アカウントがない場合などに困るため、サービスアカウントを使った認証を行う必要があると考えました。
指定ユーザーで認証する方法は後日別記事にて紹介したいと思います。
認証プロバイダーを使った方法の記事
を公開しました。(2025/06/27 追記)
Google Cloud 側の操作
サービスアカウントを作成
権限はワークフロー起動元ロールを付与してください。

鍵の発行
先程作成したサービスアカウントを選択し、鍵タブより新しい鍵を作成から P12 を選択して作成します。
JSON が推奨とされているのですが Salesforce で使える鍵の制約上 P12 を使います。(後ほど jks にコンバートします)秘密鍵のパスワードは後ほど使用しますので控えておいてください。


ワークフローを作成
サービスアカウントは先程作成したものを指定してください。今回はワークフローのソースコードは初期値のサンプルをそのまま使用します。

鍵のコンバート
先程サービスアカウントに鍵を作成した際にローカルにダウンロードされた鍵ファイル(拡張子が .p12)を Salesforce で使える鍵ファイルにコンバートします。私は MacOS を利用しているのでターミナルより下記コマンドを実行しました。Windows の方はコマンドプロンプトなどから実行してください。
keytool -importkeystore -srckeystore [ダウンロードされた鍵ファイルのパス + 鍵ファイル名(拡張子 **.p12**)] -destkeystore [出力先のパス + 鍵ファイル名(拡張子 **.jks**)] -srcstorepass [秘密鍵のパスワード] -srcalias privatekey -srcstoretype pkcs12 -deststoretype jks -destalias samplecertificate -deststorepass [秘密鍵のパスワード]
上記コマンドのsamplecertificateの部分が Salesforce での一意の名前となります。
Salesforce 側の操作
リモートサイトの設定
設定画面よりクイック検索に「リモートサイト」と入力しリモートサイトの設定へ進み「新規リモートサイト」ボタンをクリックします。リモートサイト名には任意の名前を入力し(画像では「googleapis」としています)リモートサイトの URL にはhttps://www.googleapis.comと入力し保存します。

鍵のインポート
設定画面よりクイック検索に「鍵」と入力し証明書と鍵の管理へ進み「キーストアからインポート」ボタンをクリックします。ファイルを選択より先程コンバートした jks ファイルを選択し「キーストアのパスワード」には鍵のパスワードを入力して保存します。

実行した Apex コード
HttpRequest req = new HttpRequest(); req.setEndpoint('https://workflowexecutions.googleapis.com/v1/projects/[プロジェクト ID]/locations/asia-northeast1/workflows/test-workflow/executions'); req.setMethod('POST'); req.setHeader('content-type', 'application/json'); Auth.JWT jwt = new Auth.JWT(); jwt.setAud('https://oauth2.googleapis.com/token'); jwt.setIss('salesforce-test@[プロジェクト ID].iam.gserviceaccount.com'); jwt.setAdditionalClaims(new Map<String, Object>{ 'scope' => 'https://www.googleapis.com/auth/cloud-platform' }); Auth.JWS jws = new Auth.JWS(jwt, 'samplecertificate'); Auth.JWTBearerTokenExchange bearer = new Auth.JWTBearerTokenExchange( jwt.getAud(), jws ); req.setHeader('Authorization', 'Bearer ' + bearer.getAccessToken()); JSONGenerator gen = JSON.createGenerator(true); gen.writeStartObject(); gen.writeStringField('searchTerm', 'ジョジョの奇妙な冒険'); gen.writeEndObject(); String jsonS = gen.getAsString(); gen = JSON.createGenerator(true); gen.writeStartObject(); gen.writeStringField('argument', jsonS); gen.writeEndObject(); jsonS = gen.getAsString(); System.debug('@@@jsonS=' + jsonS); req.setBody(jsonS); Http http = new Http(); HTTPResponse res = http.send(req); System.debug('@@@body=' + res.getBody());
こちらは雑に Anonymouse Apex から実行して最低限動作させるために書き殴ったコードですので実際に実装する際にはエンドポイントや鍵の一意の名前などはカスタムメタデータに入れるなどしてください。エンドポイントの URL は、ワークフローの詳細画面最下部呼び出し URLの右端にクリップボードにコピーするボタンがあるのでそちらから転記しましょう。6行目のjwt.setAudの引数は必ずhttps://oauth2.googleapis.com/token固定で、7行目のjwt.setIssはサービスアカウントのメールアドレスを記載してください。jwt.setAdditionalClaimsの scope については今回はhttps://www.googleapis.com/auth/cloud-platformとしていますがご自身の利用する API に合わせて適宜変更してください。
req.setBodyにセットする本文ですが必ず「argument」に対して渡したいパラメーターの key と value をネストしてください。(24行目の部分)今回セットしている内容は下記となってます。
{ "argument" : "{\n \"searchTerm\" : \"ジョジョの奇妙な冒険\"\n}" }
workflows のソースはデフォルトのサンプルをそのまま動かしています。
# This is a sample workflow to test or replace with your source code. # # This workflow passes the region where the workflow is deployed # to the Wikipedia API and returns a list of related Wikipedia articles. # A region is retrieved from the GOOGLE_CLOUD_LOCATION system variable # unless you input your own search term; for example, {"searchTerm": "asia"}. main: params: [input] steps: - checkSearchTermInInput: switch: - condition: '${"searchTerm" in input}' assign: - searchTerm: '${input.searchTerm}' next: readWikipedia - getLocation: call: sys.get_env args: name: GOOGLE_CLOUD_LOCATION result: location - setFromCallResult: assign: - searchTerm: '${text.split(location, "-")[0]}' - readWikipedia: call: http.get args: url: 'https://en.wikipedia.org/w/api.php' query: action: opensearch search: '${searchTerm}' result: wikiResult - returnOutput: return: '${wikiResult.body[1]}'
戻ってきたレスポンスの本文は下記の通りでした。
{ "name": "projects/xxxxxxxxxxxx/locations/asia-northeast1/workflows/test-workflow/executions/e1c724b3-d03b-4db4-9fc7-c36548ac1277", "startTime": "2025-03-11T03:36:07.122564851Z", "state": "ACTIVE", "argument": "{\"searchTerm\":\"ジョジョの奇妙な冒険\"}", "workflowRevisionId": "000004-40b", "status": {}, "createTime": "2025-03-11T03:36:07.122564851Z" }
state がACTIVEとなっていることから Http リクエストに対する戻り値としては処理をキックした結果を受け取っているようです。name の末尾にあるe1c724b3-d03b-4db4-9fc7-c36548ac1277は実行 ID です。
コンソールから実行結果を確認したところこのようになっていました。

まとめ
さて、今回は Salesforce で Apex からサービスアカウントを使った認証で Cloud Workflows を呼び出してみました。この記事がどなたかの助けとなれば幸いです。
Cloud Workflows についての詳しい解説は G-gen のこちらの記事に詳しい記載があります。
【参考にしたサイト】
江利 義陽(記事一覧)
CE部PE課でSalesforceエンジニアをやっています。Salesforce歴は14年くらいなのでチョットだけわかります。
記事への質問やフィードバックは yoshiaki.eri@serverworks.co.jp までお願いいたします。
Trailblazer プロファイル