はじめに
今回の目標は、以下のとおりです。
パブリックサブネットに配置されたEC2にSSH接続できる環境を、CloudFormationで作成する。
また、そのテンプレートをローカルからAWSCLIを利用してデプロイする。
以降では、本記事で利用したテンプレートを記載し、またそれらのパラメータについて説明しようと思います。
最後に、実際にテンプレートをCLIを利用してローカルからデプロイし、実際にSSHしてみます。
構成内容
- VPC
- InternetGateway
- EC2(Public)
やったこと
-
CloudFormation
で利用するテンプレートの作成 - テンプレートのデプロイ
- 作成したインスタンスへの、SSH接続
※事前にデプロイ用のIAMユーザ、SSH接続用のキーペアは作成済み
CloudFormation
で利用するテンプレートの作成
今回利用したテンプレート
AWSTemplateFormatVersion: "2010-09-09"
Description: A sample template
Mappings:
RegionMap:
ap-northeast-1a:
Name: "ap-northeast-1a"
ap-northeast-1c:
Name: "ap-northeast-1c"
Resources:
# ____
# ___ ___|___ \
# / _ \/ __| __) |
# | __/ (__ / __/
# \___|\___|_____|
WebInstance:
Type: AWS::EC2::Instance
Properties:
ImageId: ami-011facbea5ec0363b
InstanceType: t2.micro
KeyName: "keypair"
SubnetId:
Ref: WebPublicSubnet
SecurityGroupIds:
[Ref: PublicSecurityGroup]
Tags:
-
Key: "Name"
Value: "web"
# __ ___ __ ___
# \ \ / / '_ \ / __|
# \ V /| |_) | (__
# \_/ | .__/ \___|
# |_|
WebVpc:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
EnableDnsSupport: true
EnableDnsHostnames: true
Tags:
-
Key: Name
Value: PublicVpc
WebPublicSubnet:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !FindInMap [RegionMap, ap-northeast-1a, Name]
VpcId:
Ref: WebVpc
CidrBlock: 10.0.1.0/24
MapPublicIpOnLaunch: true
Tags:
-
Key: Name
Value: WebPublicSubnet
# ___ _ _
# |_ _|_ __ | |_ ___ _ __ _ __ ___| |_
# | || '_ \| __/ _ \ '__| '_ \ / _ \ __|
# | || | | | || __/ | | | | | __/ |_
# |___|_| |_|\__\___|_| |_| |_|\___|\__|
# ____ _
# / ___| __ _| |_ _____ ____ _ _ _
# | | _ / _` | __/ _ \ \ /\ / / _` | | | |
# | |_| | (_| | || __/\ V V / (_| | |_| |
# \____|\__,_|\__\___| \_/\_/ \__,_|\__, |
# |___/
InternetGateway:
Type: AWS::EC2::InternetGateway
InternetGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
InternetGatewayId: !Ref InternetGateway
VpcId: !Ref WebVpc
# ____ _ _____ _ _
# | _ \ ___ _ _| |_ __|_ _|_ _| |__ | | ___
# | |_) / _ \| | | | __/ _ \| |/ _` | '_ \| |/ _ \
# | _ < (_) | |_| | || __/| | (_| | |_) | | __/
# |_| \_\___/ \__,_|\__\___||_|\__,_|_.__/|_|\___|
RouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref WebVpc
DefaultRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId:
Ref: RouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId:
Ref: InternetGateway
DependsOn:
- SubnetRouteTableAssociation
SubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref RouteTable
SubnetId: !Ref WebPublicSubnet
# _ _ _
# ___ ___ ___ _ _| |_(_) |_ _ _ __ _ _ __ ___ _ _ _ __
# / __|/ _ \/ __| | | | __| | __| | | | / _` | '__/ _ \| | | | '_ \
# \__ \ __/ (__| |_| | |_| | |_| |_| | | (_| | | | (_) | |_| | |_) |
# |___/\___|\___|\__,_|\__|_|\__|\__, | \__, |_| \___/ \__,_| .__/
# |___/ |___/ |_|
PublicSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: "sg for public"
GroupName: "public sg"
VpcId: !Ref WebVpc
SecurityGroupIngress:
-
CidrIp: 0.0.0.0/0
IpProtocol: tcp
FromPort: 22
ToPort: 22
各設定値について(カッコ内は本記事での論理ID)
AWS::EC2::Instance(WebInstance
)
ImageId
どのAMIでインスタンスを作成するかを設定する。今回はAWSが提供している標準的なAMIを利用した。ここを動的に指定すれば、常に起動インスタンスのバージョンを最新にする、的なことができる、はず。
InstanceType
起動するインスタンスのタイプを設定する(そのまんま)。
KeyName
作成したインスタンスにSSHする際に利用するキーペアを設定する。ここで設定しておかないと、SSHできなるなるので注意(セッションマネージャを利用しての接続があるので、できないこともない。むしろ、そっちを利用したほうがわざわざセキュリティグループのSSH用の穴を開けなくて済むので、安全かも)。
SubnetId
インスタンスが、どのサブネットに属するかを設定する。
SecurityGroupIds
インスタンスが、どのセキュリティグループに属するかを設定する。Id「s」
とあるように、リストで書く必要がある。
Tags
タグです(そのまんま)。Key
とValue
を設定し、リソースに情報を与えることができる。「特定のインスタンスだけ操作したいぜ」等なときに便利。これ以降のTags
も同様。なので以降は割愛。
AWS::EC2::VPC(WebVpc
)
CidrBlock
利用するIPアドレスの範囲(CIDR表記)。
EnableDnsHostnames
パブリック IP アドレスを持つインスタンスが、対応するパブリック DNS ホスト名を取得するかどうか。これとEnableDnsSupport
の両方がtrue
じゃないと、インスタンスにパブリックDNSが割り当てられない。
EnableDnsSupport
DNS 解決がサポートされているかどうか。これとEnableDnsHostnames
の両方がtrue
じゃないと、インスタンスにパブリックDNSが割り当てられない。
参考:
AWS公式ドキュメント
https://docs.aws.amazon.com/ja_jp/vpc/latest/userguide/vpc-dns.html
AWSでPublic DNS(パブリックDNS)が割り当てられない時の解決法
https://qiita.com/sunadoridotnet/items/4ea689ce9f206e78a523
AWS::EC2::Subnet(WebPublicSubnet
)
AvailabilityZone
作成するサブネットがどのAvailabilityZone
に属するかを設定する。
VpcId
作成するサブネットがどのVPC
に属するかを設定する。
CidrBlock
利用するIPアドレス範囲(CIDR表記)。
MapPublicIpOnLaunch
このサブネットで起動するインスタンスが、パブリックIPを取得するかどうかを設定する。
AWS::EC2::InternetGateway(InternetGateway
)
設定値は特にないです(そもそもタグしかつけられない)。
ただ、これがないとインタネットゲートウェイが作成できない。
「どのVPC
にアタッチするか」を設定するために、
後述するAWS::EC2::VPCGatewayAttachment
と一緒に使いましょう。
AWS::EC2::VPCGatewayAttachment(InternetGatewayAttachment
)
InternetGatewayId 及び VpcId
インタネットゲートウェイとVPC
の関係を設定する。
「このインタネットゲートウェイ(=InternetGatewayId
)を、このVPC
(=VpcId
)にアタッチする」というように使う。
AWS::EC2::RouteTable(RouteTable
)
VpcId
このルートテーブルを使用するVPC
を指定。
AWS::EC2::Route(DefaultRoute
)
RouteTableId
ここで作成するルーティング情報を、どのルートテーブルに適用するかを設定する。
DestinationCidrBlock
転送するIPアドレスの範囲を設定する。今回は、これをデフォルトゲートウェイとして扱いたいので、0.0.0.0/0
を指定。
GatewayId
転送先のインタネットゲートウェイのIDを設定する。他にも転送先としてインスタンス等があり、どれか一つのみを指定する必要がある。
DependsOn
これを記述すると、
「このリソースは、DependsOn
で指定したリソースを作成したあとに作成してね」
というように、リソースの作成順序を明示的に指定できる。
基本、AWSがよしなにやってくれるが、たまに明示的に指示しないとスタックの作成に失敗する。
ルーティング設定のリソース絡みは要注意?らしい。
主な例外:
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-attribute-dependson.html
AWS::EC2::SubnetRouteTableAssociation(SubnetRouteTableAssociation
)
RouteTableId 及び SubnetId
AWS::EC2::VPCGatewayAttachment
みたいなもの。「どのルートテーブルをどのサブネットにアタッチするか」を設定する。
AWS::EC2::SecurityGroup(PublicSecurityGroup
)
GroupDescription
このセキュリティグループの説明を記述する。Description
なのだが、必須
なので注意。
GroupName
このセキュリティグループの名前をつける。こっちは任意。
VpcId
このセキュリティグループを適用するVPC
のIDを設定する。
SecurityGroupIngress
所謂インバウンドルールを設定する。以下の4つが必要。
- CidrIp
所謂ソース。SourceSecurityGroupId
等、何でもいいからソースは必要 - IpProtocol
どのプロトコルを利用するかを設定する - FromPort, ToPort
ポートの利用範囲を設定する
テンプレートのデプロイ
作成したテンプレートを、実際にデプロイします。冒頭で述べたとおり、今回はAWSCLI
を利用します。
といっても、下記コマンドを入力するだけですが…
aws cloudformation deploy --template ./template.yaml --stack-name standard-vpc --profile admin
一応コマンドの簡単な説明は以下のとおりです。
-
aws cloudformation deploy
変更セットを作成し、その変更セットを実行してから停止する。今回の実行結果の予測が既についており、とっとと反映させたい場合deploy
コマンドを利用するのが便利そう。 -
--template ./template.yaml
「どのテンプレートを利用して、変更セットを作成するか」を指定する。 -
--stack-name standard-vpc
今回作成するスタックの名称を指定する。 -
--profile admin
利用するプロファイル情報を指定する。デフォルト以外のプロファイル情報を利用する場合はこのオプションを利用して切り替える。ベストプラクティス的には最小権限で行うべきなんでしょうが、今回は個人的な遊びだからってことで…
デプロイが完了したら、コンソールでスタックが作成され、変更セットの実行が成功しているのを確認。
作成したインスタンスへの、SSH接続
最後に今回作成したインスタンスへちゃんと接続できるかを検証します。SSH接続のコマンドは以下の通り。
ssh -i keypair.pem ec2-user@{dns_name}
終わりに
今回は「インスタンスへのSSH接続ができる環境」をCloudFormation
で作成してみました。
必要なリソース自体はコンソールで作成する場合と同じなのですが、「インタネットゲートウェイのVPCへのアタッチ」等リソースに対する操作をどうCloudFormation
で表現すればいいかにすごく悩みました。
ただ、これで簡単に一つの環境をいつでも作れるようになったことを考えると、やっぱりCloudFormation
は必須なんですねぇ…
今後はこれをベースに継ぎ足していきたいと思います。
※タグがあったりなかったり、組み込み関数を利用したりしなかったり等、纏まりがなくすいません…