【AWS】RubyでEC2/EBS/ELB/RDSのタグを取得する
はじめに
こんにちは植木和樹です。AWSでは様々なリソース(EC2とかRDS)にタグを設定することで、リソースを分類したりプログラムから参照しやすくする機能があります。機能アップデートによってタグが設定できるリソースも増えています。
- Amazon EC2 リソースにタグを付ける - Amazon Elastic Compute Cloud
- Amazon Web Services ブログ: 【AWS発表】すべてのAmazon RDS のリソースにタグ付け可能に
- 【新機能】ELBにタグ付けできるようになりました!
本日はAWS SDK for Rubyを用いて、EC2/EBS/ELB/RDSからタグを取得してみたいと思います。
環境説明
CmBillingGroupタグについて
下記スクリプトに登場するCmBillingGroupタグは、弊社メンバーズのお客様に提供しているメンバーズポータルサイトでAWS利用金額の分析をする際に用いているタグ名です。CmBillingGroupの値によって利用金額を分類することができます。同一AWSアカウント内で本番用・開発用でそれぞれ利用金額の割合をみたい場合などに設定します。
必要ライブラリ
スクリプトで利用しているaws/profile_parserは望月さん作のAWSプロファイル設定ライブラリです。
formatadorは、Rubyの配列をターミナルできれいにテーブル出力してくれるライブラリです。aws-sdkとあわせて、gemコマンドで事前にインストールしておいてください。
$ gem install aws-sdk $ gem install aws-profile_parser $ gem install formatador
バージョンなど
- ruby
- ruby 1.9.3p547 (2014-05-14 revision 45962) [x86_64-darwin13.3.0]
- AWS SDK for Ruby
- aws-sdk-1.52.0
EC2とEBS
EC2に設定されたNameとCmBillingGroupタグ、また各インスタンスにアタッチされたEBSのタグを取得して表示します。
EC2の場合はDescribeInstancesの結果にタグが含まれるので、tags.に続いてタグ名のメソッドをコールするだけで比較的容易にタグが取得できます。
cm-ec2-describe-billing-tag.rb
#!/usr/bin/env ruby require 'aws-sdk' require 'optparse' require 'formatador' begin require 'aws/profile_parser' rescue LoadError; end ARGV.options do |opt| begin aws_opts = {} opt.on('-h', '--help') { puts opt.help; exit 0 } opt.on('-k', '--access-key ACCESS_KEY') { |v| aws_opts[:access_key_id] = v } opt.on('-s', '--secret-key SECRET_KEY') { |v| aws_opts[:secret_access_key] = v } opt.on('-r', '--region REGION') { |v| aws_opts[:region] = v } opt.on('--profile PROFILE') { |v| parser = AWS::ProfileParser.new; aws_opts = parser.get(v) } opt.parse! if aws_opts.empty? puts opt.help exit 1 end AWS.config(aws_opts) rescue => e $stderr.puts e exit 1 end end instances = [] volumes = [] ec2 = AWS::EC2.new ec2.instances.each do |i| instances << { :id => i.id, :name => i.tags.Name, :billing_group => i.tags.CmBillingGroup, } i.block_devices.each do |v| volume_id = v[:ebs][:volume_id] volumes << { :id => volume_id, :instance_id => i.id, :name => ec2.volumes[volume_id].tags.Name, :billing_group => ec2.volumes[volume_id].tags.CmBillingGroup, } end end Formatador.display_compact_table(instances, [:id, :name, :billing_group]) Formatador.display_compact_table(volumes, [:instance_id, :id, :name, :billing_group])
実行結果
$ ruby cm-ec2-describe-billing-tag.rb --profile default +------------+-------------+---------------+ | id | name | billing_group | +------------+-------------+---------------+ | i-ab2c61ad | Prod-Serv01 | Production | | i-5b40605d | | | +------------+-------------+---------------+ +-------------+--------------+------------------+---------------+ | instance_id | id | name | billing_group | +-------------+--------------+------------------+---------------+ | i-ab2c61ad | vol-97df729d | Prod-Serv01_root | Production | | i-5b40605d | vol-207ccf2a | | | +-------------+--------------+------------------+---------------+
ELB
ELBはEC2と違い、ELBインスタンスにタグを取得するメソッドがありません。そのためAWS::ELB::Clientを用いて低レベルなAPIコールを行いタグを取得します。
cm-elb-describe-billing-tag.rb
#!/usr/bin/env ruby require 'aws-sdk' require 'optparse' require 'formatador' begin require 'aws/profile_parser' rescue LoadError; end ARGV.options do |opt| begin aws_opts = {} opt.on('-h', '--help') { puts opt.help; exit 0 } opt.on('-k', '--access-key ACCESS_KEY') { |v| aws_opts[:access_key_id] = v } opt.on('-s', '--secret-key SECRET_KEY') { |v| aws_opts[:secret_access_key] = v } opt.on('-r', '--region REGION') { |v| aws_opts[:region] = v } opt.on('--profile PROFILE') { |v| parser = AWS::ProfileParser.new; aws_opts = parser.get(v) } opt.parse! if aws_opts.empty? puts opt.help exit 1 end AWS.config(aws_opts) rescue => e $stderr.puts e exit 1 end end loadbalancers = [] elb = AWS::ELB.new elb.load_balancers.each do |lb| response = elb.client.describe_tags(:load_balancer_names => [lb.name]) name_tag = response[:tag_descriptions].first[:tags].find { |tag| tag[:key] == 'Name' } bill_tag = response[:tag_descriptions].first[:tags].find { |tag| tag[:key] == 'CmBillingGroup' } loadbalancers << { :id => lb.name, :name => name_tag.nil? ? "" : name_tag[:value], :billing_group => bill_tag.nil? ? "" : bill_tag[:value], } end Formatador.display_compact_table(loadbalancers, [:id, :name, :billing_group])
実行結果
$ ruby cm-elb-describe-billing-tag.rb --profile default +---------------------------------+-----------+---------------+ | id | name | billing_group | +---------------------------------+-----------+---------------+ | mylb-stac-ElasticL-G94ZD9MKWYN1 | Prod-MyLB | Production | +---------------------------------+-----------+---------------+
RDS
RDSでタグを取得するのはEC2やELBよりも、さらに複雑になっています。list_tags_for_resourceに与えるインスタンス識別子はRDS名でなく、ARN(Amazon Resource Name)である必要があります。
ARN文字列には数字12桁のアカウント番号やリージョンを指定する必要があり、これらを別途取得しておく必要があります。
cm-rds-describe-billing-tag.rb
#!/usr/bin/env ruby require 'aws-sdk' require 'optparse' require 'formatador' begin require 'aws/profile_parser' rescue LoadError; end ARGV.options do |opt| begin aws_opts = {} opt.on('-h', '--help') { puts opt.help; exit 0 } opt.on('-k', '--access-key ACCESS_KEY') { |v| aws_opts[:access_key_id] = v } opt.on('-s', '--secret-key SECRET_KEY') { |v| aws_opts[:secret_access_key] = v } opt.on('-r', '--region REGION') { |v| aws_opts[:region] = v } opt.on('--profile PROFILE') { |v| parser = AWS::ProfileParser.new; aws_opts = parser.get(v) } opt.parse! if aws_opts.empty? puts opt.help exit 1 end AWS.config(aws_opts) rescue => e $stderr.puts e exit 1 end end def get_aws_region AWS.config.region end def get_aws_account_id begin iam = AWS::IAM.new iam = iam.client.get_user r = iam[:user][:arn].match('^arn:aws:iam::([0-9]{12}):.*$')[1] rescue AWS::IAM::Errors::AccessDenied result = $! r = result.to_s.match('^User: arn:aws:iam::([0-9]{12}):.*$')[1] end r end instances = [] rds = AWS::RDS.new rds.instances.each do |i| rds_arn = sprintf("arn:aws:rds:%s:%d:db:%s", get_aws_region, get_aws_account_id, i.db_instance_identifier) response = rds.client.list_tags_for_resource(:resource_name => rds_arn) name_tag = response[:tag_list].find { |tag| tag[:key] == 'Name' } bill_tag = response[:tag_list].find { |tag| tag[:key] == 'CmBillingGroup' } instances << { :id => i.id, :name => name_tag.nil? ? "" : name_tag[:value], :billing_group => bill_tag.nil? ? "" : bill_tag[:value], } end Formatador.display_compact_table(instances, [:id, :name, :billing_group])
実行結果
$ ruby cm-rds-describe-billing-tag.rb --profile default +----------------------------------+-----------+---------------+ | id | name | billing_group | +----------------------------------+-----------+---------------+ | od1ur9hcg5ij6ee | Prod-MyDB | Production | | od1ur9hcg5ij6ee-till201408051140 | | | +----------------------------------+-----------+---------------+
まとめ
EC2とEBSについてはとても簡単にインスタンスからタグを取得できます。しかしELBやRDSはインスタンスのメソッドから取得する術がなく、Clientを経由する必要があります。またRDSの場合はRDSインスタンス名でなくARNを指定しなければならず、少々複雑なコードになってしまいます。
AWS SDK for Rubyの将来的なバージョンでは、より簡単にタグが取得できるよう改善されるかもしれませんので期待しましょう。