Creating a VPC with public and private subnet in AWS with the Ruby SDK

Presents a Ruby AWS SDK script for creating a VPC with public and private subnets, internet gateway, and route table associations. Emphasizes dependency-aware orchestration, readiness waits, and cleanup logic for reliable infrastructure provisioning.

Coveros Staff

November 21, 2014

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
Coveros Staff

Coveros Staff

This post represents the collective insights of the Coveros team. Our staff consists of software experts who bring deep experience in secure agile development, DevOps, testing, and software quality. Over the past 20 years, Coveros has trained more than 30,000 professionals and worked with half of the Fortune 100 companies on mission-critical software development challenges. We draw on this extensive experience to share practical insights, proven strategies, and real-world solutions that help organizations build better software faster and more securely.