Using AWS events to start workflows

Modified on Thu, 25 May, 2023 at 12:10 PM

The setup of the AWS environment is detailed in the Direktiv documentation page. However, this is an example of how the events and data received from AWS look and how the workflow can be configured to start based on an event.


Workflow configuration

The below is a snippet of the workflow configuration:


start:
  type: event
  state: check-event
  event:
    type: "AWS API Call via CloudTrail"

states:
  #
  # We need to check that the event is a "TerminateInstances" event for AWS
  #
  - id: check-event
    log: jq(.)
    type: switch
    defaultTransition: event-filter-failed
    conditions:
      - condition: 'jq(."AWS API Call via CloudTrail".data.detail.eventName == "TerminateInstances")'
        transition: get-instance-details-aws
        transform: jq(."AWS API Call via CloudTrail".data)

  - id: event-filter-failed
    log: jq(.)
    type: noop

  - id: get-instance-details-aws
    log: jq(.)
    type: noop

The "start" definition instructs Direktiv to only start this workflow if an event from AWS called "AWS API Call via CloudTrail" is received.

In this case we didn't apply any filter on the AWS EventBridge configuration. So the first state in the workflow will check whether the event matches a "TerminateInstances" event (notifying an EC2 instances is being terminated).


AWS CloudEvent details

Let's look at the structure of the CloudEvent received from AWS:


{
  "AWS API Call via CloudTrail": {
    "data": {
      "account": "338328518639",
      "detail": {
        "awsRegion": "us-west-1",
        "eventCategory": "Management",
        "eventID": "73fe93e6-8302-40b7-9848-efcb7585596b",
        "eventName": "TerminateInstances",
        "eventSource": "ec2.amazonaws.com",
        "eventTime": "2023-02-03T03:16:58Z",
        "eventType": "AwsApiCall",
        "eventVersion": "1.08",
        "managementEvent": true,
        "readOnly": false,
        "recipientAccountId": "338328518639",
        "requestID": "d286b9f3-e4fe-4712-94a3-5198db2cbce3",
        "requestParameters": {
          "instancesSet": {
            "items": [
              {
                "instanceId": "i-0772f323027e52c97"
              }
            ]
          }
        },
        "responseElements": {
          "instancesSet": {
            "items": [
              {
                "currentState": {
                  "code": 32,
                  "name": "shutting-down"
                },
                "instanceId": "i-0772f323027e52c97",
                "previousState": {
                  "code": 16,
                  "name": "running"
                }
              }
            ]
          },
          "requestId": "d286b9f3-e4fe-4712-94a3-5198db2cbce3"
        },
        "sessionCredentialFromConsole": "true",
        "sourceIPAddress": "67.188.77.77",
        "userAgent": "AWS Internal",
        "userIdentity": {
          "accessKeyId": "ASIAU5RPTP7XTA4AZV4X",
          "accountId": "338328518639",
          "arn": "arn:aws:iam::338328518639:root",
          "principalId": "338328518639",
          "sessionContext": {
            "attributes": {
              "creationDate": "2023-02-03T03:13:06Z",
              "mfaAuthenticated": "false"
            },
            "sessionIssuer": {},
            "webIdFederationData": {}
          },
          "type": "Root"
        }
      },
      "detail-type": "AWS API Call via CloudTrail",
      "id": "40f83262-7a58-4e20-8633-49829e205eca",
      "region": "us-west-1",
      "resources": [],
      "source": "aws.ec2",
      "time": "2023-02-03T03:16:58Z",
      "version": "0"
    },
    "id": "40f83262-7a58-4e20-8633-49829e205eca",
    "source": "aws.ec2",
    "specversion": "1.0",
    "subject": "aws.ec2",
    "time": "2023-02-03T03:16:58Z",
    "type": "AWS API Call via CloudTrail"
  }
}

The structure follows the same convention as the CloudEvent specification. The top-level JSON attribute in the object is the event name ("AWS API Call via CloudTrail").


What follows is the "data" object - which contains the payload from AWS and all the information needed for machine operations in the subsequent workflow steps.


To following statement is a condition match using jq to check whether it is a specific type of event:


condition: 'jq(."AWS API Call via CloudTrail".data.detail.eventName == "TerminateInstances")'

The next jq statement removes the top-level object and only keeps the "data" object

transform: jq(."AWS API Call via CloudTrail".data)

The transformed output is printed using a log statement in the noop state called get-instance-details-aws and produces the following input to the state:

{
  "account": "338328518639",
  "detail": {
    "awsRegion": "us-west-1",
    "eventCategory": "Management",
    "eventID": "73fe93e6-8302-40b7-9848-efcb7585596b",
    "eventName": "TerminateInstances",
    "eventSource": "ec2.amazonaws.com",
    "eventTime": "2023-02-03T03:16:58Z",
    "eventType": "AwsApiCall",
    "eventVersion": "1.08",
    "managementEvent": true,
    "readOnly": false,
    "recipientAccountId": "338328518639",
    "requestID": "d286b9f3-e4fe-4712-94a3-5198db2cbce3",
    "requestParameters": {
      "instancesSet": {
        "items": [
          {
            "instanceId": "i-0772f323027e52c97"
          }
        ]
      }
    },
    "responseElements": {
      "instancesSet": {
        "items": [
          {
            "currentState": {
              "code": 32,
              "name": "shutting-down"
            },
            "instanceId": "i-0772f323027e52c97",
            "previousState": {
              "code": 16,
              "name": "running"
            }
          }
        ]
      },
      "requestId": "d286b9f3-e4fe-4712-94a3-5198db2cbce3"
    },
    "sessionCredentialFromConsole": "true",
    "sourceIPAddress": "67.188.77.77",
    "userAgent": "AWS Internal",
    "userIdentity": {
      "accessKeyId": "ASIAU5RPTP7XTA4AZV4X",
      "accountId": "338328518639",
      "arn": "arn:aws:iam::338328518639:root",
      "principalId": "338328518639",
      "sessionContext": {
        "attributes": {
          "creationDate": "2023-02-03T03:13:06Z",
          "mfaAuthenticated": "false"
        },
        "sessionIssuer": {},
        "webIdFederationData": {}
      },
      "type": "Root"
    }
  },
  "detail-type": "AWS API Call via CloudTrail",
  "id": "40f83262-7a58-4e20-8633-49829e205eca",
  "region": "us-west-1",
  "resources": [],
  "source": "aws.ec2",
  "time": "2023-02-03T03:16:58Z",
  "version": "0"
}

AWS Filters

An example of an AWS event pattern is shown below:

{
  "source": ["aws.ec2"],
  "detail-type": ["AWS API Call via CloudTrail"],
  "detail": {
    "eventSource": ["ec2.amazonaws.com"],
    "eventName": ["TerminateInstances"]
  }
}

AWS Input Transformer

The input transformer (to create the CloudEvent sent to Direktiv) is shown below:

Input path

{
  "id": "$.id",
  "source": "$.source",
  "state": "$.detail.state",
  "subject": "$.source",
  "time": "$.time",
  "type": "$.detail-type"
}

Input template

{
   "specversion":"1.0",
   "id":"<id>",
   "source":"<source>",
   "type":"<type>",
   "subject":"<subject>",
   "time":"<time>",
   "data":"<aws.events.event.json>"
}

Was this article helpful?

That’s Great!

Thank you for your feedback

Sorry! We couldn't be helpful

Thank you for your feedback

Let us know how can we improve this article!

Select at least one of the reasons
CAPTCHA verification is required.

Feedback sent

We appreciate your effort and will try to fix the article