検証用のALBを作成するCloudFormationテンプレート

AWSを使えば簡単にインフラの構築が出来ますが、ALBを作るとなるとEC2の起動、ターゲットグループの作成...etcなど様々なステップが必要です。 そこでALB自体の検証を行うのに便利なCloudFormationテンプレートを作成してみました。 これを使えばお手軽にALBを構築できます。
2022.08.31

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

コンセプト

ALBの構築作業を行っているとALBの設定の検証を行いたい局面があると思います。

そんなとき毎回EC2立てて、ターゲットグループ作って、ALB作ってと作業をするのは冗長なのでCloudFormationテンプレート一つで簡単に構築できるようにしました。

本番で使うには色々と不足している部分もあるかと思いますが、検証に使う分にはミニマルで使いやすいのではないかと思います。

構成

本テンプレートで作成できる環境は以下のようなものです。

EC2は1台でパブリックサブネットに配置しています。 これはEC2にSessionManagerにNATゲートウェイなしで接続するためです。

EC2インスタンスではNginxが起動します。 EC2のOSはAmazonLinux2です。

テンプレート

alb.yaml

AWSTemplateFormatVersion: '2010-09-09'
Description: ALB and Nginx on EC2

Resources:
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      Tags:
      - Key: Name
        Value: !Sub ${AWS::StackName}-vpc

  PublicSubnet0:
    Type: AWS::EC2::Subnet
    DependsOn: AttachGateway
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: ap-northeast-1a
      CidrBlock: 10.0.0.0/24
      Tags:
      - Key: Name
        Value: !Sub ${AWS::StackName}-public-1a-subnet
  PublicSubnet1:
    Type: AWS::EC2::Subnet
    DependsOn: AttachGateway
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: ap-northeast-1c
      CidrBlock: 10.0.1.0/24
      Tags:
      - Key: Name
        Value: !Sub ${AWS::StackName}-public-1c-subnet

  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
      - Key: Name
        Value: !Sub ${AWS::StackName}-igw
  AttachGateway:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref VPC
      InternetGatewayId: !Ref InternetGateway

  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    DependsOn: AttachGateway
    Properties:
      VpcId: !Ref VPC
      Tags:
      - Key: Name
        Value: !Sub ${AWS::StackName}-public-rt
  PublicRoute:
    Type: AWS::EC2::Route
    DependsOn: AttachGateway
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway
  
  PublicSubnet0RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet0
      RouteTableId: !Ref PublicRouteTable
  PublicSubnet1RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet1
      RouteTableId: !Ref PublicRouteTable

  ALBSecurityGroup:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
        GroupDescription: "ALB SG"
        GroupName: !Sub ${AWS::StackName}-alb-sg
        VpcId: !Ref VPC
        Tags:
          - Key: Name
            Value: !Sub ${AWS::StackName}-alb-sg
        SecurityGroupIngress:
          - IpProtocol: tcp
            FromPort: 80
            ToPort: 80
            CidrIp: 0.0.0.0/0
          - IpProtocol: tcp
            FromPort: 443
            ToPort: 443
            CidrIp: 0.0.0.0/0

  EC2SecurityGroup:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
        GroupDescription: "EC2 SG"
        GroupName: !Sub ${AWS::StackName}-ec2-sg
        VpcId: !Ref VPC
        Tags:
          - Key: Name
            Value: !Sub ${AWS::StackName}-ec2-sg
        SecurityGroupIngress:
          - IpProtocol: tcp
            FromPort: 80
            ToPort: 80
            SourceSecurityGroupId: !Ref ALBSecurityGroup

  EC2InstanceProfile: 
    Type: AWS::IAM::InstanceProfile
    Properties: 
      Path: "/"
      Roles: 
        - !Ref EC2Role
      InstanceProfileName: !Sub ${AWS::StackName}-ec2-profile
  EC2Role:
    Type: "AWS::IAM::Role"
    Properties:
        Path: "/"
        RoleName: !Sub ${AWS::StackName}-ec2-role
        Tags: 
        - Key: Name
          Value: !Sub ${AWS::StackName}-ec2-role
        AssumeRolePolicyDocument: 
          Version: 2012-10-17
          Statement:
          - Effect: Allow
            Principal:
              Service: ec2.amazonaws.com
            Action: sts:AssumeRole
        ManagedPolicyArns: 
          - "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
  KeyPair:
    Type: 'AWS::EC2::KeyPair'
    Properties:
      KeyName: !Sub ${AWS::StackName}-keypair

  WebServer:
    Type: AWS::EC2::Instance
    Properties:
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-web
      InstanceType: t3.nano
      BlockDeviceMappings:
        - DeviceName: /dev/sda1
          Ebs:
            VolumeType: gp3
            VolumeSize: 8
            DeleteOnTermination: true
            Encrypted: true
      NetworkInterfaces: 
        - AssociatePublicIpAddress: "true"
          DeviceIndex: "0"
          GroupSet: 
            - !Ref EC2SecurityGroup
          SubnetId: !Ref PublicSubnet0
      ImageId: '{{resolve:ssm:/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2}}'
      IamInstanceProfile: !Ref EC2InstanceProfile
      KeyName: !Ref KeyPair
      DisableApiTermination: false
      EbsOptimized: true
      UserData: 
        Fn::Base64: |
          #!/bin/bash
          sudo amazon-linux-extras install nginx1
          sudo systemctl enable nginx
          sudo systemctl start nginx

  ALB:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Type: "application"
      Scheme: "internet-facing"
      Name: !Sub ${AWS::StackName}-alb
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-alb
      IpAddressType: ipv4
      Subnets: 
        - !Ref PublicSubnet0
        - !Ref PublicSubnet1
      SecurityGroups: 
        - !Ref ALBSecurityGroup

  ListenerHTTP:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      DefaultActions:
        - Type: forward
          TargetGroupArn: !Ref TargetGroup
      LoadBalancerArn: !Ref ALB
      Port: 80
      Protocol: HTTP
  
  TargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      Name: !Sub ${AWS::StackName}-tg
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-tg
      Port: 80
      Protocol: HTTP
      Matcher:
        HttpCode: '200'
      VpcId: !Ref VPC
      TargetType: instance
      Targets:
        - Id: !Ref WebServer

Outputs:
  ALBURL:
    Description: ALB endpoint URL
    Value: !Join
        - ""
        - - http://
          - !GetAtt ALB.DNSName

使い方

デプロイ

今回はテンプレートにパラメータは設定していないので、そのままスタック名だけ決めてデプロイすれば問題ありません。

デプロイ途中で以下のような警告が表示されると思いますが、これはSessionManagerでEC2にアクセスする用のIAMロールを作成しているためです。 スタックの作成が完了すると「{スタック名}-xxx」といった感じでリソースが作成されます。

ALBへのアクセス

スタックの出力にALBのURLが表示されているのでここにアクセスすればNginxにアクセス可能です。

正しくデプロイされていれば以下のようなページが表示されるはずです。

今回はACMなどのリソースあとで作るものとし、HTTPS用のリスナーは定義していません。(セキュリティグループは開けてあります)

削除

ALBやEC2の削除保護は無効にしてあるのでスタックを削除すれば同じく削除されます。

EC2への接続

今回はEC2へのアクセスはセッションマネージャーの利用を想定し、それ用のロールも付与してあります。

もし、SSHなどで直接アクセスしたい場合はパラメータストアにキーペアが保存してあるのでそこから取得可能です。 パラメータストアを確認すると「/ec2/keypair/key-xxx」といったパラメータが作成されてます。 こちらのキーペアをコピペしてファイルに貼り付ければ利用可能です。

最後に

これで検証を行う際すぐにALBを用意できるようになりました。 作ったり消したりしながらALBの検証を行っていきたいと思います。