📤 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:
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_REGIONwith your topic's region (e.g.,eu-west-1)YOUR_ACCOUNT_IDwith your AWS account IDYOUR_TOPIC_NAMEwith your SNS topic name
Step 3: Configure in unusd.cloud
- Navigate to AWS Accounts in your unusd.cloud dashboard
- Click on an account to edit or add a new one
- In the Notifications step, enable Amazon SNS
- Enter your SNS Topic ARN (e.g.,
arn:aws:sns:eu-west-1:123456789012:unusd-findings) - 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
Conditionblock to restrict key usage to SNS service only. This follows the principle of least privilege.
Recommended: Create a Dedicated CMK
For better security isolation, we recommend creating a dedicated KMS key for unusd.cloud notifications:
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:
- Create an EventBridge rule that matches unusd.cloud events
- Configure multiple targets (Lambda, Step Functions, SQS, etc.)
- 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
- Check topic policy - Verify the principal
398997493752(unusd.cloud account) hassns:Publishpermission - Check KMS policy - If using CMK, verify encryption permissions for account
398997493752 - Verify ARN - Ensure the topic ARN is correctly formatted
- Check CloudWatch Logs - Look for delivery failures in SNS metrics
Invalid ARN Error
Ensure your ARN follows the correct format:
Example: arn:aws:sns:eu-west-1:123456789012:unusd-findings
Encryption Errors
If you see KMS.AccessDeniedException:
- Verify the KMS key policy includes the unusd.cloud account
- Ensure
kms:GenerateDataKey*andkms:Decryptare granted - Check the
kms:ViaServicecondition 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.