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 key | Intended source | Purpose |
|  --- | --- | --- |
| `query_compliance_findings` | Security Hub compliance/control findings | Compliance Finding data |
| `query_threats` | Security Hub threat/detection findings and/or native GuardDuty findings | Detection / threat data |
| `query_cloud_resource_inventory` | Reserved for a future inventory pipeline | Cloud resource inventory |
| `query_events` | Reserved for a future events pipeline | Cloud 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.

## Prerequisites

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

- **AWS Security Hub** – Publishes compliance/control findings and can also aggregate partner or
service findings. See: [Enabling Security Hub
CSPM](https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-settingup.html).
- **AWS GuardDuty** – Publishes detection findings. See: [Getting started with
GuardDuty](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_settingup.html).


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](https://docs.aws.amazon.com/guardduty/latest/ug/securityhub-integration.html).

## 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:


```yaml
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:


```yaml
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:


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


```bash
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:


```bash
--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:


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


```bash
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](#retrieve-stack-outputs) above), or run:


```bash
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):


```json
{
  "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

Role-Based access is recommended and is considered an [AWS best
practice](https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#bp-workloads-use-roles).

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:



```json
{
  "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:

- [Access to AWS accounts owned by third
parties](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_common-scenarios_third-party.html)
- [Create a role using custom trust
policies](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-custom.html)


### External ID Requirements

The External ID is a security mechanism that prevents the [confused deputy
problem](https://docs.aws.amazon.com/IAM/latest/UserGuide/confused-deputy.html). 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 Parameter | Description |
|  --- | --- |
| Role ARN | The ARN of the IAM role you created, for example `arn:aws:iam::123456789012:role/SynqlyAccessMyIntegration`. The role name must start with `SynqlyAccess` |
| External ID | The External ID you specified in the role's trust policy. This value must match exactly |
| Role Session Name | **OPTIONAL:** A name for the role session. If not specified, Synqly generates a default session name |
| Duration | **OPTIONAL:** 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) |


Static Credentials (IAM User)
### Static Credentials (IAM User)

AWS static credentials are **NOT RECOMMENDED** for production systems. See the [AWS best
practices](https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#bp-workloads-use-roles)
for more details.

Static credentials consist of an Access Key ID and Secret Access Key associated with an IAM user.
Use this method for simpler setups.

#### 1. Create an IAM User

1. In the AWS IAM console, go to **Users** and choose **Create user**.
2. Enter a user name (for example, `SynqlyIntegration`).
3. Do not enable console access; this user only needs programmatic access.
4. Under permissions, choose **Attach policies directly** and attach the appropriate policy for your
use case.
5. Create the user.


#### 2. Create an Access Key

1. Open the newly created user and choose **Create access key**.
2. For the use case, choose **Third-party service**.
3. Create the key and securely copy the **Access Key ID** and **Secret Access Key**.


For more details, see:

- [Managing access keys for IAM
users](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html)
- [How an IAM administrator can manage IAM user access
keys](https://docs.aws.amazon.com/IAM/latest/UserGuide/access-keys-admin-managed.html).


### Configuring the Integration Credentials

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

| Credential Parameter | Description |
|  --- | --- |
| Access Key ID | The Access Key ID from your IAM user's access key pair |
| Secret Access Key | The Secret Access Key from your IAM user's access key pair |
| Session Token | **OPTIONAL:** A temporary session token. Only required if you are using temporary credentials from AWS STS. |


## Configure the Integration

Create your integration by supplying the following configuration values.

| Integration Parameter | Description |
|  --- | --- |
| Credential | AWS credentials with access to the SQS queue(s) (static or role-based, per the tabs above). |
| Queues | Map 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 key | Operation | Description |
|  --- | --- | --- |
| `query_compliance_findings` | Compliance findings | Security Hub compliance/control findings. **Supported today.** |
| `query_threats` | Threats | Security Hub threat/detection findings and/or GuardDuty findings. **Supported today.** |
| `query_cloud_resource_inventory` | Cloud resource inventory | Reserved for a future inventory pipeline. |
| `query_events` | Events | Reserved for a future events pipeline. |


### Configuration examples

**Compliance only:**


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

**Compliance + threats:**


```json
{
  "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](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/quotas-messages.html): 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.