LambdaからSystems Managerを実行してEC2のバックアップを取得する


## はじめに
EC2の定期バックアップを取得をするのに良い方法はないかと思っていたところ、SSMでスナップショットが取得できることを知ったので、Lambdaから実行してみました。
サーバーの作成は省略しますが、Systems Managerを使用するので、SSMエージェントがデフォルトインストールされているWindows Server 2019を使用しています。

参考
[Windows インスタンスで SSM エージェント をインストールし設定する](https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/sysman-install-ssm-win.html)

# やったこと
- Windowsサーバーの作成
- Systems Managerからバックアップ取得
- LambdaからSystems Manager ドキュメントの実行

# Windowsサーバーの作成とIAMロール設定
バックアップを取得するため、EC2を1台作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/3ff3660a-e539-4e37-06c4-fb4f76992d6e.png)

セキュリティグループは最低限しか設定していません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/6cefe063-8e37-19ec-81cc-433212e216b5.png)

EC2が作成できたら、Systems Managerがインスタンスで実行できるようにIAMロールをアタッチします。
今回は検証のため、EC2FullAccess(スナップショット取得用)とSSMManagedInstanceCore(SSM実行用)のみ設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/beaa4757-6aae-0776-f7b7-38816097ba34.png)


※必要なポリシーについては以下参照
[IAM インスタンスプロファイルを Amazon EC2 インスタンスにアタッチする](https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/setup-launch-managed-instance.html)

EC2を作成して数分経つとSystems Managerのマネージドインスタンスから、対象のインスタンスが確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/82edf14c-cb37-4cc2-7ad3-b0f5446e419f.png)

## Systems Managerからバックアップ取得
さっそくLambdaでバックアップを取得...といきたいところですが、まずはSystems Managerからバックアップが取得できることを確認します。


コマンドの実行ボタンを押下します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/9f40aaa5-e609-e3e8-afa4-c7b5aad22854.png)

今回はAWSが作成している「AWSEC2-CreateVssSnapshot」ドキュメントを使用します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/1ee6e1a8-4da2-f294-d0be-0433200297af.png)

ターゲットの項目から、先ほどのサーバーを選択してドキュメントを実行します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/1716ac24-50f9-9d19-2b04-14563febf5c7.png)

コマンド履歴から実行コマンドのステータスが成功になっていることを確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/c6f0a7f4-02a6-4a64-495d-1b039500f885.png)

スナップショットが無事に取得できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/32cd56a3-ebf0-edb7-cc01-716e55aee0a6.png)

# LambdaからSystems Manager ドキュメントの実行
Systems Managerからバックアップが取得できたところで、今度はLambdaからSystems Managerを実行してみます。

まずはLambda実行用のIAMロールを作成します。
Lambdaの実行権限のみから開始して、エラーを解消しながら進めていきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/28067511-612e-196b-2f1e-363676229c10.png)

ちなみにLambdaのコードはこんな感じ(Pythonで書いています)

```lambda_function.py
import json
import traceback
import boto3
import logging
from datetime import datetime

logger = logging.getLogger()
logger.setLevel(logging.INFO)

instance_id = 'i-0bec94e365078de8c'
document_name = 'AWSEC2-CreateVssSnapshot'
exec_date = datetime.now().strftime("%Y/%m/%d_%H:%M:%S")
snapshot_name = 'testserver-snapshot-' + exec_date

def lambda_execute_ssm(event):
    
    ssm = boto3.client('ssm')
    
    logger.info("Start Get Snapshot")

    ssm_result = ssm.send_command(
        InstanceIds = [ instance_id ],
        DocumentName = document_name,
        Parameters = {
            'ExcludeBootVolume': [
                'False',
            ],
            'description': [
                'Ssm Execute Document',
            ],
            'tags': [
                'Key=Name,Value={}'.format(snapshot_name),
            ]
        }
    )
    logger.info("exec lambda result -> {}".format(ssm_result))    
    logger.info("Completed!")

def lambda_handler(event, context):
    try:
        lambda_execute_ssm(event)
    except Exception:
        logger.error('Exception -> {}'.format(traceback.format_exc()))
```


この状態でLambdaを実行してみると以下のエラーが発生

```
botocore.exceptions.ClientError: An error occurred (AccessDeniedException) when calling the SendCommand operation: User: arn:aws:sts::XXXXXXXXXXXX:assumed-role/lambda-ssm-handler/ssm-execution is not authorized to perform: ssm:SendCommand on resource: arn:aws:ec2:ap-northeast-1:XXXXXXXXXXXX:instance/i-0bec94e365078de8c
```

エラー内容を読む限りだと、SSMのSendCommandを実行しようとしているが、Lambda側にその権限がないのが原因っぽい？

LambdaのロールにSSMのSendCommand権限が許可されるポリシー(AmazonSSMMaintenanceWindowRole )をアタッチして再度Lambdaを実行すると、無事に正常終了しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/376093ca-d6d0-e4c5-3e94-49fe013b376e.png)

スナップショットも作成されているため、特に問題はなさそうです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/af857b41-65a1-2d70-8049-c2ad2b4b3202.png)

# 結論やわかったことに対する補足
Lambdaからバックアップは取得できたので、CloudEvent等でスケジュール実行させたら定期取得はできそうです。

このやり方だと削除用のLambdaも作成する必要があるので、その点だとライフサイクルマネージャーのほうが良いのかもしれませんが、ライフサイクルマネージャーは取得にタイムラグがあったり、CloudWatchEventのcronでする柔軟なスケジュール設定ができないので、それが必要ならLambda実行にしたほうがいいですね。

本当はSSMのコマンド失敗時にSNS通知させるところまでを作ろうと思いましたが、SNS通知のためのロールを渡すところでエラーが多発したため、一旦スナップショットを取得するところまでにしました。
IAMロール回りが中々理解できない。。