Closing the Jira Incident Loop with AWS DevOps Agent
Many operations teams manage incidents in Jira, while AWS DevOps Agent investigates issues using telemetry, code, and deployment data. This article presents a bridge pattern that creates a Jira issue, starts an AWS DevOps Agent investigation, records both identifiers in DynamoDB, and is designed to return the investigation summary to the original issue. It covers the architecture, role-based personas, implementation, security controls, test evidence, and production improvements.
The Workflow that creates a Jira issue, starts an AWS DevOps Agent investigation, stores the issue and task relationship in DynamoDB, and publishes findings back to Jira.
A note on the scenario and names. This article uses a synthetic demonstration environment. The saved test evidence validates Jira issue creation, AWS DevOps Agent task creation, and identifier correlation. The scheduled status and Jira write-back components are proof-of-concept source and require the production changes described below. No customer information, production account IDs, or credentials are included. AWS service behavior and feature status are based on public documentation as of July 2026. Check the current documentation before implementation.
The problem
Jira is often the system where operations teams assign incidents, record decisions, and preserve a timeline of work. AWS DevOps Agent can investigate an operational issue by correlating telemetry, code changes, deployments, and AWS resource relationships. Without an integration, engineers must start the investigation separately, copy identifiers between systems, monitor progress, and paste the final findings into Jira.
The bridge pattern in this article connects those steps. One request creates the Jira issue and starts the investigation. A correlation record keeps the Jira issue key and AWS DevOps Agent task ID together. A scheduled publisher is designed to monitor the task and add its result to the original Jira issue.
The intended operating model is:
- Jira remains the work record for the incident.
- AWS DevOps Agent performs the investigation.
- DynamoDB preserves the relationship between the two systems.
- A publisher returns the investigation result to Jira.
What you will learn
- How to create a Jira Cloud issue through Jira REST API v3
- How to start an AWS DevOps Agent investigation with
CreateBacklogTask - How to maintain a durable Jira issue to investigation mapping in DynamoDB
- How to design status monitoring and Jira write-back
- How to divide the workflow into clear operational personas without overstating the implementation
- Which proof-of-concept shortcuts must change before production use
The operational personas
This article uses persona names to make ownership easy to understand. They are responsibility labels, not four separately deployed custom agents. AWS DevOps Agent performs the managed triage and investigation. The intake and publishing responsibilities run in AWS Lambda.
| Persona | Implementation | Responsibility |
|---|---|---|
| Intake Coordinator | Incident creator Lambda | Creates the Jira issue, starts the investigation, stores the correlation record, and adds the initial Jira comment |
| Triage Specialist | Built-in AWS DevOps Agent incident triage | Correlates related incidents and decides whether the task should proceed, link to an active investigation, or be skipped. Skills can influence correlation and define skip criteria |
| Investigation Specialist | Built-in AWS DevOps Agent investigation | Reviews connected operational data, records findings, determines a root cause when evidence supports one, and produces an investigation record |
| Findings Publisher | Scheduled publisher Lambda | Checks task status, reads the available result for completed tasks, posts an approved form of that result to Jira, and updates the correlation record |
AWS introduced user-defined custom agents on June 12, 2026. Custom agents support user-defined reports, audits, and scheduled workflows, but they are not required for this pattern and were not used in this demonstration. Keeping that distinction clear makes the architecture easier to reproduce.
Architecture
The design uses two Lambda functions, one DynamoDB table, an Amazon EventBridge schedule, AWS Secrets Manager, Jira Cloud REST API v3, and an AWS DevOps Agent Agent Space.
The numbered flow is:
- An authenticated client sends a trigger to the Intake Coordinator. The demonstration accepts an optional
scenarioIdand generates a standard incident summary. - The Intake Coordinator creates a Jira issue and receives the issue key.
- The Intake Coordinator calls
CreateBacklogTaskwith task typeINVESTIGATIONand a reference to the Jira issue. - The Intake Coordinator stores the Jira issue key, task ID, state, creation time, and expiration time in DynamoDB.
- AWS DevOps Agent triages the task. It may proceed, link the task to an active investigation, or skip it.
- For a task that proceeds, AWS DevOps Agent investigates using the data sources connected to the Agent Space.
- The Findings Publisher is designed to run on an EventBridge schedule.
- The Findings Publisher reads pending mappings and checks task status.
- For completed tasks, it retrieves journal records and selects an appropriate summary.
- It publishes the reviewed result to Jira.
- It records the final internal state in DynamoDB.
Why this implementation uses the task API
AWS DevOps Agent supports generic Agent Space webhooks for external incident sources. On June 24, 2026, AWS added the choice of HMAC or API key authentication for these webhooks. A webhook is a good option when an external system only needs to submit an incident event.
This implementation uses the documented CreateBacklogTask API because it returns a task ID immediately. The task ID is stored beside the Jira issue key and is later used to retrieve status and journal records. The API also accepts reference metadata, including a reference ID and URL, so the investigation can point back to the Jira issue.
If your requirement is a one-way Jira Automation trigger, consider the generic webhook first. If your requirement includes deterministic identifier mapping and controlled write-back to the originating issue, the task API plus a durable correlation record provides the required building blocks.
Prerequisites
Before building the integration, prepare the following:
- An AWS DevOps Agent Agent Space with the required AWS accounts and operational data sources connected
- A Jira Cloud project and a dedicated Jira service account
- A Jira API token with only the permissions needed to create issues and comments
- An AWS Secrets Manager secret for the Jira site URL, service account email, token, project key, and issue type
- A DynamoDB table with a string partition key named
incidentSysId - DynamoDB Time to Live enabled on the numeric
ttlattribute if records should expire automatically - Two Lambda execution roles with least-privilege access to their required AWS APIs
- An authenticated HTTP endpoint for the incident creator Lambda
- An EventBridge schedule for the publisher Lambda
A generic secret value can use this structure:
{ "site": "https://example.atlassian.net", "email": "jira-integration@example.com", "token": "REPLACE_WITH_SECRET_VALUE", "project": "OPS", "issuetype": "10003" }
OPS and 10003 are examples. Replace them with a project key and issue type ID that exist in your Jira project. Do not place the Jira token in Lambda environment variables, source code, frontend code, or deployment logs.
Step 1: Create the Jira issue
Jira Cloud REST API v3 uses Atlassian Document Format for rich-text fields such as the issue description and comment body. The creator Lambda converts each line of text into an Atlassian Document Format paragraph, then sends the request to /rest/api/3/issue.
The following is an adapted example. The demonstration handler accepts an optional scenarioId and generates incident_title and incident_description internally.
def adf(text): paragraphs = [] for line in text.split("\n"): content = [{"type": "text", "text": line}] if line.strip() else [] paragraphs.append({"type": "paragraph", "content": content}) return {"type": "doc", "version": 1, "content": paragraphs} issue = jira("POST", "/rest/api/3/issue", { "fields": { "project": {"key": jira_config["project"]}, "issuetype": {"id": jira_config["issuetype"]}, "summary": incident_title, "description": adf(incident_description), } }) issue_key = issue["key"] issue_url = f"{jira_config['site'].rstrip('/')}/browse/{issue_key}"
The Jira response supplies the issue key. The integration uses that key for the investigation title, reference metadata, DynamoDB record, and later comments.
Step 2: Start the investigation
The creator Lambda resolves the Agent Space association used for the reference and calls CreateBacklogTask. The current API supports an INVESTIGATION task type, priority, title, description, optional client token, and optional reference metadata.
The following is an adapted excerpt from the proof of concept:
task_response = devops_agent.create_backlog_task( agentSpaceId=agent_space_id, taskType="INVESTIGATION", title=f"Investigate Jira incident {issue_key}", description=( f"Investigation triggered from Jira issue {issue_key}. " "Analyze recent operational changes and report the root cause." ), priority="HIGH", reference={ "system": reference_system, "title": f"Jira {issue_key}", "referenceId": issue_key, "referenceUrl": issue_url, "associationId": association_id, }, ) task_id = task_response["task"]["taskId"]
ListAssociations is paginated. A production implementation should read all pages or use the supported service filter, select the intended event_channel association, and stop with a clear error when that association is absent. Do not silently substitute an unrelated association.
Use a stable clientToken to prevent the same intake request from creating the AWS DevOps Agent task more than once. This protects task creation only. It does not make Jira issue creation or the complete multi-system workflow idempotent.
Step 3: Store the correlation record
The task ID and Jira issue key must survive Lambda restarts and independent component failures. A DynamoDB record provides the durable mapping.
now = datetime.datetime.now(datetime.timezone.utc) dynamodb.put_item( TableName=correlation_table, Item={ "incidentSysId": {"S": f"JIRA#{issue_key}"}, "incidentNumber": {"S": issue_key}, "taskId": {"S": task_id}, "ticketSource": {"S": "jira"}, "status": {"S": "PENDING"}, "createdAt": {"S": now.strftime("%Y-%m-%dT%H:%M:%SZ")}, "ttl": {"N": str(int(now.timestamp()) + 7 * 86400)}, }, )
The ttl value removes old records only when DynamoDB Time to Live is enabled on that attribute. Deletion is asynchronous, so application logic must not depend on immediate removal. Set retention according to your audit and support requirements.
After saving the record, the Intake Coordinator adds an initial Jira comment containing the task ID and a clear statement that the investigation has started.
Step 4: Check status and collect findings
The publisher Lambda is designed to run from EventBridge every minute. The retained proof-of-concept source contains that schedule assumption in its function documentation, but the workspace does not contain the schedule infrastructure. Verify the EventBridge rule in your deployment.
The current Task API documents these states:
PENDING_TRIAGE, LINKED, PENDING_START, IN_PROGRESS, PENDING_CUSTOMER_APPROVAL, COMPLETED, FAILED, TIMED_OUT, CANCELED, and SKIPPED.
The proof-of-concept poller recognizes a smaller set of observed values. Before production, update it to handle every documented terminal state. For LINKED, use primaryTaskId and statusReason to point Jira to the primary investigation or to track that investigation according to your process. For SKIPPED, TIMED_OUT, CANCELED, and FAILED, post an appropriate status message and preserve the exact AWS DevOps Agent state in the correlation record.
For a completed task, the publisher paginates through ListJournalRecords. In this demonstration, records named investigation_summary_md and finding were used to select written output. The public API defines recordType as a string and content as a JSON value, but it does not define those record names or guarantee string content. Treat the names as observed behavior, inspect the records in your environment, and handle non-string JSON safely.
The following hardened excerpt illustrates those checks:
content = record.get("content") if not isinstance(content, str): continue if record.get("recordType") == "investigation_summary_md" and not summary: summary = content elif record.get("recordType") == "finding": findings.append(content)
Before sending any summary to Jira, apply your data-handling rules. Investigation output can contain resource identifiers, log excerpts, topology details, and deployment information. Confirm Jira project access, retention, data residency, and redaction requirements first.
Step 5: Apply least-privilege access
Separate the two Lambda execution roles.
The Intake Coordinator needs permission to:
- Read the Jira secret
- List and select the required Agent Space association
- Create an investigation task in the intended Agent Space
- Write a correlation record
The Findings Publisher needs permission to:
- Read the Jira secret
- Read and update correlation records
- Read task status and journal records from the intended Agent Space
Scope resource permissions to the intended secret, table, and Agent Space wherever the API supports resource-level permissions. Encrypt the secret and table with AWS Key Management Service keys when your security requirements call for customer-managed keys.
Never expose the creator as an unauthenticated public Function URL. Open CORS is not authentication and does not protect against non-browser callers. Enforce AWS Identity and Access Management authentication at the Function URL or use an Amazon API Gateway authorizer. Restrict allowed origins, validate request size and fields, rate-limit callers, and remove temporary sandbox endpoints after testing.
What we validated
The saved test returned HTTP 200 with both a Jira issue key and an AWS DevOps Agent task ID. A separate Jira query confirmed that the issue existed with the expected summary, creation time, and status. This evidence validates issue creation, task creation, and identifier correlation.
The saved evidence does not prove completed-task polling, journal retrieval, Jira findings write-back, linked or skipped task handling, or duplicate prevention. Validate those paths before describing the complete loop as production-ready.
Testing also found an Atlassian API change worth documenting. A request to the former search route returned HTTP 410 with instructions to migrate to /rest/api/3/search/jql. The replacement route rejected an unbounded query, so the final verification used a project restriction. For new integrations, use the current enhanced JQL endpoint or retrieve a known issue directly with /rest/api/3/issue/{issueKey}. Check the current Atlassian REST API documentation because Jira Cloud endpoints can change independently of the AWS integration.
Production improvements
The proof of concept is intentionally small. Before using this pattern for a production incident process, make these changes:
- Add a durable workflow. Jira creation, task creation, correlation storage, and the initial comment are separate operations. Use AWS Step Functions or explicit state transitions in DynamoDB, then add reconciliation for partial failures and orphaned issues or tasks.
- Prevent duplicate intake. Create a deduplication record with a conditional DynamoDB write before creating the Jira issue. Reuse the same request key and
clientTokenwhen the same intake request is submitted again. - Prevent duplicate comments. Claim a pending record with a conditional update before write-back. Store a publication identifier and final task status so overlapping invocations or partial failures cannot post the same result twice.
- Handle every task state. Support the complete documented status list, preserve the raw task status, and define explicit handling for linked, skipped, canceled, and timed-out tasks.
- Avoid table scans. Add an index that supports queries by
ticketSourceandstatus, or use a queue-driven design when investigation volume grows. - Create an explicit failure path. The proof-of-concept poller catches per-record errors and returns success. Explicitly send failed records to Amazon Simple Queue Service or surface an invocation failure. A Lambda dead-letter queue alone will not capture errors that the function catches and suppresses.
- Protect the ingress endpoint. Require authentication, restrict origins, validate payloads, enforce quotas, and log rejected requests without recording credentials.
- Limit Jira permissions. Use a dedicated service account that can access only the intended projects and issue operations.
- Control data publication. Redact sensitive fields and confirm Jira access, retention, and residency requirements before adding investigation output to an issue.
- Improve comment formatting. Convert approved Markdown content into structured Atlassian Document Format instead of treating every line as a plain paragraph.
- Track publication attempts. Store the attempt count, last error, last update time, and publication identifier so operators can distinguish an active investigation from a failed publisher.
- Define ticket transitions. Decide whether a completed investigation should only add a comment or also transition the Jira issue. Keep automatic transitions separate from investigation logic and apply explicit safeguards.
Current feature choices
Two recent AWS DevOps Agent releases affect how teams may extend this design:
- On June 12, 2026, AWS released custom agents for user-defined operational tasks. You could use a custom agent for a scheduled incident report or audit, but the Jira bridge described here does not require one.
- On June 24, 2026, generic Agent Space webhooks added a choice between HMAC and API key authentication. HMAC provides payload integrity and replay protection, while API key authentication is simpler. A webhook is a strong option for one-way incident submission.
The direct task API remains appropriate when the integration needs the task ID at creation time and must maintain a deterministic link to Jira.
What this pattern does not do
This bridge does not provide a native Jira connector inside AWS DevOps Agent. It does not create multiple AWS DevOps Agent custom agents. It does not automatically execute remediation, approve changes, or transition Jira workflow states. Its scope is deliberately narrow: create and correlate an investigation, monitor its status, and publish an approved form of the result to the original Jira issue.
That narrow scope is useful. It keeps ticket updates predictable, preserves a human review point, and allows each component to have a small permission set.
Summary
The Jira bridge pattern uses one Lambda function for intake, one scheduled Lambda function for publication, a DynamoDB correlation table, Secrets Manager, EventBridge, Jira Cloud REST API v3, and the documented AWS DevOps Agent task APIs.
The persona model makes responsibility clear without hiding the implementation. Jira owns the incident record, AWS DevOps Agent owns triage and investigation, DynamoDB owns the mapping, and the publisher owns the return path. The saved evidence validates the creation and correlation path. The complete write-back loop should be tested after applying the task-state, idempotency, failure-recovery, security, and data-handling controls described in this article.
References
- AWS DevOps Agent, What is new
- AWS DevOps Agent, Autonomous incident response
- AWS DevOps Agent API, CreateBacklogTask
- AWS DevOps Agent API, Task
- AWS DevOps Agent API, ListJournalRecords
- AWS DevOps Agent API, ListAssociations
- AWS DevOps Agent, Invoking DevOps Agent through Webhook
- AWS DevOps Agent, Custom agents
- Amazon DynamoDB, Using time to live
- Atlassian Developer, Jira Cloud REST API v3
- Atlassian Developer, Atlassian Document Format
- Language
- English
Relevant content
- Accepted Answer
asked a month ago
- Accepted Answer
asked 2 months ago
