Wiring AWS DevOps Agent to Jira, ServiceNow, and PagerDuty via EventBridge

Wiring AWS DevOps Agent to Jira, ServiceNow, and PagerDuty via EventBridge

Cloud Platform Engineering

AWS DevOps Agent automates investigation and remediation of operational issues — reading CloudWatch alarms, querying logs, correlating signals, proposing or executing remediation steps. The question that operational teams asked immediately: where do the findings go? Most engineering organizations don’t work exclusively in the AWS console. They work in Jira, ServiceNow, PagerDuty, or their own internal systems. The agent’s investigation output needs to route there.

AWS’s EventBridge-based integration pattern answers this. The architecture is clean and extensible: the agent publishes events to EventBridge, a rule filters on the aws.aidevops source, Lambda executes the REST API call to the external system, and DynamoDB maintains the mapping between the agent’s internal task IDs and the external issue keys it creates.

The Architecture

EventBridge Rule Configuration

The agent publishes events with source: "aws.aidevops". An EventBridge rule pattern:

{
  "source": ["aws.aidevops"],
  "detail-type": ["DevOps Agent Task Created", "DevOps Agent Investigation Complete"]
}

This captures task creation (to create the external ticket) and investigation completion (to update the ticket with findings). Add additional detail-type values to capture status updates during the investigation.

Lambda Integration Handler

The Lambda function receives the EventBridge event and executes the REST API call to the external system. The function structure follows the same pattern for all three targets — only the API endpoint and payload shape differ.

For Jira:

def create_jira_issue(event_detail):
    jira_url = f"{JIRA_BASE_URL}/rest/api/3/issue"
    payload = {
        "fields": {
            "project": {"key": JIRA_PROJECT_KEY},
            "summary": event_detail["title"],
            "description": {
                "type": "doc",
                "version": 1,
                "content": [{"type": "paragraph", "content": [
                    {"type": "text", "text": event_detail["description"]}
                ]}]
            },
            "issuetype": {"name": "Incident"}
        }
    }
    response = requests.post(jira_url, json=payload, auth=(JIRA_EMAIL, JIRA_API_TOKEN))
    return response.json()["key"]  # e.g. "OPS-1234"

For PagerDuty:

def create_pagerduty_incident(event_detail):
    pd_url = "https://api.pagerduty.com/incidents"
    payload = {
        "incident": {
            "type": "incident",
            "title": event_detail["title"],
            "service": {"id": PAGERDUTY_SERVICE_ID, "type": "service_reference"},
            "body": {"type": "incident_body", "details": event_detail["description"]}
        }
    }
    headers = {
        "Authorization": f"Token token={PAGERDUTY_API_KEY}",
        "From": PAGERDUTY_EMAIL
    }
    response = requests.post(pd_url, json=payload, headers=headers)
    return response.json()["incident"]["id"]

DynamoDB Task ID Mapping

The critical link between the agent’s investigation lifecycle and the external ticket is the task ID mapping. Without it, investigation updates can’t route back to the correct ticket.

Table schema:

  • Partition key: task_id (from the aws.aidevops event)
  • Attributes: external_issue_key (Jira key, ServiceNow number, PagerDuty incident ID), system (jira/servicenow/pagerduty), created_at, status

On task creation: Lambda creates the external ticket, receives the external ID, and writes the mapping to DynamoDB.

On investigation update events: Lambda queries DynamoDB by task_id, retrieves the external_issue_key, and posts the update to the correct ticket.

def get_external_key(task_id):
    response = dynamodb.get_item(
        TableName=MAPPING_TABLE,
        Key={"task_id": {"S": task_id}}
    )
    return response["Item"]["external_issue_key"]["S"]

Credentials via Secrets Manager

Lambda environment variables are not appropriate for API credentials. Store credentials in Secrets Manager and retrieve at function initialization:

import boto3, json

def get_secret(secret_name):
    client = boto3.client('secretsmanager')
    response = client.get_secret_value(SecretId=secret_name)
    return json.loads(response['SecretString'])

# At cold start (outside handler)
JIRA_CREDS = get_secret("devops-agent/jira-credentials")
JIRA_EMAIL = JIRA_CREDS["email"]
JIRA_API_TOKEN = JIRA_CREDS["api_token"]

Retrieve at initialization (outside the handler) to avoid a Secrets Manager call on every invocation while still refreshing on Lambda cold starts.

IAM Permissions

The Lambda execution role needs:

  • dynamodb:GetItem, dynamodb:PutItem, dynamodb:UpdateItem on the mapping table
  • secretsmanager:GetSecretValue on the credential secrets
  • Standard Lambda logging permissions

EventBridge needs permission to invoke the Lambda function — add a resource-based policy via aws lambda add-permission or in the EventBridge rule configuration.

Extending to ServiceNow

ServiceNow’s Table API follows the same pattern with a different endpoint structure:

SNOW_URL = f"https://{SNOW_INSTANCE}.service-now.com/api/now/table/incident"
payload = {
    "short_description": event_detail["title"],
    "description": event_detail["description"],
    "urgency": "2",  # map from DevOps Agent severity
    "impact": "2"
}
response = requests.post(
    SNOW_URL,
    json=payload,
    auth=(SNOW_USERNAME, SNOW_PASSWORD)
)
incident_number = response.json()["result"]["number"]  # e.g. "INC0001234"

The So What

This integration pattern closes the gap between AWS DevOps Agent’s investigation capability and the organizational workflows that actually move incidents to resolution. Most operations teams have process discipline built around their ticketing and incident management systems — the agent’s findings need to enter those systems to be actioned by the right people.

The EventBridge architecture is the right choice here because it decouples the agent’s event publication from the integration target. Adding a new external system means adding a Lambda function and a DynamoDB update — the agent itself doesn’t change. The pattern also works for custom internal systems through the same REST API approach.

For platform engineering teams evaluating DevOps Agent adoption: this integration is the bridge between “interesting AWS native capability” and “fits our operational workflow.” Building it from the published pattern is a half-day of work; the architecture complexity is in the DynamoDB mapping design, not the API calls.

Content created with AI assistance and reviewed for accuracy.

💬

Join the conversation

Stack Insiders is our free community for readers who want to go deeper — share resources, ask questions, and connect with others across every vertical we cover.

Join Stack Insiders →

Newsletter coming soon.

Curated digests across AI, biohacking, photography, travel, and more. Be the first to know when we launch.