EventBridge is quietly one of the most powerful tools in AWS for building reactive systems, and its filtering capabilities are where the magic truly happens. The surprising truth is that EventBridge filters aren’t just about matching simple key-value pairs; they allow for sophisticated pattern matching that can dramatically simplify your application logic.
Let’s see this in action with a common scenario: filtering events from AWS services that have a specific prefix in their source. Imagine you’re receiving events from various AWS services, and you only want to react to those originating from services within your us-east-1 region, like aws.ec2 or aws.s3.
Here’s an example of an event that might come through:
{
"version": "0",
"id": "123e4567-e89b-12d3-a456-426614174000",
"detail-type": "AWS API Call via CloudTrail",
"source": "aws.ec2",
"account": "111122223333",
"time": "2023-10-27T10:00:00Z",
"region": "us-east-1",
"resources": [],
"detail": {
"eventVersion": "1.08",
"eventTime": "2023-10-27T10:00:00Z",
"eventSource": "ec2.amazonaws.com",
"awsRegion": "us-east-1",
"eventName": "RunInstances",
"sourceIPAddress": "203.0.113.45",
"userAgent": "console.aws.amazon.com",
"requestParameters": {
"instancesSet": {
"items": [
{
"instanceType": "t2.micro"
}
]
}
},
"responseElements": {
"instancesSet": {
"items": [
{
"instanceId": "i-0abcdef1234567890"
}
]
}
},
"requestID": "a1b2c3d4-e5f6-7890-1234-567890abcdef",
"entityId": "09876543-21fe-dcba-0987-654321fedcba",
"eventID": "f0e1d2c3-b4a5-6789-0123-456789abcdef",
"readOnly": false,
"apiVersion": "2016-11-15",
"recipientAccountId": "111122223333"
}
}
We want to create an EventBridge rule that captures events where the source field starts with aws. and also ensures that a specific field, say detail.eventName, actually exists in the event. This is crucial because not all events from a given source will have the same structure.
To achieve this, we define an Event Pattern in our rule. The pattern is a JSON object that describes the structure and values of the events we want to match.
For filtering events where the source field starts with aws., we use the prefix operator. For ensuring a field exists, we use the exists operator. Combining these, our pattern would look like this:
{
"source": [{ "prefix": "aws." }],
"detail": {
"eventName": [{ "exists": true }]
}
}
Let’s break down what’s happening here:
"source": [{ "prefix": "aws." }]: This part tells EventBridge to only consider events where thesourcefield begins with the stringaws.. This is incredibly useful for grouping events from AWS services, as most AWS service events follow this naming convention. You could also usesuffixoranything-butfor more complex string matching."detail": { "eventName": [{ "exists": true }] }: This nested part targets a field deep within the event’sdetailobject. Theexists: trueoperator ensures that theeventNamefield must be present in thedetailobject for the event to match. This is a robust way to avoid errors if your target (like a Lambda function) expects a specific field that might not always be populated.
When you create an EventBridge rule, you specify this pattern. Any event that conforms to this JSON structure will be sent to the targets configured for that rule. This means you can have a single rule that intelligently routes events from a broad category of AWS services, but only if they contain the specific data you need to process.
You can also combine the prefix and exists operators with other matching types. For instance, if you wanted to further narrow down the aws.ec2 events to only RunInstances calls, you could add:
{
"source": [{ "prefix": "aws.ec2" }],
"detail-type": ["AWS API Call via CloudTrail"],
"detail": {
"eventName": ["RunInstances"]
}
}
Here, we’re using a direct string match for eventName because we know we’re already in the aws.ec2 source and AWS API Call via CloudTrail detail type. The power lies in how these operators compose.
The prefix and exists operators are fundamental for building resilient and flexible event-driven architectures. They allow you to filter events not just by exact values, but by structural presence and string patterns, significantly reducing the need for complex pre-processing in your application code. This means your Lambda functions or other targets can focus on the business logic rather than parsing and validating incoming event data.
If you were to use a pattern that didn’t include "detail": { "eventName": [{ "exists": true }] } but your target function expected detail.eventName and a particular event from aws.ec2 happened to not have it, your target would likely throw a KeyError or AttributeError when trying to access it, causing your integration to fail. The exists check prevents this.
The next logical step after mastering source prefix and field existence filtering is to explore the anything-but operator for more nuanced exclusion of events.