

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

# Data Lifecycle Manager を使用してアプリケーション整合性のあるスナップショットを自動化
<a name="automate-app-consistent-backups"></a>

Amazon Data Lifecycle Manager では、インスタンスをターゲットとするスナップショットライフサイクルポリシーで事前スクリプトと事後スクリプトを有効にすることで、アプリケーション整合性のあるスナップショットを自動化できます。

Amazon Data Lifecycle Manager は AWS Systems Manager (Systems Manager) と統合され、アプリケーション整合性のあるスナップショットをサポートします。Amazon Data Lifecycle Manager は、事前スクリプトと事後スクリプトを含む Systems Manager (SSM) コマンドドキュメントを使用して、アプリケーション整合性のあるスナップショットを完成させるために必要なアクションを自動化します。Amazon Data Lifecycle Manager は、スナップショットの作成を開始する前に、事前スクリプト内のコマンドを実行して I/O をフリーズおよびフラッシュします。Amazon Data Lifecycle Manager はスナップショットの作成を開始した後に、事後スクリプト内のコマンドを実行して I/O を解凍します。

Amazon Data Lifecycle Manager を使用すると、以下のアプリケーション整合性のあるスナップショットを自動化できます。
+ Volume Shadow Copy Service (VSS) を使用した Windows アプリケーション
+  AWS マネージド SSDM ドキュメントを使用する SAP HANA。詳細については、「[SAP HANA 用 Amazon EBS スナップショット](https://docs.aws.amazon.com/sap/latest/sap-hana/ebs-sap-hana.html)」を参照してください。
+ SSM ドキュメントテンプレートを使用した MySQL、PostgreSQL、InterSystems IRIS などのセルフマネージドデータベース

**Topics**
+ [事前スクリプトと事後スクリプトを使用するための要件](#app-consistent-prereqs)
+ [アプリケーション整合性のあるスナップショットの使用開始](#app-consistent-get-started)
+ [Amazon Data Lifecycle Manager での VSS バックアップに関する考慮事項](#app-consistent-vss)
+ [アプリケーション整合性のあるスナップショットの責任共有](#shared-responsibility)

## 事前スクリプトと事後スクリプトを使用するための要件
<a name="app-consistent-prereqs"></a>

次の表は、Amazon Data Lifecycle Manager で事前スクリプトと事後スクリプトを使用する際の要件の概要を示しています。


|  | アプリケーションコンシステントなスナップショット |  | 
| --- |--- |--- |
| 要件 | VSS バックアップ | カスタム SSM ドキュメント | その他のユースケース | 
| --- |--- |--- |--- |
| SSM Agent installed and running on target instances | ✓ | ✓ | ✓ | 
| VSS system requirements met on target instances | ✓ |  |  | 
| VSS enabled instance profile associated with target instances | ✓ |  |  | 
| VSS components installed on target instances | ✓ |  |  | 
| Prepare SSM document with pre and post script commands |  | ✓ | ✓ | 
| Prepare Amazon Data Lifecycle Manager IAM role run pre and post scripts | ✓ | ✓ | ✓ | 
| Create snapshot policy that targets instances and is configured for pre and post scripts | ✓ | ✓ | ✓ | 

## アプリケーション整合性のあるスナップショットの使用開始
<a name="app-consistent-get-started"></a>

このセクションでは、Amazon Data Lifecycle Manager を使用してアプリケーション整合性のあるスナップショットを自動化するために必要な手順について説明します。

### ステップ 1: ターゲットインスタンスを準備する
<a name="prep-instances"></a>

Amazon Data Lifecycle Manager を使用して、ターゲットインスタンスをアプリケーション整合性のあるスナップショット用に準備する必要があります。ユースケースに応じて、以下のいずれかを実行します。

------
#### [ Prepare for VSS Backups ]

**ターゲットインスタンスを VSS バックアップ用に準備するには**

1. SSM Agent がまだインストールされていない場合は、ターゲットインスタンスにインストールします。SSM Agent がターゲットインスタンスに既にインストールされている場合は、このステップをスキップしてください。

   詳細については、「[Windows Server 用 EC2 インスタンスで SSM Agent を使用する](https://docs.aws.amazon.com/systems-manager/latest/userguide/ssm-agent-windows.html)」を参照してください。

1. SSM Agent が実行中であることを確認します。詳細については、「[SSM Agent ステータスの確認とエージェントの起動](https://docs.aws.amazon.com/systems-manager/latest/userguide/ssm-agent-status-and-restart.html)」を参照してください。

1. Systems Manager の Amazon EC2 インスタンスをセットアップします。詳細については、「*AWS Systems Manager ユーザーガイド*」の「[Systems Manager を利用した EC2 インスタンスの管理](https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-setting-up-ec2.html)」を参照してください。

1. [VSS バックアップのシステム要件が満たされていることを確認します](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/application-consistent-snapshots-prereqs.html)。

1. [VSS 対応インスタンスプロファイルをターゲットインスタンスにアタッチします](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/vss-iam-reqs.html)。

1. [VSS コンポーネントをインストールします](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/application-consistent-snapshots-getting-started.html)。

------
#### [ Prepare for SAP HANA backups ]

**ターゲットインスタンスを SAP HANA バックアップ用に準備するには**

1. ターゲットインスタンス上に SAP HANA 環境を準備します。

   1. SAP HANA と一緒にインスタンスをセットアップします。既存の SAP HANA 環境がない場合は、「[AWSでの SAP HANA 環境設定](https://docs.aws.amazon.com/sap/latest/sap-hana/std-sap-hana-environment-setup.html)」を参照してください。

   1. SystemDB に適切な管理者ユーザーとしてログインします。

   1. Amazon Data Lifecycle Manager で使用するデータベースバックアップユーザーを作成します。

      ```
      CREATE USER {{username}} PASSWORD {{password}} NO FORCE_FIRST_PASSWORD_CHANGE;
      ```

      例えば、次のコマンドは、名前が `dlm_user` でパスワードが `password` のユーザーを作成します。

      ```
      CREATE USER dlm_user PASSWORD password NO FORCE_FIRST_PASSWORD_CHANGE;
      ```

   1. 前のステップで作成したデータベースバックアップユーザーに `BACKUP OPERATOR` ロールを割り当てます。

      ```
      GRANT BACKUP OPERATOR TO {{username}}
      ```

      例えば、次のコマンドは、`dlm_user` という名前のユーザーにロールを割り当てます。

      ```
      GRANT BACKUP OPERATOR TO dlm_user
      ```

   1. オペレーティングシステムに管理者 (例: `{{sid}}adm`) としてログインします。

   1. 接続情報を保存する `hdbuserstore` エントリを作成して、ユーザーが情報を入力しなくても SAP HANA SSM ドキュメントが SAP HANA に接続できるようにします。

      ```
      hdbuserstore set DLM_HANADB_SNAPSHOT_USER localhost:3{{hana_instance_number}}13 {{username}} {{password}}
      ```

      例えば、次のようになります。

      ```
      hdbuserstore set DLM_HANADB_SNAPSHOT_USER localhost:30013 dlm_user password
      ```

   1. 接続をテストします。

      ```
      hdbsql -U DLM_HANADB_SNAPSHOT_USER "select * from dummy"
      ```

1. SSM Agent がまだインストールされていない場合は、ターゲットインスタンスにインストールします。SSM Agent がターゲットインスタンスに既にインストールされている場合は、このステップをスキップしてください。

   詳細については、「[Linux 用 EC2 インスタンスに SSM Agent を手動でインストールおよびアンインストールする](https://docs.aws.amazon.com/systems-manager/latest/userguide/manually-install-ssm-agent-linux.html)」を参照してください。

1. SSM Agent が実行中であることを確認します。詳細については、「[SSM Agent ステータスの確認とエージェントの起動](https://docs.aws.amazon.com/systems-manager/latest/userguide/ssm-agent-status-and-restart.html)」を参照してください。

1. Systems Manager の Amazon EC2 インスタンスをセットアップします。詳細については、「*AWS Systems Manager ユーザーガイド*」の「[Systems Manager を利用した EC2 インスタンスの管理](https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-setting-up-ec2.html)」を参照してください。

------
#### [ Prepare for custom SSM documents ]

**ターゲットインスタンスのカスタム SSM ドキュメントを準備するには**

1. SSM Agent がまだインストールされていない場合は、ターゲットインスタンスにインストールします。SSM Agent がターゲットインスタンスに既にインストールされている場合は、このステップをスキップしてください。
   + (Linux インスタンス) [Linux 用 EC2 インスタンスに SSM Agent を手動でインストールおよびアンインストールする](https://docs.aws.amazon.com/systems-manager/latest/userguide/manually-install-ssm-agent-linux.html)
   + (Windows インスタンス) [Windows Server 用 EC2 インスタンスで SSM Agent を使用する](https://docs.aws.amazon.com/systems-manager/latest/userguide/ssm-agent-windows.html)

1. SSM Agent が実行中であることを確認します。詳細については、「[SSM Agent ステータスの確認とエージェントの起動](https://docs.aws.amazon.com/systems-manager/latest/userguide/ssm-agent-status-and-restart.html)」を参照してください。

1. Systems Manager の Amazon EC2 インスタンスをセットアップします。詳細については、「*AWS Systems Manager ユーザーガイド*」の「[Systems Manager を利用した EC2 インスタンスの管理](https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-setting-up-ec2.html)」を参照してください。

------

### ステップ 2: SSM ドキュメントを準備する
<a name="prep-ssm-doc"></a>

**注記**  
このステップはカスタム SSM ドキュメントの場合にのみ必要です。VSS バックアップや SAP HANA には必要ありません。VSS Backups と SAP HANA の場合、Amazon Data Lifecycle Manager は AWS マネージド SSM ドキュメントを使用します。

MySQL や PostgreSQL、または InterSystems IRIS などのセルフマネージドデータベースのアプリケーション整合性のあるスナップショットを自動化している場合は、スナップショットの作成を開始する前に I/O をフリーズおよびフラッシュするための事前スクリプトと、スナップショットの作成を開始した後に I/O を解凍するための事後スクリプトを含む SSM コマンドドキュメントを作成する必要があります。

MySQL や PostgreSQL、または InterSystems IRIS データベースが標準設定を使用している場合は、以下のサンプル SSM ドキュメントコンテンツを使用して SSM コマンドドキュメントを作成できます。MySQL や PostgreSQL、または InterSystems IRIS データベースが非標準設定を使用している場合は、以下のサンプルコンテンツを SSM コマンドドキュメントの開始点として使用し、要件に合わせてカスタマイズできます。あるいは、新しい SSM ドキュメントを最初から作成する場合は、以下の空の SSM ドキュメントテンプレートを使用して、該当するドキュメントセクションに事前コマンドと事後コマンドを追加できます。

**次の点に注意してください。**  
データベース設定に対して SSM ドキュメントが適切かつ必要なアクションを実行していることを確認するのは、ユーザーの責任になります。
SSM ドキュメント内の事前スクリプトと事後スクリプトが I/O を正常にフリーズ、フラッシュ、および解凍できた場合にのみ、スナップショットのアプリケーション整合性が保証されます。
SSM ドキュメントには、`pre-script`、`post-script`、`dry-run` などの `allowedValues` の必須フィールドが含まれている必要があります。Amazon Data Lifecycle Manager は、これらのセクションのコンテンツに基づいてインスタンス上でコマンドを実行します。SSM ドキュメントにこれらのセクションがない場合、Amazon Data Lifecycle Manager は、そのドキュメントを実行に失敗したものとして扱います。

------
#### [ MySQL sample document content ]

```
###===============================================================================###
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.

# Permission is hereby granted, free of charge, to any person obtaining a copy of this
# software and associated documentation files (the "Software"), to deal in the Software
# without restriction, including without limitation the rights to use, copy, modify,
# merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
###===============================================================================###
schemaVersion: '2.2'
description: Amazon Data Lifecycle Manager Pre/Post script for MySQL databases
parameters:
  executionId:
    type: String
    default: None
    description: (Required) Specifies the unique identifier associated with a pre and/or post execution
    allowedPattern: ^(None|[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12})$
  command:
  # Data Lifecycle Manager will trigger the pre-script and post-script actions during policy execution. 
  # 'dry-run' option is intended for validating the document execution without triggering any commands
  # on the instance. The following allowedValues will allow Data Lifecycle Manager to successfully 
  # trigger pre and post script actions.
    type: String
    default: 'dry-run'
    description: (Required) Specifies whether pre-script and/or post-script should be executed.
    allowedValues:
    - pre-script
    - post-script
    - dry-run

mainSteps:
- action: aws:runShellScript
  description: Run MySQL Database freeze/thaw commands
  name: run_pre_post_scripts
  precondition:
    StringEquals:
    - platformType
    - Linux
  inputs:
    runCommand:
    - |
      #!/bin/bash

      ###===============================================================================###
      ### Error Codes
      ###===============================================================================###
      # The following Error codes will inform Data Lifecycle Manager of the type of error 
      # and help guide handling of the error. 
      # The Error code will also be emitted via AWS Eventbridge events in the 'cause' field.
      # 1 Pre-script failed during execution - 201
      # 2 Post-script failed during execution - 202
      # 3 Auto thaw occurred before post-script was initiated - 203
      # 4 Pre-script initiated while post-script was expected - 204
      # 5 Post-script initiated while pre-script was expected - 205
      # 6 Application not ready for pre or post-script initiation - 206

      ###=================================================================###
      ### Global variables
      ###=================================================================###
      START=$(date +%s)
      # For testing this script locally, replace the below with OPERATION=$1.
      OPERATION={{ command }}
      FS_ALREADY_FROZEN_ERROR='freeze failed: Device or resource busy'
      FS_ALREADY_THAWED_ERROR='unfreeze failed: Invalid argument'
      FS_BUSY_ERROR='mount point is busy'

      # Auto thaw is a fail safe mechanism to automatically unfreeze the application after the 
      # duration specified in the global variable below. Choose the duration based on your
      # database application's tolerance to freeze.
      export AUTO_THAW_DURATION_SECS="60"

      # Add all pre-script actions to be performed within the function below
      execute_pre_script() {
          echo "INFO: Start execution of pre-script"
          # Check if filesystem is already frozen. No error code indicates that filesystem 
          # is not currently frozen and that the pre-script can proceed with freezing the filesystem.
          check_fs_freeze
          # Execute the DB commands to flush the DB in preparation for snapshot
          snap_db
          # Freeze the filesystem. No error code indicates that filesystem was succefully frozen
          freeze_fs

          echo "INFO: Schedule Auto Thaw to execute in ${AUTO_THAW_DURATION_SECS} seconds."
          $(nohup bash -c execute_schedule_auto_thaw  >/dev/null 2>&1 &)
      }

      # Add all post-script actions to be performed within the function below
      execute_post_script() {
          echo "INFO: Start execution of post-script"
          # Unfreeze the filesystem. No error code indicates that filesystem was successfully unfrozen.
          unfreeze_fs
          thaw_db
      }

      # Execute Auto Thaw to automatically unfreeze the application after the duration configured 
      # in the AUTO_THAW_DURATION_SECS global variable.
      execute_schedule_auto_thaw() {
          sleep ${AUTO_THAW_DURATION_SECS}
          execute_post_script
      }

      # Disable Auto Thaw if it is still enabled
      execute_disable_auto_thaw() {
          echo "INFO: Attempting to disable auto thaw if enabled"
          auto_thaw_pgid=$(pgrep -f execute_schedule_auto_thaw | xargs -i ps -hp {} -o pgid)
          if [ -n "${auto_thaw_pgid}" ]; then
              echo "INFO: execute_schedule_auto_thaw process found with pgid ${auto_thaw_pgid}"
              sudo pkill -g ${auto_thaw_pgid}
              rc=$?
              if [ ${rc} != 0 ]; then
                  echo "ERROR: Unable to kill execute_schedule_auto_thaw process. retval=${rc}"
              else
                  echo "INFO: Auto Thaw  has been disabled"
              fi
          fi
      }

      # Iterate over all the mountpoints and check if filesystem is already in freeze state.
      # Return error code 204 if any of the mount points are already frozen.
      check_fs_freeze() {
          for target in $(lsblk -nlo MOUNTPOINTS)
          do
              # Freeze of the root and boot filesystems is dangerous and pre-script does not freeze these filesystems.
              # Hence, we will skip the root and boot mountpoints while checking if filesystem is in freeze state.
              if [ $target == '/' ]; then continue; fi
              if [[ "$target" == *"/boot"* ]]; then continue; fi

              error_message=$(sudo mount -o remount,noatime $target 2>&1)
              # Remount will be a no-op without a error message if the filesystem is unfrozen.
              # However, if filesystem is already frozen, remount will fail with busy error message.
              if [ $? -ne 0 ];then
                  # If the filesystem is already in frozen, return error code 204
                  if [[ "$error_message" == *"$FS_BUSY_ERROR"* ]];then
                      echo "ERROR: Filesystem ${target} already frozen. Return Error Code: 204"
                      exit 204
                  fi
                  # If the check filesystem freeze failed due to any reason other than the filesystem already frozen, return 201
                  echo "ERROR: Failed to check_fs_freeze on mountpoint $target due to error - $errormessage"
                  exit 201
              fi
          done
      } 

      # Iterate over all the mountpoints and freeze the filesystem.
      freeze_fs() {
          for target in $(lsblk -nlo MOUNTPOINTS)
          do
              # Freeze of the root and boot filesystems is dangerous. Hence, skip filesystem freeze 
              # operations for root and boot mountpoints.
              if [ $target == '/' ]; then continue; fi
              if [[ "$target" == *"/boot"* ]]; then continue; fi
              echo "INFO: Freezing $target"
              error_message=$(sudo fsfreeze -f $target 2>&1)
              if [ $? -ne 0 ];then
                  # If the filesystem is already in frozen, return error code 204
                  if [[ "$error_message" == *"$FS_ALREADY_FROZEN_ERROR"* ]]; then
                      echo "ERROR: Filesystem ${target} already frozen. Return Error Code: 204"
                      sudo mysql -e 'UNLOCK TABLES;'
                      exit 204
                  fi
                  # If the filesystem freeze failed due to any reason other than the filesystem already frozen, return 201
                  echo "ERROR: Failed to freeze mountpoint $targetdue due to error - $errormessage"
                  thaw_db
                  exit 201
              fi
              echo "INFO: Freezing complete on $target"
          done
      }

      # Iterate over all the mountpoints and unfreeze the filesystem.
      unfreeze_fs() {
          for target in $(lsblk -nlo MOUNTPOINTS)
          do
              # Freeze of the root and boot filesystems is dangerous and pre-script does not freeze these filesystems.
              # Hence, will skip the root and boot mountpoints during unfreeze as well.
              if [ $target == '/' ]; then continue; fi
              if [[ "$target" == *"/boot"* ]]; then continue; fi
              echo "INFO: Thawing $target"
              error_message=$(sudo fsfreeze -u $target 2>&1)
              # Check if filesystem is already unfrozen (thawed). Return error code 204 if filesystem is already unfrozen.
              if [ $? -ne 0 ]; then
                  if [[ "$error_message" == *"$FS_ALREADY_THAWED_ERROR"* ]]; then
                      echo "ERROR: Filesystem ${target} is already in thaw state. Return Error Code: 205"
                      exit 205
                  fi
                  # If the filesystem unfreeze failed due to any reason other than the filesystem already unfrozen, return 202
                  echo "ERROR: Failed to unfreeze mountpoint $targetdue due to error - $errormessage"
                  exit 202
              fi
              echo "INFO: Thaw complete on $target"
          done    
      }

      snap_db() {
          # Run the flush command only when MySQL DB service is up and running
          sudo systemctl is-active --quiet mysqld.service
          if [ $? -eq 0 ]; then
              echo "INFO: Execute MySQL Flush and Lock command."
              sudo mysql -e 'FLUSH TABLES WITH READ LOCK;'
              # If the MySQL Flush and Lock command did not succeed, return error code 201 to indicate pre-script failure
              if [ $? -ne 0 ]; then
                  echo "ERROR: MySQL FLUSH TABLES WITH READ LOCK command failed."
                  exit 201
              fi
              sync
          else 
              echo "INFO: MySQL service is inactive. Skipping execution of MySQL Flush and Lock command."
          fi
      }

      thaw_db() {
          # Run the unlock command only when MySQL DB service is up and running
          sudo systemctl is-active --quiet mysqld.service
          if [ $? -eq 0 ]; then
              echo "INFO: Execute MySQL Unlock"
              sudo mysql -e 'UNLOCK TABLES;'
          else 
              echo "INFO: MySQL service is inactive. Skipping execution of MySQL Unlock command."
          fi
      }

      export -f execute_schedule_auto_thaw
      export -f execute_post_script
      export -f unfreeze_fs
      export -f thaw_db

      # Debug logging for parameters passed to the SSM document
      echo "INFO: ${OPERATION} starting at $(date) with executionId: ${EXECUTION_ID}"

      # Based on the command parameter value execute the function that supports 
      # pre-script/post-script operation
      case ${OPERATION} in
          pre-script)
              execute_pre_script
              ;;
          post-script)
              execute_post_script
              execute_disable_auto_thaw
              ;;
          dry-run)
              echo "INFO: dry-run option invoked - taking no action"
              ;;
          *)
              echo "ERROR: Invalid command parameter passed. Please use either pre-script, post-script, dry-run."
              exit 1 # return failure
              ;;
      esac

      END=$(date +%s)
      # Debug Log for profiling the script time
      echo "INFO: ${OPERATION} completed at $(date). Total runtime: $((${END} - ${START})) seconds."
```

------
#### [ PostgreSQL sample document content ]

```
###===============================================================================###
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.

# Permission is hereby granted, free of charge, to any person obtaining a copy of this
# software and associated documentation files (the "Software"), to deal in the Software
# without restriction, including without limitation the rights to use, copy, modify,
# merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
###===============================================================================###
schemaVersion: '2.2'
description: Amazon Data Lifecycle Manager Pre/Post script for PostgreSQL databases
parameters:
  executionId:
    type: String
    default: None
    description: (Required) Specifies the unique identifier associated with a pre and/or post execution
    allowedPattern: ^(None|[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12})$
  command:
  # Data Lifecycle Manager will trigger the pre-script and post-script actions during policy execution. 
  # 'dry-run' option is intended for validating the document execution without triggering any commands
  # on the instance. The following allowedValues will allow Data Lifecycle Manager to successfully 
  # trigger pre and post script actions.
    type: String
    default: 'dry-run'
    description: (Required) Specifies whether pre-script and/or post-script should be executed.
    allowedValues:
    - pre-script
    - post-script
    - dry-run

mainSteps:
- action: aws:runShellScript
  description: Run PostgreSQL Database freeze/thaw commands
  name: run_pre_post_scripts
  precondition:
    StringEquals:
    - platformType
    - Linux
  inputs:
    runCommand:
    - |
      #!/bin/bash

      ###===============================================================================###
      ### Error Codes
      ###===============================================================================###
      # The following Error codes will inform Data Lifecycle Manager of the type of error 
      # and help guide handling of the error. 
      # The Error code will also be emitted via AWS Eventbridge events in the 'cause' field.
      # 1 Pre-script failed during execution - 201
      # 2 Post-script failed during execution - 202
      # 3 Auto thaw occurred before post-script was initiated - 203
      # 4 Pre-script initiated while post-script was expected - 204
      # 5 Post-script initiated while pre-script was expected - 205
      # 6 Application not ready for pre or post-script initiation - 206

      ###===============================================================================###
      ### Global variables
      ###===============================================================================###
      START=$(date +%s)
      OPERATION={{ command }}
      FS_ALREADY_FROZEN_ERROR='freeze failed: Device or resource busy'
      FS_ALREADY_THAWED_ERROR='unfreeze failed: Invalid argument'
      FS_BUSY_ERROR='mount point is busy'

      # Auto thaw is a fail safe mechanism to automatically unfreeze the application after the 
      # duration specified in the global variable below. Choose the duration based on your
      # database application's tolerance to freeze.
      export AUTO_THAW_DURATION_SECS="60"

      # Add all pre-script actions to be performed within the function below
      execute_pre_script() {
          echo "INFO: Start execution of pre-script"
          # Check if filesystem is already frozen. No error code indicates that filesystem 
          # is not currently frozen and that the pre-script can proceed with freezing the filesystem.
          check_fs_freeze
          # Execute the DB commands to flush the DB in preparation for snapshot
          snap_db
          # Freeze the filesystem. No error code indicates that filesystem was succefully frozen
          freeze_fs

          echo "INFO: Schedule Auto Thaw to execute in ${AUTO_THAW_DURATION_SECS} seconds."
          $(nohup bash -c execute_schedule_auto_thaw  >/dev/null 2>&1 &)
      }

      # Add all post-script actions to be performed within the function below
      execute_post_script() {
          echo "INFO: Start execution of post-script"
          # Unfreeze the filesystem. No error code indicates that filesystem was successfully unfrozen
          unfreeze_fs
      }

      # Execute Auto Thaw to automatically unfreeze the application after the duration configured 
      # in the AUTO_THAW_DURATION_SECS global variable.
      execute_schedule_auto_thaw() {
          sleep ${AUTO_THAW_DURATION_SECS}
          execute_post_script
      }

      # Disable Auto Thaw if it is still enabled
      execute_disable_auto_thaw() {
          echo "INFO: Attempting to disable auto thaw if enabled"
          auto_thaw_pgid=$(pgrep -f execute_schedule_auto_thaw | xargs -i ps -hp {} -o pgid)
          if [ -n "${auto_thaw_pgid}" ]; then
              echo "INFO: execute_schedule_auto_thaw process found with pgid ${auto_thaw_pgid}"
              sudo pkill -g ${auto_thaw_pgid}
              rc=$?
              if [ ${rc} != 0 ]; then
                  echo "ERROR: Unable to kill execute_schedule_auto_thaw process. retval=${rc}"
              else
                  echo "INFO: Auto Thaw  has been disabled"
              fi
          fi
      }

      # Iterate over all the mountpoints and check if filesystem is already in freeze state.
      # Return error code 204 if any of the mount points are already frozen.
      check_fs_freeze() {
          for target in $(lsblk -nlo MOUNTPOINTS)
          do
              # Freeze of the root and boot filesystems is dangerous and pre-script does not freeze these filesystems.
              # Hence, we will skip the root and boot mountpoints while checking if filesystem is in freeze state.
              if [ $target == '/' ]; then continue; fi
              if [[ "$target" == *"/boot"* ]]; then continue; fi

              error_message=$(sudo mount -o remount,noatime $target 2>&1)
              # Remount will be a no-op without a error message if the filesystem is unfrozen.
              # However, if filesystem is already frozen, remount will fail with busy error message.
              if [ $? -ne 0 ];then
                  # If the filesystem is already in frozen, return error code 204
                  if [[ "$error_message" == *"$FS_BUSY_ERROR"* ]];then
                      echo "ERROR: Filesystem ${target} already frozen. Return Error Code: 204"
                      exit 204
                  fi
                  # If the check filesystem freeze failed due to any reason other than the filesystem already frozen, return 201
                  echo "ERROR: Failed to check_fs_freeze on mountpoint $target due to error - $errormessage"
                  exit 201
              fi
          done
      } 

      # Iterate over all the mountpoints and freeze the filesystem.
      freeze_fs() {
          for target in $(lsblk -nlo MOUNTPOINTS)
          do
              # Freeze of the root and boot filesystems is dangerous. Hence, skip filesystem freeze 
              # operations for root and boot mountpoints.
              if [ $target == '/' ]; then continue; fi
              if [[ "$target" == *"/boot"* ]]; then continue; fi
              echo "INFO: Freezing $target"
              error_message=$(sudo fsfreeze -f $target 2>&1)
              if [ $? -ne 0 ];then
                  # If the filesystem is already in frozen, return error code 204
                  if [[ "$error_message" == *"$FS_ALREADY_FROZEN_ERROR"* ]]; then
                      echo "ERROR: Filesystem ${target} already frozen. Return Error Code: 204"
                      exit 204
                  fi
                  # If the filesystem freeze failed due to any reason other than the filesystem already frozen, return 201
                  echo "ERROR: Failed to freeze mountpoint $targetdue due to error - $errormessage"
                  exit 201
              fi
              echo "INFO: Freezing complete on $target"
          done
      }

      # Iterate over all the mountpoints and unfreeze the filesystem.
      unfreeze_fs() {
          for target in $(lsblk -nlo MOUNTPOINTS)
          do
              # Freeze of the root and boot filesystems is dangerous and pre-script does not freeze these filesystems.
              # Hence, will skip the root and boot mountpoints during unfreeze as well.
              if [ $target == '/' ]; then continue; fi
              if [[ "$target" == *"/boot"* ]]; then continue; fi
              echo "INFO: Thawing $target"
              error_message=$(sudo fsfreeze -u $target 2>&1)
              # Check if filesystem is already unfrozen (thawed). Return error code 204 if filesystem is already unfrozen.
              if [ $? -ne 0 ]; then
                  if [[ "$error_message" == *"$FS_ALREADY_THAWED_ERROR"* ]]; then
                      echo "ERROR: Filesystem ${target} is already in thaw state. Return Error Code: 205"
                      exit 205
                  fi
                  # If the filesystem unfreeze failed due to any reason other than the filesystem already unfrozen, return 202
                  echo "ERROR: Failed to unfreeze mountpoint $targetdue due to error - $errormessage"
                  exit 202
              fi
              echo "INFO: Thaw complete on $target"
          done
      }

      snap_db() {
          # Run the flush command only when PostgreSQL DB service is up and running
          sudo systemctl is-active --quiet postgresql
          if [ $? -eq 0 ]; then
              echo "INFO: Execute Postgres CHECKPOINT"
              # PostgreSQL command to flush the transactions in memory to disk
              sudo -u postgres psql -c 'CHECKPOINT;'
              # If the PostgreSQL Command did not succeed, return error code 201 to indicate pre-script failure
              if [ $? -ne 0 ]; then
                  echo "ERROR: Postgres CHECKPOINT command failed."
                  exit 201
              fi
              sync
          else 
              echo "INFO: PostgreSQL service is inactive. Skipping execution of CHECKPOINT command."
          fi
      }

      export -f execute_schedule_auto_thaw
      export -f execute_post_script
      export -f unfreeze_fs

      # Debug logging for parameters passed to the SSM document
      echo "INFO: ${OPERATION} starting at $(date) with executionId: ${EXECUTION_ID}"

      # Based on the command parameter value execute the function that supports 
      # pre-script/post-script operation
      case ${OPERATION} in
          pre-script)
              execute_pre_script
              ;;
          post-script)
              execute_post_script
              execute_disable_auto_thaw
              ;;
          dry-run)
              echo "INFO: dry-run option invoked - taking no action"
              ;;
          *)
              echo "ERROR: Invalid command parameter passed. Please use either pre-script, post-script, dry-run."
              exit 1 # return failure
              ;;
      esac

      END=$(date +%s)
      # Debug Log for profiling the script time
      echo "INFO: ${OPERATION} completed at $(date). Total runtime: $((${END} - ${START})) seconds."
```

------
#### [ InterSystems IRIS sample document content ]

```
###===============================================================================###
# MIT License
# 
# Copyright (c) 2024 InterSystems
# 
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# 
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
###===============================================================================###
schemaVersion: '2.2'
description: SSM Document Template for Amazon Data Lifecycle Manager Pre/Post script feature for InterSystems IRIS.
parameters:
  executionId:
    type: String
    default: None
    description: Specifies the unique identifier associated with a pre and/or post execution
    allowedPattern: ^(None|[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12})$
  command:
    type: String
    # Data Lifecycle Manager will trigger the pre-script and post-script actions. You can also use this SSM document with 'dry-run' for manual testing purposes.
    default: 'dry-run'
    description: (Required) Specifies whether pre-script and/or post-script should be executed.
    #The following allowedValues will allow Data Lifecycle Manager to successfully trigger pre and post script actions.
    allowedValues:
    - pre-script
    - post-script
    - dry-run

mainSteps:
- action: aws:runShellScript
  description: Run InterSystems IRIS Database freeze/thaw commands
  name: run_pre_post_scripts
  precondition:
    StringEquals:
    - platformType
    - Linux
  inputs:
    runCommand:
    - |
      #!/bin/bash
      ###===============================================================================###
      ### Global variables
      ###===============================================================================###
      DOCKER_NAME=iris
      LOGDIR=./
      EXIT_CODE=0
      OPERATION={{ command }}
      START=$(date +%s)
      
      # Check if Docker is installed
      # By default if Docker is present, script assumes that InterSystems IRIS is running in Docker
      # Leave only the else block DOCKER_EXEC line, if you run InterSystems IRIS non-containerised (and Docker is present).
      # Script assumes irissys user has OS auth enabled, change the OS user or supply login/password depending on your configuration.
      if command -v docker &> /dev/null
      then
        DOCKER_EXEC="docker exec $DOCKER_NAME"
      else
        DOCKER_EXEC="sudo -i -u irissys"
      fi
      
                    
      # Add all pre-script actions to be performed within the function below
      execute_pre_script() {
        echo "INFO: Start execution of pre-script"
        
        # find all iris running instances
        iris_instances=$($DOCKER_EXEC iris qall 2>/dev/null | tail -n +3 | grep '^up' | cut -c5-  | awk '{print $1}')
        echo "`date`: Running iris instances $iris_instances"
      
        # Only for running instances
        for INST in $iris_instances; do
      
          echo "`date`: Attempting to freeze $INST"
      
          # Detailed instances specific log
          LOGFILE=$LOGDIR/$INST-pre_post.log
          
          #check Freeze status before starting
          $DOCKER_EXEC irissession $INST -U '%SYS' "##Class(Backup.General).IsWDSuspendedExt()"
          freeze_status=$?
          if [ $freeze_status -eq 5 ]; then
            echo "`date`:   ERROR: $INST IS already FROZEN"
            EXIT_CODE=204
          else
            echo "`date`:   $INST is not frozen"
            # Freeze
            # Docs: https://docs.intersystems.com/irislatest/csp/documatic/%25CSP.Documatic.cls?LIBRARY=%25SYS&CLASSNAME=Backup.General#ExternalFreeze
            $DOCKER_EXEC irissession $INST -U '%SYS' "##Class(Backup.General).ExternalFreeze(\"$LOGFILE\",,,,,,600,,,300)"
            status=$?
      
            case $status in
              5) echo "`date`:   $INST IS FROZEN"
                ;;
              3) echo "`date`:   $INST FREEZE FAILED"
                EXIT_CODE=201
                ;;
              *) echo "`date`:   ERROR: Unknown status code: $status"
                EXIT_CODE=201
                ;;
            esac
            echo "`date`:   Completed freeze of $INST"
          fi
        done
        echo "`date`: Pre freeze script finished"
      }
                    
      # Add all post-script actions to be performed within the function below
      execute_post_script() {
        echo "INFO: Start execution of post-script"
      
        # find all iris running instances
        iris_instances=$($DOCKER_EXEC iris qall 2>/dev/null | tail -n +3 | grep '^up' | cut -c5-  | awk '{print $1}')
        echo "`date`: Running iris instances $iris_instances"
      
        # Only for running instances
        for INST in $iris_instances; do
      
          echo "`date`: Attempting to thaw $INST"
      
          # Detailed instances specific log
          LOGFILE=$LOGDIR/$INST-pre_post.log
      
          #check Freeze status befor starting
          $DOCKER_EXEC irissession $INST -U '%SYS' "##Class(Backup.General).IsWDSuspendedExt()"
          freeze_status=$?
          if [ $freeze_status -eq 5 ]; then
            echo "`date`:  $INST is in frozen state"
            # Thaw
            # Docs: https://docs.intersystems.com/irislatest/csp/documatic/%25CSP.Documatic.cls?LIBRARY=%25SYS&CLASSNAME=Backup.General#ExternalFreeze
            $DOCKER_EXEC irissession $INST -U%SYS "##Class(Backup.General).ExternalThaw(\"$LOGFILE\")"
            status=$?
      
            case $status in
              5) echo "`date`:   $INST IS THAWED"
                  $DOCKER_EXEC irissession $INST -U%SYS "##Class(Backup.General).ExternalSetHistory(\"$LOGFILE\")"
                ;;
              3) echo "`date`:   $INST THAW FAILED"
                  EXIT_CODE=202
                ;;
              *) echo "`date`:   ERROR: Unknown status code: $status"
                  EXIT_CODE=202
                ;;
            esac
            echo "`date`:   Completed thaw of $INST"
          else
            echo "`date`:   ERROR: $INST IS already THAWED"
            EXIT_CODE=205
          fi
        done
        echo "`date`: Post thaw script finished"
      }
      
      # Debug logging for parameters passed to the SSM document
        echo "INFO: ${OPERATION} starting at $(date) with executionId: ${EXECUTION_ID}"
                    
      # Based on the command parameter value execute the function that supports 
      # pre-script/post-script operation
      case ${OPERATION} in
        pre-script)
          execute_pre_script
          ;;
        post-script)
          execute_post_script
            ;;
        dry-run)
          echo "INFO: dry-run option invoked - taking no action"
          ;;
        *)
          echo "ERROR: Invalid command parameter passed. Please use either pre-script, post-script, dry-run."
          # return failure
          EXIT_CODE=1
          ;;
      esac
                    
      END=$(date +%s)
      # Debug Log for profiling the script time
      echo "INFO: ${OPERATION} completed at $(date). Total runtime: $((${END} - ${START})) seconds."
      exit $EXIT_CODE
```

詳細については、[GitHub リポジトリ](https://github.com/intersystems-community/aws/blob/master/README.md)を参照してください。

------
#### [ Empty document template ]

```
###===============================================================================###
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.

# Permission is hereby granted, free of charge, to any person obtaining a copy of this
# software and associated documentation files (the "Software"), to deal in the Software
# without restriction, including without limitation the rights to use, copy, modify,
# merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
###===============================================================================###
schemaVersion: '2.2'
description: SSM Document Template for Amazon Data Lifecycle Manager Pre/Post script feature
parameters:
  executionId:
    type: String
    default: None
    description: (Required) Specifies the unique identifier associated with a pre and/or post execution
    allowedPattern: ^(None|[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12})$
  command:
  # Data Lifecycle Manager will trigger the pre-script and post-script actions during policy execution. 
  # 'dry-run' option is intended for validating the document execution without triggering any commands
  # on the instance. The following allowedValues will allow Data Lifecycle Manager to successfully 
  # trigger pre and post script actions.
    type: String
    default: 'dry-run'
    description: (Required) Specifies whether pre-script and/or post-script should be executed.
    allowedValues:
    - pre-script
    - post-script
    - dry-run

mainSteps:
- action: aws:runShellScript
  description: Run Database freeze/thaw commands
  name: run_pre_post_scripts
  precondition:
    StringEquals:
    - platformType
    - Linux
  inputs:
    runCommand:
    - |
      #!/bin/bash

      ###===============================================================================###
      ### Error Codes
      ###===============================================================================###
      # The following Error codes will inform Data Lifecycle Manager of the type of error 
      # and help guide handling of the error. 
      # The Error code will also be emitted via AWS Eventbridge events in the 'cause' field.
      # 1 Pre-script failed during execution - 201
      # 2 Post-script failed during execution - 202
      # 3 Auto thaw occurred before post-script was initiated - 203
      # 4 Pre-script initiated while post-script was expected - 204
      # 5 Post-script initiated while pre-script was expected - 205
      # 6 Application not ready for pre or post-script initiation - 206

      ###===============================================================================###
      ### Global variables
      ###===============================================================================###
      START=$(date +%s)
      # For testing this script locally, replace the below with OPERATION=$1.
      OPERATION={{ command }}

      # Add all pre-script actions to be performed within the function below
      execute_pre_script() {
          echo "INFO: Start execution of pre-script"
      }

      # Add all post-script actions to be performed within the function below
      execute_post_script() {
          echo "INFO: Start execution of post-script"
      }

      # Debug logging for parameters passed to the SSM document
      echo "INFO: ${OPERATION} starting at $(date) with executionId: ${EXECUTION_ID}"

      # Based on the command parameter value execute the function that supports 
      # pre-script/post-script operation
      case ${OPERATION} in
          pre-script)
              execute_pre_script
              ;;
          post-script)
              execute_post_script
              ;;
          dry-run)
              echo "INFO: dry-run option invoked - taking no action"
              ;;
          *)
              echo "ERROR: Invalid command parameter passed. Please use either pre-script, post-script, dry-run."
              exit 1 # return failure
              ;;
      esac

      END=$(date +%s)
      # Debug Log for profiling the script time
      echo "INFO: ${OPERATION} completed at $(date). Total runtime: $((${END} - ${START})) seconds."
```

------

SSM ドキュメントコンテンツを取得したら、以下のいずれかの手順でカスタム SSM ドキュメントを作成します。

------
#### [ Console ]

**SSM コマンドドキュメント を作成するには**

1. [ https://console.aws.amazon.com//systems-manager/ ](https://console.aws.amazon.com//systems-manager/)で AWS Systems Manager コンソールを開きます。

1. ナビゲーションペインで **[ドキュメント]** を選択し、**[ドキュメントの作成]**、**[コマンドまたはセッション]** の順に選択します。

1. [**名前**] に、ドキュメントのわかりやすい名前を入力します。

1. **[ターゲットタイプ]** に、**[/AWS::EC2::Instance]** を選択します。

1. **[ドキュメントタイプ]** に、**[コマンド]** を選択します。

1. **[コンテンツ]** フィールドで **[YAML]** を選択し、ドキュメントコンテンツを貼り付けます。

1. **[ドキュメントタグ]** セクションで、タグキーが `DLMScriptsAccess` でタグ値が `true` のタグを追加します。
**重要**  
`DLMScriptsAccess:true` タグは、*ステップ 3: Amazon Data Lifecycle Manager IAM ロールを準備する*で使用される **AWSDataLifecycleManagerSSMFullAccess** AWS 管理ポリシーで必要です。このポリシーでは `aws:ResourceTag` 条件キーが使用されており、このタグを持つ SSM ドキュメントへのアクセスを制限します。

1. [**ドキュメントの作成**] を選択します。

------
#### [ AWS CLI ]

**SSM コマンドドキュメント を作成するには**  
[create-document](https://docs.aws.amazon.com/cli/latest/reference/ssm/create-document.html) コマンドを使用します。`--name` には、ドキュメントのわかりやすい名前を指定します。`--document-type` の場合、`Command` を指定します。`--content` には、SSM ドキュメントコンテンツを含む .yaml ファイルへのパスを指定します。`--tags` の場合、`"Key=DLMScriptsAccess,Value=true"` を指定します。

```
$ aws ssm create-document \
--content file:{{//path/to/file/documentContent.yaml}} \
--name "{{document_name}}" \
--document-type "Command" \
--document-format YAML \
--tags "Key=DLMScriptsAccess,Value=true"
```

------

### ステップ 3: Amazon Data Lifecycle Manager の IAM ロールを準備する
<a name="prep-iam-role"></a>

**注記**  
このステップは次の場合に必要です。  
カスタム IAM ロールを使用する、事前/事後スクリプト対応のスナップショットポリシーを作成または更新します。
コマンドラインを使用して、デフォルトを使用する、事前/事後スクリプト対応のスナップショットポリシーを作成または更新します。
コンソールを使用して、スナップショットを管理するためのデフォルトロール (**AWSDataLifecycleManagerDefaultRole**) を使用する、事前/事後スクリプト対応のスナップショットポリシーを作成または更新する場合は、このステップをスキップしてください。この場合、**AWSDataLifecycleManagerSSMFullAccess** ポリシーが自動的にそのロールにアタッチされます。

ポリシーに使用する IAM ロールが、ポリシーのターゲットとなるインスタンスで事前スクリプトと事後スクリプトを実行するために必要な SSM アクションを実行する権限を Amazon Data Lifecycle Manager に付与していることを確認する必要があります。

Amazon Data Lifecycle Manager には、必要なアクセス許可を含むマネージドポリシー (**AWSDataLifecycleManagerSSMFullAccess**) が用意されています。スナップショットを管理するための IAM ロールにこのポリシーをアタッチすると、確実にアクセス許可を含めることができます。

**重要**  
AWSDataLifecycleManagerSSMFullAccess マネージドポリシーでは、事前スクリプトと事後スクリプトを使用するときに、`aws:ResourceTag` 条件キーを使って特定の SSM ドキュメントへのアクセスを制限します。Amazon Data Lifecycle Manager が SSM ドキュメントにアクセスできるようにするには、SSM ドキュメントに `DLMScriptsAccess:true` のタグが付けられていることを確認する必要があります。

あるいは、カスタムポリシーを手動で作成するか、使用する IAM ロールに必要なアクセス許可を直接割り当てることもできます。AWSDataLifecycleManagerSSMFullAccess マネージドポリシーで定義されているのと同じアクセス許可を使用できますが、`aws:ResourceTag` 条件キーはオプションです。その条件キーを含めない場合は、SSM ドキュメントに `DLMScriptsAccess:true` のタグを付ける必要はありません。

以下のいずれかの方法を使用して、**AWSDataLifecycleManagerSSMFullAccess** ポリシーを IAM ロールに追加します。

------
#### [ Console ]

**マネージドポリシーをカスタムロールにアタッチするには**

1. [https://console.aws.amazon.com/iam/](https://console.aws.amazon.com/iam/) で IAM コンソールを開きます。

1. ナビゲーションパネルで [**Roles (ロール) **] を選択します。

1. スナップショットを管理するためのカスタムロールを検索し、選択します。

1. **[アクセス許可]** タブで、**[アクセス許可の追加]**、**[ポリシーをアタッチ]** の順に選択します。

1. **[AWSDataLifecycleManagerSSMFullAccess]** マネージドポリシーを検索して選択し、**[アクセス許可の追加]** を選択します。

------
#### [ AWS CLI ]

**マネージドポリシーをカスタムロールにアタッチするには**  
[attach-role-policy](https://docs.aws.amazon.com/cli/latest/reference/iam/attach-role-policy.html) コマンドを使用します。`---role-name` には、カスタムロールの名前を指定します。`--policy-arn` の場合、`arn:aws:iam::aws:policy/AWSDataLifecycleManagerSSMFullAccess` を指定します。

```
$ aws iam attach-role-policy \
--policy-arn arn:aws:iam::aws:policy/AWSDataLifecycleManagerSSMFullAccess \
--role-name {{your_role_name}}
```

------

### ステップ 4: スナップショットライフサイクルポリシーを作成する
<a name="prep-policy"></a>

アプリケーション整合性のあるスナップショットを自動化するには、インスタンスをターゲットとするスナップショットライフサイクルポリシーを作成し、そのポリシーの事前スクリプトと事後スクリプトを設定する必要があります。

------
#### [ Console ]

**スナップショットライフサイクルポリシーを作成するには**

1. Amazon EC2 コンソールの [https://console.aws.amazon.com/ec2/](https://console.aws.amazon.com/ec2/) を開いてください。

1. ナビゲーションペインで、[**Elastic Block Store**]、[**ライフサイクルマネージャー**]、[**ライフサイクルポリシーの作成**] の順に選択します。

1. リポジトリの [**ポリシータイプの選択**] 画面で、[**EBS スナップショットポリシー**] を選択し、[**次へ**] をクリックします。

1. **[Target resources]** (ターゲットリソース) セクションで、以下の操作を行います。

   1. **[ターゲットリソースタイプ]** で、[`Instance`] を選択します。

   1. **[ターゲットリソースタグ]** で、バックアップするインスタンスを識別するリソースタグを指定します。指定したタグを持つリソースのみがバックアップされます。

1. **[IAM ロール]** には、**AWSDataLifecycleManagerDefaultRole** (スナップショットを管理するためのデフォルトロール) を選択するか、事前スクリプトおよび事後スクリプト用に作成して準備したカスタムロールを選択します。

1. 必要に応じて、スケジュールと追加のオプションを設定します。メンテナンスウィンドウ中など、ワークロードに合った期間にスナップショット作成時刻をスケジュールすることをお勧めします。

   SAP HANA では、高速スナップショット復元を有効にすることをお勧めします。
**注記**  
VSS バックアップのスケジュールを有効にすると、**[特定のデータボリュームを除外]** または **[ソースからタグをコピー]** を有効にすることはできません。

1. **[事前スクリプトと事後スクリプト]** セクションで **[事前スクリプトと事後スクリプトを有効にする]** を選択し、ワークロードに応じて次の操作を行います。
   + Windows アプリケーションのアプリケーション整合性のあるスナップショットを作成するには、**[VSS バックアップ]** を選択します。
   + SAP HANA ワークロードのアプリケーション整合性のあるスナップショットを作成するには、**[SAP HANA]** を選択します。
   + カスタム SSM ドキュメントを使用して、セルフマネージドの MySQL、PostgreSQL、または InterSystems IRIS データベースを含む、他のすべてのデータベースとワークロードのアプリケーション整合性のあるスナップショットを作成するには、**[カスタム SSM ドキュメント]** を選択します。

     1. **[自動化オプション]** に、**[事前スクリプトと事後スクリプト]** を選択します。

     1. **[SSM ドキュメント]** に、準備した SSM ドキュメントを選択します。

1. 選択したオプションに応じて、以下の追加オプションを設定します。
   + **[スクリプトタイムアウト]** — (*カスタム SSM ドキュメントのみ*) Amazon Data Lifecycle Manager がスクリプトの実行試行を完了していない場合に、その試行が失敗するまでのタイムアウト期間。スクリプトがタイムアウト期間内に完了しない場合、Amazon Data Lifecycle Manager はその試行に失敗します。タイムアウト期間は、事前スクリプトと事後スクリプトに個別に適用されます。デフォルトのタイムアウト期間は 10 秒です。また、最大タイムアウト期間は 120 秒です。
   + **[失敗したスクリプトの再試行]** — タイムアウト期間内に完了しなかったスクリプトを再試行する場合は、このオプションを選択します。事前スクリプトが失敗した場合、Amazon Data Lifecycle Manager は、事前スクリプトと事後スクリプトの実行を含め、スナップショット作成プロセス全体を再試行します。事後スクリプトが失敗した場合、Amazon Data Lifecycle Manager は、事後スクリプトのみを再試行します。この場合、事前スクリプトは完了し、スナップショットが作成された可能性があります。
   + **Crash-consistent スナップショットをデフォルトで作成** – このオプションを選択すると、事前スクリプトの実行に失敗した場合に、Crash-consistent スナップショットがデフォルトで作成されます。これは、事前スクリプトと事後スクリプトが有効になっていない場合の、Amazon Data Lifecycle Manager のデフォルトのスナップショット作成動作です。再試行を有効にした場合、Amazon Data Lifecycle Manager は、再試行回数をすべて使い切った後にのみ、Crash-consistent スナップショットをデフォルトで作成します。事前スクリプトが失敗し、Crash-consistent スナップショットがデフォルトで作成されなかった場合、Amazon Data Lifecycle Manager は、そのスケジュールの実行中にインスタンスのスナップショットを作成しません。
**注記**  
SAP HANA のスナップショットを作成する場合は、このオプションを無効にすることをお勧めします。SAP HANA ワークロードのCrash-consistent スナップショットは、同じ方法では復元できません。

1. **[デフォルトポリシーの作成]** を選択します。
**注記**  
`Role with name AWSDataLifecycleManagerDefaultRole already exists` エラーが発生した場合、詳細については「[Amazon Data Lifecycle Manager の問題のトラブルシューティング](dlm-troubleshooting.md)」を参照してください。

------
#### [ AWS CLI ]

**スナップショットライフサイクルポリシーを作成するには**  
[create-lifecycle-policy](https://docs.aws.amazon.com/cli/latest/reference/dlm/create-lifecycle-policy.html) コマンドを使用して、`CreateRule` に `Scripts` パラメータを含めます。パラメータの詳細については、「[https://docs.aws.amazon.com/dlm/latest/APIReference/API_Script.html](https://docs.aws.amazon.com/dlm/latest/APIReference/API_Script.html)」を参照してください。

```
$ aws dlm create-lifecycle-policy \
--description "{{policy_description}}" \
--state ENABLED \
--execution-role-arn {{iam_role_arn}} \
--policy-details file:{{//policyDetails.json}}
```

ユースケースに応じて、ここで `policyDetails.json` には以下のいずれかが含まれます。
+ **VSS バックアップ**

  ```
  {
      "PolicyType": "EBS_SNAPSHOT_MANAGEMENT",
      "ResourceTypes": [
          "INSTANCE"
      ],
      "TargetTags": [{
          "Key": "{{tag_key}}",
          "Value": "{{tag_value}}"
      }],
      "Schedules": [{
          "Name": "{{schedule_name}}",
          "CreateRule": {
              "CronExpression": "{{cron_for_creation_frequency}}", 
              "Scripts": [{ 
                  "ExecutionHandler":"AWS_VSS_BACKUP",
                  "ExecuteOperationOnScriptFailure":{{true|false}},
                  "MaximumRetryCount":{{retries (0-3)}}
              }]
          },
          "RetainRule": {
              "Count": {{retention_count}}
          }
      }]
  }
  ```
+ **SAP HANA バックアップ**

  ```
  {
      "PolicyType": "EBS_SNAPSHOT_MANAGEMENT",
      "ResourceTypes": [
          "INSTANCE"
      ],
      "TargetTags": [{
          "Key": "{{tag_key}}",
          "Value": "{{tag_value}}"
      }],
      "Schedules": [{
          "Name": "{{schedule_name}}",
          "CreateRule": {
              "CronExpression": "{{cron_for_creation_frequency}}", 
              "Scripts": [{ 
                  "Stages": ["PRE","POST"],
                  "ExecutionHandlerService":"AWS_SYSTEMS_MANAGER",
                  "ExecutionHandler":"AWSSystemsManagerSAP-CreateDLMSnapshotForSAPHANA",
                  "ExecuteOperationOnScriptFailure":{{true|false}},
                  "ExecutionTimeout":{{timeout_in_seconds (10-120)}}, 
                  "MaximumRetryCount":{{retries (0-3)}}
              }]
          },
          "RetainRule": {
              "Count": {{retention_count}}
          }
      }]
  }
  ```
+ **カスタム SSM ドキュメント**

  ```
  {
      "PolicyType": "EBS_SNAPSHOT_MANAGEMENT",
      "ResourceTypes": [
          "INSTANCE"
      ],
      "TargetTags": [{
          "Key": "{{tag_key}}",
          "Value": "{{tag_value}}"
      }],
      "Schedules": [{
          "Name": "{{schedule_name}}",
          "CreateRule": {
              "CronExpression": "{{cron_for_creation_frequency}}", 
              "Scripts": [{ 
                  "Stages": ["PRE","POST"],
                  "ExecutionHandlerService":"AWS_SYSTEMS_MANAGER",
                  "ExecutionHandler":"{{ssm_document_name|arn}}",
                  "ExecuteOperationOnScriptFailure":{{true|false}},
                  "ExecutionTimeout":{{timeout_in_seconds (10-120)}}, 
                  "MaximumRetryCount":{{retries (0-3)}}
              }]
          },
          "RetainRule": {
              "Count": {{retention_count}}
          }
      }]
  }
  ```

------

## Amazon Data Lifecycle Manager での VSS バックアップに関する考慮事項
<a name="app-consistent-vss"></a>

Amazon Data Lifecycle Manager では、Amazon EC2 インスタンスで実行されている VSS (Volume Shadow Copy Service) 対応の Windows アプリケーションをバックアップおよび復元できます。アプリケーションに Windows VSS に登録された VSS ライターがある場合、Amazon Data Lifecycle Manager は、そのアプリケーションに対してアプリケーションと整合性のあるスナップショットを作成します。

**注記**  
Amazon Data Lifecycle Manager は現在、Amazon EC2 で実行されているリソースのアプリケーション整合性のあるスナップショットのみをサポートしています。特に、既存のインスタンスをバックアップから作成された新しいインスタンスに置き換えることでアプリケーションデータを復元できるバックアップシナリオを対象としています。VSS バックアップでは、すべてのインスタンスタイプまたはアプリケーションがサポートされているわけではありません。詳細については、「*Amazon EC2 ユーザーガイド*」の「[アプリケーションと整合性のある、Windows VSS ベースの Amazon EBS スナップショット](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/application-consistent-snapshots.html)」を参照してください。

**サポートされていない インスタンスタイプ**  
以下の Amazon EC2 インスタンスタイプは、VSS バックアップではサポートされていません。ポリシーがこれらのインスタンスタイプのいずれかをターゲットにしている場合、Amazon Data Lifecycle Manager は引き続き VSS バックアップを作成できますが、スナップショットには必要なシステムタグがタグ付けされていない可能性があります。これらのタグがないと、スナップショットは作成後に Amazon Data Lifecycle Manager によって管理されません。これらのスナップショットは手動で削除する必要がある場合があります。
+ T3: `t3.nano` \| `t3.micro`
+ T3a: `t3a.nano` \| `t3a.micro`
+ T2: `t2.nano` \| `t2.micro`

## アプリケーション整合性のあるスナップショットの責任共有
<a name="shared-responsibility"></a>

**次の点を確認する必要があります。**
+ SSM Agent がインストールされ、最新の状態で、ターゲットインスタンスで実行されている
+ Systems Manager には、ターゲットインスタンスで必要なアクションを実行する権限があります。
+ Amazon Data Lifecycle Manager には、ターゲットインスタンスでの事前スクリプトと事後スクリプトの実行に必要な Systems Manager アクションを実行する権限があります。
+ カスタムワークロード (MySQL や PostgreSQL、または InterSystems IRIS などのセルフマネージド型データベース) の場合、使用する SSM ドキュメントには、データベース設定の I/O をフリーズ、フラッシュ、および解凍するための適切で必要なアクションが含まれています。
+ スナップショット作成時刻はワークロードのスケジュールに合わせて調整されます。例えば、スケジュールされたメンテナンスウィンドウ中にスナップショットの作成をスケジュールしようとします。

**Amazon Data Lifecycle Manager では以下の動作が保証されます。**
+ スナップショットの作成は、スケジュールされたスナップショット作成時刻から 60 分以内に開始されます。
+ 事前スクリプトは、スナップショットの作成が開始される前に実行されます。
+ 事後スクリプトは、事前スクリプトが成功し、スナップショットの作成が開始された後に実行されます。Amazon Data Lifecycle Manager は、事前スクリプトが成功した場合にのみ事後スクリプトを実行します。事前スクリプトが失敗した場合、Amazon Data Lifecycle Manager は事後スクリプトを実行しません。
+ スナップショットは作成時に適切なタグでタグ付けされます。
+ CloudWatch メトリクスおよびイベントは、スクリプトが開始されたとき、およびスクリプトが失敗または成功したときに発生します。