Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Possible bug when evaluating Sub parameters with Serverless Framework #3006

Open
scottrmercer opened this issue Jan 9, 2024 · 2 comments
Open

Comments

@scottrmercer
Copy link

CloudFormation Lint Version

0.83.8

What operating system are you using?

MacOS

Describe the bug

When evaluating a Serverless.yml file, Sub parameters that use dot notation are not matched with valid_params, leading to false E1019 errors.

E1019 Parameter SiteBucket.Arn for Fn::Sub not found at resources/Resources/SiteBucketPolicy/Properties/PolicyDocument/Statement/0/Resource/Fn::Sub

E1019 Parameter Distribution.Id for Fn::Sub not found at resources/Resources/SiteBucketPolicy/Properties/PolicyDocument/Statement/0/Condition/StringEquals/AWS:SourceArn/Fn::Sub

I managed to make 2 adjustments that "fixed" the symptoms, although I am not familiar enough with the codebase to know if these qualify as anything more than hacks.

  1. Serverless files embed the Resources node under another node named resources (see my example). Because of this, Template.get_resource_names() returns an empty list. As a hack, I changed get_resource_names so that resources = self.template.get("resources", {}) is replaced with resources = self.template.get("resources", {}).get("Resources", {}), returning the full list.
  2. Once the resource names are resolved, Sub params that use dot notation do not match, as only the first segment of the parameter is in the valid_params list. In order to use dot notation to refer to properties on the resource param, I had to change line 131 of Sub.py from if parameter not in valid_params: to if parameter.split('.')[0] not in valid_params:. This resolves all issues, although it does seem hacky, like Add testing #1.

Hopefully this is just something that I am doing wrong, rather than a real bug.

Expected behavior

E1019 is not returned, and linting passes.

Reproduction template

---
service: test-service

configValidationMode: error

plugins:
  - serverless-python-requirements
  - serverless-wsgi
  - serverless-plugin-datadog
  - serverless-plugin-ifelse

params:
  prod:
    integrations_event_bus_arn: !ImportValue :infrastructure:${opt:stage}:events:arn
  default:
    integrations_event_bus_arn: arn:aws:events:${aws:region}:${param:account}:event-bus/${opt:stage}-events

provider:
  name: aws
  runtime: python3.11
  stage: ${opt:stage}
  region: us-west-2
  logs:
    restApi: true
  tracing:
    lambda: true
  tags:
    service: test
    env: ${opt:stage}
  versionFunctions: false
  memorySize: ${file(sls/variables/default.yml):memorySize, 1024}
  timeout: 30
  deploymentBucket:
    name: ${opt:stage}-slsdeploy-${param:account}-${aws:region}
    serverSideEncryption: AES256
  environment:
    AWS_ACCOUNT_ID: ${param:account}
    SERVERLESS_STAGE: ${opt:stage}
    SERVERLESS_REGION: { Ref: "AWS::Region" }
  apiGateway:
    shouldStartNameWithService: true

package:
  excludeDevDependencies: true

  patterns:
    - 'package*.json'
    - '!node_modules/**'
    - '!tests/**'

functions:

  flask_proxy:
    vpc:
      securityGroupIds:
        - !Ref SecurityGroup
      subnetIds:
        !Split
        - ","
        - !ImportValue :infrastructure:${opt:stage}:vpc:privateSubnets
    handler: wsgi.handler
    events:
      - http:
          method: get
          path: /
          cors:
            origin: "*"
            headers:
              - Content-Type
              - X-Amz-Date
              - Authorization
              - X-Api-Key
              - X-Amz-Security-Token
              - X-Amz-User-Agent
              - X-User-Role
              - X-Impersonated-User
              - X-Impersonated-User-Role
              - x-datadog-origin
              - x-datadog-parent-id
              - x-datadog-sampling-priority
              - x-datadog-trace-id
            allowCredentials: false

resources:
  Resources:
    Distribution:
      Type: AWS::CloudFront::Distribution
      DeletionPolicy: Retain
      Properties:
        DistributionConfig:
          Enabled: true
          Aliases:
            - ${self:custom.cloudfront.alias.${opt:stage}, self:custom.cloudfront.alias.default}
          DefaultRootObject: index.html
          PriceClass: PriceClass_All
          HttpVersion: http2
          Origins:
            - DomainName: !GetAtt SiteBucket.DomainName
              Id: S3Origin
              S3OriginConfig:
                OriginAccessIdentity: ''
              OriginAccessControlId: !GetAtt AccessIdentity.Id
            - DomainName: !Join
                - ''
                - - !Ref ApiGatewayRestApi
                  - .execute-api.
                  - !Ref AWS::Region
                  - .amazonaws.com
              Id: API
              CustomOriginConfig:
                OriginProtocolPolicy: https-only
          CacheBehaviors:
            - AllowedMethods:
                - GET
                - HEAD
                - PUT
                - POST
                - DELETE
                - PATCH
                - OPTIONS
              CachedMethods:
                - GET
                - HEAD
              CachePolicyId: !Ref CachePolicyAPI
              Compress: true
              PathPattern: api/*
              ResponseHeadersPolicyId: !Ref ResponseHeadersPolicy
              TargetOriginId: API
              ViewerProtocolPolicy: redirect-to-https
          DefaultCacheBehavior:
            TargetOriginId: S3Origin
            LambdaFunctionAssociations:
              - EventType: origin-request
                LambdaFunctionARN: '{{resolve:ssm:/infrastructure/${opt:stage}/edgeFunctions/frontController}}'
            AllowedMethods:
              - GET
              - HEAD
            CachePolicyId: !Ref CachePolicyS3
            Compress: true
            MaxTTL: 300
            DefaultTTL: 90
            MinTTL: 60
            ViewerProtocolPolicy: redirect-to-https
            ResponseHeadersPolicyId: !Ref ResponseHeadersPolicy
          Logging:
            Bucket: !GetAtt LogsBucket.DomainName
            Prefix: logs/cloudfront/${self:custom.cloudfront.alias.${opt:stage}, self:custom.cloudfront.alias.default}
            IncludeCookies: false
          ViewerCertificate:
            AcmCertificateArn: !ImportValue ':infrastructure:${opt:stage}:cert:cloudFrontArn'
            SslSupportMethod: sni-only
            MinimumProtocolVersion: TLSv1.2_2021
    SiteBucket:
      Type: AWS::S3::Bucket
      DeletionPolicy: Retain
      UpdateReplacePolicy: Retain
      Properties: null
    SiteBucketPolicy:
      Type: AWS::S3::BucketPolicy
      Properties:
        Bucket: !Ref SiteBucket
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
            - Sid: CloudFrontOAC
              Action: s3:GetObject
              Effect: Allow
              Resource: !Sub ${SiteBucket.Arn}/*
              Principal:
                Service: cloudfront.amazonaws.com
              Condition:
                StringEquals:
                  AWS:SourceArn: !Sub arn:${AWS::Partition}:cloudfront::${AWS::AccountId}:distribution/${Distribution.Id}
    LogsBucket:
      Type: AWS::S3::Bucket
      Properties:
        AccessControl: LogDeliveryWrite
        OwnershipControls:
          Rules:
            - ObjectOwnership: BucketOwnerPreferred
      DeletionPolicy: Retain
      UpdateReplacePolicy: Retain

@Hatter1337
Copy link

I have the same problem.
SAM template:

Conditions:
  UseProductionSettings: !Equals [!Ref Env, "prod"]
  
Parameters:
  Env:
    Description: Deploying environment
    Type: String
    Default: dev
  ProductionProvisionedConcurrency:
    Description: Number of Lambda provisioned concurrency for production environment
    Type: Number
    Default: 2

...
    
  MyFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: !Sub "my-function-${Env}"
      CodeUri: my_function/
      Handler: app.lambda_handler
      Role: !Ref LambdaRoleARN
      AutoPublishAlias: live
      ProvisionedConcurrencyConfig:
        ProvisionedConcurrentExecutions:
          !If [
            UseProductionSettings,
            !Sub "${ProductionProvisionedConcurrency}",
            1,
          ]
E1019 {'Fn::Sub': '${ProductionProvisionedConcurrency}'} is not of type 'integer'
template.yaml:104:13
  • cfn-lint version 1.3.3 (latest)

@kddejong
Copy link
Contributor

I need to validate this (E1019 {'Fn::Sub': '${ProductionProvisionedConcurrency}'} is not of type 'integer'). There is an odd issue when doing a GetAtt to an integer but I think in the case of a parameter it works as you have pointed out.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants