【AWS CloudFormation/AWS CLI】複数のテンプレートで定義したParametersの値を1つのファイルで管理する方法

記事タイトルとURLをコピーする

こんにちは。AWS CLIが好きな福島です。

はじめに

今回は、AWS CLIを使い、CloudFormationの複数のテンプレートで定義したParametersの値を1つのファイルで管理する方法をご紹介いたします。

AWS CLIを使ったCloudformationの操作については、以下にまとめているため、興味がありましたら、ご確認ください。

https://blog.serverworks.co.jp/2020/08/14/190000

概要図

例として、VPCとSubnetを作るケースで概要図を記載いたしました。
※VPCとSubnetは同一テンプレートで作るケースが多いかと存じますが、説明のために分割しました。

Before

赤字の箇所が重複しておりますが、CloudFormationでパラメーターをファイル(以降、パラメーターファイルと記載)で管理するためには、 テンプレートで定義したParametersのみをファイルに記載する必要があります。

Parametersの値が全て同一のテンプレートというのは、ほとんどのないかと存じますため、 基本的にパラメーターファイルは、テンプレートごとに作成する必要が出てきてしまいますが、そうなると管理が煩雑になるかと存じます。

After

上記図のようにパラメーターを1つのファイルで管理できれば、Beforeと比べると管理が楽になるのではないでしょうか。 ということでその方法をここから説明いたします。

結論

冒頭でも記載しましたが、パラメーターファイルには、テンプレートで定義したParametersのみを記載する必要があるため、 パラメーターをまとめたファイルからスクリプトによってテンプレートで使う値のみを抽出し、スタックの作成を行います。

使い方

内容を細かく説明する前にスクリプトの使い方を記載した方がいいかなと思ったため、先に使用方法を記載いたします。

# ./create-stack.sh [テンプレート] [パラメーターファイル]

実行例

VPC

# ./create-stack.sh serverworks-vpc.yml prd-fk-param.json 
以下のパラメーターでスタックを作成します。問題ない場合、yesと入力してください。"
##############################################################

CFN_STACK_NAME : prd-fk-serverworks-vpc

## PARAMETERS
EnvironmentName  :  prd
OwnerName        :  Serverworks
SystemName       :  fk
VpcCIDR          :  10.0.0.0/16

## 実行コマンド
aws cloudformation create-stack
--stack-name prd-fk-serverworks-vpc
--capabilities CAPABILITY_NAMED_IAM
--template-body file://serverworks-vpc.yml
--parameters ParameterKey=EnvironmentName,ParameterValue=prd ParameterKey=OwnerName,ParameterValue=Serverworks ParameterKey=SystemName,ParameterValue=fk ParameterKey=VpcCIDR,ParameterValue=10.0.0.0/16

##############################################################
yes
{
    "StackId": "arn:aws:cloudformation:ap-northeast-1:123456789012:stack/prd-fk-serverworks-vpc/fd6159d0-0951-11ed-bfd7-0650087e107d"
}
#  

Subnet

# ./create-stack.sh serverworks-subnet.yml prd-fk-param.json
以下のパラメーターでスタックを作成します。問題ない場合、yesと入力してください。"
##############################################################

CFN_STACK_NAME : prd-fk-serverworks-subnet

## PARAMETERS
EnvironmentName    :  prd
ImportStackName    :  prd-fk-serverworks-vpc
OwnerName          :  Serverworks
PublicSubnet1CIDR  :  10.0.1.0/24
PublicSubnet2CIDR  :  10.0.2.0/24
SystemName         :  fk

## 実行コマンド
aws cloudformation create-stack
--stack-name prd-fk-serverworks-subnet
--capabilities CAPABILITY_NAMED_IAM
--template-body file://serverworks-subnet.yml
--parameters ParameterKey=EnvironmentName,ParameterValue=prd ParameterKey=ImportStackName,ParameterValue=prd-fk-serverworks-vpc ParameterKey=OwnerName,ParameterValue=Serverworks ParameterKey=PublicSubnet1CIDR,ParameterValue=10.0.1.0/24 ParameterKey=PublicSubnet2CIDR,ParameterValue=10.0.2.0/24 ParameterKey=SystemName,ParameterValue=fk

##############################################################
yes
{
    "StackId": "arn:aws:cloudformation:ap-northeast-1:123456789012:stack/prd-fk-serverworks-subnet/960d5da0-0952-11ed-ac36-0eec3649f2d5"
}
#  

詳細

ファイル構成

  • ①prd-fk-param.json: パラメーターファイル
    ※スクリプトの都合、末尾は「-pamarm.json」とする必要があります。
  • ②serverworks-vpc.yml: VPC作成のテンプレート
  • ③serverworks-subnet.yml: サブネット作成のテンプレート
  • ④create-stack.sh: 上記スクリプト

①prd-fk-param.json

[
  {
    "ParameterKey": "SystemName",
    "ParameterValue": "fk"
  },
  {
    "ParameterKey": "OwnerName",
    "ParameterValue": "Serverworks"
  },
  {
    "ParameterKey": "EnvironmentName",
    "ParameterValue": "prd"
  },
  {
    "ParameterKey": "VpcCIDR",
    "ParameterValue": "10.0.0.0/16"
  },
  {
    "ParameterKey": "ImportStackName",
    "ParameterValue": "prd-fk-serverworks-vpc"
  },
  {
    "ParameterKey": "PublicSubnet1CIDR",
    "ParameterValue": "10.0.1.0/24"
  },
  {
    "ParameterKey": "PublicSubnet2CIDR",
    "ParameterValue": "10.0.2.0/24"
  }
]

②serverworks-vpc.yml

AWSTemplateFormatVersion: 2010-09-09
Description:  This template deploys a VPC

Parameters:
  SystemName:
    Description: Please specify the system name of the resource
    Type: String

  OwnerName:
    Description: Please specify the owner name of the resource
    Type: String

  EnvironmentName:
    Description: Please specify the environment name of the resource
    Type: String
    AllowedValues:
      - prd
      - stg
      - dev
      - poc

  VpcCIDR:
    Description: Please enter the IP range (CIDR notation) for this VPC
    Type: String

Resources:
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref VpcCIDR
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
        - Key: Name
          Value: !Sub ${SystemName}-${OwnerName}-${EnvironmentName}-vpc
        - Key: System
          Value: !Sub ${SystemName}
        - Key: Owner
          Value: !Sub ${OwnerName}
        - Key: Environment
          Value: !Sub ${EnvironmentName}

Outputs:
  VPC:
    Description: A reference to the created VPC
    Value: !Ref VPC
    Export:
      Name:
        Fn::Sub: ${AWS::StackName}-VPC

③serverworks-subnet.yml

AWSTemplateFormatVersion: 2010-09-09
Description:  This template deploys a VPC

Parameters:
  SystemName:
    Description: Please specify the system name of the resource
    Type: String

  OwnerName:
    Description: Please specify the owner name of the resource
    Type: String

  EnvironmentName:
    Description: Please specify the environment name of the resource
    Type: String
    AllowedValues:
      - prd
      - stg
      - dev
      - poc

  ImportStackName:
    Description: Please specify the stack name you want to import
    Type: String

  PublicSubnet1CIDR:
    Description: Please enter the IP range (CIDR notation) for the public subnet in the first Availability Zone
    Type: String

  PublicSubnet2CIDR:
    Description: Please enter the IP range (CIDR notation) for the public subnet in the second Availability Zone
    Type: String

Resources:
  PublicSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId:
        Fn::ImportValue:
          !Sub "${ImportStackName}-VPC"
      AvailabilityZone: ap-northeast-1a
      CidrBlock: !Ref PublicSubnet1CIDR
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: !Sub ${SystemName}-${OwnerName}-${EnvironmentName}-public-subnet-1a-001
        - Key: System
          Value: !Sub ${SystemName}
        - Key: Owner
          Value: !Sub ${OwnerName}
        - Key: Environment
          Value: !Sub ${EnvironmentName}

  PublicSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId:
        Fn::ImportValue:
          !Sub "${ImportStackName}-VPC"
      AvailabilityZone: ap-northeast-1c
      CidrBlock: !Ref PublicSubnet2CIDR
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: !Sub ${SystemName}-${OwnerName}-${EnvironmentName}-public-subnet-1c-001
        - Key: System
          Value: !Sub ${SystemName}
        - Key: Owner
          Value: !Sub ${OwnerName}
        - Key: Environment
          Value: !Sub ${EnvironmentName}

Outputs:
  PublicSubnet1:
    Description: A reference to the public subnet in the 1st Availability Zone
    Value: !Ref PublicSubnet1
    Export:
      Name:
        Fn::Sub: ${AWS::StackName}-PublicSubnet1
  PublicSubnet2:
    Description: A reference to the public subnet in the 2nd Availability Zone
    Value: !Ref PublicSubnet2
    Export:
      Name:
        Fn::Sub: ${AWS::StackName}-PublicSubnet2

④create-stack.sh

スクリプトを使うためには、AWS CLIおよびjqが導入されている必要があります。

#!/bin/bash

## 1. スクリプトの引数に指定されたテンプレートとパラメーターファイルを変数に定義
CFN_TEMP=${1}
CFN_PARAM=${2}

## 2. スタックの名前をパラメーターファイルおよびテンプレートファイル名から抽出
CFN_STACK_NAME=$(echo ${CFN_PARAM}-${CFN_TEMP##*/} | sed 's/-param.json//g' | awk -F. '{print $1}')

## 3. 変数(引数)がなかった場合、メッセージを出力し、処理を終了
if [[ -z ${CFN_TEMP} || -z ${CFN_PARAM} ]] ; then
   echo "第1引数: 使用するテンプレートを指定ください。"
   echo "第2引数: 使用するパラメーターを指定ください。"
   exit 9
fi

## 5. 4で抽出した値をkey変数に1つずつ代入
while read key
do

   ## 6. jqコマンドを使い、key変数に定義された情報を基にパラメーターファイルから必要な情報を抽出
   temp_value=$(cat ${CFN_PARAM} \
   | jq --arg key $key -r '.[] | select(.ParameterKey == $key) | .ParameterValue')

   ## 7. 6で抽出した情報をparameters変数に代入
   parameters+="ParameterKey=$key,ParameterValue=$temp_value "

## 4. テンプレートに定義したParametersのParameterKeyを1つずつ抽出
done << EOL
$(aws cloudformation validate-template \
--template-body file://${CFN_TEMP} \
--query "Parameters[].[ParameterKey]" \
--output text | sort)
EOL

## 8. 抽出したパラメーター,スタック名および実行コマンドを出力
cat << EOF
以下のパラメーターでスタックを作成します。問題ない場合、yesと入力してください。"
##############################################################

CFN_STACK_NAME : ${CFN_STACK_NAME}

## PARAMETERS
$(echo ${parameters} | tr " " "\n" \
| sed -e 's/ParameterKey=//g' -e 's/,ParameterValue=/ : /g' \
| column -t)

## 実行コマンド
aws cloudformation create-stack
--stack-name ${CFN_STACK_NAME}
--capabilities CAPABILITY_NAMED_IAM 
--template-body file://${CFN_TEMP} 
--parameters ${parameters}

##############################################################
EOF

## 9. 8の内容に問題ないか確認するため、ユーザーから入力を待つ。
read answer

## 10. ユーザーからyesが入力された場合、スタックを作成
if [[ ${answer} == "yes" ]] ; then
   aws cloudformation create-stack \
   --stack-name ${CFN_STACK_NAME} \
   --capabilities CAPABILITY_NAMED_IAM \
   --template-body file://${CFN_TEMP} \
   --parameters ${parameters}
## 11. yes以外が入力された場合、処理を中断
else
   echo "処理を中断しました。"
   exit 9
fi

終わりに

今回は複数のテンプレートで定義したParametersの値を1つのファイルで管理する方法をご紹介いたしました。 もう少し簡単に出来れば良かったのですが、これを実現するためにはスクリプトを作らざるを得なそうでした。

どなたかのお役に立てれば幸いです。

福島 和弥 (記事一覧)

2019/10 入社

AWS CLIが好きです。