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 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リソースの種類に関わらず使用できるリソース属性(CreationPolicy、DeletionPolicy、DependsOn、Metadata、UpdatePolicy)を設定することができる。
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』
をコマンドに付与する。
テンプレートのサンプル
テンプレートのサンプルを作成した。
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" ]]}} } } } }