DEV Community

Python-T Point
Python-T Point

Posted on • Originally published at pythontpoint.in

🔧 Automate AWS VPC route tables with a python script

💡 Understanding VPC Route Tables — Why They Matter

python script automate aws vpc route tables

Improperly managed VPC route tables can generate up to $2,000 USD per month in unnecessary data‑transfer charges when traffic is forced through NAT gateways instead of direct peering. This post shows how a python script automate aws vpc route tables removes that waste by keeping routes in sync with changing subnet layouts.

📑 Table of Contents

  • 💡 Understanding VPC Route Tables — Why They Matter
  • 🐍 Setting Up Boto3 — How a Python Script Connects
  • 🚀 Creating Route Tables Programmatically — Steps to Automate
  • 🔧 Updating Routes for Dynamic Subnets — Managing Changes
  • 🛠 Adding a New Destination
  • 🧹 Removing Stale Routes
  • 📊 Idempotent Design and Error Handling — Ensuring Reliability
  • ⚙️ Retry Logic with Exponential Backoff
  • 🔐 Using IAM Permissions Safely
  • 🟩 Final Thoughts
  • ❓ Frequently Asked Questions
  • How can I verify that a route was created correctly?
  • Is it safe to run the script concurrently from multiple CI pipelines?
  • Can I run this script from a Lambda function?
  • 📚 References & Further Reading

🐍 Setting Up Boto3 — How a Python Script Connects

Installing the AWS SDK for Python (Boto3) provides the foundation for any python script automate aws vpc route tables workflow.

$ python3 -m venv .venv
$ source .venv/bin/activate
$ pip install boto3


Collecting boto3 Downloading boto3-1.34.45-py3-none-any.whl (131 kB)
Installing collected packages: boto3
Successfully installed boto3-1.34.45
Enter fullscreen mode Exit fullscreen mode

After activating the virtual environment, configure credentials once using the AWS CLI. The script then inherits the default profile.

$ aws configure
AWS Access Key ID [****************ABCD]: AKIAEXAMPLE
AWS Secret Access Key [****************EFGH]: wJalrXUtnFEMI/K7MDENGbPxRfiCYEXAMPLEKEY
Default region name [us-east-1]: us-west-2
Default output format [json]: json


Configuration saved.
Enter fullscreen mode Exit fullscreen mode

Hard‑coding credentials would embed static keys in source files, risking accidental leakage. Using the shared credential file leverages IAM role rotation and keeps secrets out of version control.

Key point: Boto3 follows the same credential provider chain as the AWS CLI, guaranteeing consistent authentication across tools.


🚀 Creating Route Tables Programmatically — Steps to Automate

This section shows how to create a new route table and attach it to a subnet using a python script automate aws vpc route tables.

# create_route_table.py
import boto3
import sys ec2 = boto3.client('ec2')
vpc_id = sys.argv[1] # e.g. vpc-0a1b2c3d4e5f6g7h8
subnet_id = sys.argv[2] # e.g. subnet-0123456789abcdef0 def create_route_table(vpc): response = ec2.create_route_table(VpcId=vpc) rt_id = response['RouteTable']['RouteTableId'] print(f"Created route table {rt_id}") return rt_id def associate_route_table(rt_id, subnet): ec2.associate_route_table(RouteTableId=rt_id, SubnetId=subnet) print(f"Associated {rt_id} with subnet {subnet}") if __name__ == "__main__": rt = create_route_table(vpc_id) associate_route_table(rt, subnet_id)
Enter fullscreen mode Exit fullscreen mode

What this does:

  • create_route_table: Calls ec2.create_route_table, which issues a CreateRouteTable API request. The service allocates a new route‑table object in the VPC’s internal data store and returns its identifier.
  • associate_route_table: Calls ec2.associate_route_table, creating a link in the VPC’s routing metadata so that the subnet inherits the newly created table.
  • CLI arguments: The script receives the VPC and subnet IDs, avoiding hard‑coded values and enabling reuse across environments.

Run the script and verify the result with the AWS CLI.

$ python create_route_table.py vpc-0a1b2c3d4e5f6g7h8 subnet-0123456789abcdef0
Created route table rtb-0a1b2c3d4e5f6g7h8
Associated rtb-0a1b2c3d4e5f6g7h8 with subnet subnet-0123456789abcdef0


{ "RouteTables": [ { "RouteTableId": "rtb-0a1b2c3d4e5f6g7h8", "VpcId": "vpc-0a1b2c3d4e5f6g7h8", "Associations": [ { "SubnetId": "subnet-0123456789abcdef0", "RouteTableAssociationId": "rtbassoc-0a1b2c3d4e5f6g7h8", "Main": false } ], "Routes": [ { "DestinationCidrBlock": "10.0.0.0/16", "GatewayId": "local", "State": "active" } ] } ]
}
Enter fullscreen mode Exit fullscreen mode

Automation guarantees repeatability, eliminates human error, and enables version‑controlled infrastructure definitions. (Also read: 🚀 Creating aws s3 bucket policy with python boto3 tutorial)

Key point: A single script call creates and attaches a route table, making the operation idempotent when combined with existence checks.


🔧 Updating Routes for Dynamic Subnets — Managing Changes

Adding, replacing, or deleting routes as subnets are provisioned or decommissioned keeps the VPC topology current.

🛠 Adding a New Destination

When a new CIDR block appears, the script inserts a route that points to the appropriate target (e.g., a Transit Gateway).

# update_routes.py
import boto3
import sys ec2 = boto3.client('ec2')
rt_id = sys.argv[1] # e.g. rtb-0a1b2c3d4e5f6g7h8
cidr = sys.argv[2] # e.g. 10.10.0.0/16
tg_id = sys.argv[3] # e.g. tgw-0abcdef1234567890 def ensure_route(rt, destination, target): # Check if the route already exists existing = ec2.describe_route_tables(RouteTableIds=[rt])['RouteTables'][0]['Routes'] for r in existing: if r.get('DestinationCidrBlock') == destination: if r.get('TransitGatewayId') == target: print(f"Route to {destination} already points to {target}") return else: ec2.delete_route(RouteTableId=rt, DestinationCidrBlock=destination) print(f"Deleted stale route to {destination}") ec2.create_route( RouteTableId=rt, DestinationCidrBlock=destination, TransitGatewayId=target ) print(f"Added route {destination} → {target}") if __name__ == "__main__": ensure_route(rt_id, cidr, tg_id)
Enter fullscreen mode Exit fullscreen mode

What this does:

  • describe_route_tables: Retrieves the current set of routes for the table, allowing detection of duplicates.
  • delete_route: Removes an outdated entry, preventing conflicts where two routes target the same CIDR.
  • create_route: Issues a CreateRoute API call that writes a new entry into the VPC’s routing metadata, linking the CIDR to the Transit Gateway.

Execute the script after a subnet creation event.

$ python update_routes.py rtb-0a1b2c3d4e5f6g7h8 10.10.0.0/16 tgw-0abcdef1234567890
Added route 10.10.0.0/16 → tgw-0abcdef1234567890
Enter fullscreen mode Exit fullscreen mode

Dynamic updates avoid a catch‑all 0.0.0.0/0 entry, which would force all traffic through a single gateway and create a bottleneck.

🧹 Removing Stale Routes

When a subnet is deleted, its CIDR must be removed to prevent black‑hole traffic.

$ aws ec2 delete-route -route-table-id rtb-0a1b2c3d4e5f6g7h8 -destination-cidr-block 10.10.0.0/16



{ "Return": true
}
Enter fullscreen mode Exit fullscreen mode

Automating this step aligns with the principle of least privilege for network paths.

Key point: The script checks for existing routes before creating or deleting, ensuring safe, repeatable changes even when multiple processes modify the same VPC.


📊 Idempotent Design and Error Handling — Ensuring Reliability

Resilience to transient failures and safe repeated execution are critical for production automation.

⚙️ Retry Logic with Exponential Backoff

Network hiccups or API throttling raise Throttling errors. The script catches botocore.exceptions.ClientError and retries with exponential backoff. (More onPythonTPoint tutorials)

# retry_util.py
import time
import botocore.exceptions def retry(fn, max_attempts=5, base_delay=1): attempt = 0 while attempt < max_attempts: try: return fn() except botocore.exceptions.ClientError as e: if e.response['Error']['Code'] == 'Throttling': delay = base_delay * (2 ** attempt) print(f"Throttled – retrying in {delay}s") time.sleep(delay) attempt += 1 else: raise raise RuntimeError("Max retries exceeded")
Enter fullscreen mode Exit fullscreen mode

What this does:

  • retry: Wraps any AWS SDK call, catching throttling exceptions and applying exponential backoff.
  • base_delay: Starts at 1 second; each subsequent retry doubles the wait time, reducing API pressure.

Integrate the helper into the route‑creation function.

# in update_routes.py
from retry_util import retry def ensure_route(rt, destination, target): def api_call(): return ec2.create_route( RouteTableId=rt, DestinationCidrBlock=destination, TransitGatewayId=target ) retry(api_call) print(f"Added route {destination} → {target}")
Enter fullscreen mode Exit fullscreen mode

Simple fixed delays would not adapt to varying throttle limits; exponential backoff minimizes total execution time while respecting service quotas.

🔐 Using IAM Permissions Safely

Grant the script only the actions it requires.

# route-automation-policy.yaml
Version: "2012-10-17"
Statement: - Effect: Allow Action: - ec2:CreateRoute - ec2:DeleteRoute - ec2:DescribeRouteTables - ec2:AssociateRouteTable Resource: "*"
Enter fullscreen mode Exit fullscreen mode

What this does:

  • Effect: Allow enables the listed actions.
  • Resource: "*" targets all route tables; finer scoping can be added via condition keys if needed.

According to the AWS IAM documentation, least‑privilege policies are the primary defense against accidental or malicious changes.

Key point: Combining idempotent logic with scoped IAM permissions yields a robust automation pipeline that can run from CI/CD without manual oversight.


Automating VPC route tables removes the hidden cost of stale routes and keeps network traffic on the optimal path.

🟩 Final Thoughts

The presented python script automate aws vpc route tables workflow replaces manual console steps, cuts latency, and eliminates unnecessary data‑transfer fees. By using Boto3’s low‑level API calls, the script directly manipulates the routing metadata stored in the VPC control plane, guaranteeing that each change is persisted atomically.

For developers managing dynamic environments—auto‑scaled microservices, multi‑VPC peering, or on‑demand test labs—the automation pattern scales without additional operational overhead. The same utilities can be reused for other network resources (e.g., security groups) by extending the retry and idempotency helpers.

❓ Frequently Asked Questions

How can I verify that a route was created correctly?

Run aws ec2 describe-route-tables -route-table-id <rtb-id> and inspect the Routes array for the expected DestinationCidrBlock and target identifier.

Is it safe to run the script concurrently from multiple CI pipelines?

Yes. The script checks for existing routes before creating or deleting them. AWS API operations are atomic, and the retry logic handles throttling caused by parallel calls.

Can I run this script from a Lambda function?

Absolutely. Package the script with its dependencies, assign the minimal IAM policy shown earlier, and configure the Lambda VPC settings to allow outbound internet access for the AWS SDK calls.

💡 Want to practise this hands-on? DigitalOcean gives new accounts $200 free credit for 60 days — enough to spin up a full Linux/Docker/Kubernetes environment at no cost.

📚 Recommended reading: Best DevOps & cloud books on Amazon — from Linux fundamentals to Kubernetes in production, curated for working engineers.

📚 References & Further Reading

Top comments (0)