PF-09 · AWS · Python
AWS firewall request automation
A CLI that checks whether a firewall request is already open, previews changes only for what is blocked, and verifies the real traffic.
- Role
- Design and implementation
- Period
- Personal project · released Nov 2026
- Cloud
- AWS
- Scale
- 3-step CLI · 4 input formats · 4 resource types
- Key tech
- Python · boto3 · AWS · CloudWatch Logs · Athena · Excel
- 3
- Steps: confirm, modify, flowlog
- 4
- Input formats
- 4
- Resource types identified
At a glance
Problem
Firewall requests often asked for flows that were already open, which added duplicate rules, and finding the related resources and rules meant searching the console account by account.
Approach
netops confirm resolves addresses to AWS resources and evaluates security group rules and routes. Only blocked flows go to modify, which prints a plan first. flowlog searches CloudWatch Logs or Athena for the actual traffic.
Result
By default nothing changes. Adding a rule needs --apply and a typed apply, rules can only be added, and a security group without free slots is never chosen.
Up close
Rules and routes decide
What this code does: Records whether a rule or a missing route is the reason.
if any(not c.ok for c in verdict.checks):
verdict.ok = False
verdict.issues.append("Security group rule needed")
if source.resources and not target.resources:
for resource in source.resources:
ec2 = self.sessions.client("ec2", resource.account)
result = route_analysis.evaluate(
route_analysis.find_table(ec2, resource.subnet_id, resource.vpc_id), dst
)
detail = (
f"via {route_analysis.next_hop(result.routes[0])}"
if result.ok
else f"no route in {result.table_id or 'any table'}"
)
verdict.checks.append(Check("route", resource.label, result.ok, detail))
if not result.ok:
verdict.ok = False
verdict.issues.append("Route needed")
return verdict
Apply only adds rules
What this code does: The only change is adding a rule, and a rule that already exists passes quietly.
def apply(self, change: Change) -> str:
ec2 = self.sessions.client("ec2", change.account)
call = (
ec2.authorize_security_group_ingress
if change.direction == "ingress"
else ec2.authorize_security_group_egress
)
try:
call(GroupId=change.sg_id, IpPermissions=change.permissions)
except ClientError as error:
if error.response["Error"]["Code"] == "InvalidPermission.Duplicate":
return "already present"
raise
return "applied"What I learned
The safe action must be the one that needs no flag. A --dry-run option fails dangerously when someone forgets it.
What I would improve
The security group is chosen automatically, with no menu to pick another. Prefix lists are not evaluated, and flow log search has not been run against a live archive since this refactor.