Repository: cdklabs/cdk-enterprise-iac (language: python)

Issue:
 feat: Add Aspect to apply a common environment variable to all lambda functions
Enterprises operating with restricted network configurations may require certain environment variables set on a Lambda function, such as `AWS_CA_BUNDLE`.

Create an Aspect allowing a user to specify common [environment variables](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-function.html#cfn-lambda-function-environment) to _all_ lambda functions in an application


Discussion / hints:

Fix patch:
diff --git a/.projen/deps.json b/.projen/deps.json
index 1b71541..7612384 100644
--- a/.projen/deps.json
+++ b/.projen/deps.json
@@ -22,7 +22,7 @@
     },
     {
       "name": "aws-cdk-lib",
-      "version": "2.37.1",
+      "version": "2.41.0",
       "type": "build"
     },
     {
@@ -118,7 +118,7 @@
     },
     {
       "name": "aws-cdk-lib",
-      "version": "^2.37.1",
+      "version": "^2.41.0",
       "type": "peer"
     },
     {
diff --git a/.projenrc.js b/.projenrc.js
index 8655a2a..587d7f8 100644
--- a/.projenrc.js
+++ b/.projenrc.js
@@ -7,7 +7,7 @@ const { GitlabConfiguration } = require('projen/lib/gitlab');
 const project = new awscdk.AwsCdkConstructLibrary({
   author: 'Taylor Ondrey',
   authorAddress: 'ondreyt@amazon.com',
-  cdkVersion: '2.37.1',
+  cdkVersion: '2.41.0',
   defaultReleaseBranch: 'main',
   name: '@cdklabs/cdk-enterprise-iac',
   repositoryUrl: 'https://github.com/cdklabs/cdk-enterprise-iac.git',
diff --git a/API.md b/API.md
index 8705be2..8faf9f5 100644
--- a/API.md
+++ b/API.md
@@ -120,6 +120,59 @@ Name of the tag property to remove from the resource.
 
 ## Classes <a name="Classes" id="Classes"></a>
 
+### AddLambdaEnvironmentVariables <a name="AddLambdaEnvironmentVariables" id="@cdklabs/cdk-enterprise-iac.AddLambdaEnvironmentVariables"></a>
+
+- *Implements:* aws-cdk-lib.IAspect
+
+Add one or more environment variables to _all_ lambda functions within a scope.
+
+#### Initializers <a name="Initializers" id="@cdklabs/cdk-enterprise-iac.AddLambdaEnvironmentVariables.Initializer"></a>
+
+```typescript
+import { AddLambdaEnvironmentVariables } from '@cdklabs/cdk-enterprise-iac'
+
+new AddLambdaEnvironmentVariables(props: {[ key: string ]: string})
+```
+
+| **Name** | **Type** | **Description** |
+| --- | --- | --- |
+| <code><a href="#@cdklabs/cdk-enterprise-iac.AddLambdaEnvironmentVariables.Initializer.parameter.props">props</a></code> | <code>{[ key: string ]: string}</code> | : string} props - Key Value pair(s) for environment variables to add to all lambda functions. |
+
+---
+
+##### `props`<sup>Required</sup> <a name="props" id="@cdklabs/cdk-enterprise-iac.AddLambdaEnvironmentVariables.Initializer.parameter.props"></a>
+
+- *Type:* {[ key: string ]: string}
+
+: string} props - Key Value pair(s) for environment variables to add to all lambda functions.
+
+---
+
+#### Methods <a name="Methods" id="Methods"></a>
+
+| **Name** | **Description** |
+| --- | --- |
+| <code><a href="#@cdklabs/cdk-enterprise-iac.AddLambdaEnvironmentVariables.visit">visit</a></code> | All aspects can visit an IConstruct. |
+
+---
+
+##### `visit` <a name="visit" id="@cdklabs/cdk-enterprise-iac.AddLambdaEnvironmentVariables.visit"></a>
+
+```typescript
+public visit(node: IConstruct): void
+```
+
+All aspects can visit an IConstruct.
+
+###### `node`<sup>Required</sup> <a name="node" id="@cdklabs/cdk-enterprise-iac.AddLambdaEnvironmentVariables.visit.parameter.node"></a>
+
+- *Type:* constructs.IConstruct
+
+---
+
+
+
+
 ### AddPermissionBoundary <a name="AddPermissionBoundary" id="@cdklabs/cdk-enterprise-iac.AddPermissionBoundary"></a>
 
 - *Implements:* aws-cdk-lib.IAspect
diff --git a/package.json b/package.json
index f78cb9c..a2ff6a7 100644
--- a/package.json
+++ b/package.json
@@ -42,7 +42,7 @@
     "@types/node": "^14",
     "@typescript-eslint/eslint-plugin": "^5",
     "@typescript-eslint/parser": "^5",
-    "aws-cdk-lib": "2.37.1",
+    "aws-cdk-lib": "2.41.0",
     "constructs": "10.0.5",
     "eslint": "^8",
     "eslint-config-prettier": "^8.5.0",
@@ -66,7 +66,7 @@
     "typescript": "^4.8.3"
   },
   "peerDependencies": {
-    "aws-cdk-lib": "^2.37.1",
+    "aws-cdk-lib": "^2.41.0",
     "constructs": "^10.0.5"
   },
   "keywords": [
diff --git a/src/index.ts b/src/index.ts
index 50815ba..717b09d 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -4,4 +4,5 @@ SPDX-License-Identifier: Apache-2.0
 */
 export * from './patches/addPermissionsBoundary';
 export * from './patches/removeTags';
+export * from './patches/addLambdaEnvironmentVariables';
 export * from './patches/removePublicAccessBlockConfiguration';
diff --git a/src/patches/addLambdaEnvironmentVariables.ts b/src/patches/addLambdaEnvironmentVariables.ts
new file mode 100644
index 0000000..67785c3
--- /dev/null
+++ b/src/patches/addLambdaEnvironmentVariables.ts
@@ -0,0 +1,36 @@
+/*
+Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+SPDX-License-Identifier: Apache-2.0
+*/
+import { IAspect } from 'aws-cdk-lib';
+import { Function } from 'aws-cdk-lib/aws-lambda';
+import { IConstruct } from 'constructs';
+
+/**
+ * Add one or more environment variables to _all_ lambda functions within a scope
+ *
+ * @extends IAspect
+ */
+export class AddLambdaEnvironmentVariables implements IAspect {
+  /**
+   * Key value pairs of environment variables to add to all lambda functions within the scope
+   */
+  private _environmentKeyValues: { [key: string]: string };
+
+  /**
+   *
+   * @param props {[key: string]: string} props - Key Value pair(s) for environment variables to add to all lambda functions
+   */
+  constructor(props: { [key: string]: string }) {
+    this._environmentKeyValues = props;
+  }
+
+  public visit(node: IConstruct): void {
+    if (node instanceof Function) {
+      const lmb: Function = node;
+      for (const [key, value] of Object.entries(this._environmentKeyValues)) {
+        lmb.addEnvironment(key, value);
+      }
+    }
+  }
+}
diff --git a/yarn.lock b/yarn.lock
index 340d2ec..715f60a 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1232,10 +1232,10 @@ at-least-node@^1.0.0:
   resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2"
   integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==
 
-aws-cdk-lib@2.37.1:
-  version "2.37.1"
-  resolved "https://registry.yarnpkg.com/aws-cdk-lib/-/aws-cdk-lib-2.37.1.tgz#8ff0dbb784236cb5c201fe2e2de41222d83bac87"
-  integrity sha512-TN9PkfxGvwpODOBedhmCHUFMwvrNBJIadY42BYyOerk1Hw9bOT5R3phqhm4ujzJDHKXtdgRNFRg1zw6spRodjg==
+aws-cdk-lib@2.41.0:
+  version "2.41.0"
+  resolved "https://registry.yarnpkg.com/aws-cdk-lib/-/aws-cdk-lib-2.41.0.tgz#5bcff1b075dd8eb3df6346ba11940a1d75d993d5"
+  integrity sha512-wh6lDaarzb8B+43TMxEBg+yHcXU9omlUGJz9zSdgjrmeQWBV8SD0jIvrERhDFvQLmRY4Vzy7FXxkI0mU+adDHQ==
   dependencies:
     "@balena/dockerignore" "^1.0.2"
     case "1.6.3"


Test patch:
diff --git a/test/patches/addLambdaEnvironmentVariables.test.ts b/test/patches/addLambdaEnvironmentVariables.test.ts
new file mode 100644
index 0000000..b15e2da
--- /dev/null
+++ b/test/patches/addLambdaEnvironmentVariables.test.ts
@@ -0,0 +1,96 @@
+/*
+Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+SPDX-License-Identifier: Apache-2.0
+*/
+import { Aspects, Stack } from 'aws-cdk-lib';
+import { Match, Template } from 'aws-cdk-lib/assertions';
+import { Code, Function, Runtime } from 'aws-cdk-lib/aws-lambda';
+import { AddLambdaEnvironmentVariables } from '../../src/patches/addLambdaEnvironmentVariables';
+
+let stack: Stack;
+
+beforeEach(() => {
+  stack = new Stack();
+});
+
+describe('Add environment variables to all Lambda functions', () => {
+  test('adds environment variable to multiple lambdas', () => {
+    new Function(stack, 'TestFunction1', {
+      code: Code.fromInline('def handler(event, context)\n    print(event)'),
+      runtime: Runtime.PYTHON_3_9,
+      handler: 'index.handler',
+    });
+
+    new Function(stack, 'TestFunction2', {
+      code: Code.fromInline(
+        'def handler(event, context)\n    print("something")'
+      ),
+      runtime: Runtime.PYTHON_3_9,
+      handler: 'index.handler',
+    });
+
+    Aspects.of(stack).add(
+      new AddLambdaEnvironmentVariables({
+        myNeatEnvVariable: 'value 1',
+      })
+    );
+
+    const template = Template.fromStack(stack);
+
+    template.resourcePropertiesCountIs(
+      'AWS::Lambda::Function',
+      {
+        Environment: {
+          Variables: Match.objectLike({ myNeatEnvVariable: 'value 1' }),
+        },
+      },
+      2
+    );
+  });
+
+  test("adds environment variable to multiple lambdas, and doesn't remove existing", () => {
+    new Function(stack, 'TestFunction1', {
+      code: Code.fromInline('def handler(event, context)\n    print(event)'),
+      runtime: Runtime.PYTHON_3_9,
+      handler: 'index.handler',
+      environment: {
+        originalKey: 'originalVar',
+      },
+    });
+
+    new Function(stack, 'TestFunction2', {
+      code: Code.fromInline(
+        'def handler(event, context)\n    print("something")'
+      ),
+      runtime: Runtime.PYTHON_3_9,
+      handler: 'index.handler',
+    });
+
+    Aspects.of(stack).add(
+      new AddLambdaEnvironmentVariables({
+        myNeatEnvVariable: 'value 1',
+      })
+    );
+
+    const template = Template.fromStack(stack);
+
+    template.resourcePropertiesCountIs(
+      'AWS::Lambda::Function',
+      {
+        Environment: {
+          Variables: Match.objectLike({ myNeatEnvVariable: 'value 1' }),
+        },
+      },
+      2
+    );
+    template.resourcePropertiesCountIs(
+      'AWS::Lambda::Function',
+      {
+        Environment: {
+          Variables: Match.objectLike({ originalKey: 'originalVar' }),
+        },
+      },
+      1
+    );
+  });
+});