phineのブログ

電気じかけの予言者

The best way to predict the future is to invent it.

AthenaやS3 SelectでIPアドレスの比較を行う方法

Amazon AthenaS3 Select を使ってログなどを解析する場合に、特定の IP アドレスの範囲の情報だけを抽出する方法について解説します。

Athena と S3 Select

おそらくこの記事を読んでおられる方は Athena や S3 Select をご存知でしょうが、簡単にこれらのサービスについてご紹介しておきます。

Athena も S3 Select も、S3 バケットに保存されているファイルに対し、SQL ライクなクエリを実行することができるサービスです。例えば、ELB や CloudFront のログや、VPC Flow Logs などの分析を行うことができます。AWS のサービスに関連するログに限らず、CSV*1 のように表形式(リレーショナルデーターベース)に変換できるファイルであれば、Athena や S3 Select でクエリを実行することができます。Athena では複数のファイル(オブジェクト)にまたがってクエリを実行できますが、S3 Select はファイルに対してクエリを実行します。

データベースの作成方法など、基本的な使い方についてはここには記載しませんので、必要に応じて AWS のサイトなどをご参照ください。

単純な文字列比較ではなぜダメなのか

Athena は Presto という、Facebook が開発した分析基盤を使って構築されていますが、Presto には MySQL の inet_aton() や、PostgreSQL の inet 演算子のようなものが存在しません。

文字列(string)として IP アドレスを扱いますので、特定の IP アドレスの情報を抽出するには、例えば次のようなクエリを実行します*2

SELECT * FROM "sampledb"."elb_logs" WHERE backend_ip='172.36.115.74' LIMIT 10;

しかし、上記の方法では意図した検索ができないことがあります。次のようなケースです。

SELECT * FROM "sampledb"."elb_logs" WHERE backend_ip>'172.100.0.0' LIMIT 10;

先ほどの例とは異なり、大小比較をしていることに注意してください。これの何が問題なのかと言うと、文字列として大小比較をしていますので、172.2.xxx.xxx や 172.20.xxx.xxx などが検索結果に出てきてしまうのです。"2" 以上の数字で始まればよく、数字の 100 と比較しているわけではないためです。

IP アドレスの大小比較を行う方法

ではどうすればよいのかと言うと、inet_aton() 相当の変換を行うというのが一つの方法です。inet_aton() は MySQL に限らず、C 言語のライブラリなどにもあるのでご存知の方も多いでしょうが、文字列としての IP アドレスを、数値としての IP アドレスに変換する関数です。

別の方法としては、IP アドレスの 4 つの数字部分を、必ず 3 桁の数字にしてしまう(0 で埋めてしまう)方法があります。10.0.0.1 を 010.000.000.001 に変換するのです。大小比較するのに . (ドット)は不要ですから、さらにドットを削って 010000000001 としてしまいます。IP アドレスとしての表記方法としては間違っていますが、大小比較するには十分です。この方法で IP アドレスを大小比較してみましょう。

SELECT * FROM "sampledb"."elb_logs"
WHERE
  lpad(split_part(backend_ip, '.', 1), 3, '0') ||
  lpad(split_part(backend_ip, '.', 2), 3, '0') ||
  lpad(split_part(backend_ip, '.', 3), 3, '0') ||
  lpad(split_part(backend_ip, '.', 4), 3, '0')
  >
  lpad(split_part('172.100.0.0', '.', 1), 3, '0') ||
  lpad(split_part('172.100.0.0', '.', 2), 3, '0') ||
  lpad(split_part('172.100.0.0', '.', 3), 3, '0') ||
  lpad(split_part('172.100.0.0', '.', 4), 3, '0')
LIMIT 10;

WHERE 句の条件が少し複雑ですが、以下の 4 行(ip の部分は IP アドレス)で IP アドレス1つ分を表現しています。

  lpad(split_part(ip, '.', 1), 3, '0') ||
  lpad(split_part(ip, '.', 2), 3, '0') ||
  lpad(split_part(ip, '.', 3), 3, '0') ||
  lpad(split_part(ip, '.', 4), 3, '0')

例えば、lpad(split_part('172.100.0.0', '.', 4), 3, '0') ですが、これは、'172.100.0.0' を '.' で区切ってその 4 つ目を取得し、'0' で埋めて 3 文字の文字列を生成しています。 これを 4 つ分実行してからそれぞれを連結して IP アドレスを 12 文字のテキスト表現に変換しているわけです。

ここではクエリの実行結果は載せませんが、正しく大小比較できることを実際に確認してみてください。

まとめ

Athena や S3 Select で IP アドレスを比較する方法を説明しました。AWS のサービスログの解析で、特定の IP アドレスレンジにあるログを抽出するようなケースで参考になれば幸いです。

リファレンス

*1:Comma-Separated Values

*2:ここでは ELB のログが格納されているサンプルデータベースを使用していますが、その中身がどのようなものであるかは重要ではありません

SAM ローカルをプロキシ環境下で実行する

AWS Lambda の開発時に、SAM ローカル (SAM Local) から AWS API をうまく呼び出せず、少しハマってしまいましたので、どうやって解決したのかを記載しておきます。同じ問題で困っている人の参考になれば幸いです。

SAM ローカルとは

AWS Lambda では、SAM (Severless Application Model) のテンプレートからデプロイを行うことができます。まだ SAM を使ったことがなければ、AWS Lambda に特化した CloudFormation のサブセットのようなものと捉えると、理解しやすいかもしれません。

SAM はとても便利な機能ではありますが、コードを改変して、テストをして、デバッグをして、というサイクルを回すにはいくらか不便なところがあります。デプロイに時間がかかりすぎるのです。

SAM ローカルは、ローカル環境で Lambda 関数(+ API Gateway)を実行できるため、デプロイが非常に高速で、反復開発の効率アップを実現できます。もちろん、SAM ローカルで開発したコードは AWS 環境下でもそのまま動かすことができます。

解決したい問題

プロキシ経由でインターネットに接続する必要のあるネットワーク環境で SAM ローカルを使う場合は少し注意が必要です。ローカルで実行するとは言っても docker コンテナの中で動くため、プロキシ設定が Lambda 関数には引き継がれず、AWS API などの http/https 接続に失敗する(ことがある)ためです。

解決方法

解決方法は複数あるのですが、私が採った方法をご紹介します。

SAM ローカルはインストール済みであるものとします。

想定するプログラム

aws-sam-localGitHub レポジトリにサンプルコードがありますので、その中に含まれている samples/using-aws-sdk を例に説明します。このプログラムは node.js で書かれています。

プロキシ設定なしで実行した場合

まずは何のコード変更もせずに、sam コマンドを使って local invoke (ローカル実行)してみます。オプションの -e では擬似イベントを定義した .json を指定するのですが、ここで取り上げている Lambda 関数では不要なため、/dev/null を与えます。

% sam local invoke -e /dev/null

2018/04/26 21:55:52 Successfully parsed template.yaml
2018/04/26 21:55:52 Connected to Docker 1.35
2018/04/26 21:55:52 Fetching lambci/lambda:nodejs6.10 image for nodejs6.10 runtime...
nodejs6.10: Pulling from lambci/lambda
Digest: sha256:56205b1ec69e0fa6c32e9658d94ef6f3f5ec08b2d60876deefcbbd72fc8cb12f
Status: Image is up to date for lambci/lambda:nodejs6.10
2018/04/26 21:55:55 Invoking index.handler (nodejs6.10)
2018/04/26 21:55:55 Mounting /Users/phine/Downloads/aws-sam-local-develop/samples/using-aws-sdk as /var/task:ro inside runtime container
START RequestId: c49325be-f941-1cd0-1cb4-db1daf3ac265 Version: $LATEST
2018/04/26 21:55:58 Function index.handler timed out after 3 seconds

最終行を見ると、タイムアウトしていることがわかります。

Lambda 関数のタイムアウト時間をもっと長く設定していると、以下のような別のエラーが出力されます。

% sam local invoke -e /dev/null
2018/04/26 21:59:37 Successfully parsed template.yaml
2018/04/26 21:59:37 Connected to Docker 1.35
2018/04/26 21:59:37 Fetching lambci/lambda:nodejs6.10 image for nodejs6.10 runtime...
nodejs6.10: Pulling from lambci/lambda
Digest: sha256:56205b1ec69e0fa6c32e9658d94ef6f3f5ec08b2d60876deefcbbd72fc8cb12f
Status: Image is up to date for lambci/lambda:nodejs6.10
2018/04/26 21:59:37 Invoking index.handler (nodejs6.10)
2018/04/26 21:59:37 Mounting /Users/phine/Downloads/aws-sam-local-develop/samples/using-aws-sdk as /var/task:ro inside runtime container
START RequestId: 039c52d3-56c0-1609-6b3a-15cee44e7208 Version: $LATEST
2018-04-26T12:59:43.090Z  039c52d3-56c0-1609-6b3a-15cee44e7208   Error listing S3 buckets: UnknownEndpoint: Inaccessible host: `s3.amazonaws.com'. This service may not be available in the `us-east-1' region.
2018-04-26T12:59:43.098Z 039c52d3-56c0-1609-6b3a-15cee44e7208   {"errorMessage":"Inaccessible host: `s3.amazonaws.com'. This service may not be available in the `us-east-1' region.","errorType":"UnknownEndpoint","stackTrace":["Request.ENOTFOUND_ERROR (/var/task/node_modules/aws-sdk/lib/event_listeners.js:457:46)","Request.callListeners (/var/task/node_modules/aws-sdk/lib/sequential_executor.js:105:20)","Request.emit (/var/task/node_modules/aws-sdk/lib/sequential_executor.js:77:10)","Request.emit (/var/task/node_modules/aws-sdk/lib/request.js:683:14)","ClientRequest.error (/var/task/node_modules/aws-sdk/lib/event_listeners.js:295:22)","ClientRequest.<anonymous> (/var/task/node_modules/aws-sdk/lib/http/node.js:89:19)","emitOne (events.js:96:13)","ClientRequest.emit (events.js:188:7)","TLSSocket.socketErrorListener (_http_client.js:309:9)","emitOne (events.js:96:13)"]}
END RequestId: 039c52d3-56c0-1609-6b3a-15cee44e7208
REPORT RequestId: 039c52d3-56c0-1609-6b3a-15cee44e7208    Duration: 3741.55 ms    Billed Duration: 3800 ms    Memory Size: 128 MB Max Memory Used: 53 MB

{"errorMessage":"Inaccessible host: `s3.amazonaws.com'. This service may not be available in the `us-east-1' region.","errorType":"UnknownEndpoint","stackTrace":["Request.ENOTFOUND_ERROR (/var/task/node_modules/aws-sdk/lib/event_listeners.js:457:46)","Request.callListeners (/var/task/node_modules/aws-sdk/lib/sequential_executor.js:105:20)","Request.emit (/var/task/node_modules/aws-sdk/lib/sequential_executor.js:77:10)","Request.emit (/var/task/node_modules/aws-sdk/lib/request.js:683:14)","ClientRequest.error (/var/task/node_modules/aws-sdk/lib/event_listeners.js:295:22)","ClientRequest.<anonymous> (/var/task/node_modules/aws-sdk/lib/http/node.js:89:19)","emitOne (events.js:96:13)","ClientRequest.emit (events.js:188:7)","TLSSocket.socketErrorListener (_http_client.js:309:9)","emitOne (events.js:96:13)"]}

こちらはタイムアウトではなく、S3 のエンドポイントに接続できなかったことがわかります。何れにしても、AWSAPI 呼び出しに失敗していることになります。

プロキシを設定する方法

① proxy-agent をインストールします

% npm install proxy-agent --save

② template.yml に最後の3行を追加し、環境変数にプロキシを設定します

AWSTemplateFormatVersion : '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Description: |
  An example Serverless project that uses the AWS SDK to list S3 buckets

Resources:
  # Place your project resources here (AWS Lambda functions, API Gateway) etc

  ExampleFunction:
    Type: AWS::Serverless::Function
    Properties:
      Runtime: nodejs6.10
      Handler: index.handler
      Environment:
        Variables:
          Proxy: http://proxy:3128

③ index.js で環境変数を参照し、プロキシの設定処理を追加します

'use strict';

// Make sure to run 'npm install' before running this function.
// This will fetch the AWS SDK to ./node_modules.

// Create a new AWS SDK object
const AWS = require('aws-sdk');

// 以下の if 文を追加する
if (process.env.Proxy) {
    const proxy = require('proxy-agent');
    AWS.config.update({
        httpOptions: { agent: proxy(process.env.Proxy) }
    });
}

// Create S3 service object
...

環境変数 Proxy が設定されている場合のみ、プロキシの設定処理を実行していますので、SAM ローカルではなく、AWS 環境にデプロイする場合には、環境変数 Proxy だけを変更(削除)すればよいことになります。

プロキシを設定して実行した場合

以下のように、S3 バケットの情報を取得できるようになりました。

% sam local invoke -e /dev/null
2018/04/26 22:46:42 Successfully parsed template.yaml
2018/04/26 22:46:42 Connected to Docker 1.35
2018/04/26 22:46:42 Fetching lambci/lambda:nodejs6.10 image for nodejs6.10 runtime...
nodejs6.10: Pulling from lambci/lambda
Digest: sha256:56205b1ec69e0fa6c32e9658d94ef6f3f5ec08b2d60876deefcbbd72fc8cb12f
Status: Image is up to date for lambci/lambda:nodejs6.10
2018/04/26 22:46:44 Invoking index.handler (nodejs6.10)
2018/04/26 22:46:44 Mounting /Users/phine/Downloads/aws-sam-local-develop/samples/using-aws-sdk as /var/task:ro inside runtime container
START RequestId: 9cba8c79-1d8c-1c0c-85fc-b71a885b5b3f Version: $LATEST
2018-04-26T13:46:51.112Z        9cba8c79-1d8c-1c0c-85fc-b71a885b5b3f    s3://sample-s3-bucket-20180426
END RequestId: 9cba8c79-1d8c-1c0c-85fc-b71a885b5b3f
REPORT RequestId: 9cba8c79-1d8c-1c0c-85fc-b71a885b5b3f  Duration: 4348.08 ms    Billed Duration: 4400 ms        Memory Size: 128 MB     Max Memory Used: 58 MB

"ok"
Deep Dive

Amazon VPCVPC エンドポイントを使っている環境では、グローバル設定である AWS.config を書き換えるよりも、個々のサービスオブジェクトの生成時に必要に応じてプロキシを設定する方が良いでしょう。

例えば次のようにします。ここでは、環境変数が設定されているかどうかのチェックは省略しています。

const proxy = require('proxy-agent');

// Create S3 service object
const s3 = new AWS.S3({
    apiVersion: '2006-03-01',
    httpOptions: { agent: proxy(process.env.Proxy) }
});

まとめ

プロキシ環境下で SAM ローカルを使用して、Lambda 関数から AWS API を呼び出す(http/https 接続する)方法を解説しました。

SAM ローカルだけプロキシを経由する必要が場合、その設定を含むテンプレートを用意するだけでよいので、docker イメージ手を入れる必要がないのはもちろん、SAM と SAM ローカルとで同じ Lambda 関数をデプロイできることをご理解いただけたかと思います。

リファレンス

ファイル名に使用可能な文字

代表的なファイルシステムとファイル名に使用可能な文字について解説します。また、ファイル名に「改行」を使えるということを知らずにやってしまう過ちと、その対処方法についても説明します。

代表的なファイルシステムとファイル名に使用可能な文字

ファイル名に使用可能な文字はファイルシステムに依存します。よく使用されるファイルシステムは OS ごとに違いますので、以下のように表にまとめてみました。

OS ファイルシステム 使えない文字
Linux ext4 NUL と /
Linux xfs NUL
Windows NTFS NUL,/:*"?<>|
Windows FAT32 NUL,/:*"?<>|

ファイルシステムの種類はここに記載しきれないほどたくさんありますので、もっと詳しく知りたい場合は Wikipedia を参照してください。

ここで重要なのは、NUL (ヌル、¥0) 以外はファイル名に使えるということです。

ファイル名のリストをファイルに保存するには

上記のとおり、ファイル名に NUL 以外が使えるということは、改行文字もファイル名に使えるということです。ファイル名をファイルに列挙しておき、そのリストにしたがって順次処理するようなスクリプト(プログラム)を書くことがありますが、ファイル名の区切り文字として改行を使うのは適切ではないということになります。

ファイル名のリストをファイルに保存する間違った方法

以下では例として、ファイル名が "filename" で始まるファイルのリストを filelist というファイルに保存しています。

$ find . -type f -name "filename*" -fprint filelist
$ cat filelist
./filename_3
./filename_1
./filename_2
./filename
_4

このファイル自体は改行区切りですが、それだと filelist には 5 つのファイルがあるように見えてしまいます。実際にはファイルは 4 つしかありません。

試しに xargs にファイルリストをわたし、ls コマンドを実行してみましょう。filename(改行)_4 というファイル名に対し、改行区切りでは正しく扱えないことがわかります。

$ cat filelist | xargs ls -l
ls: cannot access './filename': No such file or directory
ls: cannot access '_4': No such file or directory
-rw-r--r-- 1 debian debian 0 Apr 22 09:28 ./filename_1
-rw-r--r-- 1 debian debian 0 Apr 22 09:28 ./filename_2
-rw-r--r-- 1 debian debian 0 Apr 22 09:28 ./filename_3
ファイル名のリストをファイルに保存する正しい方法

上記の問題を解決するには、ファイル名の区切り文字には NUL を使いましょう。

$ find . -type f -name "filename*" -fprint0 filelist
$ cat filelist
./filename_3./filename_1./filename_2./filename
_4

-fprint オプションの代わりに -fprint0 オプションを使っているところがミソです*1

NUL は印字(表示)できない文字であるため、cat の実行結果に NUL が含まれているのかどうかわかりませんが、例えば xargs には NUL 文字を正しく解釈できるオプションが用意されており、-0 (--null) を付けると、以下のように意図した実行結果が得られます。

$ cat filelist | xargs -0 ls -l
-rw-r--r-- 1 debian debian 0 Apr 22 09:28 ./filename_1
-rw-r--r-- 1 debian debian 0 Apr 22 09:28 ./filename_2
-rw-r--r-- 1 debian debian 0 Apr 22 09:28 ./filename_3
-rw-r--r-- 1 debian debian 5 Apr 22 09:32 ./filename?_4

※ ここでは改行文字が ? と表示されています。

まとめ

ファイル名に使用可能な文字は NUL 以外 と考えておくべき理由と、ファイル名のリストをファイルに正しく列挙する方法を紹介しました。

リファレンス

*1:標準出力に出力する場合には -print0 を使います

CloudflareのパブリックDNSをより安全に使う

パブリック DNS と言えば Google の 8.8.8.8 が有名ですが、Cloudflare のパブリック DNS も優れた機能を持っています。そこで、Cloudflare のパブリック DNS の特長と、それを macOS High Sierra で使う方法についてご紹介します。

もっとも簡単な方法

プライマリ DNS サーバーを 1.1.1.1 に、セカンダリー DNS サーバーを 1.0.0.1 にします。

  1. [システム環境設定] → [ネットワーク] から使用しているネットワークを選択します
  2. [詳細...] ボタンをして、[DNS] を選択します
  3. 2 つの IP アドレスを入力します (1.1.1.1, 1.0.0.1) f:id:phine:20180420214110p:plain
  4. [OK] ボタンをクリックします
  5. [適用] ボタンをクリックします
メリット
  • 名前解決が高速である
  • ISP に名前解決をコントロールされない *1
問題点

Cloudflare のパブリック DNS サーバーを使うようにしただけでは、我々がどのような名前解決を行なっているのかを中間者*2によって盗み見されたり、データを改ざんされたりする可能性があります。

これを解決する手段の一つとして、下記の DoH が有効です。

DoH で名前解決する

DoH とは

DoH (DNS over HTTPS) で名前解決すると、DNS の通信が HTTPS でカプセリングされ、通信内容が暗号化されているため、盗み見されたところで、具体的な通信内容まではわかりません。また、データを改ざんされることもありません。

デメリットとしては、HTTPS でカプセリングされるため、高速を謳う Cloudflare のパブリック DNS であっても、名前解決のスピードはいくらか遅くなります。

DoH 対応クライアントのインストール方法

macOS で Cloudflare に DoH 接続するには、Cloudflared を使う方法と、DnsCrypt-Proxy を使う方法があります。ここでは、Cloudflared を使う方法について紹介します。

% brew install cloudflare/cloudflare/cloudflared
% mkdir -p /usr/local/etc/cloudflared
% cat << EOF > /usr/local/etc/cloudflared/config.yaml
proxy-dns: true
proxy-dns-upstream:
 - https://1.1.1.1/dns-query
 - https://1.0.0.1/dns-query
EOF
$ sudo cloudflared service install
DNS の設定変更

Cloudflared で名前解決させるために、DNS サーバーを 127.0.0.1 に変更します。

  1. [システム環境設定] → [ネットワーク] から使用しているネットワークを選択します
  2. [詳細...] ボタンをして、[DNS] を選択します
  3. IP アドレスを入力します (127.0.0.1) f:id:phine:20180420222341p:plain
  4. [OK] ボタンをクリックします
  5. [適用] ボタンをクリックします
動作確認

きちんと名前解決できていることを確認します。

% dig +short www.example.com
93.184.216.34

まとめ

macOS で、Cloudflare のパブリック DNS に DoH で名前解決させる方法を紹介しました。これにより、秘匿性に優れた名前解決を実現できるようになりました。

リファレンス

*1:DNS は、ISP が特定サイトへの接続をブロッキングする方法の一つとなりえます。ISP がサイトブロッキングすることの是非については色々な議論がありますので、興味のある方はググってみてください。

*2:ここでは、ユーザーと Cloudflare 間のネットワークに何らかの手段で介入できる人

子供のスマホに設定しておきたいこと

書籍「Google便利すぎる!240のテクニック」から、子供の Android ケータイあるいは Google アカウントに設定しておきたい項目をご紹介します。

そもそもの経緯

我が家では、2016 年 11 月から長男(12 才)に ASUSAndroid ケータイを持たせています。このスマホには標準で「キッズモード」という機能があり、使用できるアプリを制限することができます。

2018 年 4 月、その長男も中学1年生になりました。そろそろ、キッズモードを解除しようかと考えています。とは言え、まだ一定の制限をかけるべきとは考えており、たまたま目にした「Google便利すぎる!240のテクニック」で参考になる項目がありましたので、実際に設定してみました。

Android の設定

181: なくした Android 端末を見つけ出す

設定方法は このページ に記載されています。 スマホをなくした場合に、端末を探す からスマホを見つけ出すことができるようになります。

Google アカウントの設定

020: 検索結果からアダルトコンテンツを排除する

セーフサーチを使用して、アダルトコンテンツ(ポルノ)などを含む検索結果を除外することができます。 設定方法は こちら に記載されています。

141: 子供に不適切な動画を見せないよう設定する

YouTube で制限付きモードを有効にして、成人向けのコンテンツが表示されないようにすることができます。 設定方法は こちら に記載されています。

155: 位置情報を削除して写真を共有する

Google フォトで共有した写真から、位置情報を自動的に削除することができます。 設定方法は こちら に記載されています。

書評

最後に、簡単な書評を書いておきます。

★★★★☆

本のタイトルにあるように、本書では 240 にも及ぶ Google 関連のテクニック(TIPS)が紹介されています。これだけの数にもなると、内容の濃さではなく、どれだけ多くの気づきを得られるかがポイントになってきます。私の場合、ここでは紹介していないテクニックも含めると、12 のテクニックが参考になる情報でした。その他は既に知っている、あるいは、今後もその知識は使わないだろうというものでした。

本を購入してまで読む価値があるかと言えば微妙なところですが、Amazon の Prime Reading (無料) で読める人であれば、目を通しておいて損はないと思います。

リファレンス

awscliの入力を楽にする方法

AWS (Amazon Web Services) ユーザーの多くが、awscli を使っていることでしょう。しかし、awscli はサブコマンドやオプションが多く、なかなか覚えられません。その結果、ヘルプを何度も見返すことになり、非効率に感じることがあります。そこで、awscli を便利にする2つの方法を紹介します。

  • コマンド補完
  • SAWS: A Supercharged AWS CLI

私は macOS High Sierra を使用していますが、今回ご紹介する方法は LinuxWindows でもほぼ同様に使えます。

コマンド補完

シェルコマンドを入力する場合に、TAB キーを押して部分的に入力されたコマンドを補完できるようにする方法です。ここでは Bash での設定方法を説明します*1

まず、aws_completer のパスを確認します。awscli がインストールされていれば aws_completer もインストールされているはずです。

$ which aws_completer
/usr/local/bin/aws_completer

次に、~/.bashrc に以下の1行を追加します。aws_completer のパスは、実際のパスに合わせてください。

complete -C '/usr/local/bin/aws_completer' aws

.bashrc を読み直して、上記の設定が機能しているか確認してみましょう。

$ source ~/.bashrc
$ aws s3 ← ここで TAB を2回押してみます
cp ls mb mv presign rb rm sync website

aws s3 のサブコマンド候補がリストアップされるようになりました。 同様に、オプションも自動補完されます。

$ aws s3 ls --←ここで TAB を2回押してみます
--ca-bundle --color --human-readable --no-verify-ssl --profile --region --version
--cli-connect-timeout --debug --no-paginate --output --query --request-payer
--cli-read-timeout --endpoint-url --no-sign-request --page-size --recursive --summarize

これで help コマンドを使う回数がいくらか減らせるのではないでしょうか。

SAWS: A Supercharged AWS CLI

SAWS は awscli をラップして、便利機能を提供してくれるツールです。コマンドの自動補完だけではなく、AWS リソースの補完まで行ってくれる他、コマンドや出力結果の色付け、ショートカット機能など、豊富な機能を持っています。

AWS 公式の aws-shell と類似した機能と言えますが*2aws-shell は半年以上も更新されていないため、ここでは SAWS を紹介します。

インストール

通常は、以下のコマンドでインストールできます。

$ sudo pip install saws

しかし、2018年4月16日現在、Mac OS X 環境下では既知の問題があり、追加オプションをつけてインストールを行う必要があります。

$ sudo pip install saws --upgrade --ignore-installed six

使い方

シェルプロンプトから "saws" を実行します。

$ saws
No resource cache found
Refreshing resources...
  Refreshing instance ids...
  Refreshing instance tag keys...
  Refreshing instance tag values...
  Refreshing bucket names...
  Refreshing bucket uris...
Done refreshing
Version: 0.4.3
Theme: vim
saws>

"saws>" プロンプトが表示されますので、そこから awscli を実行します。
補完されたり、色付けされたりする様子は、GitHub のページの紹介がわかりやすいかったので、以下で引用しています。

f:id:phine:20180416224425g:plain
出典:https://github.com/donnemartin/saws

サブコマンドやオプションだけではなく、AWS リソースの補完ができるのはとても便利です。

まとめ

awscli の入力を楽にする2つの方法を紹介しました。aws_completer は標準的な方法と言えるでしょうが、SAWS を使うとより便利になります。
SAWS には、ここでは紹介していない便利な機能が他にもありますので、詳しくは GitHub のページ(英語)を参照するなどして、実際に触ってみてください。

*1:ZSHtcsh を使用している場合にはこちらのページを参照してください。

*2:SAWS の開発者は aws-shell の開発にも参画しています