diff --git a/src/cdk/add-lambda-resource.ts b/src/cdk/add-lambda-resource.ts index 8d0cb05..4bbee9d 100644 --- a/src/cdk/add-lambda-resource.ts +++ b/src/cdk/add-lambda-resource.ts @@ -1,5 +1,6 @@ +import type {SharedConstructs} from './create-shared-constructs.js'; import type {LambdaRoute, StackConfig} from '../read-stack-config.js'; -import type {Stack, aws_lambda} from 'aws-cdk-lib'; +import type {aws_lambda} from 'aws-cdk-lib'; import {addCorsPreflight} from './add-cors-preflight.js'; import {createLambdaFunction} from './create-lambda-function.js'; @@ -8,10 +9,10 @@ import {aws_apigateway} from 'aws-cdk-lib'; export function addLambdaResource( stackConfig: StackConfig, route: LambdaRoute, - stack: Stack, - restApi: aws_apigateway.RestApiBase, - requestAuthorizer: aws_apigateway.IAuthorizer | undefined, + constructs: SharedConstructs, ): aws_lambda.FunctionBase { + const {restApi, requestAuthorizer} = constructs; + const { httpMethod, publicPath, @@ -30,7 +31,7 @@ export function addLambdaResource( .filter(([, {cacheKey}]) => cacheKey) .map(([parameterName]) => `method.request.querystring.${parameterName}`); - const lambdaFunction = createLambdaFunction(stackConfig, route, stack); + const lambdaFunction = createLambdaFunction(stackConfig, route, constructs); const integration = new aws_apigateway.LambdaIntegration(lambdaFunction, { cacheKeyParameters, diff --git a/src/cdk/add-s3-resource.ts b/src/cdk/add-s3-resource.ts index 9346d6d..8690d03 100644 --- a/src/cdk/add-s3-resource.ts +++ b/src/cdk/add-s3-resource.ts @@ -1,5 +1,6 @@ +import type {SharedConstructs} from './create-shared-constructs.js'; import type {S3Route} from '../read-stack-config.js'; -import type {aws_iam, aws_s3} from 'aws-cdk-lib'; +import type {aws_iam} from 'aws-cdk-lib'; import {addCorsPreflight} from './add-cors-preflight.js'; import {aws_apigateway} from 'aws-cdk-lib'; @@ -7,11 +8,9 @@ import {join} from 'path'; export function addS3Resource( route: S3Route, - restApi: aws_apigateway.RestApiBase, - bucket: aws_s3.IBucket, - bucketReadRole: aws_iam.IRole, - requestAuthorizer: aws_apigateway.IAuthorizer | undefined, + constructs: SharedConstructs, ): void { + const {restApi, bucket, bucketReadRole, requestAuthorizer} = constructs; const {type, publicPath, path, authenticationEnabled, corsEnabled} = route; if (authenticationEnabled && !requestAuthorizer) { diff --git a/src/cdk/create-lambda-function.ts b/src/cdk/create-lambda-function.ts index 3d75fbf..699d3bf 100644 --- a/src/cdk/create-lambda-function.ts +++ b/src/cdk/create-lambda-function.ts @@ -1,5 +1,5 @@ +import type {SharedConstructs} from './create-shared-constructs.js'; import type {LambdaRoute, StackConfig} from '../read-stack-config.js'; -import type {Stack} from 'aws-cdk-lib'; import {getDomainName} from '../utils/get-domain-name.js'; import {getHash} from '../utils/get-hash.js'; @@ -12,8 +12,10 @@ const maxTimeoutInSeconds = 28; export function createLambdaFunction( stackConfig: StackConfig, route: LambdaRoute, - stack: Stack, + constructs: SharedConstructs, ): aws_lambda.FunctionBase { + const {lambdaServiceRole, stack} = constructs; + const { httpMethod, publicPath, @@ -60,6 +62,7 @@ export function createLambdaFunction( runtime: aws_lambda.Runtime.NODEJS_18_X, tracing: aws_lambda.Tracing.PASS_THROUGH, logRetention: aws_logs.RetentionDays.TWO_WEEKS, + role: lambdaServiceRole, }, ); } diff --git a/src/cdk/create-lambda-service-role.ts b/src/cdk/create-lambda-service-role.ts new file mode 100644 index 0000000..aeab0e7 --- /dev/null +++ b/src/cdk/create-lambda-service-role.ts @@ -0,0 +1,15 @@ +import type {Stack} from 'aws-cdk-lib'; + +import {aws_iam} from 'aws-cdk-lib'; + +export function createLambdaServiceRole(stack: Stack): aws_iam.IRole { + return new aws_iam.Role(stack, `LambdaServiceRole`, { + assumedBy: new aws_iam.ServicePrincipal(`lambda.amazonaws.com`), + managedPolicies: [ + aws_iam.ManagedPolicy.fromAwsManagedPolicyName( + `service-role/AWSLambdaBasicExecutionRole`, + ), + aws_iam.ManagedPolicy.fromAwsManagedPolicyName(`AWSXrayWriteOnlyAccess`), + ], + }); +} diff --git a/src/cdk/create-shared-constructs.ts b/src/cdk/create-shared-constructs.ts new file mode 100644 index 0000000..edf3adc --- /dev/null +++ b/src/cdk/create-shared-constructs.ts @@ -0,0 +1,34 @@ +import type {StackConfig} from '../read-stack-config.js'; +import type {Stack, aws_apigateway, aws_iam, aws_s3} from 'aws-cdk-lib'; + +import {createBucketReadRole} from './create-bucket-read-role.js'; +import {createBucket} from './create-bucket.js'; +import {createLambdaServiceRole} from './create-lambda-service-role.js'; +import {createRequestAuthorizer} from './create-request-authorizer.js'; +import {createRestApi} from './create-rest-api.js'; +import {createStack} from './create-stack.js'; + +export interface SharedConstructs { + readonly bucket: aws_s3.IBucket; + readonly bucketReadRole: aws_iam.IRole; + readonly lambdaServiceRole: aws_iam.IRole; + readonly requestAuthorizer: aws_apigateway.IAuthorizer | undefined; + readonly restApi: aws_apigateway.RestApiBase; + readonly stack: Stack; +} + +export function createSharedConstructs( + stackConfig: StackConfig, +): SharedConstructs { + const stack = createStack(stackConfig); + const bucket = createBucket(stack); + + return { + bucket, + bucketReadRole: createBucketReadRole(stack, bucket), + lambdaServiceRole: createLambdaServiceRole(stack), + requestAuthorizer: createRequestAuthorizer(stackConfig, stack), + restApi: createRestApi(stackConfig, stack), + stack, + }; +} diff --git a/src/synthesize-command.ts b/src/synthesize-command.ts index be0a966..fa88b18 100644 --- a/src/synthesize-command.ts +++ b/src/synthesize-command.ts @@ -2,11 +2,7 @@ import type {CommandModule} from 'yargs'; import {addLambdaResource} from './cdk/add-lambda-resource.js'; import {addS3Resource} from './cdk/add-s3-resource.js'; -import {createBucketReadRole} from './cdk/create-bucket-read-role.js'; -import {createBucket} from './cdk/create-bucket.js'; -import {createRequestAuthorizer} from './cdk/create-request-authorizer.js'; -import {createRestApi} from './cdk/create-rest-api.js'; -import {createStack} from './cdk/create-stack.js'; +import {createSharedConstructs} from './cdk/create-shared-constructs.js'; import {readStackConfig} from './read-stack-config.js'; const commandName = `synthesize`; @@ -25,31 +21,20 @@ export const synthesizeCommand: CommandModule<{}, {}> = { handler: async (): Promise => { const stackConfig = await readStackConfig(); - const stack = createStack(stackConfig); - const restApi = createRestApi(stackConfig, stack); - const bucket = createBucket(stack); - const bucketReadRole = createBucketReadRole(stack, bucket); - const requestAuthorizer = createRequestAuthorizer(stackConfig, stack); + const constructs = createSharedConstructs(stackConfig); + const {stack, restApi} = constructs; for (const route of stackConfig.routes) { if (route.type === `function`) { const lambdaFunction = addLambdaResource( stackConfig, route, - stack, - restApi, - requestAuthorizer, + constructs, ); route.onSynthesize?.({stack, restApi, lambdaFunction}); } else { - addS3Resource( - route, - restApi, - bucket, - bucketReadRole, - requestAuthorizer, - ); + addS3Resource(route, constructs); } }