
Secure by Design, Cheap by Architecture: Mastering AWS Networking for Security & Cost Wins in 2026
Secure by Design, Cheap by Architecture: Master AWS networking to cut costs & boost security with endpoints, PrivateLink & 2026 tools.
In AWS, networking is the quiet force that simultaneously shapes your security posture and a surprisingly large portion of your monthly bill. Cross-AZ traffic, NAT Gateway processing, public IPv4 addresses, and internet egress quietly accumulate into five- or six-figure costs while expanding your attack surface.
The highest-ROI designs improve both security and cost at the same time. This complete guide merges deep technical insight with production-ready implementation so you can move from understanding to deployment in a single reading.
1. The Cost & Security Drivers You Actually Feel
| Component | Typical Pricing (us-east-1) | Why It Hurts | Security Side Effect |
|---|---|---|---|
| NAT Gateway | $0.045/hr + $0.045/GB processed | High-volume private → public traffic | Traffic leaves the VPC boundary |
| Cross-AZ data transfer | $0.01/GB each direction | Chatty microservices / multi-AZ NAT hairpin | Larger blast radius |
| Public IPv4 | $0.005/hr (~$3.65/month) per address | Idle Elastic IPs, public ALBs, NAT GWs | Expanded public attack surface |
| Internet egress | $0.09/GB (first 10 TB) | Direct S3/ALB serving or uncached content | Data leaves AWS-controlled paths |
| Interface VPC Endpoints | $0.01/hr per AZ + $0.01/GB | Still far cheaper than NAT for most services | Keeps traffic private |
Key free win: Gateway VPC Endpoints for S3 and DynamoDB carry zero hourly charge and zero data-processing fee.
2. High-ROI Optimizations That Improve Security
A. Replace NAT Paths with VPC Endpoints (Highest ROI)
Private workloads talking to S3, DynamoDB, ECR, CloudWatch Logs, Secrets Manager or SSM through a NAT Gateway pay both processing fees and expose traffic to the public internet path.
Solution
- Gateway Endpoints (S3 + DynamoDB) → free
- Interface Endpoints (PrivateLink) → $0.01/GB instead of $0.045/GB
Savings example: 10 TB/month of S3 traffic through NAT ≈ $450/month in processing alone. Gateway Endpoint = $0.
B. Eliminate Unnecessary Cross-AZ Traffic
A service pair exchanging 1 TB/day across zones costs roughly $600/month before any other charges.
Fixes
- Co-locate chatty tiers in the same AZ when latency allows
- EKS: enable topology-aware routing (PreferClose / PreferSameZone)
- Deploy one NAT Gateway per AZ and ensure route tables point to the local NAT
C. CloudFront + Private Origins
Serving content directly from S3 or an ALB incurs full internet egress rates. CloudFront changes the economics (S3 → CloudFront is free) and adds WAF + Shield at the edge.
3. Security Features That Directly Reduce Cost
- PrivateLink / VPC Endpoints → no public exposure + lower data-processing cost
- Security Hub + GuardDuty Runtime Monitoring (included in Threat Analytics plan since Oct 2026) → early detection of cryptomining or data exfiltration
- Security Hub Remediation Plans (launched 1 Oct 2026) → group findings by root cause and deliver multi-format fix instructions (CLI, Terraform, CDK, CloudFormation)
- AWS Well-Architected Agent (preview Oct 2026) → AI-powered analysis across cost, security, performance, and reliability with automation-ready recommendations
4. Recommended 2026 Architecture
1
2
3
4
5
6
7
8
9
10
Internet
│
CloudFront + WAF + Shield
│
ALB (private subnets) or VPC Lattice
│
Private subnets (app + data tiers – same-AZ where possible)
├── Gateway Endpoints (S3, DynamoDB) – FREE
├── Interface Endpoints (ECR, Logs, Secrets, SSM…)
└── NAT Gateway only for true internet egress (one per AZ)5. Complete Production Implementation
Prerequisites
1
2
3
terraform >= 1.9
aws cli v2
jqModule Structure
1
2
3
4
5
6
modules/
└── secure-networking/
├── main.tf
├── variables.tf
├── outputs.tf
└── versions.tfmodules/secure-networking/versions.tf
1
2
3
4
5
6
7
8
9
terraform {
required_version = ">= 1.9"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 5.70"
}
}
}modules/secure-networking/variables.tf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
variable "vpc_id" {
type = string
description = "Target VPC ID"
}
variable "private_subnet_ids" {
type = list(string)
description = "Private subnet IDs for Interface Endpoints"
}
variable "private_route_table_ids" {
type = list(string)
description = "Route table IDs for Gateway Endpoints"
}
variable "region" {
type = string
default = "us-east-1"
}
variable "enable_endpoints" {
type = map(bool)
default = {
s3 = true
dynamodb = true
ecr_api = true
ecr_dkr = true
logs = true
secretsmanager = true
ssm = true
ssmmessages = true
ec2messages = true
}
}
variable "tags" {
type = map(string)
default = {}
}modules/secure-networking/main.tf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# Restrictive Security Group for Interface Endpoints
resource "aws_security_group" "vpce" {
name_prefix = "vpce-"
description = "Security group for VPC Interface Endpoints"
vpc_id = var.vpc_id
ingress {
description = "HTTPS from VPC"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = [data.aws_vpc.this.cidr_block]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = merge(var.tags, { Name = "vpce-sg" })
}
data "aws_vpc" "this" {
id = var.vpc_id
}
# ---------- Gateway Endpoints (FREE) ----------
resource "aws_vpc_endpoint" "s3" {
count = var.enable_endpoints.s3 ? 1 : 0
vpc_id = var.vpc_id
service_name = "com.amazonaws.${var.region}.s3"
vpc_endpoint_type = "Gateway"
route_table_ids = var.private_route_table_ids
tags = merge(var.tags, { Name = "s3-gateway-endpoint" })
}
resource "aws_vpc_endpoint" "dynamodb" {
count = var.enable_endpoints.dynamodb ? 1 : 0
vpc_id = var.vpc_id
service_name = "com.amazonaws.${var.region}.dynamodb"
vpc_endpoint_type = "Gateway"
route_table_ids = var.private_route_table_ids
tags = merge(var.tags, { Name = "dynamodb-gateway-endpoint" })
}
# ---------- Interface Endpoints ----------
locals {
interface_services = {
ecr_api = "ecr.api"
ecr_dkr = "ecr.dkr"
logs = "logs"
secretsmanager = "secretsmanager"
ssm = "ssm"
ssmmessages = "ssmmessages"
ec2messages = "ec2messages"
}
}
resource "aws_vpc_endpoint" "interface" {
for_each = {
for k, v in local.interface_services : k => v
if lookup(var.enable_endpoints, k, false)
}
vpc_id = var.vpc_id
service_name = "com.amazonaws.${var.region}.${each.value}"
vpc_endpoint_type = "Interface"
subnet_ids = var.private_subnet_ids
security_group_ids = [aws_security_group.vpce.id]
private_dns_enabled = true
tags = merge(var.tags, { Name = "${each.key}-interface-endpoint" })
}
# Example restrictive S3 endpoint policy
resource "aws_vpc_endpoint_policy" "s3" {
count = var.enable_endpoints.s3 ? 1 : 0
vpc_endpoint_id = aws_vpc_endpoint.s3[0].id
policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = "*"
Action = ["s3:GetObject", "s3:PutObject", "s3:ListBucket"]
Resource = [
"arn:aws:s3:::your-app-bucket",
"arn:aws:s3:::your-app-bucket/*"
]
}]
})
}modules/secure-networking/outputs.tf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
output "gateway_endpoint_ids" {
value = {
s3 = try(aws_vpc_endpoint.s3[0].id, null)
dynamodb = try(aws_vpc_endpoint.dynamodb[0].id, null)
}
}
output "interface_endpoint_ids" {
value = { for k, v in aws_vpc_endpoint.interface : k => v.id }
}
output "vpce_security_group_id" {
value = aws_security_group.vpce.id
}Root Module Usage
1
2
3
4
5
6
7
8
9
10
11
12
13
14
module "secure_networking" {
source = "./modules/secure-networking"
vpc_id = "vpc-0abc123def"
private_subnet_ids = ["subnet-aaa", "subnet-bbb", "subnet-ccc"]
private_route_table_ids = ["rtb-111", "rtb-222"]
region = "us-east-1"
tags = {
Environment = "production"
ManagedBy = "terraform"
Purpose = "cost-security-optimization"
}
}6. Security Hub + Remediation Plans Automation
1
2
3
4
aws securityhub enable-security-hub \
--enable-default-standards \
--tags Key=Purpose,Value=cost-securityPython helper to surface top Remediation Plans (
remediate.py):1
2
3
4
5
6
7
8
9
10
11
12
13
import boto3
import json
client = boto3.client('securityhub')
response = client.list_remediation_plans(MaxResults=20)
for plan in response.get('RemediationPlans', []):
print(f"Plan ID : {plan['PlanId']}")
print(f"Priority : {plan.get('Priority', 'N/A')}")
print(f"Title : {plan.get('Title')}")
print(f"Finding Count: {plan.get('FindingCount')}")
print("---")Feed the CLI / Terraform / CDK snippets from any plan directly into your pipeline.
7. Cost Optimization Hub Queries
1
2
3
4
5
# High-value recommendations
aws cost-optimization-hub list-recommendations \
--max-results 50 \
--query 'items[?estimatedMonthlySavings>10].[resourceId,actionType,estimatedMonthlySavings,currencyCode]' \
--output table8. Well-Architected Agent Onboarding (Preview)
- Console → Well-Architected → Agent
- Select accounts/regions
- Set business goals (e.g. primary = Cost Optimization, secondary = Security, constraint = multi-AZ resilience)
- Attach the customer-managed IAM role (CloudFormation template provided by the console)
- Review prioritized recommendations with ready-to-apply IaC diffs or SSM runbooks
9. Verification Commands
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Confirm Gateway Endpoints
aws ec2 describe-vpc-endpoints \
--filters "Name=vpc-id,Values=vpc-0abc123" "Name=vpc-endpoint-type,Values=Gateway"
# Confirm Interface Endpoints
aws ec2 describe-vpc-endpoints \
--filters "Name=vpc-id,Values=vpc-0abc123" "Name=vpc-endpoint-type,Values=Interface" \
--query 'VpcEndpoints[*].[ServiceName,PrivateDnsEnabled,State]'
# Monitor NAT traffic drop
aws cloudwatch get-metric-statistics \
--namespace AWS/NATGateway \
--metric-name BytesOutToDestination \
--dimensions Name=NatGatewayId,Value=nat-0xyz \
--start-time $(date -u -d '7 days ago' +%Y-%m-%dT%H:%M:%S) \
--end-time $(date -u +%Y-%m-%dT%H:%M:%S) \
--period 86400 \
--statistics Sum10. 30-Day Execution Plan
| Week | Focus | Actions |
|---|---|---|
| 1 | Visibility | Enable Cost Optimization Hub + Cost Anomaly Detection; run CUR queries for NAT & cross-AZ |
| 2 | Quick Wins | Deploy S3 + DynamoDB Gateway Endpoints; add high-volume Interface Endpoints; clean public IPs & idle load balancers |
| 3 | Architecture | Enable EKS topology-aware routing; move public content behind CloudFront |
| 4 | Governance | Review Security Hub Remediation Plans; onboard key workloads to Well-Architected Agent; add SCPs |
Closing
In 2026, the smartest AWS architects no longer treat security, networking, and cost as three separate workstreams. The same design decisions private connectivity, restrictive endpoint policies, topology-aware placement, and edge protection simultaneously shrink the attack surface and the invoice.
Start today with the free Gateway Endpoints and a public IPv4 audit. Everything else in this guide builds on that foundation.
Enjoyed reading this content? Let the author know!
Your likes, comments, shares, and saves help creators reach more builders.
Loading recommendations
Loading article