インフラ

[AWS Cloudformation]テンプレートを再利用して本番と検証環境を作る

cloudformation

Cloudformationのベストプラクティス

テンプレートを再利用して複数の環境にスタックを複製する

というのがあります。
やり方わからなかったため、調べてみました。
今回は、「テンプレートを再利用する方法」をまとめてみます。

Contents

なぜ再利用できるようにしたいのか

クロススタック参照でテンプレートを分けていると、テンプレートファイルはどうしても複数になってしまいます。
さらに、検証と本番でテンプレートファイルを分けてしまうとテンプレートファイルの数が倍になってしまいます。
テンプレートファイルの数が増えると、ファイルの管理が複雑になりミスが発生するかもしれません。

だからといって、本番と検証を同じテンプレートファイルにするとどちら片方削除したい・変更したい時に不便です。

そういった理由から、テンプレートは再利用できる形にするのがおすすめです。

再利用できないテンプレート

まずは、再利用できないテンプレートです。
このテンプレートでは、VPCとSecurityGroupを作成しています。

  • VPC・SecurityGroup(修正前)

再利用できない理由

こちらのテンプレートでは本番と検証で変えるべきパラメータを、ベタがきしてしまっています。
これでは、本番環境を作った後に検証環境を作ろうとするとベタがきした部分を書き換なければいけません。

修正版 再利用できるテンプレート

再利用できるようにしたテンプレートです。

  • VPC・SecurityGroup(修正後)

再利用するためのポイント

変えた点です。

  • Parametersで作成時に環境名を指定できるようにする
  • Mappingで本番環境と検証環境をそれぞれ定義しておく
  • Outputsで文字列結合した値を渡す

それぞれ詳しく見ていきます。

Parametersで作成時に環境名を指定できるようにする

ユーザガイドから抜粋

オプションの Parameters セクションを使用して、テンプレートをカスタマイズします。パラメーターを使用すると、スタックを作成または更新するたびにテンプレートにカスタム値を入力できます。

Parametersでは、Cloudformationを実行する際に値を渡すことができます。
今回のテンプレートでは、システム名(任意の文字)と環境名(prod・test)を渡しています。

スタック名もシステム名+環境名で名前をつけておくと、わかりやすいと思います。
例)システム名+環境名+リソース(Network,EC2,SecurityGroup等)

Parameters:
  ProjectId:
    Description: "Project name id."
    Type: String
    AllowedPattern: '^[a-zA-Z0-9-/:-@\[-\`\{-\~]+$'
    ConstraintDescription: "InvalidValue[ProjectId]"
    Default: cfn
  EnvironmentType:
      Description: The environment type
      Type: String
      Default: test
      AllowedValues:
        - prod
        - test
      ConstraintDescription: must be a prod or test

Mappingsで本番環境と検証環境をそれぞれ定義しておく

ユーザガイドから抜粋

任意の Mappings セクションでは、キーと名前付きの一連の値とが対応付けられます。たとえば、リージョンに基づく値を設定する場合、リージョン名をキーとして必要な値を保持するマッピングを作成します。具体的なリージョンごとに必要な値を指定します。マップ内の値を取得するには、Fn::FindInMap 組み込み関数を使用します。

Mappingsでは、keyとValueで値を設定できます。
今回は検証と本番でIPアドレス等を変えるために、Mappingsを作っています。また、使用用途としては、AMIの指定に使われることも多いです。(リージョンごとにAMIIDが違うため)

Fn::FindInMap(Mappingから値を取得する関数)で、Ref関数が使用できるためParametersで指定した値(prod・test)が使われます。

CidrBlock: !FindInMap [ !Ref EnvironmentType, Subnet, PublicSubnet1 ]

Outputsで文字列結合した値を渡す

ユーザガイドから抜粋

オプションの Outputs セクションは 他のスタックにインポートする (クロススタック参照を作成)、応答として返す (スタック呼び出しについて記述)、または、AWS CloudFormation コンソールで表示する出力値を宣言します。たとえば、見つけやすいバケットを作成するスタックの S3 バケット名を出力できます。

他のスタックに渡したい値を、Outputsセクションに記載します。
今回は、VPCのスタックで、VPCID・SubnetIDを渡しています。渡す際の名前では、Sub関数で文字列結合をして使いまわした際に値が被らないようにしています。

Outputs:
  MyVPC:
    Value: !Ref VPC
    Export:
      Name: !Sub "${ProjectId}-${EnvironmentType}-vpc"

受け取る側は、ImportValue関数を使用します。
Sub関数など関数を使う際は、ImportValueの短縮記法(!ImportValue)を使えませんので以下のように書きます。

VpcId: {"Fn::ImportValue": !Sub "${ProjectId}-${EnvironmentType}-vpc"}