AWS VPC Flow Logs OTEL Dashboards¶
DevOps/SRE monitoring dashboards for AWS VPC Flow Logs collected via OpenTelemetry.
Overview¶
Based on the aws_vpcflow_otel package from elastic/integrations.
Dashboards¶
| Dashboard | File | Description |
|---|---|---|
| VPC Flow Logs Overview | overview.yaml |
High-level KPIs and time-series trends |
| Traffic Analysis | traffic.yaml |
Traffic distribution, source analysis, and security deep dive |
| Interface Analysis | interface.yaml |
Per-interface analysis and account metrics |
Dashboard Definitions¶
VPC Flow Logs Overview (overview.yaml)
---
dashboards:
# Dashboard 1: Overview - High-level KPIs and trends
- id: aws_vpcflow_otel-overview
name: '[AWS VPC OTEL] VPC Flow Logs Overview'
description: High-level status and trends for AWS VPC Flow Logs
filters:
- field: data_stream.dataset
equals: aws.vpcflow.otel
controls:
- type: options
label: Cloud Account ID
data_view: logs-*
field: cloud.account.id
- type: options
label: Network Interface
data_view: logs-*
field: network.interface.name
- type: options
label: Action
width: small
data_view: logs-*
field: aws.vpc.flow.action
panels:
# Navigation
- size: {w: 48, h: 2}
links:
layout: horizontal
items:
- label: Overview
dashboard: aws_vpcflow_otel-overview
- label: Traffic Analysis
dashboard: aws_vpcflow_otel-traffic
- label: Interface Analysis
dashboard: aws_vpcflow_otel-interface
# KPI Metrics (no section header - first section)
- title: Total Flow Records
hide_title: true
size: {w: 8, h: 4}
esql:
type: metric
query:
- FROM logs-*
- WHERE data_stream.dataset == "aws.vpcflow.otel"
- STATS total_flows = COUNT()
primary:
field: total_flows
label: Flow Records
format:
type: number
decimals: 0
- title: Rejection Rate
hide_title: true
size: {w: 8, h: 4}
esql:
type: metric
query:
- FROM logs-*
- WHERE data_stream.dataset == "aws.vpcflow.otel"
- EVAL is_rejected = CASE(aws.vpc.flow.action == "REJECT", 1, 0)
- STATS total_flows = COUNT(), rejected_flows = SUM(is_rejected)
- EVAL rejection_rate = CASE(total_flows > 0, rejected_flows::double / total_flows::double, 0)
primary:
field: rejection_rate
label: Rejection Rate
format:
type: percent
decimals: 1
- title: Total Bandwidth
hide_title: true
size: {w: 8, h: 4}
esql:
type: metric
query:
- FROM logs-*
- WHERE data_stream.dataset == "aws.vpcflow.otel" AND aws.vpc.flow.bytes IS NOT NULL
- STATS total_bytes = SUM(aws.vpc.flow.bytes)
primary:
field: total_bytes
label: Total Bandwidth
format:
type: bytes
- title: Active Interfaces
hide_title: true
size: {w: 8, h: 4}
esql:
type: metric
query:
- FROM logs-*
- WHERE data_stream.dataset == "aws.vpcflow.otel" AND network.interface.name IS NOT NULL
- STATS unique_interfaces = COUNT_DISTINCT(network.interface.name)
primary:
field: unique_interfaces
label: Active Interfaces
format:
type: number
decimals: 0
- title: Cloud Accounts
hide_title: true
size: {w: 8, h: 4}
esql:
type: metric
query:
- FROM logs-*
- WHERE data_stream.dataset == "aws.vpcflow.otel" AND cloud.account.id IS NOT NULL
- STATS unique_accounts = COUNT_DISTINCT(cloud.account.id)
primary:
field: unique_accounts
label: Cloud Accounts
format:
type: number
decimals: 0
- title: Unique Protocols
hide_title: true
size: {w: 8, h: 4}
esql:
type: metric
query:
- FROM logs-*
- WHERE data_stream.dataset == "aws.vpcflow.otel" AND network.protocol.name IS NOT NULL
- STATS unique_protocols = COUNT_DISTINCT(network.protocol.name)
primary:
field: unique_protocols
label: Unique Protocols
format:
type: number
decimals: 0
# Quick Insights
- markdown:
content: '### Quick Insights'
size: {w: 48, h: 3}
- title: Top 5 Interfaces by Rejected Traffic
size: {w: 24, h: 10}
esql:
type: bar
query:
- FROM logs-*
- WHERE data_stream.dataset == "aws.vpcflow.otel" AND aws.vpc.flow.action == "REJECT" AND network.interface.name IS NOT NULL
- STATS rejected_count = COUNT() BY network.interface.name
- SORT rejected_count DESC
- LIMIT 5
dimension:
field: network.interface.name
label: Interface
metrics:
- field: rejected_count
label: Rejected Flows
format:
type: number
decimals: 0
legend:
visible: hide
color:
palette: negative
appearance:
y_left_axis:
title: Rejected Flows
- title: Top 5 Rejected Destination Ports
size: {w: 24, h: 10}
esql:
type: bar
query:
- FROM logs-*
- WHERE data_stream.dataset == "aws.vpcflow.otel" AND aws.vpc.flow.action == "REJECT" AND destination.port IS NOT NULL
- STATS rejected_count = COUNT() BY destination.port
- SORT rejected_count DESC
- LIMIT 5
dimension:
field: destination.port
label: Dest Port
metrics:
- field: rejected_count
label: Rejected Flows
format:
type: number
decimals: 0
legend:
visible: hide
color:
palette: negative
appearance:
y_left_axis:
title: Rejected Flows
# Time-Series Trends
- markdown:
content: '### Time-Series Trends'
size: {w: 48, h: 3}
- title: Traffic Volume Over Time
size: {w: 48, h: 12}
esql:
type: area
query:
- FROM logs-*
- WHERE data_stream.dataset == "aws.vpcflow.otel" AND aws.vpc.flow.action IS NOT NULL
- STATS flow_count = COUNT() BY aws.vpc.flow.action, time_bucket = BUCKET(@timestamp, 50, ?_tstart, ?_tend)
- SORT time_bucket ASC
mode: stacked
dimension:
field: time_bucket
label: Time
data_type: date
metrics:
- field: flow_count
label: Flow Count
format:
type: number
decimals: 0
breakdown:
field: aws.vpc.flow.action
color:
palette: eui_amsterdam_color_blind
assignments:
- value: REJECT
color: '#BD271E'
- value: ACCEPT
color: '#00BF6F'
appearance:
x_axis:
title: '@timestamp'
y_left_axis:
title: Flow Count
- title: Bandwidth Usage Over Time
size: {w: 48, h: 12}
esql:
type: line
query:
- FROM logs-*
- WHERE data_stream.dataset == "aws.vpcflow.otel" AND aws.vpc.flow.bytes IS NOT NULL
- STATS total_bytes = SUM(aws.vpc.flow.bytes) BY time_bucket = BUCKET(@timestamp, 50, ?_tstart, ?_tend)
- SORT time_bucket ASC
dimension:
field: time_bucket
label: Time
data_type: date
metrics:
- field: total_bytes
label: Bytes
format:
type: bytes
appearance:
x_axis:
title: '@timestamp'
y_left_axis:
title: Bandwidth
Traffic Analysis (traffic.yaml)
---
dashboards:
# Dashboard 2: Traffic Analysis - Distribution, sources, and security
- id: aws_vpcflow_otel-traffic
name: '[AWS VPC OTEL] Traffic Analysis'
description: Detailed traffic distribution, source analysis, and security deep dive
filters:
- field: data_stream.dataset
equals: aws.vpcflow.otel
controls:
# Primary filters
- type: options
label: Cloud Account ID
data_view: logs-*
field: cloud.account.id
- type: options
label: Network Interface
data_view: logs-*
field: network.interface.name
- type: options
label: Action
width: small
data_view: logs-*
field: aws.vpc.flow.action
# Traffic filters
- type: options
label: Protocol
width: small
data_view: logs-*
field: network.protocol.name
- type: options
label: Destination Port
width: small
data_view: logs-*
field: destination.port
- type: options
label: Source Port
width: small
data_view: logs-*
field: source.port
panels:
# Navigation
- size: {w: 48, h: 2}
links:
layout: horizontal
items:
- label: Overview
dashboard: aws_vpcflow_otel-overview
- label: Traffic Analysis
dashboard: aws_vpcflow_otel-traffic
- label: Interface Analysis
dashboard: aws_vpcflow_otel-interface
# Traffic KPIs (no section header - first section)
- title: Total Flow Records
hide_title: true
size: {w: 10, h: 4}
esql:
type: metric
query:
- FROM logs-*
- WHERE data_stream.dataset == "aws.vpcflow.otel"
- STATS total_flows = COUNT()
primary:
field: total_flows
label: Flow Records
format:
type: number
decimals: 0
- title: Unique Source IPs
hide_title: true
size: {w: 10, h: 4}
esql:
type: metric
query:
- FROM logs-*
- WHERE data_stream.dataset == "aws.vpcflow.otel" AND source.address IS NOT NULL
- STATS unique_sources = COUNT_DISTINCT(source.address)
primary:
field: unique_sources
label: Unique Sources
format:
type: number
decimals: 0
- title: Unique Destination IPs
hide_title: true
size: {w: 9, h: 4}
esql:
type: metric
query:
- FROM logs-*
- WHERE data_stream.dataset == "aws.vpcflow.otel" AND destination.address IS NOT NULL
- STATS unique_destinations = COUNT_DISTINCT(destination.address)
primary:
field: unique_destinations
label: Unique Destinations
format:
type: number
decimals: 0
- title: Unique Protocols
hide_title: true
size: {w: 10, h: 4}
esql:
type: metric
query:
- FROM logs-*
- WHERE data_stream.dataset == "aws.vpcflow.otel" AND network.protocol.name IS NOT NULL
- STATS unique_protocols = COUNT_DISTINCT(network.protocol.name)
primary:
field: unique_protocols
label: Protocols
format:
type: number
decimals: 0
- title: Unique Destination Ports
hide_title: true
size: {w: 9, h: 4}
esql:
type: metric
query:
- FROM logs-*
- WHERE data_stream.dataset == "aws.vpcflow.otel" AND destination.port IS NOT NULL
- STATS unique_ports = COUNT_DISTINCT(destination.port)
primary:
field: unique_ports
label: Dest Ports
format:
type: number
decimals: 0
# Traffic Trend
- title: Traffic by Action Over Time
size: {w: 48, h: 10}
esql:
type: area
query:
- FROM logs-*
- WHERE data_stream.dataset == "aws.vpcflow.otel" AND aws.vpc.flow.action IS NOT NULL
- STATS flow_count = COUNT() BY aws.vpc.flow.action, time_bucket = BUCKET(@timestamp, 50, ?_tstart, ?_tend)
- SORT time_bucket ASC
mode: stacked
dimension:
field: time_bucket
label: Time
data_type: date
metrics:
- field: flow_count
label: Flow Count
format:
type: number
decimals: 0
breakdown:
field: aws.vpc.flow.action
color:
palette: eui_amsterdam_color_blind
assignments:
- value: REJECT
color: '#BD271E'
- value: ACCEPT
color: '#00BF6F'
appearance:
x_axis:
title: '@timestamp'
y_left_axis:
title: Flow Count
# Traffic Distribution
- title: Top Protocols
size: {w: 24, h: 12}
esql:
type: pie
query:
- FROM logs-*
- WHERE data_stream.dataset == "aws.vpcflow.otel" AND network.protocol.name IS NOT NULL
- STATS flow_count = COUNT() BY network.protocol.name
- SORT flow_count DESC
- LIMIT 10
metrics:
- field: flow_count
label: Flow Count
format:
type: number
decimals: 0
breakdowns:
- field: network.protocol.name
label: Protocol
- title: Top Destination Ports
size: {w: 24, h: 12}
esql:
type: bar
query:
- FROM logs-*
- WHERE data_stream.dataset == "aws.vpcflow.otel" AND destination.port IS NOT NULL
- STATS flow_count = COUNT() BY destination.port
- SORT flow_count DESC
- LIMIT 10
dimension:
field: destination.port
label: Dest Port
metrics:
- field: flow_count
label: Flow Count
format:
type: number
decimals: 0
legend:
visible: hide
appearance:
y_left_axis:
title: Flow Count
# Source Analysis
- markdown:
content: '### Source Analysis'
size: {w: 48, h: 3}
- title: Top Source IPs - Detailed
size: {w: 48, h: 13}
esql:
type: datatable
query:
- FROM logs-*
- WHERE data_stream.dataset == "aws.vpcflow.otel" AND source.address IS NOT NULL
- EVAL is_accepted = CASE(aws.vpc.flow.action == "ACCEPT", 1, 0)
- EVAL is_rejected = CASE(aws.vpc.flow.action == "REJECT", 1, 0)
- STATS total_flows = COUNT(), accepted_flows = SUM(is_accepted), rejected_flows = SUM(is_rejected), total_bytes = SUM(aws.vpc.flow.bytes),
total_packets = SUM(aws.vpc.flow.packets) BY source.address
- EVAL rejection_rate = CASE(total_flows > 0, rejected_flows::double / total_flows::double, 0)
- SORT total_flows DESC
- LIMIT 20
breakdowns:
- field: source.address
label: Source IP
metrics:
- field: total_flows
label: Total Flows
format:
type: number
decimals: 0
- field: accepted_flows
label: Accepted
format:
type: number
decimals: 0
- field: rejected_flows
label: Rejected
format:
type: number
decimals: 0
- field: rejection_rate
label: Rejection %
format:
type: percent
decimals: 1
- field: total_bytes
label: Bytes
format:
type: bytes
- field: total_packets
label: Packets
format:
type: number
decimals: 0
paging:
enabled: true
page_size: 10
# Security Deep Dive
- markdown:
content: '### Security Deep Dive'
size: {w: 48, h: 3}
- title: Rejected Traffic by Protocol
size: {w: 24, h: 12}
esql:
type: bar
query:
- FROM logs-*
- WHERE data_stream.dataset == "aws.vpcflow.otel" AND aws.vpc.flow.action == "REJECT" AND network.protocol.name IS NOT NULL
- STATS rejected_count = COUNT() BY network.protocol.name
- SORT rejected_count DESC
- LIMIT 10
dimension:
field: network.protocol.name
label: Protocol
metrics:
- field: rejected_count
label: Rejected Count
format:
type: number
decimals: 0
legend:
visible: hide
color:
palette: negative
appearance:
y_left_axis:
title: Rejected Count
- title: Top Rejected Ports
size: {w: 24, h: 12}
esql:
type: bar
query:
- FROM logs-*
- WHERE data_stream.dataset == "aws.vpcflow.otel" AND aws.vpc.flow.action == "REJECT" AND destination.port IS NOT NULL
- STATS rejected_count = COUNT() BY destination.port
- SORT rejected_count DESC
- LIMIT 10
dimension:
field: destination.port
label: Dest Port
metrics:
- field: rejected_count
label: Rejected Count
format:
type: number
decimals: 0
legend:
visible: hide
color:
palette: negative
appearance:
y_left_axis:
title: Rejected Count
- title: Detailed Rejection Logs
size: {w: 48, h: 13}
esql:
type: datatable
query:
- FROM logs-*
- WHERE data_stream.dataset == "aws.vpcflow.otel" AND aws.vpc.flow.action == "REJECT"
- KEEP @timestamp, source.address, destination.address, destination.port, network.protocol.name, network.interface.name, aws.vpc.flow.bytes,
aws.vpc.flow.packets
- SORT @timestamp DESC
- LIMIT 1000
breakdowns:
- field: '@timestamp'
label: Timestamp
- field: source.address
label: Source IP
- field: destination.address
label: Destination IP
- field: destination.port
label: Dest Port
- field: network.protocol.name
label: Protocol
- field: network.interface.name
label: Interface
metrics:
- field: aws.vpc.flow.bytes
label: Bytes
format:
type: bytes
- field: aws.vpc.flow.packets
label: Packets
format:
type: number
decimals: 0
paging:
enabled: true
page_size: 15
# Top Sources and Destinations
- markdown:
content: '### Top Sources and Destinations'
size: {w: 48, h: 3}
- title: Top Destination Ports
size: {w: 16, h: 12}
esql:
type: bar
query:
- FROM logs-*
- WHERE data_stream.dataset == "aws.vpcflow.otel" AND destination.port IS NOT NULL
- STATS flow_count = COUNT() BY destination.port
- SORT flow_count DESC
- LIMIT 10
dimension:
field: destination.port
label: Dest Port
metrics:
- field: flow_count
label: Flow Count
format:
type: number
decimals: 0
legend:
visible: hide
appearance:
y_left_axis:
title: Flow Count
- title: Top Destination IPs
size: {w: 16, h: 12}
esql:
type: bar
query:
- FROM logs-*
- WHERE data_stream.dataset == "aws.vpcflow.otel" AND destination.address IS NOT NULL
- STATS flow_count = COUNT() BY destination.address
- SORT flow_count DESC
- LIMIT 10
dimension:
field: destination.address
label: Destination IP
metrics:
- field: flow_count
label: Flow Count
format:
type: number
decimals: 0
legend:
visible: hide
appearance:
y_left_axis:
title: Flow Count
- title: Top Source IPs
size: {w: 16, h: 12}
esql:
type: bar
query:
- FROM logs-*
- WHERE data_stream.dataset == "aws.vpcflow.otel" AND source.address IS NOT NULL
- STATS flow_count = COUNT() BY source.address
- SORT flow_count DESC
- LIMIT 10
dimension:
field: source.address
label: Source IP
metrics:
- field: flow_count
label: Flow Count
format:
type: number
decimals: 0
legend:
visible: hide
appearance:
y_left_axis:
title: Flow Count
Interface Analysis (interface.yaml)
---
dashboards:
# Dashboard 3: Interface Analysis - Per-interface deep dive
- id: aws_vpcflow_otel-interface
name: '[AWS VPC OTEL] Interface Analysis'
description: Per-interface analysis for investigating specific network interfaces
filters:
- field: data_stream.dataset
equals: aws.vpcflow.otel
controls:
# Primary filters
- type: options
label: Cloud Account ID
data_view: logs-*
field: cloud.account.id
- type: options
label: Network Interface
data_view: logs-*
field: network.interface.name
- type: options
label: Action
width: small
data_view: logs-*
field: aws.vpc.flow.action
# Traffic filters for drill-down
- type: options
label: Destination Port
width: small
data_view: logs-*
field: destination.port
- type: options
label: Protocol
width: small
data_view: logs-*
field: network.protocol.name
panels:
# Navigation
- size: {w: 48, h: 2}
links:
layout: horizontal
items:
- label: Overview
dashboard: aws_vpcflow_otel-overview
- label: Traffic Analysis
dashboard: aws_vpcflow_otel-traffic
- label: Interface Analysis
dashboard: aws_vpcflow_otel-interface
# Interface KPIs (no section header - first section)
- title: Total Interfaces
hide_title: true
size: {w: 10, h: 4}
esql:
type: metric
query:
- FROM logs-*
- WHERE data_stream.dataset == "aws.vpcflow.otel" AND network.interface.name IS NOT NULL
- STATS total_interfaces = COUNT_DISTINCT(network.interface.name)
primary:
field: total_interfaces
label: Total Interfaces
format:
type: number
decimals: 0
- title: Total Flow Records
hide_title: true
size: {w: 9, h: 4}
esql:
type: metric
query:
- FROM logs-*
- WHERE data_stream.dataset == "aws.vpcflow.otel"
- STATS total_flows = COUNT()
primary:
field: total_flows
label: Flow Records
format:
type: number
decimals: 0
- title: Unique Source IPs
hide_title: true
size: {w: 10, h: 4}
esql:
type: metric
query:
- FROM logs-*
- WHERE data_stream.dataset == "aws.vpcflow.otel" AND source.address IS NOT NULL
- STATS unique_sources = COUNT_DISTINCT(source.address)
primary:
field: unique_sources
label: Unique Sources
format:
type: number
decimals: 0
- title: Unique Destination IPs
hide_title: true
size: {w: 9, h: 4}
esql:
type: metric
query:
- FROM logs-*
- WHERE data_stream.dataset == "aws.vpcflow.otel" AND destination.address IS NOT NULL
- STATS unique_destinations = COUNT_DISTINCT(destination.address)
primary:
field: unique_destinations
label: Unique Destinations
format:
type: number
decimals: 0
- title: Unique Protocols
hide_title: true
size: {w: 10, h: 4}
esql:
type: metric
query:
- FROM logs-*
- WHERE data_stream.dataset == "aws.vpcflow.otel" AND network.protocol.name IS NOT NULL
- STATS unique_protocols = COUNT_DISTINCT(network.protocol.name)
primary:
field: unique_protocols
label: Protocols
format:
type: number
decimals: 0
# Interface Traffic Trend
- title: Interface Traffic Over Time
size: {w: 48, h: 10}
esql:
type: area
query:
- FROM logs-*
- WHERE data_stream.dataset == "aws.vpcflow.otel" AND network.interface.name IS NOT NULL
- STATS flow_count = COUNT() BY time_bucket = BUCKET(@timestamp, 50, ?_tstart, ?_tend)
- SORT time_bucket ASC
dimension:
field: time_bucket
label: Time
data_type: date
metrics:
- field: flow_count
label: Flow Count
format:
type: number
decimals: 0
appearance:
x_axis:
title: '@timestamp'
y_left_axis:
title: Flow Count
# Interface Traffic Analysis
- title: Interface Traffic Analysis
size: {w: 48, h: 13}
esql:
type: bar
query:
- FROM logs-*
- WHERE data_stream.dataset == "aws.vpcflow.otel" AND network.interface.name IS NOT NULL AND aws.vpc.flow.action IS NOT NULL
- STATS flow_count = COUNT() BY network.interface.name, aws.vpc.flow.action
- SORT flow_count DESC
mode: stacked
dimension:
field: network.interface.name
label: Interface
metrics:
- field: flow_count
label: Flow Count
format:
type: number
decimals: 0
breakdown:
field: aws.vpc.flow.action
color:
palette: eui_amsterdam_color_blind
assignments:
- value: REJECT
color: '#BD271E'
- value: ACCEPT
color: '#00BF6F'
appearance:
y_left_axis:
title: Flow Count
# Top Interfaces by Bandwidth
- title: Top Interfaces by Bandwidth
size: {w: 48, h: 13}
esql:
type: bar
query:
- FROM logs-*
- WHERE data_stream.dataset == "aws.vpcflow.otel" AND network.interface.name IS NOT NULL AND aws.vpc.flow.bytes IS NOT NULL
- STATS total_bytes = SUM(aws.vpc.flow.bytes) BY network.interface.name
- SORT total_bytes DESC
- LIMIT 10
dimension:
field: network.interface.name
label: Interface
metrics:
- field: total_bytes
label: Bytes
format:
type: bytes
legend:
visible: hide
appearance:
y_left_axis:
title: Bytes
# Traffic Details
- markdown:
content: '### Traffic Details'
size: {w: 48, h: 3}
- title: Accepted vs Rejected by Protocol
size: {w: 24, h: 12}
esql:
type: bar
query:
- FROM logs-*
- WHERE data_stream.dataset == "aws.vpcflow.otel" AND network.protocol.name IS NOT NULL AND aws.vpc.flow.action IS NOT NULL
- STATS flow_count = COUNT() BY network.protocol.name, aws.vpc.flow.action
- SORT flow_count DESC
mode: stacked
dimension:
field: network.protocol.name
label: Protocol
metrics:
- field: flow_count
label: Flow Count
format:
type: number
decimals: 0
breakdown:
field: aws.vpc.flow.action
color:
palette: eui_amsterdam_color_blind
assignments:
- value: REJECT
color: '#BD271E'
- value: ACCEPT
color: '#00BF6F'
appearance:
y_left_axis:
title: Flow Count
- title: Bandwidth by Protocol
size: {w: 24, h: 12}
esql:
type: bar
query:
- FROM logs-*
- WHERE data_stream.dataset == "aws.vpcflow.otel" AND network.protocol.name IS NOT NULL AND aws.vpc.flow.bytes IS NOT NULL
- STATS total_bytes = SUM(aws.vpc.flow.bytes) BY network.protocol.name
- SORT total_bytes DESC
- LIMIT 10
dimension:
field: network.protocol.name
label: Protocol
metrics:
- field: total_bytes
label: Bytes
format:
type: bytes
legend:
visible: hide
appearance:
y_left_axis:
title: Bytes
# Account Analysis
- markdown:
content: '### Account Analysis'
size: {w: 48, h: 3}
- title: Traffic by Cloud Account
size: {w: 48, h: 13}
esql:
type: datatable
query:
- FROM logs-*
- WHERE data_stream.dataset == "aws.vpcflow.otel" AND cloud.account.id IS NOT NULL
- EVAL is_accepted = CASE(aws.vpc.flow.action == "ACCEPT", 1, 0)
- EVAL is_rejected = CASE(aws.vpc.flow.action == "REJECT", 1, 0)
- STATS total_flows = COUNT(), accepted_flows = SUM(is_accepted), rejected_flows = SUM(is_rejected), total_bytes = SUM(aws.vpc.flow.bytes),
unique_interfaces = COUNT_DISTINCT(network.interface.name), unique_sources = COUNT_DISTINCT(source.address) BY cloud.account.id
- EVAL rejection_rate = CASE(total_flows > 0, rejected_flows::double / total_flows::double, 0)
- SORT total_flows DESC
- LIMIT 100
breakdowns:
- field: cloud.account.id
label: Cloud Account ID
metrics:
- field: total_flows
label: Total Flows
format:
type: number
decimals: 0
- field: accepted_flows
label: Accepted
format:
type: number
decimals: 0
- field: rejected_flows
label: Rejected
format:
type: number
decimals: 0
- field: rejection_rate
label: Rejection %
format:
type: percent
decimals: 1
- field: total_bytes
label: Bytes
format:
type: bytes
- field: unique_interfaces
label: Interfaces
format:
type: number
decimals: 0
- field: unique_sources
label: Unique Sources
format:
type: number
decimals: 0
paging:
enabled: true
page_size: 10
Prerequisites¶
- AWS VPC Flow Logs: Configured for OpenTelemetry collection
- OpenTelemetry Collector: Configured for VPC Flow Logs
- Kibana: Version 8.x or later
Data Requirements¶
- Data stream dataset:
aws.vpcflow.otel - Data view:
logs-*
Field Reference¶
| Field | Description |
|---|---|
@timestamp |
Event timestamp |
aws.vpc.flow.action |
Allow/Deny action |
aws.vpc.flow.bytes |
Bytes transferred |
aws.vpc.flow.packets |
Packets transferred |
source.address |
Source IP address |
source.port |
Source port |
destination.address |
Destination IP address |
destination.port |
Destination port |
network.protocol.name |
Protocol name |
network.interface.name |
Network interface name |
cloud.account.id |
AWS account ID |