Class: MinimalPipeline::Cloudformation

Inherits:
Object
  • Object
show all
Defined in:
lib/minimal_pipeline/cloudformation.rb

Overview

Here is an example of how to use this class to deploy CloudFormation Stacks.

“` cloudformation = MinimalPipeline::Cloudformation.new

parameters =

'Vpc' => 'vpc-123456',
'AsgSubnets' => %w[sg-one sg-two sg-three],
'ElbSecurityGroup' => 'sg-123456',
'CertName' => 'example'

cloudformation.deploy_stack('EXAMPLE_ELB', parameters, 'stack.yaml') name = cloudformation.stack_output(stack_name, 'LoadBalancerName') “`

You will need the following environment variables to be present:

  • `AWS_REGION` or `region`

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(wait_max_attempts = 120, wait_delay = 30) ⇒ Cloudformation

Sets up `Aws::CloudFormation::Client` Requires environment variable `AWS_REGION` or `region` to be set.

stack create or update is complete. a stack's status

Parameters:

  • wait_max_attempts (Fixnum) (defaults to: 120)

    Number of attempts to wait until all

  • wait_delay (Fixnum) (defaults to: 30)

    The sleep interval for checking the status of


37
38
39
40
41
42
43
44
45
46
# File 'lib/minimal_pipeline/cloudformation.rb', line 37

def initialize(wait_max_attempts = 120, wait_delay = 30)
  raise 'You must set env variable AWS_REGION or region.' \
    if ENV['AWS_REGION'].nil? && ENV['region'].nil?

  region = ENV['AWS_REGION'] || ENV['region']
  @client = Aws::CloudFormation::Client.new(region: region)
  @wait_max_attempts = wait_max_attempts
  @wait_delay = wait_delay
  @outputs = {}
end

Instance Attribute Details

#clientObject (readonly)

Instance of `Aws::CloudFormation::Client`


26
27
28
# File 'lib/minimal_pipeline/cloudformation.rb', line 26

def client
  @client
end

#wait_delayObject

Returns the value of attribute wait_delay.


28
29
30
# File 'lib/minimal_pipeline/cloudformation.rb', line 28

def wait_delay
  @wait_delay
end

#wait_max_attemptsObject

Returns the value of attribute wait_max_attempts.


27
28
29
# File 'lib/minimal_pipeline/cloudformation.rb', line 27

def wait_max_attempts
  @wait_max_attempts
end

Instance Method Details

#attempt_to_create_stack(stack_name, stack_parameters, wait_options, attempt = 1) ⇒ Object


118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/minimal_pipeline/cloudformation.rb', line 118

def attempt_to_create_stack(stack_name, stack_parameters, wait_options,
                            attempt = 1)
  puts 'Creating a new stack' if ENV['DEBUG']
  @client.create_stack(stack_parameters)
  @client.wait_until(:stack_create_complete, { stack_name: stack_name },
                     wait_options)
rescue Aws::CloudFormation::Errors::Throttling => error
  raise 'Unable to attempt stack create' if attempt > 5

  delay = attempt * 15
  puts "#{error.message} - Retrying in #{delay}"
  sleep delay
  attempt += 1
  attempt_to_create_stack(stack_name, stack_parameters, wait_options,
                          attempt)
end

#attempt_to_update_stack(stack_name, stack_parameters, wait_options, attempt = 1) ⇒ Object


99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/minimal_pipeline/cloudformation.rb', line 99

def attempt_to_update_stack(stack_name, stack_parameters, wait_options,
                            attempt = 1)
  unless @client.describe_stacks(stack_name: stack_name).stacks.empty?
    puts 'Updating the existing stack' if ENV['DEBUG']
    @client.update_stack(stack_parameters)
    @client.wait_until(:stack_update_complete, { stack_name: stack_name },
                       wait_options)
  end
rescue Aws::CloudFormation::Errors::Throttling => error
  raise 'Unable to attempt stack update' if attempt > 5

  delay = attempt * 15
  puts "#{error.message} - Retrying in #{delay}"
  sleep delay
  attempt += 1
  attempt_to_update_stack(stack_name, stack_parameters, wait_options,
                          attempt)
end

#deploy_stack(stack_name, parameters, template, capabilities = ['CAPABILITY_IAM'], disable_rollback: false) ⇒ Object

Parameters:

  • stack_name (String)

    The name of the CloudFormation stack

  • parameters (Hash)

    Parameters to be passed into the stack


141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/minimal_pipeline/cloudformation.rb', line 141

def deploy_stack(stack_name, parameters, template,
                 capabilities = ['CAPABILITY_IAM'], disable_rollback: false)
  wait_options = {
    max_attempts: @wait_max_attempts,
    delay: @wait_delay
  }

  stack_parameters = {
    stack_name: stack_name,
    template_body: File.read(template),
    capabilities: capabilities,
    parameters: params(parameters)
  }

  attempt_to_update_stack(stack_name, stack_parameters, wait_options)
rescue Aws::CloudFormation::Errors::ValidationError => error
  if error.to_s.include? 'No updates are to be performed.'
    puts 'Nothing to do.' if ENV['DEBUG']
  elsif error.to_s.include? 'Template error'
    raise error
  else
    stack_parameters[:disable_rollback] = disable_rollback
    attempt_to_create_stack(stack_name, stack_parameters, wait_options)
  end
end

#params(parameters) ⇒ Hash

Converts a parameter Hash into a CloudFormation friendly structure

Parameters:

  • parameters (Hash)

    Key value pair of parameters for a CFN stack.

Returns:

  • (Hash)

    CloudFormation friendly data structure of parameter


52
53
54
55
56
57
58
# File 'lib/minimal_pipeline/cloudformation.rb', line 52

def params(parameters)
  parameter_list = []
  parameters.each do |k, v|
    parameter_list.push(parameter_key: k, parameter_value: v)
  end
  parameter_list
end

#stack_exists?(stack_name) ⇒ Boolean

Checks to see if a stack exists

Parameters:

  • stack_name (String)

    The name of the CloudFormation stack

Returns:

  • (Boolean)

    true/false depending on whether or not the stack exists


171
172
173
174
175
176
# File 'lib/minimal_pipeline/cloudformation.rb', line 171

def stack_exists?(stack_name)
  stacks = @client.describe_stacks(stack_name: stack_name).stacks
  !stacks.empty?
rescue ::Aws::CloudFormation::Errors::ValidationError
  false
end

#stack_output(stack, output) ⇒ String

Retrieves the CloudFormation stack output of a single value

Parameters:

  • stack (String)

    The name of the CloudFormation stack

  • output (String)

    The name of the output to fetch the value of

Returns:

  • (String)

    The value of the output for the CloudFormation stack


65
66
67
68
69
70
71
# File 'lib/minimal_pipeline/cloudformation.rb', line 65

def stack_output(stack, output)
  outputs = stack_outputs(stack)
  message = "#{stack.upcase} stack does not have a(n) '#{output}' output!"
  raise message unless outputs.key?(output)

  outputs[output]
end

#stack_outputs(stack, attempt = 1) ⇒ Hash

Retrieves all CloudFormation stack outputs

Parameters:

  • stack (String)

    The name of the CloudFormation stack

Returns:

  • (Hash)

    Key value pairs of stack outputs


77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/minimal_pipeline/cloudformation.rb', line 77

def stack_outputs(stack, attempt = 1)
  response = @client.describe_stacks(stack_name: stack)
  raise "#{stack.upcase} stack does not exist!" if response.stacks.empty?

  @outputs[stack] ||= {}
  if @outputs[stack].empty?
    response.stacks.first.outputs.each do |output|
      @outputs[stack][output.output_key] = output.output_value
    end
  end

  @outputs[stack]
rescue Aws::CloudFormation::Errors::Throttling => error
  raise 'Unable to get stack outputs' if attempt > 5

  delay = attempt * 15
  puts "#{error.message} - Retrying in #{delay}"
  sleep delay
  attempt += 1
  stack_outputs(stack, attempt)
end