Custom AWS EventBridge Lambda automation is how we ship the glue that GRC tools and SaaS IGA products do not: a termination event becomes a fan-out of revokes; a Config finding becomes a ticket with context; a cost anomaly becomes a scoped notification, not a 40-page CSV.
This is proof-of-capability content — a custom AWS build, not a generic "AI automation for any vertical" article. The pattern below is what we actually put in Terraform for mid-market production accounts.
Why EventBridge Instead of a Cron and a Wiki
Cron jobs that "diff HRIS every night" leave a 24-hour hole and hide failures in CloudWatch. EventBridge puts the contract in the event schema: source, detail-type, retry, and a dead-letter queue. Lambda stays small. The bus is the architecture.
We used the same shape on a production SaaS platform (dozens of Lambdas, no long-lived credentials, OIDC CI). The lesson that transferred: idempotency keys in the event, not "Lambda will probably only run once."
Architecture We Repeat
- Custom event bus (not only the default bus) so app events do not mix with AWS service events you are not ready to route.
- Rules with event patterns tight enough that a mis-tagged EC2 instance does not invoke a revoke Lambda.
- Lambda with a reserved concurrency cap. Fan-out that stampses IAM APIs will rate-limit you in the incident.
- SQS DLQ with an alarm. A rule without a DLQ is a silent drop.
- IAM on the Lambda: resource-scoped, no
*:*."just for the prototype."
# handler.py — idempotent revoke fan-out
import json
import os
import boto3
ddb = boto3.client("dynamodb")
TABLE = os.environ["IDEMPOTENCY_TABLE"]
def handler(event, _ctx):
detail = event["detail"]
eid = detail["event_id"]
try:
ddb.put_item(
TableName=TABLE,
Item={"event_id": {"S": eid}, "ttl": {"N": str(detail["ttl"])}},
ConditionExpression="attribute_not_exists(event_id)",
)
except ddb.exceptions.ConditionalCheckFailedException:
return {"status": "duplicate"}
# ... invoke per-system revokers with the same event_id
return {"status": "ok"}
DynamoDB TTL is a ceiling, not a compliance archive. Evidence goes to S3. This table only exists to make retries safe.
Trade-offs We Accept
- Lambda timeouts vs Step Functions. If the fan-out is more than a handful of systems with human approval, we graduate to Step Functions (we have shipped that pattern too). EventBridge + Lambda is the default for mechanical revokes.
- Payload size. EventBridge has a size limit. HR snapshots do not belong in
detail; store an object key and fetch. - PII on the bus. Employee IDs, not SSNs. Encrypt the archive bucket.
For cost, EventBridge is rarely the line item; Lambda duration and log retention are. Pair with CloudWatch log retention so the glue layer does not become a surprise.
This sits behind JML deprovisioning as the delivery mechanism — not a competing product.
Discuss a custom AWS build → rutagon.com/contact · 907-841-8407 · contact@rutagon.com.
Schema and Compatibility
We version detail-type (jml.terminated.v1). Consumers pin the version. A v2 that adds a field does not break v1 rules. The worst production outage in this pattern is a rule that matched too broadly after someone reused source: custom.hr for a test event in prod. Separate buses for prod and staging. Replay uses Archive on the non-prod bus.
Reserved concurrency on revoke Lambdas is mandatory. A 2,000-person layoff event (or a bad HRIS webhook replay) will otherwise stampede Okta and AWS APIs and then look like an outage instead of a control.
Observability Without a SIEM Claim
We emit metrics: events_received, duplicates, revokes_ok, revokes_fail, dlq_visible. Alarms on fail and DLQ. Dashboards are CloudWatch. That is production hygiene, not a SOC we operate. Traces go to X-Ray only if the account already has it; we do not sell observability platforms.
IAM for the bus to invoke Lambda uses a source-ARN condition. A Lambda resource policy that allows events.amazonaws.com from any bus in the org is too wide. Tighten to this bus ARN.
Custom AWS EventBridge Lambda automation we will not skip in production
Custom AWS EventBridge Lambda automation is only cheap when events are typed. We version detail-type (jml.terminated.v1). Consumers pin versions. A test event on the prod bus with source: custom.hr is how a rule fans out 2,000 revokes. Separate buses. Archive+replay stays on non-prod until you have practiced it.
AWS documents EventBridge buses and rules in the EventBridge user guide. The production add-ons are idempotency keys (HR employee ID + event ID), a DLQ with an alarm, reserved concurrency on revoke Lambdas, and a resource policy that allows events.amazonaws.com from this bus ARN only.
A layoff webhook or a bad HRIS replay will stampede Okta and AWS APIs if concurrency is unbounded. That looks like an outage, not a control. We would rather drain a queue slowly than 429 ourselves into a partial revoke mess.
Metrics we actually alarm: duplicates, revokes_fail, dlq_visible. Dashboards are CloudWatch. We do not pretend this is a SIEM we operate. Traces go to X-Ray only if the account already has it.
Poison messages and the JML EventBridge path
A malformed HR payload that fails JSON schema should dead-letter, not retry into a partial Okta disable. We validate schema at the first Lambda, drop or DLQ, and page. Retrying poison is how you disable the wrong employee ID after a type coercion ("null" vs null).
The terminate path is fail-closed on identity systems we listed in the SOW and fail-open with a ticket on systems we explicitly excluded. Pretending every SaaS app is in the first sprint is how the project never ships.
Frequently Asked Questions
Is this the same as Step Functions?
Step Functions orchestrate long workflows and approvals. EventBridge routes events. We use both. Starting with Step Functions for a 30-line revoke is ceremony.
Can EventBridge replace our GRC platform?
No. It can push evidence into the platform or a ticket system. The GRC tool stays the binder.
How do you test rules without firing production revokes?
A second bus in a non-prod account, replay with EventBridge Archive, and a Lambda that only logs. Never test revoke rules in prod with real employee IDs.
What about EventBridge Pipes?
Pipes are useful for SQS/Kinesis → target. We use them when the source is already a queue. Custom buses still own domain events.
Will you build this on GCP Eventarc?
AWS is the specialization. Concepts transfer; we do not market Eventarc production depth we have not shipped. If a warm engagement requires it, that is a scoped ramp, not a homepage claim.