やりたいこと
- SharePointOnline(SPO)の特定のフォルダ内にあるファイルをゴソっとAzure Data Lake Storage Gen2(ADLS Gen2)にコピーしたい
- MSドキュメントに1ファイルだけコピーする方法が紹介されていたので、これを参考にやってみます
Azure AD にアプリケーションを登録
Azure Portalから、Azure Active Directory > アプリの登録 > 新規登録
アプリケーションIDとテナントIDをコピーしておきましょう
続いて、このアプリケーションのシークレットを発行します
証明書とシークレット > 新しいクライアントシークレット
クライアントシークレットが作成されたら、値をコピーします
アプリケーションにSPOサイトのアクセス許可を付与
SharePoint上での作業です
アプリに権限を付与するために、以下のページを開きます
<tenant_name>
と<site_name>
は環境に合わせて変更します
https://<tenant_name>.sharepoint.com/sites/<site_name>/_layouts/15/appinv.aspx
アプリID
に先程コピーしたIDを貼り付けて、参照ボタンを押すとタイトル
に登録したアプリ名が自動で設定されます。アプリ ドメイン
、リダイレクト先のURL
、権限の要求XML
は冒頭のMSドキュメントの通り入力します
信頼します
これで、Data FactoryからSPOにアクセスするために利用するアプリが作成できました
ちなみに、SharePoint REST APIに関するページを見ると、https://{site_url}/_api/web/...
といった形でリクエストするURLが記載されていますが、{site_url}
の部分が<tenant_name>.sharepoint.com/sites/<site_name>
です
Data Factoryでパイプラインを作成
パイプラインの完成形
ここから長くなるので、先に完成形をお見せしておきます
Webアクティビティ:SPOのアクセストークンを取得
以下の通り設定します
<app_id>
、<app_secret>
、<tenant_id>
、<tenant_name>
は置き換えてください
ドキュメントに記載がありますが、セキュリティで保護された出力
をONにしているのは、シークレットの値がログに出力されないようにするためです
- URL: https://accounts.accesscontrol.windows.net//tokens/OAuth/2
- メソッド: POST
- Headers: Content-Type:application/x-www-form-urlencoded
- 本文: grant_type=client_credentials&client_id=@&client_secret=&resource=00000003-0000-0ff1-ce00-000000000000/.sharepoint.com@
- セキュリティで保護された出力: チェックON
Lookupアクティビティ:取得したいフォルダ内のファイル一覧を取得する
まずはSPOのデータセットを作成します
ソースデータセット
で新規を押してSharePoint Online リスト
を選択
データセットが作成されたら、リンクサービス
で新規を押します
テスト接続が成功することを確認しておきます
- テナント: アプリケーションのテナントID
- サービスプリンシパルID: アプリケーションID
- サービスプリンシパルキー: アプリケーションのクライアントシークレット
リンクサービスが作成できたら、リスト名
でドキュメントを選択します
データのプレビューを押して、以下のようにドキュメント一覧が表示されたらOKです
私の環境では、名前
という列にファイル名、パス
という列に/sites/<site_name>/Shared Documents/General/...
の形式でファイル名を除いたパスが表示されていました。このファイル名とパスを表す列名は後ほど使うのでコピーしておきましょう
やっとLookupアクティビティのソースデータセットが定義できました
次に、特定のフォルダ内のファイルのみコピーしたいので、クエリを使ってフィルターします
SharePoint REST APIには色んなクエリ操作が用意されていますが、今回は$filter
クエリを利用します
パスが特定の文字列ではじまるという条件にします
startswithの1つめの引数が英語ではありませんが、シングルクォートなどは必要はありません
$filter=startswith(パス,'/sites/<site_name>/Shared Documents/General/...')
startswith
のような関数は他にもあるので、他の条件を試したい場合はコチラを参考にしてください
これでもう一度プレビューしてみましょう
今度は対象フォルダのファイルだけが表示されていますね
Foreachアクティビティ:ファイル一覧をパラメーターにループ処理
ここまでで半分ぐらい終わりました
まずは、Foreachアクティビティの項目
を指定します。ループ対象の配列を指定するイメージです
動的なコンテンツの追加
をクリックして、@activity('Lookupアクティビティの名前').output.value
を入力しましょう
Lookupアクティビティの出力結果を確認したい場合は、Lookupアクティビティをデバッグ実行するとjson形式の出力が確認できます
出力サンプル
{
"count": 3,
"value": [
{
"コンテンツタイプのID": "xxxxx",
"更新日時": "2020-10-21T11:59:55Z",
"名前": "xxxxx",
"コンプライアンス資産ID": null,
"タイトル": null,
"Tags": null,
"ExtractedText": null,
"場所国地域": null,
"場所都道府県": null,
"場所市区町村": null,
"場所郵便番号コード": null,
"場所番地": null,
"場所座標": null,
"場所名前": null,
"ID": 24,
"コンテンツタイプ": "ドキュメント",
"登録日時": "2020-10-21T11:59:55Z",
"登録者Id": 10,
"更新者Id": 10,
"コピー元": null,
"承認の状況": "0",
"パス": "xxxxx",
"チェックアウト先Id": null,
"ウイルスの状態": "2628",
"現在のバージョン": true,
"Owshiddenversion": 1,
"バージョン": "1.0"
},
{
"コンテンツタイプのID": "xxxxx",
"更新日時": "2020-10-21T11:59:56Z",
"名前": "xxxxx",
"コンプライアンス資産ID": null,
"タイトル": null,
"Tags": null,
"ExtractedText": null,
"場所国地域": null,
"場所都道府県": null,
"場所市区町村": null,
"場所郵便番号コード": null,
"場所番地": null,
"場所座標": null,
"場所名前": null,
"ID": 25,
"コンテンツタイプ": "ドキュメント",
"登録日時": "2020-10-21T11:59:56Z",
"登録者Id": 10,
"更新者Id": 10,
"コピー元": null,
"承認の状況": "0",
"パス": "xxxxx",
"チェックアウト先Id": null,
"ウイルスの状態": "387602",
"現在のバージョン": true,
"Owshiddenversion": 1,
"バージョン": "1.0"
},
{
"コンテンツタイプのID": "xxxxx",
"更新日時": "2020-10-21T11:59:56Z",
"名前": "xxxxx",
"コンプライアンス資産ID": null,
"タイトル": null,
"Tags": null,
"ExtractedText": null,
"場所国地域": null,
"場所都道府県": null,
"場所市区町村": null,
"場所郵便番号コード": null,
"場所番地": null,
"場所座標": null,
"場所名前": null,
"ID": 26,
"コンテンツタイプ": "ドキュメント",
"登録日時": "2020-10-21T11:59:56Z",
"登録者Id": 10,
"更新者Id": 10,
"コピー元": null,
"承認の状況": "0",
"パス": "xxxxx",
"チェックアウト先Id": null,
"ウイルスの状態": "2683952",
"現在のバージョン": true,
"Owshiddenversion": 1,
"バージョン": "1.0"
}
],
"effectiveIntegrationRuntime": "DefaultIntegrationRuntime (Japan East)",
"billingReference": {
"activityType": "PipelineActivity",
"billableDuration": [
{
"meterType": "AzureIR",
"duration": 0.016666666666666666,
"unit": "DIUHours"
}
]
},
"durationInQueue": {
"integrationRuntimeQueue": 1
}
}
Foreach内のコピーアクティビティ(SharePointのAPIを利用してADLS Gen2にコピー)
ソースデータセットを定義します
リンクサービスの定義
ベースURL
はhttps://<tenant_name>.sharepoint.com/sites/<site_name>/_api/web/
の形式**(末尾の"/"を忘れないようにしましょう)** 1
ここまで進めると、バイナリのデータセット定義が以下のようになっていると思います
ファイルのフルパスをパラメーターで指定したいので、パラメーターを追加します
GetFileByServerRelativeUrl('@{uriComponent(dataset().FilePath)}')/$value
GetFileByServerRelativeUrlはSharePoint REST APIのメソッドで相対URLを指定してファイルをダウンロードします。GetFileByServerRelativeUrlメソッドの引数に指定している@{uriComponent(dataset().FilePath)}
の部分は、FilePath
というパラメーターをData Factoryの関数uriComponent
を使ってエンコードしています
Copyアクティビティの残りのソース定義を設定していきます
先程データセット定義で作成したFilePath
というパラメーターがここに表示されています。Foreachでループ中の要素が持っているファイル名とファイルパスの情報を使ってファイルのフルパスを動的に指定します
動的なコンテンツの追加
をクリックして、@{item().パス}/@{item().名前}
と入力しましょう
ループ中の要素にアクセスする際はitem()
を使います
item()
の後ろには、SPOのデータをプレビューした時にコピーしたファイル名とパスを表す列名を使います
要求メソッド
、追加のヘッダー
はドキュメント通りです
- 要求メソッド: GET
- 追加のヘッダー:
@{concat('Authorization: Bearer ', activity('<Web-activity-name>').output.access_token)}
シンクデータセットを定義します
こちらはADLS Gen2のデータセット定義で特に注意すべきポイントもないので詳細は割愛します。SPOのファイル名をそのまま使いたいケースがほとんどかと思うので、ソースデータセットと同じように、FileName
のようなパラメーターを追加して、そのパラメーターに@item().名前
を渡しておきましょう
さいごに
もっと簡単にできるのかと思っていましたが、思ったより苦戦しました…
他に良い方法があれば教えて下さいm(_ _)m
-
以下の説明を読むと、ベースURLと相対URLの間の"/"はData Factoryがつけてくれそうなもんですが、つけてくれなかったのでベースURLの末尾につけました
↩