Jira Event Forwarder
An AWS Lambda function that receives Jira Automation webhook events and forwards them to an SNS topic with structured metadata for filtering. Validates Bearer token authentication and restricts access to Atlassian egress IP ranges via an API Gateway resource policy.
The original Jira JSON payload is forwarded unchanged as the SNS message body.
Features
- Bearer Token Authentication: Validates
Authorization: Bearer <token>headers with constant-time comparison, supporting multiple tokens for zero-downtime rotation - Atlassian IP Restriction: API Gateway resource policy restricts access to Atlassian egress IP ranges (IPv4 and IPv6), fetched dynamically at deploy time
- Structured Metadata: Extracts event type, project key, and issue type as SNS message attributes
- SNS Subscription Filtering: Enables precise event routing to subscribers
- Custom Domain: Optional custom domain with automatic TLS certificate management via ACM and Route53
Architecture
Single Lambda function handles the webhook processing:
- Handler - API Gateway endpoint (
/webhook) that validates the Bearer token, extracts metadata from the Jira payload and request headers, and publishes to SNS
Prerequisites
- AWS CLI configured with appropriate credentials (IAM permissions for Lambda, API Gateway, SNS, CloudFormation, CloudWatch Logs, and optionally Route53/ACM for custom domain)
- Podman or Docker (for containerized SAM build/deploy)
- Python 3.11 or later (for development)
Deployment
Build and deploy using containerized AWS SAM CLI:
cd jira-event-forwarder
make build # Build Lambda package
make deploy # First deployment (interactive/guided)
make redeploy # Subsequent deployments (non-interactive)
Deployment outputs: ApiEndpoint - API Gateway endpoint URL
(<api-endpoint>)
Custom Domain
Optional custom domain with automatic TLS certificate management (ACM +
Route53). Requires a Route53 hosted zone. Deploy with CustomDomainName and
HostedZoneId parameters - CloudFormation handles certificate creation, DNS
validation, and configuration. Certificate validation takes 5-30 minutes; allow
up to 1 hour for DNS propagation.
Parameters
| Parameter | Description | Default |
|---|---|---|
ResourcePrefix |
Prefix for resources | myapp-prod |
SnsTopicArn |
SNS topic ARN | (required) |
JiraWebhookTokens |
Jira webhook tokens (JSON array) | [] |
SentryDsn |
Optional Sentry DSN | `` |
AtlassianIpv4Cidrs |
Atlassian egress IPv4 CIDRs (comma-list) | `` |
AtlassianIpv6Cidrs |
Atlassian egress IPv6 CIDRs (comma-list) | `` |
CustomDomainName |
Custom domain name | `` |
HostedZoneId |
Route53 hosted zone | `` |
Resource naming: Lambda and API resources follow
{ResourcePrefix}-{type}-{name} pattern (e.g., myapp-prod-lambda-handler,
myapp-prod-api).
Prerequisites: Requires an existing SNS topic. Deploy
hummingbird-events-topic first to create the topic, then use its
ARN for the SnsTopicArn parameter.
Usage
Jira Automation allows only one trigger per rule, and the “Multiple issue
events” trigger does not expose which event fired. Since each event needs a
distinct X-Jira-Event-Type header, configure 6 separate automation rules
— one per event type:
| Trigger | X-Jira-Event-Type value |
|---|---|
| Created | issue_created |
| Updated | issue_updated |
| Deleted | issue_deleted |
| Assigned | issue_assigned |
| Transitioned | issue_transitioned |
| Moved | issue_moved |
Each rule’s “Send web request” action:
- URL:
<api-endpoint> - Method: POST
- Headers:
Authorization: Bearer <token>— enter the token value and click “Hidden” to mask it in the Jira Automation UIX-Jira-Event-Type: <value from table above>
- Body: “Issue data (Jira format)”
SNS Subscription Filter Examples:
All Jira issue events:
{
"source": ["jira"]
}
Specific Jira event types:
{
"source": ["jira"],
"event_type": ["issue_created", "issue_updated"]
}
Events from a specific project:
{
"source": ["jira"],
"project_key": ["HUM"]
}
Development
See the main README for development workflows.
make setup # Install dependencies
make check # Lint code (ruff)
make fmt # Format code
make test # Run unit tests
make coverage # Run tests with coverage
Configuration
Lambda function receives configuration via environment variables (automatically set by CloudFormation):
| Variable | Description |
|---|---|
SNS_TOPIC_ARN |
SNS topic ARN |
JIRA_WEBHOOK_TOKENS |
Jira webhook tokens (JSON array) |
SENTRY_DSN |
Optional Sentry DSN |
AWS_REGION |
AWS region (auto-set) |
Event Metadata
The Lambda function extracts metadata from Jira webhook events and request headers, adding them as SNS message attributes:
| Attribute | Description | Example |
|---|---|---|
source |
Always "jira" |
jira |
event_type |
From X-Jira-Event-Type header |
issue_created |
project_key |
Jira project key | HUM |
issue_type |
Issue type name | Story, Bug |
content_encoding |
Compression format | gzip+base64 |
The event_type attribute is set from the X-Jira-Event-Type request header.
Each Jira Automation rule hardcodes the appropriate value for its trigger type
in this header (see Usage). If the header is absent, the event_type
attribute is omitted from the SNS message.
Security & Limitations
Security:
- Bearer token validation via
Authorizationheader with constant-time comparison (hmac.compare_digest) - API Gateway resource policy restricting source IPs to Atlassian egress
ranges (IPv4 and IPv6 CIDRs both via
aws:SourceIp), fetched dynamically at deploy time fromip-ranges.atlassian.com - Supports multiple active tokens for zero-downtime rotation
- Missing or invalid tokens return HTTP 401; no configured tokens returns HTTP 500
- SNS topic follows least privilege principle
- CloudWatch logs capture all webhook deliveries (7-day retention)
- Sentry integration for error tracking
Limitations:
- Lambda timeout: 30 seconds
- Lambda memory: 256 MB
- Webhook payload size: Up to 6 MB (API Gateway limit)
- SNS limits the compressed and base64-encoded message plus attributes to 256 KiB
The forwarder compresses the original JSON for SNS transport. Consumers that
decode gzip+base64 receive the unchanged webhook payload.
License
This project is licensed under the GNU General Public License v3.0 or later - see the LICENSE file for details.