【AWS】PowerShell を使ってフォルダごと S3 にコピーする

# 概要

- PowerShell から AWS CLI コマンドでコピーを実行する。

# 機能

## S3 保存処理 (SaveToS3.ps1)

### メイン処理

コピー元、保存先が記載されている CSV ファイルを読み込み、取得した列情報を元に件数分の処理 [S3 フォルダコピー] を実行します。

#### パラメーター

| No.  | 変数名 | 型           | 説明                     |
| :--: | ------ | ------------ | ------------------------ |
|  1   | csv    | ファイル情報 | csv ファイルを指定する。 |

#### 戻り値

##### 整数型

- 処理が正常に終了した場合、リターンコードとして 0 を返す。
- 処理に失敗した際は、エラー番号をリターンコードとして返す。

#### CSV ファイル構成

csv ファイルの列構成は以下の通り。

| No.  | 列名       | 説明                             |
| :--: | ---------- | -------------------------------- |
|  1   | source     | コピー元となるディレクトリパス。 |
|  2   | bucketName | S3 の保存先となるバケット名。    |
|  3   | folderName | バケット名配下のフォルダ名。     |

## AWS 共通処理 (AwsUtility.ps1)

### S3 フォルダコピー (Copy-S3Folder)

コピー元となるディレクトリの情報と保存先となる S3 バケット名、フォルダ名を指定して、フォルダ構成ごと S3 にコピーします。

#### パラメーター

| No.  | 変数名     | 型               | 説明                                    |
| :--: | ---------- | ---------------- | --------------------------------------- |
|  1   | Sources     | ディレクトリ情報 | コピー元となるディレクトリを指定する。  |
|  2   | BucketName | 文字列           | S3 の保存先となるバケット名を指定する。 |
|  3   | FolderName | 文字列           | バケット名配下のフォルダ名を指定する。  |

#### 例外

##### DirectoryNotFoundException

source に指定されたパスのフォルダが存在しない場合に発生する。

#### 動作

1. コピー元パス存在判定
   - Sources に指定されたパスのフォルダが存在しない場合は例外を発生させる。
2. 日付フォルダパス生成
   - BucketName と FolderName で日付フォルダパスを生成する。
3. S3 へのコピー
   - 日付フォルダパスのフォルダ内に Sources で指定したフォルダ (サブフォルダ含む) をコピーする。
   - AWS CLI コマンドを使用する。

#### AWS CLI コピーコマンド使用例：

```powershell
aws s3 cp $source $savePath --recursive
```

### S3 日付フォルダパス生成 (Build-S3-Path)

保存先となる S3 バケット名、フォルダ名を指定して、日付フォルダパスを作成します。


#### パラメーター

| No.  | 変数名     | 型               | 説明                                    |
| :--: | ---------- | ---------------- | --------------------------------------- |
|  1   | BucketName | 文字列           | S3 の保存先となるバケット名を指定する。 |
|  2   | FolderName | 文字列           | バケット名配下のフォルダ名を指定する。  |

#### 戻り値

##### 文字列型

- 日付フォルダパスを返します。

#### 動作

1. 日付フォルダ名 (yyyyMMdd) 作成
   - s3://{bucketName}/{folderName}/ フォルダの配下に日付 (yyyyMMdd) フォルダを追加したパスを作成する。

---
# ソースファイル

---
## SaveToS3.ps1

```powershell
cd $PSScriptRoot
. ..\LogUtility.ps1
. .\AwsUtility.ps1

<#
.SYNOPSIS
 S3 フォルダーコピー
.DESCRIPTION
 CSV ファイルのデータを元に S3 にフォルダーごとコピーします。
.EXAMPLE
 Copy-ToS3 xxxx.csv
.INPUTS
 1. CSV ファイル
.OUTPUTS
 なし
.NOTES
 CSV ファイルの列構成
 source, bucketName, folderName
#>
function Copy-ToS3 {
    param (
        # CSV ファイル
        [Parameter(ValueFromPipeline, Mandatory)]
        [System.IO.FileInfo[]] $Csv
    )
    Begin {
    }
    Process {
        log ("ファイル名：{0}" -f $Csv.FullName)
        ipcsv $Csv | % {
            s3cpy $_.source $_.bucketName $_.folderName
        }
    }
    End {
    }
}

try {
    Copy-ToS3 $Args[0]

    return 0
} catch {
    $ex = $_.Exception
    log ("ERROR: {0}" -f $ex)
    return $ex.HResult
}
```

---
## AwsUtility.ps1

```powershell
$env:tz = "jst"
. ..\LogUtility.ps1

<#
.SYNOPSIS
 S3 フォルダーコピー
.DESCRIPTION
 バケット名、フォルダー名を指定して、S3 にフォルダーごとコピーします。
.EXAMPLE
 $Sources | s3cpy -BucketName $BucketName -FolderName $FolderName
.INPUTS
 1. ファイル情報のコレクション
 2. バケット名
 3. フォルダー名
.OUTPUTS
 なし
.NOTES
 コピー元のフォルダーが存在しない場合、例外が発生します。
#>
function Copy-S3Folder {
    [Alias('s3cpy')]
    param (
        # ファイル情報のコレクション
        [Parameter(ValueFromPipeline, Mandatory)]
        [IO.DirectoryInfo[]] $Sources,
        # バケット名
        [Parameter(Mandatory)]
        [String] $BucketName,
        # フォルダー名
        [Parameter(Mandatory)]
        [String] $FolderName
    )
    Begin {
    }
    Process {
        $Sources | % {
            [IO.DirectoryInfo] $source = $_

            if (!$source.Exists) {
                $msg = "コピー元：<{0}> が存在しません。" -f $source
                throw New-Object IO.DirectoryNotFoundException $msg
            }

            log ("[コピー元] {0}" -f $source)

            # 日付フォルダパス作成
            $savePath = Build-S3Path $BucketName $FolderName
            $savePath += $source.Name

            log ("[コピー先] {0}" -f $savePath)

            # データコピー
            aws s3 cp $source.FullName $savePath --recursive
        }
    }
    End {
    }
}

<#
.SYNOPSIS
 日付フォルダーパス生成
.DESCRIPTION
 バケット名、フォルダー名を指定して日付フォルダパスを生成します。
.EXAMPLE
 Build-S3Path $BucketName $FolderName
.INPUTS
 1. バケット名
 2. フォルダー名
.OUTPUTS
 日付フォルダーパスを返します。
.NOTES
 日付フォルダーの書式は [yyyyMMdd]
#>
function Build-S3Path {
    [OutputType([String])]
    param (
        # バケット名
        [Parameter(Mandatory)]
        [String] $BucketName,
        # フォルダー名
        [Parameter(Mandatory)]
        [String] $FolderName
    )
    Begin {
        $yyyyMMdd = Get-Date -f "yyyyMMdd"
    }
    Process {
        "s3://{0}/{1}/{2}/" -f $BucketName.Trim("/"), $FolderName.Trim("/"), $yyyyMMdd
    }
    End {
    }
}
```