見出し画像

AWS CLI短縮構文をシェルスクリプトから使うときに陥りやすい罠

電通デジタルでSREをしている神田です。突然ですが、みなさんAWS CLI短縮構文をご存知でしょうか?

awsコマンドを使っているときに、コマンドのオプションとしてfoo=barfoo,bar,baz といった構造を持ったパラメータを要求されることがあります。このパラメータを記述するための構文が短縮構文(shorthand)です。

本記事では、普段なんとなくお世話になっている短縮構文をシェルスクリプトから使ったときに陥りやすい罠を紹介します。

AWS CLI短縮構文の簡単な例

短縮構文は、JSONと等価な値を簡潔に表現するための構文です。JSONとの対応をみると内容を理解しやすいです。


例えば、

["foo","bar","baz"]

というJSONの配列は、短縮構文では、

foo,bar,baz

と書くことができます。

また、

{"key1": "val1", "key2": "val2", "key3": "val3"}

といったオブジェクトは、短縮構文では、

key1=val1,key2=val2,key3=val3

と書くことができます。この時、カンマの間に空白を入れることはできません。空白はオブジェクトの区切り文字として扱われるためです。

短縮構文のもう少し複雑な例

もう少し複雑な例として次のようなオブジェクトの配列を見てみます。

[
 {"Key": "Tag1", "Value": "Val1"},
 {"Key": "Tag2", "Value": "Val2"},
 {"Key": "Tag3", "value": "Val3"}
]

この場合、短縮構文では、

Key=Tag1,Value=Val1 Key=Tag2,Value=Val2 Key=Tag3,Value=Val3

というように、空白で区切ることでオブジェクトの配列を表現することができます。

シェルスクリプトから使うときの罠

非常に便利な短縮構文ですが、シェルスクリプトの中で使うときには注意が必要です。

ここでは、以下のような文字列パラメータを受け取るテンプレートからCloudFormationスタックを作成するシェルスクリプトを考えてみます。

# template.ymlとして保存されているとします
Parameters:
 Foo:
   Type: String
Resources:
 #...

このテンプレートのパラメータFooBarという値を渡してスタックを作成するコマンドは、

aws cloudformation create-stack \
   --stack-name ShorthandTestStack \
   --template-body file://template.yml \
   --capabilities CAPABILITY_IAM CAPABILITY_AUTO_EXPAND \
   --parameters ParameterKey=Foo,ParameterValue=Bar

となります。

aws cloudformation create-stackコマンドの詳しい使い方は公式リファレンスを参照してください。



実際に運用されるシェルスクリプトの中では、共通の設定ファイルから取り出した値をスタックのパラメータとして渡したいということがよくあります。

この例でも、params.jsonというJSON形式の設定ファイルから値を読み出す形に書き換えてみます。JSONから値を取得するにはjqコマンドを使います。

修正したコマンドは、

val=$(jq -r ".Foo" < params.json)

params="ParameterKey=Foo,ParameterValue=$val"

aws cloudformation create-stack \
   --stack-name ShorthandTestStack \
   --template-body file://template.yml \
   --capabilities CAPABILITY_IAM CAPABILITY_AUTO_EXPAND \
   --parameters $params

となります。

一見すると何の問題もない書き換えに見えるのですが、実はparams.jsonの設定値によっては正常に動作しなくなっています。

例えば、params.jsonが以下のようなケースではエラーが発生します。

{"Foo": "Bar,Baz"}

実際に実行すると次のようなエラーが発生します。

Parameter validation failed:
Invalid type for parameter Parameters[0].ParameterValue, value: ['Bar', 'Baz'], type: <class 'list'>, valid types: <class 'str'>


これは変数が展開されると以下のようになるからです。

aws cloudformation create-stack \
   --stack-name ShorthandTestStack \
   --template-body file://template.yml \
   --capabilities CAPABILITY_IAM CAPABILITY_AUTO_EXPAND \
   --parameters ParameterKey=Foo,ParameterValue=Bar,Baz

ここでParameterValueに渡されているBar,Bazは短縮構文では配列として解釈され、create-stackが期待している型とことなるため上記のエラーが発生します。


この問題は、変数展開で展開された値を '(シングルクォート)で囲うことで回避することができます。シングルクォートで囲われた値はカンマや空白が含まれていたとしても文字列として解釈されるため、ParameterValueが期待する型と一致します。最終的に問題が解決されたコマンドは以下の通りです。

val=$(jq -r ".Foo" < params.json)

# $valをシングルクオートで囲う
params="ParameterKey=Foo,ParameterValue='$val'"

aws cloudformation create-stack \
   --stack-name ShorthandTestStack \
   --template-body file://template.yml \
   --capabilities CAPABILITY_IAM CAPABILITY_AUTO_EXPAND \
   --parameters $params

今回はコマンドが短く見通しが良いため、問題点にすぐに気がつけましたが実際のシェルスクリプトの中では入力値によって問題が発生したりしなかったりする厄介なバグの原因となるため注意が必要です。

まとめ

AWS CLI短縮構文(shorthand)を簡単に紹介し、シェルスクリプトから扱うときに陥りやすい罠を解説しました。本記事がAWS CLIを使った自動化にかかわる方の少しでもお役に立てれば幸いです。