【问题标题】:Serverless Framework - Lambda@Edge Deployment for a predefined Cloud Front Distribution无服务器框架 - 用于预定义 Cloud Front Distribution 的 Lambda@Edge 部署
【发布时间】:2020-10-17 14:29:36
【问题描述】:

我已经尝试了一天来配置自动化 lambda@Edge 以通过无服务器框架与分发相关联,但事情并不顺利。

这是documentation,他们说我们可以从资源中使用预定义的云前端分布,但没有显示如何?

这是我的Resources.yml,其中包括 S3 存储桶以及与它相关联的两个分布的来源:

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

现在我已经完成了有关 CloudFront 的所有设置,我只想在边缘添加一个 lambda 以验证我的私有内容(带有 Id 的来源:S3OriginPrivate)。所以这是我的 serverless.yml 文件:

        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

在部署时我遇到了这个问题:

TypeError: Cannot read property 'replace' of undefined

这说明这个 id 已经存在并且不能像我想的那样被替换。我的主要重点是在无服务器框架内部署边缘的 lambda 并与云前端相关联,因此我再次尝试将几乎所有内容添加到云形成资源中,并且仅依赖无服务器框架来部署功能,这里是我的serverless.yml 和资源文件:

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

资源:

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

但是我遇到了许多与定义版本等相关的错误。我搜索、调试和调查了好几个小时,但似乎很难配置。有关如何通过无服务器框架使 lambda edge 与预定义的云前端一起工作的任何帮助?

【问题讨论】:

    标签: aws-lambda amazon-cloudformation amazon-cloudfront serverless-framework aws-lambda-edge


    【解决方案1】:

    使用无服务器框架这样做有点棘手,但我通过将云形成与无服务器框架结合起来解决了这个问题。我在这里有另一个问题的答案,其中包含如何做到这一点的完整描述:

    How to access AWS CloudFront that connected with S3 Bucket via Bearer token of a specific user (JWT Custom Auth)

    我不想在这里重复所有内容,而且我发现这个问题非常重要,并且面对许多没有具体解决方案的人,所以如果您遇到任何问题,请告诉我。

    方法是在 serverless.yml 中创建函数,然后在云结构中创建版本、角色和另一个函数,帮助您发布和动态使用它。

    p>

    这是我的 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)}
    

    这是我的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
    

    但你会在我的另一个问题的答案中找到完整的描述。

    【讨论】:

      猜你喜欢
      • 2021-10-07
      • 1970-01-01
      • 2020-10-10
      • 1970-01-01
      • 2020-07-19
      • 1970-01-01
      • 1970-01-01
      • 2023-03-13
      • 2019-03-02
      相关资源
      最近更新 更多