この記事は フラー株式会社 Advent Calendar 2021 22日目の記事です。
21日目は @Yoshiatsu_Irei さんで 何かかく でした。

@Yoshiatsu_Irei さんは沖縄からの参戦です
沖縄時間ではまだ2021/12/21日です!!


早いもので前回のブログから1年経ってしまいました。
フラー株式会社ではモバイルアプリやWEBサービスのクラウド基盤にAWSを採用し、AWSの構成管理にCloudFormationを利用して構築しています。
今回は「とある案件」ですでに出来上がった環境へのリソースの追加時にパラメータ周りでハマった話備忘録を書こうと思います

普段はスタックをネストさせることなく、構築しているケースが多いのですが、今回は諸事情によりスタックをネストさせて構築していました。
そもそもパラメータが必要なケースとはどのような時でしょうか
ネストさせていない場合だとparameter-overridesにはEnvironmentを指定して各環境の設定はMappingsでとる運用をしていました。

cfn.yaml
AWSTemplateFormatVersion: 2010-09-09

Parameters:
  Environment:
    Type: String
    Default: development
    AllowedValues:
      - development
      - staging
      - production
  Ec2ImageId:
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2

Mappings:
  EnvToConfigs:
    development:
      BaseDomain: "example.com"

この場合ですとBaseDomainが必要な場合は以下のように取得することができます

cfn.yaml
BaseDomain: !FindInMap [EnvToConfigs, !Ref Environment, BaseDomain]

しかし、今回はすでにVPCやサブネットができあがった状態であった為VPCのIDやサブネットのIDなどの値を直に埋め込んでいかなければなりません
cloudformationのスタックをネストさせるにはAWS::CloudFormation::Stackを利用します。
下記ではHostedZoneを作成する例をあげます

cfn_base.yaml
AWSTemplateFormatVersion: "2010-09-09"

Parameters:
  BaseDomain:
    Type: String

Resources:
  HostedZone:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: "hostedzone.yaml"
      Parameters:
        BaseDomain: !Ref BaseDomain

ParametersでBaseDomainを受け取るためにcloudformation実行時の引数であるparameter-overridesにBaseDomainを追加してパラメータを渡す必要があります

$ aws cloudformation deploy \
    --stack-name "example-stack" \
    --template-file packaged.yaml \
    --capabilities CAPABILITY_NAMED_IAM CAPABILITY_AUTO_EXPAND \
    --parameter-overrides "BaseDomain=example.com"

CLIドキュメントのparameter-overridesの項目によれば

"ParameterKey1=ParameterValue1" "ParameterKey2=ParameterValue2" ...

のようにダブルクウォートで囲った文字列を並べていくと。。。。
パラメータが1つや2つであればいいのですが、今回冒頭で書いた通りすでに出来上がっている環境があるところに追加する場合はVPCのCIDR、VPCのID、サブネット・・・などパラメータがどんどん増えていきます

うーん つらい!

つらいので何かいい方法がないか模索する

パラメータを環境ごとの外部ファイルにしてなんとか読み込みできないかと試行錯誤してみました

1. env形式にして読み込ませてみる

development.cfg
Environment=development
BaseDomain=example.com
....

env形式のcfgファイルを作成し、cloudformation実行時にcatで呼び出す

$ aws cloudformation deploy \
    --stack-name "example-stack" \
    --template-file packaged.yaml \
    --capabilities CAPABILITY_NAMED_IAM CAPABILITY_AUTO_EXPAND \
    --parameter-overrides $(cat development.cfg)

動いた!!!が、弊社で導入しているlintチェックに引っかかってしまいました。SC2046
このlintの回避策はダブルクオォートで囲うことです

$ aws cloudformation deploy \
    --stack-name "example-stack" \
    --template-file packaged.yaml \
    --capabilities CAPABILITY_NAMED_IAM CAPABILITY_AUTO_EXPAND \
    --parameter-overrides "$(cat development.cfg)"

lintチェックの警告が消えた!やったーと思っていたらどうも挙動がおかしい
コマンド実行時のログをよく見ていると

$ aws cloudformation deploy \
    --stack-name "example-stack" \
    --template-file packaged.yaml \
    --capabilities CAPABILITY_NAMED_IAM CAPABILITY_AUTO_EXPAND \
    --parameter-overrides 'Environment=development
BaseDomain=example.com'

シングルクウォートで囲われて1つの文字列になってしまいエラーになります

ここで残された選択肢としては2つ

  1. このlintチェックは無視する
  2. 別の方法を模索する

今回は別方法を模索しました

2. jsonファイルで読み込む(採用)

他にいい方法はないもんかとネットサーフィンしながらいろいろ探してみたのですが
jq使ってゴリゴリにやっているものばかりであまりスマートじゃない うーむと悩んでいる時に
githubを検索していたら面白いissueを発見しました

aws/aws-cli #2828 Allow cloudformation deploy to accept a paramater file github.com

注目すべきは2017年にissueが作成されて色々議論されて末にプルリクエストが2020年に作成されてマージされている点です aws/aws-cli #5443 Add support for cf and codepipeline parameters file to cf deploy github.com

このプルリクエストがマージされたことでAWS CLI v.2.0.39からcloudformation deployでjsonファイルを扱えるようになりました

さて、これでようやくパラメータをjsonで扱うことができるのですが、扱うjsonにはお決まりがあります。
以下がそのサンプルです

development.json
[
    {
        "ParameterKey": "Environment",
        "ParameterValue": "development"
    },
    {
        "ParameterKey": "BaseDomain",
        "ParameterValue": "example.com"
    }
]
または
development.json
[
    "Environment": "development",
    "BaseDomain": "example.com"
]

ParameterKeyとParameterValueを入れたオブジェクトの配列にする必要があります。
ParameterKeyとParameterValue以外の文字列を入れた場合は例外が発生しますので注意が必要です

以下がエラー詳細です

JSON passed to --parameter-overrides must be one of the formats: ["Key1=Value1","Key2=Value2", ...] , [{"ParameterKey": "Key1", "ParameterValue": "Value1"}, ...] , ["Parameters": {"Key1": "Value1", "Key2": "Value2", ...}]

jsonを単純にcatで呼び出した場合シングルクウォートで囲まれてしまう問題が再発してしまいます file://でファイルを直接指定できるそうなのでそちらで指定すればダブルクウォートの問題も解決できます

$ aws cloudformation deploy \
    --stack-name "example-stack" \
    --template-file packaged.yaml \
    --capabilities CAPABILITY_NAMED_IAM CAPABILITY_AUTO_EXPAND \
    --parameter-overrides "file://development.json"

やったね!

\( ‘ω')/ウオオオオオアアアーーーッ!

最後に

ブログを書いている最中に気づいてしまったのですが

当初下記URLのドキュメントを読んで書いていました
https://docs.aws.amazon.com/cli/latest/reference/cloudformation/deploy/index.html

AWS CLIのドキュメントを読み慣れている方はすでにわかってらっしゃると思いますが、上記のURLはAWS CLI v1のものでした(latestってついてるじゃん!)

最新のaws cli v2のURLは下記URLになります
https://awscli.amazonaws.com/v2/documentation/api/latest/reference/cloudformation/deploy/index.html

最新の方には

--parameter-overrides (string) A list of parameter structures that specify input parameters for your stack template. (省略)  
 or JSON file (see Examples)

しっかりJSON file と書いてありました(Exampleも綺麗でした)

公式ドキュメントを読む時はCLIのバージョンをしっかり確認しようと思いました。


明日はフラー株式会社 Advent Calendar 2021 23日目 @m-coder さんで Chromebookでも使えるアプリ です お楽しみに〜