1
1

More than 1 year has passed since last update.

AWS LambdaでSSM RunCommandを実行する(AWS SDK for JavaScript v3)

Posted at

概要

LambdaからSSMのRunCommandを実行する処理の作成方法について記述します。言語はJavaScriptでSDKのv3(バージョン3)で書いてあります。

v3では、サービスのインスタンスを作成して、非同期でsendメソッドを呼び出すというのが全サービス共通の書き方になります。sendメソッドはPromise型を返します。何のアクションを行うかはsendメソッドの引数の型によって判断されます。

公式ドキュメント

サンプルプログラム

処理の流れ

SSMClient生成 → SendCommandCommand生成 → send実行 → ListCommandsCommand生成 → send実行 (RunCommandが終了するまで繰り返し) → send実行 → send実行 → ...

注意点としては、RunCommandを実行した後その処理がサーバーで正常終了したのか異常終了したのかすぐにわからない点です。RunCommandのsendメソッドの実行直後のthenではステータス1PendingIn Progressになることが多いと思います。その為、RunCommand実行後その実行したコマンドのrunCommandIdを退避し、ステータスがSuccessかそれ以外になるまで繰り返し実行結果を取得する処理2を入れています。

handler.js
'use strict';

const { SSMClient, SendCommandCommand } = require('@aws-sdk/client-ssm');

const startSsmRunCommandHandler = async (event) => {
    // SSMインスタンスを生成
    const client = new SSMClient({ region: process.env.AWS_REGION });
    
    // RunCommandのコマンドインスタンスを生成
    const commandRunCommand = new SendCommandCommand({
        DocumentName: 'AWS-RunShellScript',
        InstanceIds: ['i-01234567'],
        Parameters: {
            commands: ["touch test"],
            workingDirectory: ['/root']
        },
    });

    // 実行したRunCommandのIDを格納するための変数
    let runCommandId;
    // RunCommandを実行
    await client
        .send(commandRunCommand)
        .then((data) => {
            console.info(`SSM RunCommand Success: ${data}`);
            // RunCommandのIDを取得します。
            runCommandId = data.Command.CommandId;
        })
        .catch((error) => {
            console.error(`SSM RunCommand Failed: ${error.message}`);
            throw error;
        });

    // 実行したRunCommandのリスト取得のコマンドインスタンを生成
    const commandList = new ListCommandsCommand({
        CommandId: runCommandId
    });

    // ListCommandを実行する関数
    const runCommandStatusGetFunction = async () => {
        await client
            .send(commandList)
            .then((data) => {
                console.info(`RunCommand List: ${JSON.stringify(data)}`);
                const cmd = data.Commands[0];
                const status = cmd.Status;
                switch (status) {
                    case 'Pending':
                    case 'InProgress':
                    case 'Delayed':
                        // Pending, InProgress, Delayedの場合はエラーをスローして再実行する。
                        throw new Error('ListCommand Re-run.');
                    case 'Success':
                        console.info(`SSM RunCommand Finished.`);
                    default:
                        // 上記ステータス以外はlambdaを異常終了させる。
                        context.fail(new Error(`SSM RunCommand Failed.`));
                }
            });
    };

    // エラーが発生した場合再実行を行う関数
    const retry = (func, onError) => {
        const _retry = (e) => {
            return onError(e)
              .catch((e) => {
                throw e;
              })
              .then(func)
              .catch(_retry);
        };
        return func().catch(_retry);
    };

    // 実行結果が特定できるまで繰り返し処理する。
    await retry(runCommandStatusGetFunction, (e) => {
        console.error(e);
        return new Promise((resolve) => setTimeout(resolve, 2000));
    });
};

module.exports = {
    startSsmRunCommandHandler: startSsmRunCommandHandler
};
  1. 公式 コマンドのステータスについて

  2. JavaScript で非同期処理のリトライ

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1