Azure BlobStorageに保管したファイルをPowershellで管理する

#はじめに
Azureサービス内で標準的に利用されるストレージサービスとして「Azure Storage」があります。その中で最も一般的に使うのは「Blob」タイプのストレージではないかと思います。このストレージをジョブ等でPowershellを使って管理する為に注意するポイントとPowershellコマンド群の使い方を実際にありそうなシチュエーションのスクリプトを作って説明したいと思います。(この記事は2020/03/31時点の情報です)
参考情報
[Az.Storageリファレンス](https://docs.microsoft.com/ja-jp/powershell/module/az.storage/?view=azps-5.0.0)

#シチュエーション
ストレージ管理に一番ありがちな、WebAppから出力されたログデータを1ヵ月たったら削除する。というようなログローテーションの仕組みを考えたいと思います。
その際ファイル構造は以下のように出力されているとします。

``` ファイル構造
/applog (コンテナー)
  /yyyymmdd(仮想ディレクトリ)
    /app1.log
    /app2.log
```

ちなみに実際に作成してみるとポータル上(Explorer上)は以下のような感じです。
![001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/515252/0dde26a6-a9df-4081-b603-a02d74e37dcd.png)

今回は接続方法としてスタンダードな「ストレージキーアクセス」、コンテナーのアクセスレベルは何でも良いですが匿名アクセス禁止の「プライベート」としています。

#コンテナーと仮想ディレクトリ
上の階層図で記載しましたが、Blobには「コンテナー」と「仮想ディレクトリ」という種類があります。MSのマニュアルを読む限りではどちらも「ディレクトリ」として扱えると記載されており、実際GUI上ではアクセスポリシーが変更できない以外はフォルダとして扱う事ができ、アクセスURLも`https://applog.blob.core.windows.net/applog/yyyymmdd`のように区別無く扱えます。
しかし、実際には以下のような性質を持っています。
・コンテナー
単体で作成/削除が可能。ルートにしか作成できない。複数階層にコンテナーを配置できない。Portal上から作成可能
・仮想ディレクトリ
単体で作成/削除が不可能。コンテナーの配下にしか作成できない。複数階層に仮想ディレクトリを配置できる。Portalでは作成不可能
※ファイルアップロードにて「アップロード先のフォルダ」でパスを切ればディレクトリ構成にはできます。

つまり仮想ディレクトリは、実際にはディレクトリとしては存在しないパスだけの存在(仮想なのでその通りですが)
コンテナ―が実際に存在するディレクトリとしてAzure内では利用可能な存在になります。　なので別のリソース等でBlobを出力先として指定する場合はコンテナ―の指定のみが可能でその下にアプリケーションによって仮想ディレクトリが作成される動きとなっています。

ちなみPowershellでBlobストレージのファイルリストを取得するコマンドを実行してみるとこんな感じで、ファイル名がパス付で表示されています。-Contextは認証情報をオブジェクト化しておくと便利です
PowershellでBlobを扱う場合、ファイルはこの形式でしか扱えません。

``` get-azstrageblob.ps
Get-AzStorageBlob -Container $container -Context $stConn

   AccountName: testyoustorage, ContainerName: applog
Name                 BlobType  Length          ContentType                    LastModified         AccessTier SnapshotTime                 IsDeleted  VersionId
----                 --------  ------          -----------                    ------------         ---------- ------------                 ---------  ---------
20200201/app1.log    BlockBlob 35              application/octet-stream       2020-10-29 06:22:25Z Hot                                     False
20200201/app2.log    BlockBlob 35              application/octet-stream       2020-10-29 06:22:25Z Hot                                     False
```

この結果が表すことは、Powershellで階層化されたBlobファイルを扱うときには、ファイル名を指定したいときはパスから分離する処理が必要で、コマンドで指定する時にはパス付で取り扱う必要があるということです（面倒）

#Blob名からパスの分離
今回のシチュエーションでは実ファイルはコンテナ―の下に「yyyymmdd」の形式で仮想ディレクトリを作成し配置しています。
なので以下のような処理を行って、Blobファイル名から「/」を分離してファイルを取り扱えるようにします。

``` spllitBlobFileName.ps
#ファイルリストをオブジェクトで取得する
$blobInfo = Get-AzStorageBlob -Container $container -Context $stConn

#ファイル毎に繰り返し処理を実施
foreach($path in $blobInfo){

    #-splitのオプションを付けて、配列オブジェクトに分離したディレクトリ、ファイル名を格納
    $dir=$path.Name -split "/"
    
    #配列[0]にはyyyyMMddの「文字列」が入っているので「日付」フォーマットに変換
    $dirDate=[datetime]::ParseExact($dir[0],"yyyyMMdd",$null)

    #削除する基準日付を取得(今より$logkeepdurationの分だけ前の日付)
    $referDate=(Get-Date).AddDays(-$logKeepDuration)

    #仮想ディレクトリの日付が基準日より前ならBlobファイルを削除する
    if ($dirDate -le $referDate) {

        #Blobファイルの削除
        Remove-AzStorageBlob -Blob $path.Name -Container $container -Context $stConn

    }

}
```
パス分離として必要なのは最初のリストで取り出したファイル名を-splitオプションを付けて分離して配列に格納した所までです。
残りはよくある何日前までのファイルを削除みたいな処理を入れてみました。
ファイルの最終更新日(LastModified)から計算しても良いでしょうし、上記のようにファイル名で処理しても良いかと思います。
但し、Azure上で処理する場合、フォーマット変換時に何も指定しない(第三オプションで$nullを指定した)場合、フォーマットはAzureサーバ内のCultureInfo(時刻でいうとグリニッジ標準時、言語はenみたいな)が適用されるので時刻管理をオンプレと連携させたりしている場合は注意が必要です。
また、Blobファイル名は上で説明したようにパス＋ファイル名を指定する必要があるので注意です。
仮想ディレクトリは配下のファイルが削除されると自動的に消滅します。(ここからもディレクトリとして扱われていないことが分かります)


#まとめ
AzureStorageはコンテナ―と仮想ディレクトリという概念で構成されており、Azure上で管理する場合はコンテナー(第１層)だけで管理、構成するのが便利で、本来の使い方のようです。
コンテナ内に仮想ディレクトリを配置し、通常ファイル管理と同じようディレクトリ管理はできるが機能制限や、追加処理を行う必要がある点には注意が必要です。
つまり単純にWindowsのディレクトリのようには利用できませんということですね。

#感想
まとめている途中で忙しくなってしまいすっかり投げていたせいで途中から日付が変わってしまってサボった期間が分かりやすい。。。
しかもBlobStorageのローテーション機能が実装されてしまっているという、、、いや嬉しいんですが。。。