Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@
"github.vscode-pull-request-github",
"streetsidesoftware.code-spell-checker",
"timonwong.shellcheck",
"github.vscode-github-actions"
"github.vscode-github-actions",
"dbaeumer.vscode-eslint",
"vitest.explorer"
],
"settings": {
"cSpell.words": [
Expand Down
5 changes: 4 additions & 1 deletion .vscode/eps-cdk-utils.code-workspace
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,10 @@
".vscode"
],
"eslint.useFlatConfig": true,
"eslint.format.enable": true
"eslint.format.enable": true,
"[typescript]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
}
},
"extensions": {
"recommendations": [
Expand Down
70 changes: 57 additions & 13 deletions packages/cdkConstructs/src/constructs/PythonLambdaFunction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import {
ManagedPolicy,
PolicyStatement,
Role,
IManagedPolicy
IManagedPolicy,
IRole
} from "aws-cdk-lib/aws-iam"
import {
Architecture,
Expand All @@ -18,6 +19,8 @@ import {
import {join} from "node:path"
import {createSharedLambdaResources} from "./lambdaSharedResources"
import {addSuppressions} from "../utils/helpers"
import {IKey} from "aws-cdk-lib/aws-kms"
import {CfnDeliveryStream} from "aws-cdk-lib/aws-kinesisfirehose"

export interface PythonLambdaFunctionProps {
/**
Expand All @@ -42,7 +45,7 @@ export interface PythonLambdaFunctionProps {
/**
* A map of environment variables to set for the lambda function.
*/
readonly environmentVariables?: {[key: string]: string}
readonly environmentVariables?: { [key: string]: string }
/**
* Optional additional IAM policies to attach to role the lambda executes as.
*/
Expand Down Expand Up @@ -80,16 +83,45 @@ export interface PythonLambdaFunctionProps {
* @default Architecture.X86_64
*/
readonly architecture?: Architecture
/**
* Any files to exclude from the Lambda asset bundle.
* Defaults to these files
* "tests",
* "pytest.ini",
* ".vscode",
* "__pycache__",
* "*.pyc"
*/
/**
* Any files to exclude from the Lambda asset bundle.
* Defaults to these files
* "tests",
* "pytest.ini",
* ".vscode",
* "__pycache__",
* "*.pyc"
*/
readonly excludeFromAsset?: Array<string>
/**
* Optional KMS key for encrypting CloudWatch Logs.
* If not provided, the value is imported from account resources export.
*/
readonly cloudWatchLogsKmsKey?: IKey
/**
* Optional IAM policy for allowing CloudWatch to use the KMS key for encrypting logs.
* If not provided, the value is imported from account resources export.
*/
readonly cloudwatchEncryptionKMSPolicy?: IManagedPolicy
/**
* Optional firehose delivery stream for forwarding logs to Splunk.
* If not provided, the value is imported from account resources export.
*/
readonly splunkDeliveryStream?: CfnDeliveryStream
/**
* Optional IAM role for the subscription filter that forwards logs to Splunk.
* If not provided, the value is imported from account resources export.
*/
readonly splunkSubscriptionFilterRole?: IRole
/**
* Optional IAM policy for allowing lambdas to use Lambda Insights log groups and streams.
* If not provided, the value is imported from account resources export.
*/
readonly lambdaInsightsLogGroupPolicy?: IManagedPolicy
/**
* Whether to create a subscription filter on the Lambda log group to forward logs to Splunk. Defaults to true.
*/
readonly addSplunkSubscriptionFilter?: boolean

}

Expand Down Expand Up @@ -185,14 +217,26 @@ export class PythonLambdaFunction extends Construct {
".vscode",
"__pycache__",
"*.pyc"
]
],
cloudWatchLogsKmsKey,
cloudwatchEncryptionKMSPolicy,
splunkDeliveryStream,
splunkSubscriptionFilterRole,
lambdaInsightsLogGroupPolicy,
addSplunkSubscriptionFilter
} = props

const {logGroup, role, insightsLayer} = createSharedLambdaResources(this, {
functionName,
logRetentionInDays,
additionalPolicies,
architecture
architecture,
cloudWatchLogsKmsKey,
cloudwatchEncryptionKMSPolicy,
splunkDeliveryStream,
splunkSubscriptionFilterRole,
lambdaInsightsLogGroupPolicy,
addSplunkSubscriptionFilter
})

const layersToAdd = [insightsLayer]
Expand Down
48 changes: 46 additions & 2 deletions packages/cdkConstructs/src/constructs/TypescriptLambdaFunction.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {Duration} from "aws-cdk-lib"
import {
IManagedPolicy,
IRole,
ManagedPolicy,
PolicyStatement,
Role
Expand All @@ -16,6 +17,8 @@ import {Construct} from "constructs"
import {join} from "node:path"
import {createSharedLambdaResources} from "./lambdaSharedResources"
import {addSuppressions} from "../utils/helpers"
import {IKey} from "aws-cdk-lib/aws-kms"
import {CfnDeliveryStream} from "aws-cdk-lib/aws-kinesisfirehose"

export interface TypescriptLambdaFunctionProps {
/**
Expand Down Expand Up @@ -84,6 +87,35 @@ export interface TypescriptLambdaFunctionProps {
* @default Architecture.X86_64
*/
readonly architecture?: Architecture
/**
* Optional KMS key for encrypting CloudWatch Logs.
* If not provided, the value is imported from account resources export.
*/
readonly cloudWatchLogsKmsKey?: IKey
/**
* Optional IAM policy for allowing CloudWatch to use the KMS key for encrypting logs.
* If not provided, the value is imported from account resources export.
*/
readonly cloudwatchEncryptionKMSPolicy?: IManagedPolicy
/**
* Optional firehose delivery stream for forwarding logs to Splunk.
* If not provided, the value is imported from account resources export.
*/
readonly splunkDeliveryStream?: CfnDeliveryStream
/**
* Optional IAM role for the subscription filter that forwards logs to Splunk.
* If not provided, the value is imported from account resources export.
*/
readonly splunkSubscriptionFilterRole?: IRole
/**
* Optional IAM policy for allowing lambdas to use Lambda Insights log groups and streams.
* If not provided, the value is imported from account resources export.
*/
readonly lambdaInsightsLogGroupPolicy?: IManagedPolicy
/**
* Whether to create a subscription filter on the Lambda log group to forward logs to Splunk. Defaults to true.
*/
readonly addSplunkSubscriptionFilter?: boolean
}

const getDefaultLambdaOptions = (
Expand Down Expand Up @@ -202,14 +234,26 @@ export class TypescriptLambdaFunction extends Construct {
projectBaseDir,
timeoutInSeconds = 50,
runtime = Runtime.NODEJS_24_X,
architecture = Architecture.X86_64
architecture = Architecture.X86_64,
cloudWatchLogsKmsKey,
cloudwatchEncryptionKMSPolicy,
splunkDeliveryStream,
splunkSubscriptionFilterRole,
lambdaInsightsLogGroupPolicy,
addSplunkSubscriptionFilter
} = props

const {logGroup, role, insightsLayer} = createSharedLambdaResources(this, {
functionName,
logRetentionInDays,
additionalPolicies,
architecture
architecture,
cloudWatchLogsKmsKey,
cloudwatchEncryptionKMSPolicy,
splunkDeliveryStream,
splunkSubscriptionFilterRole,
lambdaInsightsLogGroupPolicy,
addSplunkSubscriptionFilter
})

const lambdaFunction = new NodejsFunction(this, functionName, {
Expand Down
73 changes: 46 additions & 27 deletions packages/cdkConstructs/src/constructs/lambdaSharedResources.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import {Construct} from "constructs"
import {Fn, RemovalPolicy} from "aws-cdk-lib"
import {Architecture, ILayerVersion, LayerVersion} from "aws-cdk-lib/aws-lambda"
import {Key} from "aws-cdk-lib/aws-kms"
import {Stream} from "aws-cdk-lib/aws-kinesis"
import {IKey, Key} from "aws-cdk-lib/aws-kms"
import {CfnLogGroup, CfnSubscriptionFilter, LogGroup} from "aws-cdk-lib/aws-logs"
import {
IManagedPolicy,
IRole,
ManagedPolicy,
PolicyStatement,
Role,
Expand All @@ -14,12 +14,20 @@ import {
import {NagSuppressions} from "cdk-nag"
import {LAMBDA_INSIGHTS_LAYER_ARNS} from "../config"
import {addSuppressions} from "../utils/helpers"
import {CfnDeliveryStream} from "aws-cdk-lib/aws-kinesisfirehose"
import {Stream} from "aws-cdk-lib/aws-kinesis"

export interface SharedLambdaResourceProps {
readonly functionName: string
readonly logRetentionInDays: number
readonly additionalPolicies: Array<IManagedPolicy>
readonly architecture: Architecture
readonly cloudWatchLogsKmsKey?: IKey
readonly cloudwatchEncryptionKMSPolicy?: IManagedPolicy
readonly splunkDeliveryStream?: CfnDeliveryStream
readonly splunkSubscriptionFilterRole?: IRole
readonly lambdaInsightsLogGroupPolicy?: IManagedPolicy
readonly addSplunkSubscriptionFilter?: boolean
}

export interface SharedLambdaResources {
Expand All @@ -30,28 +38,24 @@ export interface SharedLambdaResources {

export const createSharedLambdaResources = (
scope: Construct,
{
props: SharedLambdaResourceProps
): SharedLambdaResources => {
const {
functionName,
logRetentionInDays,
additionalPolicies,
architecture
}: SharedLambdaResourceProps
): SharedLambdaResources => {
const cloudWatchLogsKmsKey = Key.fromKeyArn(
scope, "cloudWatchLogsKmsKey", Fn.importValue("account-resources:CloudwatchLogsKmsKeyArn"))

const cloudwatchEncryptionKMSPolicy = ManagedPolicy.fromManagedPolicyArn(
scope, "cloudwatchEncryptionKMSPolicyArn", Fn.importValue("account-resources:CloudwatchEncryptionKMSPolicyArn"))

const splunkDeliveryStream = Stream.fromStreamArn(
scope, "SplunkDeliveryStream", Fn.importValue("lambda-resources:SplunkDeliveryStream"))

const splunkSubscriptionFilterRole = Role.fromRoleArn(
scope, "splunkSubscriptionFilterRole", Fn.importValue("lambda-resources:SplunkSubscriptionFilterRole"))

const lambdaInsightsLogGroupPolicy = ManagedPolicy.fromManagedPolicyArn(
scope, "lambdaInsightsLogGroupPolicy", Fn.importValue("lambda-resources:LambdaInsightsLogGroupPolicy"))

architecture,
cloudWatchLogsKmsKey = Key.fromKeyArn(
scope, "cloudWatchLogsKmsKey", Fn.importValue("account-resources:CloudwatchLogsKmsKeyArn")),
cloudwatchEncryptionKMSPolicy = ManagedPolicy.fromManagedPolicyArn(
scope, "cloudwatchEncryptionKMSPolicyArn", Fn.importValue("account-resources:CloudwatchEncryptionKMSPolicyArn")),
splunkDeliveryStream,
splunkSubscriptionFilterRole = Role.fromRoleArn(
scope, "splunkSubscriptionFilterRole", Fn.importValue("lambda-resources:SplunkSubscriptionFilterRole")),
lambdaInsightsLogGroupPolicy = ManagedPolicy.fromManagedPolicyArn(
scope, "lambdaInsightsLogGroupPolicy", Fn.importValue("lambda-resources:LambdaInsightsLogGroupPolicy")),
addSplunkSubscriptionFilter = true
} = props
const insightsLambdaLayerArn = architecture === Architecture.ARM_64
? LAMBDA_INSIGHTS_LAYER_ARNS.arm64
: LAMBDA_INSIGHTS_LAYER_ARNS.x64
Expand All @@ -68,12 +72,27 @@ export const createSharedLambdaResources = (
const cfnlogGroup = logGroup.node.defaultChild as CfnLogGroup
addSuppressions([cfnlogGroup], ["CW_LOGGROUP_RETENTION_PERIOD_CHECK"])

new CfnSubscriptionFilter(scope, "LambdaLogsSplunkSubscriptionFilter", {
destinationArn: splunkDeliveryStream.streamArn,
filterPattern: "",
logGroupName: logGroup.logGroupName,
roleArn: splunkSubscriptionFilterRole.roleArn
})
if (addSplunkSubscriptionFilter) {
// This is in an if statement to ensure correct value is used
// importing and coercing to cfnDeliveryStream causes issues
if (splunkDeliveryStream) {
new CfnSubscriptionFilter(scope, "LambdaLogsSplunkSubscriptionFilter", {
destinationArn: splunkDeliveryStream.attrArn,
filterPattern: "",
logGroupName: logGroup.logGroupName,
roleArn: splunkSubscriptionFilterRole.roleArn
})
} else {
const splunkDeliveryStreamImport = Stream.fromStreamArn(
scope, "SplunkDeliveryStream", Fn.importValue("lambda-resources:SplunkDeliveryStream"))
new CfnSubscriptionFilter(scope, "LambdaLogsSplunkSubscriptionFilter", {
destinationArn: splunkDeliveryStreamImport.streamArn,
filterPattern: "",
logGroupName: logGroup.logGroupName,
roleArn: splunkSubscriptionFilterRole.roleArn
})
}
}

const putLogsManagedPolicy = new ManagedPolicy(scope, "LambdaPutLogsManagedPolicy", {
description: `write to ${functionName} logs`,
Expand Down
Loading
Loading