UNPKG

osls

Version:

Open-source alternative to Serverless Framework

1,337 lines (1,318 loc) 95.7 kB
'use strict'; const AWS = require('../../aws/sdk-v2'); const BbPromise = require('bluebird'); const _ = require('lodash'); const naming = require('./lib/naming.js'); const fsp = require('fs').promises; const getS3EndpointForRegion = require('./utils/get-s3-endpoint-for-region'); const memoizeeMethods = require('memoizee/methods'); const readline = require('readline'); const { ALB_LISTENER_REGEXP } = require('./package/compile/events/alb/lib/validate'); const d = require('d'); const path = require('path'); const spawnExt = require('child-process-ext/spawn'); const ServerlessError = require('../../serverless-error'); const awsRequest = require('../../aws/request'); const { cfValue } = require('../../utils/aws-schema-get-cf-value'); const reportDeprecatedProperties = require('../../utils/report-deprecated-properties'); const deepSortObjectByKey = require('../../utils/deep-sort-object-by-key'); const { progress, log } = require('@serverless/utils/log'); // AWS SDK v3 infrastructure const AWSClientFactory = require('../../aws/client-factory'); const { createCommand } = require('../../aws/commands'); const { buildClientConfig, shouldUseS3Acceleration } = require('../../aws/config'); const { transformV3Error } = require('../../aws/error-utils'); const isLambdaArn = RegExp.prototype.test.bind(/^arn:[^:]+:lambda:/); const isEcrUri = RegExp.prototype.test.bind( /^\d+\.dkr\.ecr\.[a-z0-9-]+..amazonaws.com\/([^@]+)|([^@:]+@sha256:[a-f0-9]{64})$/ ); function caseInsensitive(str) { return { type: 'string', regexp: new RegExp(`^${str}$`, 'i').toString() }; } function resolveRuntimeManagement(input) { if (typeof input === 'string') { return { mode: input, }; } return input; } const constants = { providerName: 'aws', }; const imageNamePattern = '^[a-z][a-z0-9-_]{1,31}$'; const apiGatewayUsagePlan = { type: 'object', properties: { quota: { type: 'object', properties: { limit: { type: 'integer', minimum: 0 }, offset: { type: 'integer', minimum: 0 }, period: { enum: ['DAY', 'WEEK', 'MONTH'] }, }, additionalProperties: false, }, throttle: { type: 'object', properties: { burstLimit: { type: 'integer', minimum: 0 }, rateLimit: { type: 'integer', minimum: 0 }, }, additionalProperties: false, }, }, additionalProperties: false, }; const impl = { /** * Determine whether the given credentials are valid. It turned out that detecting invalid * credentials was more difficult than detecting the positive cases we know about. Hooray for * whack-a-mole! * @param credentials The credentials to test for validity * @return {boolean} Whether the given credentials were valid */ validCredentials: (credentials) => { let result = false; if (credentials) { if ( // valid credentials loaded (credentials.accessKeyId && credentials.accessKeyId !== 'undefined' && credentials.secretAccessKey && credentials.secretAccessKey !== 'undefined') || // a role to assume has been successfully loaded, the associated STS request has been // sent, and the temporary credentials will be asynchronously delivered. credentials.roleArn ) { result = true; } } return result; }, /** * Add credentials, if present, to the given results * @param results The results to add the given credentials to if they are valid * @param credentials The credentials to validate and add to the results if valid */ addCredentials: (results, credentials) => { if (impl.validCredentials(credentials)) { results.credentials = credentials; } }, /** * Add credentials, if present, from the environment * @param results The results to add environment credentials to * @param prefix The environment variable prefix to use in extracting credentials */ addEnvironmentCredentials: (results, prefix) => { if (prefix) { const environmentCredentials = new AWS.EnvironmentCredentials(prefix); impl.addCredentials(results, environmentCredentials); } }, /** * Add credentials from a profile, if the profile and credentials for it exists * @param results The results to add profile credentials to * @param profile The profile to load credentials from */ addProfileCredentials: (results, profile) => { if (profile) { const params = { profile }; if (process.env.AWS_SHARED_CREDENTIALS_FILE) { params.filename = process.env.AWS_SHARED_CREDENTIALS_FILE; } // Setup a MFA callback for asking the code from the user. params.tokenCodeFn = (mfaSerial, callback) => { const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); rl.question(`Enter MFA code for ${mfaSerial}: `, (answer) => { rl.close(); callback(null, answer); }); }; const profileCredentials = new AWS.SharedIniFileCredentials(params); if ( !( profileCredentials.accessKeyId || profileCredentials.sessionToken || profileCredentials.roleArn ) ) { throw new ServerlessError( `AWS profile "${profile}" doesn't seem to be configured`, 'UNRECOGNIZED_AWS_PROFILE' ); } impl.addCredentials(results, profileCredentials); } }, /** * Add credentials, if present, from a profile that is specified within the environment * @param results The prefix of the profile's declaration in the environment * @param prefix The prefix for the environment variable */ addEnvironmentProfile: (results, prefix) => { if (prefix) { const profile = process.env[`${prefix}_PROFILE`]; impl.addProfileCredentials(results, profile); } }, }; const baseAlbAuthorizerProperties = { onUnauthenticatedRequest: { enum: ['allow', 'authenticate', 'deny'] }, requestExtraParams: { type: 'object', maxProperties: 10, additionalProperties: { type: 'string' }, }, scope: { type: 'string' }, sessionCookieName: { type: 'string' }, sessionTimeout: { type: 'integer', minimum: 0 }, }; const oidcAlbAuthorizer = { type: 'object', properties: { type: { const: 'oidc' }, authorizationEndpoint: { format: 'uri', type: 'string' }, clientId: { type: 'string' }, clientSecret: { type: 'string' }, issuer: { format: 'uri', type: 'string' }, tokenEndpoint: { format: 'uri', type: 'string' }, userInfoEndpoint: { format: 'uri', type: 'string' }, ...baseAlbAuthorizerProperties, }, required: [ 'type', 'authorizationEndpoint', 'clientId', 'issuer', 'tokenEndpoint', 'userInfoEndpoint', ], additionalProperties: false, }; const cognitoAlbAuthorizer = { type: 'object', properties: { type: { const: 'cognito' }, userPoolArn: { $ref: '#/definitions/awsArn' }, userPoolClientId: { type: 'string' }, userPoolDomain: { type: 'string' }, ...baseAlbAuthorizerProperties, }, required: ['type', 'userPoolArn', 'userPoolClientId', 'userPoolDomain'], additionalProperties: false, }; class AwsProvider { constructor(serverless, options) { this.naming = { provider: this }; this.options = options; this.provider = this; // only load plugin in an AWS service context this.serverless = serverless; // Notice: provider.sdk is used by plugins. Do not remove without deprecating first and // offering a reliable alternative this.sdk = AWS; // AWS SDK v3 infrastructure this.clientFactory = null; // Will be initialized when needed this._v3Enabled = process.env.SLS_AWS_SDK_V3 === '1'; // Feature flag for gradual rollout this.serverless.setProvider(constants.providerName, this); this.hooks = { initialize: () => { // Support deploymentBucket configuration as an object const provider = this.serverless.service.provider; if (provider && provider.deploymentBucket) { if (_.isObject(provider.deploymentBucket)) { // store the object in a new variable so that it can be reused later on provider.deploymentBucketObject = provider.deploymentBucket; if (provider.deploymentBucket.name) { // (re)set the value of the deploymentBucket property to the name (which is a string) provider.deploymentBucket = provider.deploymentBucket.name; } else { provider.deploymentBucket = null; } } } reportDeprecatedProperties( 'PROVIDER_IAM_SETTINGS_V3', { 'provider.role': 'provider.iam.role', 'provider.rolePermissionsBoundary': 'provider.iam.role.permissionsBoundary', 'provider.iam.role.permissionBoundary': 'provider.iam.role.permissionsBoundary', 'provider.iamManagedPolicies': 'provider.iam.role.managedPolicies', 'provider.iamRoleStatements': 'provider.iam.role.statements', 'provider.cfnRole': 'provider.iam.deploymentRole', }, { serviceConfig: this.serverless.service } ); }, }; if (this.serverless.service.provider.name === 'aws') { // Below ideally should be in hooks.initialize, but variables resolution depend on this this.serverless.service.provider.region = this.getRegion(); require('../../utils/aws-sdk-patch'); serverless.configSchemaHandler.defineProvider('aws', { definitions: { awsAccountId: { type: 'string', pattern: '^\\d{12}$', }, awsAlbListenerArn: { type: 'string', pattern: ALB_LISTENER_REGEXP.source, }, awsAlexaEventToken: { type: 'string', minLength: 0, maxLength: 256, pattern: '^[a-zA-Z0-9._\\-]+$', }, awsApiGatewayAbbreviatedArn: { type: 'string', pattern: '^execute-api:/', }, awsApiGatewayApiKeys: { type: 'array', items: { anyOf: [ { type: 'string' }, { $ref: '#/definitions/awsApiGatewayApiKeysProperties', }, { type: 'object', maxProperties: 1, additionalProperties: { type: 'array', items: { oneOf: [ { type: 'string' }, { $ref: '#/definitions/awsApiGatewayApiKeysProperties' }, ], }, }, }, ], }, }, awsApiGatewayApiKeysProperties: { type: 'object', properties: { name: { type: 'string' }, value: { type: 'string' }, description: { type: 'string' }, customerId: { type: 'string' }, enabled: { type: 'boolean' }, }, additionalProperties: false, }, awsHttpApiPayload: { type: 'string', enum: ['1.0', '2.0'], }, awsArn: { anyOf: [ { $ref: '#/definitions/awsArnString' }, { $ref: '#/definitions/awsCfFunction' }, ], }, awsArnString: { type: 'string', pattern: '^arn:', }, awsCfArrayInstruction: { anyOf: [ { type: 'array', items: { $ref: '#/definitions/awsCfInstruction' }, }, { $ref: '#/definitions/awsCfSplit' }, ], }, awsSecretsManagerArnString: { type: 'string', pattern: 'arn:[a-z-]+:secretsmanager:[a-z0-9-]+:\\d+:secret:[A-Za-z0-9/_+=.@-]+', }, awsCfFunction: { anyOf: [ { $ref: '#/definitions/awsCfImport' }, { $ref: '#/definitions/awsCfJoin' }, { $ref: '#/definitions/awsCfGetAtt' }, { $ref: '#/definitions/awsCfRef' }, { $ref: '#/definitions/awsCfSub' }, { $ref: '#/definitions/awsCfBase64' }, { $ref: '#/definitions/awsCfToJsonString' }, ], }, awsCfGetAtt: { type: 'object', properties: { 'Fn::GetAtt': { type: 'array', minItems: 2, maxItems: 2, items: { type: 'string', minLength: 1 }, }, }, required: ['Fn::GetAtt'], additionalProperties: false, }, awsCfGetAZs: { type: 'object', properties: { 'Fn::GetAZs': { oneOf: [{ type: 'string', minLength: 1 }, { $ref: '#/definitions/awsCfRef' }], }, }, required: ['Fn::GetAZs'], additionalProperties: false, }, awsCfImport: { type: 'object', properties: { 'Fn::ImportValue': {}, }, additionalProperties: false, required: ['Fn::ImportValue'], }, awsCfIf: { type: 'object', properties: { 'Fn::If': { type: 'array', minItems: 3, maxItems: 3, items: { $ref: '#/definitions/awsCfInstruction' }, }, }, required: ['Fn::If'], additionalProperties: false, }, awsCfImportLocallyResolvable: { type: 'object', properties: { 'Fn::ImportValue': { type: 'string' }, }, additionalProperties: false, required: ['Fn::ImportValue'], }, awsCfInstruction: { anyOf: [{ type: 'string', minLength: 1 }, { $ref: '#/definitions/awsCfFunction' }], }, awsCfJoin: { type: 'object', properties: { 'Fn::Join': { type: 'array', minItems: 2, maxItems: 2, items: [{ type: 'string' }, { type: 'array' }], additionalItems: false, }, }, required: ['Fn::Join'], additionalProperties: false, }, awsCfSelect: { type: 'object', properties: { 'Fn::Select': { type: 'array', minItems: 2, maxItems: 2, items: { anyOf: [ { type: 'number' }, { type: 'string' }, { type: 'array' }, { $ref: '#/definitions/awsCfFindInMap' }, { $ref: '#/definitions/awsCfGetAtt' }, { $ref: '#/definitions/awsCfGetAZs' }, { $ref: '#/definitions/awsCfIf' }, { $ref: '#/definitions/awsCfSplit' }, { $ref: '#/definitions/awsCfRef' }, ], }, }, }, required: ['Fn::Select'], additionalProperties: false, }, awsCfRef: { type: 'object', properties: { Ref: { type: 'string', minLength: 1 }, }, required: ['Ref'], additionalProperties: false, }, awsCfSplit: { type: 'object', properties: { 'Fn::Split': { type: 'array', minItems: 2, maxItems: 2, items: { oneOf: [{ type: 'string' }, { $ref: '#/definitions/awsCfFunction' }], }, }, }, required: ['Fn::Split'], additionalProperties: false, }, awsCfFindInMap: { type: 'object', properties: { 'Fn::FindInMap': { type: 'array', minItems: 3, maxItems: 3, items: { oneOf: [{ type: 'string' }, { $ref: '#/definitions/awsCfFunction' }], }, }, }, required: ['Fn::FindInMap'], additionalProperties: false, }, awsCfSub: { type: 'object', properties: { 'Fn::Sub': {}, }, required: ['Fn::Sub'], additionalProperties: false, }, awsCfBase64: { type: 'object', properties: { 'Fn::Base64': {}, }, }, awsCfToJsonString: { type: 'object', properties: { 'Fn::ToJsonString': { anyOf: [{ type: 'object' }, { type: 'array' }], }, }, required: ['Fn::ToJsonString'], additionalProperties: false, }, awsIamPolicyAction: { type: 'array', items: { type: 'string' } }, awsIamPolicyPrincipal: { anyOf: [ { const: '*' }, { type: 'object', properties: { AWS: { anyOf: [ { const: '*' }, { $ref: '#/definitions/awsCfIf' }, { type: 'array', items: { anyOf: [ { $ref: '#/definitions/awsAccountId' }, { $ref: '#/definitions/awsArn' }, ], }, }, ], }, Federated: { type: 'array', items: { type: 'string' } }, Service: { type: 'array', items: { type: 'string' } }, CanonicalUser: { type: 'array', items: { type: 'string' } }, }, additionalProperties: false, }, ], }, awsIamPolicyResource: { anyOf: [ { const: '*' }, { $ref: '#/definitions/awsArn' }, { $ref: '#/definitions/awsLogicalResourceId' }, { type: 'array', items: { anyOf: [ { const: '*' }, { $ref: '#/definitions/awsArn' }, { $ref: '#/definitions/awsLogicalResourceId' }, ], }, }, ], }, // Definition of Statement taken from https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_grammar.html#policies-grammar-bnf awsIamPolicyStatements: { type: 'array', items: { type: 'object', properties: { Sid: { type: 'string' }, Effect: { enum: ['Allow', 'Deny'] }, Action: { $ref: '#/definitions/awsIamPolicyAction' }, NotAction: { $ref: '#/definitions/awsIamPolicyAction' }, Principal: { $ref: '#/definitions/awsIamPolicyPrincipal' }, NotPrincipal: { $ref: '#/definitions/awsIamPolicyPrincipal' }, Resource: { $ref: '#/definitions/awsIamPolicyResource' }, NotResource: { $ref: '#/definitions/awsIamPolicyResource' }, Condition: { type: 'object' }, }, additionalProperties: false, allOf: [ { required: ['Effect'] }, { oneOf: [{ required: ['Action'] }, { required: ['NotAction'] }] }, { oneOf: [{ required: ['Resource'] }, { required: ['NotResource'] }] }, ], }, }, awsKmsArn: { anyOf: [ { $ref: '#/definitions/awsCfFunction' }, { type: 'string', pattern: '^arn:aws[a-z-]*:kms' }, ], }, awsLambdaArchitecture: { enum: ['arm64', 'x86_64'] }, awsLambdaEnvironment: { type: 'object', patternProperties: { '^[A-Za-z_][a-zA-Z0-9_]*$': { anyOf: [ { const: '' }, { $ref: '#/definitions/awsCfInstruction' }, { $ref: '#/definitions/awsCfIf' }, { $ref: '#/definitions/awsCfSelect' }, ], }, }, additionalProperties: false, }, awsLambdaLayers: { type: 'array', items: { $ref: '#/definitions/awsArn' }, }, awsLambdaMemorySize: { type: 'integer', minimum: 128, maximum: 10240 }, awsLambdaRole: { anyOf: [ { type: 'string', minLength: 1 }, { $ref: '#/definitions/awsCfSub' }, { $ref: '#/definitions/awsCfImport' }, { $ref: '#/definitions/awsCfGetAtt' }, ], }, awsLambdaRuntime: { enum: [ 'dotnet6', 'dotnet8', 'go1.x', 'java21', 'java17', 'java11', 'java8', 'java8.al2', 'nodejs14.x', 'nodejs16.x', 'nodejs18.x', 'nodejs20.x', 'nodejs22.x', 'provided', 'provided.al2', 'provided.al2023', 'python3.7', 'python3.8', 'python3.9', 'python3.10', 'python3.11', 'python3.12', 'python3.13', 'ruby2.7', 'ruby3.2', 'ruby3.3', 'ruby3.4', ], }, awsLambdaRuntimeManagement: { oneOf: [ { enum: ['auto', 'onFunctionUpdate'] }, { type: 'object', properties: { mode: { enum: ['auto', 'onFunctionUpdate', 'manual'] }, arn: { $ref: '#/definitions/awsArn' }, }, additionalProperties: false, }, ], }, awsLambdaTimeout: { type: 'integer', minimum: 1, maximum: 900 }, awsLambdaTracing: { anyOf: [{ enum: ['Active', 'PassThrough'] }, { type: 'boolean' }] }, awsLambdaVersioning: { type: 'boolean' }, awsLambdaVpcConfig: { type: 'object', properties: { ipv6AllowedForDualStack: { type: 'boolean' }, securityGroupIds: { anyOf: [ { type: 'array', items: { anyOf: [ { $ref: '#/definitions/awsCfInstruction' }, { $ref: '#/definitions/awsCfIf' }, ], }, maxItems: 5, }, { $ref: '#/definitions/awsCfSplit' }, { $ref: '#/definitions/awsCfFindInMap' }, ], }, subnetIds: { anyOf: [ { type: 'array', items: { anyOf: [ { $ref: '#/definitions/awsCfInstruction' }, { $ref: '#/definitions/awsCfIf' }, ], }, maxItems: 16, }, { $ref: '#/definitions/awsCfSplit' }, { $ref: '#/definitions/awsCfFindInMap' }, ], }, }, additionalProperties: false, required: ['securityGroupIds', 'subnetIds'], }, awsLogicalResourceId: { type: 'string', pattern: '^[#A-Za-z0-9-_./]+[*]?$', }, awsLogGroupName: { type: 'string', pattern: '^[/#A-Za-z0-9-_.]+$', }, awsLogRetentionInDays: { type: 'number', // https://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_PutRetentionPolicy.html#API_PutRetentionPolicy_RequestSyntax enum: [ 1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365, 400, 545, 731, 1827, 2192, 2557, 2922, 3288, 3653, ], }, awsLogDataProtectionPolicy: { type: 'object', properties: { Name: { type: 'string' }, Description: { type: 'string' }, Version: { type: 'string' }, Statement: { type: 'array' }, }, additionalProperties: false, required: ['Name', 'Version', 'Statement'], }, awsResourceCondition: { type: 'string' }, awsResourceDependsOn: { type: 'array', items: { type: 'string' } }, awsResourcePolicyResource: { anyOf: [ { const: '*' }, { $ref: '#/definitions/awsArn' }, // API Gateway Resource Policy resource property abbreviated syntax - https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-resource-policies-examples.html { $ref: '#/definitions/awsApiGatewayAbbreviatedArn' }, { type: 'array', items: { anyOf: [ { const: '*' }, { $ref: '#/definitions/awsArn' }, // API Gateway Resource Policy resource property abbreviated syntax - https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-resource-policies-examples.html { $ref: '#/definitions/awsApiGatewayAbbreviatedArn' }, ], }, }, ], }, awsResourcePolicyStatements: { type: 'array', items: { type: 'object', properties: { Sid: { type: 'string' }, Effect: { enum: ['Allow', 'Deny'] }, Action: { $ref: '#/definitions/awsIamPolicyAction' }, NotAction: { $ref: '#/definitions/awsIamPolicyAction' }, Principal: { $ref: '#/definitions/awsIamPolicyPrincipal' }, NotPrincipal: { $ref: '#/definitions/awsIamPolicyPrincipal' }, Resource: { $ref: '#/definitions/awsResourcePolicyResource' }, NotResource: { $ref: '#/definitions/awsResourcePolicyResource' }, Condition: { type: 'object' }, }, additionalProperties: false, allOf: [ { required: ['Effect'] }, { oneOf: [{ required: ['Action'] }, { required: ['NotAction'] }] }, { oneOf: [{ required: ['Resource'] }, { required: ['NotResource'] }] }, ], }, }, awsResourceTags: { type: 'object', patternProperties: { '^(?!aws:)[\\w./=+:\\-_\\x20]{1,128}$': { type: 'string', maxLength: 256, }, }, additionalProperties: false, }, awsS3BucketName: { type: 'string', // pattern sourced from https://stackoverflow.com/questions/50480924/regex-for-s3-bucket-name pattern: '(?!^(\\d{1,3}\\.){3}\\d{1,3}$)(^(([a-z0-9]|[a-z0-9][a-z0-9-]*[a-z0-9])\\.)*([a-z0-9]|[a-z0-9][a-z0-9-]*[a-z0-9])$)', minLength: 3, maxLength: 63, }, ecrImageUri: { type: 'string', pattern: '^\\d+\\.dkr\\.ecr\\.[a-z0-9-]+..amazonaws.com\\/([^@]+)|([^@:]+@sha256:[a-f0-9]{64})$', }, filterPatterns: { type: 'array', minItems: 1, maxItems: 10, items: { type: 'object' }, }, }, provider: { properties: { alb: { type: 'object', properties: { targetGroupPrefix: { type: 'string', maxLength: 16 }, authorizers: { type: 'object', additionalProperties: { anyOf: [oidcAlbAuthorizer, cognitoAlbAuthorizer], }, }, }, additionalProperties: false, }, apiGateway: { type: 'object', properties: { apiKeys: { $ref: '#/definitions/awsApiGatewayApiKeys' }, apiKeySourceType: { anyOf: ['HEADER', 'AUTHORIZER'].map(caseInsensitive), }, binaryMediaTypes: { type: 'array', items: { type: 'string', pattern: '^\\S+\\/\\S+$' }, }, description: { type: 'string' }, disableDefaultEndpoint: { type: 'boolean' }, metrics: { type: 'boolean' }, minimumCompressionSize: { type: 'integer', minimum: 0, maximum: 10485760 }, resourcePolicy: { $ref: '#/definitions/awsResourcePolicyStatements' }, restApiId: { $ref: '#/definitions/awsCfInstruction' }, restApiResources: { anyOf: [ { type: 'array', items: { type: 'object', properties: { path: { type: 'string' }, resourceId: { type: 'string' }, }, required: [], additionalProperties: false, }, }, { type: 'object' }, ], }, restApiRootResourceId: { $ref: '#/definitions/awsCfInstruction' }, request: { type: 'object', properties: { schemas: { type: 'object', additionalProperties: { type: 'object', properties: { schema: { type: 'object' }, name: { type: 'string' }, description: { type: 'string' }, }, required: ['schema'], additionalProperties: false, }, }, }, additionalProperties: false, }, shouldStartNameWithService: { type: 'boolean' }, stage: { type: 'string' }, timeoutInMillis: { type: 'integer', minimum: 50 }, usagePlan: { anyOf: [ apiGatewayUsagePlan, { type: 'array', items: { type: 'object', additionalProperties: apiGatewayUsagePlan, maxProperties: 1, }, }, ], }, websocketApiId: { $ref: '#/definitions/awsCfInstruction' }, }, additionalProperties: false, }, apiName: { type: 'string' }, architecture: { $ref: '#/definitions/awsLambdaArchitecture' }, cfnRole: { $ref: '#/definitions/awsArn' }, cloudFront: { type: 'object', properties: { cachePolicies: { type: 'object', additionalProperties: { type: 'object', properties: { Comment: { type: 'string' }, DefaultTTL: { type: 'integer', minimum: 0 }, MaxTTL: { type: 'integer', minimum: 0 }, MinTTL: { type: 'integer', minimum: 0 }, ParametersInCacheKeyAndForwardedToOrigin: { type: 'object', properties: { CookiesConfig: { type: 'object', properties: { CookieBehavior: { enum: ['none', 'whitelist', 'allExcept', 'all'] }, Cookies: { type: 'array', items: { type: 'string' } }, }, required: ['CookieBehavior'], additionalProperties: false, }, EnableAcceptEncodingBrotli: { type: 'boolean' }, EnableAcceptEncodingGzip: { type: 'boolean' }, HeadersConfig: { type: 'object', properties: { HeaderBehavior: { enum: ['none', 'whitelist'] }, Headers: { type: 'array', items: { type: 'string' } }, }, required: ['HeaderBehavior'], additionalProperties: false, }, QueryStringsConfig: { type: 'object', properties: { QueryStringBehavior: { enum: ['none', 'whitelist', 'allExcept', 'all'], }, QueryStrings: { type: 'array', items: { type: 'string' } }, }, required: ['QueryStringBehavior'], additionalProperties: false, }, }, required: [ 'CookiesConfig', 'EnableAcceptEncodingGzip', 'HeadersConfig', 'QueryStringsConfig', ], additionalProperties: false, }, }, required: [ 'DefaultTTL', 'MaxTTL', 'MinTTL', 'ParametersInCacheKeyAndForwardedToOrigin', ], additionalProperties: false, }, }, }, additionalProperties: false, }, deploymentBucket: { anyOf: [ { $ref: '#/definitions/awsS3BucketName' }, { type: 'object', properties: { blockPublicAccess: { type: 'boolean' }, skipPolicySetup: { type: 'boolean' }, maxPreviousDeploymentArtifacts: { type: 'integer', minimum: 0 }, name: { $ref: '#/definitions/awsS3BucketName' }, versioning: { type: 'boolean' }, serverSideEncryption: { enum: ['AES256', 'aws:kms'] }, sseCustomerAlgorithim: { type: 'string' }, sseCustomerKey: { type: 'string' }, sseCustomerKeyMD5: { type: 'string' }, sseKMSKeyId: { type: 'string' }, tags: { $ref: '#/definitions/awsResourceTags' }, }, additionalProperties: false, }, ], }, deploymentPrefix: { type: 'string' }, disableRollback: { type: 'boolean' }, endpointType: { anyOf: ['REGIONAL', 'EDGE', 'PRIVATE'].map(caseInsensitive), }, environment: { $ref: '#/definitions/awsLambdaEnvironment' }, eventBridge: { type: 'object', properties: { useCloudFormation: { type: 'boolean' }, }, additionalProperties: false, }, httpApi: { type: 'object', properties: { authorizers: { type: 'object', additionalProperties: { oneOf: [ { type: 'object', properties: { type: { const: 'jwt' }, name: { type: 'string' }, identitySource: { $ref: '#/definitions/awsCfInstruction' }, issuerUrl: { $ref: '#/definitions/awsCfInstruction' }, audience: { anyOf: [ { $ref: '#/definitions/awsCfInstruction' }, { type: 'array', items: { $ref: '#/definitions/awsCfInstruction' }, }, ], }, }, required: ['identitySource', 'issuerUrl', 'audience'], additionalProperties: false, }, { type: 'object', properties: { type: { const: 'request' }, name: { type: 'string' }, functionName: { type: 'string' }, functionArn: { $ref: '#/definitions/awsCfInstruction' }, managedExternally: { type: 'boolean' }, resultTtlInSeconds: { type: 'integer', minimum: 0, maximum: 3600 }, enableSimpleResponses: { type: 'boolean' }, payloadVersion: { $ref: '#/definitions/awsHttpApiPayload' }, identitySource: { anyOf: [ { $ref: '#/definitions/awsCfInstruction' }, { type: 'array', items: { $ref: '#/definitions/awsCfInstruction' }, }, ], }, }, required: ['type'], }, ], }, }, cors: { anyOf: [ { type: 'boolean' }, { type: 'object', properties: { allowCredentials: { type: 'boolean' }, allowedHeaders: { type: 'array', items: { type: 'string' } }, allowedMethods: { type: 'array', items: { type: 'string' } }, allowedOrigins: { type: 'array', items: { type: 'string' } }, exposedResponseHeaders: { type: 'array', items: { type: 'string' } }, maxAge: { type: 'integer', minimum: 0 }, }, additionalProperties: false, }, ], }, id: { anyOf: [ { type: 'string' }, { $ref: '#/definitions/awsCfImportLocallyResolvable' }, ], }, name: { type: 'string' }, description: { type: 'string' }, payload: { type: 'string' }, metrics: { type: 'boolean' }, useProviderTags: { const: true }, disableDefaultEndpoint: { type: 'boolean' }, shouldStartNameWithService: { type: 'boolean' }, }, additionalProperties: false, }, iam: { type: 'object', properties: { role: { anyOf: [ { $ref: '#/definitions/awsLambdaRole' }, { type: 'object', properties: { name: { type: 'string', pattern: '^[A-Za-z0-9/_+=,.@-]{1,64}$', }, path: { type: 'string', pattern: '(^\\/$)|(^\\/[\u0021-\u007f]+\\/$)', }, managedPolicies: { type: 'array', items: { $ref: '#/definitions/awsArn' }, }, statements: { $ref: '#/definitions/awsIamPolicyStatements' }, permissionBoundary: { $ref: '#/definitions/awsArn' }, permissionsBoundary: { $ref: '#/definitions/awsArn' }, tags: { $ref: '#/definitions/awsResourceTags' }, }, additionalProperties: false, }, ], }, deploymentRole: { $ref: '#/definitions/awsArn' }, }, additionalProperties: false, }, iamManagedPolicies: { type: 'array', items: { $ref: '#/definitions/awsArn' } }, iamRoleStatements: { $ref: '#/definitions/awsIamPolicyStatements' }, ecr: { type: 'object', properties: { scanOnPush: { type: 'boolean' }, images: { type: 'object', patternProperties: { [imageNamePattern]: { anyOf: [ { type: 'object', properties: { uri: { $ref: '#/definitions/ecrImageUri' }, path: { type: 'string' }, file: { type: 'string' }, buildArgs: { type: 'object', additionalProperties: { type: 'string' } }, cacheFrom: { type: 'array', items: { type: 'string' } }, platform: { type: 'string' }, provenance: { type: 'string' }, }, additionalProperties: false, }, { type: 'string', }, ], }, }, }, }, required: ['images'], additionalProperties: false, }, kmsKeyArn: { $ref: '#/definitions/awsKmsArn' }, lambdaHashingVersion: { type: 'string', enum: ['20200924', '20201221'], }, layers: { $ref: '#/definitions/awsLambdaLayers' }, logRetentionInDays: { $ref: '#/definitions/awsLogRetentionInDays', }, logDataProtectionPolicy: { $ref: '#/definitions/awsLogDataProtectionPolicy', }, logs: { type: 'object', properties: { frameworkLambda: { type: 'boolean' }, httpApi: { anyOf: [ { type: 'boolean' }, { type: 'object', properties: { format: { type: 'string' }, }, additionalProperties: false, }, ], }, restApi: { anyOf: [ { type: 'boolean' }, { type: 'object', properties: { accessLogging: { type: 'boolean' }, executionLogging: { type: 'boolean' }, format: { type: 'string' }, fullExecutionData: { type: 'boolean' }, level: { enum: ['INFO', 'ERROR'] }, role: { $ref: '#/definitions/awsArn' }, roleManagedExternally: { type: 'boolean' }, }, additionalProperties: false, }, ], }, websocket: { anyOf: [ { type: 'boolean' }, { type: 'object', properties: { accessLogging: { type: 'boolean' }, executionLogging: { type: 'boolean' }, format: { type: 'string' }, fullExecutionData: { type: 'boolean' }, level: { enum: ['INFO', 'ERROR'] }, }, additionalProperties: false, }, ], }, }, }, memorySize: { $ref: '#/definitions/awsLambdaMemorySize' }, notificationArns: { type: 'array', items: { $ref: '#/definitions/awsArnString' } }, profile: { type: 'string' }, region: { enum: [ 'us-east-1', 'us-east-2', 'us-gov-east-1', 'us-gov-west-1', 'us-iso-east-1', 'us-iso-west-1', 'us-isob-east-1', 'us-west-1', 'us-west-2', 'af-south-1', 'ap-east-1', 'ap-northeast-1', 'ap-northeast-2', 'ap-northeast-3', 'ap-south-1', 'ap-south-2', 'ap-southeast-1', 'ap-southeast-2', 'ap-southeast-3', 'ap-southeast-4', 'ca-central-1', 'cn-north-1', 'cn-northwest-1', 'eu-central-1', 'eu-central-2', 'eu-north-1', 'eu-south-1', 'eu-south-2', 'eu-west-1', 'eu-west-2', 'eu-west-3', 'il-central-1', 'me-central-1', 'me-south-1', 'sa-east-1', ], }, role: { $ref: '#/definitions/awsLambdaRole' }, rolePermissionsBoundary: { $ref: '#/definitions/awsArnString' }, rollbackConfiguration: { type: 'object', properties: { RollbackTriggers: { type: 'array', items: { type: 'object', properties: { Arn: { $ref: '#/definitions/awsArnString' }, Type: { const: 'AWS::CloudWatch::Alarm' }, }, additionalProperties: false, required: ['Arn', 'Type'], }, }, MonitoringTimeInMinutes: { type: 'integer', minimum: 0 }, }, additionalProperties: false, }, runtime: { $ref: '#/definitions/awsLambdaRuntime' }, runtimeManagement: { $ref: '#/definitions/awsLambdaRuntimeManagement' }, deploymentMethod: { enum: ['changesets', 'direct'] }, s3: { type: 'object', additionalProperties: require('./package/compile/events/s3/config-schema'), }, stage: { $ref: '#/definitions/stage' }, stackName: { type: 'string', pattern: '^[a-zA-Z][a-zA-Z0-9-]*$', maxLength: 128, }, stackParameters: { type: 'array', items: { type: 'object', properties: { ParameterKey: { type: 'string' }, ParameterValue: { type: 'string' }, UsePreviousValue: { type: 'boolean' }, ResolvedValue: { type: 'string' }, }, additionalProperties: false, }, }, stackPolicy: { $ref: '#/definitions/awsIamPolicyStatements' }, stackTags: { $ref: '#/definitions/awsResourceTags' }, tags: { $ref: '#/definitions/awsResourceTags' }, timeout: { $ref: '#/definitions/awsLambdaTimeout' }, tracing: { type: 'object', properties: { apiGateway: { type: 'boolean' }, lambda: { $ref: '#/definitions/awsLambdaTracing' }, }, additionalProperties: false, }, vpc: { $ref: '#/definitions/awsLambdaVpcConfig' }, vpcEndpointIds: { $ref: '#/definitions/awsCfArrayInstruction' }, versionFunctions: { $ref: '#/definitions/awsLambdaVersioning' }, websocket: { type: 'object', properties: { useProviderTags: { type: 'boolean' }, }, additionalP