【AWS】SSH/SessionManagerによる踏み台をCloudFormationで構築する

# 目的
下記記事でまとめた要件別踏み台構成をクラウドフォーメーションで作成するためのymlファイルです。
https://qiita.com/eiji-noguchi/items/d78fd5a61dc778ce3293

構成パターンは以下になります。
[1.EC2インスタンスがパブリックサブネットにあってssh接続をしたいとき](#1ec2インスタンスがパブリックサブネットにあってssh接続をしたいとき)
[2.EC2インスタンスがパブリックサブネットにあってインターネットへの経路を持つがSession Managerを通してSSH接続する場合](#2ec2インスタンスがパブリックサブネットにあってインターネットへの経路を持つがsession-managerを通してssh接続する場合)
[3.EC2インスタンスがプライベートサブネットにあってインターネットへの経路を持たずSession Managerを通して接続する場合](#3ec2インスタンスがプライベートサブネットにあってインターネットへの経路を持たずsession-managerを通して接続する場合)

# 1.EC2インスタンスがパブリックサブネットにあってssh接続をしたいとき
基本的な踏み台構成になります。
シンプルの構成かつ、外部とのトラフィックが発生する場合にこの構成をとることが多くなると思います。

###### 構成図
<img width="600" alt="ssh" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621838/461e6cc8-bca2-834d-7fc6-d98341bf0d1b.png">

###### ymlファイル

```yml
AWSTemplateFormatVersion: "2010-09-09"
Description: bastion for ssh
Resources:
  ############### VPC ###############
  # VPCの作成
  my000Vpc:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsHostnames: true
      EnableDnsSupport: true
      InstanceTenancy: default
      Tags:
        - Key: Name
          Value: my000Vpc

  ############### Subnet ###############
  # ・踏み台(bastion)等の運用管理用パブリックサブネット
  ## ssh用パブリックサブネット
  ### サブネットの作成(AvailabilityZone_ap-northeast-1a)
  my000SubnetPublicSsh1A:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: 10.0.8.0/24
      VpcId:
        Ref: my000Vpc
      AvailabilityZone: 
        Fn::Select: 
          - 0
          - Fn::GetAZs: ""
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: my000-subnet-public-ssh-1a
        - Key: Type
          Value: Public

  ############### RouteTable ###############
  # ・踏み台(bastion)等の運用管理用パブリックサブネット
  ## 運用管理用パブリックサブネットのルートテーブル
  ### ルートテーブルの作成
  my000Routemanagement:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId:
        Ref: my000Vpc
      Tags:
        - Key: Name
          Value: my000-route-management
  ### ルートテーブルのデフォルトルート(IGWへ向ける)
  my000RoutemanagementDefault:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId:
        Ref: my000Routemanagement
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId:
        Ref: my000Igw
    DependsOn:
      - my000VpcgwAttachment
  ### ルートテーブルをサブネットへ紐づける(AvailabilityZone_ap-northeast-1a)
  my000RouteSshAssociation1A:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId:
        Ref: my000Routemanagement
      SubnetId:
        Ref: my000SubnetPublicSsh1A

  ############### IGW ###############
  # インターネットへ通信するためのゲートウェイの作成
  my000Igw:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: my000-igw
  # インターネットゲートウェイをVPCにアタッチ
  my000VpcgwAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId:
        Ref: my000Vpc
      InternetGatewayId:
        Ref: my000Igw

  ############### Security groups ###############
  # ・踏み台(bastion)等の運用管理用セキュリティグループ
  ## ssh用セキュリティグループ
  my000SgSsh:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security Group of ssh
      GroupName: ssh
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: 0.0.0.0/0
          Description: Allow 22 inbound traffic
      SecurityGroupEgress:
        - CidrIp: 0.0.0.0/0
          Description: Allow all outbound traffic by default
          IpProtocol: "-1"
      Tags:
        - Key: Name
          Value: my000-sg-ssh
      VpcId:
        Ref: my000Vpc

  ############### EC2インスタンス ###############
  # key-pair作成
  my000KeyPair:
    Type: AWS::EC2::KeyPair
    Properties:
      KeyName: my000-pair
      KeyType: rsa

  # ssh接続用踏み台インスタンス作成
  my000EC2InstanceSsh:
    Type: AWS::EC2::Instance
    Properties:
      InstanceType: t2.micro
      SubnetId: 
        Ref: my000SubnetPublicSsh1A
      ImageId: ami-072bfb8ae2c884cc4
      SecurityGroupIds:
        - Ref: my000SgSsh
      KeyName: 
        Ref: my000KeyPair
      Tags:
        - Key: Name
          Value: my000-ssh-instance
```

# 2.EC2インスタンスがパブリックサブネットにあってインターネットへの経路を持つがSession Managerを通してSSH接続する場合
セキュリティグループのインバウンドルールを設定しなくてもいいため、パターン1よりもよりセキュアになります。
また、セッション履歴も管理したい場合にはこの構成になるかと思います。

###### 構成図
<img width="600" alt="ssmpub" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621838/e8a67a79-62fd-90d7-27ca-4ddeef200a50.png">

###### ymlファイル

```yml:
AWSTemplateFormatVersion: "2010-09-09"
Description: bastion for ssm public
Resources:
  ############### VPC ###############
  # VPCの作成
  my001Vpc:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 11.0.0.0/16
      EnableDnsHostnames: true
      EnableDnsSupport: true
      InstanceTenancy: default
      Tags:
        - Key: Name
          Value: my001Vpc

  ############### Subnet ###############
  # ・踏み台(bastion)等の運用管理用パブリックサブネット
  ## 踏み台インスタンス用パブリックサブネット
  ### サブネットの作成(AvailabilityZone_ap-northeast-1a)
  my001SubnetPrivateBastion1A:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: 11.0.8.0/24
      VpcId:
        Ref: my001Vpc
      AvailabilityZone: 
        Fn::Select: 
          - 0
          - Fn::GetAZs: ""
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: my001-subnet-public-ssm-1a
        - Key: Type
          Value: public

  ############### RouteTable ###############
  # ・踏み台(bastion)等の運用管理用パブリックサブネットのルートテーブル
  ## 運用管理用パブリックサブネットのルートテーブル
  ### ルートテーブルの作成
  my001Routemanagement:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId:
        Ref: my001Vpc
      Tags:
        - Key: Name
          Value: my001-route-management
  ### ルートテーブルのデフォルトルート(IGWへ向ける)
  my001RoutemanagementDefault:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId:
        Ref: my001Routemanagement
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId:
        Ref: my001Igw
    DependsOn:
      - my001VpcgwAttachment
  ### ルートテーブルをサブネットへ紐づける(AvailabilityZone_ap-northeast-1a)
  my001RouteSsmAssociation1A:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId:
        Ref: my001Routemanagement
      SubnetId:
        Ref: my001SubnetPrivateBastion1A

  ############### IGW ###############
  # インターネットへ通信するためのゲートウェイの作成
  my001Igw:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: my001-igw
  # インターネットゲートウェイをVPCにアタッチ
  my001VpcgwAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId:
        Ref: my001Vpc
      InternetGatewayId:
        Ref: my001Igw

  ############### Security groups ###############
  # ・踏み台(bastion)等の運用管理用セキュリティグループ
  ## ssm用セキュリティグループ
  my001SgSsm:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security Group of ssm
      GroupName: ssm
      SecurityGroupEgress:
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: 0.0.0.0/0
          Description: Allow 443 outbound traffic
      Tags:
        - Key: Name
          Value: my001-sg-ssm
      VpcId:
        Ref: my001Vpc

  ############### EC2インスタンス ###############
  # key-pair作成
  my001KeyPair:
    Type: AWS::EC2::KeyPair
    Properties:
      KeyName: my001-pair
      KeyType: rsa

  # ssm(public)接続用踏み台インスタンス作成
  my001EC2InstanceSsmPub:
    Type: AWS::EC2::Instance
    Properties:
      InstanceType: t2.micro
      SubnetId: 
        Ref: my001SubnetPrivateBastion1A
      ImageId: ami-072bfb8ae2c884cc4
      SecurityGroupIds:
        - Ref: my001SgSsm
      IamInstanceProfile:
        Ref: my001InstanceProfileEC2
      KeyName: 
        Ref: my001KeyPair
      Tags:
        - Key: Name
          Value: my001-ssm-instance-public

  ############### ロール ###############
  # ssm接続用ロール作成
  my001IAMRoleEC2:
    Type: AWS::IAM::Role
    Properties:
      Path: /
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action:
              - sts:AssumeRole
            Principal:
              Service:
                - ec2.amazonaws.com
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
      RoleName: my001-ssm-role

  my001InstanceProfileEC2:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Path: /
      Roles:
        - Ref: my001IAMRoleEC2
      InstanceProfileName: my001-EC2InstanceProfile
```

# 3.EC2インスタンスがプライベートサブネットにあってインターネットへの経路を持たずSession Managerを通して接続する場合
一番セキュアにな構成になります。
経路を完全に閉じた状態でSSMを利用する場合にはこの構成になるかと思います。

###### 構成図
<img width="600" alt="ssmpri" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621838/0362193b-f0e6-ace2-fea9-979e51a205c3.png">

###### ymlファイル

```yml
AWSTemplateFormatVersion: "2010-09-09"
Description: bastion for ssm private
Resources:
  ############### VPC ###############
  # VPCの作成
  my002Vpc:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 12.0.0.0/16
      EnableDnsHostnames: true
      EnableDnsSupport: true
      InstanceTenancy: default
      Tags:
        - Key: Name
          Value: my002Vpc

  ############### Subnet ###############
  # ・踏み台(bastion)等の運用管理用パブリックサブネット
  ## 踏み台インスタンス用プライベートサブネット
  ### サブネットの作成(AvailabilityZone_ap-northeast-1a)
  my002SubnetPrivateBastion1A:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: 12.0.8.0/24
      VpcId:
        Ref: my002Vpc
      AvailabilityZone: 
        Fn::Select: 
          - 0
          - Fn::GetAZs: ""
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: my002-subnet-private-ssm-1a
        - Key: Type
          Value: Private

  ## インターフェース型VPCエンドポイント用プライベートサブネット
  ### サブネットの作成(AvailabilityZone_ap-northeast-1a)
  my002SubnetPrivateSsm1A:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: 12.0.16.0/24
      VpcId:
        Ref: my002Vpc
      AvailabilityZone: 
        Fn::Select: 
          - 0
          - Fn::GetAZs: ""
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: my002-subnet-private-ssmendpoint-1a
        - Key: Type
          Value: Private

  ############### Security groups ###############
  # ・踏み台(bastion)等の運用管理用セキュリティグループ
  ## 踏み台インスタンス用セキュリティグループ
  my002SgBastion:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security Group of bastion
      GroupName: bastion
      SecurityGroupEgress:
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: 0.0.0.0/0
          Description: Allow 443 outbound traffic
      Tags:
        - Key: Name
          Value: my002-sg-bastion
      VpcId:
        Ref: my002Vpc
  ## インターフェース型VPCエンドポイント用セキュリティグループ
  my002SgSsm:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security Group of bastion
      GroupName: ssm
      SecurityGroupEgress:
        - CidrIp: 0.0.0.0/0
          Description: Allow all outbound traffic by default
          IpProtocol: "-1"
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: 12.0.8.0/24
          Description: Allow 443 inbound traffic
      Tags:
        - Key: Name
          Value: my002-sg-ssm
      VpcId:
        Ref: my002Vpc

  ############### EC2インスタンス ###############
  # key-pair作成
  my002KeyPair:
    Type: AWS::EC2::KeyPair
    Properties:
      KeyName: my002-pair
      KeyType: rsa

  # ssh接続用踏み台インスタンス作成
  my002EC2InstanceSsmPub:
    Type: AWS::EC2::Instance
    Properties:
      InstanceType: t2.micro
      SubnetId: 
        Ref: my002SubnetPrivateBastion1A
      ImageId: ami-072bfb8ae2c884cc4
      SecurityGroupIds:
        - Ref: my002SgSsm
      IamInstanceProfile:
        Ref: my002InstanceProfileEC2
      KeyName: 
        Ref: my002KeyPair
      Tags:
        - Key: Name
          Value: my002-ssm-private

  ############### ロール ###############
  # ssm接続用ロール作成
  my002IAMRoleEC2:
    Type: AWS::IAM::Role
    Properties:
      Path: /
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action:
              - sts:AssumeRole
            Principal:
              Service:
                - ec2.amazonaws.com
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
      RoleName: my002-ssm-role

  my002InstanceProfileEC2:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Path: /
      Roles:
        - Ref: my002IAMRoleEC2
      InstanceProfileName: my002-EC2InstanceProfile

  ############### VPCEndpoint ###############
  #ec2messages
  my002EndPointec2messages:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      PrivateDnsEnabled: true
      SecurityGroupIds:
        - !Ref my002SgSsm
      ServiceName: com.amazonaws.ap-northeast-1.ec2messages
      SubnetIds:
        - Ref: my002SubnetPrivateSsm1A
      VpcEndpointType: Interface
      VpcId: 
        Ref: my002Vpc

  #ssm
  my002EndPointssm:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      PrivateDnsEnabled: true
      SecurityGroupIds:
        - Ref: my002SgSsm
      ServiceName: com.amazonaws.ap-northeast-1.ssm
      SubnetIds:
        - Ref: my002SubnetPrivateSsm1A
      VpcEndpointType: Interface
      VpcId:
        Ref: my002Vpc

  #ssmmessages
  my002EndPointssmmessages:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      PrivateDnsEnabled: true
      SecurityGroupIds:
        - !Ref my002SgSsm
      ServiceName: com.amazonaws.ap-northeast-1.ssmmessages
      SubnetIds:
        - Ref: my002SubnetPrivateSsm1A
      VpcEndpointType: Interface
      VpcId:
        Ref: my002Vpc
```