AWS EventBridge to SNS Topic - amazon-web-services

I'm working on how to use the default AWS EventBridge Event Bus, then use a resource based policy which will use Cloudtrail to pull an API Call. EventBridge then invokes a SNS topic from another account.
I have the CF template and i was wondering whether anyone had any pointers or similar experience?
Resources:
#Selects the default EventBridge Event Bus
Type: AWS::Events::EventBus
Properties:
#EventSourceName: String
Name: "default" #Name of the Event Bus
#The EventBridge Event Resource Based Policy
EventBridgeResourcedBasedPolicy:
Type: AWS::Events::EventBusPolicy #EventBridge Event bus resource based policy
Properties:
StatementId: "Assume-API-Call-TEST" #Name of the Policy ID for clarity
Statement:
Effect: "Allow" #Allow access to the following below
Principal: "*" #Allow all events
Action: "events.PutEvents" #Required to add custom events that can be matched to rules.
Resource: "arn:aws:events:eu-west-2:XXXXXXX:event-bus/default" #ARN of the eventbus which this policy will attach to
Condition:
StringEquals:
"aws:PrincipalOrgID": "o-XXXXX" #Allows the entire organisation access to the eventbridge event bus
Type: AWS::Events::Rule
Properties:
Description: EventRule-API-ASSUME-RULE-TEST #The event bus rule which will be used to watch for events from a single event bus, in this instance would be in the "default" event bus
EventBusName: "Assume-API-Call-TEST" #Eventbridge event bus name
EventPattern: #Defining the EventPattern
Source: aws.cloudtrail #Using CloudTrail to log SSO API Call
Detail-Type: AWS API Call via CloudTrail #Event type is captured within Cloudtrail
Detail:
EventSource: cloudtrail.amazonaws.com #Use cloudtrail service to capture the AssumeRoleWithSaml attribute so we can filter out.
EventName: AssumeRoleWithSAML #Using the Assume Role for testing, in prod needs to be changed to sso-directory:CreateUser
Account:
!Sub '${AWS::AccountId}' #Using the account ID of the account to which this CF is deployed, testing will be the InfrastructureStaging account
State: ENABLED #Whether the rule is ENABLED or DISABLED
Targets:
#Action: 'sns:Publish' #attribute action to publish to SNS
Arn: !Ref arn:aws:sns:eu-west-2:XXXXX:CentralTestAlerting #ARN of the SNS topic within the Support account
Id: "CentralTestAlerting" #Name of the Event Bus
Sid: "Dead-letter queue permissions"
Effect: "Allow"
Principal:
Service: "events.amazonaws.com"
Action: "sqs:SendMessage"
Resource: "arn:aws:sqs:us-west-2:XXXXX:MyEventDLQ"
Condition:
ArnEquals:
aws:SourceArn: "arn:aws:events:us-west-2:XXXXX:rule/MyTestRule"

Related

Catch event when an SSM-agent becomes active

I want to trigger a lambda whenever a new EC2 instance is registred in SSM's Fleet Manager (meaning the instance can be connected to using SSM), however I can't find what pattern to use in EventBridge.
Within EventBridge, I tried using the following pattern I found in the docs (so far its looks like the closest thing to my goal):
{
"source": ["aws.ssm"],
"detail-type": ["Inventory Resource State Change"]
}
However when I create a new EC2 and wait for its SSM agent to become active, it still doesn't trigger the above pattern.
Any idea how to catch this kind of event?
I think you have to go through CloudTrail API call.
Please find below a CloudFormation template I used in the past that was working. Please note that it just provides the SSM resources. You need to add your own SQS queue as well (see SQS.ARN) and I've used the association with the tag registration set to enabled. So that if you have a lambda function connected, you can set it to false so if the instance connect again, it won't go to the same process again.
AWSTemplateFormatVersion: "2010-09-09"
Description: >
SSM Registration event
# Description of the resources to be created.
Resources:
RegistrationDocument:
Type: AWS::SSM::Document
Properties:
DocumentType: Command
Content:
schemaVersion: "2.2"
description: >
An Automation Document ran by registered instances that gathers their software inventory
and automatically updates their AWS SSM Agent to the latest version.
mainSteps:
- name: GatherSoftware
action: aws:softwareInventory
- name: Sleep
action: aws:runShellScript
inputs:
runCommand:
- sleep 20 || true
- name: UpdateAgent
action: aws:updateSsmAgent
inputs:
agentName: amazon-ssm-agent
source: https://s3.{Region}.amazonaws.com/amazon-ssm-{Region}/ssm-agent-manifest.json
allowDowngrade: "false"
RegistrationDocumentAssociation:
Type: AWS::SSM::Association
Properties:
AssociationName: !Sub registration-association-${AWS::StackName}
Name: !Ref RegistrationDocument
Targets:
- Key: tag:registration
Values:
- enabled
RegistrationEventRule:
Type: AWS::Events::Rule
Properties:
Description: >
Events Rule that monitors registration of AWS SSM instances
and logs them to an SQS queue.
EventPattern:
source:
- aws.ssm
detail-type:
- AWS API Call via CloudTrail
detail:
eventName:
- UpdateInstanceAssociationStatus
requestParameters:
associationId:
- !Ref RegistrationDocumentAssociation
executionResult:
status:
- Success
State: ENABLED
Targets:
- Arn: SQS.ARN
Id: SqsRegistrationSubscription
SqsParameters:
MessageGroupId: registration.events

Aws Lambda is not authorized to perform: SNS:Publish on resource: +358

I have made Cognito PostConfirmation lambda function. When user will successfully signup then I want to send them SMS. For that I am using AWS-SNS. I have created one Sns Topic and attached to my PostConfirmation lambda function. I gave permission to the lambda for Sns publishing. In cloudwatch it says, That lambda does not have authorize to perform this Sns publishing.
I am getting this error in cloudwatch:
PostConfirmation is not authorized to perform: SNS:Publish on
resource: +358.... because no identity-based policy allows the
SNS:Publish action
I am not sure what I am missing.
This is my YAML file:
plugins:
- serverless-webpack
- serverless-offline
- serverless-plugin-warmup
- serverless-iam-roles-per-function
## post Confirmation
PostConfirmation:
handler: src/handlers/postConfirmation.postConfirmation
events:
- cognitoUserPool:
pool: ${self:provider.environment.COGNITO}
trigger: PostConfirmation
existing: true
iamRoleStatements:
- Effect: Allow
Action:
- cognito-idp:*
Resource: arn:aws:cognito-idp:*:*:*
- Effect: Allow
Action:
- dynamodb:PutItem
- lambda:InvokeFunction # Added this like mentioned above
Resource: 'arn:aws:dynamodb:${opt:region, self:provider.region}:*:table/${self:provider.environment.ITEM_TABLE}'
- Effect: Allow
Action:
- sns:Publish ## This is where I am giving my permisson
- sns:SetSMSAttributes
Resource: !Ref SendMessageSns ## Sns Topic
resources:
Resources:
SendMessageSns:
Type: AWS::SNS::Topic
Properties:
DisplayName: It will send sms when user successfully signUp
TopicName: ${self:service}-${opt:stage, self:provider.stage}-successful
This is how I am trying to publishing the message
import { SNS } from '#aws-sdk/client-sns';
const snsClient = new SNS({ region: 'eu-north-1' });
exports.postConfirmation = async (event: any, context: any) => {
const messageParams = {
Message:
'congrats it works',
PhoneNumber: '+358.......',
};
try {
console.log('1');
const snsSucess = await snsClient.publish(messageParams);
console.log('Success.', snsSucess);
console.log('2');
context.done(null, event);
} catch (error) {
console.log('error', { error });
return {
statusCode: 500,
body: JSON.stringify(error),
};
}
};
You allowed publish to the SNS topic, but are trying to send the SMS directly to the phone number. When publishing to the SNS topic you need to publish message to the topic and subscribe the phone number to the topic, see https://docs.aws.amazon.com/sns/latest/dg/sms_publish-to-topic.html
Alternatively you can publish directly to phone number, but you'd need to modify the IAM policy and also possibly move outside of SMS sandbox - https://docs.aws.amazon.com/sns/latest/dg/sms_publish-to-phone.html

Trigger the lambda with dynamodb when a specific entry is registered

I need to trigger a lambda when a specific object registers on DynamoDB.
For example:
If I create a User with a POST /my-website/user and, I store this User on DynamoDB, I want to trigger
my Lambda.
I don't want to trigger the Lambda if the registered object is
different from the User.
For the management of my stack, I use Serverless (with a serverless.yml file) and CloudFormation syntax.
With the serverless documentation, I can't figure out how I can trigger my Lambda only when a specific entry is registered to DynamoDB ( https://www.serverless.com/framework/docs/providers/aws/events/streams ).
Thanks in advance,
EDIT:
Thank you for your answers :)
It's work:
statement:
handler: lambda/statement.php
layers:
- arn:aws:lambda:#{AWS::Region}:<account_id>:layer:php-73:1
iamRoleStatements:
- Effect: Allow
Action:
- dynamodb:ListStreams
- dynamodb:GetItem
events:
- stream:
type: dynamodb
arn: arn:aws:dynamodb:eu-west-3:<account_id>:table/dev-project/stream/2020-11-18T22:34:01.579
maximumRetryAttempts: 1
batchSize: 1
filterPatterns:
- eventName: [INSERT]
dynamodb:
NewImage:
__partitionKey:
S: [myPk]
You have to setup stream filters. The process is explained in:
NEW: DynamoDB Streams Filtering in Serverless Framework
You attach the DynamoDB stream event onto the lambda in your serverless file in the function's "events" sections. https://carova.io/snippets/serverless-aws-dynamodb-stream-to-lamba-function
If you're creating the Dynamo table using serverless, you could output the table's StreamArn as a stack variable.
https://carova.io/snippets/serverless-aws-cloudformation-output-stack-variables
If they're in the same file, you don't need the Output section, you could just set the "events" sections of the lambda and reference
arn: { Fn::GetAtt: [DynamoTable, StreamArn] }
in the arn section of the specific event.
Stream Filters worked (thank you). Here's the final configuration:
statement:
handler: lambda/statement.php
layers:
- arn:aws:lambda:#{AWS::Region}:<account_id>:layer:php-73:1
iamRoleStatements:
- Effect: Allow
Action:
- dynamodb:ListStreams
- dynamodb:GetItem
events:
- stream:
type: dynamodb
arn: arn:aws:dynamodb:eu-west-3:<account_id>:table/dev-project/stream/2020-11-18T22:34:01.579
maximumRetryAttempts: 1
batchSize: 1
filterPatterns:
- eventName: [INSERT]
dynamodb:
NewImage:
__partitionKey:
S: [myPk]

Access Event ID in a AWS RDS Event subscription

As described in the documentation, when creating a RDS Event subscription you can select any number Event categories that will produce specific Event messages.
Then you can choose to send a notification to an E-mail, SMS or, which is my case, to a SNS topic that triggers a Lambda execution.
How to access the RDS Event ID - i.e. RDS-EVENT-0006 - from the Lambda event parameter?
Add a trigger event like this cloudformation example :
DbRestoredEventRule:
Type: AWS::Events::Rule
Properties:
Name: "xyz-db-restored"
Description: "xyz restored"
EventPattern:
source:
- "aws.rds"
detail-type:
- "RDS DB Instance Event"
detail:
EventCategories:
- "availability"
Message:
- 'DB instance restarted'
Targets:
- Arn:
Fn::GetAtt:
- "MigrationDataFunction"
- "Arn"
Id: "TargetFunctionV1"

How to enable cloudwatch logs and assign custom domain name in cloudformation

I have a cloudformation template to build my api using the API Gateway.
I don't know how to:
Enable cloudwatch logs for the stage in the cloudformation template
Assign the stage to a Custom Domain Name in the cloudformation template.
Is either of these possible in a json cloudformation template?
Cloudwatch logs:
Yes you can enable cloudwatch logs in cloudformation:
Configure CloudTrail log file delivery to CloudWatch Logs.
Create a AWS CloudFormation stack by using the template.
the cloudwatch entry should be something simalar to this:
"SecurityGroupChangesAlarm": {
"Type": "AWS::CloudWatch::Alarm",
"Properties": {
"AlarmName" : "CloudTrailSecurityGroupChanges",
"AlarmDescription" : "Alarms when an API call is made to create, update or delete a Security Group.",
"AlarmActions" : [{ "Ref" : "AlarmNotificationTopic" }],
"MetricName" : "SecurityGroupEventCount",
"Namespace" : "CloudTrailMetrics",
"ComparisonOperator" : "GreaterThanOrEqualToThreshold",
"EvaluationPeriods" : "1",
"Period" : "300",
"Statistic" : "Sum",
"Threshold" : "1"
}
},
Check the aws official doc everything is detailed there.
Custom Domain Name:
the custom domain name is not defined in the cloudformation template. It should be created separately as specified in aws doc:
Sign in to the API Gateway console at https://console.aws.amazon.com/apigateway.
Choose Custom Domain Names from the main navigation pane.
Choose Create in the secondary navigation pane.
In Create Custom Domain Name
setup DNS using Amazon Route 53
Update Jul 5 2017: The AWS::ApiGateway::DomainName resource is now available, so a Custom Resource is no longer needed for this part.
Original post Dec 24 2016:
Enable cloudwatch logs for the stage in the cloudformation template
To enable CloudWatch logs for an ApiGateway Stage using CloudFormation for every method call to your API, you need to set the DataTraceEnabled property to true for all methods in your AWS::ApiGateway::Stage resource.
As noted in the Set Up a Stage section of the documentation, you will also need to associate your API Gateway account with the proper IAM permissions to push data to CloudWatch Logs. For this purpose, you will also need to create an AWS::ApiGateway::Account resource that references an IAM role containing the AmazonAPIGatewayPushToCloudWatchLogs managed policy, as described in the documentation example:
CloudWatchRole:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- "apigateway.amazonaws.com"
Action: "sts:AssumeRole"
Path: "/"
ManagedPolicyArns:
- "arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs"
Account:
Type: "AWS::ApiGateway::Account"
Properties:
CloudWatchRoleArn:
"Fn::GetAtt":
- CloudWatchRole
- Arn
Assign the stage to a Custom Domain Name in the cloudformation template
Unfortunately, CloudFormation does not provide an official resource corresponding to the DomainName APIGateway REST API. Fortunately, Carl Nordenfelt's unofficial API Gateway for CloudFormation project does provide Custom::ApiDomainName. Here's the example provided in the documentation:
TestApiDomainName:
Type: Custom::ApiDomainName
Properties:
ServiceToken: {Lambda_Function_ARN}
domainName: example.com
certificateName: testCertificate
certificateBody": "-----BEGIN CERTIFICATE-----line1 line2 ... -----END CERTIFICATE-----"
certificateChain: "-----BEGIN CERTIFICATE-----line1 line2 ... -----END CERTIFICATE-----"
certificatePrivateKey: "-----BEGIN RSA PRIVATE KEY-----line1 line2 ... -----END RSA PRIVATE KEY-----"
Also note that once the domain name has been created, you should create a Route53 alias record that points to !GetAtt TestApiDomainName.distributionDomainName and the static CloudFront hosted zone ID (Z2FDTNDATAQYW2), for example:
myDNSRecord:
Type: AWS::Route53::RecordSet
Properties:
HostedZoneName:
!Ref HostedZone
Name:
!Ref DomainName
Type: A
AliasTarget:
DNSName: !GetAtt TestApiDomainName.distributionDomainName
HostedZoneId: Z2FDTNDATAQYW2