はじめに
AWS CLI 使っていますか?
私も折りに触れ使っています。複数のアカウントを横断して一度にリソースの状況を知りたいときに利用したり、Web コンソールではわかりづらいリソースの包含関係がわかったりもして AWS のリソースの理解の一助になるのもいいんですよね。
ただ、AWS CLI の取得時や結果に対するフィルタリングの --filters
や --query
オプションや JSON で出力した場合に jq
でゴニョゴニョするのもぜんぜん慣れません 。やりだすとパズル解くみたいで楽しいですし、オプションの記載も宣言的で好感をもっていますが。
なので、慣れたプログラミング言語で IDE の恩恵を受けながら AWS の API を気軽に叩けるツールを書けたらいいなと思い、AWS SDK for JavaScript を利用した Node.js ツールを VS Code で書く、みたいなことをしていました。
しかし、その際に必要になるたびに毎回1から「npm init
して〜」と小さなツールを量産するのは管理が面倒だしそのたびに GitHub リポジトリに上げるのも大仰だなと思っていました。
oclif
そこで知ったのが oclif です。
The Open CLI Framework Create command line tools your users love
oclif: The Open CLI Framework · Create command line tools your users love
とのことで、Node.js のコマンドラインツールを便利に作れるフレームワークのようです。
オプションやヘルプの記載もフォローしてくれていて、開発者はロジックの記載に専念できそうです。
また「必要になるたびに」コマンドを増やしていくだけでリポジトリも増やす必要ないし一元管理できてこれも楽ちんです。試してみます。
なお、以下の環境で試しました。
$ node -v
v18.13.0
0. 前提
IAM ロールの名前一覧を取得する CLI アプリを作りたいと思います。その後、オプションを付与することで ARN も一覧に加えて表示できるようにしてみます。
参考までに、AWS CLI で取得できるバルクデータはこんな感じです。
$ aws iam list-roles --profile default
{
"Roles": [
{
"Path": "/aws-service-role/organizations.amazonaws.com/",
"RoleName": "AWSServiceRoleForOrganizations",
"RoleId": "************",
"Arn": "arn:aws:iam::**********:role/aws-service-role/organizations.amazonaws.com/AWSServiceRoleForOrganizations",
"CreateDate": "2019-07-26T01:51:57+00:00",
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "[organizations.amazonaws.com](http://organizations.amazonaws.com)"
},
"Action": "sts:AssumeRole"
}
]
},
"Description": "Service-linked role used by AWS Organizations to enable integration of other AWS services with Organizations.",
"MaxSessionDuration": 3600
},
{
"Path": "/aws-service-role/support.amazonaws.com/",
"RoleName": "AWSServiceRoleForSupport",
"RoleId": "************",
"Arn": "arn:aws:iam::**********:role/aws-service-role/support.amazonaws.com/AWSServiceRoleForSupport",
"CreateDate": "2019-07-26T01:47:23+00:00",
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "[support.amazonaws.com](http://support.amazonaws.com)"
},
"Action": "sts:AssumeRole"
}
]
},
"Description": "Enables resource access for AWS to provide billing, administrative and support services",
"MaxSessionDuration": 3600
},
{
"Path": "/aws-service-role/trustedadvisor.amazonaws.com/",
"RoleName": "AWSServiceRoleForTrustedAdvisor",
"RoleId": "************",
"Arn": "arn:aws:iam::**********:role/aws-service-role/trustedadvisor.amazonaws.com/AWSServiceRoleForTrustedAdvisor",
"CreateDate": "2019-07-26T01:47:23+00:00",
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "[trustedadvisor.amazonaws.com](http://trustedadvisor.amazonaws.com)"
},
"Action": "sts:AssumeRole"
}
]
},
"Description": "Access for the AWS Trusted Advisor Service to help reduce cost, increase performance, and improve security of your AWS environment.",
"MaxSessionDuration": 3600
}
]
}
1. CLI アプリの新規作成
$ npx oclif generate aws-tools
_-----_
| | ╭──────────────────────────╮
|--(o)--| │ Time to build an oclif │
`---------´ │ CLI! Version: 3.7.3 │
( _´U`_ ) ╰──────────────────────────╯
/___A___\ /
| ~ |
__'.___.'__
´ ` |° ´ Y `
Cloning into '/path/to/aws-tools'...
? npm package name aws-tools
? command bin name the CLI will export qcaws
? description AWS Cli Tools for Quartetcom
? author Naoyasu @naoyes
? license MIT
? Who is the GitHub owner of repository (https://github.com/OWNER/repo) quartetcom
? What is the GitHub name of repository (https://github.com/owner/REPO) aws-tools
? Select a package manager npm
force aws-tools/package.json
force aws-tools/.gitignore
force aws-tools/LICENSE
:
Yeoman おじさんの質問に答えていくと初期状態を作成してくれます。
2. サンプルツールを動かしてみる
$ cd ./aws-tools
$ ./bin/run
AWS Cli Tools for Quartetcom
VERSION
aws-tools/0.0.0 darwin-arm64 node-v18.13.0
USAGE
$ qcaws [COMMAND]
TOPICS
hello Say hello to the world and others
plugins List installed plugins.
COMMANDS
hello Say hello
help Display help for qcaws.
plugins List installed plugins.
初期状態では hello
, help
, plugins
の3つのコマンドがあるようです。
$ ./bin/run hello --help
Say hello
USAGE
$ qcaws hello PERSON -f <value>
ARGUMENTS
PERSON Person to say hello to
FLAGS
-f, --from=<value> (required) Who is saying hello
DESCRIPTION
Say hello
EXAMPLES
$ oex hello friend --from oclif
hello friend from oclif! (./src/commands/hello/index.ts)
COMMANDS
hello world Say hello world
$ ./bin/run hello everyone --from naoyes
hello everyone from naoyes! (./src/commands/hello/index.ts)
$ ./bin/run hello world
hello world! (./src/commands/hello/world.ts)
hello
コマンドのヘルプを見て実際に使ってみました。
3. IAM ロール一覧コマンドを実装する。
3-1. 雛形作成
$ npx oclif generate command list-iam-roles
_-----_
| | ╭──────────────────────────╮
|--(o)--| │ Adding a command to │
`---------´ │ aws-tools Version: 3.8.1 │
( _´U`_ ) ╰──────────────────────────╯
/___A___\ /
| ~ |
__'.___.'__
´ ` |° ´ Y `
create src/commands/list-iam-roles.ts
create test/commands/list-iam-roles.test.ts
No change to package.json was detected. No package manager install will be executed.
上記のようにコマンド用とテスト用の TS ファイルが追加されました。
src/commands/list-iam-roles.ts
の内容は以下です。
import {Args, Command, Flags} from '@oclif/core'
export default class ListIamRoles extends Command {
static description = 'describe the command here'
static examples = [
'<%= config.bin %> <%= command.id %>',
]
static flags = {
// flag with a value (-n, --name=VALUE)
name: Flags.string({char: 'n', description: 'name to print'}),
// flag with no value (-f, --force)
force: Flags.boolean({char: 'f'}),
}
static args = {
file: Args.string({description: 'file to read'}),
}
public async run(): Promise<void> {
const {args, flags} = await this.parse(ListIamRoles)
const name = flags.name ?? 'world'
this.log(`hello ${name} from /Users/naoyasu.manabe/ghq/github.com/quartetcom/aws-tools/src/commands/list-iam-roles.ts`)
if (args.file && flags.force) {
this.log(`you input --force and --file: ${args.file}`)
}
}
}
3-2. 実装
上記を参考に IAM ロールの Name
が一覧されるよう実装しました。
結果、src/commands/list-iam-roles.ts
の内容は以下です。
import {Args, Command, Flags} from '@oclif/core'
import { ListRolesCommand, IAMClient } from '@aws-sdk/client-iam'
export default class ListIamRoles extends Command {
static description = 'IAM Role のリストアップ'
static examples = [
'<%= config.bin %> <%= command.id %>',
]
static flags = {
}
static args = {
}
public async run(): Promise<void> {
const {args, flags} = await this.parse(ListIamRoles)
const client = new IAMClient({})
const command = new ListRolesCommand({});
try {
const data = await client.send(command);
// process data.
data.Roles?.map(_ => this.log(_.RoleName))
} catch (error) {
// error handling.
this.logJson(error)
} finally {
// finally.
}
}
}
3-3. 実行
さっそく追加したコマンドが実装できているか見てみます。
$ ./bin/run help
AWS Cli Tools for Quartetcom
VERSION
aws-tools/0.0.0 darwin-arm64 node-v18.13.0
USAGE
$ qcaws [COMMAND]
TOPICS
hello Say hello to the world and others
plugins List installed plugins.
COMMANDS
hello Say hello
help Display help for qcaws.
plugins List installed plugins.
あれ?COMMANDS
に追加したはずの list-iam-roles
コマンドが見当たりませんね。
$ ./bin/dev help
AWS Cli Tools for Quartetcom
VERSION
aws-tools/0.0.0 darwin-arm64 node-v18.13.0
USAGE
$ qcaws [COMMAND]
TOPICS
hello Say hello to the world and others
plugins List installed plugins.
COMMANDS
hello Say hello
help Display help for qcaws.
list-iam-roles IAM Role のリストアップ
plugins List installed plugins.
実行コマンドを ./bin/dev
にしたら list-iam-roles
が出てきました。src/commands/list-iam-roles.ts
に記載した description
も表示されています。
./bin/dev
はビルド前のソースコードを ts-node で実行しているようです。
$ ./bin/dev list-iam-roles
Name: AWSServiceRoleForOrganizations
Name: AWSServiceRoleForSupport
Name: AWSServiceRoleForTrustedAdvisor
無事に IAM ロールの Name
一覧が取得できました。
3-4. 改造
次はフラグオプション -a
あるいは --arn
が付与された場合には ARN
も加えて表示するようにしてみます。
src/commands/list-iam-roles.ts` の内容の差分は以下です。
static flags = {
+ arn: Flags.boolean({char: 'a', description: 'ARN も表示したい場合は指定してください'}),
}
static args = {
@@ -22,7 +23,11 @@ export default class ListIamRoles extends Command {
try {
const data = await client.send(command);
// process data.
- data.Roles?.map(_ => this.log(_.RoleName))
+ data.Roles?.map(_ => {
+ const name = `Name: ${_.RoleName}`
+ const arn = flags.arn ? `Arn: ${_.Arn}` : ''
+ this.log([name, arn].join(' / '))
+ })
$ ./bin/dev list-iam-roles --help
IAM Role のリストアップ
USAGE
$ qcaws list-iam-roles [-a]
FLAGS
-a, --arn ARN も表示したい場合は指定してください
DESCRIPTION
IAM Role のリストアップ
EXAMPLES
$ qcaws list-iam-roles
追加したフラグオプションが FLAGS
に記載され
$ ./bin/dev list-iam-roles -a
Name: AWSServiceRoleForOrganizations / Arn: arn:aws:iam::**********:role/aws-service-role/organizations.amazonaws.com/AWSServiceRoleForOrganizations
Name: AWSServiceRoleForSupport / Arn: arn:aws:iam::**********:role/aws-service-role/support.amazonaws.com/AWSServiceRoleForSupport
Name: AWSServiceRoleForTrustedAdvisor / Arn: arn:aws:iam::**********:role/aws-service-role/trustedadvisor.amazonaws.com/AWSServiceRoleForTrustedAdvisor
実行したら所望の出力結果が得られました。
さいごに
結構細かい作業のログまで記載はしましたが内容としてはそれほどのボリュームではないと思います。oclif こんな感じなんだーと思っていただけたら幸いです。
oclif 自体はもっと多機能ですがそれほど複雑だということでもないのでよかったら公式ドキュメントをご覧いただいて遊んでみてください。
AWS から取得したデータを openai - npm を使って ChatGPT API に食わせてみる、なんていうのも手軽にできるかなーと思います。