cdk/lib/national-delivery-fulfilment.ts (138 lines of code) (raw):

import { GuScheduledLambda } from '@guardian/cdk'; import type { GuStackProps } from '@guardian/cdk/lib/constructs/core'; import { GuStack } from '@guardian/cdk/lib/constructs/core'; import { GuAllowPolicy, GuRole } from "@guardian/cdk/lib/constructs/iam"; import type { App } from 'aws-cdk-lib'; import { Duration } from 'aws-cdk-lib'; import { Schedule } from 'aws-cdk-lib/aws-events'; import { ArnPrincipal, Effect, PolicyStatement } from 'aws-cdk-lib/aws-iam'; import { Runtime } from 'aws-cdk-lib/aws-lambda'; import { Bucket } from 'aws-cdk-lib/aws-s3'; import { StringParameter } from "aws-cdk-lib/aws-ssm"; import { ComparisonOperator, Metric } from 'aws-cdk-lib/aws-cloudwatch'; import { Topic } from 'aws-cdk-lib/aws-sns'; import { GuAlarm } from "@guardian/cdk/lib/constructs/cloudwatch"; export class NationalDeliveryFulfilment extends GuStack { constructor(scope: App, id: string, props: GuStackProps) { super(scope, id, props); const app = 'national-delivery-fulfilment'; const nationalDeliveryFulfilmentLambda = new GuScheduledLambda( this, 'national-delivery-fulfilment-lambda', { description: 'A lambda to handle fulfilment for national delivery', functionName: `membership-${app}-${this.stage}`, handler: 'national-delivery-fulfilment/index.handler', runtime: Runtime.NODEJS_18_X, memorySize: 1024, fileName: `${app}.zip`, app: app, rules: [{ schedule: Schedule.cron({ day: '*', hour: '*', minute: '30', }), }], monitoringConfiguration: {noMonitoring: true}, timeout: Duration.seconds(15 * 60), // overriding the default duration which is not 15 minutes }, ); const bucketName = `gu-national-delivery-fulfilment-${this.stage.toLowerCase()}` const dataBucket = new Bucket(this, 'DataBucket', { bucketName: bucketName, versioned: true }); nationalDeliveryFulfilmentLambda.addToRolePolicy( new PolicyStatement({ actions: ['s3:PutObject'], effect: Effect.ALLOW, resources: [dataBucket.arnForObjects('*')], }), ); nationalDeliveryFulfilmentLambda.addToRolePolicy( new PolicyStatement({ actions: ['ssm:GetParameter'], effect: Effect.ALLOW, resources: ['*'], }), ); const supplierRoleArn = StringParameter.valueForStringParameter( this, `/national-delivery-fulfilment/${this.stage}/supplierRoleArn`, ); const supplierFulfilmentRole = new GuRole( this, `AllowFulfilmentBucketRole${this.stage}`, { roleName: this.stage == "CODE" ? "SandboxPaperroundAccess" : "ProductionPaperroundAccess", assumedBy: new ArnPrincipal( supplierRoleArn, ), }); supplierFulfilmentRole.attachInlinePolicy( new GuAllowPolicy( this, "AllowFulfilmentBucketPolicy", { actions: [ "s3:ListBucket" ], resources: [ `arn:aws:s3:::${bucketName}` ], } ) ); supplierFulfilmentRole.attachInlinePolicy( new GuAllowPolicy( this, "AllowFulfilmentBucketGetFilesPolicy", { actions: [ "s3:GetObject" ], resources: [ `arn:aws:s3:::${bucketName}/fulfilment/*` ], } ) ); supplierFulfilmentRole.attachInlinePolicy( new GuAllowPolicy( this, "AllowFulfilmentBucketPutFailedDeliveryPolicy", { actions: [ "s3:GetObject", "s3:PutObject" ], resources: [ `arn:aws:s3:::${bucketName}/failed-deliveries/uploads/*` ], } ) ); const errorMetric = new Metric({ namespace: 'AWS/Lambda', metricName: 'Errors', statistic: 'Sum', dimensionsMap: { FunctionName: `membership-national-delivery-fulfilment-${this.stage}`, } }); const snsTopicName = `alarms-handler-topic-${this.stage}`; const isProd = this.stage === 'PROD'; new GuAlarm(this, 'ErrorExecutionAlarm', { app, snsTopicName: snsTopicName, alarmName: `${app}: error`, alarmDescription: `${app}: error while executing lambda`, metric: errorMetric, comparisonOperator: ComparisonOperator.GREATER_THAN_THRESHOLD, threshold: 0, evaluationPeriods: 1, actionsEnabled: isProd, }); } }