AWS CLIでサービスの各種コマンドを動かしてみる(CloudFormation編)

awscli-cloudformation

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

久々となりましたこのシリーズ、今回は、CloudFormationについて見て行きたいと思います。

目次

 

CloudFormationのテンプレート編集環境を整える

まずはAWS CLIのインストールから。導入はこの辺りのコマンドでサクッと。

$ sudo easy_install pip
$ sudo pip install awscli
$ sudo pip install awscli --upgrade
$ complete -C aws_completer aws
$ aws --version

環境変数周りも最低限実行させるだけであれば以下環境変数を設定するだけで行けます。

export AWS_DEFAULT_REGION=ap-northeast-1 (※任意のリージョン名を指定)
export AWS_ACCESS_KEY_ID=XXXXXXXXXXXXXXXXXXXXX
export AWS_SECRET_ACCESS_KEY=XXXXXXXXXXXXXXXXXXXXXXXXXXX 

ついでCloudFormationのテンプレートファイル。ブツはJSONファイルなので極端な話テキストエディタ等で自由に編集出来るのですが、ファイルの構成上、また取り扱う要素の規模によっては数百行〜数千行の大きなファイルとなってしまう可能性があり、非常にミスを誘発しやすい側面がありますので、ここではAWS Toolkit for Eclipseを使って土台を整える事にします(導入方法の記載があるエントリは以下)。こちらを整える事で、一部補完と構文チェック等をIDE上で行ってくれるようになります。

今回のお試し用に作ってみたファイルはこちら。Security Groupを作成するテンプレートです。任意のVPCを作成した上で、そのVPCをテンプレート内部で直に指定しています。

cfn01-template

{
  "AWSTemplateFormatVersion" : "2010-09-09",
  "Description" : "Create Security Group.",
  "Resources" : {
    "InstanceSecurityGroup" : {
      "Type" : "AWS::EC2::SecurityGroup",
      "Properties" : {
        "GroupDescription" : "Allow http to client host",
        "VpcId" : "(任意のVpc-ID)",
        "SecurityGroupIngress" : [{
          "IpProtocol" : "tcp",
          "FromPort" : "80",
          "ToPort" : "80",
          "CidrIp" : "0.0.0.0/0"
        }],
        "SecurityGroupEgress" : [{
           "IpProtocol" : "tcp",
           "FromPort" : "80",
           "ToPort" : "80",
           "CidrIp" : "0.0.0.0/0"
        }]
      }
    }
  }
}

 

スタック(Stack)に関する操作

スタックに関する操作は以下。

create-stack
describe-stacks
list-stacks
update-stack
cancel-update-stack
delete-stack

create-stack

テンプレート情報を元に、スタックを作成します。

上記テンプレートを元に実行してみます。ターミナルでEclipse配下の実行対象ファイルがあるフォルダに移動した後に以下コマンドを実行。(コマンドは実際は1行です)

$ aws cloudformation create-stack
  --stack-name cm-sg-http
  --region ap-northeast-1
  --template-body file://SecurityGroup.template
{
    "StackId": "arn:aws:cloudformation:ap-northeast-1:XXXXXXXXXX:stack/cm-sg-http/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXX"
}

出来ました。実際管理コンソールで中身も確認出来てます。

cfn04-sg

テンプレートを変更してみましょう。4〜9行目にParametersセクションを追加し、その入力値をVPC-IDとして利用。31〜36行目で、作成したセキュリティグループのIDを出力しています。

{
  "AWSTemplateFormatVersion" : "2010-09-09",
  "Description" : "Create Security Group.",
  "Parameters" : {
    "vpc-id" :{
      "Type" : "String",
      "Description" : "VPC-ID that Security Group depends."
    }
  }, 
  "Resources" : {
    "InstanceSecurityGroup" : {
      "Type" : "AWS::EC2::SecurityGroup",
      "Properties" : {
        "GroupDescription" : "Allow http to client host",
        "VpcId" : { "Ref" : "vpc-id" },
        "SecurityGroupIngress" : [{
          "IpProtocol" : "tcp",
          "FromPort" : "80",
          "ToPort" : "80",
          "CidrIp" : "0.0.0.0/0"
        }],
        "SecurityGroupEgress" : [{
           "IpProtocol" : "tcp",
           "FromPort" : "80",
           "ToPort" : "80",
           "CidrIp" : "0.0.0.0/0"
        }]
      }
    }
  },
  "Outputs" : {
    "SecurityGroup" : {
      "Description" : "Security Group ID",
      "Value" :  { "Ref" : "InstanceSecurityGroup" }
    }
  }
}

実行。内容は同じですがスタック名を別名にしています。この場合ですと上記で作ったセキュリティグループと同じ内容で別のスタック(セキュリティグループ)が出来上がる形になります。

$ aws cloudformation create-stack
  --stack-name cm-sg-http-v2
  --region ap-northeast-1
  --template-body file://SecurityGroup.template
  --parameters ParameterKey=vpcId,ParameterValue=vpc-XXXXXXX
{
    "StackId": "arn:aws:cloudformation:ap-northeast-1:XXXXXXXXXXXX:stack/cm-sg-http-v2/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXX"
}

実行後の管理コンソール。

cfn02-stacks

cfn03-sgs

describe-stacks

指定スタックの詳細な情報を表示します。スタック名を指定しない場合はアカウントに紐づく全ての作成済みスタックの一覧を表示します。

$ aws cloudformation describe-stacks --stack-name cm-sg-http
{
    "Stacks": [
        {
            "StackId": "arn:aws:cloudformation:ap-northeast-1:XXXXXXXXXXXXX:stack/cm-sg-http/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXX", 
            "Description": "Create Security Group.", 
            "Tags": [], 
            "StackStatusReason": null, 
            "CreationTime": "2013-10-24T15:31:38.960Z", 
            "StackName": "cm-sg-http", 
            "StackStatus": "CREATE_COMPLETE", 
            "DisableRollback": false
        }
    ]
}

$ aws cloudformation describe-stacks --region ap-northeast-1 | jq '.Stacks | length'
7

list-stacks

該当アカウントで作成・実行したCloudFormationのスタック一覧を表示します。

$ aws cloudformation list-stacks
{
    "StackSummaries": [
        {
            "StackId": "arn:aws:cloudformation:ap-northeast-1:XXXXXXXXXXXX:stack/cm-sg-http-v2/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXX", 
            "TemplateDescription": "Create Security Group.", 
            "StackStatusReason": null, 
            "CreationTime": "2013-10-24T16:08:05.217Z", 
            "StackName": "cm-sg-http-v2", 
            "StackStatus": "CREATE_COMPLETE"
        }, 
        {
            "StackId": "arn:aws:cloudformation:ap-northeast-1:XXXXXXXXXXXX:stack/cm-sg-http/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXX", 
            "TemplateDescription": "Create Security Group.", 
            "StackStatusReason": null, 
            "CreationTime": "2013-10-24T15:31:38.960Z", 
            "StackName": "cm-sg-http", 
            "StackStatus": "CREATE_COMPLETE"
        },
        :
        :
}

update-stack

テンプレートを用いてスタックの更新を行います。更新に先立ち、さっき使ったテンプレートを一部編集してみます。ポート番号設定値の箇所を直書き(80)の値からパラメータを介して設定出来るように変更してみます(というケースが発生したという仮定で進めてみます)。

{
  "AWSTemplateFormatVersion" : "2010-09-09",
  "Description" : "Create Security Group.",
  "Parameters" : {
    "vpcId" :{
      "Type" : "String",
      "Description" : "VPC-ID that Security Group depends."
    },
    "PortNumber" : {
      "Type" : "String",
      "Description" : "valid PortNumber."
    }
  }, 
  "Resources" : {
    "InstanceSecurityGroup" : {
      "Type" : "AWS::EC2::SecurityGroup",
      "Properties" : {
        "GroupDescription" : "Allow http to client host",
        "VpcId" : { "Ref" : "vpcId" },
        "SecurityGroupIngress" : [{
          "IpProtocol" : "tcp",
          "FromPort" : { "Ref" : "PortNumber" },
          "ToPort" : { "Ref" : "PortNumber" },
          "CidrIp" : "0.0.0.0/0"
        }],
        "SecurityGroupEgress" : [{
           "IpProtocol" : "tcp",
           "FromPort" : "80",
           "ToPort" : "80",
           "CidrIp" : "0.0.0.0/0"
        }]
      }
    }
  },
  "Outputs" : {
    "SecurityGroup" : {
      "Description" : "Security Group ID",
      "Value" :  { "Ref" : "InstanceSecurityGroup" }
    }
  }
}

実行。80→8080になるようにしてみました。

$ aws cloudformation update-stack
    --stack-name cm-sg-http
    --template-body file://SecurityGroup.template
    --parameters ParameterKey=vpcId,ParameterValue=vpc-0f5de367 ParameterKey=PortNumber,ParameterValue=8080
{
    "StackId": "arn:aws:cloudformation:ap-northeast-1:XXXXXXXXXX:stack/cm-sg-http/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXX"
}

スタック実行結果。

update_stack

そして実際の内容はこちら。更新されていますね。

update_stack2

cancel-update-stack

更新中のスタック処理をキャンセルします。これが成功すると、スタック更新処理はロールバックされ、以前のスタック構成に戻ります。※ちなみにUPDATE_IN_PROGRESSステータスのスタックのみに有効です。

...という訳で更新に時間が掛かりそうなインスタンスを用意する事にします。RDSインスタンスを一旦作り、それを更新する際にキャンセルするという流れで確認してみましょう。公式ドキュメントからリソースを引っ張ってきて、以下の様なテンプレートを作成しました。更新の確認を行いたいだけなのでアクセスする為の諸情報は特に連携させていませんのであしからず。

{
  "AWSTemplateFormatVersion" : "2010-09-09",
  "Description" : "Create RDS Instance.",
  "Parameters" : {
    "DBName" :{
      "Type" : "String",
      "Description" : "Database Name."
    },
    "DBAllocatedStorage" : {
      "Type" : "String",
      "Description" : "DB Allocated Storage."
    }
  }, 
  "Resources" : {
    "DBInstance" : {
       "Type": "AWS::RDS::DBInstance",
       "Properties": {
          "DBName"            : { "Ref" : "DBName" },
          "Engine"            : "MySQL",
          "EngineVersion"     : "5.6.13",
          "MasterUsername"    : "rootusername",
          "DBInstanceClass"   : "db.t1.micro",
          "DBSecurityGroups"  : [ { "Ref" : "DBSecurityGroup" } ],
          "AllocatedStorage"  : { "Ref" : "DBAllocatedStorage" },
          "MasterUserPassword": "rootpassword"
       }
    },

    "DBSecurityGroup": {
       "Type": "AWS::RDS::DBSecurityGroup",
       "Properties": {
          "DBSecurityGroupIngress": { "EC2SecurityGroupName": { "Ref": "WebAndDBServerSecurityGroup" } },
          "GroupDescription"      : "Frontend and DB Access"
       }
    },

    "WebAndDBServerSecurityGroup" : {
       "Type" : "AWS::EC2::SecurityGroup",
       "Properties" : {
          "GroupDescription" : "Enable HTTP access via port 80 and SSH and 3306 access",
          "SecurityGroupIngress" : [
             {"IpProtocol" : "tcp", "FromPort" : "80", "ToPort" : "80", "CidrIp" : "0.0.0.0/0"},
             {"IpProtocol" : "tcp", "FromPort" : "22", "ToPort" : "22", "CidrIp" : "0.0.0.0/0"},
             {"IpProtocol" : "tcp", "FromPort" : "3306", "ToPort" : "3306", "CidrIp" : "0.0.0.0/0"}
          ]
       }
    }
  }
}

上記テンプレートを用いてRDSのスタックを作成。

$ aws cloudformation create-stack
    --stack-name cmrds
    --template-body file://RDSMySQL.template
    --parameters ParameterKey=DBName,ParameterValue=cmrds ParameterKey=DBAllocatedStorage,ParameterValue=5
{
    "StackId": "arn:aws:cloudformation:ap-northeast-1:XXXXXXXXXXXX:stack/cmrds/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
}

出来ました。

update_stack3

更新用のコマンドを実行します。ストレージ容量を5→10GiBに変更するようにしてみました。

$ aws cloudformation update-stack
    --stack-name cmrds
    --template-body file://RDSMySQL.template
    --parameters
        ParameterKey=DBName,ParameterValue=cmrds
        ParameterKey=DBAllocatedStorage,ParameterValue=10

実行直後のステータスです。更新が始まっています。

cancel-update_stack1

更新処理が終わる前に以下のコマンドを実行します。

$ aws cloudformation cancel-update-stack --stack-name cmrds

ステータスを確認します。ロールバック処理が走り、イベント情報には『キャンセルされました』の旨が表示され、しばらくするとステータスも戻りました。

cancel-update_stack3

delete-stack

所定のスタックを削除します。削除処理を実行すると、ステータスも削除中に変更されます。

$ aws cloudformation delete-stack --stack-name cmrds

 

テンプレート(Template)に関する操作

テンプレートに関するコマンドは以下2つ。

get-template
validate-template

get-template

指定したスタックで使用したテンプレートの情報を表示します。

$ aws cloudformation get-template --stack-name cm-sg-http
{
    "TemplateBody": {
        "AWSTemplateFormatVersion": "2010-09-09", 
        "Outputs": {
            "SecurityGroup": {
                "Description": "Security Group ID", 
                "Value": {
                    "Ref": "InstanceSecurityGroup"
                }
            }
        }, 
        "Resources": {
            "InstanceSecurityGroup": {
                "Type": "AWS::EC2::SecurityGroup", 
                "Properties": {
                    "SecurityGroupIngress": [
                        {
                            "ToPort": {
                                "Ref": "PortNumber"
                            }, 
                            "IpProtocol": "tcp", 
                            "CidrIp": "0.0.0.0/0", 
                            "FromPort": {
                                "Ref": "PortNumber"
                            }
                        }
                    ], 
                    "VpcId": {
                        "Ref": "vpcId"
                    }, 
                    "GroupDescription": "Allow http to client host", 
                    "SecurityGroupEgress": [
                        {
                            "ToPort": "80", 
                            "IpProtocol": "tcp", 
                            "CidrIp": "0.0.0.0/0", 
                            "FromPort": "80"
                        }
                    ]
                }
            }
        }, 
        "Description": "Create Security Group.", 
        "Parameters": {
            "vpcId": {
                "Type": "String", 
                "Description": "VPC-ID that Security Group depends."
            }, 
            "PortNumber": {
                "Type": "String", 
                "Description": "valid PortNumber."
            }
        }
    }
}

validate-template

テンプレートの構文チェックを行います。特に問題ない場合はテンプレートの中身が表示されるようですが、以下のように一部細工をしてみると...

validate-template

エラーが表示されました。

$ aws cloudformation validate-template --template-body file://SecurityGroup.template
A client error (ValidationError) occurred: Template format error: JSON not well-formed. (line 5, column 14)

スタックイベント(Stack events)に関する操作

対応するコマンドは以下。

describe-stack-events

describe-stack-events

指定のスタック名で実施されている各種イベントの内容を一覧表示します。

$ aws cloudformation describe-stack-events --stack-name cm-sg-http
{
    "StackEvents": [
        {
            "StackId": "arn:aws:cloudformation:ap-northeast-1:XXXXXXXXXX:stack/cm-sg-http/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", 
            "EventId": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", 
            "ResourceStatus": "UPDATE_COMPLETE", 
            "ResourceType": "AWS::CloudFormation::Stack", 
            "Timestamp": "2013-10-27T05:47:15.200Z", 
            "StackName": "cm-sg-http", 
            "PhysicalResourceId": "arn:aws:cloudformation:ap-northeast-1:XXXXXXXXXX:stack/cm-sg-http/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", 
            "LogicalResourceId": "cm-sg-http"
        }, 
        {
            "StackId": "arn:aws:cloudformation:ap-northeast-1:XXXXXXXXXX:stack/cm-sg-http/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", 
            "EventId": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", 
            "ResourceStatus": "UPDATE_COMPLETE_CLEANUP_IN_PROGRESS", 
            "ResourceType": "AWS::CloudFormation::Stack", 
            "Timestamp": "2013-10-27T05:47:13.902Z", 
            "StackName": "cm-sg-http", 
            "PhysicalResourceId": "arn:aws:cloudformation:ap-northeast-1:XXXXXXXXXX:stack/cm-sg-http/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", 
            "LogicalResourceId": "cm-sg-http"
        },
        :
        :

 

リソース(Resources)に関する操作

対応するコマンドは以下。

describe-stack-resource                                  
describe-stack-resources
list-stack-resources

describe-stack-resource

指定スタック名のリソースに関する情報を表示します。リソース名と論理リソースIDで対象を指定。

$ aws cloudformation describe-stack-resource --stack-name cm-sg-http --logical-resource-id InstanceSecurityGroup
{
    "StackResourceDetail": {
        "StackId": "arn:aws:cloudformation:ap-northeast-1:XXXXXXXXXX:stack/cm-sg-http/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXX", 
        "ResourceStatus": "UPDATE_COMPLETE", 
        "ResourceType": "AWS::EC2::SecurityGroup", 
        "LastUpdatedTimestamp": "2013-10-27T05:47:12.543Z", 
        "StackName": "cm-sg-http", 
        "PhysicalResourceId": "sg-XXXXXXXXXX", 
        "LogicalResourceId": "InstanceSecurityGroup"
    }
}

describe-stack-resources

指定スタック名に紐づくリソースの一覧を表示します。

$ aws cloudformation describe-stack-resources --stack-name XXXXXXXXXXXX-Network | jq '.StackResources[].ResourceType'
"AWS::EC2::Subnet"
"AWS::EC2::RouteTable"
"AWS::EC2::NetworkAcl"
"AWS::EC2::Subnet"
"AWS::EC2::RouteTable"
"AWS::EC2::NetworkAcl"
"AWS::EC2::Subnet"
"AWS::EC2::Subnet"
"AWS::EC2::SecurityGroup"
"AWS::EC2::SubnetRouteTableAssociation"
"AWS::EC2::NetworkAclEntry"
"AWS::EC2::SubnetNetworkAclAssociation"
"AWS::EC2::NetworkAclEntry"
"AWS::EC2::SubnetNetworkAclAssociation"
"AWS::EC2::NetworkAclEntry"
"AWS::EC2::NetworkAclEntry"
"AWS::EC2::NetworkAclEntry"
"AWS::EC2::NetworkAclEntry"
"AWS::EC2::NetworkAclEntry"
"AWS::EC2::NetworkAclEntry"
"AWS::EC2::SubnetRouteTableAssociation"
"AWS::EC2::Route"
"AWS::EC2::SubnetRouteTableAssociation"
"AWS::EC2::NetworkAclEntry"
"AWS::EC2::NetworkAclEntry"
"AWS::EC2::SubnetNetworkAclAssociation"
"AWS::EC2::SubnetRouteTableAssociation"
"AWS::EC2::SubnetNetworkAclAssociation"
$

list-stack-resources

指定スタックの全てのリソースを表示。describe〜は『Running』及び『Deleted』なものを表示するのに対し、こちらは『全て』、という認識で良いのかしら?(ヘルプより)

$ aws cloudformation list-stack-resources --stack-name XXXXXXXXXXX-Network | jq '.StackResourceSummaries | length'
28

まとめ

CloudFormationについては当エントリで紹介したコマンドで以上となります。CloudFormation自体、インスタンス作成で大幅に効率化・時間を短縮出来ますが、このAWS CLIを利用する事で更にその辺りがスムーズに進められるのではないでしょうか。ターミナルからコマンド数発で環境がみるみるうちに出来上がっていくのは壮観です。