見出し画像

Salesforce組織間データ移行ツール SFDX Data Move Utility (SFDMU)の使い方

こんにちは遠藤です。

ゆるっとSalesforce #16では「スクラッチ組織作成Tips」を共有させていただきました。この記事ではイベントで取り上げた話題の中からSalesforce組織間でデータを移行するためのツール 「SFDX Data Move Utility (以下SFDMU)」の使い方について解説します。

SFDMUとは

SFDMUは、名前の通りSalesforce組織間でデータを移行するためのツールです。主にsfdxコマンドのプラグインをインストールして使います。
デスクトップのGUIアプリもあるようですが、バイナリをダウンロードしてインストールできる形では配布されていないためちょっと使いにくいですね。

SFDMUの情報ですが、SalesforceのGithubリポジトリで公開されていますが、公式のブログ記事などは見当たりません。
今のところ、あくまで非公式なので自己責任で使用するツールということになります。

使い方は、Help Centerが公開されています。

以下、Githubの説明の引用ですが、組織間ダイレクトまたはCSVを介してデータを移行することができるツールです。
強力なのは、参照関係を自動で解決してエクポート・インポートしてくれます。

SFDX Data Move Utility (SFDMU) is the most modern and powerful salesforce data migration tool. It will help you to quickly populate your org (dev/sandbox/scratch/prod) with data imported from another org or CSV files. Supports all CRUD operations (Insert / Update / Upsert / Delete) for multiple related objects at a single run.

さらに、データローダやsfdxのtreeコマンドを置き換えることを想定しているそうです。

This Tool is the innovative and very handy alternative to the traditional salesforce data loader application as well as to the set of the force:data:tree commands.

と言っている割にはあまり情報が見当たらず、メンテナンスが心配になります。

日本語の情報としては、昨年のDeveloper Meetup #29のイベントで小坂さんが発表していたものを見つけました。

では、以下実際に使ってみての所感をまとめています。

SFDMUの使い方

Get Startedに沿ってセットアップして、動かしてみました。

インストール

sfdmuプラグインをplugins:installコマンドでインストールします。

sfdx plugins:install sfdmu

移行ジョブの定義ファイルの準備

export.jsonという名前の設定ファイルを用意します。
以下は取引先と取引先責任者を移行する例です。

{
    "objects": [
        {
            "query": "SELECT Id, Name, Type, BillingAddress, Phone, Description FROM Account",
            "operation": "Upsert"
        },
        {
            "query": "SELECT Id, AccountId, LastName, FirstName, Phone, Email FROM Contact",
            "operation": "Upsert",
            "externalId": "Email"
        }
    ]
}

Acountの項目BillingAddressを指定していますが、住所型のようにコンポジットの項目名も使えるようです。BillingStateなど個別に指定しなくて良いのは便利ですね。

externalIdは省略可能ですが、省略した場合は「Name」フィールドが使われます。実際には明示的に指定するのが無難そうです。

また、externalIdに指定する項目は外部Idが定義されていなくても問題ありません。

Supports External Id field of any type even formula and auto-name fields, a field does not need to be defined as External Id.

データ移行ジョブを実行

ソース組織とターゲット組織にsfdxでログインしておきます。

$ sfdx force:auth:web:login -a source-org
$ sfdx force:auth:web:login -a target-org

--sourceusernameにソース組織のエイリアスまたはユーザ名を、--targetusernameに移行先の組織のエイリアスまたはユーザ名を指定してsfdmu:runコマンドを実行します。

$ sfdx sfdmu:run --sourceusername source-org --targetusername target-org
...
Command in progress... done
 
[8:47:39.219] Command succeeded.
[8:47:39.220] Execution of the command sfdmu:run has been completed. Exit code 0 (SUCCESS).
[8:47:39.220] Total time elapsed: 00h 00m 20s 178ms .

ちなみにソース組織の取引先レコードは以下のとおりです。

ターゲットの組織で確認してみます。

8レコードすべて移行されました。
キャプチャはありませんが、取引先責任者も取引先関連付けられた状態で追加されることが確認できます。

ログ出力とエラーの確認方法

コマンドを実行すると中間ファイルのCSVとログが出力されます。
export.jsonが配置されているディレクトリにbinary_cache、logsおよびtargetという名前のディレクトリが作成されます。

以下、targetに出力された中間ファイルのCSVの例です。レコードの追加・更新に成功したレコードのId列にはtarget組織のレコードIdが出力されます。

Name,Type,BillingGeocodeAccuracy,BillingCity,BillingCountry,BillingLatitude,BillingLongitude,BillingPostalCode,BillingState,BillingStreet,Phone,Errors,Id
エッジ コミュニケーションズ,Customer - Direct,,Austin,,,,,TX,"312 Constitution Place
Austin, TX 78767
USA",(512) 757-6000,,0010w000014IYOPAA4
Burlington Textiles Corp of America,Customer - Direct,,Burlington,USA,,,27215,NC,525 S. Lexington Ave,(336) 222-7000,,0010w000014IYOQAA4
Pyramid Construction Inc.,Customer - Channel,,Paris,France,,,75251,,2 Place Jussieu,(014) 427-4427,,0010w000014IYORAA4
ディッケンソン plc,Customer - Channel,,Lawrence,USA,,,66045,KS,1301 Hoch Drive,(785) 241-6200,,0010w000014IYOSAA4
グランドホテル&リゾート Ltd,Customer - Direct,,Chicago,,,,,IL,"2334 N. Michigan Avenue, Suite 1500
Chicago, IL 60601, USA",(312) 596-1000,,0010w000014IYOTAA4
ユナイテッド オイル&ガス Corp.,Customer - Direct,,New York,,,,,NY,"1301 Avenue of the Americas 
New York, NY 10019
USA",(212) 842-5500,,0010w000014IYOUAA4
エクスプレス ロジスティクス&トランスポート,Customer - Channel,,Portland,,,,,OR,"620 SW 5th Avenue Suite 400
Portland, Oregon 97204
United States",(503) 421-7800,,0010w000014IYOVAA4
アリゾナ大学,Customer - Direct,,Tucson,,,,,AZ,"888 N Euclid 
Hallis Center, Room 501
Tucson, AZ 85721
United States",(520) 773-9050,,0010w000014IYOWAA4
"ユナイテッド オイル&ガス, UK",Customer - Direct,,,,,,,UK,"Kings Park, 17th Avenue, Team Valley Trading Estate,
Gateshead, Tyne and Wear NE26 3HS
United Kingdom",+44 191 4956203,,0010w000014IYOXAA4
"ユナイテッド オイル&ガス, Singapore",Customer - Direct,,Singapore,,,,,Singapore,"9 Tagore Lane
Singapore, Singapore 787472
Singapore",(650) 450-8810,,0010w000014IYOYAA4
GenePoint,Customer - Channel,,Mountain View,,,,,CA,"345 Shoreline Park
Mountain View, CA 94043
USA",(650) 867-3450,,0010w000014IYOZAA4
sForce,,,San Francisco,US,,,94087,CA,The Landmark @ One Market,(415) 901-7000,,0010w000014IYOaAAO

中間のCSVファイルは、実際に移行対象として抽出されたレコードのみが出力されます。更新の際はSELECT句に列挙した項目の値で差分を検出して、更新するレコードを抽出しているようです。

また、入力規則でエラーになるなど更新に失敗した場合、エラーメッセージがCSVに出力されます。

AccountId,FirstName,Phone,Email,Errors
0016D00000vuiiCQAQ,太郎,042-626-3111,,値を入力してください: [LastName]
0016D00000vuiarQAA,太郎,03-3695-1111,,値を入力してください: [LastName]
0016D00000vuiarQAA,一郎,03-3695-1111,,値を入力してください: [LastName]

その他Tips

export.jsonのファイル名は固定なので、複数のジョブを構成する必要がある場合は以下のようにディレクトリを分けます。

  • job1

    • export.json

  • job2

    • export.json

ディレクトリは--pathで指定できます。

sfdx sfdmu:run --path job1 --sourceusername source-org --targetusername target-org

複雑な参照関係の解決ができるか?検証

SFDMUの強力なところは、柔軟にexternalIdを設定することで参照関係を解決しながらエクスポート・インポートが可能なことで、シンプルな参照関係であれば自動で解決してくれます。

そこで、実際に複雑な参照関係を持つオブジェクトのレコードが組織間で移行できるか試してみました。

オブジェクトは商談と商品を使います。
ER図にすると以下のようになります。

ER図中のキーは、論理的に主キーとなる項目を表しており、これらをexternalIdにセットします。
これらのオブジェクトを移行するexport.jsonは以下のようになります。

{
    "objects": [
        {
            "query": "SELECT Id, Name, Type, BillingAddress, Phone, Description FROM Account",
            "operation": "Upsert",
            "externalId": "Name"
        },
        {
            "query": "SELECT Id, Name, AccountId, Pricebook2Id, Probability, StageName, Amount, CloseDate, Description FROM Opportunity",
            "operation": "Upsert",
            "externalId": "Name"
        },
        {
            "query": "SELECT Id, Name, IsActive, Description FROM Pricebook2",
            "operation": "Upsert",
            "externalId": "Name"
        },
        {
            "query": "SELECT Id, Name, ProductCode, IsActive, Description, Family FROM Product2",
            "operation": "Upsert",
            "externalId": "ProductCode"
        },
        {
            "query": "SELECT Id, Name, Pricebook2Id, Product2Id, UnitPrice, IsActive, UseStandardPrice, ProductCode FROM PricebookEntry WHERE ProductCode != null ORDER BY CreatedDate",
            "operation": "Upsert",
            "externalId": "Pricebook2.Name;Product2.ProductCode"
        },
        {
            "query": "SELECT Id, OpportunityId, PricebookEntryId, Product2Id, ProductCode, SortOrder, Name, Quantity, UnitPrice, ListPrice, ServiceDate, Description FROM OpportunityLineItem",
            "operation": "Upsert",
            "externalId": "Opportunity.Name;PricebookEntry.PriceBook2.Name;PricebookEntry.ProductCode"
        }
    ]
}

価格表エントリ(PricebookEntry)や商談商品(OpportunityLineItem)のexternalIdには複合キーを指定しています。

価格表エントリ(PricebookEntry)は、価格表(Pricebook2)と商品(Product2)の結合オブジェクトで、価格表.名前(Name)と商品.商品コード(ProductCode)を複合キーとしてレコードが一意に決まります。
よって、商談商品から価格表エントリへの参照先の指定も「PricebookEntry.PriceBook2.Name;PricebookEntry.ProductCode」の2つの項目をexternalIdで指定しています(ER図の青枠)。

また、価格表エントリにはORDER BY CreatedDateを指定していますが、標準価格表「Standard Price Book」の価格表エントリを先に追加する必要があるため、作成日時でソートすることで回避しています。

取引先と商談のexternalIdですが、今回は標準項目のみを使用しているのでデフォルトのNameを指定していますが、商談名を変更すると別レコードとして判定されてしまうため重複したレコードができてしまいます。
実際に運用で使用するには、商談にも外部ID用の自動採番項目などを追加してexternalIdに追加する必要があります。

こちらの動作は、イベントにてデモで解説させていただきました。

まとめ

以上、externalIdは論理的にキーを設計しないとならないので若干難しいですが、ちゃんと動いてますしなかなか便利そうです。

今回、紹介したのはまだSFDMUの基本的な機能でHelp Centerにはまだまだ便利そうな機能があります。
インポート・エクスポートに使用できるCSVの形式も検証していないので、そのあたりも機会があれば見ておきたいところです。

sfdxのランタイムを準備する必要がありシステム管理者の方が利用するにはハードルが高そうで、今の所データローダーの代替にはならなそうですが、インストール可能なデスクトップアプリがリリースされることを期待したいところです。