Amazon VPCを使ったサーバ環境をCloudFormerでCloudFormationのテンプレートにする

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

はじめに

前回「Amazon VPCを使ったサーバ環境をコマンドラインツールで構築する」はAWS EC2 Command Line Toolsを使って、VPC上にサブネットとEC2、RDSインスタンスを作成しました。

前回記事内にも書きましたがコマンドラインでAWSのサービスを作成する場合、各種IDを把握するのが大変でした。VPC IDやEC2インスタンスIDなど様々なIDをコマンド引数として指定する必要があり、慣れないと予想以上に構築に時間がかかってしまいます。

そこで今回からはCloudFormationという環境構築サービスを使って、環境構築を省力化してみたいと思います。

CloudFormerについて

「CloudFormer」というのは、すでに自分がAWSに構築済みの環境からCloudFormationのテンプレートを作成してくれるツールです。CloudFormationを使うにあたり、いきなり構築の元となる「テンプレート」を作成するのは敷居が高いので、今回はCloudFormerを使ってテンプレートを作ってみます。

CloudFormerでCloudFormationのテンプレートを作成する

作成するにあたっては、すでに弊社ブログで以前とりあげた記事を参考にしてください。

恐るべきツールCloudFormerとは一体何だ!?

この記事通り、ツールが動作するEC2インスタンスを自環境に用意し、ブラウザでツールのページにアクセスし、テンプレート化したいEC2インスタンスの選択・・・など指示通りに回答していくだけで簡単にテンプレートを作ることができました。

CloudFormerが作成したテンプレートの問題点

CloudFormerを使ってCloudFormationのテンプレートを作れましたが、作成されたテンプレートには色々問題があります。

  1. PrivtiateIPアドレスが固定になっている
  2. sshのキー名(KeyName)が固定になっている
  3. VPCサブネットIDが固定になっている
  4. VPCやサブネットのリソース作成がテンプレートに含まれていない
  5. ルートテーブルも作成されないのでNATへのルーティング設定が自動化されていない
  6. AMIのIDが固定(リージョンごとに変更したい)
  7. セキュリティグループの通信元(Source)に別のセキュリティグループが設定されていない
  8. セキュリティグループがVPCに紐付けられていない
  9. RDSのデータベース名が"MyDatabase"固定になっている(???)

値が固定になっているものはテンプレートに手を加えてパラメータ化してあげれば良さそうです。VPC作成まではサポートしてくれない上、サブネットIDが固定なので、そのまま流用できそうにありません。「VPCを使わないEC2を使っていいのは小学生まで」なのでこれは困りました。VPCに対応していない部分については、今後テンプレートの書き方を覚え修正することにしましょう。

まとめ

さて今回はCloudFormerを使ってCloudFormationのテンプレートを作成しました。作成されたテンプレートは再利用性が低く、またVPCも考慮していないため残念ながら実用的には使えなさそうです。

ただ、すでに自分で作成し構成を把握している環境を基にテンプレートを作ってくれるので、テンプレート内でどのようにリソースを定義しパラメータを設定しているのかなんとなく読めるのではないかと思います。CloudFormationのテンプレートに慣れるためのステップとしてCloudFormerを試してみてはいかがでしょうか。

【おまけ】作成されたテンプレート

{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Resources": {
    "eip54249103142": {
      "Type": "AWS::EC2::EIP",
      "Properties": {
        "InstanceId": {
          "Ref": "instancei3f1fcb3d"
        }
      }
    },
    "instancei3f1fcb3d": {
      "Type": "AWS::EC2::Instance",
      "Properties": {
        "AvailabilityZone": "ap-northeast-1a",
        "DisableApiTermination": "FALSE",
        "ImageId": "ami-173fbf16",
        "InstanceType": "t1.micro",
        "KernelId": "aki-44992845",
        "KeyName": "my_keypair",
        "Monitoring": "false",
        "Tags": [
          {
            "Key": "Name",
            "Value": "public"
          }
        ],
        "SubnetId": "subnet-2af6e843",
        "PrivateIpAddress": "10.0.0.251"
      }
    },
    "instancei2118cc23": {
      "Type": "AWS::EC2::Instance",
      "Properties": {
        "AvailabilityZone": "ap-northeast-1a",
        "DisableApiTermination": "FALSE",
        "ImageId": "ami-173fbf16",
        "InstanceType": "t1.micro",
        "KernelId": "aki-44992845",
        "KeyName": "my_keypair",
        "Monitoring": "false",
        "Tags": [
          {
            "Key": "Name",
            "Value": "private"
          }
        ],
        "SubnetId": "subnet-22f6e84b",
        "PrivateIpAddress": "10.0.1.80"
      }
    },
    "instanceifdf92eff": {
      "Type": "AWS::EC2::Instance",
      "Properties": {
        "AvailabilityZone": "ap-northeast-1a",
        "DisableApiTermination": "FALSE",
        "ImageId": "ami-12d86d13",
        "InstanceType": "t1.micro",
        "KernelId": "aki-d209a2d3",
        "KeyName": "my_keypair",
        "Monitoring": "false",
        "Tags": [
          {
            "Key": "Name",
            "Value": "nat"
          }
        ],
        "SubnetId": "subnet-2af6e843",
        "PrivateIpAddress": "10.0.0.63"
      }
    },
    "rdstestdb": {
      "Type": "AWS::RDS::DBInstance",
      "Properties": {
        "AllocatedStorage": "5",
        "BackupRetentionPeriod": "0",
        "DBInstanceClass": "db.t1.micro",
        "DBName": "MyDatabase",
        "DBParameterGroupName": "my-dbpg-test-mysql55",
        "Engine": "mysql",
        "EngineVersion": "5.5.27",
        "MasterUsername": "testuser",
        "MasterUserPassword": "testuser",
        "Port": "3306",
        "PreferredBackupWindow": "17:33-18:03",
        "PreferredMaintenanceWindow": "fri:20:11-fri:20:41"
      }
    },
    "sgmysgtestnat": {
      "Type": "AWS::EC2::SecurityGroup",
      "Properties": {
        "GroupDescription": "SG for NAT (public subnet)",
        "SecurityGroupIngress": [
          {
            "IpProtocol": "tcp",
            "FromPort": "80",
            "ToPort": "80"
          },
          {
            "IpProtocol": "tcp",
            "FromPort": "22",
            "ToPort": "22"
          }
        ]
      }
    },
    "sgmysgtestmysql": {
      "Type": "AWS::EC2::SecurityGroup",
      "Properties": {
        "GroupDescription": "SG for test mysql (private subnet)",
        "SecurityGroupIngress": [
          {
            "IpProtocol": "tcp",
            "FromPort": "3306",
            "ToPort": "3306"
          }
        ]
      }
    },
    "sgmysgtestpub": {
      "Type": "AWS::EC2::SecurityGroup",
      "Properties": {
        "GroupDescription": "SG for test (public subnet)",
        "SecurityGroupIngress": [
          {
            "IpProtocol": "icmp",
            "FromPort": "-1",
            "ToPort": "-1",
            "CidrIp": "0.0.0.0/0"
          },
          {
            "IpProtocol": "tcp",
            "FromPort": "22",
            "ToPort": "22",
            "CidrIp": "0.0.0.0/0"
          },
          {
            "IpProtocol": "tcp",
            "FromPort": "80",
            "ToPort": "80",
            "CidrIp": "0.0.0.0/0"
          }
        ]
      }
    },
    "sgmysgtestpri": {
      "Type": "AWS::EC2::SecurityGroup",
      "Properties": {
        "GroupDescription": "SG for test (private subnet)",
        "SecurityGroupIngress": [
          {
            "IpProtocol": "tcp",
            "FromPort": "22",
            "ToPort": "22"
          },
          {
            "IpProtocol": "udp",
            "FromPort": "161",
            "ToPort": "161"
          }
        ]
      }
    }
  },
  "Description": "VPC environment with Public/Private subnets.\r\n1 EC2 instance and 1 RDS instance in Private Subnet.\r\n2 EC2 instances (Web and NAT) in Public subnet.",
  "Outputs": {
    "rdstestdbEndpoint": {
      "Value": {
        "Fn::GetAtt": [
          "rdstestdb",
          "Endpoint.Address"
        ]
      }
    }
  }
}