inquirer.jsを使用してCLI上でAssumeRoleできるカスタムコマンドを作成してみた

inquirer.jsを使用してCLI上でAssumeRoleできるカスタムコマンドを作成してみた

Clock Icon2024.10.21

リテールアプリ共創部のるおんです。
AWSの複数アカウントを扱う開発環境では、AssumeRoleを使用して異なるアカウントやロールに切り替える必要がしばしばあります。
今までは以下のブログに書いたように、CLI上でのAssumeRole操作をより簡単にするために自前でShellScriptを書いて自動化していました。

https://dev.classmethod.jp/articles/aws-cli-assume-role/

今回は、この実装をShellScriptではなく、inquirer.js というライブラリを使用してNode.jsで実装することで、より直感的で綺麗なインターフェースを実現してみました。
AWSumeでいいと言われればそれまでですが、普段は上の自前CLIコマンドを使っていたのでそれを改善するという目的で実装してみたいと思います。

inquirer.jsとは

inquirer.jsは、Node.jsのコマンドラインインターフェース(CLI)アプリケーションを作成するためのライブラリです。このライブラリを使用することで、開発者は簡単に対話的なコマンドラインプロンプトを作成できます。
よく、コマンドライン上で選択形式のインターフェースに遭遇することがありますが、inquirer.jsを用いて作られていることがあります。

https://github.com/SBoudrias/Inquirer.js

実装

1. セットアップ

zsh
mkdir inquirerjs-assumerole
cd inquirerjs-assumerole
npm init

inquirer.jsをインストールします。
https://www.npmjs.com/package/@inquirer/prompts

zhs
npm i @inquirer/prompts

2. スクリプトの作成

次に、index.jsというファイルを作成し、以下のコードを記述します。

index.js
const { select, input } = require('@inquirer/prompts');
const { exec } = require('child_process');
const fs = require('fs');
const util = require('util');

const execPromise = util.promisify(exec);

// profileの一覧の名前を取得(例:[profile dev] => dev)
async function getProfiles() {
  const configFile = `${process.env.HOME}/.aws/config`;
  const content = fs.readFileSync(configFile, 'utf8');
  return content.match(/\[profile\s(.+)\]/g).map(line => line.match(/\[profile\s(.+)\]/)[1]);
}

async function assumeRole() {
  try {
    const profiles = await getProfiles();

    // プロファイルを選択する
    const profile = await select({
      message: 'Which profile do you want to use?',
      choices: profiles.map(p => ({ value: p, name: p }))
    });

    // MFAコードを入力する
    const mfaCode = await input({
      message: 'Enter your MFA code:'
    });

    const { stdout: roleArn } = await execPromise(`aws configure get role_arn --profile ${profile}`);
    const { stdout: serialNumber } = await execPromise(`aws configure get mfa_serial --profile ${profile}`);
    const { stdout: sourceProfile } = await execPromise(`aws configure get source_profile --profile ${profile}`);
    const { stdout: region } = await execPromise(`aws configure get region --profile ${profile}`);

    const date = Math.floor(Date.now() / 1000);

    const { stdout: output } = await execPromise(`aws sts assume-role \
      --role-arn ${roleArn.trim()} \
      --serial-number ${serialNumber.trim()} \
      --role-session-name ${date}-session \
      --profile ${sourceProfile.trim()} \
      --duration-seconds 3600 \
      --token-code ${mfaCode}`);

    const credentials = JSON.parse(output).Credentials;

    const envScript = `
    export AWS_ACCESS_KEY_ID=${credentials.AccessKeyId}
    export AWS_SECRET_ACCESS_KEY=${credentials.SecretAccessKey}
    export AWS_SESSION_TOKEN=${credentials.SessionToken}
    export AWS_DEFAULT_REGION=${region.trim() || 'ap-northeast-1'}
    `;

    fs.writeFileSync('aws-env.sh', envScript);

    console.log(`${profile} profile is set.`);
    console.log('You can now use AWS CLI with the assumed role.')

  } catch (error) {
    console.error('An error occurred:', error);
  }
}

assumeRole();

このスクリプトは以下の機能を持っています:

  • AWS設定ファイルからプロファイル一覧を取得
  • ユーザーにプロファイルを選択させる
  • MFAコードの入力を求める
  • 選択されたプロファイルの情報を使ってAssumeRoleを実行。

実装していて気づいたんですが、Node.jsから直接シェルの環境変数を設定することは難しいため、AssumeRoleの際に受け取った情報を一時的なファイルに出力し、シェルスクリプトからその値を参照して環境変数としてセットするように以下の関数を別で作成しました。

3. シェル関数の作成

次に、.zshrc(または使用しているシェルの設定ファイル)に以下の関数を追加します。

# aws assume role(aar)するための関数
function aar() {
  node /Users/username/inquirerjs-assumerole/index.js
  if [ -f aws-env.sh ]; then
    source aws-env.sh
    rm aws-env.sh
  fi
}

このaar関数を使用してinquirer.jsで実装したスクリプトを呼び出し、環境変数に値をセットするということです。

使用方法

設定が完了したら、ターミナルでaarコマンドを実行するだけでAssumeRoleのプロセスが開始されます。プロファイルを選択し、MFAコードを入力すれば、自動的に認証情報が設定されます。
よくセットアップ時などにみる綺麗なインターフェースになりました。
スクリーンショット 2024-10-21 19.18.15

スクリーンショット 2024-10-21 19.18.45

おわりに

以上、inquirer.jsを使用してCLI上でAssumeRoleできるカスタムコマンドの作成方法をご紹介しました。
どなたかの参考になれば幸いです。

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.