AWS Systems ManagerでEC2にアクセスする構築ハンズオン

## はじめに
何度プライベートサブネットに`EC2` を構築し`AWS Systems Manager(SSM)`から接続する構築を行なったか覚えてもいませんが、毎回何処か抜けたり、十分な時間を待たずにSSMのフリートマネージャーを見ては「まだ反映されていない？？？何処か間違ったか、、、ｶﾀｶﾀ、、、繋がらん、、、」など、個人的には憂鬱な作業だったので CFn テンプレート化して、これを流すだけでアクセスが出来る構築が出来るようにしてしまいます。

------------

## 構成図
![](https://cloud5.jp/wp-content/uploads/2023/01/スクリーンショット-2023-01-28-9.29.46-640x252.png)

------------

## ハンズオン
### 構築のながれ
#### 1.ネットワーク系 + エンドポイント系 + EC2系 テンプレート作成

-----------

### 1.ネットワーク系 + エンドポイント系 + EC2系 テンプレート作成
#### 1.1.CloudFormation構築リソース一覧
主に構築されるAWSリソースの一覧を記載

##### 1.1.1.ネットワーク系
`VPC`と`プライベートサブネット`、`ルートテーブル`を作成

|サービス名|その他|
|:-:|:-:|
|VPC|192.168.0.0/16 ※ デフォルトCIDR|
|PrivateSubnet|192.168.0.0/24 ※ デフォルトCIDR|
|PrivateRouteTable| - |

##### 1.1.2.エンドポイント系
エンドポイント3種類インターフェイス型 と それら3つにアタッチさせるSGの作成 

|サービス名|エンドポイントサービス名|その他|
|:-:|:-:|:-:|
|EndPoints|com.amazonaws.[region].ec2messages|インターフェイス型|
|EndPoints|com.amazonaws.[region].ssm|インターフェイス型|
|EndPoints|com.amazonaws.[region].ssmmessages|インターフェイス型|
|EndPoints SG| - |IN : 192.168.0.0/16 からの 443 許可 / OUT : すべて許可|

##### 1.1.3.EC2系
`SSM`のフリートマネージャー からアクセスする先の`EC2`を作成

|サービス名|その他|
|:-:|:-:|
|EC2|AMI : Windows_Server-2016-Japanese-Full-Base-2023.01.19|
|EC2 SG|IN : 設定なし / OUT : すべて許可|


<br>

#### 1.2.CloudFormation テンプレート

```yaml
AWSTemplateFormatVersion: "2010-09-09"
Description:
  Create NetWork EC2 SSM
# ------------------------------------------------------------#
#  Metadata
# ------------------------------------------------------------#
Metadata:
  "AWS::CloudFormation::Interface":
    ParameterGroups:
      - Label:
          default: "VPC Configuration"
        Parameters:
          - Name
          - VPCCIDR
          - PrivateSubnetCIDR
      - Label:
          default: "EC2 Configuration"
        Parameters:
          - AMIId
          - InstanceType
          - KeyPair
      - Label:
          default: "Tags"
        Parameters:
          - UserName
# ------------------------------------------------------------#
#  InputParameters
# ------------------------------------------------------------#
Parameters:
  Name:
    Type: String
    Default: 【リソース名】

  VPCCIDR:
    Type: String
    Default: 192.168.0.0/16

  PrivateSubnetCIDR:
    Type: String
    Default: 192.168.0.0/24

  AMIId:
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Default: /aws/service/ami-windows-latest/Windows_Server-2016-Japanese-Full-Base

  InstanceType:
    Type: String
    Default: t2.micro

  KeyPair:
    Type: String
    Default: 【自身のキーペア】
  
  UserName:
    Type: String
    Default: 【タグを利用する場合記入】

# ------------------------------------------------------------#
#  Resources
# ------------------------------------------------------------#
Resources:
# ------------------------------------------------------------#
#  VPC
# ------------------------------------------------------------#
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref VPCCIDR
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
        - Key: Name
          Value: !Sub ${Name}-VPC
        - Key: User
          Value: !Ref UserName
# ------------------------------------------------------------#
#  Subnet
# ------------------------------------------------------------#
  PrivateSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !Select [0, !GetAZs ""]
      CidrBlock: !Ref PrivateSubnetCIDR
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub ${Name}-PrivateSubnet
        - Key: User
          Value: !Ref UserName
# ------------------------------------------------------------#
#  RouteTable
# ------------------------------------------------------------#
  PrivateRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub ${Name}-PrivateRouteTable
        - Key: User
          Value: !Ref UserName

  PrivateSubnetRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnet
      RouteTableId: !Ref PrivateRouteTable
# ------------------------------------------------------------#
#  Endpoint
# ------------------------------------------------------------#
  EndpointSSM:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      PrivateDnsEnabled: true
      SecurityGroupIds:
        - !Ref EndpointSecurityGroup
      ServiceName: !Sub com.amazonaws.${AWS::Region}.ssm
      SubnetIds:
        - !Ref PrivateSubnet
      VpcEndpointType: Interface
      VpcId: !Ref VPC

  EndpointSSMMessages:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      PrivateDnsEnabled: true
      SecurityGroupIds:
        - !Ref EndpointSecurityGroup
      ServiceName: !Sub com.amazonaws.${AWS::Region}.ssmmessages
      SubnetIds:
        - !Ref PrivateSubnet
      VpcEndpointType: Interface
      VpcId: !Ref VPC

  EndpointEC2Messages:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      PrivateDnsEnabled: true
      SecurityGroupIds:
        - !Ref EndpointSecurityGroup
      ServiceName: !Sub com.amazonaws.${AWS::Region}.ec2messages
      SubnetIds:
        - !Ref PrivateSubnet
      VpcEndpointType: Interface
      VpcId: !Ref VPC
# ------------------------------------------------------------#
#  Endpoint SG
# ------------------------------------------------------------#
  EndpointSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: EndpointSecurityGroup
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub ${Name}-EndpointSecurityGroup
        - Key: User
          Value: !Ref UserName
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: !Ref VPCCIDR

# ------------------------------------------------------------#
#  EC2
# ------------------------------------------------------------#
  EC2Instance:
    Type: AWS::EC2::Instance
    Properties:
      InstanceType: !Ref InstanceType
      SubnetId: !Ref PrivateSubnet
      ImageId: !Ref AMIId
      SecurityGroupIds:
        - !Ref EC2SecurityGroup
      IamInstanceProfile: !Ref EC2InstanceProfile
      KeyName: !Ref KeyPair
      Tags:
        - Key: Name
          Value: !Sub ${Name}-EC2Instance
        - Key: User
          Value: !Ref UserName
# ------------------------------------------------------------#
#  EC2 Role
# ------------------------------------------------------------#
  EC2IAMRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub ${Name}-SSM-role
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - ec2.amazonaws.com
            Action:
              - sts:AssumeRole
      Path: /
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore

  EC2InstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Path: /
      Roles:
        - Ref: EC2IAMRole
      InstanceProfileName: !Sub ${Name}-EC2InstanceProfile
# ------------------------------------------------------------#
#  EC2 SG
# ------------------------------------------------------------#
  EC2SecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: EC2SecurityGroup
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub ${Name}-EC2SecurityGroup
        - Key: User
          Value: !Ref UserName
# ------------------------------------------------------------#
#  Outputs
# ------------------------------------------------------------#
Outputs:
  VPC:
    Value: !Ref VPC
    Export:
      Name: !Sub ${Name}-VPC

  VPCCIDR:
    Value: !Ref VPCCIDR
    Export:
      Name: !Sub ${Name}-VPCCIDR

  PrivateSubnet:
    Value: !Ref PrivateSubnet
    Export:
      Name: !Sub ${Name}-PrivateSubnet

  EndpointSSM:
    Value: !Ref EndpointSSM
    Export:
      Name: !Sub ${Name}-EndpointSSM

  EndpointSSMMessages:
    Value: !Ref EndpointSSMMessages
    Export:
      Name: !Sub ${Name}-EndpointSSMMessages

  EndpointEC2Messages:
    Value: !Ref EndpointEC2Messages
    Export:
      Name: !Sub ${Name}-EndpointEC2Messages

  EC2Instance:
    Value: !Ref EC2Instance
    Export:
      Name: !Sub ${Name}-EC2Instance
```

<br />

### 挙動の確認
#### ① AWS SystemsManager(SSM)からフリートマネージャーを押下
![](https://cloud5.jp/wp-content/uploads/2023/01/スクリーンショット-2023-01-28-7.26.29-640x339.png)

#### ②『ノードアクション』より『リモートデスクトップ（RDP）との接続』を押下
![](https://cloud5.jp/wp-content/uploads/2023/01/スクリーンショット-2023-01-28-7.09.00.png)

#### ③『キーペア』を選択して、自身のキーペアを参照し『Connect』を押下
![](https://cloud5.jp/wp-content/uploads/2023/01/スクリーンショット-2023-01-28-7.09.43-1-640x569.png)

#### ④EC2（Windows）にアクセスすることができる
![](https://cloud5.jp/wp-content/uploads/2023/01/スクリーンショット-2023-01-28-7.10.45-640x389.png)


<br />

------------

## さいごに
もうこれで、ほぼ間違えず構築が出来るという方法を手にいれることが出来ました。
今までの必ずと言っていいほど毎回検索 → スクラッチ → 繋がらない → ポリシー、SG、etc、、、何か抜け漏れ? の循環から、やっと抜け出せそうです。