【アップデート】CloudFormationに事前にリソースがどう変更されるのかを確認できる「Change Sets」が追加されました!

2016.03.30

花見に行きたい(酒飲みたい)森永です。

CloudFormationに素敵なアップデートが来たのでご紹介します!

Change Setsとは

今まではCloudFormationのテンプレートをアップデートする際、どのリソースが追加・削除・変更されるのか、変更される場合は、置換が発生するかしないのかという確認は、実際に実行してみないと困難でした。

今回追加されたChange Setsで、アップデートを実行する前にどのような変更が加えられるかを確認できるようになりました!
アップデートにおける簡単なdry-runのような機能と考えるといいかと思います!すごい!
あくまでも簡単なです。詳しくは後述します。

AWS CloudFormation Adds Change Sets for Insight into Stack Updates

試してみる

以下の図のようにVPCにSubnetを2つ配置という構成でひとまずスタックを作成します。
ひとつのAZにサブネットが寄っていて気持ち悪いですね。

構成図1

テンプレートはこんな感じです。

{
  "AWSTemplateFormatVersion" : "2010-09-09",
  "Description" : "VPC Network Template",
  "Mappings" : {
    "StackConfig" : {
      "VPC"                : { "CIDR" : "10.0.0.0/16" },
      "Subnet1"    : { "CIDR" : "10.0.0.0/24" },
      "Subnet2"    : { "CIDR" : "10.0.1.0/24" }
    }
  },
  "Resources" : {
    "VPC" : {
      "Type" : "AWS::EC2::VPC",
      "Properties" : {
        "CidrBlock" : { "Fn::FindInMap" : [ "StackConfig", "VPC", "CIDR" ]},
        "InstanceTenancy" : "default"
      }
    },
    "InternetGateway" : {
      "Type" : "AWS::EC2::InternetGateway"
    },
    "AttachGateway" : {
      "Type" : "AWS::EC2::VPCGatewayAttachment",
      "Properties" : {
        "VpcId" : { "Ref" : "VPC" },
        "InternetGatewayId" : { "Ref" : "InternetGateway" }
      }
    },
    "RouteTable" : {
      "Type" : "AWS::EC2::RouteTable",
      "DependsOn" : "AttachGateway",
      "Properties" : {
        "VpcId" : { "Ref" : "VPC" }
      }
    },
    "Route" : {
      "Type" : "AWS::EC2::Route",
      "DependsOn" : "AttachGateway",
      "Properties" : {
        "RouteTableId" : { "Ref" : "RouteTable" },
        "DestinationCidrBlock" : "0.0.0.0/0",
        "GatewayId" : { "Ref" : "InternetGateway" }
      }
    },
    "Subnet1": {
      "Type": "AWS::EC2::Subnet",
      "DependsOn" : "AttachGateway",
      "Properties": {
        "VpcId": { "Ref": "VPC" },
        "AvailabilityZone": { "Fn::Select": [ "0", { "Fn::GetAZs" : { "Ref": "AWS::Region" } }] },
        "CidrBlock": { "Fn::FindInMap" : [ "StackConfig", "Subnet1", "CIDR" ]}
      }
    },
    "Subnet1RouteTableAssociation" : {
      "Type" : "AWS::EC2::SubnetRouteTableAssociation",
      "Properties" : {
        "SubnetId" : { "Ref" : "Subnet1" },
        "RouteTableId" : { "Ref" : "RouteTable" }
      }
    },
    "Subnet2": {
      "Type": "AWS::EC2::Subnet",
      "DependsOn" : "AttachGateway",
      "Properties": {
        "VpcId": { "Ref": "VPC" },
        "AvailabilityZone": { "Fn::Select": [ "0", { "Fn::GetAZs" : { "Ref": "AWS::Region" } }] },
        "CidrBlock": { "Fn::FindInMap" : [ "StackConfig", "Subnet2", "CIDR" ]}
      }
    },
    "Subnet2RouteTableAssociation" : {
      "Type" : "AWS::EC2::SubnetRouteTableAssociation",
      "Properties" : {
        "SubnetId" : { "Ref" : "Subnet2" },
        "RouteTableId" : { "Ref" : "RouteTable" }
      }
    }
  }
}

スタックの作成時には特に変わったことはありません。
今までどおり作成します。

Create_A_New_Stack

ここからが本番です。
先ほどのテンプレートを以下のようにアップデートしようと思います。

  • サブネットを2つのAZに分散
    • 別のAZに作り直すことになるので、サブネットの置換が発生するはず!
  • VPCでDNSホスト名の設定をオンに
  • サブネットでパブリックIP自動割当をオンに
    • 設定変更だけなので、置換は発生しないはず!

構成図2

{
  "AWSTemplateFormatVersion" : "2010-09-09",
  "Description" : "VPC Network Template",
  "Mappings" : {
    "StackConfig" : {
      "VPC"                : { "CIDR" : "10.0.0.0/16" },
      "Subnet1"    : { "CIDR" : "10.0.0.0/24" },
      "Subnet2"    : { "CIDR" : "10.0.2.0/24" }
    }
  },
  "Resources" : {
    "VPC" : {
      "Type" : "AWS::EC2::VPC",
      "Properties" : {
        "CidrBlock" : { "Fn::FindInMap" : [ "StackConfig", "VPC", "CIDR" ]},
        "InstanceTenancy" : "default",
        "EnableDnsHostnames" : "true"
      }
    },
    
    ...省略...
    
    "Subnet1": {
      "Type": "AWS::EC2::Subnet",
      "DependsOn" : "AttachGateway",
      "Properties": {
        "VpcId": { "Ref": "VPC" },
        "AvailabilityZone": { "Fn::Select": [ "0", { "Fn::GetAZs" : { "Ref": "AWS::Region" } }] },
        "CidrBlock": { "Fn::FindInMap" : [ "StackConfig", "Subnet1", "CIDR" ]},
        "MapPublicIpOnLaunch" : "true"
      }
    },
    
    ...省略...
    
    "Subnet2": {
      "Type": "AWS::EC2::Subnet",
      "DependsOn" : "AttachGateway",
      "Properties": {
        "VpcId": { "Ref": "VPC" },
        "AvailabilityZone": { "Fn::Select": [ "1", { "Fn::GetAZs" : { "Ref": "AWS::Region" } }] },
        "CidrBlock": { "Fn::FindInMap" : [ "StackConfig", "Subnet2", "CIDR" ]}
      }
    },
    
    ...省略...
    
  }
}

では早速やっていきましょう。
先ほど作成したスタックを選択し、「Create Change Set」を選択します。

CloudFormation_Management_Console

新しいテンプレートを選択します。
勿論、テンプレートはそのままに、パラメータだけ変更することも可能です。

Create_A_New_Change_Set

Change Setの名前と説明を記載します。
こちらはスタック名とはまた違うものとなります。

Create_A_New_Change_Set 2

最後に、タグや通知の設定を行います。
今回は何も指定しないで先へ進みます。

Create_A_New_Change_Set 3

レビュー画面で問題なければ「Create change set」を選択します。

Create_A_New_Change_Set 4

Change Setの詳細画面が出てきて、しばらくすると「Status」がCREATE_COMPLETEになります。
この時点では実際にアップデートはしていないので早いです。
今回は変更だけを加えたので、「Action」がModifyとなっていることが確認できます。

個人的に嬉しいのは「Replacement」です。
CloudFormationでアップデートをした際に、変更するプロパティによってはリソースを作りなおす(置換する)必要があるのですが、EC2のインスタンスを作りなおされたら結構困ります。
「Replacement」を確認することで、アップデート時にそのリソースが置換されるのかが分かります。

Change_Set_Detail

ちなみにこの詳細画面は、スタック詳細画面の一番最下段から確認できます。

Stack_Detail

では、実際にアップデートを実行してみましょう!
Change Setの詳細画面の右上にある「Execute」をクリックします。

Change_Set_Detail 4

確認画面が出ますので、「Execute」!

Change_Set_Detail 5

すると実際のアップデート作業が始まります。
正常に完了したらUPDATE_COMPLETEと出ます!
Replacementと出ていたSubnet2は削除されているのが分かりますね。

CloudFormation_Management_Console 2

もう一つ別のChange Setを作成してみましょう。
今度は以下の様な変更を加えます。

  • Subnet2を消して、Subnet3を追加
    • 実際にはリネームするだけですが、removeaddを確認できるはず!
  • AvailabilityZoneに存在しないAZを指定
    • dry-runであれば検知して警告して欲しいところ…

構成図3

{
  "AWSTemplateFormatVersion" : "2010-09-09",
  "Description" : "VPC Network Template",
  "Mappings" : {
    "StackConfig" : {
      "VPC"                : { "CIDR" : "10.0.0.0/16" },
      "Subnet1"    : { "CIDR" : "10.0.0.0/24" },
      "Subnet3"    : { "CIDR" : "10.0.3.0/24" }
    }
  },
  "Resources" : {
    
    ...省略...
    
    "Subnet3": {
      "Type": "AWS::EC2::Subnet",
      "DependsOn" : "AttachGateway",
      "Properties": {
        "VpcId": { "Ref": "VPC" },
        "AvailabilityZone": { "Fn::Select": [ "3", { "Fn::GetAZs" : { "Ref": "AWS::Region" } }] },
        "CidrBlock": { "Fn::FindInMap" : [ "StackConfig", "Subnet3", "CIDR" ]}
      }
    },
    "Subnet3RouteTableAssociation" : {
      "Type" : "AWS::EC2::SubnetRouteTableAssociation",
      "Properties" : {
        "SubnetId" : { "Ref" : "Subnet3" },
        "RouteTableId" : { "Ref" : "RouteTable" }
      }
    }
  }
}

手順はさきほどと同じなので省略しますが、詳細画面に予想していたRemoveAddが出ていることを確認できました。
が、期待していたAvailabilityZoneに関しての警告は出ず、「Status」はCREATE_COMPLETEとなりました。

Change_Set_Detail 3

実行してみたらどうなるでしょうか。

CloudFormation_Management_Console 3

やはり、アップデートに失敗しました。
エラー文を見ると、AZのインデックス3なんて存在しないよ!とのことです。ごもっとも。

最後に

CloudFormationのアップデートを事前に確認できるということで非常に嬉しいアップデートでした。
これで、テンプレートを間違えて消したくないリソースを消してしまったり、リソースの置換をしてしまって大変なことになる、ということは減るかと思います。

が、設定した値が正しいかどうかは未だ確認できないようですので、簡単なdry-runの機能として捉えて、今までどおりしっかり確認したうえでアップデート作業は行ったほうが宜しいかと思います。