AWS CodeBuild invoked from CodePipeline produces artefact which cannot be used for AWS Lambda - aws-lambda

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

Related

How to fix a MalformedPolicyDocument in a aws cloudformation template

Hi I'm trying to create a cloudformation to create an Iam role in aws, although I keep getting the error "MalformedPolicyDocument"
AWSTemplateFormatVersion: '2010-09-09'
Resources:
testRole:
Type: 'AWS::IAM::Role'
Properties:
RoleName: 'role-test'
Description: 'role'
MaxSessionDuration: '14400'
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Resource:
AWS: !Join
- ""
- - 'arn:aws:iam::'
- '12345678912'
- ':root/role'
Action:
- 'sts:AssumeRole'
ManagedPolicyArns:
- 'arn:aws:iam::aws:policy/AmazonEC2ReadOnlyAccess'
- 'arn:aws:iam::aws:policy/AmazonSSMReadOnlyAccess'
- 'arn:aws:iam::aws:policy/AWSCloudTrailReadOnlyAccess'
Path: /
MangedInlinePolicy:
Type: 'AWS::IAM::Policy'
Properties:
PolicyName: 'Inline'
PolicyDocument:
Version: '2012-10-17'
Statement:
- Sid: 'VisualEditor0'
- Effect: 'Allow'
- Action: 'sts:AssumeRole'
- Resource:
- 'arn:aws:iam::12345678911:role/role'
Roles:
- !Ref testRole
Outputs:
ArmisRoleArn:
Value: !GetAtt [ArmisRole, Arn]
I believe the error is down to the "AWS: !Join" part but I may be wrong, I'm still pretty new with cloudformation, so any help would be great.
Thanks

AWS CloudFormation setting SNS trigger to Lambda

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!

AWS Automation Document not updating Lambda Alias

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.

Serverless Framework - Lambda#Edge Deployment for a predefined Cloud Front Distribution

I have been trying for a day to configure automating a lambda#Edge to be associated with a Distribution through the serverless framework but things aren't working well.
Here is the documentation and they said we can use a predefined cloud front distribution from resources but not shown how?
Here is my Resources.yml that include the S3 bucket and associated two distribution's origins to it:
Resources:
ResourcesBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: ${self:custom.resourcesBucketName}
AccessControl: Private
CorsConfiguration:
CorsRules:
- AllowedHeaders: ['*']
AllowedMethods: ['PUT']
AllowedOrigins: ['*']
ResourcesBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket:
Ref: ResourcesBucket
PolicyDocument:
Statement:
# Read permission for CloudFront
- Action: s3:GetObject
Effect: "Allow"
Resource:
Fn::Join:
- ""
-
- "arn:aws:s3:::"
-
Ref: "ResourcesBucket"
- "/*"
Principal:
CanonicalUser: !GetAtt CloudFrontOriginAccessIdentity.S3CanonicalUserId
CloudFrontOriginAccessIdentity:
Type: AWS::CloudFront::CloudFrontOriginAccessIdentity
Properties:
CloudFrontOriginAccessIdentityConfig:
Comment:
Fn::Join:
- ""
-
- "Identity for accessing CloudFront from S3 within stack "
-
Ref: "AWS::StackName"
- ""
# I can use this instead of Fn::Join !Sub 'Identity for accessing CloudFront from S3 within stack #{AWS::StackName}' Getting benefit of
# serverless-pseudo-parameters plugin
# Cloudfront distro backed by ResourcesBucket
ResourcesCdnDistribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Origins:
# S3 origin for private resources
- DomainName: !Sub '${self:custom.resourcesBucketName}.s3-${self:provider.region}.amazonaws.com'
Id: S3OriginPrivate
S3OriginConfig:
OriginAccessIdentity: !Sub 'origin-access-identity/cloudfront/#{CloudFrontOriginAccessIdentity}'
# S3 origin for public resources
- DomainName: !Sub '${self:custom.resourcesBucketName}.s3-${self:provider.region}.amazonaws.com'
Id: S3OriginPublic
S3OriginConfig:
OriginAccessIdentity: !Sub 'origin-access-identity/cloudfront/#{CloudFrontOriginAccessIdentity}'
Enabled: true
Comment: CDN for public and provate static content.
DefaultRootObject: index.html
HttpVersion: http2
DefaultCacheBehavior:
AllowedMethods:
- DELETE
- GET
- HEAD
- OPTIONS
- PATCH
- POST
- PUT
Compress: true
TargetOriginId: S3OriginPublic
ForwardedValues:
QueryString: false
Headers:
- Origin
Cookies:
Forward: none
ViewerProtocolPolicy: redirect-to-https
CacheBehaviors:
-
PathPattern: 'private/*'
TargetOriginId: S3OriginPrivate
AllowedMethods:
- DELETE
- GET
- HEAD
- OPTIONS
- PATCH
- POST
- PUT
Compress: true
ForwardedValues:
QueryString: false
Headers:
- Origin
Cookies:
Forward: none
ViewerProtocolPolicy: redirect-to-https
-
PathPattern: 'public/*'
TargetOriginId: S3OriginPublic
AllowedMethods:
- DELETE
- GET
- HEAD
- OPTIONS
- PATCH
- POST
- PUT
Compress: true
ForwardedValues:
QueryString: false
Headers:
- Origin
Cookies:
Forward: none
ViewerProtocolPolicy: redirect-to-https
PriceClass: PriceClass_200
Now I have all set regarding the CloudFront and I just want to add a lambda at the edge to authenticate my private content (The Origin with Id: S3OriginPrivate). So here is my serverless.yml file:
service: mda-app-uploads
plugins:
- serverless-offline
- serverless-pseudo-parameters
- serverless-iam-roles-per-function
custom:
stage: ${opt:stage, self:provider.stage}
resourcesBucketName: ${self:custom.stage}-mda-resources-bucket
provider:
name: aws
runtime: nodejs12.x
stage: ${opt:stage, 'dev'}
region: us-east-1
versionFunctions: true
resources:
- ${file(resources/s3-cloudfront.yml)}
# functions:
functions:
mdaAuthEdge:
handler: mda-edge-auth.handler
events:
- cloudFront:
eventType: viewer-request
origin:
Id: S3OriginPrivate
When deploying I am getting this issue:
TypeError: Cannot read property 'replace' of undefined
This telling that this id already exists and can't be replaced as I think. My main focus is to get the lambda at edge deployed and associated with the cloud front within the serverless framework, so I made another trial to add almost everything to the cloud formation resources and depend only on the serverless framework in deploying the function and here was my serverless.yml and the resources file:
service: mda-app-uploads
plugins:
- serverless-offline
- serverless-pseudo-parameters
- serverless-iam-roles-per-function
custom:
stage: ${opt:stage, self:provider.stage}
resourcesBucketName: ${self:custom.stage}-mda-resources-bucket
provider:
name: aws
runtime: nodejs12.x
stage: ${opt:stage, 'dev'}
region: us-east-1
versionFunctions: true
resources:
# Buckets
- ${file(resources/s3-cloudfront.yml)}
# functions:
functions:
mdaAuthEdge:
handler: mda-edge-auth.handler
role: LambdaEdgeFunctionRole
The resources:
Resources:
LambdaEdgeFunctionRole:
Type: "AWS::IAM::Role"
Properties:
Path: "/"
ManagedPolicyArns:
- "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
-
Sid: "AllowLambdaServiceToAssumeRole"
Effect: "Allow"
Action:
- "sts:AssumeRole"
Principal:
Service:
- "lambda.amazonaws.com"
- "edgelambda.amazonaws.com"
LambdaEdgeFunctionPolicy:
Type: "AWS::IAM::Policy"
Properties:
PolicyName: MainEdgePolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
Effect: "Allow"
Action:
- "lambda:GetFunction"
- "lambda:GetFunctionConfiguration"
Resource: !Ref MdaAuthAtEdgeLambdaFunction.Version #!Join [':', [!GetAtt MdaAuthAtEdgeLambdaFunction.Arn, '2']]
Roles:
- !Ref LambdaEdgeFunctionRole
ResourcesBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: ${self:custom.resourcesBucketName}
AccessControl: Private
CorsConfiguration:
CorsRules:
- AllowedHeaders: ['*']
AllowedMethods: ['PUT']
AllowedOrigins: ['*']
ResourcesBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket:
Ref: ResourcesBucket
PolicyDocument:
Statement:
# Read permission for CloudFront
- Action: s3:GetObject
Effect: "Allow"
Resource:
Fn::Join:
- ""
-
- "arn:aws:s3:::"
-
Ref: "ResourcesBucket"
- "/*"
Principal:
CanonicalUser: !GetAtt CloudFrontOriginAccessIdentity.S3CanonicalUserId
CloudFrontOriginAccessIdentity:
Type: AWS::CloudFront::CloudFrontOriginAccessIdentity
Properties:
CloudFrontOriginAccessIdentityConfig:
Comment:
Fn::Join:
- ""
-
- "Identity for accessing CloudFront from S3 within stack "
-
Ref: "AWS::StackName"
- ""
# I can use this instead of Fn::Join !Sub 'Identity for accessing CloudFront from S3 within stack #{AWS::StackName}' Getting benefit of
# serverless-pseudo-parameters plugin
# Cloudfront distro backed by ResourcesBucket
ResourcesCdnDistribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Origins:
# S3 origin for private resources
- DomainName: !Sub '${self:custom.resourcesBucketName}.s3-${self:provider.region}.amazonaws.com'
Id: S3OriginPrivate
S3OriginConfig:
OriginAccessIdentity: !Sub 'origin-access-identity/cloudfront/#{CloudFrontOriginAccessIdentity}'
# S3 origin for public resources
- DomainName: !Sub '${self:custom.resourcesBucketName}.s3-${self:provider.region}.amazonaws.com'
Id: S3OriginPublic
S3OriginConfig:
OriginAccessIdentity: !Sub 'origin-access-identity/cloudfront/#{CloudFrontOriginAccessIdentity}'
Enabled: true
Comment: CDN for public and provate static content.
DefaultRootObject: index.html
HttpVersion: http2
DefaultCacheBehavior:
AllowedMethods:
- DELETE
- GET
- HEAD
- OPTIONS
- PATCH
- POST
- PUT
Compress: true
TargetOriginId: S3OriginPublic
ForwardedValues:
QueryString: false
Headers:
- Origin
Cookies:
Forward: none
ViewerProtocolPolicy: redirect-to-https
CacheBehaviors:
-
PathPattern: 'private/*'
TargetOriginId: S3OriginPrivate
AllowedMethods:
- DELETE
- GET
- HEAD
- OPTIONS
- PATCH
- POST
- PUT
Compress: true
LambdaFunctionAssociations:
-
EventType: origin-request
LambdaFunctionARN: !Ref MdaAuthEdgeLambdaFunction.Version
#!Join [':', [!GetAtt MdaAuthAtEdgeLambdaFunction.Arn, '2']]
# arn:aws:lambda:eu-west-1:219511374676:function:mda-aws-functions-dev-authLambdaAtEdge:1
ForwardedValues:
QueryString: false
Headers:
- Origin
Cookies:
Forward: none
ViewerProtocolPolicy: redirect-to-https
-
PathPattern: 'public/*'
TargetOriginId: S3OriginPublic
AllowedMethods:
- DELETE
- GET
- HEAD
- OPTIONS
- PATCH
- POST
- PUT
Compress: true
ForwardedValues:
QueryString: false
Headers:
- Origin
Cookies:
Forward: none
ViewerProtocolPolicy: redirect-to-https
PriceClass: PriceClass_200
But I've faced many errors related to defining the version and so on. I searched, debugged, and investigated that for many hours but seems hard configuration. Any help on how to get lambda edge works with predefined cloud front through the serverless framework?
It's a bit tricky to do so using a serverless framework but I solved it by combining cloud formation with the serverless framework. I have the answer here to another question which contains a full description of how to do so:
How to access AWS CloudFront that connected with S3 Bucket via Bearer token of a specific user (JWT Custom Auth)
I don't want to repeat everything again here and also I found the question so important and facing many people without a concrete solution so pleas let me know in case you are facing any issue.
The approach is to just create the function inside the serverless.yml then inside the cloud formation you can do all the magic of creating the versions, roles and another function that will help you publish your arn and use it dynamically.
Here is my Serverless.yml:
service: mda-app-uploads
plugins:
- serverless-offline
- serverless-pseudo-parameters
- serverless-iam-roles-per-function
- serverless-bundle
custom:
stage: ${opt:stage, self:provider.stage}
resourcesBucketName: ${self:custom.stage}-mda-resources-bucket
resourcesStages:
prod: prod
dev: dev
resourcesStage: ${self:custom.resourcesStages.${self:custom.stage}, self:custom.resourcesStages.dev}
provider:
name: aws
runtime: nodejs12.x
stage: ${opt:stage, 'dev'}
region: us-east-1
versionFunctions: true
functions:
oauthEdge:
handler: src/mda-edge-auth.handler
role: LambdaEdgeFunctionRole
memorySize: 128
timeout: 5
resources:
- ${file(resources/s3-cloudfront.yml)}
Here is my resources/s3-cloudfront.yml:
Resources:
AuthEdgeLambdaVersion:
Type: Custom::LatestLambdaVersion
Properties:
ServiceToken: !GetAtt PublishLambdaVersion.Arn
FunctionName: !Ref OauthEdgeLambdaFunction
Nonce: "Test"
PublishLambdaVersion:
Type: AWS::Lambda::Function
Properties:
Handler: index.handler
Runtime: nodejs12.x
Role: !GetAtt PublishLambdaVersionRole.Arn
Code:
ZipFile: |
const {Lambda} = require('aws-sdk')
const {send, SUCCESS, FAILED} = require('cfn-response')
const lambda = new Lambda()
exports.handler = (event, context) => {
const {RequestType, ResourceProperties: {FunctionName}} = event
if (RequestType == 'Delete') return send(event, context, SUCCESS)
lambda.publishVersion({FunctionName}, (err, {FunctionArn}) => {
err
? send(event, context, FAILED, err)
: send(event, context, SUCCESS, {FunctionArn})
})
}
PublishLambdaVersionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: PublishVersion
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action: lambda:PublishVersion
Resource: '*'
LambdaEdgeFunctionRole:
Type: "AWS::IAM::Role"
Properties:
Path: "/"
ManagedPolicyArns:
- "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
-
Sid: "AllowLambdaServiceToAssumeRole"
Effect: "Allow"
Action:
- "sts:AssumeRole"
Principal:
Service:
- "lambda.amazonaws.com"
- "edgelambda.amazonaws.com"
LambdaEdgeFunctionPolicy:
Type: "AWS::IAM::Policy"
Properties:
PolicyName: MainEdgePolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
Effect: "Allow"
Action:
- "lambda:GetFunction"
- "lambda:GetFunctionConfiguration"
Resource: !GetAtt AuthEdgeLambdaVersion.FunctionArn
Roles:
- !Ref LambdaEdgeFunctionRole
ResourcesBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: ${self:custom.resourcesBucketName}
AccessControl: Private
CorsConfiguration:
CorsRules:
- AllowedHeaders: ['*']
AllowedMethods: ['PUT']
AllowedOrigins: ['*']
ResourcesBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket:
Ref: ResourcesBucket
PolicyDocument:
Statement:
# Read permission for CloudFront
- Action: s3:GetObject
Effect: "Allow"
Resource:
Fn::Join:
- ""
-
- "arn:aws:s3:::"
-
Ref: "ResourcesBucket"
- "/*"
Principal:
CanonicalUser: !GetAtt CloudFrontOriginAccessIdentity.S3CanonicalUserId
- Action: s3:PutObject
Effect: "Allow"
Resource:
Fn::Join:
- ""
-
- "arn:aws:s3:::"
-
Ref: "ResourcesBucket"
- "/*"
Principal:
AWS: !GetAtt LambdaEdgeFunctionRole.Arn
- Action: s3:GetObject
Effect: "Allow"
Resource:
Fn::Join:
- ""
-
- "arn:aws:s3:::"
-
Ref: "ResourcesBucket"
- "/*"
Principal:
AWS: !GetAtt LambdaEdgeFunctionRole.Arn
CloudFrontOriginAccessIdentity:
Type: AWS::CloudFront::CloudFrontOriginAccessIdentity
Properties:
CloudFrontOriginAccessIdentityConfig:
Comment:
Fn::Join:
- ""
-
- "Identity for accessing CloudFront from S3 within stack "
-
Ref: "AWS::StackName"
- ""
# Cloudfront distro backed by ResourcesBucket
ResourcesCdnDistribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Origins:
# S3 origin for private resources
- DomainName: !Sub '${self:custom.resourcesBucketName}.s3.amazonaws.com'
Id: S3OriginPrivate
S3OriginConfig:
OriginAccessIdentity: !Sub 'origin-access-identity/cloudfront/#{CloudFrontOriginAccessIdentity}'
# S3 origin for public resources
- DomainName: !Sub '${self:custom.resourcesBucketName}.s3.amazonaws.com'
Id: S3OriginPublic
S3OriginConfig:
OriginAccessIdentity: !Sub 'origin-access-identity/cloudfront/#{CloudFrontOriginAccessIdentity}'
Enabled: true
Comment: CDN for public and provate static content.
DefaultRootObject: index.html
HttpVersion: http2
DefaultCacheBehavior:
AllowedMethods:
- DELETE
- GET
- HEAD
- OPTIONS
- PATCH
- POST
- PUT
Compress: true
TargetOriginId: S3OriginPublic
ForwardedValues:
QueryString: false
Headers:
- Origin
Cookies:
Forward: none
ViewerProtocolPolicy: redirect-to-https
CacheBehaviors:
-
PathPattern: 'private/*'
TargetOriginId: S3OriginPrivate
AllowedMethods:
- DELETE
- GET
- HEAD
- OPTIONS
- PATCH
- POST
- PUT
Compress: true
LambdaFunctionAssociations:
-
EventType: viewer-request
LambdaFunctionARN: !GetAtt AuthEdgeLambdaVersion.FunctionArn
ForwardedValues:
QueryString: false
Headers:
- Origin
Cookies:
Forward: none
ViewerProtocolPolicy: redirect-to-https
-
PathPattern: 'public/*'
TargetOriginId: S3OriginPublic
AllowedMethods:
- DELETE
- GET
- HEAD
- OPTIONS
- PATCH
- POST
- PUT
Compress: true
ForwardedValues:
QueryString: false
Headers:
- Origin
Cookies:
Forward: none
ViewerProtocolPolicy: redirect-to-https
PriceClass: PriceClass_200
But you will find the full description in my other question's answer.

Can i add a codecommit trigger to my lambda function via CloudFormation

Im writing a lambda function that i want to be triggered by somebody updating the master branch of a repo. The repo already exists on the account.
Is there a way in cloudformation that i can add the trigger to the lambda function? I guess at a snip i could cretae some cloudwatch rule to trigger the lambda, but would rather keep it all inside the lambda.
Thanks
R
If you are using AWS serverless transform then you can self contain it within the lambda. Although the transform generates the cloudwatch rule and the lambda permission, so it's basically the same you mentioned.
nevertheless here's an example to do what you want
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: Pipeline which triggers lambda for codecommit changes
Parameters:
BranchName:
Default: master
Description: The GIT branch
Type: String
RepositoryName:
Description: The GIT repository
Type: String
StackOwner:
Description: The stack owner
Type: String
Resources:
BasicLambdaRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
Path: "/"
Policies:
- PolicyName: root
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action: logs:*
Resource: arn:aws:logs:*:*:*
- Effect: Allow
Action: '*'
Resource: '*'
PipelineTriggerFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: src/trigger
Handler: codetrigger.handler
MemorySize: 256
Role: !GetAtt BasicLambdaRole.Arn
Runtime: python3.6
Timeout: 900
Environment:
Variables:
TestVariable: "TestValue"
Events:
CodeCommitPushEvent:
Type: CloudWatchEvent
Properties:
Pattern:
source:
- aws.codecommit
resources:
- !Sub 'arn:aws:codecommit:${AWS::Region}:${AWS::AccountId}:${RepositoryName}'
detail:
event:
- referenceCreated
- referenceUpdated
repositoryName:
- !Ref RepositoryName
referenceName:
- !Ref BranchName
Tags:
'owner': !Ref StackOwner
'task': !Ref RepositoryName
Obviously, specify the lambda role better and not give all permissions as provided in the example.

Resources