Salesforce Platform Events are a powerful real-time messaging mechanism inside the Salesforce ecosystem. The problem: they don’t natively fan out to AWS services. If you want an order event from Salesforce to trigger a Step Functions workflow, update a DynamoDB table, and notify an SQS queue - you need a bridge.
aws-sfdc-event-bridge is that bridge, built with API Gateway, Python Lambda, and EventBridge, all managed by Terraform.
Architecture
Salesforce Platform Event
│
▼ (Outbound Message / Apex callout)
API Gateway (REST endpoint)
│
▼ HMAC-SHA256 validation
Python Lambda
│
▼
Amazon EventBridge (custom event bus)
│
┌────┼────┐
▼ ▼ ▼
SQS SNS Step Functions
Salesforce sends events to the API Gateway endpoint via either an Outbound Message (declarative, no Apex required for simple payloads) or an Apex callout from a Platform Event trigger. The Lambda validates the HMAC signature, parses the payload, and publishes a structured event to EventBridge.
From EventBridge, rules route events to any number of downstream targets: SQS queues for async processing, SNS topics for fan-out to multiple consumers, or Step Functions for orchestrated workflows.
HMAC Validation
The most important security layer is the HMAC signature check. Without it, anyone who discovers the API Gateway URL can inject arbitrary events into your AWS infrastructure.
The Salesforce side generates a signature:
String payload = JSON.serialize(eventData);
String hmac = EncodingUtil.convertToHex(
Crypto.generateMac('HmacSHA256',
Blob.valueOf(payload),
Blob.valueOf(secret)
)
);
req.setHeader('X-Salesforce-HMAC', hmac);
The Lambda validates it:
import hmac, hashlib
def validate_signature(body: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode(),
body,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)
hmac.compare_digest is critical - it prevents timing attacks that could otherwise be used to brute-force the secret by measuring response latency.
The secret lives in AWS Secrets Manager, not in the Lambda environment variables. The Lambda fetches it on cold start and caches it for the duration of the execution environment.
EventBridge Event Structure
Every event published to EventBridge follows a consistent envelope:
{
"source": "salesforce.platform-events",
"detail-type": "OrderPlaced__e",
"detail": {
"orderId": "a1B3x000001ABCDEF",
"customerId": "0013x000001ABCDEF",
"amount": 149.99,
"currency": "GBP",
"timestamp": "2026-02-03T14:23:00Z"
}
}
detail-type is the Salesforce Platform Event API name. EventBridge rules can match on this to route specific event types to specific targets:
resource "aws_cloudwatch_event_rule" "order_placed" {
event_bus_name = aws_cloudwatch_event_bus.salesforce.name
event_pattern = jsonencode({
source = ["salesforce.platform-events"]
detail-type = ["OrderPlaced__e"]
})
}
Terraform Layout
The entire infrastructure is defined in Terraform, split by concern:
terraform/
├── api_gateway.tf # REST API, stage, deployment
├── lambda.tf # function, IAM role, log group
├── eventbridge.tf # custom bus, rules, targets
├── secrets.tf # HMAC secret in Secrets Manager
├── sqs.tf # target queues (with DLQ)
└── variables.tf # environment-agnostic config
No long-lived credentials anywhere. The Lambda’s IAM role has least-privilege permissions: events:PutEvents on the custom bus, secretsmanager:GetSecretValue on the HMAC secret, nothing else. API Gateway uses a resource policy to allow invocation from Lambda only.
Dead Letter Queues and Retries
Lambda is configured with a dead-letter queue for failed invocations (e.g. if Secrets Manager is temporarily unavailable). EventBridge rules have retry policies. SQS targets have their own DLQs.
The failure handling chain:
Lambda fails → DLQ (Lambda DLQ)
EventBridge delivery fails → EventBridge retry (up to 24h)
SQS consumer fails → SQS DLQ after maxReceiveCount
Each layer’s DLQ publishes to an SNS topic that emails the ops team. No silent failures.
Deployment
cd terraform/
terraform init
terraform plan -var="environment=prod"
terraform apply -var="environment=prod"
The outputs include the API Gateway URL, which you configure in Salesforce as the callout endpoint. The HMAC secret is generated by Terraform and stored in Secrets Manager - you retrieve it once to configure the Apex side:
aws secretsmanager get-secret-value \
--secret-id sfdc-event-bridge-hmac-prod \
--query SecretString --output text
Why Not Just Use MuleSoft?
MuleSoft is the canonical answer for Salesforce–AWS integration in enterprise environments. It’s also expensive, operationally complex, and overkill for event forwarding. For organisations that are already AWS-native and don’t need the full MuleSoft feature set (data transformation, complex routing, API management), a lightweight Lambda-based bridge is faster to deploy, cheaper to run, and easier to maintain.
The project is on GitHub.