前回、AWS の設定用JSONで少し幸せになる方法 ってことで書いたネタの続き。
いろいろいじってると、もう少し便利にしたくなってきました。
設定のコメントアウト
1個目はシンプルなもの。
CloudFormation のデバッグをしていると、いろいろエラーが出てきて、コメントアウトしたい!ってなるんだけど、前回の正規表現だと、意識的にコメントをつけることは出来ても、JSONとして書いているところをコメントアウト出来ない。
ということで、下記のようにコメントアウトにも対応しました。先頭に 「//」がある時しか対応してないけど、個人的にはこれだけで十分です。
def self.strip_comments(str)
ret_str = str.gsub(/\/\/[^'"]*$/, "\n").gsub(/^\/\/*$/, "")
return ret_str
end
CloudFormation のファイル分割
JSONファイルが長くなるとやっぱりわけたい。
もともと CloudFormation には分割のための TemplateUrl とかあるんですが、手元のファイルで分割したりしながらやりたいわけで…
でも、分割しちゃうと、いろいろな IDの参照とかが出来なくなってしまうので、いちいち IDを確認して Parameters に書き入れるなんて作業が必要になり、作っては消し するたびにむなしいコピペが発生します。
Terraform 導入も考えたけど、まだいろいろツラみもあるようだし…
ファイル分割できるだけで だいぶ幸せになれるんだけどな〜 と思って、少し Ruby SDK を眺めたところ、それなりに便利な方法が出来ました :-)
前提
- VPC で切られた環境に構築。
- CloudFormation の設定ファイルは、例えば下記みたいに分割
- ネットワーク設定
- 踏み台サーバ
- ELB
- CloudFormation の各設定は、自分で依存関係を意識した上で、それぞれ順番に別stackとして実行する。
- 分割された個々のファイルの中で、参照したい {"Ref": } については、ちゃんと "Parameters" の中で定義しておく(値は後で上書かれる)。
分割した設定ファイルをそれぞれ分割して別stack として実行するので、踏み台サーバの stack だけを削除して、デバッグして再実行する なんてことも出来ます。
もちろん、設定ファイルは、もっと増やしてもOKです。
Parameters の解決方法
AWS SDK for Ruby を入れている状態で、下記みたいにやってみると...
[ec2-user@ip-xx-xxx-xx-xx scripts]$ aws.rb --region ap-northeast-1
Aws> cf = Aws::CloudFormation::Client.new
=> #<Aws::CloudFormation::Client>
Aws> cf.describe_stack_resources({ :stack_name => "sample" })
いろいろなリソースが出てくるんですが、すべてのリソースについて下記のような記載が出てきます。
logical_resource_id="AttacheGateway",
physical_resource_id="VPC-f-Attac-xxxxxxxxxxxxx",
resource_type="AWS::EC2::VPCGatewayAttachment",
どうやら logical_resource_id というのが CloudFormation で書いた定義名で、physical_resource_id というのが、参照時に入れるべき値のようです。
とすると、これらの値を取得して Parameters につっこんであげれば、参照の問題が解決しそう。
ファイル分割の実行例
だいぶ省略して雑(&せつな)に書いてますが、こんな感じの ruby スクリプトを用意しました。
#! /usr/bin/ruby
require './aws-common.rb'
stacks = {
"network" => {
:template => "./json/network.json",
:ref => []
},
"fumidai" => {
:template => "./json/fumidai.json",
:ref => ["network"]
},
"elb" => {
:template => "./json/elb.json",
:ref => ["network"]
}
}
vpc_name = ARGV[0]
template = ARGV[1]
stack_name = vpc_name + "-" + template
params = AWSutil.load_params_from_stack(vpc_name + "-" + stacks[template][:ref][0])
params = AWSutil.delete_unused_params(stacks[template][:template], params)
AWSutil.cf_start(stack_name, stacks[template][:template], params)
require 'aws-sdk'
require 'aws_config'
require './jsoncfg.rb'
Aws.config.update(AWSConfig.default.config_hash)
module AWSutil
def self.cf_start(stack_name, template_file, params)
cf = Aws::CloudFormation::Client.new
template = JSON_cfg.loadFromFile(template_file)
template_body = JSON.generate(template)
puts JSON.pretty_generate(template)
stack = Aws::CloudFormation::Stack.new(stack_name)
if stack.exists? then
cf.update_stack({ stack_name: stack_name, template_body: template_body, on_failure: "DO_NOTHING", capabilities: ["CAPABILITY_IAM"], parameters: params })
else
cf.create_stack({ stack_name: stack_name, template_body: template_body, on_failure: "DO_NOTHING", capabilities: ["CAPABILITY_IAM"], parameters: params })
end
end
def self.delete_unused_params(template_file, params)
template = JSON_cfg.loadFromFile(template_file)
t_params = template["Parameters"]
new_params = []
params.each do |param|
if t_params[param["parameter_key"]]
p param["parameter_key"] + ": " + param["parameter_value"]
new_params.push param
else
end
end
return new_params
end
def self.load_params_from_stack(stack_name)
cf = Aws::CloudFormation::Client.new
resources = cf.describe_stack_resources({ :stack_name => stack_name })
params = []
resources[0].each do | res |
param = {}
param["parameter_key"] = res[:logical_resource_id]
param["parameter_value"] = res[:physical_resource_id]
param["use_previous_value"] = true
params.push(param)
end
return params
end
end
やっていることはシンプルで、下記のとおりです。
- 実行するstackの中で、参照したいリソースに関するstack を定義する
- 参照先の stack に関する describe_stack_resources を呼ぶ
- describe_stack_resources の返り値をもとに params を作る
- 実行したいテンプレートに存在しない params を削除する
- CloudFormation 実行時の Parameters として取得した params を設定したうえでテンプレートを実行する
% ruby ./aws-cf-script.rb sample elb
こうすると、elb が参照している "sample-network" stack のリソースを取得し、VPCのIDなどを parameters として取り込んで、"sample-elb" stack を実行してくれます。
こんな感じで、ファイルも分割できて、コメントもつけられるようになり、CloudFormation の設定用JSON を作る作業がさらに少し幸せになりました :-)