Skip to content

Commit

Permalink
feat(aws-lambda-ssm-string-parameter): New aws-lambda-ssm-string-para…
Browse files Browse the repository at this point in the history
…meter pattern implementation (#64) (#175)

* fix(aws-lambda-ssm-string-parameter) Construct design and foundation.

* fix(aws-lambda-ssm-string-parameter) Added architecture diagram.

* initial updates

* fix(aws-lambda-ssm-string-parameter) Fixed formatting and updated unit tests.

* fix(aws-lambda-ssm-string-parameter) Fixed construct main code, and added VPC endpoint configuration for SSM.

* fix(aws-lambda-ssm-string-parameter) Updated permissions to follow the same model of the other constructs.

* Update to version v1.98.0

* fix(aws-lambda-ssm-string-parameter) Construct design and foundation.

* fix(aws-lambda-ssm-string-parameter) Added architecture diagram.

* initial updates

* fix(aws-lambda-ssm-string-parameter) Fixed formatting and updated unit tests.

* fix(aws-lambda-ssm-string-parameter) Fixed construct main code, and added VPC endpoint configuration for SSM.

* fix(aws-lambda-ssm-string-parameter) Updated permissions to follow the same model of the other constructs.

* fix(aws-lambda-ssm-string-parameter): Reverted package json.

* fix(aws-lambda-ssm-string-parameter): Reverted package json.

* fix(aws-lambda-ssm-string-parameter): Added unit tests structure.

* fix(aws-lambda-ssm-string-parameter): Added unit tests

* fix(aws-lambda-ssm-string-parameter): Added missing integration tests and snapshots.

* fix(aws-lambda-ssm-string-parameter): Code review fixes.

* fix(aws-lambda-ssm-string-parameter): Added missing snapshot.

* fix(aws-lambda-ssm-string-parameter): Added missing snapshot.

Co-authored-by: Neville Lewis <[email protected]>
Co-authored-by: biffgaut <[email protected]>
  • Loading branch information
3 people committed May 19, 2021
1 parent 33a9dd7 commit b0567e4
Show file tree
Hide file tree
Showing 32 changed files with 3,744 additions and 10 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -472,4 +472,4 @@ General Availability of the AWS Solutions Constructs!! 🎉🎉🥂🥂🍾🍾
- aws-s3-lambda pattern added
- aws-sns-lambda pattern added
- aws-sqs-lambda pattern added
- core pattern added
- core pattern added
Original file line number Diff line number Diff line change
Expand Up @@ -716,7 +716,7 @@ test("Test minimal deployment with an existing VPC", () => {
});

// --------------------------------------------------------------
// Test minimal deployment with an existing VPC and existing Lambda function not in a VPCs
// Test minimal deployment with an existing VPC and existing Lambda function not in a VPC
//
// buildLambdaFunction should throw an error if the Lambda function is not
// attached to a VPC
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,7 @@ test("Test minimal deployment with an existing VPC", () => {
});

// --------------------------------------------------------------
// Test minimal deployment with an existing VPC and existing Lambda function not in a VPCs
// Test minimal deployment with an existing VPC and existing Lambda function not in a VPC
//
// buildLambdaFunction should throw an error if the Lambda function is not
// attached to a VPC
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,18 +99,18 @@ test('Test deployment w/ existing function', () => {
handler: 'index.handler',
code: lambda.Code.fromAsset(`${__dirname}/lambda`)
};
const existingFuntion = defaults.deployLambdaFunction(stack, lambdaFunctionProps);
const existingFunction = defaults.deployLambdaFunction(stack, lambdaFunctionProps);

const pattern = new LambdaToSecretsmanager(stack, 'lambda-to-secretsmanager-stack', {
existingLambdaObj: existingFuntion,
existingLambdaObj: existingFunction,
secretProps: { removalPolicy: RemovalPolicy.DESTROY },
});
// Assertion 1
expect(stack).toHaveResource("AWS::SecretsManager::Secret", {
GenerateSecretString: {},
});
// Assertion 2
expect(pattern.lambdaFunction).toBe(existingFuntion);
expect(pattern.lambdaFunction).toBe(existingFunction);
});

// --------------------------------------------------------------
Expand Down Expand Up @@ -291,7 +291,7 @@ test("Test minimal deployment with an existing VPC", () => {
});

// --------------------------------------------------------------
// Test minimal deployment with an existing VPC and existing Lambda function not in a VPCs
// Test minimal deployment with an existing VPC and existing Lambda function not in a VPC
//
// buildLambdaFunction should throw an error if the Lambda function is not
// attached to a VPC
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ test("Test minimal deployment with an existing VPC", () => {
});

// --------------------------------------------------------------
// Test minimal deployment with an existing VPC and existing Lambda function not in a VPCs
// Test minimal deployment with an existing VPC and existing Lambda function not in a VPC
//
// buildLambdaFunction should throw an error if the Lambda function is not
// attached to a VPC
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ test("Test minimal deployment with an existing VPC", () => {
});

// --------------------------------------------------------------
// Test minimal deployment with an existing VPC and existing Lambda function not in a VPCs
// Test minimal deployment with an existing VPC and existing Lambda function not in a VPC
//
// buildLambdaFunction should throw an error if the Lambda function is not
// attached to a VPC
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
lib/*.js
test/*.js
*.d.ts
coverage
test/lambda/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
lib/*.js
test/*.js
*.js.map
*.d.ts
node_modules
*.generated.ts
dist
.jsii

.LAST_BUILD
.nyc_output
coverage
.nycrc
.LAST_PACKAGE
*.snk
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Exclude typescript source and config
*.ts
tsconfig.json
coverage
.nyc_output
*.tgz
*.snk
*.tsbuildinfo

# Include javascript files and typescript declarations
!*.js
!*.d.ts

# Exclude jsii outdir
dist

# Include .jsii
!.jsii

# Include .jsii
!.jsii
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# aws-lambda-ssmstringparameter module
<!--BEGIN STABILITY BANNER-->

---

![Stability: Experimental](https://img.shields.io/badge/stability-Experimental-important.svg?style=for-the-badge)

> All classes are under active development and subject to non-backward compatible changes or removal in any
> future version. These are not subject to the [Semantic Versioning](https://semver.org/) model.
> This means that while you may use them, you may need to update your source code when upgrading to a newer version of this package.
---
<!--END STABILITY BANNER-->

| **Reference Documentation**:| <span style="font-weight: normal">https://docs.aws.amazon.com/solutions/latest/constructs/</span>|
|:-------------|:-------------|
<div style="height:8px"></div>

| **Language** | **Package** |
|:-------------|-----------------|
|![Python Logo](https://docs.aws.amazon.com/cdk/api/latest/img/python32.png) Python|`aws_solutions_constructs.aws_lambda_ssm_string_parameter`|
|![Typescript Logo](https://docs.aws.amazon.com/cdk/api/latest/img/typescript32.png) Typescript|`@aws-solutions-constructs/aws-lambda-ssmstringparameter`|
|![Java Logo](https://docs.aws.amazon.com/cdk/api/latest/img/java32.png) Java|`software.amazon.awsconstructs.services.lambdassmstringparameter`|

This AWS Solutions Construct implements the AWS Lambda function and AWS Systems Manager Parameter Store String parameter with the least privileged permissions.

Here is a minimal deployable pattern definition in Typescript:

``` javascript
const { LambdaToSsmstringparameterProps, LambdaToSsmstringparameter } from '@aws-solutions-constructs/aws-lambda-ssmstringparameter';

const props: LambdaToSsmstringparameterProps = {
lambdaFunctionProps: {
runtime: lambda.Runtime.NODEJS_14_X,
// This assumes a handler function in lib/lambda/index.js
code: lambda.Code.fromAsset(`${__dirname}/lambda`),
handler: 'index.handler'
},
stringParameterProps: { stringValue: "test-string-value" }
};

new LambdaToSsmstringparameter(this, 'test-lambda-ssmstringparameter-stack', props);

```

## Initializer

``` text
new LambdaToSsmstringparameter(scope: Construct, id: string, props: LambdaToSsmstringparameterProps);
```

_Parameters_

* scope [`Construct`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_core.Construct.html)
* id `string`
* props [`LambdaToSsmstringparameterProps`](#pattern-construct-props)

## Pattern Construct Props

| **Name** | **Type** | **Description** |
|:-------------|:----------------|-----------------|
|existingLambdaObj?|[`lambda.Function`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-lambda.Function.html)|Existing instance of Lambda Function object, providing both this and `lambdaFunctionProps` will cause an error.|
|lambdaFunctionProps?|[`lambda.FunctionProps`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-lambda.FunctionProps.html)|User provided props to override the default props for the Lambda function.|
|existingStringParameterObj?|[`ssm.StringParameter`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-ssm.StringParameter.html)|Existing instance of SSM String parameter object, providing both this and `stringParameterProps` will cause an error|
|stringParameterProps?|[`ssm.StringParameterProps`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-ssm.StringParameterProps.html)|Optional user provided props to override the default props for SSM String parameter. If existingStringParameterObj is not set stringParameterProps is required. The only supported [`ssm.StringParameterProps.type`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-ssm.StringParameterProps.html#type) is [`STRING`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-ssm.ParameterType.html#string) if a different value is provided it will be overridden.|
|stringParameterEnvironmentVariableName?|`string`|Optional Name for the SSM String parameter environment variable set for the Lambda function.|
|existingVpc?|[`ec2.IVpc`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-ec2.IVpc.html)|An optional, existing VPC into which this pattern should be deployed. When deployed in a VPC, the Lambda function will use ENIs in the VPC to access network resources and an Interface Endpoint will be created in the VPC for AWS Systems Manager Parameter. If an existing VPC is provided, the `deployVpc` property cannot be `true`. This uses `ec2.IVpc` to allow clients to supply VPCs that exist outside the stack using the [`ec2.Vpc.fromLookup()`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-ec2.Vpc.html#static-fromwbrlookupscope-id-options) method.|
|vpcProps?|[`ec2.VpcProps`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-ec2.VpcProps.html)|Optional user-provided properties to override the default properties for the new VPC. `enableDnsHostnames`, `enableDnsSupport`, `natGateways` and `subnetConfiguration` are set by the pattern, so any values for those properties supplied here will be overrriden. If `deployVpc` is not `true` then this property will be ignored.|
|deployVpc?|`boolean`|Whether to create a new VPC based on `vpcProps` into which to deploy this pattern. Setting this to true will deploy the minimal, most private VPC to run the pattern:<ul><li> One isolated subnet in each Availability Zone used by the CDK program</li><li>`enableDnsHostnames` and `enableDnsSupport` will both be set to true</li></ul>If this property is `true` then `existingVpc` cannot be specified. Defaults to `false`.|
|stringParameterPermissions|`string`|Optional SSM String parameter permissions to grant to the Lambda function. One of the following may be specified: "Read", "ReadWrite".

## Pattern Properties

| **Name** | **Type** | **Description** |
|:-------------|:----------------|-----------------|
|lambdaFunction|[`lambda.Function`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-lambda.Function.html)|Returns an instance of lambda.Function created by the construct|
|stringParameter|[`ssm.StringParameter`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-ssm.StringParameter.html)|Returns an instance of ssm.StringParameter created by the construct|
|vpc?|[`ec2.IVpc`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-ec2.IVpc.html)|Returns an interface on the VPC used by the pattern (if any). This may be a VPC created by the pattern or the VPC supplied to the pattern constructor.|

## Default settings

Out of the box implementation of the Construct without any override will set the following defaults:

### AWS Lambda Function
* Configure limited privilege access IAM role for Lambda function
* Enable reusing connections with Keep-Alive for NodeJs Lambda function
* Enable X-Ray Tracing
* Set Environment Variables
* (default) SSM_STRING_PARAMETER_NAME
* AWS_NODEJS_CONNECTION_REUSE_ENABLED (for Node 10.x and higher functions)

### Amazon AWS Systems Manager Parameter Store String
* Enable read-only access for the associated AWS Lambda Function
* Creates a new SSM String parameter with the values provided
* Retain the SSM String parameter when deleting the CloudFormation stack

## Architecture
![Architecture Diagram](architecture.png)

***
&copy; Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/**
* Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance
* with the License. A copy of the License is located at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES
* OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions
* and limitations under the License.
*/

// Imports
import * as defaults from "@aws-solutions-constructs/core";
import * as lambda from "@aws-cdk/aws-lambda";
import * as ssm from "@aws-cdk/aws-ssm";
import * as ec2 from "@aws-cdk/aws-ec2";
import {Construct} from "@aws-cdk/core";

/**
* @summary The properties for the LambdaToSsmstringparameter class.
*/
export interface LambdaToSsmstringparameterProps {
/**
* Existing instance of Lambda Function object, if this is set then the lambdaFunctionProps is ignored.
*
* @default - None
*/
readonly existingLambdaObj?: lambda.Function;
/**
* User provided props to override the default props for the Lambda function.
*
* @default - Default properties are used.
*/
readonly lambdaFunctionProps?: lambda.FunctionProps;
/**
* Existing instance of SSM String parameter object, If this is set then the stringParameterProps is ignored.
*
* @default - Default props are used
*/
readonly existingStringParameterObj?: ssm.StringParameter;
/**
* Optional user provided props to override the default props for SSM String parameter. If existingStringParameterObj
* is not set stringParameterProps is required. The only supported string parameter type is ParameterType.STRING.
*
* @default - Default props are used
*/
readonly stringParameterProps?: ssm.StringParameterProps;
/**
* An existing VPC for the construct to use (construct will NOT create a new VPC in this case)
*/
readonly existingVpc?: ec2.IVpc;
/**
* Properties to override default properties if deployVpc is true
*/
readonly vpcProps?: ec2.VpcProps;
/**
* Whether to deploy a new VPC
*
* @default - false
*/
readonly deployVpc?: boolean;
/**
* Optional Name for the SSM String parameter environment variable set for the Lambda function.
*
* @default - SSM_STRING_PARAMETER_NAME
*/
readonly stringParameterEnvironmentVariableName?: string;
/**
* Optional SSM String parameter permissions to grant to the Lambda function.
* One of the following may be specified: "Read", "ReadWrite".
*
* @default - Read access is given to the Lambda function if no value is specified.
*/
readonly stringParameterPermissions?: string;
}

/**
* @summary The LambdaToSsmstringparameter class.
*/
export class LambdaToSsmstringparameter extends Construct {
public readonly lambdaFunction: lambda.Function;
public readonly stringParameter: ssm.StringParameter;
public readonly vpc?: ec2.IVpc;

/**
* @summary Constructs a new instance of the LambdaToSsmstringparameter class.
* @param {cdk.App} scope - represents the scope for all the resources.
* @param {string} id - this is a a scope-unique id.
* @param {LambdaToSsmstringparameterProps} props - user provided props for the construct.
* @since 1.49.0
* @access public
*/
constructor(scope: Construct, id: string, props: LambdaToSsmstringparameterProps) {
super(scope, id);
defaults.CheckProps(props);

if (props.deployVpc || props.existingVpc) {
this.vpc = defaults.buildVpc(scope, {
defaultVpcProps: defaults.DefaultIsolatedVpcProps(),
existingVpc: props.existingVpc,
userVpcProps: props.vpcProps,
constructVpcProps: {
enableDnsHostnames: true,
enableDnsSupport: true,
},
});

defaults.AddAwsServiceEndpoint(scope, this.vpc, defaults.ServiceEndpointTypes.SSM);
}

// Setup the Lambda function
this.lambdaFunction = defaults.buildLambdaFunction(this, {
existingLambdaObj: props.existingLambdaObj,
lambdaFunctionProps: props.lambdaFunctionProps,
vpc: this.vpc,
});

// Setup the SSM String parameter
if (props.existingStringParameterObj) {
this.stringParameter = props.existingStringParameterObj;
} else {
if (!props.stringParameterProps) {
throw new Error("existingStringParameterObj or stringParameterProps needs to be provided.");
}
this.stringParameter = defaults.buildSsmStringParameter(this, 'stringParameter', props.stringParameterProps);
}

// Configure environment variables
const stringParameterEnvironmentVariableName = props.stringParameterEnvironmentVariableName || 'SSM_STRING_PARAMETER_NAME';
this.lambdaFunction.addEnvironment(stringParameterEnvironmentVariableName, this.stringParameter.parameterName);

// Add the requested or default SSM String parameter permissions
this.stringParameter.grantRead(this.lambdaFunction);
if (props.stringParameterPermissions) {
const _permissions = props.stringParameterPermissions.toUpperCase();

if (_permissions === 'READWRITE') {
this.stringParameter.grantWrite(this.lambdaFunction);
}
}
}
}
Loading

0 comments on commit b0567e4

Please sign in to comment.