#!/usr/bin/env ruby # The "main" gem is a framework for command line applications require "main" # load configuration from .env file require 'dotenv/load' # Import only the relevant AWS SDK require 'aws-sdk-acm' require 'aws-sdk-elasticloadbalancingv2' # Useful for debugging # require "pry" class ACM attr_reader :client def initialize(client) @client = client end # return the ARN of a certificate from its domain name def certificate_arn_by_domain(domain_name) cert_summary = client.list_certificates.certificate_summary_list.detect { |cert_summary| cert_summary.domain_name == domain_name } cert_summary.certificate_arn end # return the certificate_summary_list object from AWS sdk or yields if a block is given def list_certificates(&block) response = client.list_certificates if block_given? response.certificate_summary_list.each do |cert_summary| yield(cert_summary) end else response.certificate_summary_list end end # return the certificate object from AWS sdk or yields if a block is given def describe_certificate(certificate_arn_or_domain_name, &block) certificate_arn = if acm_arn?(certificate_arn_or_domain_name) certificate_arn_or_domain_name else certificate_arn_by_domain(certificate_arn_or_domain_name) end response = client.describe_certificate(certificate_arn: certificate_arn) certificate = response.certificate if block_given? yield(certificate) else certificate end end # return the certificate ARN of the created certificate def request_certificate(domain_name) response = client.request_certificate({ domain_name: domain_name, validation_method: "DNS", # idempotency_token: "IdempotencyToken", }) response.certificate_arn end # return true/false if the input looks like an ACM ARN def acm_arn?(input) /\Aarn:aws:acm:/.match(input) end end # print the certificate status (used in a yielded block) def print_certificate_status(certificate) puts "Domain name: #{certificate.domain_name}" puts " ARN: #{certificate.certificate_arn}" puts " Status: #{certificate.status}" puts " Created at: #{certificate.created_at}" puts " Not before: #{certificate.not_before}" puts " Not after: #{certificate.not_after}" puts " Issuer: #{certificate.issuer}" puts " Renewable eligibility: #{certificate.renewal_eligibility}" if certificate.domain_validation_options certificate.domain_validation_options.each do |validation_option| puts " Validation option: #{validation_option.validation_method}" puts " Status: #{validation_option.validation_status}" case validation_option.validation_method when "DNS" if validation_option.resource_record puts " Record name: #{validation_option.resource_record.name}" puts " Record type: #{validation_option.resource_record.type}" puts " Record value: #{validation_option.resource_record.value}" else puts " Record: _unavailable_" end when "EMAIL" puts " Emails : #{validation_option.validation_emails}" end end else puts " Validation option: _unavailable_" end end # print the certificate summary (used in a yielded block) def print_certificate_summary(cert_summary) puts "Domain name: #{cert_summary.domain_name}" puts " ARN: #{cert_summary.certificate_arn}" end class ELBv2 attr_reader :client def initialize(client) @client = client end # return a boolean indicating if the cert has been attached to the listener def add_listener_certificate(listener_arn, certificate_arn) response = client.add_listener_certificates({ listener_arn: listener_arn, certificates: [ { certificate_arn: certificate_arn, # is_default: false, }, ], }) response.certificates.first.certificate_arn == certificate_arn end # return the load_balancer object from AWS sdk or yields if a block is given def describe_load_balancer(load_balancer_arn) response = client.describe_load_balancers({ load_balancer_arns: [ load_balancer_arn ], }) load_balancer = response.load_balancers.first if block_given? yield(load_balancer) else load_balancer end end end Main { mode 'list' do def run() acm = ACM.new(acm_client) acm.list_certificates do |cert_summary| print_certificate_summary(cert_summary) end rescue Aws::Errors::ServiceError => ex exit_status exit_failure puts ex.message end end mode 'status' do argument('domain') { required } def run() domain_name = params[:domain].value acm = ACM.new(acm_client) acm.describe_certificate(domain_name) do |certificate| exit_status exit_failure unless certificate.status == "ISSUED" print_certificate_status(certificate) end rescue Aws::Errors::ServiceError => ex puts "#{ex.class.name}: #{ex.message}" exit_failure! end end mode 'create' do argument('domain') { required } def run() domain_name = params[:domain].value acm = ACM.new(acm_client) certificate_arn = acm.request_certificate(domain_name) acm.describe_certificate(certificate_arn) do |certificate| exit_status exit_failure unless certificate.status == "ISSUED" print_certificate_status(certificate) end rescue Aws::Errors::ServiceError => ex puts "#{ex.class.name}: #{ex.message}" exit_failure! end end mode 'enable' do argument('domain') { required } environment('ELB_ARN'){ required } environment('ELB_LISTENER_ARN'){ required } def run() domain_name = params['domain'].value elb_arn = params['ELB_ARN'].value elb_listener_arn = params['ELB_LISTENER_ARN'].value acm = ACM.new(acm_client) certificate_arn = acm.certificate_arn_by_domain(domain_name) certificate = acm.describe_certificate(certificate_arn) if certificate.status == "ISSUED" elbv2 = ELBv2.new(elbv2_client) elbv2.add_listener_certificate(elb_listener_arn, certificate_arn) elbv2.describe_load_balancer(elb_arn) do |load_balancer| puts "Certificate has been added to load-balancer, change DNS configuration :" puts " Record name: #{domain_name}." puts " Record type: CNAME" puts " Record value: #{load_balancer.dns_name}." end else exit_status exit_failure puts "Certificate for '#{domain_name}' is not available for ELB (status: #{certificate.status})" end rescue Aws::Errors::ServiceError => ex puts "#{ex.class.name}: #{ex.message}" exit_failure! end end def acm_client args = {} args[:profile] = ENV['AWS_PROFILE'] if ENV['AWS_PROFILE'] # args[:logger] = Logger.new(STDOUT) Aws::ACM::Client.new(args) end def elbv2_client args = {} args[:profile] = ENV['AWS_PROFILE'] if ENV['AWS_PROFILE'] Aws::ElasticLoadBalancingV2::Client.new(args) end }