便利なCloudFormation拡張機能

VSCodeを使ってCloudFormationの開発環境を構築する機会がありました。
導入して効果的だったVSCode拡張機能について紹介します。

紹介するのは次の2つです。

  • CloudFormation
  • cfn-lint

CloudFormation

CloudFormationのテンプレートを効率よく作成するためのツールです。
https://marketplace.visualstudio.com/items?itemName=aws-scripting-guy.cform

テンプレートには含まなければいけない必須のフィールドも存在します。

CloudFormationを初めて書くような時、公式ドキュメントを見ながらそれぞれのフィールドを作っていくことになり、地味に面倒です。

また、不慣れなうちはフォーマットエラーを起こしてしまうこともしばしばあります。

この拡張機能を使うと、一瞬でCloudFormation用の空のテンプレートを作成でき、作成時のエラーを減らすことができます。

使い方は簡単で、空のyamlファイル(jsonでも可)を作成し、「start」と入力してtabキーを押すと空のテンプレートを作成できます。

さらに、リソースごとにも細かく必須フィールドの指定をしなければなりませんが、リソース別のフォーマット作成も簡単に行うことができます。

例えば、「ec2-instance」と入力してtabキーを押すと、次のような必須のフィールド等を含んだ空のテンプレートを作成してくれます。

拡張機能を使う際の注意点として、yaml形式でテンプレートで関数の短縮記法の関数(!Ref 等)を使用すると構文エラーとして認識されてしまいます。

これを防ぐため、VScodeの設定ファイル(settings.json)に次のような記述を追加する必要があります。テンプレートとして使用される短縮記法の関数を指定します。この時、文字列やブーリアンなどではなく、配列やオブジェクトを指定する必要がある関数に対しては、「sequence」や「mapping」といったタイプを指定する必要があります。詳細はこちらを参照ください。

   // Custom tags for the parser to use
   "yaml.customTags": [
       "!And",
       "!And sequence",
       "!If",
       "!If sequence",
       "!Not",
       "!Not sequence",
       "!Equals",
       "!Equals sequence",
       "!Or",
       "!Or sequence",
       "!FindInMap sequence",
       "!Base64",
       "!Base64 mapping",
       "!Cidr",
       "!Cidr sequence",
       "!Ref",
       "!Sub",
       "!GetAtt",
       "!GetAZs",
       "!ImportValue",
       "!Select",
       "!Select sequence",
       "!Split",
       "!Split sequence",
       "!Join sequence"
   ],
   // Enable/disable default YAML formatter (requires restart)
   "yaml.format.enable": true,

CloudFormation Linter

CloudFormationのテンプレート解析ツールです。
https://marketplace.visualstudio.com/items?itemName=kddejong.vscode-cfn-lint

テンプレートを書いている最中は書き方に誤りがあっても気が付かず、いざデプロイをしてみて初めて間違っていることに気が付くことはよくあります。

そのような煩わしさを解消してくれる便利なツールです。

例えば、以下の誤りを含むテンプレートを考えます。

# cfnlintdemo/templates/bad-routeable-association.yaml
---
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Bad SubnetRouteTableAssociation'

Parameters:
 PublicRouteTable:
   Type: String

 PrivateRouteTable:
   Type: String

 PublicSubnet01:
   Type: String

 PrivateSubnet01:
   Type: String


Resources:
 PublicSubnetRouteTableAssociation1:
     Type: AWS::EC2::SubnetRouteTableAssociation
     Properties:
       RouteTableId: {Ref: PublicRouteTable}
       SubnetId: {Ref: PublicSubnet01}

 PrivateSubnetRouteTableAssociation1:
     Type: AWS::EC2::SubnetRouteTableAssociation
     Properties:
       RouteTableId: {Ref: PrivateRouteTable}
       SubnetId: {Ref: PublicSubnet01}

ぱっと見ではどのあたりが間違っているかがわかりにくいですが、cfn-lintの拡張機能をインストールすると、誤りがある箇所を赤い波線で示してくれます。

誤りの箇所にマウスカーソルを合わせると、エラーの原因が表示されます。
表示されたエラーは次のような内容でした。

[cfn-lint] E3022: SubnetId in PublicSubnetRouteTableAssociation1 is also associated with PrivateSubnetRouteTableAssociation1

このとおり、別のルートテーブルもこのサブネットに紐づけられていることが原因のエラーだとすぐにわかりました。

また、黄色の波線がPrivateSubnet1についています。これは、対象のパラメータがどこからも参照されていないことを示す警告です。

本来であれば、PrivateSubnet RouteTableAssociation1にこのサブネットが紐付けられるべきだったわけですね。

cfn-lintにはテンプレートの参照関係をグラフで表示してくれる機能もあります。(※pydotのインストールが別途必要)
EC2インスタンス用のテンプレートサンプルで作成し、グラフ化するとこんな感じになります。

グラフの元になったテンプレートはこちら。

AWSTemplateFormatVersion: '2010-09-09' 

Parameters:
 EC2ImageId:
   Type: String
   Description: "Specifies the AMI ID for your instances."

 AllowIngressCidrIP:
   Type: String
 Resources:
 # IAM Role for EC2
 TestIAMRole:
   Type: AWS::IAM::Role
   Properties:
     RoleName: Test-IAM-Role
     AssumeRolePolicyDocument:
       Version: '2012-10-17'
       Statement:
         - Effect: Allow
           Principal:
             Service: ec2.amazonaws.com
           Action: sts:AssumeRole
     ManagedPolicyArns:
       - arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess
       - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore

 # IAM InstanceProfile for EC2
 TestIAMInstanceProfile:
   Type: "AWS::IAM::InstanceProfile"
   Properties:
     Roles:
       -
         !Ref TestIAMRole

 ## EC2 Security Group
 TestEC2SecurityGroup:
   Type: AWS::EC2::SecurityGroup
   Properties:
     GroupName: TestEC2SecurityGroup
     GroupDescription: TestEC2SecurityGroup
     VpcId:
       Fn::ImportValue: TestVPC
     Tags:
       - Key: Name
         Value: TestEC2SecurityGroup

 ## EC2 Security Group Ingress
 TestEC2SecurityGroupIngress:
   Type: AWS::EC2::SecurityGroupIngress
   Properties:
     GroupId: !Ref TestEC2SecurityGroup
     IpProtocol: tcp
     FromPort: 8080
     ToPort: 8080
     CidrIp: !Ref AllowIngressCidrIP

 ## EC2 Launch Template
 TestLaunchTemplate:
   Type: AWS::EC2::LaunchTemplate
   Properties:
     LaunchTemplateName: TestLaunchTemplate
     LaunchTemplateData:
       InstanceType: t2.micro
       IamInstanceProfile:
         Name: !Ref TestIAMInstanceProfile
       ImageId: !Ref EC2ImageId
       NetworkInterfaces:
         - DeviceIndex: 0
           AssociatePublicIpAddress: true
           SubnetId: !ImportValue TestVPCSubnet
           Groups:
             - !Ref TestEC2SecurityGroup
       BlockDeviceMappings:
         - DeviceName: /dev/xvda
           Ebs:
             VolumeSize: 8
             VolumeType: gp3

 ## EC2 Instance
 TestEC2Instance:
   Type: AWS::EC2::Instance
   Properties:
     LaunchTemplate:
       LaunchTemplateId: !Ref TestLaunchTemplate
       Version: !GetAtt TestLaunchTemplate.LatestVersionNumber
     Tags:
       - Key: Name
         Value: TestEC2Instance

他の人が作ったテンプレートを解析する場合などで役立つ機能かもしれません。

まとめ

CloudFormationのテンプレートを作成する時に役立つVScode拡張機能を2つ紹介しました。
機会があれば試してみてください。

コメント

タイトルとURLをコピーしました