自動化で、マネージド ID と azcopy login を使用する際に気をつけること

# はじめに

[前回](https://qiita.com/tetsuya-ooooo/items/17ce562d37085156e8ac)の投稿のつづきです。

前回は、マネージド ID を使って Azure リソースを操作するために ```Connect-AZAccount``` で Azure に接続する際の注意点について書きました。

今回は、マネージド ID を使って ```azcopy``` によるストレージを操作する際の注意点について書きます。 


# 検証環境

前回と同様、仮想マシン (Windows) 上で、同一マネージド ID を使って ```azcopy``` でストレージを操作 (Blob の転送など) する PowerShell スクリプトを同じユーザーで同時に複数セッションで実行してみました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230264/c1f41adf-3c97-b2c6-b205-cf8804660b0d.png)

実行する PowerShell スクリプトは、このように書いていました。

```powershell
# マネージド ID のクライアント ID
$clientId = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'

# Azure AD にログイン
azcopy login --identity --identity-client-id $clientId

# これ以降に自動化したい操作を実装

# コピー元 URI
$sourceUri = 'https://sourcexxxxx.blob.core.windows.net/source/'
# コピー先 URI
$destUri = 'https://destinationxxxxx.blob.core.windows.net/destination/'

# コンテナー配下をコピー
azcopy copy --recursive=true $sourceUri $destUri
```

ここでは、マネージド ID を使って Azure AD にログインするために ```azcopy login``` で行っていますが、この点についてちょっとマズいことがあります。


# 気をつけること

## 問題点 : ログイン情報はキャッシュされる

```azcopy login``` で作成されたログイン情報は、ユーザープロファイル配下にキャッシュします。

https://learn.microsoft.com/ja-jp/azure/storage/common/storage-ref-azcopy-login?WT.mc_id=AZ-MVP-5003113

一文だけなのでちょっと見落としそうですが、上記のドキュメントに以下のように記載されています。

> このコマンドは、OS の組み込みメカニズムを使用して、現在のユーザーの暗号化されたログイン情報をキャッシュします。

検証したところ、このキャッシュされたログイン情報は、PowerShell セッション間で共有できてしまいます。前述したように、PowerShell スクリプトを同じユーザーで同時に複数実行すると、後続処理でエラーが発生するおそれがあります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230264/560285d2-9dd8-8e64-fb88-2846af465518.png)


## 解決策

残念ながら ```azcopy login``` で解決するのは困難です...自動化で使用するのは避けたほうがいいでしょう。だからといって、```azcopy``` が使えないというわけではありません。

前述の問題点を解決するには、[前回紹介](https://qiita.com/tetsuya-ooooo/items/17ce562d37085156e8ac#%E8%A7%A3%E6%B1%BA%E7%AD%96)した ```Connect-AzAccount -Scope Process``` で Azure に接続します。Blob の転送などを行う場合は、SAS トークン付きの URI を生成して行います。

冒頭の PowerShell スクリプトを以下のように修正します。

```powershell
# マネージド ID のクライアント ID
$clientId = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
# Azure サブスクリプション ID
$subscriptionId = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
# リソースグループ名
$resourceGroupName = 'rg-hoge'
# コピー元ストレージアカウント名
$sourceStorageAccountName = 'sourcexxxxx'
# コピー元コンテナー名
$sourceContainerName = 'source'
# コピー先ストレージアカウント名
$destStorageAccountName = 'destinationxxxxx'
# コピー先コンテナー名
$destContainerName = 'destination'

# Azureに接続
Connect-AzAccount -Scope Process -Identity -AccountId $clientId
# サブスクリプションを変更
Set-AzContext -Scope Process -Subscription $subscriptionId

# コピー元ストレージコンテキストの生成
$sourceStorageAccount = `
    Get-AzStorageAccount -ResourceGroupName $resourceGroupName `
                         -Name $sourceStorageAccountName
$sourceStorageContext = $sourceStorageAccount.Context

# コピー先ストレージコンテキストの生成
$destStorageAccount = `
    Get-AzStorageAccount -ResourceGroupName $resourceGroupName `
                         -Name $destStorageAccountName
$destStorageContext = $destStorageAccount.Context

# コピー元ストレージコンテナー URI を取得
$sourceStorageContainer = `
    Get-AzStorageContainer -Name $sourceStorageContainerName `
                           -Context $sourceStorageContext
$sourceUri = $sourceStorageContainer.CloudBlobContainer.Uri.AbsoluteUri
# コピー先ストレージコンテナー URI を取得
$destStorageContainer = `
    Get-AzStorageContainer -Name $destStorageContainerName `
                           -Context $destStorageContext
$destUri = $destStorageContainer.CloudBlobContainer.Uri.AbsoluteUri

# SAS トークンの有効期間の算出
$startTime = (Get-Date).AddSeconds(-300)
$expiryTime = $startTime.AddHours(1)

# コピー元 SAS トークンの生成
$sourceSASToken = `
    New-AzStorageContainerSASToken -Name $sourceStorageContainerName `
                                   -Context $sourceStorageContext `
                                   -Permission 'racwld' `
                                   -StartTime $startTime -ExpiryTime $expiryTime
# コピー先 SAS トークンの生成
$destSASToken = `
    New-AzStorageContainerSASToken -Name $destStorageContainerName `
                                   -Context $destStorageContext `
                                   -Permission 'racwld' `
                                   -StartTime $startTime -ExpiryTime $expiryTime

# Blob 転送
azcopy copy --recursive=true "$sourceUri$sourceSASToken" "$destUri$destSASToken"
```

# まとめ

仮想マシンから、自動化で (マネージド ID を使って) ```azcopy``` でストレージを操作する際 (PowerShell スクリプト) は、Azure に接続する場合は ```azcopy login``` ではなく、```Connect-AzAccount -Scope Process``` で行いましょう。