Skip to content

📤 SNS Topic Integration

This guide explains how to configure Amazon SNS (Simple Notification Service) as a notification channel for unusd.cloud scan findings. This feature enables powerful automation workflows based on scan events.

Quick Reference - unusd.cloud Access Details

Property Value
AWS Account ID 398997493752
Region eu-west-1 (Ireland)
Scanner Role ARN arn:aws:iam::398997493752:role/unusd-cloud-saas-scanner-prod-ScannerRole-*
Account Root ARN arn:aws:iam::398997493752:root

Use these values when configuring your SNS topic policy and KMS key policy.

Overview

The SNS integration allows you to receive raw scan findings as structured JSON payloads to your SNS topic. This enables you to:

  • Automate remediation - Trigger Lambda functions to automatically clean up unused resources
  • Notify asset owners - Route findings to resource owners via email, SMS, or other channels
  • Integrate with ITSM - Create tickets in ServiceNow, Jira, or PagerDuty
  • Build dashboards - Stream data to analytics platforms for custom reporting
  • Archive findings - Store scan results in S3 for compliance and auditing

Data Model

Each scan triggers an SNS message with a well-structured JSON payload following AWS best practices for event-driven architectures:

{
  "version": "1.0",
  "source": "unusd.cloud",
  "eventType": "scan.completed",
  "eventTime": "2024-01-15T08:30:00Z",
  "accountId": "123456789012",
  "accountAlias": "production",
  "region": "eu-west-1",
  "scanId": "scan-a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "summary": {
    "totalFindings": 45,
    "totalPotentialSavings": 1250.5,
    "currency": "USD",
    "scanDuration": 180,
    "servicesScanned": 15
  },
  "findings": [
    {
      "id": "finding-001",
      "service": "EC2",
      "resourceType": "Instance",
      "resourceId": "i-0abc123def456789",
      "resourceArn": "arn:aws:ec2:eu-west-1:123456789012:instance/i-0abc123def456789",
      "region": "eu-west-1",
      "finding": "Stopped instance for more than 7 days",
      "severity": "medium",
      "potentialSavings": 150.0,
      "currency": "USD",
      "recommendation": "Consider terminating or creating an AMI backup",
      "tags": {
        "Name": "legacy-webserver",
        "Project": "migration",
        "Owner": "platform-team@company.com"
      },
      "metadata": {
        "instanceType": "m5.xlarge",
        "launchTime": "2023-06-15T10:00:00Z",
        "stoppedSince": "2024-01-01T00:00:00Z"
      }
    }
  ],
  "metadata": {
    "unusdVersion": "2.5.0",
    "templateVersion": "1.4.0",
    "customTags": ["Project", "Owner"],
    "exceptionTag": "unusd"
  }
}

Payload Fields Reference

Field Type Description
version string Schema version for forward compatibility
source string Always unusd.cloud
eventType string Event type: scan.completed, scan.failed
eventTime string ISO 8601 timestamp of the event
accountId string AWS Account ID that was scanned
accountAlias string Friendly name configured in unusd.cloud
scanId string Unique identifier for this scan
summary object Aggregated scan statistics
findings array List of individual findings
metadata object Additional context about the scan

Finding Severity Levels

Severity Description Typical Savings
critical Immediate attention required > $1000/month
high Significant cost impact $500-1000/month
medium Moderate savings opportunity $100-500/month
low Minor optimization < $100/month
info Informational, no immediate action N/A

Setup Instructions

Step 1: Create an SNS Topic

If you don't have an existing topic, create one in your AWS account:

aws sns create-topic --name unusd-findings --region eu-west-1

Step 2: Configure Topic Policy

You must update your SNS topic policy to allow unusd.cloud to publish messages.

unusd.cloud Production Account: 398997493752

Scanner Lambda Role ARN: arn:aws:iam::398997493752:role/unusd-cloud-saas-scanner-prod-*

Add the following statement to your SNS topic's access policy:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowUnusdCloudPublish",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::398997493752:root"
      },
      "Action": "sns:Publish",
      "Resource": "arn:aws:sns:YOUR_REGION:YOUR_ACCOUNT_ID:YOUR_TOPIC_NAME"
    }
  ]
}

More Restrictive Policy: For a more restrictive policy, you can specify the exact Lambda execution role instead of the account root:

{
  "Sid": "AllowUnusdCloudPublish",
  "Effect": "Allow",
  "Principal": {
    "AWS": "arn:aws:iam::398997493752:role/unusd-cloud-saas-scanner-prod-ScannerRole-*"
  },
  "Action": "sns:Publish",
  "Resource": "arn:aws:sns:YOUR_REGION:YOUR_ACCOUNT_ID:YOUR_TOPIC_NAME"
}

Note: The role suffix may change during stack updates. Using the account root is more stable.

Replace:

  • YOUR_REGION with your topic's region (e.g., eu-west-1)
  • YOUR_ACCOUNT_ID with your AWS account ID
  • YOUR_TOPIC_NAME with your SNS topic name

Step 3: Configure in unusd.cloud

  1. Navigate to AWS Accounts in your unusd.cloud dashboard
  2. Click on an account to edit or add a new one
  3. In the Notifications step, enable Amazon SNS
  4. Enter your SNS Topic ARN (e.g., arn:aws:sns:eu-west-1:123456789012:unusd-findings)
  5. Save your configuration

Cross-Region Support

Cross-region publishing is fully supported. unusd.cloud operates from eu-west-1 (Ireland), but you can configure an SNS topic in any AWS region. This is useful if you want to keep your automation infrastructure close to your workloads.

Simply use the full ARN with your desired region:

arn:aws:sns:us-east-1:123456789012:my-topic
arn:aws:sns:ap-southeast-1:123456789012:my-topic
arn:aws:sns:eu-central-1:123456789012:my-topic

Latency Consideration: Cross-region publishing adds minimal latency (typically < 100ms). For most automation use cases, this is negligible.

Encryption Considerations

If your SNS topic uses server-side encryption (SSE), additional configuration is required depending on the key type.

Using AWS Managed Key (aws/sns)

⚠️ Not supported.

The AWS managed key (alias/aws/sns) cannot be shared with external accounts. You must use a Customer Managed Key (CMK) instead.

Using Customer Managed Key (CMK)

When using a CMK for SNS encryption, you must grant unusd.cloud permissions to use the key for encryption.

unusd.cloud Production Account: 398997493752

Add the following statement to your KMS key policy:

{
  "Sid": "AllowUnusdCloudEncrypt",
  "Effect": "Allow",
  "Principal": {
    "AWS": "arn:aws:iam::398997493752:root"
  },
  "Action": ["kms:GenerateDataKey*", "kms:Decrypt"],
  "Resource": "*",
  "Condition": {
    "StringEquals": {
      "kms:ViaService": "sns.YOUR_REGION.amazonaws.com"
    }
  }
}

Replace YOUR_REGION with the region where your SNS topic is located (e.g., eu-west-1, us-east-1).

More Restrictive KMS Policy: For tighter security, specify the Lambda execution role:

{
  "Sid": "AllowUnusdCloudEncrypt",
  "Effect": "Allow",
  "Principal": {
    "AWS": "arn:aws:iam::398997493752:role/unusd-cloud-saas-scanner-prod-ScannerRole-*"
  },
  "Action": ["kms:GenerateDataKey*", "kms:Decrypt"],
  "Resource": "*",
  "Condition": {
    "StringEquals": {
      "kms:ViaService": "sns.YOUR_REGION.amazonaws.com"
    }
  }
}

⚠️ Security Note: Always use the Condition block to restrict key usage to SNS service only. This follows the principle of least privilege.

For better security isolation, we recommend creating a dedicated KMS key for unusd.cloud notifications:

aws kms create-key \
  --description "CMK for unusd.cloud SNS notifications" \
  --region eu-west-1

Then update your SNS topic to use this key:

aws sns set-topic-attributes \
  --topic-arn arn:aws:sns:eu-west-1:123456789012:unusd-findings \
  --attribute-name KmsMasterKeyId \
  --attribute-value arn:aws:kms:eu-west-1:123456789012:key/your-key-id

Example Use Cases

1. Auto-Remediation with Lambda

Create a Lambda function that automatically handles findings:

import json
import boto3

def lambda_handler(event, context):
    message = json.loads(event['Records'][0]['Sns']['Message'])

    for finding in message['findings']:
        if finding['service'] == 'EBS' and finding['resourceType'] == 'Snapshot':
            # Auto-delete old snapshots
            if finding['severity'] in ['low', 'medium']:
                delete_snapshot(finding['resourceId'], finding['region'])

    return {'statusCode': 200}

def delete_snapshot(snapshot_id, region):
    ec2 = boto3.client('ec2', region_name=region)
    ec2.delete_snapshot(SnapshotId=snapshot_id)

2. S3 Archival

Store all findings in S3 for compliance:

import json
import boto3
from datetime import datetime

def lambda_handler(event, context):
    message = json.loads(event['Records'][0]['Sns']['Message'])

    s3 = boto3.client('s3')
    key = f"unusd-findings/{message['accountId']}/{datetime.now().strftime('%Y/%m/%d')}/{message['scanId']}.json"

    s3.put_object(
        Bucket='my-compliance-bucket',
        Key=key,
        Body=json.dumps(message, indent=2),
        ContentType='application/json'
    )

3. EventBridge Integration

Route SNS messages to EventBridge for advanced routing:

  1. Create an EventBridge rule that matches unusd.cloud events
  2. Configure multiple targets (Lambda, Step Functions, SQS, etc.)
  3. Use content-based filtering for specific finding types

Infrastructure as Code Examples

Terraform

Complete Terraform example to create an SNS topic with the correct policy for unusd.cloud:

# SNS Topic for unusd.cloud findings
resource "aws_sns_topic" "unusd_findings" {
  name = "unusd-cloud-findings"
}

# SNS Topic Policy allowing unusd.cloud to publish
resource "aws_sns_topic_policy" "unusd_findings" {
  arn = aws_sns_topic.unusd_findings.arn

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid       = "AllowUnusdCloudPublish"
        Effect    = "Allow"
        Principal = {
          AWS = "arn:aws:iam::398997493752:root"
        }
        Action   = "sns:Publish"
        Resource = aws_sns_topic.unusd_findings.arn
      }
    ]
  })
}

# Optional: Create a KMS key for SNS encryption
resource "aws_kms_key" "unusd_sns" {
  description             = "KMS key for unusd.cloud SNS notifications"
  deletion_window_in_days = 7
  enable_key_rotation     = true

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid       = "EnableRootPermissions"
        Effect    = "Allow"
        Principal = {
          AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"
        }
        Action   = "kms:*"
        Resource = "*"
      },
      {
        Sid       = "AllowUnusdCloudEncrypt"
        Effect    = "Allow"
        Principal = {
          AWS = "arn:aws:iam::398997493752:root"
        }
        Action = [
          "kms:GenerateDataKey*",
          "kms:Decrypt"
        ]
        Resource = "*"
        Condition = {
          StringEquals = {
            "kms:ViaService" = "sns.${data.aws_region.current.name}.amazonaws.com"
          }
        }
      }
    ]
  })
}

resource "aws_kms_alias" "unusd_sns" {
  name          = "alias/unusd-sns"
  target_key_id = aws_kms_key.unusd_sns.key_id
}

# Apply KMS encryption to SNS topic
resource "aws_sns_topic" "unusd_findings_encrypted" {
  name              = "unusd-cloud-findings-encrypted"
  kms_master_key_id = aws_kms_key.unusd_sns.id
}

data "aws_caller_identity" "current" {}
data "aws_region" "current" {}

output "sns_topic_arn" {
  description = "SNS Topic ARN to configure in unusd.cloud"
  value       = aws_sns_topic.unusd_findings.arn
}

CloudFormation

Complete CloudFormation template:

AWSTemplateFormatVersion: "2010-09-09"
Description: SNS Topic for unusd.cloud scan findings

Parameters:
  EnableEncryption:
    Type: String
    Default: "false"
    AllowedValues: ["true", "false"]
    Description: Enable KMS encryption for the SNS topic

Conditions:
  UseEncryption: !Equals [!Ref EnableEncryption, "true"]

Resources:
  # SNS Topic
  UnusdFindingsTopic:
    Type: AWS::SNS::Topic
    Properties:
      TopicName: unusd-cloud-findings
      KmsMasterKeyId: !If [UseEncryption, !Ref UnusdSnsKey, !Ref "AWS::NoValue"]

  # SNS Topic Policy
  UnusdFindingsTopicPolicy:
    Type: AWS::SNS::TopicPolicy
    Properties:
      Topics:
        - !Ref UnusdFindingsTopic
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Sid: AllowUnusdCloudPublish
            Effect: Allow
            Principal:
              AWS: arn:aws:iam::398997493752:root
            Action: sns:Publish
            Resource: !Ref UnusdFindingsTopic

  # KMS Key (conditional)
  UnusdSnsKey:
    Type: AWS::KMS::Key
    Condition: UseEncryption
    Properties:
      Description: KMS key for unusd.cloud SNS notifications
      EnableKeyRotation: true
      KeyPolicy:
        Version: "2012-10-17"
        Statement:
          - Sid: EnableRootPermissions
            Effect: Allow
            Principal:
              AWS: !Sub arn:aws:iam::${AWS::AccountId}:root
            Action: kms:*
            Resource: "*"
          - Sid: AllowUnusdCloudEncrypt
            Effect: Allow
            Principal:
              AWS: arn:aws:iam::398997493752:root
            Action:
              - kms:GenerateDataKey*
              - kms:Decrypt
            Resource: "*"
            Condition:
              StringEquals:
                kms:ViaService: !Sub sns.${AWS::Region}.amazonaws.com

  UnusdSnsKeyAlias:
    Type: AWS::KMS::Alias
    Condition: UseEncryption
    Properties:
      AliasName: alias/unusd-sns
      TargetKeyId: !Ref UnusdSnsKey

Outputs:
  TopicArn:
    Description: SNS Topic ARN to configure in unusd.cloud
    Value: !Ref UnusdFindingsTopic
    Export:
      Name: UnusdFindingsTopicArn

Deploy with encryption:

aws cloudformation create-stack \
  --stack-name unusd-sns-integration \
  --template-body file://unusd-sns.yaml \
  --parameters ParameterKey=EnableEncryption,ParameterValue=true

Troubleshooting

Message Not Received

  1. Check topic policy - Verify the principal 398997493752 (unusd.cloud account) has sns:Publish permission
  2. Check KMS policy - If using CMK, verify encryption permissions for account 398997493752
  3. Verify ARN - Ensure the topic ARN is correctly formatted
  4. Check CloudWatch Logs - Look for delivery failures in SNS metrics

Invalid ARN Error

Ensure your ARN follows the correct format:

arn:aws:sns:<region>:<account-id>:<topic-name>

Example: arn:aws:sns:eu-west-1:123456789012:unusd-findings

Encryption Errors

If you see KMS.AccessDeniedException:

  1. Verify the KMS key policy includes the unusd.cloud account
  2. Ensure kms:GenerateDataKey* and kms:Decrypt are granted
  3. Check the kms:ViaService condition matches your region

Benefits Summary

Benefit Description
Real-time Automation React to findings immediately after each scan
Flexible Routing Send to Lambda, SQS, HTTP endpoints, or other services
Structured Data Well-defined JSON schema for easy parsing
Cross-Region Deploy subscribers in any AWS region
Secure Supports encrypted topics with CMK
Audit Trail Maintain complete history of all findings

For additional help, contact support@unusd.cloud or visit our documentation.