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

Requires capabilities : [CAPABILITY_AUTO_EXPAND] #11566

Open
4 tasks done
scanianick opened this issue Nov 29, 2022 · 6 comments
Open
4 tasks done

Requires capabilities : [CAPABILITY_AUTO_EXPAND] #11566

scanianick opened this issue Nov 29, 2022 · 6 comments

Comments

@scanianick
Copy link

scanianick commented Nov 29, 2022

Are you certain it's a bug?

  • Yes, it looks like a bug

Is the issue caused by a plugin?

  • It is not a plugin issue

Are you using the latest v3 release?

  • Yes, I'm using the latest v3 release

Is there an existing issue for this?

  • I have searched existing issues, it hasn't been reported yet

Issue description

When running serverless deploy an error occur stating "Requires capabilities : [CAPABILITY_AUTO_EXPAND]" but this should be default behavior is my understanding.

This is when role is specified under the resource part.

Service configuration (serverless.yml) content

service: ${env:CI_PROJECT_NAME, 'clad-region-service-api'}

frameworkVersion: "3"
useDotenv: true

custom:
  baseUrlV1: v1
  pythonRequirements:
    pipCmdExtraArgs: 
      - '--platform manylinux2014_x86_64'
      - "--only-binary=:all:"
    slim: true
    slimPatternsAppendDefaults: false
    slimPatterns:
      - '**/requirements.txt'
  logRetentionInDays: 365  # used to set a global value for all functions
  stackName: ${self:service}-${opt:stage, self:provider.stage}
  customDomain:
    domainName: ${self:custom.config.apidomain}
    certificateName: ${self:custom.config.acmcertificate}
    basePath: region
    stage: ${opt:stage, self:provider.stage}
    createRoute53Record: false
  config: ${file(config.${opt:stage, self:provider.stage}.yml), 'api.clad.devtest.aws.scania.com'}
  dynamoDbTableName: ${env:DYNAMODB_TABLE_NAME, 'clad-region-service-tbl-dev'}
  apiKeyExecutionRole: ${self:service}-${opt:stage, self:provider.stage}-ApiKeyExecutionRole
  iamAuthExecutionRole: ${self:service}-${opt:stage, self:provider.stage}-IamAuthExecutionRole
  associateWaf:
    name: clad-wafv2-public-WebACLPublic
    version: V2

package:
  patterns:
    - '!./**'
    - './python/src/**'
  excludeDevDependencies: false # need to be false to have quick deployments, default value true

provider:
  name: aws
  deploymentMethod: direct # Can not set this to direct. Getting error about auto_expand
  runtime: python3.9
  timeout: 30
  logs:
    restApi: false
  stackTags:
    GitLabCommitSHA: ${env:CI_COMMIT_SHA, 'local-sls-deployment'}
  environment:
    PYTHONPATH: ${env:PYTHONPATH, 'python/src'}
    LOG_LEVEL: ${env:LOG_LEVEL, 'DEFAULT'}
    LOG_LEVEL_BOTOCORE: ${env:LOG_LEVEL_BOTOCORE, 'DEFAULT'}
    DYNAMODB_TABLE_NAME: ${self:custom.dynamoDbTableName}
    JWT_PROVIDER_URL: ${env:JWT_PROVIDER_URL, ''}
    AUTHORIZED_FG_CLIENTS: ${env:AUTHORIZED_FG_CLIENTS, 'clad_test'}
    COGNITO_USER_POOL_ID: ${env:COGNITO_USER_POOL_ID, 'eu-west-1_usWPsvCHv'}
    COGNITO_USER_POOL_AWS_ACCOUNT_NUMBER: ${env:COGNITO_USER_POOL_AWS_ACCOUNT_NUMBER, '123'}
    ENVIRONMENT_STAGE: ${env:ENVIRONMENT_STAGE, 'dev'}
    IAM_ROLE_ALLOWED_ACCOUNT: ${env:IAM_ROLE_ALLOWED_ACCOUNT, '123'}
    INFOSEC_ACCOUNT: ${env:INFOSEC_ACCOUNT, '123'}
  iam:
    role:
      statements:
        - Effect: Allow
          Action:
            - dynamodb:Query
            - dynamodb:Scan
            - dynamodb:GetItem
            - dynamodb:DeleteItem
            - dynamodb:UpdateItem
            - dynamodb:PutItem
            - dynamodb:DescribeTable
          Resource:
            - "arn:aws:dynamodb:${opt:region, self:provider.region}:*:table/${self:custom.dynamoDbTableName}"
        - Effect: Allow
          Action:
            - apigateway:POST
            - apigateway:PUT
            - apigateway:PATCH
            - apigateway:DELETE
            - apigateway:GET
            - apigateway:HEAD
            - apigateway:OPTIONS
          Resource:
            - "arn:aws:apigateway:${opt:region, self:provider.region}::/apikeys/*"
            - "arn:aws:apigateway:${opt:region, self:provider.region}::/*"
        - Effect: Allow
          Action:
            - execute-api:Invoke
          Resource:
            - "arn:aws:execute-api:${opt:region, self:provider.region}:*:*"
        - Effect: Allow
          Action:
            - ec2:DescribeRegions
          Resource:
            - '*'
        - Effect: Allow
          Action:
            - cognito-idp:ListUsersInGroup
            - cognito-idp:ListUsers
          Resource: 
            'Fn::ImportValue': 'clad-cognito-userpool-CladUserPoolArn'
        - Effect: Allow
          Action: sts:AssumeRole
          Resource: 
            - arn:aws:iam::${aws:accountId}:role/clad-account-service-aws-api-${opt:stage, self:provider.stage}-IamAuthExecutionRole
            - arn:aws:iam::${aws:accountId}:role/clad-ipam-service-${opt:stage, self:provider.stage}-IamAuthExecutionRole
        
  apiGateway:
    resourcePolicy:
      - Effect: Allow
        Principal: "*"
        Action: execute-api:Invoke
        Resource:
          - execute-api:/*/GET/v1/cognito/region/account/*
          - execute-api:/*/PUT/v1/cognito/region/account/*

      - Effect: Allow
        Principal: "*"
        Action: execute-api:Invoke
        Resource:
          - execute-api:/*/GET/${self:custom.baseUrlV1}/fg/region
          - execute-api:/*/GET/${self:custom.baseUrlV1}/fg/region/account
          - execute-api:/*/POST/${self:custom.baseUrlV1}/fg/region/account
          - execute-api:/*/GET/${self:custom.baseUrlV1}/fg/region/account/*
          - execute-api:/*/PUT/${self:custom.baseUrlV1}/fg/region/account/*
        Condition:
          IpAddress:
            'Fn::Transform':
              Name: 'AWS::Include'
              Parameters:
                Location: s3://clad-masked-resourcepolicy-${sls:stage}/FederationGatewayIpFilter.yml

      - Effect: Allow
        Principal: "*"
        Action: execute-api:Invoke
        Resource:
          - execute-api:/*/*/*
        Condition:
          IpAddress:
            'Fn::Transform':
              Name: 'AWS::Include'
              Parameters:
                Location: s3://clad-masked-resourcepolicy-${sls:stage}/CognitoIpFilter.yml

      - Effect: Allow
        Principal:
          AWS:
            - "${aws:accountId}" # Could be none?
        Action: execute-api:Invoke
        Resource:
          - execute-api:/*/*/*

plugins:
  - serverless-python-requirements
  - serverless-plugin-log-retention
  - serverless-domain-manager
  - serverless-associate-waf

functions:
  get_supported_regions:
    handler: region.get_supported_regions
    events:
      - http:  
          path: ${self:custom.baseUrlV1}/region
          method: get
          cors: true
          authorizer:
            type: AWS_IAM

  get_supported_regions_fg:
    handler: region.get_supported_regions_fg
    events:
      - http:
          path: ${self:custom.baseUrlV1}/fg/region
          method: get
          cors: true
          authorizer: 
            name: jwt_authenticator
            type: request

  get_supported_regions_cognito:
    handler: region_cognito.get_supported_regions_cognito
    events:
      - http:
          path: ${self:custom.baseUrlV1}/cognito/region
          method: get
          cors: true
          authorizer:
            name: CognitoCladAuthorizer
            type: COGNITO_USER_POOLS
            arn: 
              'Fn::ImportValue': 'clad-cognito-userpool-CladUserPoolArn'

  get_specific_account_and_region:
    handler: region.get_specific_account_and_region
    events:
      - http:
          path: ${self:custom.baseUrlV1}/region/account/{account_id}/region/{region_id}
          method: get
          cors: true
          authorizer:
            type: AWS_IAM
          request:
            parameters:
              paths:
                account_id: true

  get_specific_account: 
    handler: region.get_specific_account
    events:
      - http:  
          path: ${self:custom.baseUrlV1}/region/account/{account_id}
          method: get
          cors: true
          authorizer:
            type: AWS_IAM
          request:
            parameters:
              paths:
                account_id: true

  get_specific_account_fg:
    handler: region.get_specific_account_fg
    events:
      - http:
          path: ${self:custom.baseUrlV1}/fg/region/account/{account_id}
          method: get
          cors: true
          authorizer: 
            name: jwt_authenticator
            type: request
          request:
            parameters:
              paths:
                account_id: true

  get_specific_account_cognito:
    handler: region_cognito.get_specific_account_cognito
    events:
      - http:
          path: ${self:custom.baseUrlV1}/cognito/region/account/{account_id}
          method: get
          cors: true
          authorizer:
            name: CognitoCladAuthorizer
            type: COGNITO_USER_POOLS
            arn:
              'Fn::ImportValue': 'clad-cognito-userpool-CladUserPoolArn'
          request:
            parameters:
              paths:
                account_id: true

  get_all:
    handler: region.get_all
    # layers:
    #   - ${cf:clad-lambda-layer-cryptography-patch-${opt:stage}.ContentLambdaLayerQualifiedArn}
    events:
      - http:
          path: ${self:custom.baseUrlV1}/region/account/
          method: get
          cors: true
          authorizer: aws_iam

  get_all_fg:
    handler: region.get_all_fg
    events:
      - http:
          path: ${self:custom.baseUrlV1}/fg/region/account/
          method: get
          cors: true
          authorizer: 
            name: jwt_authenticator
            type: request

  create:
    handler: region.create
    events:
      - http:  
          path: ${self:custom.baseUrlV1}/region/account/
          method: post
          cors: true
          authorizer: aws_iam

  create_fg:
    handler: region.create_fg
    events:
      - http:
          path: ${self:custom.baseUrlV1}/fg/region/account/
          method: post
          cors: true
          authorizer: 
            name: jwt_authenticator
            type: request

  update:
    handler: region.update
    events:
      - http: 
          path: ${self:custom.baseUrlV1}/region/account/{account_id}
          method: put
          cors: true
          request:
            parameters:
              paths:
                account_id: true
          authorizer: aws_iam

  update_fg:
    handler: region.update_fg
    events:
      - http:
          path: ${self:custom.baseUrlV1}/fg/region/account/{account_id}
          method: put
          cors: true
          request:
            parameters:
              paths:
                account_id: true
          authorizer: 
            name: jwt_authenticator
            type: request

  update_availabilityzones_cognito:
    handler: availabilityzones.update_availabilityzones_cognito
    events:
      - http:
          path: ${self:custom.baseUrlV1}/cognito/region/account/{account_id}/{region}/vpc/availabilityzones
          method: put
          cors: true
          authorizer:
            name: CognitoCladAuthorizer
            type: COGNITO_USER_POOLS
            arn: 
              'Fn::ImportValue': 'clad-cognito-userpool-CladUserPoolArn'
          request:
            parameters:
              paths:
                account_id: true
                region: true

  create_vpc_cognito:
    handler: vpc.create_vpc_cognito
    events:
      - http:
          path: ${self:custom.baseUrlV1}/cognito/region/account/{account_id}/{region}/vpc
          method: post
          cors: true
          authorizer:
            name: CognitoCladAuthorizer
            type: COGNITO_USER_POOLS
            arn: 
              'Fn::ImportValue': 'clad-cognito-userpool-CladUserPoolArn'
          request:
            parameters:
              paths:
                account_id: true
                region: true
  
  delete_vpc:
    handler: vpc.delete_vpc
    events:
      - http:
          path: ${self:custom.baseUrlV1}/region/account/{account_id}/{region}/vpc
          method: put
          cors: true
          request:
            parameters:
              paths:
                account_id: true
                region: true
          authorizer: aws_iam

  delete_vpc_cognito:
    handler: vpc.delete_vpc_cognito
    events:
      - http:
          path: ${self:custom.baseUrlV1}/cognito/region/account/{account_id}/{region}/vpc
          method: put
          cors: true
          authorizer:
            name: CognitoCladAuthorizer
            type: COGNITO_USER_POOLS
            arn: 
              'Fn::ImportValue': 'clad-cognito-userpool-CladUserPoolArn'
          request:
            parameters:
              paths:
                account_id: true
                region: true

  jwt_authenticator:
    handler: clad_auth_utilities.jwt.jwt_authenticator

resources:
  Resources:
    ApiKeyExecutionRole:
      Type: AWS::IAM::Role
      Properties:
        Path: /
        RoleName: ${self:custom.apiKeyExecutionRole}
        AssumeRolePolicyDocument:
          Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Principal:
                AWS:
                  - '${aws:accountId}'                 # The stage (DEV / PROD) account it self
                  - '${self:provider.environment.IAM_ROLE_ALLOWED_ACCOUNT}'   # The allowed account (DEV / PROD) account
                  - '${self:provider.environment.INFOSEC_ACCOUNT}'            # infosec- dev or prod account
              Action: sts:AssumeRole
        ManagedPolicyArns:    
          - !Ref ApiKeyExecutionRolePolicy

    ApiKeyExecutionRolePolicy:
      Type: AWS::IAM::ManagedPolicy
      Properties:
        ManagedPolicyName: ${self:custom.apiKeyExecutionRole}-Policy
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
            # Needed to get query stage (dev/prod)
            - Effect: Allow
              Action:
                - 'cloudformation:DescribeStacks'
              Resource:
                - 'arn:aws:cloudformation:${opt:region, self:provider.region}:${aws:accountId}:stack/${self:custom.stackName}*'
                - 'arn:aws:cloudformation:${opt:region, self:provider.region}:${aws:accountId}:stack/${self:custom.stackName}/*'
            # Needed to retrieve API Key and Url
            - Effect: Allow
              Action:
                - 'cloudformation:ListExports'
              Resource:
                - '*'
            - Effect: Allow
              Action:
                - 'apigateway:GET'
              Resource:
                - 'arn:aws:apigateway:${opt:region, self:provider.region}::/apikeys/*'
                # - 'arn:aws:execute-api:${opt:region, self:provider.region}:${aws:accountId}:*/${opt:stage}/*/*'

    IamAuthExecutionRole:
      Type: AWS::IAM::Role
      Properties:
        Path: /
        RoleName: ${self:custom.iamAuthExecutionRole}
        AssumeRolePolicyDocument:
          Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Principal:
                AWS:
                  - '${aws:accountId}'                 # The stage (DEV / PROD) account it self
                  - '${self:provider.environment.IAM_ROLE_ALLOWED_ACCOUNT}'   # The allowed account (DEV / PROD) account
                  - '${self:provider.environment.INFOSEC_ACCOUNT}'            # infosec- dev or prod account
              Action: sts:AssumeRole
        ManagedPolicyArns:    
          - !Ref IamAuthExecutionRolePolicy

    IamAuthExecutionRolePolicy:
      Type: AWS::IAM::ManagedPolicy
      Properties:
        ManagedPolicyName: ${self:custom.iamAuthExecutionRole}-Policy
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Action:
                - 'execute-api:Invoke'
              Resource:
                - 'arn:aws:execute-api:${opt:region, self:provider.region}:${aws:accountId}:*/${opt:stage}/GET/v1/region'
                - 'arn:aws:execute-api:${opt:region, self:provider.region}:${aws:accountId}:*/${opt:stage}/*/*'

  Outputs:
    ApiGatewayRestApi:
      Description: Endpoint for this API
      Value:
        Fn::Join:
          - ''
          - - 'https://'
            - Fn::Join:
                - '.'
                - - Ref: ApiGatewayRestApi
                  - 'execute-api'
                  - ${opt:region, self:provider.region}
                  - 'amazonaws'
                  - 'com'
            - '/'
            - ${opt:stage, self:provider.stage}
      Export:
        Name: ${self:custom.stackName}-ApiEndpoint

Command name and used flags

sls deploy -s dev -r eu-west-1

Command output

✖ Stack clad-region-service-api-dev failed to deploy (113s)
Environment: darwin, node 14.16.0, framework 3.25.1, plugin 6.2.2, SDK 4.3.2
Credentials: Local, "scania-clad-dev" profile
Docs:        docs.serverless.com
Support:     forum.serverless.com
Bugs:        github.com/serverless/serverless/issues

Error:
Requires capabilities : [CAPABILITY_AUTO_EXPAND]

Environment information

Framework Core: 3.25.1
Plugin: 6.2.2
SDK: 4.3.2
@medikoo
Copy link
Contributor

medikoo commented Nov 30, 2022

@scanianick please provide full serverless.yml configuration

@scanianick
Copy link
Author

I updated the original post. Thanks for the assistance!

@vatsal-kavida
Copy link

I too when try to run this command -> serverless deploy --config dev/serverless.yaml --stage $STAGE --region $REGION --verbose --capabilities CAPABILITY_AUTO_EXPAND , it gives me the same error, also, I tried putting it in the provider section under capabilities property but it doesn't recognises it

@medikoo
Copy link
Contributor

medikoo commented May 12, 2023

I assume in this case CF stack deployment should be additionally equipped with CAPABILITY_AUTO_EXPAND capability (which is handled here:

Capabilities: ['CAPABILITY_IAM', 'CAPABILITY_NAMED_IAM'],
Parameters: [],
Tags: Object.keys(stackTags).map((key) => ({ Key: key, Value: stackTags[key] })),
};
if (templateBody) {
params.TemplateBody = JSON.stringify(templateBody);
}
if (templateUrl) {
params.TemplateURL = templateUrl;
}
if (
(templateUrl &&
this.serverless.service.provider.compiledCloudFormationTemplate &&
this.serverless.service.provider.compiledCloudFormationTemplate.Transform) ||
(templateBody && templateBody.Transform)
) {
params.Capabilities.push('CAPABILITY_AUTO_EXPAND');
}

Question is, should it be added by the Framework or used plugin automatically, or should we rather provide a back door for users to extend this list via configuration settings.

@scanianick @vatsal-kavida what do you think? Why exactly this capability is required in your case?

@scanianick
Copy link
Author

I think it should be a back door for users to extend this list via configuration settings. Since that is the behaviour in cloudformation that you need to add it if needed. So assume it should be the same for serverless framework. But both ways could work.

@medikoo
Copy link
Contributor

medikoo commented Jun 1, 2023

Thanks, in light of that, we probably can introduce provider.stackExtraCapabilities property to host that.

PR's welcome!

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

No branches or pull requests

3 participants