DEV Community

Ace7-coder
Ace7-coder

Posted on

AWS FinOps Field Notes (2026-11-02): Eliminating Idle NAT Gateways & Unattached EIPs with Python

While auditing cloud infrastructure costs this week, recurring configuration gaps continue to quietly inflate monthly cloud bills.

Field Insight: The Hidden Cost Trap

Tim O'Brien: Says fifty abandoned 100 GB unattached EBS volumes cost $500/month ($6k/year), and that clearing a couple hundred forgotten S3 buckets and EBS volumes is a program rather than a script because every terabyte needs an owner to justify it.

Automated Detection: Auditing Idle NAT Gateways and Unattached Elastic IPs with Boto3

import boto3
from datetime import datetime, timedelta, timezone

def audit_idle_network_costs(region='us-east-1'):
    ec2 = boto3.client('ec2', region_name=region)
    cw = boto3.client('cloudwatch', region_name=region)
    now = datetime.now(timezone.utc)
    seven_days_ago = now - timedelta(days=7)

    # 1. Unassociated Elastic IPs ($0.005/hr = ~$3.60/month each)
    eips = ec2.describe_addresses().get('Addresses', [])
    unattached_eips = [
        addr['AllocationId']
        for addr in eips
        if 'AssociationId' not in addr and 'InstanceId' not in addr
    ]

    # 2. Idle NAT Gateways (<1MB traffic over 7 days = ~$32.40/month base)
    nats = ec2.describe_nat_gateways(Filters=[{'Name': 'state', 'Values': ['available']}]).get('NatGateways', [])
    idle_nats = []
    for nat in nats:
        res = cw.get_metric_statistics(
            Namespace='AWS/NATGateway',
            MetricName='BytesOutFromDestination',
            Dimensions=[{'Name': 'NatGatewayId', 'Value': nat['NatGatewayId']}],
            StartTime=seven_days_ago,
            EndTime=now,
            Period=604800,
            Statistics=['Sum']
        )
        datapoints = res.get('Datapoints', [])
        total_bytes = datapoints[0]['Sum'] if datapoints else 0
        if total_bytes < 1_000_000:
            idle_nats.append(nat['NatGatewayId'])

    print(f"[{region}] Unattached EIPs: {len(unattached_eips)} | Idle NAT Gateways: {len(idle_nats)}")
    return {'unattached_eips': unattached_eips, 'idle_nat_gateways': idle_nats}

if __name__ == '__main__':
    audit_idle_network_costs()

Enter fullscreen mode Exit fullscreen mode

Running one-off audit scripts in single regions does not scale across enterprise AWS Organizations. Sustained cloud cost management requires cross-account role assumption (sts:AssumeRole), cost anomaly correlation, and automated scheduled auditing.

Top comments (0)