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