1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【AWS】CloudFormationを使ったALBとEC2の構成テンプレートと検証手順

Posted at

1. はじめに

1-1 ご挨拶

初めまして、井村と申します。

AWSやCloudFormationは、久しぶりに触れる機会があり、そのたびに調査しています。

他のことにも時間を使いたいため、Application Load Balancer(以下ALB)とEC2を構築するCloudFormationを備忘録として残します。

1-2 構築と検証の流れ

CloudFormationを用いてALB、EC2を構築します。
HTTP接続を確認した後、秘密鍵と自己署名のサーバ証明書を作成し、HTTPS接続が可能であることを確認します。

2. 構成図

CloudFormationを利用して作成するシステム構成図は以下の通りです。

architecture.png

3. 構築

以下のCloudFormationテンプレートを利用します。

web-server.yaml
yaml
AWSTemplateFormatVersion: 2010-09-09
Description: This is a template for creating a load balancer and server.

Parameters:
  System:
    Type: String
    Description: The system name for the resources.
    Default: timura
  Env:
    Type: String
    Description: The environment for the resources (e.g., dev, prod).
    Default: dev
  AmiId:
    Type: AWS::SSM::Parameter::Value<String>
    Default: /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64
    Description: The AMI ID
  InstanceType:
    Type: String
    Description: The instance type for the EC2 instances.
    Default: t3.nano
    AllowedValues:
      - t3.micro
      - t3.nano
      - t2.micro

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: System Information
        Parameters:
          - System
          - Env
      - Label:
          default: Instance Information
        Parameters:
          - AmiId
    ParameterLabels:
      System:
        default: System Name
      Env:
        default: Environment
      AmiId:
        default: AMI ID

Resources:
  # Create a VPC
  VPC:
    Type: AWS::EC2::VPC
    Description: Create a VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsHostnames: true
      EnableDnsSupport: true
      InstanceTenancy: default
      Tags:
        - Key: Name
          Value: !Sub ${System}-${Env}-vpc

  # Create public subnets
  PubSnet01:
    Type: AWS::EC2::Subnet
    Description: Create a public subnet01
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.0.0.0/24
      AvailabilityZone: !Select [0, !GetAZs ""]
      Tags:
        - Key: Name
          Value: !Sub ${System}-${Env}-snet-pub01
  PubSnet02:
    Type: AWS::EC2::Subnet
    Description: Create a public subnet02
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.0.1.0/24
      AvailabilityZone: !Select [1, !GetAZs ""]
      Tags:
        - Key: Name
          Value: !Sub ${System}-${Env}-snet-pub02

  # Create private subnets
  PriSnet01:
    Type: AWS::EC2::Subnet
    Description: Create a private subnet01
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.0.10.0/24
      AvailabilityZone: !Select [0, !GetAZs ""]
      Tags:
        - Key: Name
          Value: !Sub ${System}-${Env}-snet-pri01
  PriSnet02:
    Type: AWS::EC2::Subnet
    Description: Create a private subnet02
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.0.11.0/24
      AvailabilityZone: !Select [1, !GetAZs ""]
      Tags:
        - Key: Name
          Value: !Sub ${System}-${Env}-snet-pri02

  # Create an Internet Gateway
  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Description: Create an Internet Gateway
    Properties:
      Tags:
        - Key: Name
          Value: !Sub ${System}-${Env}-igw

  # Attach the Internet Gateway to the VPC
  AttachGateway:
    Type: AWS::EC2::VPCGatewayAttachment
    Description: Attach the Internet Gateway to the VPC
    Properties:
      VpcId: !Ref VPC
      InternetGatewayId: !Ref InternetGateway

  # Create a Elastic IP for the Nat Gateway
  NatEIP:
    Type: AWS::EC2::EIP
    Description: Create an Elastic IP for the Nat Gateway
    Properties:
      Domain: vpc
      Tags:
        - Key: Name
          Value: !Sub ${System}-${Env}-eip-natgw

  # Create an Nat Gateway
  NatGateway:
    Type: AWS::EC2::NatGateway
    Description: Create a Nat Gateway
    Properties:
      AllocationId: !GetAtt NatEIP.AllocationId
      SubnetId: !Ref PubSnet01
      Tags:
        - Key: Name
          Value: !Sub ${System}-${Env}-natgw

  # Create route tables for the public subnets
  PubRouteTable01:
    Type: AWS::EC2::RouteTable
    Description: Create a route table for the public subnet01
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub ${System}-${Env}-rt-pub01
  PubRouteTable02:
    Type: AWS::EC2::RouteTable
    Description: Create a route table for the public subnet02
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub ${System}-${Env}-rt-pub02

  # Create route tables for the private subnets
  PriRouteTable01:
    Type: AWS::EC2::RouteTable
    Description: Create a route table for the private subnet01
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub ${System}-${Env}-rt-pri01
  PriRouteTable02:
    Type: AWS::EC2::RouteTable
    Description: Create a route table for the private subnet02
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub ${System}-${Env}-rt-pri02

  # Create routes for the public subnets
  PubRouteTable01Route:
    Type: AWS::EC2::Route
    Description: Create a route for the public subnet01
    DependsOn: AttachGateway
    Properties:
      RouteTableId: !Ref PubRouteTable01
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway
  PubRouteTable02Route:
    Type: AWS::EC2::Route
    Description: Create a route for the public subnet02
    DependsOn: AttachGateway
    Properties:
      RouteTableId: !Ref PubRouteTable02
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway

  # Create routes for the private subnets
  PriRouteTable01Route:
    Type: AWS::EC2::Route
    Description: Create a route for the private subnet01
    Properties:
      RouteTableId: !Ref PriRouteTable01
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId: !Ref NatGateway
  PriRouteTable02Route:
    Type: AWS::EC2::Route
    Description: Create a route for the private subnet02
    Properties:
      RouteTableId: !Ref PriRouteTable02
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId: !Ref NatGateway

  # Associate the public subnets with the public route tables
  PubSnet01RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Description: Associate the public subnet01 with the public route table
    Properties:
      SubnetId: !Ref PubSnet01
      RouteTableId: !Ref PubRouteTable01
  PubSnet02RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Description: Associate the public subnet02 with the public route table
    Properties:
      SubnetId: !Ref PubSnet02
      RouteTableId: !Ref PubRouteTable02

  # Associate the private subnets with the private route tables
  PriSnet01RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Description: Associate the private subnet01 with the private route table
    Properties:
      SubnetId: !Ref PriSnet01
      RouteTableId: !Ref PriRouteTable01
  PriSnet02RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Description: Associate the private subnet02 with the private route table
    Properties:
      SubnetId: !Ref PriSnet02
      RouteTableId: !Ref PriRouteTable02

  # Create nacls for the public subnets
  PubNacl01:
    Type: AWS::EC2::NetworkAcl
    Description: Create a nacl for the public subnet01
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub ${System}-${Env}-nacl-pub01
  PubNacl02:
    Type: AWS::EC2::NetworkAcl
    Description: Create a nacl for the public subnet02
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub ${System}-${Env}-nacl-pub02

  # Create nacls for the private subnets
  PriNacl01:
    Type: AWS::EC2::NetworkAcl
    Description: Create a nacl for the private subnet01
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub ${System}-${Env}-nacl-pri01
  PriNacl02:
    Type: AWS::EC2::NetworkAcl
    Description: Create a nacl for the private subnet02
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub ${System}-${Env}-nacl-pri02

  # Associate the public nacls with the public subnets
  PubNacl01Association:
    Type: AWS::EC2::SubnetNetworkAclAssociation
    Description: Associate the nacl for the public subnet01
    Properties:
      NetworkAclId: !Ref PubNacl01
      SubnetId: !Ref PubSnet01
  PubNacl02Association:
    Type: AWS::EC2::SubnetNetworkAclAssociation
    Description: Associate the nacl for the public subnet02
    Properties:
      NetworkAclId: !Ref PubNacl02
      SubnetId: !Ref PubSnet02

  # Associate the private nacls with the private subnets
  PriNacl01Association:
    Type: AWS::EC2::SubnetNetworkAclAssociation
    Description: Associate the nacl for the private subnet01
    Properties:
      NetworkAclId: !Ref PriNacl01
      SubnetId: !Ref PriSnet01
  PriNacl02Association:
    Type: AWS::EC2::SubnetNetworkAclAssociation
    Description: Associate the nacl for the private subnet02
    Properties:
      NetworkAclId: !Ref PriNacl02
      SubnetId: !Ref PriSnet02

  # Create Netwrork ACL rules for the public subnets
  PubNacl01InboundRule:
    Type: AWS::EC2::NetworkAclEntry
    Description: Create an inbound rule for the public subnet01
    Properties:
      Egress: false
      NetworkAclId: !Ref PubNacl01
      RuleNumber: 1000
      Protocol: -1
      RuleAction: allow
      CidrBlock: 0.0.0.0/0
  PubNacl01OutboundRule:
    Type: AWS::EC2::NetworkAclEntry
    Description: Create an outbound rule for the public subnet01
    Properties:
      Egress: true
      NetworkAclId: !Ref PubNacl01
      RuleNumber: 1000
      Protocol: -1
      RuleAction: allow
      CidrBlock: 0.0.0.0/0
  PubNacl02InboundRule:
    Type: AWS::EC2::NetworkAclEntry
    Description: Create an inbound rule for the public subnet02
    Properties:
      Egress: false
      NetworkAclId: !Ref PubNacl02
      RuleNumber: 1000
      Protocol: -1
      RuleAction: allow
      CidrBlock: 0.0.0.0/0
  PubNacl02OutboundRule:
    Type: AWS::EC2::NetworkAclEntry
    Description: Create an outbound rule for the public subnet02
    Properties:
      Egress: true
      NetworkAclId: !Ref PubNacl02
      RuleNumber: 1000
      Protocol: -1
      RuleAction: allow
      CidrBlock: 0.0.0.0/0

  # Create Netwrork ACL rules for the private subnets
  PriNacl01InboundRule:
    Type: AWS::EC2::NetworkAclEntry
    Description: Create an inbound rule for the private subnet01
    Properties:
      Egress: false
      NetworkAclId: !Ref PriNacl01
      RuleNumber: 1000
      Protocol: -1
      RuleAction: allow
      CidrBlock: 0.0.0.0/0
  PriNacl01OutboundRule:
    Type: AWS::EC2::NetworkAclEntry
    Description: Create an outbound rule for the private subnet01
    Properties:
      Egress: true
      NetworkAclId: !Ref PriNacl01
      RuleNumber: 1000
      Protocol: -1
      RuleAction: allow
      CidrBlock: 0.0.0.0/0
  PriNacl02InboundRule:
    Type: AWS::EC2::NetworkAclEntry
    Description: Create an inbound rule for the private subnet02
    Properties:
      Egress: false
      NetworkAclId: !Ref PriNacl02
      RuleNumber: 1000
      Protocol: -1
      RuleAction: allow
      CidrBlock: 0.0.0.0/0
  PriNacl02OutboundRule:
    Type: AWS::EC2::NetworkAclEntry
    Description: Create an outbound rule for the private subnet02
    Properties:
      Egress: true
      NetworkAclId: !Ref PriNacl02
      RuleNumber: 1000
      Protocol: -1
      RuleAction: allow
      CidrBlock: 0.0.0.0/0

  # Create security groups for the public subnets
  PubSg01:
    Type: AWS::EC2::SecurityGroup
    Description: Create a security group for the public subnet01
    Properties:
      GroupDescription: Security group for the public subnet01
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub ${System}-${Env}-sg-pub01
  PubSg02:
    Type: AWS::EC2::SecurityGroup
    Description: Create a security group for the public subnet02
    Properties:
      GroupDescription: Security group for the public subnet02
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub ${System}-${Env}-sg-pub02

  # Create security groups for the private subnets
  PriSg01:
    Type: AWS::EC2::SecurityGroup
    Description: Create a security group for the private subnet01
    Properties:
      GroupDescription: Security group for the private subnet01
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          SourceSecurityGroupId: !Ref PubSg01
      Tags:
        - Key: Name
          Value: !Sub ${System}-${Env}-sg-pri01
  PriSg02:
    Type: AWS::EC2::SecurityGroup
    Description: Create a security group for the private subnet02
    Properties:
      GroupDescription: Security group for the private subnet02
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          SourceSecurityGroupId: !Ref PubSg02
      Tags:
        - Key: Name
          Value: !Sub ${System}-${Env}-sg-pri02

  # Create an SSMRole for the instances
  SSMRole:
    Type: AWS::IAM::Role
    Description: Create an SSM role for the instances
    Properties:
      RoleName: !Sub ${System}-${Env}-ssm-role
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: ec2.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
      Tags:
        - Key: Name
          Value: !Sub ${System}-${Env}-ssm-role

  # Create an instance profile for the SSM role
  SSMInstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Description: Create an instance profile for the SSM role
    Properties:
      Roles:
        - !Ref SSMRole
      InstanceProfileName: !Sub ${System}-${Env}-ssm-role

  # Create an EC2 instance
  EC2:
    Type: AWS::EC2::Instance
    Description: Create a web server instance
    Properties:
      InstanceType: !Ref InstanceType
      ImageId: !Ref AmiId
      SubnetId: !Ref PriSnet01
      SecurityGroupIds:
        - !Ref PriSg01
      IamInstanceProfile: !Ref SSMInstanceProfile
      UserData:
        Fn::Base64: !Sub |
          #!/bin/bash
          timedatectl set-timezone Asia/Tokyo
          hostnamectl set-hostname ${System}-${Env}-ec2-web
          dnf update -y
          dnf install -y httpd
          dnf install -y openssl
          systemctl start httpd
          systemctl enable httpd
          echo "This is $(hostname)" > /var/www/html/index.html

      Tags:
        - Key: Name
          Value: !Sub ${System}-${Env}-ec2-web

  # Create an Application Load Balancer
  ALB:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Description: Create an Application Load Balancer
    Properties:
      Name: !Sub ${System}-${Env}-alb
      Subnets:
        - !Ref PubSnet01
        - !Ref PubSnet02
      SecurityGroups:
        - !Ref PubSg01
        - !Ref PubSg02
      Scheme: internet-facing
      Tags:
        - Key: Name
          Value: !Sub ${System}-${Env}-alb

  # Create a target group for the ALB
  ALBTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Description: Create a target group for the ALB
    Properties:
      Name: !Sub ${System}-${Env}-tg-web
      Port: 80
      Protocol: HTTP
      VpcId: !Ref VPC
      TargetType: instance
      Targets:
        - Id: !Ref EC2
      Tags:
        - Key: Name
          Value: !Sub ${System}-${Env}-tg-web

  # Create a listener for the ALB
  ALBListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Description: Create a listener for the ALB
    Properties:
      LoadBalancerArn: !Ref ALB
      Port: 80
      Protocol: HTTP
      DefaultActions:
        - Type: forward
          TargetGroupArn: !Ref ALBTargetGroup

Outputs:
  ALBDnsName:
    Description: The DNS name of the Application Load Balancer
    Value: !GetAtt ALB.DNSName

以下補足になります。

  • Metadataセクション:「AWS::CloudFormation::Interface」はCloudFormationコンソールでのパラメータの表示方法をカスタマイズします。beforeがMetadataなし、afterがMetadataありのコンソール画面になります。パラメータ欄がカテゴライズできます。

before

Meta_data_No_2.png

after

Meta_data_Yes_2.png

  • 組み込み関数 1 : サブネットのアベイラビリティゾーンを指定する記述です。
yaml
AvailabilityZone: !Select [0, !GetAZs ""]

!GetAZs は、指定されたリージョンのすべてのアベイラビリティゾーンのリストを返します。空の文字列("")を指定すると、スタックが作成されている現在のリージョンのアベイラビリティゾーンが返されます。

[
    "ap-northeast-1a",
    "ap-northeast-1c",
    "ap-northeast-1d"
]

!Select関数は、リストから特定のインデックスの値を選択します。この場合、インデックス0を指定しているため、最初のアベイラビリティゾーンが選択されます。

よって"ap-northeast-1a"が選定されます。

  • 組み込み関数 2 : 指定されたリソースの論理ID(この場合はPubSnet01)を参照し、そのリソースのIDを返します。
SubnetId: !Ref PubSnet01

公式ドキュメントより戻り値は参照されるエンティティのタイプによって異なるとのこと。例えば、 AWS::EC2::EIP リソースは IP アドレスを返し、 AWS::EC2::Instance はインスタンス ID を返します。

  • 組み込み関数 3 : ALBという論理IDを持つリソースのDNSName属性を取得しています。
Value: !GetAtt ALB.DNSName

上記はアプリケーションロードバランサー(ALB)のDNS名を取得することを意味します。

DNS_Name_ALB_2.png

4. 確認

CloudFormationの出力値をブラウザのURL欄に貼り付けると、以下のように表示されます。

http-access-ok_2.png

HTTP接続できることが確認できました。

次にEC2へログイン後、以下コマンドを実行し秘密鍵と自己署名のサーバ証明書を作成します。

bash
# 作業ディレクトリへ移動
cd /etc/pki/tls/private/;pwd

# 秘密鍵およびサーバ証明書の作成
# x509が自己署名証明書、nodesが秘密鍵を暗号化しない(パスフレーズを設定しない)オプション
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout server.key -out server.crt

### 以下は opensslコマンドの補足です。
### Common NameはALBのドメイン名になります。以下は東京リージョンの例です。
Country Name (2 letter code) [XX]:JP
State or Province Name (full name) []:Tokyo
Locality Name (eg, city) [Default City]:Shinjuku
Organization Name (eg, company) [Default Company Ltd]:Example Corp
Organizational Unit Name (eg, section) []:Example Dept
Common Name (eg, your name or your servers hostname) []:timura-dev-alb-1224505215.ap-northeast-1.elb.amazonaws.com
Email Address []:timura@example.com
### ここまで補足

# ファイル存在の確認
ls -l

# 秘密鍵ファイルの中身を取得
sudo cat server.key

# サーバ証明書ファイルの中身を取得
sudo cat server.crt

上記ファイルの中身をALBに設定しHTTPS接続出来るよう設定します。

作成したALBを選択し、[リスナーの追加] をクリックします。

ALB_001.png

プロトコルは[HTTPS]を選択、ターゲットグループはCloudFormationで作成されたものを選択します。

ALB_002.png

証明書の取得先は[証明書をインポート]を選択します。

証明書のプライベートキーには [sudo cat server.key] の内容を、証明書本文には [sudo cat server.crt] の内容を貼り付けてください。

ALB_003.png

[送信]をクリックすると設定が更新されます。

ALB_004.png

それではプロトコルをhttpsに指定し、ALBのDNS名をブラウザのURL欄に貼り付けます。

last_001.png

last_002.png

無事にHTTPS接続が確認できました。

5. 参考資料

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?