CFn でプライベートサブネットに配置した AWS Cloud9 をセットアップする

記事タイトルとURLをコピーする

こんにちは、近藤(りょう)です!

"Public" ではなく "Private" な環境で開発を行う場合に VPC など諸々の下準備(主に足回り)が必要でしたので 環境構築の 効率化重視 で「CFn(CloudFormation)を用いて AWS Cloud9 の環境をセットアップ」をしてみました。

AWS Cloud9 について

AWS Cloud9 は、ブラウザのみを使用してコードを記述、実行、デバッグできる無料のクラウドベースの統合開発環境 (IDE) です。IDE には、コードエディタ、デバッガー、ターミナルが含まれています。

AWS Cloud9 には、JavaScript、Python、PHP などの一般的なプログラム言語に不可欠なツールがあらかじめパッケージ化されているため、新しいプロジェクトを開始するためにファイルをインストールしたり、開発マシンを設定したりする必要はありません。

AWS Cloud9(Cloud IDE でコードを記述、実行、デバッグ)| AWS

Private な環境でのAWS Cloud9 利用について

AWS Cloud9 には SSH と SSM(AWS Systems Manager)2つのネットワーク接続方式が提供されていますが、Private な環境(Private Subnet上) に AWS Cloud9 を構築するので "SSM 接続方式" を選択する必要があります。

  • 以下、Amazon VPC 要件 AWS Cloud9 の抜粋
    • 環境が SSH 経由で EC2 インスタンスに直接アクセスしている場合、インスタンスはパブリックサブネットでのみ起動できます。
    • Systems Manager を使って no-ingress Amazon EC2 インスタンスにアクセスしている場合、インスタンスはパブリックサブネットまたはプライベートサブネットに起動できます。
    • プライベートサブネットを使用している場合は、パブリックサブネットで NAT ゲートウェイをホストして、サブネットのインスタンスによるインターネットとの通信を許可します。


パブリックサブネットで NAT ゲートウェイをホストして、サブネットのインスタンスによるインターネットとの通信を許可します。」と記載がありますが
Systems Manager でプライベート EC2 インスタンスを管理できるようにVPCエンドポイントを利用すればできるようですね。

必要なVPCエンドポイント

  • com.amazonaws.[region].ec2messages
  • com.amazonaws.[region].ssm
  • com.amazonaws.[region].ssmmessages

参考

AWS Systems Manager を使用して no-ingress EC2 インスタンスにアクセスする - AWS Cloud9

インターネットにアクセスせずにプライベート EC2 インスタンスを管理する | AWS re:Post

ただ、インターネットにつながっていないとそれはそれで開発効率は落ちそうです。

今回の構成

Private Subnet 上に AWS Cloud9 を配置して NAT Gateway 経由で外部(インターネット)への接続ができるような構成を構築します。

Cloud9の構成(Private)-1

環境構築してみる

東京リージョンにAWS Cloud9を構築してみます。

CFn 実行

東京リージョンでCFnをぽちっとな。
※Parametersは各々修正お願いします。

AWSTemplateFormatVersion: '2010-09-09'
Description: Create a VPC with Public/Private Subnets, NAT Gateway, and Cloud9 IDE on Private Subnet using Session Manager

Parameters:
  EnvironmentName:
    Description: An environment name that is prefixed to resource names
    Type: String
    Default: myapp

  VpcCIDR:
    Description: Please enter the IP range (CIDR notation) for this VPC
    Type: String
    Default: 10.200.0.0/16

  AvailabilityZoneName:
    Description: Please enter the AvailabilityZoneName for Availability Zone
    Type: String
    Default: ap-northeast-1a

  PublicSubnetCIDR:
    Description: Please enter the IP range (CIDR notation) for the public subnet
    Type: String
    Default: 10.200.0.0/24

  PrivateSubnetCIDR:
    Description: Please enter the IP range (CIDR notation) for the private subnet
    Type: String
    Default: 10.200.1.0/24

  Cloud9OwnerArn:
    Type: String
    Default: <Cloud9 OWNER ARN>
    # Cloud9OwnerArnの部分はCloud9環境のオーナーにしたいIAMユーザー、ロールのARNを記載
    # 別アカウントからスイッチロールした先のアカウントでCloud9を構築する場合はarn:aws:sts::スイッチ先アカウント番号:assumed-role/スイッチ先ロール名/セッション名(ユーザー名)

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      -
        Label:
          default: "Basic Configuration"
        Parameters:
          - "EnvironmentName"
          - "VpcCIDR"
          - "AvailabilityZoneName"
          - "PublicSubnetCIDR"
          - "PrivateSubnetCIDR"
          - "Cloud9OwnerArn"

Resources:
# ------------------------------------------------------------#
# VPC
# ------------------------------------------------------------#

  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref VpcCIDR
      EnableDnsHostnames: true
      EnableDnsSupport: true
      InstanceTenancy: default
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-vpc

# ------------------------------------------------------------#
# PublicSubnet
# ------------------------------------------------------------#

  PublicSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Ref AvailabilityZoneName
      CidrBlock: !Ref PublicSubnetCIDR
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-public-subnet

# ------------------------------------------------------------#
# PrivateSubnet
# ------------------------------------------------------------#

  PrivateSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Ref AvailabilityZoneName
      CidrBlock: !Ref PrivateSubnetCIDR
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-private-subnet

# ------------------------------------------------------------#
# InternetGateway and associate
# ------------------------------------------------------------#

  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-igw

  InternetGatewayAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref VPC
      InternetGatewayId: !Ref InternetGateway

# ------------------------------------------------------------#
# Nat Gateway
# ------------------------------------------------------------#

  NatGateway:
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId: !GetAtt NatGatewayEIP.AllocationId
      SubnetId: !Ref PublicSubnet
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-ngw

  NatGatewayEIP:
    Type: AWS::EC2::EIP
    Properties:
      Domain: vpc

# ------------------------------------------------------------#
# PublicSubnet Route Table
# ------------------------------------------------------------#

  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-public-route

  DefaultPublicRoute:
    Type: AWS::EC2::Route
    DependsOn: InternetGatewayAttachment
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway

# ------------------------------------------------------------#
# PrivateSubnet Route Table
# ------------------------------------------------------------#

  PrivateRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-private-route

  DefaultPrivateRoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PrivateRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId: !Ref NatGateway

# ------------------------------------------------------------#
# PublicSubnet Associate
# ------------------------------------------------------------#

  PublicSubnetRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PublicRouteTable
      SubnetId: !Ref PublicSubnet

# ------------------------------------------------------------#
# PrivateSubnet Associate
# ------------------------------------------------------------#

  PrivateSubnetRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PrivateRouteTable
      SubnetId: !Ref PrivateSubnet

# ------------------------------------------------------------#
# Cloud9
# ------------------------------------------------------------#

  Cloud9Environment:
    Type: AWS::Cloud9::EnvironmentEC2
    Properties:
      InstanceType: t2.micro
      SubnetId: !Ref PrivateSubnet
      AutomaticStopTimeMinutes: 30
      ImageId: resolve:ssm:/aws/service/cloud9/amis/amazonlinux-2023-x86_64
      Name: !Sub ${EnvironmentName}-cloud9
      ConnectionType: CONNECT_SSM
      OwnerArn: !Ref Cloud9OwnerArn

Outputs:
  Cloud9EnvironmentId:
    Description: The ID of the Cloud9 Environment
    Value: !Ref Cloud9Environment

  VPCId:
    Description: The ID of the VPC
    Value: !Ref VPC

  PublicSubnetId:
    Description: The ID of the Public Subnet
    Value: !Ref PublicSubnet

  PublicSubnetCIDR:
    Description: The CIDR of the Public Subnet
    Value: !Ref PublicSubnetCIDR

  PrivateSubnetId:
    Description: The ID of the Private Subnet
    Value: !Ref PrivateSubnet

  PrivateSubnetCIDR:
    Description: The CIDR of the Private Subnet  
    Value: !Ref PrivateSubnetCIDR

  NATGatewayId:
    Description: The ID of the NAT Gateway
    Value: !Ref NatGateway

  NATGatewayEIP:
    Description: The Elastic IP of the NAT Gateway
    Value: !Ref NatGatewayEIP

  Cloud9EnvironmentName:
    Description: The Name of the Cloud9 Environment
    Value: !GetAtt Cloud9Environment.Name

AWS Cloud9 の CFnの記載は「AWS::Cloud9::EnvironmentEC2」をご参照ください。

CFn 状態確認

CFnで作成するとマネージメントコンソールで AWS Cloud9 を作成した時と同じように AWS Cloud9用のCFn が動くみたいです。

Cloud9のCFn状態-1

ネットワーク 状態確認

CFnで作成した AWS Cloud9 のサブネットを確認します。

Cloud9のサブネット確認-1

PrivateSubnet上に起動しているのが確認できます。(サブネット IDで検索)

Cloud9のサブネット確認-2

リソースマップも確認してみる。(NATGW経由になっている)

Cloud9のサブネット確認-3

AWS Cloud9 接続

AWS Cloud9を開いてみましょう。
対象の環境の「開く」を押下する。

Cloud9の接続-1

AWS Cloud9へ接続できました。
※すぐ立ち上がらないこともありますのでその時はブラウザを更新してください

Cloud9の接続-2

これでPrivate な環境でAWS Cloud9を作成することができました。
AWS Cloud9からNatGW経由でインターネットへ通信することもできます。

まとめ

Private な環境で開発をする必要があるといった場合に VPC などを手動で作成しておくのは手間がかかるのでCFnを利用した環境作成をご紹介させていただきました。

あまり、必要になることはないかもしれませんがどなたかの支えになれば幸いです。

近藤 諒都

(記事一覧)

カスタマーサクセス部CS5課

夜行性ではありません。朝活派です。

趣味:お酒、旅行、バスケ、掃除、家庭用パン作り(ピザも)など