lambdaからEC2ディレクトリに配置したCSVファイルにアクセスし、CSVファイル内の値を取得してlambdaに返す方法[ubuntu(Linux)]

0

やりたい事としてはタイトルの通りになります。

現状、やりたいことを実現するために以下の方法を実施していますが、テスト時にErrorとなってしまっています。 1.lambdaからEC2(ubuntu)ディレクトリに配置したシェルスクリプトファイルを実行 2.起動したシェルスクリプトファイルで同ディレクトリに配置されたCSVファイルを読み取り値を出力(echo)

以下のlambdaコードで1.を実装していますが、テスト時に下記エラーが発生しています。 改善方法があれば教えてください。 あるいは、他に取るべき手法があれば教えてください。 ※可能であればサンプルコードもあると非常にありがたいです。

<エラーメッセージ>

"errorType": "Runtime.ImportModuleError", "errorMessage": "Error: Cannot find module '@aws-sdk/client-ssm'\nRequire stack:\n- /var/task/index.js\n- /var/runtime/index.mjs", "trace": [ "Runtime.ImportModuleError: Error: Cannot find module '@aws-sdk/client-ssm'",

<コード>

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

const startSsmRunCommandHandler = async (event) => {
    // SSMインスタンスを生成
    const client = new SSMClient({ region: '<リージョン名を指定>' });
    
    // RunCommandのコマンドインスタンスを生成
    const commandRunCommand = new SendCommandCommand({
        DocumentName: 'AWS-RunShellScript',
        InstanceIds: ['対象のインスタンスID'],
        Parameters: {
            commands: ["sh <起動するシェルスクリプトファイル名>"],
            workingDirectory: ['/home/ubuntu/<ディレクトリ名>']
        },
    });

    // 実行した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
};
質問済み 2ヶ月前149ビュー
1回答
0
承認された回答

こんな感じでサンプルコード作ってみました。
おそらく新しいバージョンのNode.jsを使用していて「require」が使えないのではないかと思います。

SendCommandCommandとGetCommandInvocationCommandの使い方は以下のドキュメントに記載されています。
https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/ssm/command/SendCommandCommand/
https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/ssm/command/GetCommandInvocationCommand/

GetCommandInvocationCommandを実行するときに5秒間待機する処理にしましたが、この書き方ではなくレスポンスに含まれるStatusが「Success」以外の時にループさせるような処理でもよいと思います。
https://docs.aws.amazon.com/systems-manager/latest/userguide/monitor-commands.html

import { SSMClient, SendCommandCommand, GetCommandInvocationCommand } from "@aws-sdk/client-ssm";

export const handler = async (event) => {
  // TODO implement
  const client = new SSMClient({region: 'ap-northeast-1'});
  const input = {
      DocumentName: 'AWS-RunShellScript',
      InstanceIds: ['EC2 ID],
      Parameters: {
          commands: ["sh test.sh"],
          workingDirectory: ['/home/ec2-user/']
      }
  }
  const command = new SendCommandCommand(input);
  const response = await client.send(command);
  const commandId = response.Command.CommandId;
  const getCommandInput = {
    CommandId: commandId,
    InstanceId: 'EC2 ID'
  }
  await new Promise(resolve => setTimeout(resolve, 5000));  
  const getCommand = new GetCommandInvocationCommand(getCommandInput);
  const getResponse = await client.send(getCommand);

  console.log(getResponse.StandardOutputContent);
};

profile picture
エキスパート
回答済み 2ヶ月前
  • シェルスクリプトはこんな感じです。

    #!/bin/bash
    
    cat test.csv
    

    CSVのサンプルは以下になります。

    test,kobayashi,riku
    

    Lambdaを実行すると以下のようにCloudWatch Logsに記録されます。 a

  • Riku_Kobayashi様 早々にご回答いただきありがとうございます。

    丁寧なご回答およびサンプルコードのおかげもあり、無事やりたい事が実現できました。 時間待機ではなくステータスに応じて、ループし ・待機 ・GetCommandInvocationCommandを実行 ・異常終了 を使い分ける書き方も調査して試してみます。

    早々かつ的確なご回答ありがとうございました。 承認させていただきます。

ログインしていません。 ログイン 回答を投稿する。

優れた回答とは、質問に明確に答え、建設的なフィードバックを提供し、質問者の専門分野におけるスキルの向上を促すものです。

質問に答えるためのガイドライン