[小ネタ]AWS CLIの引数はファイルやURLでも渡せます

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

クラスメソッドのAWSコンサル部では毎週勉強会を開いています。 先日は AWS CLI をテーマに講師を担当しました。 勉強会のまとめは後日に譲るとして、講師として一番驚いたのが表題の件です。

AWS CLI はコマンドラインオプションの引数に直接文字列を渡す以外にも

  • テキスト/バイナリーファイルのパスを指定して、そのデータ
  • URL を指定して URL を GET したレスポンスボディー

といった形でも値を渡す事ができます。

みんな知っているだろうとさらっと流そうと思ったら、参加者のほとんどがこの事実を知らないということに驚愕しました。

AWS CLI 布教のために、共有したいと思います。 説明を単純化するために、コマンドの実行環境は Mac の Bash シェルとします。

文字列で渡す

一番素直な形です。

オプションに渡した引数をそのまま文字列として評価します。

$ aws ec2 create-key-pair --key-name 'My Key Pair'

この例では、オプション --key-name の引数 'My Key Pair' という名前で鍵が作成されます。

テキストファイルで渡す

引数をテキストファイルとして渡します。

JSON 形式のデータをコマンドラインから渡そうとすると、エスケープ処理が面倒なため、JSON はテキストファイルに指定して、引数にはファイルのパスを渡すのがお勧めです。

$ aws ec2 run-instances --image-id ami-05355a6c --block-device-mappings '[{\"DeviceName\":\"/dev/sdb\",\"Ebs\":{\"VolumeSize\":20,\"DeleteOnTermination\":false,\"VolumeType\":\"standard\"}}]'

のようなエスケープだらけのコマンドが

$ cat /tmp/device.json
[{
    "DeviceName":"/dev/sdb",
    "Ebs":{
        "VolumeSize":20,
        "DeleteOnTermination":false,
        "VolumeType":"standard"
    }
}]
$ aws ec2 run-instances --image-id ami-05355a6c --block-device-mappings file:///tmp/device.json

というようにスッキリ書けます。

バイナリファイルで渡す

  • aws ec2 run-instances – --user-data parameter(容量制限のために gzip 圧縮データを渡せられる)
  • aws s3api put-object – --sse-customer-key parameter(任意の256ビットの共通鍵)
  • aws kms decrypt – --ciphertext-blob parameter(暗号化されたデータ)

などはの用途では、引数で渡したいデータはいっぱんにバイナリーデータのため、コマンドラインからはうまく渡せません。 file:// 形式で渡したいところですが、 file:// はファイルシステムエンコードを使ってユニコード化します。

バイナリデータを文字列ではなくバイト列として扱い時にはデータを保存したファイルパスを fileb:// で接頭して渡せば実現可能です。 fileb の b は binary の b なのでしょう。

$ dd if=/dev/urandom bs=1 count=32 > sse.key
32+0 records in
32+0 records out
32 bytes (32 B) copied, 0.000164441 s, 195 kB/s
$ aws s3api put-object --bucket my-bucket --key test.txt --body test.txt --sse-customer-key fileb://sse.key --sse-customer-algorithm AES256
{
    "SSECustomerKeyMD5": "iVg8oWa8sy714+FjtesrJg==",
    "SSECustomerAlgorithm": "AES256",
    "ETag": "\"a6118e84b76cf98bf04bbe14b6045c6c\""
}

URL で渡す

一番知られていないと思われるのが、このスタイルです。 http:// または https:// で始まる引数を渡すと URL を GET したレスポンスボディーが引数で利用されます。

S3 に public-read 権限で JSON ファイルであげておき、コマンド実行時に、その S3 のパスを URL 形式で指定するといった使い方ができます。

$ curl --silent http://my-bucket.s3.amazonaws.com/filename.json
[
  {
    "DeviceName": "/dev/sdb",
    "Ebs": {
      "VolumeSize": 20,
      "DeleteOnTermination": false,
      "VolumeType": "standard"
    }
  }
]
$ aws ec2 run-instances --image-id ami-a13d6891 --block-device-mappings http://my-bucket.s3.amazonaws.com/filename.json

メタデータ(http://169.254.169.254/latest/meta-data/***)をサクッと渡せるのも嬉しいです。

URL の落とし穴

惜しむらくは、この機能はあまり知られておらず、単に文字列として URL を渡したコマンドが不思議な挙動をして、デバッグしてようやくこの仕様を知ることになるケースが多いことです。 現在のところ、URL そのものを渡したいときはファイル形式で渡すのが現実的です。

なお、この挙動はトップレベルの引数だけで、たとえば次のような入れ子の時は、適用され適用されません。

$ aws ec2 create-tags --resources $ID --tags Key=Name,Value=http://www.google.com/
$ aws ec2 describe-tags --filters Name=resource-id,Values=$ID
{
    "Tags": [
        {
            "ResourceType": "instance",
            "ResourceId": "i-a0000000",
            "Value": "http://www.google.com/",
            "Key": "Name"
        }
    ]
}

参考