Get-S3ObjectV2で指定フォルダ直下のフォルダ一覧を取得

AWS Tools for PowerShellで、AWS S3の指定フォルダ直下のフォルダを取得したかった。例えばバケット内に次のような構成があるとして…

```
AWSLogs/
  +-- o-xxx9xxx9xx/
        +-- 000000000000/
        |     +-- ap-northeast-1/
        |     :
        +-- 111111111111/
        :
```

… `AWSLogs/o-xxx9xxx9xx/` 直下の `000000000000/` `111111111111/` `…` というリストを得たい。

結論としては、以下のようにすればいい。

```PowerShell
$objects = Get-S3ObjectV2 -BucketName $bucketName -Prefix "AWSLogs/o-xxx9xxx9xx/" -Delimiter "/" -Select CommonPrefixes
```

`$objects` には以下が格納されている。型は文字列。

```PowerShell
AWSLogs/o-xxx9xxx9xx/000000000000/
AWSLogs/o-xxx9xxx9xx/111111111111/
 :
```

# 直下のものの取得： `-Delimiter "/"` オプション

`-Prefix` でのプレフィックス指定だけだと、内部的には「フルパスがこれに前方一致する」ものをすべて返してしまうので、「指定ディレクトリ以下のファイルを再帰的にリストアップする」ような動作になる。期待動作と違ったり、該当するものが多いと時間がかかったり上限（1度に1,000件まで）に引っかかったりする。

直下のもの＝プレフィックスから次の `/` までの要素だけを取得するには、 `-Delimiter "/"` を指定する。

```PowerShell
$objects = Get-S3ObjectV2 -BucketName $bucketName -Prefix "AWSLogs/o-xxx9xxx9xx/" -Delimiter "/"
```

# フォルダーの取得：`-Select CommonPrefixes` オプション

`Get-S3ObjectV2` をそのまま実行すると、オブジェクト（ファイル的なもの）だけが返される。ディレクトリに相当するものはCommonPrefixと呼ばれており、取得したい時には `-Select CommonPrefixes` を付ける。

```PowerShell
$objects = Get-S3ObjectV2 -BucketName $bucketName -Prefix "AWSLogs/o-xxx9xxx9xx/" -Delimiter "/" -Select CommonPrefixes
```

フォルダ相当のものが、文字列型で返される。

```PowerShell
PS> $objects[0]
AWSLogs/o-xxx9xxx9xx/000000000000/
PS> $objects[0].GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     String                                   System.Object
```

# 参考

以下を参考にした。

* [Setting the delimiter parameter for Get-S3ObjectV2 should return a collection of objects · Issue #234 · aws/aws-tools-for-powershell · GitHub](https://github.com/aws/aws-tools-for-powershell/issues/234#issuecomment-1065936161)

AWS CLIやSDKでのS3利用経験があれば、わりとすぐに「 `-Delimiter "/"` 使うだけだろ、悩むとこある？」と思う気がする。でも実際には上述の通り「オブジェクトだけが返さ、CommonPrefixは返されない」ということになる。ここの解決方法を、このGitHubイシューで知った。

まず「[パイプライン処理と $AWSHistory - AWS Tools for PowerShell](https://docs.aws.amazon.com/ja_jp/powershell/latest/userguide/pstools-pipelines.html)」によれば、AWS Tools for PowerShellは次の挙動をする。

* コレクションを返すコマンドレットは、コレクション内のオブジェクトが返される。
* 内部で実行したAWS APIの結果値が、上のコレクションの他にページング制御フィールドではない追加フィールドを含む場合は、`$AWSHistory` セッション変数に記録される。

つまり、APIの実行結果そのままではない。[`Get-S3ObjectV2`](https://docs.aws.amazon.com/ja_jp/powershell/latest/reference/items/Get-S3ObjectV2.html)コマンドレットであれば、Synopsysに書かれているように内部では `S3 ListObjectsV2` APIを実行している。その結果値は以下で解説されている通り `Contents` に格納されたオブジェクトのコレクションとは別に、 `CommonPrefixes` にCommonPrefixが格納されている。

* [あるフォルダ直下の要素のみをリストアップするには - Amazon S3における「フォルダ」という幻想をぶち壊し、その実体を明らかにする | DevelopersIO](https://dev.classmethod.jp/articles/amazon-s3-folders/#toc-9)

で、`Get-S3ObjectV2`コマンドレットはそのAPIの実行結果から `Contents` だけを抜き出されて返し、CommonPrefixはたぶん（確認してないけど）`$AWSHistory`変数に格納する。前記のGithub Issueでは…

> 「そういういものだから」（[2021.10.16](https://github.com/aws/aws-tools-for-powershell/issues/234#issuecomment-944432536)）
> 「いやもっとわかりやすく」（[2021.10.18](https://github.com/aws/aws-tools-for-powershell/issues/234#issuecomment-945543229)）
> 「`-Select CommonPrefixes`でもイケるっぽいけど、せめてドキュメントのExampleに入れようよ」（[2022.5.13](https://github.com/aws/aws-tools-for-powershell/issues/234#issuecomment-1065936161)）
> 「わかったよドキュメント改定するよ」（[2023.04.08](https://github.com/aws/aws-tools-for-powershell/issues/234#issuecomment-1500558380)）

…みたいなやり取りしてて、これ読んで`-Select CommonPrefixes`でイケるのがわかって助かったけど、最後のコメントから1年余り経過した2024年5月10日現在、ドキュメントは改定されてないですよね？