This will guide you through using Boto3 to create a VPC programmatically.
This guide assumes:
- That you have an AWS account
- That you have Boto3 authentication configured
This will especially be helpful for you if:
- You're migrating from EC2-Classic to VPC
- You're migrating from Boto to Boto3
If you would just like the commands to run, you can skip to the code summary. Otherwise, read on for a step-by-step guide.
Detailed Guide
On the AWS VPC console, there are helpful wizards that will create and configure your VPC. Let's take the VPC with a Single Public Subnet wizard, and do the same thing using Boto3.
These are the goals:
- Create a new VPC
- Create a subnet for that VPC
- Create a security group that will allow you to SSH, HTTP, and HTTPS traffic into your VPC-launched instances
- Launch an instance into your VPC (optional)
What the wizard creates:
The VPC with a Single Public Subnet wizard creates for you one VPC, and:
- One internet gateway
- Two route tables:
- A secondary route table
- A "main" route table
- "The main route table controls the routing for all subnets that are not explicitly associated with any other route table. You can add, remove, and modify routes in the main route table." [src]
- A subnet, by default named 'Public Subnet', automatically associated with the secondary route table, and given access to the internet gateway
1. Creating the VPC
In an example file called vpc.py
, let's create a VPC.
For this, we'll use Boto3's EC2 resource objects:
import boto3
ec2 = boto3.resource(service_name='ec2')
Let's use ec2.create_vpc()
to create our VPC and return it. The function returns a VPC resource object.
# vpc.py
import boto3
def create_and_configure_vpc():
ec2 = boto3.resource('ec2')
vpc = ec2.create_vpc(CidrBlock='10.0.0.0/16', AmazonProvidedIpv6CidrBlock=True)
return vpc
Okay, so that should automatically set everything up! Kidding.
At this point, we have a VPC in our default region, and a main route table that's automatically created and attached to our VPC. We also have a default security group that allows traffic to and from within that security group.
2. Adding a subnet and gateway:
We can create a subnet by using the VPC resource's create_subnet()
function. Let's modify what we have in create_and_configure_vpc()
append the following before return vpc
:
# vpc.py in create_and_configure_vpc()
# ... snip ...
# Assign IPv6 block for subnet using CIDR provided by Amazon, except different size (must use /64)
ipv6_subnet_cidr = vpc.ipv6_cidr_block_association_set[0]['Ipv6CidrBlock']
ipv6_subnet_cidr = ipv6_subnet_cidr[:-2] + '64'
subnet = vpc.create_subnet(CidrBlock='10.0.0.0/24', Ipv6CidrBlock=ipv6_subnet_cidr)
return vpc
Let's create an Internet Gateway and attach it to our VPC.
Add the following lines to your function before the return:
# vpc.py in create_and_configure_vpc()
# ... snip ...
internet_gateway = ec2.create_internet_gateway()
internet_gateway.attach_to_vpc(VpcId=vpc.vpc_id)
return vpc
3. Creating our route table
Next, let's create a route table.
# vpc.py in create_and_configure_vpc()
# ... snip ...
route_table = vpc.create_route_table()
A newly created route table will have routes for the local network through IPv4 addresses and (if enabled) IPv6. However, we need to add the routes for our internet gateway, so let's write in the following in our function:
# vpc.py in create_and_configure_vpc()
# ... snip ...
route_ig_ipv4 = route_table.create_route(DestinationCidrBlock='0.0.0.0/0', GatewayId=internet_gateway.internet_gateway_id)
route_ig_ipv6 = route_table.create_route(DestinationIpv6CidrBlock='::/0', GatewayId=internet_gateway.internet_gateway_id)
Lastly, don't forget to associate this route table with our subnet!
# vpc.py in create_and_configure_vpc()
# ... snip ...
route_table.associate_with_subnet(SubnetId=subnet.id)
Recap! After adding these three code snippets to our current function, here's what we have so far in vpc.py
:
# vpc.py
import boto3
def create_and_configure_vpc():
ec2 = boto3.resource('ec2')
# Create the VPC, and request an IPv6 CIDR block
vpc = ec2.create_vpc(CidrBlock='10.0.0.0/16', AmazonProvidedIpv6CidrBlock=True)
# Assign IPv6 block for subnet using CIDR provided by Amazon, except different size (must use /64)
ipv6_subnet_cidr = vpc.ipv6_cidr_block_association_set[0]['Ipv6CidrBlock']
ipv6_subnet_cidr = ipv6_subnet_cidr[:-2] + '64'
subnet = vpc.create_subnet(CidrBlock='10.0.0.0/24', Ipv6CidrBlock=ipv6_subnet_cidr)
internet_gateway = ec2.create_internet_gateway()
internet_gateway.attach_to_vpc(VpcId=vpc.vpc_id)
route_table = vpc.create_route_table()
route_ig_ipv4 = route_table.create_route(DestinationCidrBlock='0.0.0.0/0', GatewayId=internet_gateway.internet_gateway_id)
route_ig_ipv6 = route_table.create_route(DestinationIpv6CidrBlock='::/0', GatewayId=internet_gateway.internet_gateway_id)
route_table.associate_with_subnet(SubnetId=subnet.id)
return vpc
If at this point you feel the urge to break this up into smaller functions, feel free to do so. Adding all the code to a single function is just for the sake of this example.
4. Security Groups
Whew. Lastly, we need to create and configure our security groups. In this example, we'll only create one. Add the following line to our function:
# vpc.py in create_and_configure_vpc()
# ... snip ...
sg = vpc.create_security_group(GroupName="sample-name", Description="A sample description")
This creates an empty group with no rules. In this case, we'll allow incoming TCP traffic on port 22 from all IP address (which ultimately you should modify for security reasons). We'll also allow incoming TCP traffic on port 80 and 443.
# vpc.py in create_and_configure_vpc()
# ... snip ...
ip_ranges = [{
'CidrIp': '0.0.0.0/0'
}]
ip_v6_ranges = [{
'CidrIpv6': '::/0'
}]
perms = [{
'IpProtocol': 'TCP',
'FromPort': 80,
'ToPort': 80,
'IpRanges': ip_ranges,
'Ipv6Ranges': ip_v6_ranges
}, {
'IpProtocol': 'TCP',
'FromPort': 443,
'ToPort': 443,
'IpRanges': ip_ranges,
'Ipv6Ranges': ip_v6_ranges
}, {
'IpProtocol': 'TCP',
'FromPort': 22,
'ToPort': 22,
'IpRanges': ip_ranges,
'Ipv6Ranges': ip_v6_ranges
}]
And lastly, add these rules as ingress rules to the security group we created:
# vpc.py in create_and_configure_vpc()
# ... snip ...
sg.authorize_ingress(IpPermissions=perms)
The IpPermissions
parameter allows us to specify more than one rule at a time. The Code Summary shows the final function body.
Code Summary:
And you're done! Instances launched in this subnet should allow traffic to the internet, and TCP traffic from port 80, 22, and 443.
Here's the code summary!
Ideally you might want to separate these into separate functions and handle for HTTP error codes on the API requests.
def create_and_configure_vpc():
ec2 = boto3.resource('ec2')
# Create the VPC
vpc = ec2.create_vpc(CidrBlock='10.0.0.0/16', AmazonProvidedIpv6CidrBlock=True)
# Create an Internet Gateway and attach it to the VPC
internet_gateway = ec2.create_internet_gateway()
internet_gateway.attach_to_vpc(VpcId=vpc.vpc_id) # Returns None
# Route Table
route_table = vpc.create_route_table()
route_ig_ipv4 = route_table.create_route(DestinationCidrBlock='0.0.0.0/0', GatewayId=internet_gateway.internet_gateway_id)
route_ig_ipv6 = route_table.create_route(DestinationIpv6CidrBlock='::/0', GatewayId=internet_gateway.internet_gateway_id)
# Create a subnet in our VPC
# Assign IPv6 block for subnet using CIDR provided by Amazon, except different size (must use /64)
ipv6_subnet_cidr = vpc.ipv6_cidr_block_association_set[0]['Ipv6CidrBlock']
ipv6_subnet_cidr = ipv6_subnet_cidr[:-2] + '64'
subnet = vpc.create_subnet(CidrBlock='10.0.0.0/24', Ipv6CidrBlock=ipv6_subnet_cidr)
# Associate it with subnet
route_table.associate_with_subnet(SubnetId=subnet.id)
# Security groups
sg = vpc.create_security_group(GroupName="sample-name", Description="A sample description")
ip_ranges = [{
'CidrIp': '0.0.0.0/0'
}]
ip_v6_ranges = [{
'CidrIpv6': '::/0'
}]
perms = [{
'IpProtocol': 'TCP',
'FromPort': 80,
'ToPort': 80,
'IpRanges': ip_ranges,
'Ipv6Ranges': ip_v6_ranges
}, {
'IpProtocol': 'TCP',
'FromPort': 443,
'ToPort': 443,
'IpRanges': ip_ranges,
'Ipv6Ranges': ip_v6_ranges
}, {
'IpProtocol': 'TCP',
'FromPort': 22,
'ToPort': 22,
'IpRanges': ip_ranges, # Remember to change this!
'Ipv6Ranges': ip_v6_ranges # Remember to change this!
}]
sg.authorize_ingress(IpPermissions=perms)
return vpc
*Using boto3 to launch an instance is beyond the scope of this tutorial. However it should be straightforward using the AWS web console:
- Click 'Launch Instance'
- Choose your VPC and the subnet to launch the instance into
- When prompted, select 'Existing Security Groups' and select the security group we created (
sample-name
)
Scribbles, notes, and troubleshooting:
- "If your instance was launched from an AMI that is not configured to use DHCPv6, you must manually configure your instance to recognize an IPv6 address assigned to the instance." src
Links and extra reading:
- http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/TroubleshootingInstancesConnecting.html
- http://boto3.readthedocs.io/en/latest/reference/services/ec2.html?highlight=vpc#vpc
- http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPCRouteTables.html
- http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/default-vpc.html#default-vpc-basics
- http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/get-started-ipv6.html
- https://www.digitalocean.com/community/tutorials/understanding-ip-addresses-subnets-and-cidr-notation-for-networking
- http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/vpc-ip-addressing.html
Originally published: Mar 19, 2017