Cloudformationで作成した環境を時限的に削除する方法

2015.02.28

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

こんにちは、せーのです。今日はCloudformationでテスト環境、実験環境をサクッと作る際に便利なTipsをご紹介します。

使い捨て用環境を作りたい

例えば一時的にアプリを試す環境が必要になった時、環境を構築し、アプリをデプロイし、色々設定変更や新しいソースを試した後、その環境を削除するのを忘れることはないでしょうか。目的が「環境構築」ではなく「アプリのテスト」であるため、アプリをテストし終わった時点でもう環境の後始末とか面倒でしょうがありません。弊社も新しい機能やロジックを試すためにテスト環境をガンガン立てるのですが「落とし忘れ」のEC2というのがちょくちょく出てきて妙な課金がかかってしまった、なんてことがあったりします。

そういう時用にこのTipsを覚えておくと、万が一環境の落とし忘れがあった際も自動的に削除してくれます。

自分を殺すシェルをatコマンドで忍ばせる

やり方は意外と簡単です。

  • 1. 自分のstackをdeleteするAWS CLIを書いたシェルファイルを作成
  • 2. 1で作ったシェルファイルをatコマンドで指定時間後に実行するようにセット
  • 3. 1と2をEC2立ち上げ時のcfn-initとして設定する

これでOKです。具体的にはcfn-initはこのように書きます。

 "Resources" : {
    "WebServerInstance": {  
      "Type": "AWS::EC2::Instance",
      "Metadata" : {
        "AWS::CloudFormation::Init" : {
          "configSets" : {
            "All" : [ "ConfigureSampleApp", "ConfigureTTL" ]
          },

この例ですと[ConfigureSampleApp]と[ConfigureTTL]という2つのリソースを[WebServerInstance]というEC2を立ち上げる時に処理する、という意味です。今回は上に書いた1、2を[ConfigureTTL]というリソースに書きました。

"ConfigureTTL" : {
   "files" : {
      "/usr/local/bin/deletestack.sh" : {
       "content" : { "Fn::Join" : ["", [ "aws cloudformation delete-stack --region ", { "Ref" : "AWS::Region" }, " --stack-name ", { "Ref" : "AWS::StackId" } ]]},
       "mode"    : "000755",
       "owner"   : "root",
       "group"   : "root"
     }
   },
   "commands" : {
     "schedule_stack_deletion" : {
       "command" : { "Fn::Join" : ["", [ "at -f /usr/local/bin/deletestack.sh now + ", { "Ref" : "StackTTL" }, " minutes" ]]}
     }
   }
 }
}

Cloudformationの書き方とAWS CLI、atコマンドの書き方がわかればなんとなくわかると思います。[StackTTL]に書いた分数が経過すると[deletestack.sh]が実行され、中の[delete-stack]コマンドが叩かれて自分のstackが削除される、という仕組みです。

やってみた

やってみるまでもないのですが、一応やってみます。今回はサンプルがあるのでこちらを流し込みます。

schedulingCFn1

[StackTTL]は変数となっているので好きな数字を入力します。今回は10分後に削除するように設定します。

schedulingCFn2

完成です。簡単ですね。

schedulingCFn3

EC2も立ち上がっています。そして10分後、勝手に消えていきました。

まとめ

いかがでしたでしょうか。今回はサンプルでしたが実際に使う際には鍵も作成、適用し、SSHログインできるようにしておきましょう。ポイントとしてはEC2のIAM RoleにCloudformationを操作する権限を与えておくことです。

  "StackDeletorRole": {
       "Type": "AWS::IAM::Role",
       "Metadata" : { "Comment" : "This role requires permissions to delete each of the stack resources. All other stack resources depend directly or indirectly on this role via DependsOn, Ref, or Fn::GetAtt, to ensure that the role is the last of the stack resources to be deleted." },
       "Properties": {
          "AssumeRolePolicyDocument": {
             "Version" : "2012-10-17",
             "Statement": [ {
                "Effect": "Allow",
                "Principal": {
                   "Service": [ "ec2.amazonaws.com" ]
                },
                "Action": [ "sts:AssumeRole" ]
             } ]
          },
          "Path": "/",
          "Policies" : [{
            "PolicyName": "AllowStackDeletionPolicy",
            "PolicyDocument": {
              "Version" : "2012-10-17",
              "Statement": [
                {
                  "Effect": "Allow",
                  "Action": [ "cloudformation:DeleteStack" ],
                  "Resource": { "Ref" : "AWS::StackId" }
                },
                {
                  "Effect": "Allow",
                  "Action": [ "ec2:DescribeInstances" ],
                  "Resource": "*"
                },
                {
                  "Effect": "Allow",
                  "Action": [ "ec2:TerminateInstances", "ec2:DeleteSecurityGroup" ],
                  "Resource": "*",
                  "Condition": { "StringEquals": { "ec2:ResourceTag/aws:cloudformation:stack-id": { "Ref" : "AWS::StackId" } } }
                },
                {
                  "Effect": "Allow",
                  "Action": [ "iam:DeleteInstanceProfile", "iam:RemoveRoleFromInstanceProfile" ],
                  "Resource": { "Fn::Join" : ["", ["arn:aws:iam::", { "Ref" : "AWS::AccountId" }, ":instance-profile/", { "Ref" : "AWS::StackName" }, "-", "WebServerIAMInstanceProfile", "-*" ]] }
                },
                {
                  "Effect": "Allow",
                  "Action": [ "iam:DeleteRole", "iam:DeleteRolePolicy" ],
                  "Resource": { "Fn::Join" : ["", ["arn:aws:iam::", { "Ref" : "AWS::AccountId" }, ":role/", { "Ref" : "AWS::StackName" }, "-", "StackDeletorRole", "-*" ]] }
                }
              ]
            }
          }]
       }
    }

そしてこのIAM Roleは最後に削除されるようにDependOnプロパティを使って依存をかけていくことが大事です。 ひとつ作っておくと後々とても楽ちんなこの仕組み、是非とも使いこなせるようにしましょう!

参考サイト