[アップデート] CloudFormationで言語機能を拡張するTransformがサポートされました

2022.09.20

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

しばたです。

すこし前の話ですがCloudFormationの言語機能を拡張するAWS::LanguageExtensions Transformがサポートされました。
AWSからのアナウンスは以下となります。

CloudFormation Transform

従来からCloudFormationにはテンプレートの内容に所定の変換処理を行うTransformと呼ばれる機能が存在しています。

一番身近な例としてはAWS Serverless Application Model (AWS SAM)で使われるAWS::Serverless Transformかと思います。
このTransformによりAWS SAMではCloudFormationに独自の構文を追加し機能を拡張しています。

他にも

といったTransformが存在しており、今回新たに

が追加されました。

CloudFormation の言語拡張

AWSのアナウンスにもありますがCloudFormationの言語機能についてはGitHub上で議論されています。

このリポジトリで機能追加のリクエストについてRFCの形でまとめられ、今回3つの機能が正式に追加されました。
他にも幾つかの機能がレビュー中ですので詳細はGitHubをご覧ください。

ちなみに「なぜ言語機能の拡張をTransformで実装することにしたのか?」については特に触れられていませんでした。
個人的に予想できる理由はいくつか思いつくものの本筋ではないのでここでは語らないでおきます。
ただ、他言語で言うところのマクロに相当するTransformで拡張を実装する以上、拡張できる機能にある程度の制約が付くことは気に留めておくと良いでしょう。

AWS::LanguageExtensions transform

今回AWS::LanguageExtensions transformで追加された3つの機能についてそれぞれ解説していきます。

1. Fn::Length 関数

Fn::Lengthは配列やリストの要素数を返す関数です。
RFCはこちら。

引数に配列/リストまたは配列を返す特定の関数を指定してやるだけのシンプルなものですが、デプロイ時に要素数が動的に変わる場合は非サポートとのことです。この点はTransformがデプロイ前に実行される都合仕方ないでしょう。

# YAMLでの使用方法
Fn::Length : "配列 or リスト or 配列を返す特定関数"

個人的には全然使い方を思いつかなかったのですがRFCを見るとパラメーターで指定された要素数を条件判定に使っていました。
今回はシンプルにパラメーターで指定したセキュリティグループの数を返すだけのサンプルで動作確認してみました。

AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::LanguageExtensions
Parameters:
  SGList:
    Description: "List of security groups"
    Type: List<AWS::EC2::SecurityGroup::GroupName>
Resources:
  # ダミーリソース
  NullResource:
    Type: AWS::CloudFormation::WaitConditionHandle
Outputs:
  Result:
    Value:
      # パラメーターで選んだセキュリティグループの要素数を返すだけ
      Fn::Length:
        Ref: SGList

結果はこんな感じで要素数3を返してくれます。

2. Fn::ToJsonString 関数

Fn::ToJsonStringは引数の内容をエスケープされたJSONに変換する関数です。
RFCはこちら。

こちらも使い方はシンプルで引数にJSONに変換したいオブジェクトを指定してやるだけです。
JSONへの変換なので入力値にコメントがある場合は除外されます。

# YAMLでの使用方法
Fn::ToJsonString : "オブジェクト or 配列 or リスト or 特定関数の結果"

RFCによればJSONでの指定が必要なCloudWatch Dashboardでの利用例が挙げられていました。

AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::LanguageExtensions
Resources:
  # RFCのサンプルをそのまま拝借
  MyDashboard:
    Type: AWS::CloudWatch::Dashboard
    Properties:
      DashboardBody:
        Fn::ToJsonString:
          start: "-PT6H"
          periodOverride: inherit
          widgets:
          - type: text
            x: 0
            y: 7
            width: 3
            height: 3
            properties:
              markdown: Hello world

変換後のテンプレートはこんな感じでYAMLの記述がこんな感じのJSONに変換されます。

{\"start\":\"-PT6H\",\"periodOverride\":\"inherit\",\"widgets\":[{\"type\":\"text\",\"x\":0,\"y\":7,\"width\":3,\"height\":3,\"properties\":{\"markdown\":\"Hello world\"}}]}

ちなみに出来上がったダッシュボードはこちらになります。

3. DeletionPolicyとUpdateReplacePolicy属性の拡張

最後はDeletionPolicyUpdateReplacePolicy属性の適用範囲が拡張され、それぞれの属性を特定の関数や疑似パラメーターと組み合わせて使える様になります。
RFCはこちら。

RFCのサンプルにある様に

  • リージョン毎でポリシーを変えたい
  • 環境毎(開発環境や本番環境)でポリシーを変えたい

といった利用ができる様になり、今回はドキュメントにある例をほぼそのまま例示しますが、以下の様な感じでパラメーターで指定された環境(Prod,Staging,Dev)に応じてDynamoDBテーブルのポリシーを変えることができます。

AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::LanguageExtensions
Parameters:
  Stage:
    Type: String
    AllowedValues:
      - Prod
      - Staging
      - Dev
Conditions:
  IsProd: !Equals 
    - !Ref Stage
    - Prod
# ドキュメントの例に最低限必要なプロパティを追加
Resources:
  Table:
    Type: AWS::DynamoDB::Table
    Properties:
      KeySchema:
        - AttributeName: primaryKey
          KeyType: HASH
      AttributeDefinitions:
        - AttributeName: primaryKey
          AttributeType: S
      ProvisionedThroughput:
        ReadCapacityUnits: 1
        WriteCapacityUnits: 1
    DeletionPolicy: !If 
      - IsProd
      - Retain
      - Delete
    UpdateReplacePolicy: !If 
      - IsProd
      - Retain
      - Delete

試しにパラメーターにDevを指定した場合、変換後のテンプレートは下図の様になりDeletionPolicyUpdateReplacePolicyともにDeleteになっていることがわかります。

補足 : 複数のTransformを指定する場合

複数のTransformを指定する場合は配列で指定してやります。
例えば以下の様な記述にするとAWS::LanguageExtensionsAWS::Serverlessの順で評価されます。

# 実際に動作するかまでは未確認
AWSTemplateFormatVersion: 2010-09-09
Transform:
  - AWS::LanguageExtensions
  - AWS::Serverless

最後に

以上となります。

地味だけど痒い所に手が届く拡張だなという印象です。
機能拡張はRFCを経て行われていますのでどうしても欲しい機能がある場合は自分から提案してみるのも良いでしょう。