Do AWS support SES in CloudFormation? - amazon-web-services

I'm trying to figure out how to automate the creation of several cloud resources in AWS, using CloudFormation.
Now I need to include the creation of SES (Simple Email Service) domain, but couldn't find the documentation, but I've already checked:
Simple Email Service Documentation
CloudFormation Resource Types Documentation
Do AWS support SES in CloudFormation?

CloudFormation provides several built-in Amazon SES resource types, but as of 2018 2020 2022 is still missing the ones many people need: domain and email verification.
Fortunately, CloudFormation has the ability to define your own custom resource types. I've built Custom::SES_Domain and Custom::SES_EmailIdentity resources that are designed to play well with other CloudFormation resources. Get them here: https://github.com/medmunds/aws-cfn-ses-domain.
Once you've pulled the custom CfnSESResources into your template, you can verify an SES domain like this:
Resources:
# Provision a domain with Amazon SES:
MySESDomain:
Type: Custom::SES_Domain
Properties:
ServiceToken: !GetAtt CfnSESResources.Outputs.CustomDomainIdentityArn
Domain: "example.com"
EnableSend: true
EnableReceive: false
# Then add all required DNS records for SES verification and usage:
MyRoute53RecordsForSES:
Type: AWS::Route53::RecordSetGroup
Properties:
HostedZoneName: "example.com."
RecordSets: !GetAtt MySESDomain.Route53RecordSets
Full instructions are in the repository. Custom::SES_Domain has properties for controlling several common SES domain options, and exposes attributes that feed into your CloudFormation DNS resources: either a standard AWS::Route53::RecordSetGroup resource as shown above, or other (external) DNS providers via zone file entries.

Unfortunately this is currently not supported, but who knows Re:Invent 2017 is around the corner ,,,
Question asked on AWS Developer Forum
It is possible by creating a custom function, some blog about SES and cloudformation.

Though AWS Cloudformation is not currently supported use the AWS SDKs ( e.g Node SDK) to provision the SES resources required.
Its a common practice to use custom code with AWS SDKs and AWS CLI commands in combination with CloudFormation to provision resources AWS since each approach can be advantages, based on the parameters, number of resources, repetitions and etc.

Here is the current list of SES Resource Types supported by CloudFormation:
AWS::SES::ConfigurationSet
AWS::SES::ConfigurationSetEventDestination
AWS::SES::ReceiptFilter
AWS::SES::ReceiptRule
AWS::SES::ReceiptRuleSet
AWS::SES::Template

Update October 2022
CloudFormation now supports the AWS::SES::EmailIdentity resource, which allows us to define both domains and email addresses through infrastructure as code.
According to the CloudFormation release history this resource was added on June 30, 2022.

CloudFormation provides a nativ AWS::SES::EmailIdentity resource now. (since 30.07.2022)
Here is an example with automated Route53 DEKIM setup/verification:
EmailIdentity:
Type: AWS::SES::EmailIdentity
Properties:
EmailIdentity: {your.domain.com}
Route53DEKIM:
Type: AWS::Route53::RecordSetGroup
Properties:
HostedZoneId: {ZoneId}
RecordSets:
- Name: !GetAtt EmailIdentity.DkimDNSTokenName1
Type: CNAME
TTL: '3600'
ResourceRecords:
- !GetAtt EmailIdentity.DkimDNSTokenValue1
- Name: !GetAtt EmailIdentity.DkimDNSTokenName2
Type: CNAME
TTL: '3600'
ResourceRecords:
- !GetAtt EmailIdentity.DkimDNSTokenValue2
- Name: !GetAtt EmailIdentity.DkimDNSTokenName3
Type: CNAME
TTL: '3600'
ResourceRecords:
- !GetAtt EmailIdentity.DkimDNSTokenValue3
{your.domain.com} and {ZoneId} must be adapted.

Not supported. But, you can make it handled by lambda.
AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: >-
A simple email example
Resources:
FunctionEmailHandler:
Type: 'AWS::Serverless::Function'
Properties:
Handler: email.handler
Runtime: nodejs6.10
CodeUri: ..
Description: >-
...
Tags:
App: your app
MemorySize: 128
Timeout: 10
Policies:
- Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- 's3:GetObject'
Resource: '*'
LambdaInvokePermission:
Type: "AWS::Lambda::Permission"
Properties:
Action: 'lambda:InvokeFunction'
FunctionName: !GetAtt FunctionEmailHandler.Arn
Principal: ses.amazonaws.com
SESEmailRecievedRule:
Type: "AWS::SES::ReceiptRule"
Properties:
RuleSetName: your default rule set name
After: store-email-to-s3
Rule:
Name: email-recieved-rule
Enabled: true
Actions:
- LambdaAction:
FunctionArn: !GetAtt FunctionEmailHandler.Arn
InvocationType: Event

Related

Creating a AWS WorkMail Lambda invoke permission in CloudFormation

When I create a Lambda::Permission resource in Cloudformation I have an issue with validating the principal. Currently I have it set to use the !Sub function and that's not a valid principal according to CloudFormation. Does any one have any experience with creating a workmail invoke permission?
AWSTemplateFormatVersion: 2010-09-09
Resources:
WorkmailInvokePermission:
Type: 'AWS::Lambda::Permission'
Properties:
Action: 'lambda:InvokeFunction'
FunctionName: arn:aws:<region>:<function_arn>
Principal: !Sub workmail.${AWS::Region}.amazonaws.com
Outputs: {}
I figured it out, it was a pretty simple, the problem was I was in a region where Workmail wasn't actually available so it ended up creating an error.

How to add a resource based policy to a lambda using AWS SAM

I want to create a deployment script for some lambda functions using AWS SAM. Two of those functions will be deployed into one account(account A) but will be triggered by an s3 bucket object creation event in a second account(account B). From what I know the only way to do this is by using adding a resource based policy to my lambda. But I don't know how to do that in AWS SAM. My current yaml file looks like this.
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
deploy-test-s3-triggered-lambda
Parameters:
AppBucketName:
Type: String
Description: "REQUIRED: Unique S3 bucket name to use for the app."
Resources:
S3TriggeredLambda:
Type: AWS::Serverless::Function
Properties:
Role: arn:aws:iam::************:role/lambda-s3-role
Handler: src/handlers/s3-triggered-lambda.invokeAPI
CodeUri: src/handlers/s3-triggered-lambda.js.zip
Runtime: nodejs10.x
MemorySize: 128
Timeout: 60
Policies:
S3ReadPolicy:
BucketName: !Ref AppBucketName
Events:
S3NewObjectEvent:
Type: S3
Properties:
Bucket: !Ref AppBucket
Events: s3:ObjectCreated:*
AppBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Ref AppBucketName
What do I need to add to this yaml file in order to tie a resource based policy that allows for cross account access to my lambda function?
This can be done achieved with the help of AWS::Lambda::Permission using aws_cdk.aws_lambda.CfnPermission.
For example, to allow your lambda to be called from a role in another account, add the following to your CDK:
from aws_cdk import aws_lambda
aws_lambda.CfnPermission(
scope,
"CrossAccountInvocationPermission",
action="lambda:InvokeFunction",
function_name="FunctionName",
principal="arn:aws:iam::111111111111:role/rolename",
)
If your bucket and your Lambda function exist in separate accounts I don't know if it's possible to modify both of them from SAM / a single CloudFormation template.
Don't think cross account s3 event is possible with SAM, may need to go back to CFN.

How do you get the API endpoint's URL when it's created within the Events property of Cloudformation's Lambda definition

I'm creating a Lambda function via CloudFormation (AWS' SAM: Serverless Application Model) and have defined an API endpoint via the Lambda-function's Events property.
...
MyFunction:
Type: AWS::Serverless::Function
Properties:
Description: Do amazing things
CodeUri: my_function/
Events:
Trigger:
Type: Api
Properties:
Path: /p1/{v1}
Method: post
Handler: app.run
...
I'd now like to use the URL of the endpoint that's been created in another part of the CloudFormation YAML file. I've tried to use the SAM documentation for Lambda but the only return values have to do with the Function's ARN and resource name.
Specifically, thought I think unrelated to the exact question, I want to use the API endpoint as a subscription for an SNS Topic.
¿How can I get the URL of the API Endpoint?
you can directly reference the RestApi resource like this.
Resources:
apiGateway:
Type: AWS::ApiGateway::RestApi
Properties:
Name: !Sub ${AWS::StackName}-my-api
Description: my-api-edge
Outputs:
apiGatewayInvokeURL:
Value: !Sub "https://${apiGateway}.execute-api.${AWS::Region}.amazonaws.com/${apiGatewayStageName}"
lambdaArn:
Value: !GetAtt "lambdaFunction.Arn"
I think I found the answer across a couple of places.
This Stack Overflow post shows that there is an implicit reference created that you can use as follows
!Ref ServerlessRestApi
This was supported in practice, by a SAM Respository App
And then I re-read, more closely, the SAM API documentation which shows the RestApiId Property. It says
...Typically, this is set to reference an AWS::Serverless::Api resource defined in this template. If not defined, a default AWS::Serverless::Api resource is created..."
So it looks like you can reference it as !Ref ServerlessRestApi without any modification to the YAML in the original question, or you could add the following Property, RestApiId: MyAPI, and reference it as !Ref MyAPI.
However, to get the actual URL, it looks like you have to use a Fn::Sub to glue together a couple of parts. Pahud Hsieh does it in his SAM Repository app above
Outputs:
APIUrlPrefix:
Value:
Fn::Sub:
- https://${ServerlessRestApi}.execute-api.${Region}.amazonaws.com/Prod/incomingwebhooks/
- Region:
Ref: AWS::Region
ServerlessRestApi:
Ref: ServerlessRestApi
...

Best way to add a custom domain to lambda?

I am trying to create a lambda function using SAM, however I can't work out how to add a custom domain to it. Do I need to add a whole ApiGateway to my CloudFormation template just to change the domain or is there is an easier way?
My domain is in Route53 and I have a certificate for it in ACM.
My template is currently as follows:
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: An AWS Serverless Application that uses the ASP.NET Core framework running in Amazon Lambda.
Resources:
ExampleWebApi:
Type: AWS::Serverless::Function
Properties:
Handler: Example.WebApi::Example.WebApi.LambdaEntryPoint::FunctionHandlerAsync
Runtime: dotnetcore2.1
CodeUri: ''
MemorySize: 128
Timeout: 10
Role: null
Policies:
- AWSLambdaFullAccess
Environment:
Variables: {}
Events:
PutResource:
Type: Api
Properties:
Path: "/{proxy+}"
Method: ANY
Yes, you need to use API Gateway in order to define a custom domain for a lambda function.

How to attach an existing role to serverless.yml?

I want to attach an existing role to my serverless.yml file, I have created a role in aws console, my code works fine when I test it in aws console, but when I try to test it with the http endpoint it gives me the following:
{"message": "Internal server error"}
I think is because I did not specify any role in the serverless.yml file for the simple reason that I don't know how to do it.
Here is my serverless.yml file :
Resources:
ec2-dev-instance-status:
Properties:
Path: "arn:aws:iam::119906431229:role/lambda-ec2-describe-status"
RoleName: lambda-ec2-describe-status
Type: "AWS::IAM::Role"
functions:
instance-status:
description: "Status ec2 instances"
events:
-
http:
method: get
path: users/create
handler: handler.instance_status
role: "arn:aws:iam::119906431229:role/lambda-ec2-describe-status"
provider:
name: aws
region: us-east-1
runtime: python2.7
stage: dev
resources: ~
service: ec2
Please help.
Thank you.
According to the documentation, there's a few ways to attach existing roles to a function (or entire stack)
Role defined as a Serverless resource
resources:
Resources:
myCustRole0:
Type: AWS::IAM::Role
# etc etc
functions:
func0:
role: myCustRole0
Role defined outside of the Serverless stack
functions:
func0:
role: arn:aws:iam::0123456789:role//my/default/path/roleInMyAccount
Note that the role you use must have additional permissions to log to cloudwatch etc, otherwise you won't get logging.