Dear Loyal Readers,

The basic scenario for a VPC is a public facing subnet, and a private subnet only accessible via the first subnet (this is the web server w/ data backend scenario, aka Scenario 2).  Unlike spawning an instance like I did in my previous post, the challenge to this script is that many of the objects depend on each other, and they don’t get created instantaneously.  So, I am going to have to create the object, but then wait for it to exist before creating something else that depends on it. And, when I detect an error, I’ll have tear it all down in reverse order.

 

# First load what I need
require 'aws-sdk'
require 'net/http'
require 'net/ssh'
require File.expand_path('./ruby_config.rb')
# Then name everything
vpc_idx=4
vpc_name="jpm_vpc#{vpc_idx}"
ig_name ="jpm_ig#{vpc_idx}"
rt_name ="jpm_rt_public#{vpc_idx}"
sn_public_name = "jpm_sn_public#{vpc_idx}"
sn_private_name= "jpm_sn_private#{vpc_idx}"
vpc_ip_base="10.10"
vpc_cidr="#{vpc_ip_base}.0.0/16"
sn_public_cidr="#{vpc_ip_base}.#{vpc_idx}.0/24"
sn_private_cidr="#{vpc_ip_base}.#{vpc_idx+1}.0/24"
# Initialize some variables
vpc_id=nil
ig_id=nil
sn_ids=[]
rt_id=nil
ec2=nil
ec2client=nil
begin 
  ec2 = AWS::EC2.new
  ec2client = ec2.client
rescue
  raise "Failed to get EC2 client"
end
begin # Wrap the whole thing for cleanup rescuing
  ## Create VPC
  puts "\nCreating vpc(#{vpc_name})"
  vpc_res = ec2client.create_vpc :cidr_block=>vpc_cidr
  vpc_id = vpc_res[:vpc][:vpc_id]

  # Wait until done
  print "Waiting for vpc(#{vpc_id}) to exist"
  until ec2client.describe_vpcs[:vpc_set].select { |v| v[:vpc_id]== vpc_id }.size == 1 do
    print "."
    sleep 1
  end
  puts " Done"
  print "Found vpc(#{vpc_id}), waiting for 'available'"
  until ec2client.describe_vpcs[:vpc_set].select { |v| v[:vpc_id]== vpc_id }.first[:state] == "available" do
    print "."
    sleep 1
  end
  puts " Done"

  # name vpc
  puts "Naming vpc: #{vpc_name}"
  ec2client.create_tags( :resources=>[ vpc_id ], :tags=>[:key=>"Name",:value=>vpc_name] )
  ## Create internet gateway
  puts "\nCreating Internet Gateway(#{ig_name})"
  ig_res = ec2client.create_internet_gateway
  ig_id  = ig_res[:internet_gateway][:internet_gateway_id]
  print "Wating for Internet(#{ig_id}) to exist"
  until ec2client.describe_internet_gateways[:internet_gateway_set].select { |ig| ig[:internet_gateway_id]==ig_id }.size == 1 do
    print "."
    sleep 1
  end
  puts " Done"

  # name ig
  puts "Naming internet gateway(#{ig_id}) #{ig_name}"
  ec2client.create_tags( :resources=>[ ig_id ], :tags=>[:key=>"Name",:value=>ig_name] )
  ## Attach IG to VPC
  puts "Attaching Internet Gateway(#{ig_id}) to vpc(#{vpc_id})"
  ec2client.attach_internet_gateway( :internet_gateway_id=>ig_id, :vpc_id=>vpc_id )
  ## create subnets [public/private]
  puts "\nCreating public subnet(#{sn_public_name}): #{sn_public_cidr}"
  sn1_res = ec2client.create_subnet( :vpc_id=>vpc_id, :cidr_block=>sn_public_cidr )
  sn1_id = sn1_res[:subnet][:subnet_id]
  puts "Creating private subnet(#{sn_private_name}): #{sn_private_cidr}"
  sn2_res = ec2client.create_subnet( :vpc_id=>vpc_id, :cidr_block=>sn_private_cidr )
  sn2_id  = sn2_res[:subnet][:subnet_id]
  sn_ids = [sn1_id,sn2_id]
  puts "Waiting for subnets(#{sn_ids.join(",")}) to exist"
  sleep 1 until ec2client.describe_subnets[:subnet_set].select{ |s| s[:subnet_id] == sn1_id }.size == 1
 sleep 1 until ec2client.describe_subnets[:subnet_set].select{ |s| s[:subnet_id] == sn2_id }.size == 1
  puts "Found subnets, waiting for 'available'"
  sleep 1 until ec2client.describe_subnets( :subnet_ids=>sn_ids)[:subnet_set].select { |s| s[:state] == "available" }.size == 2
  puts "Subnets are ready"
  # Name subnets
  puts "Naming subnet(#{sn1_id}): #{sn_public_name}"
  ec2client.create_tags( :resources=>[ sn1_id ], :tags=>[:key=>"Name",:value=>sn_public_name] )
  puts "Naming subnet(#{sn2_id}): #{sn_private_name}"
  ec2client.create_tags( :resources=>[ sn2_id ], :tags=>[:key=>"Name",:value=>sn_private_name] )
  ##  set up routing with net/16, 0.0.0.0/0 ig for public
  puts "\nCreating Routing table #{rt_name}"
  rt_res = ec2client.create_route_table( :vpc_id=>vpc_id )
  rt_id = rt_res[:route_table][:route_table_id]
  my_route_table=nil; ec2.route_tables.each { |rt| my_route_table=rt if rt_id==rt.route_table_id }
  my_route_table.create_route( "0.0.0.0/0", { :internet_gateway=>ig_id } )
  ## Get current rt associations for that subnet and disassociate
  ec2client.describe_route_tables[:route_table_set].select { |rt| !rt[:association_set].empty?}.map { |as_list| as_list[:association_set] }.flatten.select{ |as| as[:subnet_id]==sn1_id }.map { |as| as[:route_table_association_id]}.each { |as_id|
    puts "  Dissocciating routingtable/subnet using #{as_id}"
    ec2client.disassociate_route_table( :association_id=>as_id )
    sleep 1
  }
  puts "Associating RouteTable #{rt_name}(#{rt_id}) with Subnet #{sn_public_name}(#{sn1_id})"
  ec2client.associate_route_table( :subnet_id=>sn1_id, :route_table_id=>rt_id )
  ec2client.create_tags( :resources=>[ rt_id ], :tags=>[:key=>"Name",:value=>rt_name] )
  if FORCE_CLEANUP
    puts "Cleanup forced in 15 sec"
    sleep 15
    raise "was just a test"
  end
rescue => error
  # clean up
  puts "\nGot error: #{error.message}\n#{error.backtrace}"
  if !sn_ids.empty?
    sn_ids.each { |sn_id|
      puts "Found Subnet(#{sn_id}), attempting to clean up"
      puts ec2client.delete_subnet( :subnet_id=>sn_id )
    }
end
  if rt_id
    puts "Found RoutingTable(#{rt_id}), attempting to clean up"
    puts ec2client.delete_route_table( :route_table_id=>rt_id )
  end
  if ig_id
    puts "Found InternetGateway(#{ig_id}), attempting to clean up"
    puts ec2client.detach_internet_gateway( :internet_gateway_id=>ig_id, :vpc_id=>vpc_id )
    puts ec2client.delete_internet_gateway( :internet_gateway_id=>ig_id )
  end
  if vpc_id
    puts "Found vpc(#{vpc_id}), attempting to cleanup"
    puts ec2client.delete_vpc( :vpc_id=>vpc_id )
  end
end #begin

Until the robots take over,
Jonathan Malachowski

Leave a comment

Your email address will not be published. Required fields are marked *

X