Skip to main content
INS // Insights

VPC Flow Logs SOC 2 Evidence Architecture

Updated September 2026 · 5 min read

VPC Flow Logs SOC 2 evidence architecture is the design we ship when an examiner wants proof that network traffic is recorded and reviewable, and finance refuses to ingest every ENI into CloudWatch Logs at $0.50/GB for the first 10 TB (see CloudWatch Logs vended log pricing). Flow logs are not free. Destination choice is the difference between a control you can keep for a Type II window and a line item you turn off after the first bill.

We do not claim this is a SOC 2 certification. We claim it is a queryable, retainable, scoped network record that backs CC6 / CC7 monitoring narratives — and that it does not require Logs Insights on every packet.

VPC Flow Logs SOC 2 Evidence Architecture Destination

VPC Flow Logs publish to CloudWatch Logs, S3, or Firehose. All three incur vended-log delivery charges; CloudWatch Logs also incurs ingest-tier pricing that starts at $0.50/GB in the large US Regions. S3 delivery is a different (lower) vended rate plus S3 storage and optional Parquet conversion. Firehose adds its own ingest.

The architecture we deliver for mid-market SaaS:

  1. S3 destination, Hive-style prefixes, Parquet.
  2. Athena with partition projection — no daily MSCK REPAIR.
  3. REJECT (and selected ACCEPT) on the VPCs in scope, not “all ENIs, all traffic, all accounts” on day one.
  4. Central log archive account. Member accounts cannot delete the evidence prefix.
  5. CloudWatch Logs only for a short-lived troubleshooting ENI or a NAT that needs a metric filter. Not the estate.

That last point is how teams accidentally recreate the $0.50 path: someone enables flow logs on the VPC in the console and clicks CloudWatch because it is the default they remember.

{
  "DeliverLogsPermissionArn": "arn:aws:iam::123456789012:role/flowlogs-to-s3",
  "LogDestinationType": "s3",
  "LogDestination": "arn:aws:s3:::org-flow-logs-archive/AWSLogs/",
  "ResourceType": "VPC",
  "ResourceId": "vpc-0abc",
  "TrafficType": "REJECT",
  "LogFormat": "${srcaddr} ${dstaddr} ${srcport} ${dstport} ${protocol} ${action} ${bytes} ${start} ${end} ${vpc-id} ${subnet-id} ${instance-id} ${tcp-flags} ${flow-direction} ${traffic-path}",
  "DestinationOptions": {
    "FileFormat": "parquet",
    "HiveCompatiblePartitions": true,
    "PerHourPartition": true
  }
}

Custom format is not decoration. Default format is missing flow-direction and traffic-path. Examiners asking “did this go to the internet?” need those fields. We version the format string in Terraform so a console edit cannot silently drop columns mid-window.

Athena That an Examiner Can Watch You Run

Evidence that “we have a bucket” is the vault problem again. Evidence is a saved query plus a result set from the observation window.

-- Rejected inbound to data-tier subnets — rolling 7-day window
SELECT
  date, srcaddr, dstaddr, dstport, protocol, sum(bytes) AS bytes
FROM vpc_flow_logs
WHERE action = 'REJECT'
  AND date >= CAST(current_date - interval '7' day AS varchar)
  AND subnet_id IN ('subnet-data-a', 'subnet-data-b')
GROUP BY 1, 2, 3, 4, 5
ORDER BY bytes DESC
LIMIT 100;

Partition projection:

CREATE EXTERNAL TABLE vpc_flow_logs (
  srcaddr string,
  dstaddr string,
  srcport int,
  dstport int,
  protocol int,
  action string,
  bytes bigint,
  start bigint,
  "end" bigint,
  vpc_id string,
  subnet_id string,
  instance_id string,
  tcp_flags int,
  flow_direction string,
  traffic_path int
)
PARTITIONED BY (
  `aws-account-id` string,
  region string,
  date string
)
ROW FORMAT SERDE 'org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe'
LOCATION 's3://org-flow-logs-archive/AWSLogs/'
TBLPROPERTIES (
  'projection.enabled' = 'true',
  'projection.date.type' = 'date',
  'projection.date.range' = 'NOW-400DAYS,NOW',
  'projection.date.format' = 'yyyy/MM/dd',
  'storage.location.template' = 's3://org-flow-logs-archive/AWSLogs/${aws-account-id}/vpcflowlogs/${region}/${date}'
);

Athena is $5/TB scanned. Parquet + partition projection is why this stays cheaper than CloudWatch ingest. A text dump of ALL traffic with no partitions is how people conclude “Athena is expensive” and go back to $0.50/GB.

We store the query in the evidence repo and re-run it in the walkthrough. Result CSVs land in a prefix the GRC tool can attach. Same packet style as GuardDuty / Security Hub aggregation.

Scope Beats “Log Everything”

ALL traffic on every ENI is how flow logs become the largest observability line. The control objective is unauthorized or unexpected network paths, not a full packet capture.

What we enable first:

  • REJECT on production VPCs (failed SG / NACL attempts).
  • ACCEPT on the NAT and egress-only paths if the narrative is “we monitor outbound.”
  • Skip DHCP/DNS chatter-heavy subnets until someone has a question that needs them.

Aggregation interval: 1 minute vs 10 minutes is a volume lever on non-Nitro; Nitro often emits at a finer interval regardless. We still set 10 minutes on S3 destinations unless IR needs 1-minute bins.

Tag the destination bucket with cost-allocation tags (control=vpc-flow-logs, framework=soc2-evidence) so Organizations tag policies keep this spend visible. Pair with SOC 2-ready AWS architecture — account vending should create the flow-log subscription, not leave it as a ticket after the first examination.

S3 bucket policy: TLS only, deny delete from member roles, lifecycle to IA after 90 days, expire after the retention in the policy (often 365 days). Object Lock if the examiner has been burned by “someone cleaned the bucket.”

This is security automation plus AWS cloud infrastructure: the log path, the query, the retention, the bill.

Need flow-log evidence that does not ingest at CloudWatch rates? 907-841-8407 or contact@rutagon.com.

Talk to us about VPC Flow Logs SOC 2 evidence →

Frequently Asked Questions

Is S3 delivery free compared to CloudWatch Logs?

No. Vended-log delivery still applies, plus S3 storage and optional Parquet conversion. It is still the architecture we use because it avoids CloudWatch Logs ingest at $0.50/GB for the first tier and because Athena + Parquet is how you keep a year of evidence without a Logs Insights bill. Read the current destination rates on the CloudWatch pricing page before you model.

Do we have to log ACCEPT and REJECT on every ENI?

No. Start with REJECT on in-scope VPCs and ACCEPT on egress points that matter to the narrative. ALL traffic on every ENI is how the control gets turned off after one invoice. Scope is a control decision, not a missing checkbox.

Can we use CloudWatch Logs Insights instead of Athena?

Yes, for a short window on a single ENI. Estate-wide CloudWatch ingest is the $0.50 path this architecture exists to avoid. If someone needs Insights, enable a temporary CloudWatch subscription on that ENI and delete it when the incident closes.

What does an examiner actually sample?

A flow-log configuration (VPC ID, destination ARN, traffic type, format), retention on the bucket, and a query result from the observation window — for example rejected inbound to a data subnet. A console screenshot of “Flow logs: enabled” without a query is the weak artifact.

Does this architecture certify us for SOC 2?

No. It produces network-monitoring evidence that supports CC6/CC7 narratives. A licensed firm examines the full control set. We build the log path and the query packet; we do not issue a report.