4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

CloudFormationをゼロから勉強する。(その6:Transformによるインクルード)

Last updated at Posted at 2020-10-03

はじめに

今回はまだ試していなかったTransformのうち、他テンプレートのインクルード機能を使ってみようと思います。

Transformセクションについて

CloudFormationからLambda関数を呼び出す場合や、他のテンプレートをインクルードする場合に使用します。

今回使う他テンプレートのインクルードは、テンプレート内のどこでも呼び出すことができ、呼び出す位置によって若干構文が変わってきます。

ただ、インクルードで使う場合はトップレベルで使う場合でもFn::Transformだけ覚えておけばよいと思います。(理由は後述)

Transformサンプル(トップレベルで使用する場合の構文)
Transform:
  Name: 'AWS::Include'
  Parameters:
    Location: 's3://MyAmazonS3BucketName/MyFileName.yaml'
Transformサンプル(他セクションで使用する場合の構文)
Fn::Transform:
  Name: 'AWS::Include'
  Parameters:
    Location: 's3://MyAmazonS3BucketName/MyFileName.yaml'

Transform設定があるテンプレートの実行方法

Transformを含むテンプレートは今までのようにaws cloudformation create-stackコマンドでは実行できず、aws cloudformation packageコマンドでS3のバケットにインクルードするファイルをアップロードする必要があります。

aws cloudformation packageコマンドでアップロード後は、aws cloudformation deployコマンドでスタックを作成・実行します。

create-stackdeployもどちらもスタックを作成できますが、変換を伴うテンプレートの実行(今回の場合、TransformによるLocationの変換)を行う場合はdeployで行う必要があるようです。

Transformでインクルードする際の注意点

メインのテンプレートから呼び出すファイルですが、基本的にはコードの一部分をカット&ペーストして別ファイルとしてインクルードすることができますがいくつか注意しなければいけない点があるので以下記載します。

1.インクルードファイルの書式

インクルードファイルでは!Refなどの組み込み関数や!FindInMapのような短縮形の記載は使えないようで、Ref:Fn::FindInMap:のような記載方法に書き直す必要があります。

また、{}[]を使った記載もフォーマットエラーとなります。

YAMLの書式についてあまり詳しくありませんが、いわゆるブロックスタイルの書式でないとインクルードファイルは読み込んでくれないようです。

そのため、上記の書式が使われている部分を抜き出してインクルードファイルとする場合は、別の記載方法に変換する必要があります。

2.同じインデントレベルでの複数インクルードファイルの指定

同じ構造内で同じインデントレベルでの複数Transform定義の指定はできません。

例えば、ResourcesセクションでEC2インスタンスの定義とサブネットの定義をTransformでインクルードしようとしても最後に記載したインクルードファイルの定義しか反映されません。

また、Locationに2つ書いた場合は、実行時に失敗します。

そのため、同じインデントレベルで複数インクルードする場合は、1つのファイルにまとめるか、構成を変更する必要があります。

最後のインクルードのみ実行
Resources:
  Fn::Transform:  ★実行されない
    Name: AWS::Include
    Parameters:
      Location:
        - s3://[S3バケット名]/c102fec7b84d7dc46b57e4ea6c3ecc85
  Fn::Transform:  ★実行される
    Name: AWS::Include
    Parameters:
      Location:
        - s3://[S3バケット名]/1a887907495d757aa66496d127a9d9ae
実行不可
Resources:
  Fn::Transform:  ★以下の書き方はエラーで失敗
    Name: AWS::Include
    Parameters:
      Location:
        - s3://[S3バケット名]/c102fec7b84d7dc46b57e4ea6c3ecc85
        - s3://[S3バケット名]/1a887907495d757aa66496d127a9d9ae

3.トップレベルでTransform指定した場合の変換

Transformでインクルードファイルを指定した場合、前述のpackageコマンドを実行してインクルードファイルをアップロードすると、Locationのファイル名がアップロード先のS3URLに変換されます。

但し、トップレベルでの記載方法でTransformを設定すると、packageコマンドでLocation部分が変換されないようです。

Fn::Transformの記載でもトップレベルで設定できるので、インクルードでTransformを使う場合はFn::Transformだけ使えばよいかと思います。

Transformの記載でpackageコマンド実行した場合の例
AWSTemplateFormatVersion: 2010-09-09
Transform:
  Name: AWS::Include
  Parameters:
    Location: description.yaml  ★変換されない
Fn::Transformの記載でpackageコマンド実行した場合の例
AWSTemplateFormatVersion: 2010-09-09
Fn::Transform:
  Name: AWS::Include
  Parameters:
    Location: s3://[S3バケット名]/b7a2955ca33cb3908c36b49284fd487a  ★変換された

今回作成する構成

今回も今までと同様、前回のテンプレートを基に作成していきます。

前回からの追加要素として以下を追加します。

  1. デザイナー画面からのパラメータ入力項目を分かりやすく整える。
  2. EC2インスタンスの記述をTransformで別ファイルからインクルードする。

パラメータ入力画面の整理

大体コマンドで実行しているのであまり関係ないのですが、Metadataセクションにパラメータ入力の順番等を記載することでパラメータ入力画面の項目を整えることができるようなので実装してみようと思います。

MetaDataセクションにAWS::CloudFormation::Interfaceを設定することで画面整理できます。

MetadataKeyによるパラメータ入力項目整理
Metadata:
  'AWS::CloudFormation::Interface':
    ParameterGroups:
      - Label:
          default: "Network Configuration"
        Parameters:
          - VPCRange
          - SubnetRange
      - Label:
          default: "EC2 Configuration"
        Parameters:
          - EnvType
    ParameterLabels:
      VPCRange:
        default: "Input VPC Range"
      SubnetRange:
        default: "Input Subnet Range"
      EnvType:
        default: "Select Environment"
  • MetaData記載なし
    default.jpg
  • MetaData記載あり
    metadatakey.jpg

尚、上記のParameterLabelsは入力しないとMetadata記載なしの場合と同様、Parametersセクションの項目名が表示されます。

今まで入力項目の順番がバラバラになるのが気になっていましたが、これで運用者がGUI画面上から入力する場合でも入力間違いが減らせそうです。

インクルードファイルの作成

前回のテンプレートからEC2インスタンス部分をカット&ペーストします。

ただ、前述の通り、{}[]などは使用できません。

そのため、短縮系や{}等使われている箇所を変換していきます。

Ref短縮形の変換

前回のテンプレートでは、!Ref!FinfInMapを短縮形で使用しているため、通常の構文で記載した場合は以下となります。(!FindInMapは後述)

変換前の構文
SubnetId: !Ref EC2SJOWQ
変換後の構文
SubnetId:
  - Ref: EC2SJOWQ

括弧とFindInMap短縮形の変換

前回のテンプレートではKeyNameの指定で括弧や短縮形を多用しています。

括弧や短縮形が多くて変換が難しいですが、括弧の部分をそれぞれ1つのグループとして、グループ内は同列の要素として変換した結果が以下となります。

変換前の構文
KeyName: {Fn::If: [EnvSelect, !FindInMap [ Keypair, productionkey, KEYPAIR ], !FindInMap [ Keypair, stagingkey, KEYPAIR ]]}
変換後の構文
KeyName:
  Fn::If:
    - EnvSelect
    - Fn::FindInMap:
      - Keypair
      - productionkey
      - KEYPAIR
    - Fn::FindInMap:
      - Keypair
      - stagingkey
      - KEYPAIR

完成したインクルードファイル

以下の内容をec2.yamlとしてローカルに保存しておきます。

ブロックスタイルに変換後のインクルードファイル(展開して下さい)
ブロックスタイルに変換後のテンプレートファイル
EC2I3Q8S8:
  Type: 'AWS::EC2::Instance'
  Properties:
    ImageId: ami-0cc75a8978fbbc969
    InstanceType: t2.micro
    KeyName:
      Fn::If:
        - EnvSelect
        - Fn::FindInMap:
          - Keypair
          - productionkey
          - KEYPAIR
        - Fn::FindInMap:
          - Keypair
          - stagingkey
          - KEYPAIR
    NetworkInterfaces:
      - AssociatePublicIpAddress: "true"
        SubnetId:
          Ref: EC2SJOWQ
        DeviceIndex: 0
EC2Instance:
  Type: 'AWS::EC2::Instance'
  Properties:
    ImageId: ami-0cc75a8978fbbc969
    InstanceType: t2.micro
    KeyName:
      Fn::If:
        - EnvSelect
        - Fn::FindInMap:
          - Keypair
          - productionkey
          - KEYPAIR
        - Fn::FindInMap:
          - Keypair
          - stagingkey
          - KEYPAIR
    NetworkInterfaces:
      - SubnetId:
          Ref: EC2SJOWQ
        DeviceIndex: 0
  Condition: EnvSelect

テンプレートファイルの修正

先ほどカット&ペーストで切り出したEC2インスタンスのリソースが記載されていた箇所にTransformを記載します。

Transformの記載(抜粋)
Resources:
  Fn::Transform:
    Name: AWS::Include
    Parameters:
      Location: "ec2.yaml"

完成したテンプレートの変換

完成したテンプレートファイルは以下となります。

この時点ではIncludeLocationで指定しているファイル名はローカルで作成したファイル名(ec2.yaml)で問題ありません。

完成したテンプレートファイル(展開して下さい)
/home/ec2-user/cloudformation.yaml
AWSTemplateFormatVersion: 2010-09-09
Metadata:
  'AWS::CloudFormation::Interface':
    ParameterGroups:
      - Label:
          default: "Network Configuration"
        Parameters:
          - VPCRange
          - SubnetRange
      - Label:
          default: "EC2 Configuration"
        Parameters:
          - EnvType
    ParameterLabels:
      VPCRange:
        default: "Input VPC Range"
      SubnetRange:
        default: "Input Subnet Range"
      EnvType:
        default: "Select Environment"
  'AWS::CloudFormation::Designer':
    e1390660-013a-4523-9893-ccd6074e430f:
      size:
        width: 190
        height: 190
      position:
        x: 390
        'y': 90
      z: 0
      embeds:
        - e6cc612e-5a0f-459b-8329-3a5852613031
    e6cc612e-5a0f-459b-8329-3a5852613031:
      size:
        width: 140
        height: 140
      position:
        x: 410
        'y': 130
      z: 1
      parent: e1390660-013a-4523-9893-ccd6074e430f
      iscontainedinside:
        - e1390660-013a-4523-9893-ccd6074e430f
Resources:
  EC2VPC1WR8B:
    Type: 'AWS::EC2::VPC'
    Properties:
      CidrBlock: !Ref VPCRange
    Metadata:
      'AWS::CloudFormation::Designer':
        id: e1390660-013a-4523-9893-ccd6074e430f
  EC2SJOWQ:
    Type: 'AWS::EC2::Subnet'
    Properties:
      VpcId: !Ref EC2VPC1WR8B
      CidrBlock: !Ref SubnetRange
    Metadata:
      'AWS::CloudFormation::Designer':
        id: e6cc612e-5a0f-459b-8329-3a5852613031
  Fn::Transform:
    Name: AWS::Include
    Parameters:
      Location: "ec2.yaml"
Parameters:
  VPCRange:
    Type: String
    Description: "VPC Subnet Range"
  SubnetRange:
    Type: String
    Description: "Subnet Range"
  EnvType:
    Type: String
    Description: "Production or Staging"
    AllowedValues: ["Production", "Staging"]
Outputs:
  VPCRegion:
    Description: "Region Name"
    Value: !Ref AWS::Region
Mappings:
  Keypair:
    productionkey:
      "KEYPAIR": "production_key"
    stagingkey:
      "KEYPAIR": "staging_key"
Conditions:
  EnvSelect: {"Fn::Equals": [{"Ref" : "EnvType"}, "Production"]}

以下packageコマンドを実行することで、テンプレートファイルの変換とインクルードファイルのS3バケットへのアップロードが行われます。

また、テンプレートファイルの変換は、テンプレートファイルとして指定したファイルが直接変換されるのではなく、--output-template-fileで指定したファイルに変換後の内容が出力されます。(今回は出力後のテンプレートファイルをcf.yamlとします。)

尚、インクルードファイルがアップロードされるS3バケットは予め作成しておいてください。

packageコマンドによる変換
aws cloudformation package --template-file cloudformation.yaml --s3-bucket [S3バケット名] --output-template-file cf.yaml

完成したテンプレートの実行

変換後のテンプレートファイルは以下となります。

TransformLocationだけではなく、yamlのブロックスタイルへの変更が行われるようです。

変換されたテンプレートファイル(展開して下さい)
/home/ec2-user/cf.yaml
AWSTemplateFormatVersion: 2010-09-09
Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
    - Label:
        default: Network Configuration
      Parameters:
      - VPCRange
      - SubnetRange
    - Label:
        default: EC2 Configuration
      Parameters:
      - EnvType
    ParameterLabels:
      VPCRange:
        default: Input VPC Range
      SubnetRange:
        default: Input Subnet Range
      EnvType:
        default: Select Environment
  AWS::CloudFormation::Designer:
    e1390660-013a-4523-9893-ccd6074e430f:
      size:
        width: 190
        height: 190
      position:
        x: 390
        y: 90
      z: 0
      embeds:
      - e6cc612e-5a0f-459b-8329-3a5852613031
    e6cc612e-5a0f-459b-8329-3a5852613031:
      size:
        width: 140
        height: 140
      position:
        x: 410
        y: 130
      z: 1
      parent: e1390660-013a-4523-9893-ccd6074e430f
      iscontainedinside:
      - e1390660-013a-4523-9893-ccd6074e430f
Resources:
  EC2VPC1WR8B:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock:
        Ref: VPCRange
    Metadata:
      AWS::CloudFormation::Designer:
        id: e1390660-013a-4523-9893-ccd6074e430f
  EC2SJOWQ:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId:
        Ref: EC2VPC1WR8B
      CidrBlock:
        Ref: SubnetRange
    Metadata:
      AWS::CloudFormation::Designer:
        id: e6cc612e-5a0f-459b-8329-3a5852613031
  Fn::Transform:
    Name: AWS::Include
    Parameters:
      Location: s3://[S3バケット名]/[アップロードされたインクルードファイル名]
Parameters:
  VPCRange:
    Type: String
    Description: VPC Subnet Range
  SubnetRange:
    Type: String
    Description: Subnet Range
  EnvType:
    Type: String
    Description: Production or Staging
    AllowedValues:
    - Production
    - Staging
Outputs:
  VPCRegion:
    Description: Region Name
    Value:
      Ref: AWS::Region
Mappings:
  Keypair:
    productionkey:
      KEYPAIR: production_key
    stagingkey:
      KEYPAIR: staging_key
Conditions:
  EnvSelect:
    Fn::Equals:
    - Ref: EnvType
    - Production

このテンプレートを指定してdeployコマンドでスタックを作成していきますが、deployコマンドはcreate-stackのコマンドのようにパラメータファイルを外部ファイルとして読み込むことができないため、パラメータの入力は--parameter-overridesコマンドで指定します。

deployコマンドによるスタック作成
aws cloudformation deploy --template-file cf.yaml --stack-name stack-test --parameter-overrides SubnetRange=172.24.0.0/24 VPCRange=172.24.0.0/16 EnvType=Production

おわりに

今回はTransformによるファイルのインクルードを試しましたが、色々と制約があるためちょっと使いづらいと感じました。

今回の方法でTransformをバリバリ使うのであれば、普段から短縮形や括弧などを使うフロースタイルでの構文は使わないよう心がければよさそうではありますが、複数のテンプレートを使用するやり方はTransformではなく他の方法がいくつかあるようなので、そもそもTransformを使うべきではないのかもしれません。

次回は変更セットドリフト検出について勉強してみようと思います。

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?