AWSTemplateFormatVersion: '2010-09-09' | |
Parameters: | |
Nonce: | |
Type: String | |
Outputs: | |
Host: | |
Value: !GetAtt Distribution.DomainName | |
Resources: | |
Bucket: | |
Type: AWS::S3::Bucket | |
Distribution: | |
Type: AWS::CloudFront::Distribution | |
Properties: | |
DistributionConfig: | |
Enabled: true | |
Origins: | |
- Id: !Ref Bucket | |
DomainName: !GetAtt Bucket.DomainName | |
S3OriginConfig: {} | |
DefaultCacheBehavior: | |
TargetOriginId: !Ref Bucket | |
ForwardedValues: | |
QueryString: true | |
ViewerProtocolPolicy: redirect-to-https | |
LambdaFunctionAssociations: | |
- EventType: viewer-request | |
LambdaFunctionARN: !GetAtt IndexLambdaVersion.FunctionArn | |
IndexLambda: | |
Type: AWS::Lambda::Function | |
Properties: | |
Role: !GetAtt IndexLambdaRole.Arn | |
Runtime: nodejs6.10 | |
Handler: index.handler | |
Code: | |
ZipFile: | | |
exports.handler = (event, ctx, cb) => { | |
const status = '200' | |
const headers = { | |
'content-type': [{ | |
key: 'Content-Type', | |
value: 'application/json' | |
}] | |
} | |
const body = JSON.stringify(event, null, 2) | |
const response = {status, headers, body} | |
cb(null, response) | |
} | |
IndexLambdaRole: | |
Type: AWS::IAM::Role | |
Properties: | |
AssumeRolePolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Effect: Allow | |
Principal: | |
Service: | |
- lambda.amazonaws.com | |
- edgelambda.amazonaws.com | |
Action: sts:AssumeRole | |
ManagedPolicyArns: | |
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole | |
IndexLambdaVersion: | |
Type: Custom::LatestLambdaVersion | |
Properties: | |
ServiceToken: !GetAtt PublishLambdaVersion.Arn | |
FunctionName: !Ref IndexLambda | |
Nonce: !Ref Nonce | |
# Custom resource for getting latest version of a lambda, | |
# as required by CloudFront. | |
PublishLambdaVersion: | |
Type: AWS::Lambda::Function | |
Properties: | |
Handler: index.handler | |
Runtime: nodejs6.10 | |
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: '*' |
This comment has been minimized.
This comment has been minimized.
Thanks. Got me up and running quick. |
This comment has been minimized.
This comment has been minimized.
Thanks for this - I assume this only works in 'us-east-1'? |
This comment has been minimized.
This comment has been minimized.
How long does the deployment takes every time you change the lambda code? As my understanding it will take approximately 20-30 minutes to replicate the function every code change. |
This comment has been minimized.
This comment has been minimized.
Needs to upgrade node version to 10.x and the region must be us-east-1 |
This comment has been minimized.
This comment has been minimized.
Thanks jed , this post has been alot helpful !! |
This comment has been minimized.
This comment has been minimized.
Thanks much for this gist jed, and the highly-functional 'custom resource' - very helpful for my purposes, and a place I've found AWS documentation and support lacking. I deployed with an updated Runtime: of nodejs12.x with success. One question - about the use of the nonce. Is this to provide idempotence, allowing multiple instances in a given account, or some other purpose I'm just not grokking? Thanks again!! |
This comment has been minimized.
This comment has been minimized.
i haven't used this in a while, but IIRC the nonce is to force a redeploy of the template, otherwise CloudFormation would just tell you that the template hasn't changed. |
This comment has been minimized.
This comment has been minimized.
I was reading through AWS docs. VersionedIndexLambda:
Type: 'AWS::Lambda::Version'
Properties:
FunctionName: !Ref IndexLambda
Distribution:
Type: AWS::CloudFront::Distribution
......
......
LambdaFunctionAssociations:
- EventType: viewer-request
LambdaFunctionARN: !Ref VersionedIndexLambda
Anybody tried this way? |
This comment has been minimized.
This comment has been minimized.
@nom3ad I have tried this, I have found that the lambda version does not update when you redeploy your code. Jed has gifted you the solution above. |
This comment has been minimized.
This comment has been minimized.
@LpmRaven Because So, what you would have to do is create a second version to replace the current version. First Deploy VersionedIndexLambdaA:
Type: 'AWS::Lambda::Version'
Properties:
FunctionName: !Ref IndexLambda
Distribution:
Type: AWS::CloudFront::Distribution
......
......
LambdaFunctionAssociations:
- EventType: viewer-request
LambdaFunctionARN: !Ref VersionedIndexLambdaA Second Deploy - Will create a second lambda version now and deploy it to the distribution VersionedIndexLambdaA:
Type: 'AWS::Lambda::Version'
Properties:
FunctionName: !Ref IndexLambda
VersionedIndexLambdaB:
Type: 'AWS::Lambda::Version'
Properties:
FunctionName: !Ref IndexLambda
Distribution:
Type: AWS::CloudFront::Distribution
......
......
LambdaFunctionAssociations:
- EventType: viewer-request
LambdaFunctionARN: !Ref VersionedIndexLambdaB It has some oddities to it but it could be better than creating a whole other lambda just to deploy another lambda. |
This comment has been minimized.
This comment has been minimized.
Yes @haydenk, but unfortunately that is not at all practical for continuous deployment. Its much easier to have a lambda just publish a new version every time you make code changes. |
This comment has been minimized.
This comment has been minimized.
So I have taken some of the code here and turned it into a CI/CD pipeline that will update to a new lambda@edge version every time code is released. https://github.com/LpmRaven/lambda-edge-language-region-redirect/tree/master/cloudformation Hopefully, it will help someone as I had to generate a random nonce in 'buildspec' to force update the version number. |
This comment has been minimized.
This comment has been minimized.
@LpmRaven I agree it's awkward but I wouldn't say it's not practical. The current solution is relying on an outside resource to finish the deployment process after CloudFormation update has been completed. Keeping it in CloudFormation allows you easily to revert to Just to re-emphasis, I do admit it is a very awkward solution. Awkward enough to say either solution would be fine, it's just a matter of what you're willing to deal with long term. |
This comment has been minimized.
This comment has been minimized.
@haydenk Take a look at the link I provided in the previous reply. You can revert to a previous lambda version by hardcoding the arn attached to the CloudFront distribution ...and you don't need to change cloudformation every time you update your lambda@edge. |
This comment has been minimized.
This comment has been minimized.
Lambda update problem already solved in AWS SAM - see https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-specification-generated-resources-function.html#sam-specification-generated-resources-function-autopublishalias. Just add AutoPublishAlias to AWS::Serverless::Function and SAM will create hash-based versions automatically than update CloudFront bindings. |
This comment has been minimized.
Thanks. Very concise.