フォルダごとS3に移動するPowershellスクリプト

記事タイトルとURLをコピーする

こんにちは、SWX3人目の熊谷(悠)です。
今回S3 や SNS についての説明や AWS Tools for Windows PowerShell のインストール手順などは本稿では取り扱いません。

概要図

BLOG-S3-PowerShell.png

実行ログ保管に CloudWatch Logs も使った方が良さそうですが、簡単なスクリプトなので下記の点から、SNS のみとしています。

  • 単一障害点を無くすため
  • 構成をシンプルにするため
  • 成功通知が来ない事で実行の失敗や予期しない問題に気づけるようにするため

処理概要

タスクスケジューラなどで定期実行される事を想定しています。

  1. カレントディレクトリにスクリプトの実行ログを出力する。
  2. 前日までのスクリプト実行ログファイルを S3 へアップロードする。
  3. スクリプト実行ログのフォルダから前日までのログファイルを削除する。
  4. 移動対象のフォルダを S3 へアップロードする。
  5. アップロードしたフォルダ内のファイルを削除する。
  6. SNS で実行結果を通知する。

移動イメージ

実行マシン上のディレクトリ構成は一例です。

  • 対象フォルダ配下のフォルダ/ファイルも同じ構成でアップロードします。
  • ルートディレクトリから対象フォルダまでの階層が深い場合、親の階層構造は維持したままアップロードします。
  • 指定していないフォルダのファイルや、対象フォルダより上の階層のファイルはアップロードしません。
C:\example\ROOT(指定するルートディレクトリ)
│
│ app.txt
│
├─config
│      buzz.txt
│      fizz.txt
│
├─data(指定する対象フォルダ)
│      hoge.txt
│      huga.txt
│
├─export(指定する対象フォルダ)
│   │  foo.txt
│   │  bar.txt
│   │
│   └─file
│          baz.txt
│
└─Documents
    │  aaa.txt
    │  bbb.txt
    │
    ├─data(指定する対象フォルダ)
    │      ccc.txt
    │      ddd.txt
    │
    └─file
           eee.txt
           fff.txt
powershell-move-123456789012
/backup
│
├─log
│      move2S3-20230417.log
│      move2S3-20230418.log
│
├─data(指定した対象フォルダ)
│      hoge.txt
│      huga.txt
│
├─export(指定した対象フォルダ)
│   │  foo.txt
│   │  bar.txt
│   │
│   └─file
│          baz.txt
│
└─Documents
    │
    └─data(指定した対象フォルダ)
           ccc.txt
           ddd.txt

AWS Tools for Windows PowerShellについて

PowerShell コマンドラインから AWS リソースに対する操作が行えるツールです。 PowerShell 版 AWS CLI のようなものです。

ローカルのWindowsやLinux、MacOSにもインストールできます。

https://aws.amazon.com/jp/powershell/

AWS 公式 AMI の Windows には AWS Tools for Windows PowerShell がプリインストールされています。

Tools for Windows PowerShell (AWSPowerShell モジュール) は、すべての Windows ベースの Amazon Machine Image (AMI) にデフォルトでインストールされます。

https://docs.aws.amazon.com/powershell/latest/userguide/pstools-getting-set-up-windows.html

環境情報

本稿ではツールをインストールする代わりに、以下の AMI で EC2 を起動しています。

AMI ID:ami-0b168f9cd578fe5d5
AMI 名:Windows_Server-2022-English-Full-Base-2023.03.15

PS > Get-ComputerInfo

WindowsBuildLabEx                                       : 20348.1.amd64fre.fe_release.210507-1500
WindowsCurrentVersion                                   : 6.3
WindowsEditionId                                        : ServerDatacenter
WindowsProductName                                      : Windows Server 2022 Datacenter
WindowsVersion                                          : 2009
OSDisplayVersion                                        : 21H2


PS > $PSVersionTable

Name                           Value
----                           -----
PSVersion                      5.1.20348.1366
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.20348.1366
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1


PS > Get-AWSPowerShellVersion

AWS Tools for Windows PowerShell
Version 4.1.285
Copyright 2012-2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.

Amazon Web Services SDK for .NET
Core Runtime Version 3.7.105.13
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.

Release notes: https://github.com/aws/aws-tools-for-powershell/blob/master/CHANGELOG.md

This software includes third party software subject to the following copyrights:
- Logging from log4net, Apache License
[http://logging.apache.org/log4net/license.html]

リソース作成

移動先 S3 バケットと、スクリプト実行結果通知先 SNS トピックを作成します。
必要に応じて後項の IAM 権限設定も適宜実施してください。

S3

名前は指定して、その他はデフォルト設定で大丈夫です。 2023-04-18_16h52_19.png

SNS

名前と、タイプはスタンダードを指定して、その他はデフォルト設定で大丈夫です。 2023-04-18_16h54_46.png

サブスクリプションも作成します。 2023-04-18_17h02_37.png

権限設定

本稿では下記の IAM ポリシーをアタッチした IAM ロールを、 EC2 にアタッチしています。

スクリプトを実行する上で必要最小限の権限しか許可していません。
S3 バケット名や SNS トピック ARN 指定箇所は作成したリソースの値で置き換えてください。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "s3:PutObject",
            "Resource": [
                "arn:aws:s3:::powershell-move-123456789012",
                "arn:aws:s3:::powershell-move-123456789012/*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": "sns:Publish",
            "Resource": "arn:aws:sns:ap-northeast-1:123456789012:powershell-move"
        }
    ]
}

スクリプト

AWS Tools for PowerShell には、AWS CLI のs3 mv(あるいは Linux のmvコマンド)相当のコマンドレットが無い為、mv のような動作をするようにスクリプトを作成しました。
※引数などには対応していません。

$ErrorActionPreference = 'Stop'設定について
Write-S3Object コマンドレットはエラー発生時に終了するエラーとなるので catch できますが、フォルダ検索や削除処理、Publish-SNSMessage コマンドレットは終了しないエラーで処理が継続するため catch できず、正常に動作していないのに成功した事になってしまうため設定しています。 エラーハンドリングが雑ですが、処理一つ一つに-ErrorAction Stopを付けたり$?で分岐するのは手間だったので、全体に適用されるよう指定しています。

$ROOT_DIRECTORY = "C:\example\ROOT"
$MOVE_TARGET_FOLDER_NAME = "data", "export" # カンマ区切りで複数指定可能
$S3_BUCKET_NAME = "powershell-move-123456789012"
$DESTINATION_PATH = "backup"
$SNS_TOPIC_ARN = "arn:aws:sns:ap-northeast-1:123456789012:powershell-move"
$LOG_FOLDER_NAME = "log"
$LOGFILE_NAME = "move2S3-" + (Get-date -Format "yyyyMMdd") + ".log"

function Write-Log ($message) {
    if (!(Test-Path $LOG_FOLDER_NAME)) {
        New-Item $LOG_FOLDER_NAME -Type Directory
    }
    $txt = (Get-date -Format "yyyy/MM/dd HH:mm:ss") + " " + $message
    Write-Output $txt | Out-File -FilePath "$LOG_FOLDER_NAME\$LOGFILE_NAME" -Append
    return $null
}

function Set-Error ($error) {
    Write-Log "エラー発生"
    $error[0] >> "$LOG_FOLDER_NAME\$LOGFILE_NAME"
    Write-Log ("Publish-SNSMessage -TopicArn $SNS_TOPIC_ARN -Subject 【失敗】S3への移動スクリプト実行中にエラーが発生しました -Message (失敗) " + [Net.Dns]::GetHostName() + " にて S3への移動スクリプト の実行中にエラーが発生しました")
    Publish-SNSMessage -TopicArn $SNS_TOPIC_ARN -Subject "【失敗】S3への移動スクリプト実行中にエラーが発生しました" -Message ("(失敗) " + [Net.Dns]::GetHostName() + " にて S3への移動スクリプト の実行中にエラーが発生しました") | ForEach-Object {Write-Log $_}
    exit 1
}

try {
    $ErrorActionPreference = 'Stop'
    Write-Log "処理開始"

    Write-Log "前日までの実行ログ移動"
    Write-Log "Write-S3Object -BucketName $S3_BUCKET_NAME -KeyPrefix $DESTINATION_PATH/$LOG_FOLDER_NAME -Folder $LOG_FOLDER_NAME -Recurse"
    Write-S3Object -BucketName $S3_BUCKET_NAME -KeyPrefix "$DESTINATION_PATH/$LOG_FOLDER_NAME" -Folder $LOG_FOLDER_NAME -Recurse
    Write-Log "Remove-Item $LOG_FOLDER_NAME -Exclude $LOGFILE_NAME -Recurse"
    Remove-Item $LOG_FOLDER_NAME -Exclude $LOGFILE_NAME -Recurse

    Write-Log "移動対象ファイル一覧"
    $target_folder_path_array = @()
    foreach ($folder_name in $MOVE_TARGET_FOLDER_NAME) {
        $target_folder_path = (Get-ChildItem -Path $ROOT_DIRECTORY -Filter $folder_name -Attributes D -Recurse).FullName
        $target_folder_path_array += $target_folder_path
        (Get-ChildItem $target_folder_path -File).Name >> "$LOG_FOLDER_NAME\$LOGFILE_NAME"
    }

    Write-Log "対象フォルダ移動"
    foreach ($target_folder_path in $target_folder_path_array) {
        $key_path =  $target_folder_path.Replace($ROOT_DIRECTORY, "").Replace("\", "/")
        Write-Log "Write-S3Object -BucketName $S3_BUCKET_NAME -KeyPrefix $DESTINATION_PATH$key_path -Folder $target_folder_path -Recurse"
        Write-S3Object -BucketName $S3_BUCKET_NAME -KeyPrefix $DESTINATION_PATH$key_path -Folder $target_folder_path -Recurse

        Write-Log "Remove-Item -Path ${target_folder_path}\* -Recurse"
        Remove-Item -Path "${target_folder_path}\*" -Recurse
    }

    Write-Log "Publish-SNSMessage -TopicArn $SNS_TOPIC_ARN -Message (成功) S3への移動処理が完了しました"
    Publish-SNSMessage -TopicArn $SNS_TOPIC_ARN -Message "(成功) S3への移動処理が完了しました" | ForEach-Object {Write-Log $_}
    exit 0
} catch {
    Set-Error $error
} finally {
    Write-Log "処理終了"
    $ErrorActionPreference = 'Continue'
}

S3 には 秒間 3,500 件の Put リクエストが可能ですが、スクリプトはこの制約を超えないという想定のもと、作成しています。
制約を超えて、エラーコード「503 Slow Down」となる場合、ループ処理の中に Sleep を入れる等の処置が必要です。

S3 バケットのプレフィックスごとに 1 秒あたり 3,500 件の PUT/COPY/POST/DELETE リクエスト、または 5,500 件の GET/HEAD リクエストを送信することができます。

https://repost.aws/ja/knowledge-center/http-5xx-errors-s3

実行結果

成功時メール 2023-04-18_16h46_34.png

失敗時メール 2023-04-18_16h48_36.png

S3 バケット上の格納イメージ 2023-04-18_17h08_45.png 2023-04-18_17h22_11.png 2023-04-18_17h23_54.png

t3a.large のインスタンスタイプで 1440 ファイル、1 ファイルあたり 1.5MB(合計ファイルサイズは2.1GB)を対象として計測したところ、5分程度で実行完了しました。

参考

AWS Tools for Windows PowerShell

https://docs.aws.amazon.com/powershell/latest/userguide/pstools-welcome.html

インストール・セットアップ

https://aws.amazon.com/jp/powershell/

https://docs.aws.amazon.com/powershell/latest/userguide/pstools-getting-set-up.html

https://docs.aws.amazon.com/powershell/latest/userguide/pstools-getting-started.html

使用例

https://docs.aws.amazon.com/powershell/latest/userguide/pstools-using.html

https://docs.aws.amazon.com/ja_jp/powershell/latest/userguide/pstools-s3-upload-object.html

コマンドレットリファレンス

https://docs.aws.amazon.com/powershell/latest/reference/Index.html

S3 アップロード

https://docs.aws.amazon.com/powershell/latest/reference/items/Write-S3Object.html

SNS 送信

https://docs.aws.amazon.com/powershell/latest/reference/items/Publish-SNSMessage.html

PowerShell

ログ出力

https://tooljp.com/windows/chigai/html/Programming/Write_Host-Write_Output_echo-chigai.html

ファイル削除

https://qiita.com/mima_ita/items/ae31f3a19389e69b307f#%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E5%89%8A%E9%99%A4

パイプ処理

https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/about/about_pipelines

エラーハンドリング

https://learn.microsoft.com/en-us/powershell/scripting/learn/deep-dives/everything-about-exceptions

https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_try_catch_finally

https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_preference_variables#erroractionpreference

https://devblogs.microsoft.com/scripting/understanding-non-terminating-errors-in-powershell/