AWS SDK も楽しいです


こんにちは、内製開発グループの山下です。

AWS のリソース一覧を得る

以前に AWS CLI を用いた AWS リソース一覧の取得方法について投稿をしました。もう1年半になるのですね。

blog.tech-monex.com

この投稿は「AWS CLI コマンドを用いて JSON 形式のリソース情報を得て、jq コマンドで整形して CSV データ形式の出力を得る」話でした。

昨日の自分は他人

さて、久しぶりに AWS リソース一覧を得ようとし、ツールをちょっと修正しようとして、困惑しました。以前に作成したスクリプト、特に jq でオブジェクトを操作しているあたり、難しくてよくわからない。。

記憶とは薄れていくものです。老いるとそのスピードは加速していきます。。

開発中は簡単なものから、だんだん複雑なものに育てていきます。その過程をわかったうえで開発作業を継続しているので、全体が見渡せ、なかなかのスピードでコードを書いていけます。でも時間が経つと、その過程の肌感覚は失われてしまう。

シェルスクリプト、そして jq でガリガリ整形処理するコードは短くて楽しいけれど、保守性が低くてメンテが難しいよね、もう細かいとこ忘れちゃったよ、辛いよ、ということです。。

コードは最高のドキュメントかもしれない

シェルスクリプト処理を、まともな開発言語を用いて書き直すのは定番の対応です。なので今回は、NodeJS 環境の JavaScript (TypeScript) 言語を用いて、AWS SDK を用いてリソース一覧を得るツールとして書き直すことにしました。

JavaScript (TypeScript)  は JSON 形式のデータを処理するのに適した言語で、書き方にもよりますが、可読性もなかなか良いです。また使える開発者も多く、今後のメンテを誰かにお任せするのも楽そうです。

というわけで、AWS SDK を用いて作り直したリソース一覧を取得するコードを書いてみているので、その一部を簡単にご紹介します。

まずは公式ドキュメント

AWS リソースって数多くの種類があり、AWS CLI コマンドのオプションも数多くありましたよね。なので SDK の API もやはり数多くあります。公式ドキュメント AWS SDK for JavaScript のドキュメント を頼っていきましょう。

バージョン 2 もまだ多く使われているようですが、私は新しもの好きなので AWS SDK for JavaScript、バージョン 3 のほうを使います。残念ながら英語ですがAPI リファレンスを主に参照しています。

docs.aws.amazon.com

 

今回は EC2 Client を使って、リソース情報を得てみましょう。NodeJS なので、まずは最初にあるモジュールの導入からですね。

EC2 Client を初期化する

API リファレンスにはサンプルコードも豊富にあります。まずはサービス利用の起点となる EC2 Client を初期化してみましょう。

import { EC2Client } from "@aws-sdk/client-ec2";

const region = 'ap-northeast-1';
const client = new EC2Client({ region });

なおこのコードを実行する環境は、既に AWS CLI が動作する、つまり AWS 認証情報がセットされていることを前提としています。

STS Client を使う

これだけだと簡単なのですが、実は私の環境はスイッチロールが必要で、しかも MFA 認証までセットされています。この簡単なコードでは動きません。

仕方ないので STS Client を使って、一時的な認証情報 (アクセスキーIDやシークレットアクセスキー)を得て、それを使ってサービス初期化をおこないます。

import { STSClient, AssumeRoleCommand, AssumeRoleCommandInput } from "@aws-sdk/client-sts";
const stsClient = new STSClient({ region });
const params: AssumeRoleCommandInput = {
    RoleArn: roleArn, // スイッチ先ロールのARN
    SerialNumber: mfaSerial, // 利用するMFAのARN
    TokenCode: <string>mfaToken, // MFA の入力キー
    RoleSessionName: new Date().getTime().toString(),
    DurationSeconds: 900,
};
const { Credentials } = await stsClient.send(new AssumeRoleCommand(params));

こんな感じで得られた Credentials を EC2 Client 初期化で利用します。

const client = new EC2Client({
    region,
    credentials: {
        accessKeyId: <string>Credentials?.AccessKeyId,
        secretAccessKey: <string>Credentials?.SecretAccessKey,
        sessionToken: Credentials?.SessionToken,
    }
});

ちょっと面倒に感じますが、STS を利用するのは起動時の一度だけで、後は得られた認証情報を使いまわします。

MFA の入力キー mfaToken は毎回異なるので、プログラム起動時に引数として受け取るようにしています。

EC2 インスタンス情報を得る

さて EC2 リソース情報を得ていきましょう。各操作はコマンドオブジェクトになっていて (Commandパターンですね!)、必要なコマンドを new して EC2 Client 経由で送信します。非同期なのでサンプルでは await して結果を待っています。

import { DescribeInstancesCommand } from "@aws-sdk/client-ec2";

const response = await client.send(
  new DescribeInstancesCommand({})
);

なお、今回の説明では、全インスタンスを得るために特に属性は指定しません。

さて、得られた response には Reservations 属性があり、そこに Instances の配列があります。この Instances が各 EC2 インスタンスの情報の配列になっています。

例えば以下のように EC2 インスタンス情報を参照します。プログラムなので二重の配列なんかの処理も簡単に対応できます。

if (response && response.Reservations) {
  for (const reservations of response.Reservations) {
      if (reservations.Instances) {
          for (const instance of reservations.Instances) {
                  // ここに各インスタンスの出力処理
          }      
      }
  }
}

CSV 化して出力する

実際に利用する部分ですが、CSV 化して出力する部分は、どうとでも書けるとおもいます。参考にはあまり適していませんが、配列にいれたデータを標準出力に出す例をいちおう紹介しますね。

以下みたいなベタに配列を作成する感じ。各属性の種類に応じて、文字列化の処理を工夫したりも簡単です。もっと多くの属性値が取れますが、以下のサンプルでは一部だけにしています。

const header: string[] = [
    'ImageId',
    'InstanceType',
];
const data = [header];

if (response && response.Reservations) {
    for (const reservations of response.Reservations) {
        if (reservations.Instances) {
            for (const instance of reservations.Instances) {
                data.push([
                    instance.ImageId,
                    instance.InstanceType,
                ]);
            }
        }
    }
}

for (const line of data) {
    const cols = line.map(col => (col || '').toString().replace(/"/g, '""'));
    console.log('"' + cols.join('","') + '"');
}

この先は

コマンドが多くて最初は戸惑うのですが、AWS CLI で指定するコマンドとほぼ類似であり、コマンド名も似ているので、AWS CLI を使った経験があればすぐに馴染めるとおもいます。

API リファレンスを見ながらいろいろ試し、結果として得られた JSON 化された情報をいろいろ操作して、学んでいきましょう。

私の作成しているツールも対象となる AWS リソースの拡大をしつつ、コマンド送信時のリトライ処理を追加したり、CSV に出力しないで NoSQL DB である nedb に一旦インサートしたり、など拡張を続けています。

最後に

というわけで、今回はコード多めな感じになりました。

私も書き始めで、まだ無駄がありそうなコードですが、まあ、こんな感じで使えるんだな、と生暖かい目で見ていただければとおもいます。

読んでいただきありがとうございました。マネックスの採用にご興味のある方は、ぜひ以下募集をご覧ください。

www.monexgroup.jp