Skip to content

This guide walks you through creating Amazon EventBridge rules that route Cloud Security events to SQS queues, and configuring Synqly to read those queues. Each operation type can use a separate queue, so you deploy one EventBridge pipeline per operation. The integration uses the same AWS credential options as other AWS providers: static credentials (IAM user access keys) or role-based access (IAM role assumption).

For AWS EventBridge SQS, the recommended split is:

Queue keyIntended sourcePurpose
query_compliance_findingsSecurity Hub compliance/control findingsCompliance Finding data
query_threatsSecurity Hub threat/detection findings and/or native GuardDuty findingsDetection / threat data
query_cloud_resource_inventoryReserved for a future inventory pipelineCloud resource inventory
query_eventsReserved for a future events pipelineCloud activity events

Today, query_compliance_findings and query_threats are supported in the AWS EventBridge SQS provider. The other queue keys are available in configuration so you can pre-wire queues as the additional operations are implemented.

Before you begin, ensure the following AWS services are enabled in your target account and Region:

If you want Security Hub to aggregate GuardDuty findings, open Security Hub → Integrations and ensure Amazon: GuardDuty shows Accepting findings. See: Integrating with AWS Security Hub.

Deploy the EventBridge pipelines

Deploy one CloudFormation stack per queue. The recommended starting point is:

  1. A compliance stack for query_compliance_findings
  2. A threats stack for query_threats

Each stack creates:

  1. An SQS Standard queue
  2. An IAM role that lets EventBridge send messages to that queue
  3. One or more EventBridge rules that route the intended source events into that queue

Compliance template

Use this queue for queues.query_compliance_findings. It accepts only Security Hub compliance/control findings, rather than all Security Hub or GuardDuty findings.

The compliance rule is intentionally strict. It keeps only:

  • source = aws.securityhub
  • detail-type = Security Hub Findings - Imported
  • ProductName = Security Hub
  • GeneratorId values that start with security-control/
  • findings with Compliance.SecurityControlId and Compliance.Status
  • RecordState = ACTIVE

This keeps the compliance queue aligned with OCSF Compliance Finding data and avoids mixing in threat/detection findings.

Save the following template as eventbridge-sqs-compliance.yaml and deploy it:

AWSTemplateFormatVersion: "2010-09-09"
Description: >
  Send Security Hub compliance/control findings to an SQS Standard queue via EventBridge.

Parameters:
  QueueName:
    Type: String
    Default: synqly-compliance-findings
    Description: "Name of the SQS Standard queue that will hold Security Hub compliance/control findings."

Resources:
  SecurityEventsQueue:
    Type: AWS::SQS::Queue
    Properties:
      QueueName: !Ref QueueName
      MessageRetentionPeriod: 1209600
      VisibilityTimeout: 60

  EventBridgeToSqsRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service: events.amazonaws.com
            Action: sts:AssumeRole
      Policies:
        - PolicyName: "SendEventsToSqsPolicy"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                  - sqs:SendMessage
                Resource: !GetAtt SecurityEventsQueue.Arn

  SecurityEventsRule:
    Type: AWS::Events::Rule
    Properties:
      Description: "Send Security Hub compliance/control findings to an SQS Standard queue"
      EventBusName: "default"
      State: ENABLED
      EventPattern:
        source:
          - "aws.securityhub"
        detail-type:
          - "Security Hub Findings - Imported"
        detail:
          findings:
            ProductArn:
              - prefix: "arn:aws:securityhub:"
            ProductName:
              - "Security Hub"
            GeneratorId:
              - prefix: "security-control/"
            Compliance:
              SecurityControlId:
                - exists: true
              Status:
                - exists: true
            RecordState:
              - "ACTIVE"
      Targets:
        - Id: "SecurityEventsQueueTarget"
          Arn: !GetAtt SecurityEventsQueue.Arn
          RoleArn: !GetAtt EventBridgeToSqsRole.Arn

Outputs:
  QueueUrl:
    Description: "URL of the SQS Standard queue that receives compliance findings"
    Value: !Ref SecurityEventsQueue

  QueueArn:
    Description: "ARN of the SQS Standard queue that receives compliance findings"
    Value: !GetAtt SecurityEventsQueue.Arn

  EventBridgeRoleArn:
    Description: "IAM role used by EventBridge to send messages to the SQS queue"
    Value: !GetAtt EventBridgeToSqsRole.Arn

These queue settings are intentional:

  • MessageRetentionPeriod: 1209600 keeps messages for 14 days, which is the maximum SQS retention window and gives you more time to recover if Synqly is temporarily unable to consume the queue.
  • VisibilityTimeout: 60 hides a received message for 60 seconds so Synqly can process and delete it without immediate redelivery.

Threats template

Use this queue for queues.query_threats. It creates a single SQS queue and routes:

  • Security Hub threat/detection findings
  • Native GuardDuty findings

The threats stack uses two EventBridge rules that feed the same queue:

  • A Security Hub rule that keeps active threat findings and excludes compliance/control findings by requiring Types = Threats and Compliance.SecurityControlId = false
  • A GuardDuty rule that accepts native GuardDuty Finding events directly

Save the following template as eventbridge-sqs-threats.yaml and deploy it:

AWSTemplateFormatVersion: "2010-09-09"
Description: >
  Send Security Hub threat/detection findings and native GuardDuty findings to an SQS Standard queue via EventBridge.

Parameters:
  QueueName:
    Type: String
    Default: synqly-threat-findings
    Description: "Name of the SQS Standard queue that will hold Security Hub threat/detection findings and GuardDuty findings."

Resources:
  ThreatEventsQueue:
    Type: AWS::SQS::Queue
    Properties:
      QueueName: !Ref QueueName
      MessageRetentionPeriod: 1209600
      VisibilityTimeout: 60

  EventBridgeToSqsRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service: events.amazonaws.com
            Action: sts:AssumeRole
      Policies:
        - PolicyName: "SendThreatEventsToSqsPolicy"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                  - sqs:SendMessage
                Resource: !GetAtt ThreatEventsQueue.Arn

  SecurityHubThreatsRule:
    Type: AWS::Events::Rule
    Properties:
      Description: "Send Security Hub threat/detection findings to an SQS Standard queue"
      EventBusName: "default"
      State: ENABLED
      EventPattern:
        source:
          - "aws.securityhub"
        detail-type:
          - "Security Hub Findings - Imported"
        detail:
          findings:
            RecordState:
              - "ACTIVE"
            Types:
              - "Threats"
            Compliance:
              SecurityControlId:
                - exists: false
      Targets:
        - Id: "SecurityHubThreatsQueueTarget"
          Arn: !GetAtt ThreatEventsQueue.Arn
          RoleArn: !GetAtt EventBridgeToSqsRole.Arn

  GuardDutyThreatsRule:
    Type: AWS::Events::Rule
    Properties:
      Description: "Send native GuardDuty findings to an SQS Standard queue"
      EventBusName: "default"
      State: ENABLED
      EventPattern:
        source:
          - "aws.guardduty"
        detail-type:
          - "GuardDuty Finding"
      Targets:
        - Id: "GuardDutyThreatsQueueTarget"
          Arn: !GetAtt ThreatEventsQueue.Arn
          RoleArn: !GetAtt EventBridgeToSqsRole.Arn

Outputs:
  QueueUrl:
    Description: "URL of the SQS Standard queue that receives threat findings"
    Value: !Ref ThreatEventsQueue

  QueueArn:
    Description: "ARN of the SQS Standard queue that receives threat findings"
    Value: !GetAtt ThreatEventsQueue.Arn

  EventBridgeRoleArn:
    Description: "IAM role used by EventBridge to send messages to the SQS queue"
    Value: !GetAtt EventBridgeToSqsRole.Arn

These queue settings are intentional:

  • MessageRetentionPeriod: 1209600 keeps messages for 14 days, which is the maximum SQS retention window and gives you more time to recover if Synqly is temporarily unable to consume the queue.
  • VisibilityTimeout: 60 hides a received message for 60 seconds so Synqly can process and delete it without immediate redelivery.

Deploy the stacks

From a directory containing the templates:

aws cloudformation deploy \
  --template-file eventbridge-sqs-compliance.yaml \
  --stack-name eventbridge-sqs-compliance \
  --region us-east-1 \
  --capabilities CAPABILITY_NAMED_IAM
aws cloudformation deploy \
  --template-file eventbridge-sqs-threats.yaml \
  --stack-name eventbridge-sqs-threats \
  --region us-east-1 \
  --capabilities CAPABILITY_NAMED_IAM

To use different queue names:

--parameter-overrides QueueName=my-security-queue

Recommended defaults:

  • Compliance queue: synqly-compliance-findings
  • Threats queue: synqly-threat-findings

Retrieve stack outputs

After deployment, get the Queue URL and Queue ARN from each stack:

aws cloudformation describe-stacks \
  --stack-name eventbridge-sqs-compliance \
  --region us-east-1 \
  --query 'Stacks[0].Outputs'
aws cloudformation describe-stacks \
  --stack-name eventbridge-sqs-threats \
  --region us-east-1 \
  --query 'Stacks[0].Outputs'

Copy each QueueUrl value that you want to use in the integration. The URL includes your AWS account ID, for example:

  • https://sqs.us-east-1.amazonaws.com/123456789012/synqly-compliance-findings
  • https://sqs.us-east-1.amazonaws.com/123456789012/synqly-threat-findings

To find your account ID separately, run aws sts get-caller-identity and use the Account value.

Add SQS Consumer Permissions

The credential (IAM user or role) used by Synqly must have these permissions on each queue you configure:

  • sqs:ReceiveMessage
  • sqs:DeleteMessage
  • sqs:GetQueueAttributes

If you use an AdministratorAccess role, you already have them. Otherwise, create a policy with the queue ARN and attach it to the identity Synqly uses.

Get the queue ARN from the stack outputs (see Retrieve stack outputs above), or run:

aws cloudformation describe-stacks \
  --stack-name eventbridge-sqs-compliance \
  --region us-east-1 \
  --query 'Stacks[0].Outputs[?OutputKey==`QueueArn`].OutputValue' \
  --output text

If you also deploy the threats stack, repeat the same command with --stack-name eventbridge-sqs-threats.

Example policy (replace the queue ARNs with the queues you use):

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "sqs:ReceiveMessage",
        "sqs:DeleteMessage",
        "sqs:GetQueueAttributes"
      ],
      "Resource": [
        "<compliance-queue-arn>",
        "<threats-queue-arn>"
      ]
    }
  ]
}

Attach this policy to the IAM user or role (or SSO permission set) that Synqly uses.

AWS Credentials Configuration

Synqly supports two methods for authenticating with AWS: static credentials (IAM user access keys) and role-based access (IAM role assumption). Role-based access is recommended for production environments because it uses short-lived credentials and provides better auditability through CloudTrail.

Role-Based Access

Role-Based access is recommended and is considered an AWS best practice.

Role-based access uses AWS IAM roles to grant Synqly temporary credentials to access resources in your AWS account. This eliminates long-lived credentials and provides better security through the principle of least privilege.

1. Create an IAM Role

Create a role in your AWS account with a name that starts with SynqlyAccess (for example, SynqlyAccessS3Reader). This naming convention is required.

  1. In the AWS IAM console, go to Roles and choose Create role.
  2. For trusted entity type, choose Custom trust policy.
  3. Enter the following trust policy:
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::733459310821:role/SynqlyIntegrationAccess"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "StringEquals": {
          "sts:ExternalId": "YOUR_EXTERNAL_ID"
        }
      }
    }
  ]
}

Replace YOUR_EXTERNAL_ID with a unique identifier you generate (for example, a UUID). You will provide this External ID when configuring the integration.

  1. Name the role with a SynqlyAccess prefix (for example, SynqlyAccessMyIntegration).
  2. Attach the appropriate permissions policy for your use case.
  3. Create the role and note its ARN.

For more details, see:

External ID Requirements

The External ID is a security mechanism that prevents the confused deputy problem. It ensures that only authorized requests from Synqly can assume your role.

The External ID must contain only the following characters:

  • Alphanumeric characters (a-z, A-Z, 0-9)
  • Special characters: + = , . @ : / -
  • Must be between 2 and 1224 characters in length

Configuring the Integration Credentials

When creating an AWS integration in Synqly, provide the following configuration values based on your chosen authentication method.

Credential ParameterDescription
Role ARNThe ARN of the IAM role you created, for example arn:aws:iam::123456789012:role/SynqlyAccessMyIntegration. The role name must start with SynqlyAccess
External IDThe External ID you specified in the role's trust policy. This value must match exactly
Role Session NameOPTIONAL: A name for the role session. If not specified, Synqly generates a default session name
DurationOPTIONAL: The duration of the role session in seconds. The value can range from 900 seconds (15 minutes) up to the maximum session duration configured on your role (default is 1 hour)

Configure the Integration

Create your integration by supplying the following configuration values.

Integration ParameterDescription
CredentialAWS credentials with access to the SQS queue(s) (static or role-based, per the tabs above).
QueuesMap of operation to queue URL. Add entries for each operation you want to enable. See below.

Synqly infers the AWS Region from the configured SQS queue URL. If you configure multiple queues, use queues from the same AWS Region.

Queues by operation

Use the queues object to map each Cloud Security operation to its SQS queue URL. Each operation can use a different queue and EventBridge pipeline.

Queue keyOperationDescription
query_compliance_findingsCompliance findingsSecurity Hub compliance/control findings. Supported today.
query_threatsThreatsSecurity Hub threat/detection findings and/or GuardDuty findings. Supported today.
query_cloud_resource_inventoryCloud resource inventoryReserved for a future inventory pipeline.
query_eventsEventsReserved for a future events pipeline.

Configuration examples

Compliance only:

{
  "type": "cloudsecurity_awseventbridgesqs",
  "credential": { "..." },
  "queues": {
    "query_compliance_findings": "https://sqs.us-east-1.amazonaws.com/123456789012/synqly-compliance-findings"
  }
}

Compliance + threats:

{
  "type": "cloudsecurity_awseventbridgesqs",
  "credential": { "..." },
  "queues": {
    "query_compliance_findings": "https://sqs.us-east-1.amazonaws.com/123456789012/synqly-compliance-findings",
    "query_threats": "https://sqs.us-east-1.amazonaws.com/123456789012/synqly-threat-findings"
  }
}

Legacy (deprecated): You can still use queue_url for compliance findings. It is equivalent to queues.query_compliance_findings. When both are present, queues takes precedence.

Message retrieval limits

Synqly retrieves up to 10 messages per query or async operation. This limit is imposed by Amazon SQS message quotas: a single ReceiveMessage request can return at most 10 messages.

Synqly treats these queues as consumable event queues. After Synqly successfully reads and processes a message, it deletes that message from SQS so it is not returned again on a later read.

You can pass a limit parameter on the API request to request fewer messages (for example, limit=5), but the maximum remains 10.