ぺーぺーSEのブログ

備忘録・メモ用サイト。

CloudFormation事始め

AWS CloudFormationについてまとめる。

CloudFormationを使用する際には、テンプレートスタックの作業を行う。
テンプレートは、AWS リソースとそのプロパティを記述するためのJSONファイル。
スタックを作成するたびに、AWS CloudFormation はテンプレートに記述されているリソースをプロビジョニングする。

テンプレート

テンプレートは以下のフォーマットで記載する。

{
  "AWSTemplateFormatVersion" : "version date",

  "Description" : "JSON string",

  "Metadata" : {
    // template metadata
  },

  "Parameters" : {
    // set of parameters
  },

  "Mappings" : {
    // set of mappings
  },

  "Conditions" : {
    // set of conditions
  },

  "Resources" : {
    // set of resources
  },

  "Outputs" : {
    // set of outputs
  }
}
  • AWSTemplateFormatVersion(任意)
    • AWS CloudFormationテンプレートバージョン
    • 現在は「2010-09-09」一択
  • Description(オプション)
    • テンプレートの説明
  • Metadata(オプション)
    • テンプレートに関する追加情報
  • Parameters(任意)
    • スタックの作成・更新時にテンプレートに渡すことができるパラメータ
    • テンプレートのResources、Outputsを参照できる
  • Mappings(任意)
    • Conditionsのパラメータに指定できるKey-Valueを定義できる
    • Resources・Outputsでは「Fn::FindInMap」組み込み関数でKey-Valueを使用できる
  • Conditions(オプション)
    • AWSリソースの作成如何の条件を定義できる
    • スタックがテスト用か本番用かを条件付きで作成できたりする
  • Resources(必須)
    • AWSリソースとそのプロパティを定義する
  • Outputs(任意)
    • スタックのプロパティで確認したい項目を定義する
    • AWS CLIaws cloudformation describe-stacks」コマンドで確認できる

スタック

AWS CloudFormationを使用する際、関連リソースはスタックと呼ばれる単一のユニットとして管理する。
スタックを作成、更新、削除することで、AWSリソースを作成、更新、削除する。
スタックはテンプレートで定義することができる。

リソース(Resources)の書き方

テンプレートで定義できるものの1つ。
これ以外は任意かオプションとなっていて、これが本丸。
下記の形式で記載する。

{
    "Resources" : {
        "リソース名" : {
            "Type" : "AWSリソースの種類",
            "Properties" : {
                // AWSリソースの種類によりユニークな属性定義
            }
            // 以下、リソース属性(オプション)
            // CreationPolicy
            // DeletionPolicy
            // DependsOn
            // Metadata
            // UpdatePolicy
        }
    }
}

リソース名は任意につけることができ、テンプレート内でリソースを識別するために使用する。
Nameタグには反映されないので、"Properties"."Tags"に自分で設定する必要がある。

AWSリソースの種類は、
AWS::ProductIdentifier::ResourceType
の形式で表現される。
AWSリソースプロパティタイプのリファレンスに全てのAWSリソースプロパティタイプの定義が記載されている。テンプレートを作成するときはココをよーく見る。
リソースの定義では、AWSリソースの種類によりユニークな属性定義(Properties)以外にも、AWSリソースの種類に関わらず使用できるリソース属性(CreationPolicyDeletionPolicyDependsOnMetadataUpdatePolicy)を設定することができる。

AWS CLIでCloudFormationを操作

テンプレートの構文チェック

$ aws cloudformation validate-template \
                      --template-body file://test.json

スタックの作成

$ aws cloudformation create-stack \
                   --stack-name test-stack \
                   --template-body file://test.json

スタックの削除

$ aws cloudformation delete-stack \
                     --stack-name test-stack

スタックの更新

$ aws cloudformation update-stack \
                   --stack-name test-stack \
                   --template-body file://test.json \
                   --capabilities CAPABILITY_IAM

CloudFormationでIAMユーザーを作成する場合、ウィザード途中でチェックボックスをonにする必要がある。
これをパスするために「create-stack」「update-stack」する際は
--capalilities CAPABILITY_IAM
をコマンドに付与する。

テンプレートのサンプル

テンプレートのサンプルを作成した。

  • マルチAZのVPC作成
    • インターネットゲートウェイの作成とアタッチ
    • ルートテーブルの設定
    • セキュリティの設定
      • ネットワークACLとセキュリティグループ
  • RDS(MySQL)の作成
    • DBサブネットグループを作成してマルチAZに
  • EC2の作成
    • EC2のロール作成して設定
    • キーペアの設定
      • キーペアは事前にManagementConsoleで作成しておく必要がある
    • MySQL Clientのyum install

EC2インスタンス起動時に様々なコマンドを実行する方法についてはここを参照。

※どこからでもSSHできる設定になっているので注意!

{
    "AWSTemplateFormatVersion" : "2010-09-09",
    "Resources" : {
        "sampleVPC" : {
            "Type" : "AWS::EC2::VPC",
            "Properties" : {
                "CidrBlock" : "10.0.0.0/16",
                "EnableDnsSupport" : "true",
                "EnableDnsHostnames" : "true",
                "InstanceTenancy" : "default",
                "Tags" : [ {"Key" : "Name", "Value" : "sample-vpc"} ]
            }
        },
        "sampleInternetGateway" : {
            "Type" : "AWS::EC2::InternetGateway",
            "Properties" : {
                "Tags" : [ {"Key" : "Name", "Value" : "sample-igw"} ]
            }
        },
        "AttachGateway" : {
           "Type" : "AWS::EC2::VPCGatewayAttachment",
           "Properties" : {
              "VpcId" : { "Ref" : "sampleVPC" },
              "InternetGatewayId" : { "Ref" : "sampleInternetGateway" }
           }
        },
        "sampleSubnet0" : {
            "Type" : "AWS::EC2::Subnet",
            "Properties" : {
                "VpcId" : { "Ref" : "sampleVPC" },
                "CidrBlock" : "10.0.0.0/24",
                "AvailabilityZone" : "ap-northeast-1a",
                "MapPublicIpOnLaunch" : "true",
                "Tags" : [ { "Key" : "Name", "Value" : "sample-vpc-subnet0" } ]
            }
        },
        "sampleSubnet1" : {
            "Type" : "AWS::EC2::Subnet",
            "Properties" : {
                "VpcId" : { "Ref" : "sampleVPC" },
                "CidrBlock" : "10.0.1.0/24",
                "AvailabilityZone" : "ap-northeast-1c",
                "MapPublicIpOnLaunch" : "true",
                "Tags" : [ { "Key" : "Name", "Value" : "sample-vpc-subnet1" } ]
            }
        },
        "sampleRouteTable" : {
            "Type" : "AWS::EC2::RouteTable",
            "Properties" : {
                "VpcId" : { "Ref" : "sampleVPC" },
                "Tags" : [ { "Key" : "Name", "Value" : "sample-vpc-rt" } ]
            }
        },
        "internetRoute" : {
            "Type" : "AWS::EC2::Route",
            "Properties" : {
                "RouteTableId" : { "Ref" : "sampleRouteTable" },
                "DestinationCidrBlock" : "0.0.0.0/0",
                "GatewayId" : { "Ref" : "sampleInternetGateway" }
            }
        },
        "sampleSubnet0SampleRouteTableAssociation" : {
            "Type" : "AWS::EC2::SubnetRouteTableAssociation",
            "Properties" : {
                "SubnetId" : { "Ref" : "sampleSubnet0" },
                "RouteTableId" : { "Ref" : "sampleRouteTable" }
            }
        },
        "sampleSubnet1SampleRouteTableAssociation" : {
            "Type" : "AWS::EC2::SubnetRouteTableAssociation",
            "Properties" : {
                "SubnetId" : { "Ref" : "sampleSubnet1" },
                "RouteTableId" : { "Ref" : "sampleRouteTable" }
            }
        },
        "sampleNetworkAcl" : {
            "Type" : "AWS::EC2::NetworkAcl",
            "Properties" : {
                "VpcId" : { "Ref" : "sampleVPC" },
                "Tags" : [ { "Key" : "Name", "Value" : "sample-vpc-acl" } ]
            }
        },
        "sampleInBoundNetworkAclEntry" : {
            "Type" : "AWS::EC2::NetworkAclEntry",
            "Properties" : {
                "NetworkAclId" : { "Ref" : "sampleNetworkAcl" },
                "RuleNumber" : "100",
                "Protocol" : "-1",
                "RuleAction" : "allow",
                "Egress" : "false",
                "CidrBlock" : "0.0.0.0/0",
                "Icmp" : { "Code" : "-1", "Type" : "-1" },
                "PortRange" : { "From" : "0", "To" : "65535" }
            }
        },
        "sampleOutBoundNetworkAclEntry" : {
            "Type" : "AWS::EC2::NetworkAclEntry",
            "Properties" : {
                "NetworkAclId" : { "Ref" : "sampleNetworkAcl" },
                "RuleNumber" : "100",
                "Protocol" : "-1",
                "RuleAction" : "allow",
                "Egress" : "true",
                "CidrBlock" : "0.0.0.0/0",
                "Icmp" : { "Code" : "-1", "Type" : "-1" },
                "PortRange" : { "From" : "0", "To" : "65535" }
            }
        },
        "sampleSubnet0NetworkAclAssociation" : {
           "Type" : "AWS::EC2::SubnetNetworkAclAssociation",
           "Properties" : {
              "SubnetId" : { "Ref" : "sampleSubnet0" },
              "NetworkAclId" : { "Ref" : "sampleNetworkAcl" }
           }
        },
        "sampleSubnet1NetworkAclAssociation" : {
           "Type" : "AWS::EC2::SubnetNetworkAclAssociation",
           "Properties" : {
              "SubnetId" : { "Ref" : "sampleSubnet1" },
              "NetworkAclId" : { "Ref" : "sampleNetworkAcl" }
           }
        },
        "defaultSecurityGroup" : {
            "Type" : "AWS::EC2::SecurityGroup",
            "Properties" : {
                "VpcId" : { "Ref" : "sampleVPC" },
                "GroupDescription" : "Allow all communications in VPC",
                "SecurityGroupIngress" : [
                    { "IpProtocol" : "tcp", "FromPort" : "0", "ToPort" : "65535", "CidrIp" : "0.0.0.0/0" },
                    { "IpProtocol" : "udp", "FromPort" : "0", "ToPort" : "65535", "CidrIp" : "0.0.0.0/0" },
                    { "IpProtocol" : "icmp", "FromPort" : "-1", "ToPort" : "-1", "CidrIp" : "0.0.0.0/0" }
                ],
                "Tags" : [ {"Key" : "Name", "Value" : "default-sg"} ]
            }
        },
        "sshSecurityGroup" : {
            "Type" : "AWS::EC2::SecurityGroup",
            "Properties" : {
                "VpcId" : { "Ref" : "sampleVPC" },
                "GroupDescription" : "Enable SSH access via port 22",
                "SecurityGroupIngress" : [
                    { "IpProtocol" : "tcp", "FromPort" : "22",  "ToPort" : "22",  "CidrIp" : "0.0.0.0/0" }
                ],
                "Tags" : [ {"Key" : "Name", "Value" : "ssh-sg"} ]
            }
        },
        "publicWebSecurityGroup" : {
            "Type" : "AWS::EC2::SecurityGroup",
            "Properties" : {
                "VpcId" : { "Ref" : "sampleVPC" },
                "GroupDescription" : "Public Security Group with HTTP access on port 443 from the internet",
                "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" }
                ],
                "Tags" : [ {"Key" : "Name", "Value" : "public-web-sg"} ]
            }
        },
        "appSecurityGroup" : {
            "Type" : "AWS::EC2::SecurityGroup",
            "Properties" : {
                "VpcId" : {"Ref" : "sampleVPC"},
                "GroupDescription" : "Marker security group for Application server.",
                "SecurityGroupIngress" : [
                    { "IpProtocol" : "-1", "FromPort" : "0", "ToPort" : "65535", "SourceSecurityGroupId" : { "Fn::GetAtt" : [ "publicWebSecurityGroup" , "GroupId" ] } }
                ],
                "Tags" : [ {"Key" : "Name", "Value" : "app-sg"} ]
            }
        },
        "dbSecurityGroup" : {
            "Type" : "AWS::EC2::SecurityGroup",
            "Properties" : {
                "VpcId" : {"Ref" : "sampleVPC"},
                "GroupDescription" : "Marker security group for RDS.",
                "SecurityGroupIngress" : [
                    { "IpProtocol" : "-1", "FromPort" : "0", "ToPort" : "65535", "SourceSecurityGroupId" : { "Fn::GetAtt" : [ "appSecurityGroup" , "GroupId" ] } }
                ],
                "Tags" : [ {"Key" : "Name", "Value" : "db-sg"} ]
            }
        },
        "sampleDBSubnetGroup" : {
            "Type" : "AWS::RDS::DBSubnetGroup",
            "Properties" : {
                "DBSubnetGroupDescription" : "description",
                "SubnetIds" : [
                    { "Ref" : "sampleSubnet0" },
                    { "Ref" : "sampleSubnet1" }
                ],
                "Tags" : [ {"Key" : "Name", "Value" : "sample-db-sng"} ]
            }
        },
        "sampeMySQL" : {
            "Type" : "AWS::RDS::DBInstance",
            "Properties" : {
                "AllocatedStorage" : "100",
                "BackupRetentionPeriod" : "",
                "DBInstanceClass" : "db.t1.micro",
                "DBName" : "sample_db",
                "DBSubnetGroupName" : { "Ref" : "sampleDBSubnetGroup" },
                "Engine" : "MySQL",
                "EngineVersion" : "5.6.27",
                "MasterUsername" : "dbadmin",
                "MasterUserPassword" : "Password",
                "MultiAZ" : true,
                "PubliclyAccessible" : false,
                "VPCSecurityGroups" : [ { "Ref" : "dbSecurityGroup" } ],
                "Tags" : [ { "Key" : "Name", "Value" : "sample-mysql" } ]
            }
        },     
        "sampleEC2Role": {
            "Type": "AWS::IAM::Role",
            "Properties": {
                "AssumeRolePolicyDocument": {
                    "Version" : "2012-10-17",
                    "Statement": [ {
                        "Effect": "Allow",
                        "Principal": {
                           "Service": [ "ec2.amazonaws.com" ]
                        },
                        "Action": [ "sts:AssumeRole" ]
                    } ]
                },
                "Path": "/",
                "Policies": [ {
                    "PolicyName": "PowerUserPolicy",
                    "PolicyDocument": {
                        "Version" : "2012-10-17",
                        "Statement": [ {
                            "Effect": "Allow",
                            "Action": "*",
                            "Resource": "*"
                        } ]
                    }
                } ]
            }
        },
        "sampleEC2Profile" : {
            "Type" : "AWS::IAM::InstanceProfile",
            "Properties" : {
                "Path": "/",
                "Roles" : [ { "Ref" : "sampleEC2Role" } ]
            }
        },
        "sampleEC2" : {
            "Type" : "AWS::EC2::Instance",
            "Metadata" : {
                "AWS::CloudFormation::Init" : {
                    "configSets" : {
                        "Install" : [ "Install" ]
                    },
                    "Install" : {
                        "packages" : {
                            "yum" : {
                                "mysql" : []
                            }
                        }
                    }
                }
            },
            "Properties" : {
                "AvailabilityZone" : "ap-northeast-1a",
                "IamInstanceProfile" : { "Ref" : "sampleEC2Profile" },
                "ImageId" : "ami-59bdb937",
                "InstanceInitiatedShutdownBehavior" : "stop",
                "InstanceType" : "m4.large",
                "KeyName" : "sample-key",
                "SecurityGroupIds" : [
                    { "Fn::GetAtt" : [ "appSecurityGroup" , "GroupId" ] },
                    { "Fn::GetAtt" : [ "sshSecurityGroup" , "GroupId" ] }
                ],
                "SubnetId" : { "Ref" : "sampleSubnet0" },
                "Tags" : [ { "Key" : "Name", "Value" : "sample-ec2" } ],
                "Tenancy" : "default",
                "UserData"       : { "Fn::Base64" : { "Fn::Join" : ["", [
                    "#!/bin/bash -xe\n",
                    "yum update -y aws-cfn-bootstrap\n",
                    
                    "# Install the files and packages from the metadata\n",
                    "/opt/aws/bin/cfn-init -v ",
                    "         --stack ", { "Ref" : "AWS::StackName" },
                    "         --resource sampleEC2 ",
                    "         --configsets Install ",
                    "         --region ", { "Ref" : "AWS::Region" }, "\n"
                ]]}}
            }
        }
    }
}