YAML形式のCloudFormationテンプレートが数値をどのように判別するのか確認してみた

とある出来事をきっかけに、YAML形式のCloudFormationテンプレートが数値を入力した際、数値をどのように読みとるのか?を検証してみました。
2022.06.14

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

みなさん、こんにちは。

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となります。

以下発端の結果を振り返ると、0300192にしっかり変換されていましたね。一致しています。

結果、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)でした。