0
0

More than 3 years have passed since last update.

EC2にSSH接続できる環境を、CloudFormationを利用して作ってみた

Last updated at Posted at 2020-02-16

はじめに

今回の目標は、以下のとおりです。

パブリックサブネットに配置されたEC2にSSH接続できる環境を、CloudFormationで作成する。
また、そのテンプレートをローカルからAWSCLIを利用してデプロイする。

以降では、本記事で利用したテンプレートを記載し、またそれらのパラメータについて説明しようと思います。
最後に、実際にテンプレートをCLIを利用してローカルからデプロイし、実際にSSHしてみます。

構成内容

  • VPC
  • InternetGateway
  • EC2(Public)

最低限なインフラ構成.png

やったこと

  1. CloudFormationで利用するテンプレートの作成
  2. テンプレートのデプロイ
  3. 作成したインスタンスへの、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

タグです(そのまんま)。KeyValueを設定し、リソースに情報を与えることができる。「特定のインスタンスだけ操作したいぜ」等なときに便利。これ以降の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

    利用するプロファイル情報を指定する。デフォルト以外のプロファイル情報を利用する場合はこのオプションを利用して切り替える。ベストプラクティス的には最小権限で行うべきなんでしょうが、今回は個人的な遊びだからってことで…

デプロイが完了したら、コンソールでスタックが作成され、変更セットの実行が成功しているのを確認。

スクリーンショット 2020-02-16 15.37.08.png

作成したインスタンスへの、SSH接続

最後に今回作成したインスタンスへちゃんと接続できるかを検証します。SSH接続のコマンドは以下の通り。

ssh -i keypair.pem ec2-user@{dns_name}

スクリーンショット 2020-02-16 15.50.38.png

終わりに

今回は「インスタンスへのSSH接続ができる環境」をCloudFormationで作成してみました。
必要なリソース自体はコンソールで作成する場合と同じなのですが、「インタネットゲートウェイのVPCへのアタッチ」等リソースに対する操作をどうCloudFormationで表現すればいいかにすごく悩みました。

ただ、これで簡単に一つの環境をいつでも作れるようになったことを考えると、やっぱりCloudFormationは必須なんですねぇ…

今後はこれをベースに継ぎ足していきたいと思います。

※タグがあったりなかったり、組み込み関数を利用したりしなかったり等、纏まりがなくすいません…

0
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
0
0