概要
Power Automate Desktopを使ってローカルファイルをMicrosoft GraphからOneDriveにアップロードし期限パスワード付きの共有リンクを作成します。
- Microsoft Graph Rest APIを使うアプリはAzure ADで身元を確認(認証)し、アクセス権限付与を承認する手続きが必要になります。
- そのためAzure ADにアプリを登録する必要があります。
- 今回は認証承認フローにMSAL.PSを使用します。
- MSAL.PSを使用することで複雑なOAuth2.0承認コードフローを用意しなくても行うことができます。
- Power Automate Desktopからアクセストークンが有効期限内であれば、再認証なしに使いたかったのでフローが少し長めです。
- 都度、認証する単純なものであれば少ないアクションでアップロード可能です。(最後にコピペ可能サンプルコードを載せています)
環境
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
例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を設定している場合はサインイン要求を承認します。
はじめてアプリに承認コードフローをおこなう場合、アクセス許可を与える必要があります。
承認コードフローが完了するとアクセストークンを取得できます。
MSAL.PSを利用することで、わずか1行のスクリプトでアクセストークンを取得することができました。
MSAL.PSのこのスクリプトで何がおこなわれているか確認してみる
Fiddlerをつかって何がおこなわれているのかざっくり確認してみます。
OAuth 2.0 承認エンドポイント(v2)https://login.microsoftonline.com/$tenantIDoauth2/v2.0/authorize
にS256code ChallengeオプションをつかってAuthrization Codeを要求しています。
redirectで応答がきてIDを認証、Auethenticatorで承認後、Authrization codeが発行されます。
OAuth 2.0 トークン エンドポイント(v2)https://login.microsoftonline.com/tenantID/oauth2/v2.0/token
にcode_verifierを含めた情報を送信してアクセストークンを発行依頼します。
確認されるとアクセストークンが発行されるというながれでした。
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)を出力するようにしました。
$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」としました。
-
ASEでテキストを暗号化する
%PowershellOutput%(アクセストークンと有効期限)を暗号化します。暗号化キーやオプションは任意で設定してください。ただし複号するときも同じ条件にしてください。
-
テキストをファイルに書き込みます
%EnvironmentVariableValue%\token.txtに暗号化した%EncryptedText%を書き込みます。
アクセストークンを複号化し、Graph APIでOneDriveにファイルをアップロード
任意の名前でフローを作成します。自分は「OneDrive Upload&Create Link」としました。大まかな流れは次のとおりです。
-
Windows環境変数を取得
TEMPを指定しました。 -
ファイルからテキストを読み取ります。
%EnvironmentVariableValue%\token.txt
を指定しています。
-
End
-
テキストの分割
復号化した%DecryptedText%はテキストです。このアクションでアクセストークンと有効期限にわけてリスト化します。改行コードで区切ればOKです。
-
End
-
変数の設定
共有リンク作成時のパスワードを設定するためです。今回はテストのため12345にしてます(;´∀`)入力できるようにしたり、任意で設定してください。
-
datetimeをテキストに変換
共有リンクを作成する際の期限設定に、必要な形式にします。
例2020-05-19T23:59:59Zの形式が必要になります。zはリクエスト時に追加しています。
-
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
################# 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
まとめ
Microsoft IDプラットフォームとMicrosoft Graphのよい基礎学習になりました。
MSAL.PSを使って認証、承認フローは簡単にできますがPower Automate Desktopからアクセストークンを何度も使うには工夫が必要でした。自動化で実際使うにはここから先のアイデア次第だと思いました。
都度認証してもよいならファイル選択してOneDriveアップロードするフローは2アクションで作れます。最後にそれを記しておきます。またコピペで試せるようにしています。
※ClientID TenantID Scope OneDriveのフォルダー指定はハードコードです。適宜書き換えてください。
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アクション💦
参考