はじめに
この記事は、TDCソフト株式会社 Advent Calendar 2023の13日目です。
業務内でCloudFormationを触る機会があり、その際にかなり苦労してしまったのではまりポイントと解決法をつらつらと書きます。
CloudFormationとは?
ECSやEC2などのAWSリソースをまとめて自動で作成することが出来るサービスです。
json
、yaml
で構成されるリソースファイルに記載されているAWSリソースをまとめて自動作成してくれます。リソース間の依存関係も考慮して作成してくれるため、既存の環境と同じものを作りたい場合に重宝します。
アーキテクチャ
下図の黄色枠内のリソースを作成するCloudFormationを作りました。
フロントはS3、バックエンドはECS経由して別VPCにあるDBにアクセスする感じです。
VPC間の通信はVPCピアリング接続をしています。
また、今回はFormer2を利用してCloudFormationを作成しています。
Former2については以下の記事をご参照ください。
つまづいたポイント
サブネットとインターネットゲートウェイが繋がらない
背景
VPCピアリング接続を行う際に、対象のVPCに対してお互いのルートを双方に追加する必要がある。
問題点
リソースとしてルートテーブルとルートを追加したが、何故かルートテーブルが2つ作られておりサブネット⇔IGWが繋がっていない。
EC2RouteTable:
Type: "AWS::EC2::RouteTable"
Properties:
VpcId: !Ref EC2VPC
EC2Route1a:
Type: "AWS::EC2::Route"
Properties:
DestinationCidrBlock: "0.0.0.0/0"
VpcPeeringConnectionId: !Ref EC2VPCPeeringConnection
RouteTableId: !Ref EC2RouteTable
原因
VPCリソースを新規作成すると、自動でメインルートテーブルが作成されるようです。
そのため、複数のルートテーブルを必要としない場合、ルートテーブルのリソース作成は不要になります。
となると自動作成されたメインルートテーブルにルートを追加すればいいのですが、CloudFormationは自力でメインルートテーブルを参照できません。
CloudFormation を使用して Amazon VPC を作成する場合、CloudFormation はデフォルトで作成されるメインルートテーブルを認識しません。そのため、ルートテーブルに関する情報を Amazon VPC と CloudFormation の間で渡すことはできません。その結果、CloudFormation テンプレートからルートテーブルを参照できないため、メインルートテーブルにルートを追加または削除することはできません。
そのため、ルートを追加したい場合はLambdaを経由してメインルートテーブルのIDを取ってこないといけません。
上記リンク内のテンプレートをそのまま流用して、RouteTableId
でLambdatrigger.RouteTableID
を呼び出せばメインルートテーブルにルートを追加できます、
EC2Route1a:
Type: "AWS::EC2::Route"
Properties:
DestinationCidrBlock: "0.0.0.0/0"
VpcPeeringConnectionId: !Ref EC2VPCPeeringConnection
RouteTableId: !GetAtt Lambdatrigger.RouteTableID
InternetGatewayがアタッチできない
原因
Gateway Attachmentが記述されていない。
EC2VPCGatewayAttachment:
Type: "AWS::EC2::VPCGatewayAttachment"
Properties:
InternetGatewayId: !Ref EC2InternetGateway
VpcId: !Ref EC2VPC
これによってVPCとInternetGatewayが繋がります。
Former2だけでは作成できないリソースなので、忘れずに追記しましょう。
VPCピアリングのアクセプタ側のルートテーブルにリクエスタ側のルートを追加したい
背景
CloudFormationのスタック作成時に上記の操作を自動で行いたい。
また、アクセプタ側にあるEC2内のDBにアクセスしたいため、セキュリティグループのインバウンド追加も行いたい。
解決策
リクエスタ側のルートテーブルを指定してルートのリソースを作成します。
# DB側ルートテーブルへのルート追加
EC2RouteDB1a:
Type: "AWS::EC2::Route"
Properties:
DestinationCidrBlock: !Select
- 0
- Fn::Cidr: [!Ref VpcCidrBlock, 1, 8]
VpcPeeringConnectionId: !Ref EC2VPCPeeringConnection
RouteTableId: "アクセプタ側のルートテーブルID"
SecurityGroupIngress
でセキュリティグループへのインバウンド追加を行います。
# DB側セキュリティグループのインバウンド追加
EC2SecurityGroupIngress:
Type: AWS::EC2::SecurityGroupIngress
Properties:
CidrIp: !Ref VpcCidrBlock
FromPort: 5432
GroupId: "セキュリティグループID"
IpProtocol: "tcp"
ToPort: 5432
Cloudformationで作成したCloudFrontリソースのタグをスタック更新によって変更できない
問題点
CloudFormationで作成したリソースのタグを一括で切り替えたかったが、CloudFrontディストリビューションのみタグが変更できない。
原因
スタック更新時のタグ変更はResource Tags
プロパティを使用しており、リソースグループを参照しています。
CloudFormationはスタック作成時に自動でリソースグループを作成している、これを利用してタグの変更処理を行っています。
しかし、CloudFrontはリソースグループでサポートされていないため、タグ変更の対象にならないとのこと。
次の点に注意してください。
- ディストリビューションをタグ付けできますが、オリジンアクセスアイデンティティや無効化をタグ付けすることはできません。
- タグエディタおよび Resource Groups は、現在のところ CloudFront でサポートされていません。
解決策
スタックによるタグ変更が(おそらく)出来ないため、CLIで個別に変更します。
# CloudFrontディストリビューションを一覧取得
resources=$(aws cloudfront list-distributions --query 'DistributionList.Items[].{ARN: ARN, DomainName: DomainName, Id: Id}' --output json)
for resource in $(echo "$resources" | jq -c '.[]'); do
# arnを抽出
arn=$(echo "$resource" | jq -r '.ARN')
# 付いているタグ一覧を取得
tags=$(aws cloudfront list-tags-for-resource --resource "$arn" --output json)
# environmentタグの値がreleaseか確認
release_cloudfront_tag=$(echo "$tags" | jq '.Tags.Items[] | select(.Key == "environment" and .Value == "release")')
if [ -n "$release_cloudfront_tag" ]; then
RELEASE_DISTRIBUTION_ARN=$arn
fi
done
# environmentタグを削除
aws cloudfront untag-resource --resource "$RELEASE_DISTRIBUTION_ARN" --tag-keys Items=environment,release
# environmentタグを値mainとして追加
aws cloudfront tag-resource --resource "$RELEASE_DISTRIBUTION_ARN" --tags 'Items=[{Key=environment,Value="main"}]'
もっとスマートな方法があるかもしれません…
おわりに
CloudFormationは非常に便利ですが、如何せん参考文献が少なく、ドキュメントも読みにくいのでかなり手探りで進めてしまい苦労しました。
特にCloudFrontのタグ更新については各々のドキュメントを読み漁り、「これだと合点がいくかな?」という結論に至っただけで、もしかしたら普通にスタックからタグ更新出来る裏ワザがあるのかもしれません。
(余談)今回AWS周りの知識が皆無の状態でCloudFormationを作成してしまったのですが、リソース間の依存関係や全体の構成を考慮して作る必要があったので知識だけはモリモリ身についてました。なのでAWS初学者はCloudFormation触るのが意外にいいのかもしれません。