I want to add SNS as a trigger for Lambda in CloudFormation template but I it is not working for me. Below is the code I applied and I get lambda, sns and lambda subscription to SNS but I can't add trigger to lambda. Does anyone have any idea how to do it?
LambdaRole:
Type: 'AWS::IAM::Role'
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- 'sts:AssumeRole'
Path: /
Policies:
- PolicyName: lambda_policy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action: '*'
Resource: '*'
Lambdafunction:
Type: AWS::Lambda::Function
Properties:
Handler: index.handler
Role: !GetAtt LambdaRole.Arn
# Role: !GettAtt [LambdaRole, Arn]
Code:
S3Bucket: lambda-s3
S3Key: lambda.zip
Runtime: python3.9
Timeout: 30
PermissionSNStoLambda:
Type: 'AWS::Lambda::Permission'
Properties:
Action: 'lambda:InvokeFunction'
FunctionName: !Ref Lambdafunction
Principal: sns.amazonaws.com
test2Topic:
Type: 'AWS::SNS::Topic'
Properties:
DisplayName: Scale of Test Web group
Subscription:
- Protocol: lambda
Endpoint: !GetAtt Lambdafunction.Arn
SNSPolicy:
Type: 'AWS::SNS::TopicPolicy'
Properties:
Topics:
- !Ref test2Topic
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal: AWS: '*'
Action:
- 'SNS:GetTopicAttributes'
- 'SNS:SetTopicAttributes'
- 'SNS:AddPermission'
- 'SNS:RemovePermission'
- 'SNS:DeleteTopic'
- 'SNS:Subscribe'
- 'SNS:ListSubscriptionsByTopic'
- 'SNS:Publish'
- 'SNS:Receive'
Resource: !Ref test2Topic
This is the most logical for me to use to add the trigger, but no success:
SNSTriggersLambda:
Type: AWS::Lambda::EventInvokeConfig
Properties:
DestinationConfig:
OnFailure:
Destination: !Ref test2Topic
OnSuccess:
Destination: !Ref test2Topic
FunctionName: !Ref Lambdafunction
MaximumEventAgeInSeconds: 70
MaximumRetryAttempts: 1
Qualifier: $LATEST
I just managed to resolve the issue. I needed one extra line in permission section stating source ARN for SNS Topic:
PermissionSNStoLambda:
Type: 'AWS::Lambda::Permission'
Properties:
Action: 'lambda:InvokeFunction'
FunctionName: !Ref Lambdafunction
Principal: sns.amazonaws.com
SourceArn: !Ref test2Topic
It works now!
I am trying to create an AWS ElasticSearch Domain (now as AWS OpenSearch service) by cloudformation template but getting error "Resource handler returned message: "null" (RequestToken: 90149a2b-10a1-2609-20e0-9e839731fc2f, HandlerErrorCode: InternalFailure)".
CloudFormation template:
AWSTemplateFormatVersion: "2010-09-09"
Description: ""
Resources:
LogsLogGroup:
Type: "AWS::Logs::LogGroup"
Properties:
LogGroupName: "/aws/aes/domains/domainName/application-logs"
LogsLogGroup2:
Type: "AWS::Logs::LogGroup"
Properties:
LogGroupName: "/aws/aes/domains/domainName/index-logs"
LogsLogGroup3:
Type: "AWS::Logs::LogGroup"
Properties:
LogGroupName: "/aws/aes/domains/domainName/search-logs"
OpenSearchServiceDomain:
Type: "AWS::OpenSearchService::Domain"
DependsOn:
- LogsLogGroup
- LogsLogGroup2
- LogsLogGroup3
Properties:
DomainName: "domainName"
ClusterConfig:
DedicatedMasterCount: 3
DedicatedMasterEnabled: true
DedicatedMasterType: "c4.large.search"
InstanceCount: 2
InstanceType: "c4.large.search"
ZoneAwarenessEnabled: true
ZoneAwarenessConfig:
AvailabilityZoneCount: 2
WarmEnabled: false
AccessPolicies:
Version: '2012-10-17'
Statement:
-
Effect: 'Allow'
Principal:
AWS: '*'
Action: 'es:*'
Resource: 'arn:aws:es:us-east-1:xxxxxxxxx:domain/domainName/*'
VPCOptions:
SecurityGroupIds:
- !ImportValue "sg-xxxxxxxxx"
SubnetIds:
- !ImportValue "subnet-xxxxxxxxx"
- !ImportValue "subnet-xxxxxxxxx"
- !ImportValue "subnet-xxxxxxxxx"
EncryptionAtRestOptions:
Enabled: true
NodeToNodeEncryptionOptions:
Enabled: true
AdvancedOptions:
"rest.action.multi.allow_explicit_index": "true"
EBSOptions:
EBSEnabled: true
VolumeType: "gp2"
VolumeSize: 200
LogPublishingOptions:
ES_APPLICATION_LOGS:
CloudWatchLogsLogGroupArn: !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/aes/domains/domainName/application-logs"
Enabled: true
INDEX_SLOW_LOGS:
CloudWatchLogsLogGroupArn: !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/aes/domains/domainName/index-logs"
Enabled: true
SEARCH_SLOW_LOGS:
CloudWatchLogsLogGroupArn: !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/aes/domains/domainName/search-logs"
Enabled: true
DomainEndpointOptions:
EnforceHTTPS: true
TLSSecurityPolicy: "Policy-Min-TLS-1-0-2019-07"
AdvancedSecurityOptions:
Enabled: true
InternalUserDatabaseEnabled: true
MasterUserOptions:
MasterUserName: "user"
MasterUserPassword: "password"
We had the same vague error. Adding AmazonOpenSearchServiceFullAccess policy to the deploy user got beyond it.
Just an FYI - you don't need AccessPolicies if you're creating an VPC based Domain OR you don't need Security Groups/Subnets if you're creating an Internet based Domain.
AccessPolicies are only required for an Internet based Domains
VPCOptions are only required for VPC based Domains
I've created an Automation Document using cloud formation to update the live alias for a given function. It runs ok without any errors and I'm not seeing anything cloud trail. But when I check which version is set to alias:live it is left unchanged.
template.yml
AWSTemplateFormatVersion: "2010-09-09"
Description: "AWS CloudFormation Template for Response Plans"
Parameters:
Environment:
Type: String
Default: "sandbox"
Domain:
Type: String
Team:
Type: String
NotificationARN:
Type: AWS::SSM::Parameter::Value<String>
Resources:
ResponsePlan:
Type: AWS::SSMIncidents::ResponsePlan
Properties:
Actions:
- SsmAutomation:
RoleArn: !GetAtt Role.Arn
DocumentName: UpdateAliasDocument
DisplayName: "UpdateLambdaAlias"
IncidentTemplate:
Impact: 3
NotificationTargets:
- SnsTopicArn:
Ref: NotificationARN
Summary: "String"
Title: "String"
Name: "UpdateLambdaAlias"
Tags:
- Key: "Team"
Value: !Ref Team
- Key: "Domain"
Value: !Ref Domain
- Key: "Environment"
Value: !Ref Environment
Document:
Type: AWS::SSM::Document
Properties:
Content:
schemaVersion: "2.2"
parameters:
FunctionVersion:
type: "String"
default: "1"
FunctionName:
type: "String"
mainSteps:
- name: "UpdateLambdaAlias"
action: aws:runShellScript
inputs:
runCommand:
- aws lambda update-alias --function-name {{FunctionName}} --name live --function-version {{FunctionVersion}}
DocumentType: "Command"
TargetType: /
Tags:
- Key: "Team"
Value: !Ref Team
Role:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action: sts:AssumeRole
Path: /
Policies:
- PolicyName: EC2Instances
PolicyDocument:
Statement:
- Effect: Allow
Action:
- ec2:*
Resource:
- !Sub arn:${AWS::Partition}:ec2:${AWS::Region}:${AWS::AccountId}:instance/*
- PolicyName: UpdateAliasPolicy
PolicyDocument:
Statement:
- Effect: Allow
Action:
- lambda:UpdateFunctionConfiguration
Resource:
- !Sub arn:${AWS::Partition}:lambda:${AWS::Region}:${AWS::AccountId}:function:${Environment}-*
Instance:
Type: AWS::EC2::Instance
Properties:
ImageId: ami-0c2b8ca1dad447f8a
InstanceType: t2.micro
Monitoring: true
Tags:
- Key: "Team"
Value: !Ref Team
Update
Looks like not target is being found to run the script on
It looks like your Role entry does not have the required permissions to execute the update-alias command. Your policy only allows for lambda:UpdateFunctionConfiguration.
You will at least need the lambda:UpdateAlias permission as well. If this is not enough, you could try being very permissive with your role and then reducing the permissions afterwards.
This is my cloudformation template, which deploys successfully:
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: Deploy Da Blog
Resources:
ArticleTable:
Type: AWS::DynamoDB::Table
Properties:
KeySchema:
- AttributeName: id
KeyType: HASH
AttributeDefinitions:
- AttributeName: id
AttributeType: S
ProvisionedThroughput:
ReadCapacityUnits: 1
WriteCapacityUnits: 1
TableName: !Sub ${AWS::StackName}-Article
UpdateReplacePolicy: Retain
DeletionPolicy: Retain
############################### BLOG API ############################
BlogRestApiLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub '/aws/apigateway/${AWS::StackName}'
BlogRestApiLoggingRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub 'BlogRestApiLoggingRole-${AWS::StackName}'
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service: apigateway.amazonaws.com
Action:
- sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs
## specifies the IAM role that Amazon API Gateway uses to write API logs to Amazon CloudWatch Logs
BlogRestApiAccount:
Type: AWS::ApiGateway::Account
Properties:
CloudWatchRoleArn: !GetAtt [ BlogRestApiLoggingRole, Arn ]
BlogRestApi:
Type: AWS::ApiGateway::RestApi
Properties:
Name: !Sub '${AWS::StackName}'
Description: Blog API
EndpointConfiguration:
Types:
- EDGE
BlogRestApiDeployment:
Type: AWS::ApiGateway::Deployment
Properties:
RestApiId: !Ref BlogRestApi
Description: Automatically created by the RestApi construct
DependsOn:
- BlogRestApi
- BlogApiResource
- BlogApiIdResource
- ListArticlesMethod
- PostArticleMethod
- GetArticleMethod
BlogRestApiStageProd:
Type: AWS::ApiGateway::Stage
DependsOn:
- BlogRestApiDeployment
Properties:
RestApiId: !Ref BlogRestApi
DeploymentId: !Ref BlogRestApiDeployment
StageName: prod
MethodSettings:
- LoggingLevel: INFO
ResourcePath: '/*'
HttpMethod: '*'
MetricsEnabled: true
BlogApiRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub 'BlogApiRole-${AWS::StackName}'
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service: apigateway.amazonaws.com
Action:
- sts:AssumeRole
BlogApiPolicy:
Type: AWS::IAM::Policy
DependsOn:
- BlogApiRole
- ListArticlesFunction
- PostArticleFunction
- GetArticleFunction
Properties:
PolicyName: !Sub 'BlogApiPolicy-${AWS::StackName}'
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: 'Allow'
Action: 'lambda:InvokeFunction'
Resource:
- !GetAtt ListArticlesFunction.Arn
- !GetAtt PostArticleFunction.Arn
- !GetAtt GetArticleFunction.Arn
Roles:
- !Ref BlogApiRole
############################### END OF Blog API ############################
############################### START OF Blog Functions ####################
BlogApiResource:
Type: AWS::ApiGateway::Resource
Properties:
ParentId: !GetAtt [ BlogRestApi, RootResourceId ]
PathPart: article
RestApiId: !Ref BlogRestApi
BlogApiIdResource:
Type: AWS::ApiGateway::Resource
Properties:
ParentId: !Ref BlogApiResource
PathPart: "{id}"
RestApiId: !Ref BlogRestApi
BlogApiOptionsMethod:
Type: AWS::ApiGateway::Method
DependsOn:
- BlogApiResource
Properties:
ApiKeyRequired: true
AuthorizationType: NONE
HttpMethod: OPTIONS
Integration:
Type: MOCK
PassthroughBehavior: WHEN_NO_TEMPLATES
RequestTemplates:
'application/json': '{"statusCode": 200}'
IntegrationResponses:
- StatusCode: 200
ResponseParameters:
method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
method.response.header.Access-Control-Allow-Methods: "'GET,POST,PATCH,OPTIONS'"
method.response.header.Access-Control-Allow-Origin: "'*'"
ResponseTemplates:
'application/json': '{}'
MethodResponses:
- StatusCode: 200
ResponseParameters:
method.response.header.Access-Control-Allow-Headers: false
method.response.header.Access-Control-Allow-Methods: false
method.response.header.Access-Control-Allow-Origin: false
ResourceId: !Ref BlogApiResource
RestApiId: !Ref BlogRestApi
ListArticlesFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: !Sub '${AWS::StackName}-list-articles-function'
Handler: com.zenithwebfoundry.blog.api.ListArticlesHandler
CodeUri:
Key: !Ref ParamCodePackage
Bucket: !Ref ParamCodeBucket
Policies:
- Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- dynamodb:Query
Resource: !GetAtt [ ArticleTable, Arn ]
Runtime: java11
Timeout: 10
MemorySize: 256
Environment:
Variables:
TABLE_NAME: !Ref ArticleTable
PRIMARY_KEY: id
DependsOn:
- ArticleTable
ListArticlesMethod:
Type: AWS::ApiGateway::Method
Properties:
HttpMethod: GET
ResourceId: !Ref BlogApiResource
RestApiId: !Ref BlogRestApi
AuthorizationType: NONE
Integration:
IntegrationHttpMethod: POST
Type: AWS
Uri: !Sub 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ListArticlesFunction.Arn}/invocations'
PassthroughBehavior: WHEN_NO_TEMPLATES
RequestTemplates:
'application/json': !Ref ParamAPIGatewayRequestEventMappingTemplate
IntegrationResponses:
- StatusCode: 200
ResponseTemplates:
'application/json': !Ref ParamAPIGatewayResponseEventMappingTemplate
MethodResponses:
- StatusCode: 200
OperationName: ListArticles
PostArticleFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: !Sub '${AWS::StackName}-post-article-function'
Handler: com.zenithwebfoundry.blog.api.SaveArticleHandler
CodeUri:
Key: !Ref ParamCodePackage
Bucket: !Ref ParamCodeBucket
Policies:
- Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- dynamodb:PutItem
Resource: !GetAtt [ ArticleTable, Arn ]
Runtime: java11
Timeout: 10
MemorySize: 256
Environment:
Variables:
TABLE_NAME: !Ref ArticleTable
PRIMARY_KEY: id
DependsOn:
- ArticleTable
PostArticleMethod:
Type: AWS::ApiGateway::Method
Properties:
HttpMethod: POST
ResourceId: !Ref BlogApiResource
RestApiId: !Ref BlogRestApi
AuthorizationType: NONE
Integration:
IntegrationHttpMethod: POST
Type: AWS
Uri: !Sub 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${PostArticleFunction.Arn}/invocations'
PassthroughBehavior: WHEN_NO_TEMPLATES
RequestTemplates:
'application/json': !Ref ParamAPIGatewayRequestEventMappingTemplate
IntegrationResponses:
- StatusCode: 200
ResponseTemplates:
'application/json': !Ref ParamAPIGatewayResponseEventMappingTemplate
MethodResponses:
- StatusCode: 200
OperationName: PostArticle
GetArticleFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: !Sub '${AWS::StackName}-get-article-function'
Handler: com.zenithwebfoundry.blog.api.GetArticleHandler
CodeUri:
Key: !Ref ParamCodePackage
Bucket: !Ref ParamCodeBucket
Policies:
- Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- dynamodb:GetItem
Resource: !GetAtt [ ArticleTable, Arn ]
Runtime: java11
Timeout: 10
MemorySize: 256
Environment:
Variables:
TABLE_NAME: !Ref ArticleTable
PRIMARY_KEY: id
DependsOn:
- ArticleTable
GetArticleMethod:
Type: AWS::ApiGateway::Method
Properties:
HttpMethod: GET
ResourceId: !Ref BlogApiIdResource
RestApiId: !Ref BlogRestApi
AuthorizationType: NONE
Integration:
IntegrationHttpMethod: POST
Type: AWS
Uri: !Sub 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GetArticleFunction.Arn}/invocations'
PassthroughBehavior: WHEN_NO_TEMPLATES
RequestTemplates:
'application/json': !Ref ParamAPIGatewayRequestEventMappingTemplate
IntegrationResponses:
- StatusCode: 200
ResponseTemplates:
'application/json': !Ref ParamAPIGatewayResponseEventMappingTemplate
MethodResponses:
- StatusCode: 200
OperationName: GetArticle
############################### END OF Blog Functions ######################
BlogAPIDomainName:
Type: AWS::ApiGateway::DomainName
Properties:
DomainName: !Join [ ".", ['blogapi', !Ref DomainName]]
EndpointConfiguration:
Types:
- EDGE
CertificateArn: !Ref DomainCert
SecurityPolicy: TLS_1_0
BlogAPIHostedZone:
Type: AWS::Route53::HostedZone
Properties:
Name: !Ref BlogAPIDomainName
BlogAPIBasePathMapping:
Type: AWS::ApiGateway::BasePathMapping
Properties:
DomainName: !Ref BlogAPIDomainName
RestApiId: !Ref BlogRestApi
Stage: 'prod'
Route53RecordSetGroup:
Type: AWS::Route53::RecordSetGroup
Properties:
HostedZoneId: !Ref DomainHostedZoneId
RecordSets:
- Name: !Join [ "", ['blog', '.', !Ref DomainName, '.']]
Type: A
TTL: '300'
ResourceRecords:
- 52.64.238.177
- Name: !Join [ ".", ['blogapi', !Ref DomainName]]
Type: A
AliasTarget:
# HostedZoneId: !Ref BlogAPIHostedZone # distributionHostedZoneId - alias target name does not lie in the target zone
HostedZoneId: Z2FDTNDATAQYW2
# https://j97h8bvml9.execute-api.ap-southeast-2.amazonaws.com/prod/articles
DNSName: !GetAtt [BlogAPIDomainName, DistributionDomainName]
AssetsBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Join [ ".", [ !Ref 'AWS::StackName', 'assets' ] ]
CorsConfiguration:
CorsRules:
- AllowedHeaders: ['*']
AllowedMethods: [GET,PUT,POST,DELETE,HEAD]
AllowedOrigins: ['http://localhost*']
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
WebBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Join [ ".", [ !Ref 'AWS::StackName', 'web' ] ]
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
WebsiteConfiguration:
IndexDocument: 'index.html'
ErrorDocument: 'index.html'
Parameters:
ParamCodePackage:
Type: String
ParamCodeBucket:
Type: String
DomainName:
Description: "Public DNS Zone Name"
Type: String
DomainHostedZoneId:
Type: String
Description: 'The AWS HostedZoneId of the above domain name'
DomainCert:
Type: String
Description: 'The arn reference to the certificate used with the domain.'
ParamRequestMappingTemplate:
Type: String
Description: 'Read from resources/templates'
SaveArticleHandler:
Type: String
Default: 'com.zenithwebfoundry.blog.api.SaveArticleHandler'
GetArticleHandler:
Type: String
Default: 'com.zenithwebfoundry.blog.api.GetArticleHandler'
ListArticlesHandler:
Type: String
Default: 'com.zenithwebfoundry.blog.api.ListArticlesHandler'
ParamAPIGatewayRequestEventMappingTemplate:
Type: String
Default: '{
"resource" : "$context.resourceId",
"path" : "$context.path",
"httpMethod" : "$context.httpMethod",
"headers": {
#foreach($header in $input.params().header.keySet())
"$header": "$util.escapeJavaScript($input.params().header.get($header))" #if($foreach.hasNext),#end
#end
},
"method": "$context.httpMethod",
"pathParameters": {
#foreach($param in $input.params().path.keySet())
"$param": "$util.escapeJavaScript($input.params().path.get($param))" #if($foreach.hasNext),#end
#end
},
"queryStringParameters": {
#foreach($queryParam in $input.params().querystring.keySet())
"$queryParam": "$util.escapeJavaScript($input.params().querystring.get($queryParam))" #if($foreach.hasNext),#end
#end
},
"body" : $input.json("$"),
"isBase64Encoded": false
}'
ParamAPIGatewayResponseEventMappingTemplate:
Type: String
Default: '#set($statusCode = $input.path("$.statusCode"))
#set($context.responseOverride.status = $statusCode)
#set($headers = $input.path("$.headers"))
#foreach($key in $headers.keySet())
#set($context.responseOverride.header[$key] = $headers[$key])
#end
#set($context.responseOverride.header.Access-Control-Allow-Headers = "Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token")
#set($context.responseOverride.header.Access-Control-Allow-Methods = "*")
#set($context.responseOverride.header.Access-Control-Allow-Origin = "*")
{
"body": $input.json("$.body")
}
'
Outputs:
ArticleEndpoint:
Value: !Join ["", ['https://', !Ref BlogRestApi, '.execute-api.ap-southeast-2.', !Ref 'AWS::URLSuffix', '/', !Ref BlogRestApiStageProd, '/'] ]
The problem is that when I try to call the ListArticlesFunction using the following curl expression:
curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X GET https://blogapi.zenithwebfoundry.com/article
I get the following:
HTTP/2 500
content-type: application/json
content-length: 36
date: Sun, 30 Aug 2020 09:09:50 GMT
x-amzn-requestid: c1948ed5-18c5-4807-8904-3c1c2af06c25
x-amzn-errortype: InternalServerErrorException
x-amz-apigw-id: SE3y1G71ywMF5fw=
x-cache: Error from cloudfront
via: 1.1 647846f53eba457a8e4ba1d1d42a6336.cloudfront.net (CloudFront)
x-amz-cf-pop: SYD1-C1
x-amz-cf-id: 6bnMhNRUdf1znTmD0vQn86UZcMF_j9JCzFb-JvhwwT9j6ch4P8t20g==
{"message": "Internal server error"}
Checking cloudwatch, I see the slightly cryptic error:
Execution failed due to configuration error: Invalid permissions on Lambda function
The same thing happens if I go into the APIGateway service console and go to the article GET resource. So at least its consistent.
I have tested the handler in isolation and there's no compilation problems anywhere, so I'm pretty sure its stack-related.
I gather that the problem must either be in the BlogApiPolicy Policy or the Lambda Policy, but nothing I set seems to work. Does anyone know what cloudformation shenanigans is needed to get this right?
The answer is that I needed the role added as a Credentials: entry in the method of each Lambda, for example:
ListArticlesMethod:
Type: AWS::ApiGateway::Method
Properties:
HttpMethod: GET
ResourceId: !Ref BlogApiResource
RestApiId: !Ref BlogRestApi
AuthorizationType: NONE
Integration:
IntegrationHttpMethod: POST
Type: AWS
Uri: !Sub 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ListArticlesFunction.Arn}/invocations'
Credentials: !GetAtt [BlogApiRole, Arn]
PassthroughBehavior: WHEN_NO_TEMPLATES
RequestTemplates:
'application/json': !Ref ParamAPIGatewayRequestEventMappingTemplate
IntegrationResponses:
- StatusCode: 200
ResponseTemplates:
'application/json': !Ref ParamAPIGatewayResponseEventMappingTemplate
MethodResponses:
- StatusCode: 200
OperationName: ListArticles
Why this is suddenly needed, when this was never needed before, is quite beyond me, but I must thank #Marcin for his/her patient assistance and convincing my doubting mind.
Based on the comments.
The issue was that BlogApiRole, although being created, it was not used in any of the API Gateway methods. This is required, because API Gateway needs to have explicit permissions to invoke a lambda functions. To enable this, Credentials in AWS::ApiGateway::Method Integration should be set.
The alternative is to use AWS::Lambda::Permission. The use of the AWS::Lambda::Permission is especially useful when there are many methods in the API gateway which require invoking the lambda. The reason is that you can create one such AWS::Lambda::Permission for the lambda function which can allow API gateway access to the lambda as a whole. This saves us from defining Credentials for each method independently.
I would like to automate deployment of AWS Lambda developed in java. For this I created CodePipeline which is triggered on git push command to CodeCommit repository. Next step in CodePipeline is CodeBuild project. CodeBuild uses following buildspec.yml file:
version: 0.1
phases:
build:
commands:
- echo Entering build phase...
- echo Build started on `date`
- mvn package shade:shade
- mv target/Output-1.0.jar .
artifacts:
files:
- Output-1.0.jar
When CodeBuild project is run manually it will upload jar file to s3 bucket. This jar file can be without any problem used to update lambda and everything works as expected. But if CodeBuild is run via CodePipeline, result is jar file wrapped inside zip. Since this zip cannot be used for updating lambda function, I am not sure what I should do here since CodePipeline overwrites any packaging set for CodeBuild project.
Idea is that CodePipeline triggers CodeBuild which produces output which additional lambda will took and update lambda function with it. Is it somehow possible that output of CodeBuild which is invoked from CodePipeline be jar instead of zip ? If not, what should I do here then ?
Any help is appreciated.
A zip or a jar file can both be used to update a Lambda Function, you just need to add a "Deploy Step" using Cloudformation to your CodePipeline.
This is a nodejs build/pipeline, try to adapt to your java project:
Project Files
buildspec.yml
version: 0.2
phases:
install:
commands:
- echo install phase
pre_build:
commands:
- echo pre_build phase
build:
commands:
- npm install --production
post_build:
commands:
- echo post build
artifacts:
type: zip
files:
- index.js
- node_modules/**/*
- package.json
- template.yml
- configuration.json
discard-paths: no
configuration.json
{
"Parameters": {
"BucketName" : { "Fn::GetArtifactAtt" : ["Build", "BucketName"]},
"ObjectKey" : { "Fn::GetArtifactAtt" : ["Build", "ObjectKey"]}
}
}
template.yml (you need to add a AWS::Lambda::Permission)
AWSTemplateFormatVersion: "2010-09-09"
Description: "My Lambda Template"
Parameters:
BucketName:
Type: String
ObjectKey:
Type: String
Roles:
Type: String
Default: Roles
LambdaRole:
Type: String
Default: LambdaRole
Resources:
MyLambdaFunction:
Type: AWS::Lambda::Function
Properties:
Description: 'My Lambda Handler'
Handler: index.handler
Runtime: nodejs6.10
Timeout: 5
Code:
S3Bucket:
Ref: BucketName
S3Key:
Ref: ObjectKey
Role:
Fn::Join:
- ""
- - "arn:aws:iam::"
- !Ref AWS::AccountId
- ":role/"
- Fn::ImportValue:
Fn::Join:
- ""
- - Ref: Roles
- "-"
- Ref: LambdaRole
Roles Template
AWSTemplateFormatVersion: '2010-09-09'
Description: 'The AWS Resource Roles'
Resources:
CodeBuildRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
Effect: Allow
Principal:
Service: codebuild.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AWSCodeBuildAdminAccess
- arn:aws:iam::aws:policy/CloudWatchFullAccess
- arn:aws:iam::aws:policy/AWSCodeCommitFullAccess
- arn:aws:iam::aws:policy/AmazonS3FullAccess
CodePipelineRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
Effect: Allow
Principal:
Service: codepipeline.amazonaws.com
Action: sts:AssumeRole
Policies:
-
PolicyName: CloudFormationFullAccess
PolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: "Allow"
Action:
- "cloudformation:*"
Resource: "*"
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AWSCodePipelineFullAccess
- arn:aws:iam::aws:policy/AWSCodeCommitFullAccess
- arn:aws:iam::aws:policy/AmazonS3FullAccess
- arn:aws:iam::aws:policy/AWSCodeBuildAdminAccess
- arn:aws:iam::aws:policy/AWSLambdaFullAccess
CloudFormationRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
Effect: Allow
Principal:
Service: cloudformation.amazonaws.com
Action: sts:AssumeRole
Policies:
-
PolicyName: CloudFormationFullAccess
PolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: "Allow"
Action: "cloudformation:*"
Resource: "*"
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AWSCodePipelineFullAccess
- arn:aws:iam::aws:policy/AWSCodeCommitFullAccess
- arn:aws:iam::aws:policy/AmazonS3FullAccess
- arn:aws:iam::aws:policy/AWSCodeBuildAdminAccess
- arn:aws:iam::aws:policy/AWSLambdaFullAccess
- arn:aws:iam::aws:policy/AmazonAPIGatewayAdministrator
LambdaRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
Policies:
-
PolicyName: CloudFormationFullAccess
PolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: "Allow"
Action: "cloudformation:*"
Resource: "*"
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AWSLambdaFullAccess
- arn:aws:iam::aws:policy/AWSCodePipelineFullAccess
- arn:aws:iam::aws:policy/AmazonSESFullAccess
Outputs:
CodeBuildRoleOutput:
Description: 'Maybe API CodeBuildRole ARN'
Value: !Ref 'CodeBuildRole'
Export:
Name: !Sub '${AWS::StackName}-CodeBuildRole'
CodePipelineRoleOutput:
Description: 'Maybe API CodePipelineRole ARN'
Value: !Ref 'CodePipelineRole'
Export:
Name: !Sub '${AWS::StackName}-CodePipelineRole'
CloudFormationRoleOutput:
Description: 'Maybe API CloudFormationRole ARN'
Value: !Ref 'CloudFormationRole'
Export:
Name: !Sub '${AWS::StackName}-CloudFormationRole'
LambdaRoleOutput:
Description: 'Maybe API LambdaRole ARN'
Value: !Ref 'LambdaRole'
Export:
Name: !Sub '${AWS::StackName}-LambdaRole'
CodePipeline Bucket
AWSTemplateFormatVersion: '2010-09-09'
Description: 'The AWS S3 CodePipeline Bucket'
Resources:
CodePipelineBucket:
Type: AWS::S3::Bucket
DeletionPolicy: Retain
Properties:
BucketName: my-code-pipeline-bucket
VersioningConfiguration:
Status: Enabled
AccessControl: BucketOwnerFullControl
Outputs:
CodePipelineBucketOutput:
Description: 'CodePipeline Bucket Ref'
Value: !Ref CodePipelineBucket
Export:
Name: !Sub '${AWS::StackName}-CodePipelineBucketRef'
CodeBuild Template
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Nodejs CodeBuild Template'
Parameters:
Artifact:
Type: String
Default: artifact
Roles:
Type: String
Default: Roles
CodeBuildRole:
Type: String
Default: CodeBuildRole
Resources:
NodejsCodeBuild:
Type: AWS::CodeBuild::Project
DeletionPolicy: Retain
Properties:
ServiceRole:
Fn::ImportValue:
Fn::Join:
- ""
- - Ref: Roles
- "-"
- Ref: CodeBuildRole
Artifacts:
Type: no_artifacts
Environment:
ComputeType: BUILD_GENERAL1_SMALL
Image: aws/codebuild/eb-nodejs-6.10.0-amazonlinux-64:4.0.0
Type: LINUX_CONTAINER
Source:
Type: S3
Location: !Ref Artifact
Outputs:
NodejsCodeBuildOutput:
Description: 'Nodejs CodeBuild Ref'
Value: !Ref 'NodejsCodeBuild'
Export:
Name: !Sub '${AWS::StackName}-NodejsCodeBuildRef'
CodePipeline Template
AWSTemplateFormatVersion: '2010-09-09'
Description: 'CodePipeline for Nodejs Applications'
Parameters:
Roles:
Type: String
Default: Roles
CodePipelineRole:
Type: String
Default: CodePipelineRole
CloudFormationRole:
Type: String
Default: CloudFormationRole
CodePipelineBucket:
Type: String
Default: CodePipelineBucket
CodePipelineBucketRef:
Type: String
Default: CodePipelineBucketRef
PipelineName:
Type: String
Default: PipelineName
CodeBuildProject:
Type: String
Default: NodejsCodeBuild
CodeBuildProjectRef:
Type: String
Default: NodejsCodeBuildRef
Branch:
Type: String
Default: master
Repository:
Type: String
Default: my-repository-name
LambdaStack:
Type: String
Default: LambdaStack
Resources:
NodejsCodePipeline:
Type: AWS::CodePipeline::Pipeline
Properties:
Name: !Ref PipelineName
RoleArn:
Fn::Join:
- ""
- - "arn:aws:iam::"
- !Ref AWS::AccountId
- ":role/"
- Fn::ImportValue:
Fn::Join:
- ""
- - Ref: Roles
- "-"
- Ref: CodePipelineRole
ArtifactStore:
Location:
Fn::Join:
- ""
- - Fn::ImportValue:
Fn::Join:
- ""
- - Ref: CodePipelineBucket
- "-"
- Ref: CodePipelineBucketRef
Type: S3
Stages:
- Name: Source
Actions:
- InputArtifacts: []
Name: Source
ActionTypeId:
Category: Source
Owner: AWS
Version: 1
Provider: CodeCommit
OutputArtifacts:
- Name: Master
Configuration:
BranchName: !Ref Branch
RepositoryName: !Ref Repository
RunOrder: 1
- Name: Build
Actions:
- Name: Build
ActionTypeId:
Category: Build
Owner: AWS
Version: 1
Provider: CodeBuild
InputArtifacts:
- Name: Master
OutputArtifacts:
- Name: Build
Configuration:
ProjectName:
Fn::Join:
- ""
- - Fn::ImportValue:
Fn::Join:
- ""
- - Ref: CodeBuildProject
- "-"
- Ref: CodeBuildProjectRef
RunOrder: 1
- Name: Stage
Actions:
- Name: Sandbox
ActionTypeId:
Category: Deploy
Owner: AWS
Version: 1
Provider: CloudFormation
InputArtifacts:
- Name: Build
OutputArtifacts:
- Name: Deploy
Configuration:
StackName: !Ref LambdaStack
ActionMode: CREATE_UPDATE
Capabilities: CAPABILITY_IAM
TemplateConfiguration: Build::configuration.json
TemplatePath: Build::template.yml
ParameterOverrides: |
{
"BucketName" : { "Fn::GetArtifactAtt" : ["Build", "BucketName"]},
"ObjectKey" : { "Fn::GetArtifactAtt" : ["Build", "ObjectKey"]}
}
RoleArn:
Fn::Join:
- ""
- - "arn:aws:iam::"
- !Ref AWS::AccountId
- ":role/"
- Fn::ImportValue:
Fn::Join:
- ""
- - Ref: Roles
- "-"
- Ref: CloudFormationRole
RunOrder: 1