5
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Power Automate DesktopからOneDriveにファイルをアップロードして期限パスワード付きの共有リンクを作成する

Posted at

概要

Power Automate Desktopを使ってローカルファイルをMicrosoft GraphからOneDriveにアップロードし期限パスワード付きの共有リンクを作成します。

  • Microsoft Graph Rest APIを使うアプリはAzure ADで身元を確認(認証)し、アクセス権限付与を承認する手続きが必要になります。
  • そのためAzure ADにアプリを登録する必要があります。
  • 今回は認証承認フローにMSAL.PSを使用します。
  • MSAL.PSを使用することで複雑なOAuth2.0承認コードフローを用意しなくても行うことができます。
  • Power Automate Desktopからアクセストークンが有効期限内であれば、再認証なしに使いたかったのでフローが少し長めです。
  • 都度、認証する単純なものであれば少ないアクションでアップロード可能です。(最後にコピペ可能サンプルコードを載せています)
    One.gif

環境

Windows 10 Pro 21H1
PowerShell 5.1.19041.1237
MASL.PS 4.36.1.1
Microsoft 365 E5 Developer
Power Automate Desktop 2.12.171.2.21216

注意事項

  • Azure ADにアプリを登録できる権限が必要です。
  • MASL.PSをインストールする必要があります。
  • アクセストークンを暗号化してローカルに保存する方法を取っています。
  • 細かい例外処理はしていません。
  • このフローでアップロードできるファイルサイズは4MBまでです。
  • 自己責任でお願いいたします。
  • 2021年9月の情報です。
  • 認証と承認の定義についてはDocsを参考にしています。

準備

MSAL.PSのインストール

GitHubにあるAzure Active DirectoryのMSAL.PSレポジトリを参考にインストールします。PowerShellは管理者権限で立ち上げます。

アプリの登録

Azure Active Directoryの管理センターにアクセスしてアプリの登録を行います。

任意の名前をつけて登録します。

登録ができたら認証タブに移動してプラットフォームを追加します。

https://login.microsoftonline.com/common/oauth2/nativeclientをチェックして構成します。

APIのアクセス許可タブに移動しファイルに対する権限を追加します。

委任されたアクセス許可を選択します。

Filesを展開してFiles.ReadWriteをチェックしアクセス許可を追加します。

アプリの登録は以上です。

MSAL.PSで承認コードフローを実行して確認してみる

今回はWindows PowerShell ISEを使用します。
MSAL.PSのREADMEのようにPowerShellにコマンドを入力し使用例を表示します。
Get-Help Get-MsalToken -Examples

2021-09-19-02-05-54.png

例2の承認コードフローを使用しますのでエディターにコピペします。

Get-MsalToken -ClientId '00000000-0000-0000-0000-000000000000' -TenantId '00000000-0000-0000-0000-000000000000' -Interactive -Scope
     'https://graph.microsoft.com/User.Read' -LoginHint user@domain.com

Azure Active Directoryの管理センターから先ほど登録したアプリの情報に書き換えます。
概要タブにある
ClientIdはアプリケーション (クライアント) ID
TenantIdはディレクトリ (テナント) ID

APIのアクセス許可からhttps://graph.microsoft.com/Files.ReadWriteをコピーしスクリプトに追加します。

-LoginHintは使わないので削除し実行します。

必要な情報を入れたら実行してみます。
サインイン情報を入力しAuthenticatorを設定している場合はサインイン要求を承認します。

2021-09-19-02-35-48.png

はじめてアプリに承認コードフローをおこなう場合、アクセス許可を与える必要があります。

2021-09-19-03-46-44.png

承認コードフローが完了するとアクセストークンを取得できます。

MSAL.PSを利用することで、わずか1行のスクリプトでアクセストークンを取得することができました。

MSAL.PSのこのスクリプトで何がおこなわれているか確認してみる

Fiddlerをつかって何がおこなわれているのかざっくり確認してみます。

OAuth 2.0 承認エンドポイント(v2)https://login.microsoftonline.com/$tenantIDoauth2/v2.0/authorize
にS256code ChallengeオプションをつかってAuthrization Codeを要求しています。

2021-09-19-04-11-46.png

redirectで応答がきてIDを認証、Auethenticatorで承認後、Authrization codeが発行されます。

2021-09-19-11-46-38.png

OAuth 2.0 トークン エンドポイント(v2)https://login.microsoftonline.com/tenantID/oauth2/v2.0/token
にcode_verifierを含めた情報を送信してアクセストークンを発行依頼します。

2021-09-19-11-55-50.png

確認されるとアクセストークンが発行されるというながれでした。

2021-09-19-12-01-02.png

OAuth 2.0認証、承認コードフローについてDocsに詳細な解説があります。たった1行のスクリプトで同様のフローが行えることを確認できました。

MSALのコードをPower Automate Desktopのフローを考えて加工する

MSALで取得したクセストークンは同じPowerShellセッション内ではキャッシュから呼び出せます。しかし、セッションが変わるとキャッシュを呼び出せませんでした。方法はあるかもしれませんがわからないです。Power Automate Desktopの「PowerShell実行アクション」はアクション毎でセッションがリセットされるため毎回認証を要求されます。何度かフローを利用したいときに、毎回パスワード入力やAuthenticatorをするのはさすがに面倒です。そこでアクセストークンの有効期間3599秒(1時間)はアクセストークンを再利用したいので、どこかに安全に保管する必要があります。
アクセストークンのライフタイムはAzure ADのポリシーで設定できますが、デフォルトでは1時間です。

アクセストークンはJWT(JSON Web Token)というタイプです。ランダムな文字列に見えますが簡単にデコードできてしまいます。

アクセストークを貼り付けてみると内容を確認できます。JWTをローカルに保存するのはあまり推奨されていません。そこでJWT自体をPower Automate Desktop暗号化して保存し、再利用時に複号化する方法を考えました。そのためにあらかじめ少しだけコードを加工しておきます。各値を変数化して見やすくし、アクセストークンと有効期限(UTC)を出力するようにしました。

PowerShell
$appid='00000000-0000-0000-0000-000000000000' #Your App ID
$tenantid='00000000-0000-0000-0000-000000000000' #Your Tenant ID
$scopes=('https://graph.microsoft.com/User.Read', 'https://graph.microsoft.com/Files.ReadWrite') #Your App Scpopes

$Response = Get-MsalToken -ClientId $appid -TenantId $tenantid -Interactive -Scope $scopes

Write-Output $Response.AccessToken
Write-Output $Response.ExpiresOn.ToString("yyyy/MM/dd HH:mm:ss")

Power Automate Desktopのフロー作成

MSAL.PSを使ったアクセストークンの取得と暗号化

任意の名前でフローを作成します。自分は「MSAL Get Token」としました。

2021-09-19-13-15-27.png

  1. Windows環境変数を取得
    TEMPを指定しました。
    2021-09-19-13-17-51.png

  2. PowerShellスクリプトの実行
    先ほど作ったMSALのコードを貼り付けます。
    2021-09-19-13-20-23.png

  3. if
    %ScriptError%を判定します。
    2021-09-19-13-21-36.png

  4. フローを停止する
    2021-09-19-13-22-29.png

  5. End
    2021-09-19-13-23-18.png

  6. ASEでテキストを暗号化する
    %PowershellOutput%(アクセストークンと有効期限)を暗号化します。暗号化キーやオプションは任意で設定してください。ただし複号するときも同じ条件にしてください。
    2021-09-19-13-27-43.png

  7. テキストをファイルに書き込みます
    %EnvironmentVariableValue%\token.txtに暗号化した%EncryptedText%を書き込みます。
    2021-09-19-13-29-25.png

アクセストークンを複号化し、Graph APIでOneDriveにファイルをアップロード

任意の名前でフローを作成します。自分は「OneDrive Upload&Create Link」としました。大まかな流れは次のとおりです。
MAINflow.png

Flow.jpg

  1. Windows環境変数を取得
    TEMPを指定しました。

  2. 現在日時を取得します
    特定のタイムゾーンで国地域をUTCとします。UTCは選択肢にないので書き込みます。
    2021-09-19-15-59-40.png

  3. ラベル
    TokenCheckとしました
    2021-09-19-16-00-26.png

  4. ファイルからテキストを読み取ります。
    %EnvironmentVariableValue%\token.txtを指定しています。
    2021-09-19-16-02-46.png

    またエラー発生時の処理としてFileContents%''%として空を代入します。
    2021-09-19-16-03-39.png

  5. If
    2021-09-19-16-05-53.png

  6. Desktopフローを実行
    先に作成したMSAL Get Token フローを指定しています。
    2021-09-19-16-07-31.png

  7. 移動先
    2021-09-19-16-15-22.png

  8. End

  9. AESでテキストを解読する
    MSAL Get Tokenで暗号化したときと同じ設定にします。
    2021-09-19-16-10-11.png

  10. テキストの分割
    復号化した%DecryptedText%はテキストです。このアクションでアクセストークンと有効期限にわけてリスト化します。改行コードで区切ればOKです。
    2021-09-19-16-13-17.png

  11. 変数の設定
    2021-09-19-16-18-10.png

  12. テキストをdatetimeに変換
    アクセストークンの有効期限をテキスト形式からdatetime形式に変換します。
    2021-09-19-16-19-35.png

  13. 日付の減算
    アクセストークンの有効期限から現在日時を減算して秒で表します。この計算のためUTCにしています。
    2021-09-19-16-22-32.png

  14. If
    300秒を切ったらアクセストークンを再取得するための判定です。
    2021-09-19-16-26-20.png

  15. Desktopフローを実行
    先に作成したMSAL Get Token フローを指定しています。
    2021-09-19-16-07-31.png

  16. 移動先
    2021-09-19-16-15-22.png

  17. End

  18. 特別なフォルダーを取得
    いつものです。デスクトップを指定しています。
    2021-09-19-16-29-32.png

  19. ファイルの選択ダイアログを表示
    アップロードするファイルを選択します。
    2021-09-19-16-31-43.png

  20. 変数の設定
    共有リンク作成時のパスワードを設定するためです。今回はテストのため12345にしてます(;´∀`)入力できるようにしたり、任意で設定してください。
    2021-09-19-16-38-05.png

  21. 加算する日時
    アップロード時からの共有リンクの期限を設定します。とりあえず3日にしてみました。
    2021-09-19-16-40-48.png

  22. datetimeをテキストに変換
    共有リンクを作成する際の期限設定に、必要な形式にします。
    例2020-05-19T23:59:59Zの形式が必要になります。zはリクエスト時に追加しています。
    2021-09-19-16-49-11.png

  23. PowerShellスクリプトの実行
    Invoke-RestMethodを使いファイルのアップローおよびパス付期限付きリンクの作成を行います。アップロードするOneDriveのフォルダーはテストのためハードコーディングしているので適宜指定してください。今回は$onedriveFolderName="FolderA"としています。
    ファイルはバイナリで読み込みます。
    $fileBytes = [System.IO.File]::ReadAllBytes("%SelectedFile%")として-Bodyの引数に指定すればOKです。-ContentType 'application/octet-stream'とすることでさまざまなファイルがアップロードできます。
    ただし単純にPUTしているだけなので同じファイル名が存在する場合置き換えになります。
    またアップロードできるファイルは4MB以下です。

    https://docs.microsoft.com/ja-jp/graph/api/driveitem-put-content?view=graph-rest-1.0&tabs=http

    Link作成の部分は22で述べたように"expirationDateTime":"%FormattedDateTime%Z"でZを追加します。
    また"scope": "anonymous"にしているので注意してください。

    https://docs.microsoft.com/ja-jp/graph/api/driveitem-createlink?view=graph-rest-1.0&tabs=http

PowerShell
################# Upload File under 4MB Only#######################
$headers = @{Authorization = " Bearer %Token%" }
$fileBytes = [System.IO.File]::ReadAllBytes("%SelectedFile%")
$onedriveFolderName="FolderA"
$response = Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0//me/drive/root:/$onedriveFolderName/%SelectedFile.Name%:/content" -Method 'PUT'-ContentType 'application/octet-stream' -Headers $headers -Body $fileBytes
$itemID = $response.id
################# Create Link#####################################
$requestbody = '{
    "type": "view",
    "password": "%PassWord%",
    "scope": "anonymous",
    "expirationDateTime":"%FormattedDateTime%Z"
  }'
$response = Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0//me/drive/items/$itemID/createLink" -Method 'POST'-ContentType 'application/json' -Headers $headers -Body $requestbody
$response | ConvertTo-Json
  1. JSONをカスタムオブジェクトに変換
    2021-09-19-17-30-48.png

  2. クリップボードテキストを設定
    生成した共有リンクをクリップボードに格納します。
    2021-09-19-17-32-31.png

  3. メッセージを表示
    終了メッセージです。任意で💦
    2021-09-19-17-35-00.png

まとめ

Microsoft IDプラットフォームとMicrosoft Graphのよい基礎学習になりました。
MSAL.PSを使って認証、承認フローは簡単にできますがPower Automate Desktopからアクセストークンを何度も使うには工夫が必要でした。自動化で実際使うにはここから先のアイデア次第だと思いました。

都度認証してもよいならファイル選択してOneDriveアップロードするフローは2アクションで作れます。最後にそれを記しておきます。またコピペで試せるようにしています。
※ClientID TenantID Scope OneDriveのフォルダー指定はハードコードです。適宜書き換えてください。

2021-09-19-18-11-55.png

PowerAutomateDesktop
Display.SelectFile Title: $'''ファイル選択''' IsTopMost: True CheckIfFileExists: False SelectedFile=> SelectedFile ButtonPressed=> ButtonPressed
System.RunPowershellScript Script: $'''$appid=\'00000000-0000-0000-0000-000000000000\' #Your App ID
$tenantid=\'00000000-0000-0000-0000-000000000000\' #Your Tenant ID
$scopes=(\'https://graph.microsoft.com/User.Read\', \'https://graph.microsoft.com/Files.ReadWrite\') #Your App Scpopes
$Response = Get-MsalToken -ClientId $appid -TenantId $tenantid -Interactive -Scope $scopes
$Token= $Response.AccessToken
################# Upload File under 4MB Only#######################
$headers = @{Authorization = \" Bearer $Token\" }
$fileBytes = [System.IO.File]::ReadAllBytes(\"%SelectedFile%\")
$onedriveFolderName=\"YourFoldeName\" ###########Your OneDrive Upload Folder
Invoke-RestMethod -Uri \"https://graph.microsoft.com/v1.0//me/drive/root:/$onedriveFolderName/%SelectedFile.Name%:/content\" -Method \'PUT\'-ContentType \'application/octet-stream\' -Headers $headers -Body $fileBytes''' ScriptOutput=> PowershellOutput ScriptError=> ScriptError

これだけ長々書いて2アクション💦

参考

5
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?