-
-
Save jed/56b1f58297d374572bc51c59394c7e7f to your computer and use it in GitHub Desktop.
#!/bin/sh | |
aws cloudformation deploy \ | |
--template-file stack.yaml \ | |
--stack-name edge-lambda-test \ | |
--capabilities CAPABILITY_IAM \ | |
--parameter-overrides Nonce=$RANDOM |
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: '*' |
@LpmRaven Because AWS::Lambda::Version
resources can only be created or delete not updated.
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.
@LpmRaven Because
AWS::Lambda::Version
resources can only be created or delete not updated.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 VersionedIndexLambdaASecond 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 VersionedIndexLambdaBIt has some oddities to it but it could be better than creating a whole other lambda just to deploy another lambda.
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.
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.
@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 VersionedIndexLambdaA
if for some reason VersionedIndexLambdaB
has some unintended side effects, for some reason, with another deployment.
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.
@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.
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.
Has @haydenk points out, just adding a new AWS::Lambda::Version
works, and it's actually practical because you can remove the previous AWS::Lambda::Version
in the same update, making it compatible with continuous deployment. Also the AutoPublishAlias
is only available for SAM not CloudFormation.
@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.