YAML形式のCloudFormationテンプレートが数値をどのように判別するのか確認してみた
みなさん、こんにちは。
AWS事業本部コンサルティング部@東京オフィスの芦沢(@ashi_ssan)です。
CloudFormationテンプレートの検証中に以下のような出来事があったのでブログにしました。
発端
YAML形式で作成したCloudFormationテンプレートのタグの設定を以下のような内容で実行したところ、おかしな?挙動をしました。
AWSTemplateFormatVersion: 2010-09-09 Description: test vpc template Resources: vpc: Type: 'AWS::EC2::VPC' Properties: CidrBlock: 10.0.0.0/16 Tags: - Key: hoge Value: 0300
テンプレートの実行結果として、hogeタグは0300
ではなく、192
と表示されました。
0300
をクオートしてないな?と気づいた方、鋭いですね。正解です。
以下のように0300
をクオートしてしまえば数値ではなく文字列とみなされるようで、想定通り0300
と表示され、解決しました。
AWSTemplateFormatVersion: 2010-09-09 Description: test vpc template Resources: vpc: Type: 'AWS::EC2::VPC' Properties: CidrBlock: 10.0.0.0/16 Tags: - Key: hoge Value: "0300"
本エントリの教訓として持ち帰っていただきたいことは、YAML形式のCFnテンプレートを書く際 数値はしっかりクオートしましょう
です。
ですがこれで終わり、では学びが少ないですよね。
ブログの残りの枠でどうしてこのように別の数字に変換されてしまったのか、を追ってみようと思います。
いきなりまとめ
YAML形式のCloudFormationは、YAML1.1をサポートしており、数値(Integer)を以下のように判別します。
- 先頭が0ではじまる数値の場合、8進数とみなす
- 先頭が0xではじまる数値の場合、16進数とみなす
- 60以下の数値が:(コロン)で区切られている数値の場合、60進数とみなす
- 上記に当てはまらない数値の場合、10進数とみなす
- 数値がクオートされている場合、文字列とみなす
CloudFormationテンプレート(YAML1.1)の仕様を読み解く
公式ドキュメントによると、CloudFormationはYAML1.1をサポートしています。
AWS CloudFormation は、いくつかの例外を除き、YAML バージョン 1.1 の仕様をサポートしています。
そこで、YAML1.1の公式ドキュメントを確認したところ、2.4. Tags - Example 2.19. Integers
の欄で数値(Integers)に関連する以下の記述がありました。
canonical: 12345 decimal: +12,345 sexagesimal: 3:25:45 octal: 014 hexadecimal: 0xC
直訳すると上から標準、10進数、60進数、8進数、16進数
と訳すことができ、公式ドキュメントの例は10進数と60進数で12345
、8進数と16進数で12
、と各数値を各々の進数法で表したものであると読み取りました。
ここでの記述を元に、YAML1.1では数値を以下のように判断している、と推測しました。
- 60以下の数値が:(コロン)で区切られている数値の場合、60進数とみなす
- 先頭が0ではじまる数値の場合、8進数とみなす
- 先頭が0xではじまる数値の場合、16進数とみなす
また、ドキュメントに記述はないですが、上記に当てはまらない場合の数値は10進数と判定されることで変換されないと想定し、以下と推測します。
- 上記に当てはまらない数値の場合、10進数とみなす
発端の章ではさっと対応してしまいましたが、クオートされたらなんで8進数に変換されなかったのか?の答えもYAML1.1の公式ドキュメントに記載がありました。
2.4. Tags - Example 2.21. Miscellaneous
の欄で以下の記載があります。
string: '12345'
クオートされている数値はstring(文字列)とみなされるようですね。以下の条件も推測できるかと思います。
- 数値がクオートされている場合、文字列とみなす
推測した内容を検証してみる
ここまでに推測した内容をまとめて検証してみたいと思います。
- 先頭が0の数値の場合、8進数とみなす
- 先頭が0xの数値の場合、16進数とみなす
- 桁毎に:(コロン)で区切られている場合、60進数とみなす
- 上記以外の数値の場合、10進数とみなす
- 数値がクオートされている場合、文字列とみなす
以下のようなテンプレートをマネジメントコンソールから実行して、パラメータのデフォルト値として指定した各進数法で記載した値が想定通り12345
に変換される(もしくはされない)ことを確認します。
AWSTemplateFormatVersion: 2010-09-09 Description: test vpc template Metadata: AWS::CloudFormation::Interface: ParameterGroups: - Label: default: CFn Template Number Check Parameters: - OCTAL - HEXADECIMAL - SEXAGECIMAL - DECIMAL - STRING Parameters: OCTAL: Type: Number Default: 030071 HEXADECIMAL: Type: Number Default: 0x3039 SEXAGECIMAL: Type: Number Default: 3:25:45 DECIMAL: Type: String Default: 12345 STRING: Type: String Default: "12345" Resources: vpc: Type: 'AWS::EC2::VPC' Properties: CidrBlock: 10.0.0.0/16 Tags: - Key: octal Value: !Ref OCTAL - Key: hexadecimal Value: !Ref HEXADECIMAL - Key: sexagesimal Value: !Ref SEXAGECIMAL - Key: decimal Value: !Ref DECIMAL - Key: string Value: !Ref STRING
結果、以下のようにすべて想定通り12345
と表示されました。
参考までに、このようなテンプレートで検証した経緯は以下です。
- 検証を進めていく中で、
各数値が本当に数値なのか
を判断するために、データ型
を判別する必要があると気づきました。 - とくに10進数の場合、進数法に合わせた別の数値に変換されるわけではないため変換されているのかが見た通りの値では判断できません。
- そこで、Parametesにデータ型としてNumberを指定した上で各数値を入力することで、String(文字列)ではなく数値であることを確認することにしました。
- またParametersのデフォルトの値として、各々の変換元の進数法に応じた数値を入力すると、CFnのマネジメントコンソール画面に変換後の数字が表示されることに気づき、多少検証の手間を省くことができました。
再度発端のテンプレートに戻る
再度、元ネタのタグ設定に戻ってみましょう。
AWSTemplateFormatVersion: 2010-09-09 Description: test vpc template Resources: vpc: Type: 'AWS::EC2::VPC' Properties: CidrBlock: 10.0.0.0/16 Tags: - Key: hoge Value: 0300
8進数の数値0300
を10進数に変換すると192
となります。
以下発端の結果を振り返ると、0300
は192
にしっかり変換されていましたね。一致しています。
結果、hogeタグは0300ではなく、
192
と表示されました。
おまけ(明示的に10進数であると表記しようとしてみた)
また、YAML1.1のIntegerの例にあった+12,345(符号と3桁区切りのカンマをつける)
が数値として認識され、10進数に変換されるのか?を確認してみました。
AWSTemplateFormatVersion: 2010-09-09 Description: test vpc template Parameters: DECIMAL: Type: Number Default: +12,345 Resources: vpc: Type: 'AWS::EC2::VPC' Properties: CidrBlock: 10.0.0.0/16 Tags: - Key: decimal Value: !Ref DECIMAL
結果は、先頭にプラス(+)をつける、カンマ(,)をつけるのどちらもCloudFormationのバリデーションを通過できず、数値(Number)であると判定されませんでした。
まとめ
YAML形式のCloudFormationは、YAML1.1をサポートしており、数値(Integer)を以下のように判別します。
- 先頭が0ではじまる数値の場合、8進数とみなす
- 先頭が0xではじまる数値の場合、16進数とみなす
- 60以下の数値が:(コロン)で区切られている数値の場合、60進数とみなす
- 上記に当てはまらない数値の場合、10進数とみなす
- 数値がクオートされている場合、文字列とみなす
最後に
CloudFormationの話をというかYAML1.1の仕様の話で、すごく細かく小さなネタですが、偶然見つけたのでブログにしてみました。
CloudFormationテンプレートを書いている際に同様の疑問が浮かんだ方でしたら刺さることもあるネタだと思います。
このブログがいつかどこかの誰かの役に立てればと思います。
以上、AWS事業本部コンサルティング部@東京オフィスの芦沢(@ashi_ssan)でした。