Skip to content

Best practices for building company default constructs #3235

Description

@lapair

Having built a few production stacks we are seeing patterns in the component parts we use, and want to enforce internal best practices: timeouts / env values / roles etc as well as keep actual stack logic as small/focused as possible. I was discussing this with @eladb on gitter...

I've been playing with something like: lib/our-company/lambda.ts (with a lib/our-company/index.ts so we can import all constructs with a simple single import):

interface OurCompanyILamdaProps {
    assetPath: string, // what are we deploying
    environment?: any,
    handler?: string,
    description?: string,
    timeout?: number
}

export class Lambda extends cdk.Construct {

    public readonly function: lambda.Function;

    constructor(scope: cdk.Construct, id: string, props: OurCompanyILamdaProps) {
        super(scope, id);

        let env: any = props.environment ? props.environment : {};

        // make sure we always have the ENV available to the lambda function.
        env.DEPLOY_ENV = process.env.DEPLOY_ENV; 

        // construct ID needs to be unique
        let constructId = id + env.DEPLOY_ENV;

        let lam = new lambda.Function(scope, constructId, {
            code: lambda.Code.asset(props.assetPath),
            handler: props.handler ? props.handler : "index.handler",
            runtime: lambda.Runtime.NODEJS_10_X,
            environment: props.environment,
            description: props.description,
            timeout: cdk.Duration.seconds(props.timeout ? props.timeout : 60)
        })
 
       // Do stuff.. 
       // if prop.allowAccessSecrets ... create role and assign to lambda
       // if prop.addMonitoring ...
       // if prop.someThingElse ...

        this.function = lam;
    }
}

Then assuming stacks/NAME/index.ts:

        import * as OurCompany from '../constructs/';

        let lambFunction = new OurCompany.Lambda(this, 'aThing', {
            assetPath: __dirname + '/lambda/function_a/',
            environment: environment
        }).function; // note .function here to get the underlying construct if needed
        aDynamoDBTable.grantReadWriteData(lambFunction);

Other sorts of defaults we are thinking of:

  • All queues get a DLQ
  • Monitoring / alerts
  • Permissions to secrets (by creating specific role), but just pass in the secret string
  • Backup settings on DynamoDB table
  • possibly tagging - though I know you should be able to do that at stack level, last time I tried it hasn't worked.

Additionally we could build feature factory constructs... 'swagger file + lambda functions' = apigateway (I've basically used https://gist.github.com/abbottdev/17379763ebc14a5ecbf2a111ffbcdd86 - from #1461 - and mundged it to hide the implementation of parsing the swagger file).

Some of this will include differences we want between production and development environments - but the goal being standards and best practices (for our company) over all.

Feedback / thoughts would be most appreciated.

Metadata

Metadata

Assignees

Labels

@aws-cdk/coreRelated to core CDK functionalityeffort/mediumMedium work item – several days of effortfeature-requestA feature should be added or improved.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions