あるプロジェクトでSalesforce(SF)とPower BIを連携する必要があったのですが、Power Queryなどの既存のツールがあまり使い勝手が良くなかったので、オリジナルのプロセスを作りました。今回はその備忘記事です。一部、画面が英語ですが、ご了承ください。

技術的には、SF側はパーソナルセキュリティトークンを使ったAPIでデータ取得し、Power BIへは、MS Graph API経由で、Sharepointにデータをアップロードしています。

1. Salesforceのセキュリティトークン

セールスフォースにログインして、右上アイコンをhoverし、設定のリンクをクリックします。

サイドメニューから、「私のセキュリティトークンのリセット」をクリックします。すると、新しいセキュリティトークンが記載されたEメールが飛んできます。

2. MS Graph APIのセットアップ

最初に以下の項目を整理する必要があります。

  • TENANT_ID
  • CLIENT_ID
  • CLIENT_SECRET
  • パーミッションの設定
  • SHAREPOINT_HOST_NAME
  • SITE_NAME
  • FOLDER_PATH

TENANT_ID

これは、ディレクトリーIDと同じで、Azureポータルの画面で確認できます。

CLIENT_ID

Azureポータルでアプリケーションの登録をします。Go to the setup screen by clicking on リンクをクリックし、 セットアップ画面に進みます。もしくは、サイドメニューから「Azure Active Directory」 → 「App registrations」とクリックしていきます。

次に、画面やや左上にある「+ New registration」ボタンを押し以下の画面に進みます。

・Name: 何でも好きな名前を

・Supportted Account types: “Single tenant”で今回の場合はOK

・Rdirect URI: https://login.microsoftonline.com/{TENANT_ID}/oauth2/token

そして、Register ボタンをクリックすると、以下の画面がで出てきます。「Application (client) ID」がCLIENT_IDなのでメモしておきましょう.

CLIENT_SECRET

さらに、「Certificates & secrets」をサイドメニューから選択し、client secretを生成します。

「Value」がCLIENT_SECRETです(Secret IDではありません)。

パーミッションの設定

アプリにパーミッションを設定します。サイドメニューからAPI permissionsをクリックし、+Add a permission → Microsoft Graph → Delegated permissionsと進みます。我々のケースでは、「Files.ReadWrite.All」で十分でしたので、選択し付与します。

SHAREPOINT_HOST_NAME

こちらはURLから取り出します。

{your_company_name}.sharepoint.com

SITE_NAME

こちらもURLから取り出します。

https://{your_company_name}.sharepoint.com/sites/{SITE_NAME}

以下の例では、「msteams_6f23fd」がSITE_NAMEです。

FOLDER_PATH

「General/{folder_name}」のような、Sharepoint内のフォルダ構造です。

3. Salesforceからデータをダウンロード

ここからは少しコードを使って処理を自動化します。

Simple-salesforceというライブラリーを使うとSFのデータを扱うのは簡単です。

ここのコードでのYOUR_SF_EMAIL と  YOUR_SF_PWは、SFのログインに使うEメールとパスワードです。

YOUR_SF_SECURITY_TOKENは、この記事の冒頭でEメールで送られてきたトークンです。

以下のコードは例として、案件オブジェクトからいくつかのカラムの全行データを取得しています。

(データ処理は、SFのSQL(正確には、SOQL)を使うよりも、PythonのPandasなどで処理した方が早いことが多いのではと思います。)

後で、ファイルをSharepointへアップロードするため、この例では、CSV形式のファイルに一旦アウトプットしています。


from simple_salesforce import Salesforce
sf = Salesforce(
    username=YOUR_SF_EMAIL,
    password=YOUR_SF_PW,
    security_token=YOUR_SF_SECURITY_TOKEN)

opty_df = sf_to_df(sf.query_all("""
SELECT Id18__c,Name,AccountId,StageName,Field2__c,OwnerId
FROM Opportunity
"""))
opty_df.to_csv("data.csv",index=False)

4. MS Sharepointへアップロード

Power BIがデータソースとして使えるように、SharepointへCSVをアップロードします。

最初はアクセストークンの取得です。


import requests
import json
import urllib
import os

token_url = f'https://login.microsoftonline.com/{TENANT_ID}/oauth2/token'
ENDPOINT = 'https://graph.microsoft.com/v1.0'

token_data = {
 'grant_type': 'password',
 'client_id': CLIENT_ID,
 'client_secret': CLIENT_SECRET,
 'resource': 'https://graph.microsoft.com',
 'scope':'https://graph.microsoft.com',
 'Username': YOUR_MS_EMAIL, #Account with no 2MFA
 'password':YOUR_MS_PASSWORD,
}

token_r = requests.post(token_url, data=token_data)
token = token_r.json().get('access_token')

続いて、ドライブやフォルダのIDを調べます。(ファイルアップロードに必要なので)

## get site id ##
r2 =  requests.get(f'{ENDPOINT}/sites/{SHAREPOINT_HOST_NAME}:/sites/{SITE_NAME}',
             headers={'Authorization': 'Bearer ' + token})
site_id = r2.json()['id']

## get dirve id ##
r3 = requests.get(f'{ENDPOINT}/sites/{site_id}/drive',
                  headers={'Authorization': 'Bearer ' + token})
drive_id = r3.json()['id']

## get folder id ##
r4 = requests.get(f'{ENDPOINT}/drives/{drive_id}/root:/{FOLDER_PATH}',
                  headers={'Authorization': 'Bearer ' + token})
folder_id = r4.json()['id']

そして、CSVファイルをアップロードします。


FILENAME = 'data.csv'
## create upload url ##
r5 = requests.post(
    f'{ENDPOINT}/drives/{drive_id}/items/{folder_id}:/{FILENAME}:/createUploadSession',
    headers={'Authorization': 'Bearer ' + token},
    json={
        '@microsoft.graph.conflictBehavior': 'replace',
        'description': 'A large test file',
        'fileSystemInfo': {'@odata.type': 'microsoft.graph.fileSystemInfo'},
        'name': FILENAME
    }
)
upload_url = r5.json()['uploadUrl']

## measure filesize and upload the file ##
st = os.stat(FILENAME)
size = st.st_size
CHUNK_SIZE = 10485760
chunks = int(size / CHUNK_SIZE) + 1 if size % CHUNK_SIZE > 0 else 0

with open(FILENAME, 'rb') as fd:
    start = 0
    for chunk_num in range(chunks):
        chunk = fd.read(CHUNK_SIZE)
        bytes_read = len(chunk)
        upload_range = f'bytes {start}-{start + bytes_read - 1}/{size}'
        print(f'chunk: {chunk_num} bytes read: {bytes_read} upload range: {upload_range}')
        result = requests.put(
            upload_url,
            headers={
                'Content-Length': str(bytes_read),
                'Content-Range': upload_range
            },
            data=chunk
        )
        result.raise_for_status()
        start += bytes_read
print(f'uploading done for {FILENAME}')

以上がSalesforceのデータをPower BIで使うための一通りの流れです。Power BIには、Powerクエリという機能があり、Salesforceとの連携もサポートしていますが、クエリの操作性はあまり良くないので、複雑なデータ処理を反映するために、弊社では上記方法を多用しています。

上記のSharepointへアップロードするコードは、Microsoft 365へのログインとして、2段階認証を設定していない場合に有効なコードです。もし、2段階認証を利用している場合は、アクセストークン取得のコードが以下のようになります。


h = {"Content-Type": "application/x-www-form-urlencoded",
         'Accept': 'application/json'}
    d = {'grant_type' : 'client_credentials',
        'resource':'https://graph.microsoft.com/',
        'client_secret':CLIENT_SECRET,
        'client_id':APP_ID}
        

    r = requests.post(f'https://login.microsoftonline.com/{TENANT_ID}/oauth2/token',headers=h,data=d)

    token = r.json()['access_token']


Posted by: Ryoichi Fujita(藤田亮一)