Windows端末のaws-cliでバケットをローカルにダウンロードしようとしたけど、S3オブジェクトキーにコロンがあってエラーになった話

S3バケットのファイルを`aws s3 cp s3://<<バケット名>> <<ダウンロードディレクトリ>> --recursive`と再帰的にダウンロードしようとした際に、オブジェクトキーに`コロン（：）`が含まれていてうまくダウンロードできなかった話。

これは後述するWindowsで利用できない文字　かつ　S3のオブジェクトキーで利用が推奨されていない文字である　`コロン（:）`が悪さをしていたケースとなります。

本記事ではコロン等がありWIndowsでは一括でローカルにダウンロードできなかった際の対策をPowerShellで書いてみました。

## Windowsではファイルに利用できない文字がある

`Windows`ではファイルに利用できない文字が色々とありますが、`コロン（：）`はその中の一文字です。

ちなみに`Windows`で利用できない名前は下記`Path`クラスの`GetInvalidFileNameChars`メソッドで取得できます。

[Path.GetInvalidFileNameChars Method](https://learn.microsoft.com/en-us/dotnet/api/system.io.path.getinvalidfilenamechars?view=net-7.0)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1670622/35a63bd7-9703-ce23-f56f-37ae5d4a3933.png)

## S3ではオブジェクトキーの命名についてと非推奨の文字がある

[オブジェクトキー名の作成](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/object-keys.html)

S3ではバケット内のオブジェクトを一意に識別する要素としてオブジェクトキーがありますが。

このオブジェクトキーについて、AWSドキュメントでは上記のようにガイドラインと非推奨な文字が記載されています。

Windowsでは利用できない`コロン（：）`について、非推奨とはなっていますが。
あくまで非推奨のためオブジェクトキーとしては利用できます。

## Windowsでは利用できない文字が、S3のオブジェクトキーにあるとどうなるか

上記のような差異があるため、S3のオブジェクトキーにWindowsの駄目文字を含んでいる場合に、Windows上で`aws cli`にて該当ファイルを含むS3バケットを'sync'だったり、`cp`に`--recursive`オプションをつけて一括ダウンロードしようとした場合。

オブジェクトキーに含まれる駄目文字がネックになってダウンロードが失敗します。

ファイル名にWindowsの駄目文字が含まれているだけなので、ダウンロード先の指定でコロンを抜いたファイル名にすれば問題なくダウンロードできます。

```powershell

# 下記はコマンドではfoo:bar.txtというファイルを作成しようとしてエラー
# $lastexitcodeが1で終了します
aws cp s3://<<バケット名>>/foo:bar.txt ./

#下記はダウンロード先をfoo_bar.txtという文字に変えているのでダウンロードOK
aws cp s3://<<バケット名>>/foo:bar.txt .foo_bar.txt
```

ダウンロード対象のファイル名が少ない場合は、1ファイルずつリネームしながらダウンロードすれば問題ありませんが、対象が多い場合面倒です。

## 今回駄目だったケースについて

回避策の前に、今回駄目だったケースについて。

基本はコロンのような非推奨な制約のある文字をS3のオブジェクトキーに使わないようにするのが一番の解決策かとは思います。

が、そもそも今回問題となったケースについて。

今回、一括でダウンロードしたかったバケットは`AWS Backup`の標準機能 `Backup Audit Manager`で`AWS Bakup`のバックアップ結果をS3にレポート出力しているバケットでした。

`Backup Audit Manager`ではS3に保管するオブジェクトキーに日付情報が含まれていますが、日付情報が[ISO 8601](https://ja.wikipedia.org/wiki/ISO_8601)となっており、このため出力されるオブジェクトキーにコロンが含まれてしまいます。

[AWS BackupAudit Managerとバックアップの監査およびレポートの作成をする](https://docs.aws.amazon.com/ja_jp/aws-backup/latest/devguide/aws-backup-audit-manager.html)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1670622/98cf09cd-2134-f082-246d-ffc2ffbb86cf.png)

上記画像をみると、出力されたレポートのオブジェクトキーにコロンが含まれている事がわかります。

このため、このままの状態でダウンロード先を変更しないで`aws-cli`コマンドでダウンロードするとエラーになります。

なお、マネジメントコンソール上からこのファイルをダウンロードした場合は、`コロン`が`_`に置換されてダウンロードされる動作となっていました。

## PowerShellでダウンロード先の駄目文字を置換しながら一括ダウンロードする

ファイルの数がそこそこあったのでPowerShellで下記のように記述して一括してダウンロードしてみました。

```powershell
$bucketName = '<<バケット名>>'
$directory = 'c:\temp\backup_report'

# s3バケットのオブジェクトキー一覧をリスト
$s3ObjectKeys = aws s3api list-objects --bucket $bucketName --output text --query "Contents[].{Key:Key}"

foreach ( $s3ObjectKey in $s3ObjectKeys ) {

    $source = "s3://{0}/{1}" -f $bucketName , $s3ObjectKey

    # 駄目文字をリプレイス（ディレクトリ構造は残したいので/はリプレイス対象から除外）
    $replaceS3ObjectKey = [RegEx]::Replace($source, "[{0}]" -f [RegEx]::Escape(([string][System.IO.Path]::GetInvalidFileNameChars() -replace '/' , '')) , '')
    $destinationPath = Join-Path -Path $directory  -ChildPath $replaceS3ObjectKey

    # s3 cpでダウンロード
    aws s3 cp $source $destinationPath

}
```

## 総評

バケットを一括ダウンロードしようとしてファイルが保存されず、最初は訳がわかりませんでしたが。

`--debug`をオプションをつけて実行し、結果を眺めていると、これはコロンが不味そうだとわかりました。

検索すると下記のissueがありました。

[Can't sync to Windows when colon in file name (cont..) #5813](https://github.com/aws/aws-cli/issues/5813)

AWS標準機能を用いて出力したレポートのオブジェクトキーに非推奨のコロンが入っているのはちょっと残念な気はしますが。