カピバラ好きなエンジニアブログ

興味ある技術とか検証した内容を赴くままに書いていきます。カピバラの可愛さこそ至高。

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

タイトル通りです。
サーバーの作成は省略しますが、Systems Managerを使用するので、SSMエージェントがデフォルトインストールされているWindows Server 2019を使用しています。

docs.aws.amazon.com



やったこと

  • Windowsサーバーの作成
  • Systems Managerからバックアップ取得
  • LambdaからSystems Manager ドキュメントの実行



Windowsサーバーの作成とIAMロール設定

バックアップを取得するため、EC2を1台作成します。 f:id:live-your-life-dd18:20191009140114p:plain


セキュリティグループは最低限しか設定していません。 f:id:live-your-life-dd18:20191009140329p:plain


EC2が作成できたら、Systems Managerがインスタンスで実行できるようにIAMロールをアタッチします。
今回は検証のため、EC2FullAccess(スナップショット取得用)とSSMManagedInstanceCore(SSM実行用)のみ設定 f:id:live-your-life-dd18:20191009160957p:plain


※必要なポリシーについては以下参照 docs.aws.amazon.com


EC2を作成して数分経つとSystems Managerのマネージドインスタンスから、対象のインスタンスが確認できます。 f:id:live-your-life-dd18:20191009140716p:plain



Systems Managerからバックアップ取得

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


コマンドの実行ボタンを押下します。 f:id:live-your-life-dd18:20191009140931p:plain


今回はAWSが作成している「AWSEC2-CreateVssSnapshot」ドキュメントを使用します。 f:id:live-your-life-dd18:20191009141443p:plain


ターゲットの項目から、先ほどのサーバーを選択してドキュメントを実行します。 f:id:live-your-life-dd18:20191009141527p:plain


コマンド履歴から実行コマンドのステータスが成功になっていることを確認します。 f:id:live-your-life-dd18:20191009142235p:plain


スナップショットが無事に取得できました。 f:id:live-your-life-dd18:20191009142446p:plain



LambdaからSystems Manager ドキュメントの実行

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


まずはLambda実行用のIAMロールを作成します。 Lambdaの実行権限のみから開始して、エラーを解消しながら進めていきます。 f:id:live-your-life-dd18:20191009152439p:plain


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

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を実行すると、無事に正常終了しました。 f:id:live-your-life-dd18:20191009154051p:plain


スナップショットも作成されているため、特に問題はなさそうです。 f:id:live-your-life-dd18:20191009154254p:plain



結論やわかったことに対する補足

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