-
Notifications
You must be signed in to change notification settings - Fork 2.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Typescript example of AWS AppConfig hosted configuration with lambda using AppConfig Lambda extension integration #361
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
# AWS AppConfig Hosted Configuration with Lambda Extension | ||
<!--BEGIN STABILITY BANNER--> | ||
--- | ||
|
||
![Stability: Stable](https://img.shields.io/badge/stability-Stable-success.svg?style=for-the-badge) | ||
|
||
> **This is a stable example. It should successfully build out of the box** | ||
> | ||
> This examples does is built on Construct Libraries marked "Stable" and does not have any infrastructure prerequisites to build. | ||
|
||
--- | ||
<!--END STABILITY BANNER--> | ||
|
||
This an example of AppConfig fature toggle use case with a hosted configuration and a consuming Lambda using AppConfig Lambda extension integration. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This would be a great place to include a brief description of why this pattern might be useful. |
||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. An explanation of the purpose of the lambda would also be important here. At the moment it is very difficult to decipher (and I imagine even more so for someone with less context than me). |
||
## Build | ||
|
||
To build this app, you need to be in this example's root folder. Then run the following: | ||
|
||
```bash | ||
npm install -g aws-cdk | ||
npm install | ||
npm run build | ||
``` | ||
|
||
This will install the necessary CDK, then this example's dependencies, and then build your TypeScript files and your CloudFormation template. | ||
|
||
## Deploy | ||
|
||
Run `cdk deploy`. This will deploy / redeploy your Stack to your AWS Account. | ||
|
||
After the deployment you will see the API's URL, which represents the url you can then use. | ||
|
||
## The Component Structure | ||
|
||
The whole component contains: | ||
|
||
- An AppConfig Application, Environment, Hosted Configuration Profile and Deployment Strategy. | ||
- Lambda pointing to `src/lambda-handler.ts`, containing code for __consuming__ AppConfig configuration data. | ||
|
||
## CDK Toolkit | ||
|
||
The [`cdk.json`](./cdk.json) file in the root of this repository includes | ||
instructions for the CDK toolkit on how to execute this program. | ||
|
||
After building your TypeScript code, you will be able to run the CDK toolkits commands as usual: | ||
|
||
$ cdk ls | ||
<list all stacks in this program> | ||
|
||
$ cdk synth | ||
<generates and outputs cloudformation template> | ||
|
||
$ cdk deploy | ||
<deploys stack to your account> | ||
|
||
$ cdk diff | ||
<shows diff against deployed stack> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"app": "node index" | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,89 @@ | ||||||||||||||||||||
import * as cdk from '@aws-cdk/core'; | ||||||||||||||||||||
import { CfnApplication, CfnEnvironment, CfnConfigurationProfile, CfnHostedConfigurationVersion, CfnDeploymentStrategy, CfnDeployment } from '@aws-cdk/aws-appconfig'; | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You should space long imports on separate lines (or import
Suggested change
|
||||||||||||||||||||
import { Function, AssetCode, Runtime, LayerVersion } from '@aws-cdk/aws-lambda'; | ||||||||||||||||||||
import { Effect, PolicyStatement } from '@aws-cdk/aws-iam'; | ||||||||||||||||||||
|
||||||||||||||||||||
export class AppConfigHostedConfigurationStack extends cdk.Stack { | ||||||||||||||||||||
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) { | ||||||||||||||||||||
super(scope, id, props); | ||||||||||||||||||||
|
||||||||||||||||||||
const application = new CfnApplication(this,'AppConfigApplication', { | ||||||||||||||||||||
name: 'AppConfigSampleApplication', | ||||||||||||||||||||
description: 'Sample AppConfig Application using CDK' | ||||||||||||||||||||
}); | ||||||||||||||||||||
|
||||||||||||||||||||
const environment = new CfnEnvironment(this, 'LambdaDevelopmentEnvironment', { | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be useful here to include both a dev and prod environment with appropriate configurations for each. This would show the real power/reason to use AppConfig over a standard deployment strategy. |
||||||||||||||||||||
applicationId: application.ref, | ||||||||||||||||||||
name: 'AppConfigSampleLambdaDevelopmentEnvironment', | ||||||||||||||||||||
description: 'Sample AppConfig Development environment for Lambda implementation' | ||||||||||||||||||||
}); | ||||||||||||||||||||
|
||||||||||||||||||||
const configurationProfile = new CfnConfigurationProfile(this, 'ConfigurationProfile', { | ||||||||||||||||||||
applicationId: application.ref, | ||||||||||||||||||||
name: 'AppConfigSampleConfigurationProfile', | ||||||||||||||||||||
locationUri: 'hosted', | ||||||||||||||||||||
description: 'Sample AppConfig configuration profile' | ||||||||||||||||||||
}); | ||||||||||||||||||||
|
||||||||||||||||||||
const hostedConfigurationProfile = new CfnHostedConfigurationVersion(this, 'HostedConfigurationProfile', { | ||||||||||||||||||||
applicationId: application.ref, | ||||||||||||||||||||
configurationProfileId: configurationProfile.ref, | ||||||||||||||||||||
contentType: 'application/json', | ||||||||||||||||||||
content: '{\"boolEnableLimitResults\": true, \"intResultLimit\":5}', | ||||||||||||||||||||
latestVersionNumber: 1 | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is optional to prevent overwriting previous config versions, but since there is only one version being deployed, this should be skipped |
||||||||||||||||||||
}); | ||||||||||||||||||||
|
||||||||||||||||||||
hostedConfigurationProfile.addMetadata('description', 'Sample AppConfig hosted configuration profile content'); | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this not provided in props? |
||||||||||||||||||||
|
||||||||||||||||||||
const deploymentStrategy = new CfnDeploymentStrategy(this, 'DeploymentStrategy', { | ||||||||||||||||||||
name: 'Custom.AllAtOnce', | ||||||||||||||||||||
deploymentDurationInMinutes: 0, | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My experience with AppConfig is very limited, but should this not be greater than 0? |
||||||||||||||||||||
growthFactor: 100, | ||||||||||||||||||||
finalBakeTimeInMinutes: 0, | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same question as above? |
||||||||||||||||||||
replicateTo: 'NONE', | ||||||||||||||||||||
growthType: 'LINEAR', | ||||||||||||||||||||
description: 'Sample AppConfig deployment strategy - All at once deployment (i.e., immediate)' | ||||||||||||||||||||
}); | ||||||||||||||||||||
|
||||||||||||||||||||
const deployment = new CfnDeployment(this, 'Deployment', { | ||||||||||||||||||||
applicationId: application.ref, | ||||||||||||||||||||
configurationProfileId: configurationProfile.ref, | ||||||||||||||||||||
configurationVersion: '1', | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another magic number. This should be:
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @NGL321 not exactly intuitive, but looks like |
||||||||||||||||||||
deploymentStrategyId: deploymentStrategy.ref, | ||||||||||||||||||||
environmentId: environment.ref, | ||||||||||||||||||||
}); | ||||||||||||||||||||
Comment on lines
+10
to
+54
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider leaving an inline comment to indicate that these are essential components to an AppConfig configuration, and explain what each does along the way. For inspiration, take a look at the Cloudformation description |
||||||||||||||||||||
|
||||||||||||||||||||
deployment.addDependsOn(hostedConfigurationProfile); | ||||||||||||||||||||
|
||||||||||||||||||||
deployment.addMetadata('description', 'Sample AppConfig initial deployment'); | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again, provide in props? |
||||||||||||||||||||
|
||||||||||||||||||||
const sampleAppConfigLambda = new Function(this, 'sampleAppConfigLambda', { | ||||||||||||||||||||
code: new AssetCode('src'), | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. By standards this should be:
Suggested change
This also requires adjusting imports |
||||||||||||||||||||
functionName: 'SampleAppConfigLambda', | ||||||||||||||||||||
handler: 'lambda-handler.handler', | ||||||||||||||||||||
runtime: Runtime.NODEJS_12_X, | ||||||||||||||||||||
environment: { | ||||||||||||||||||||
AWS_APPCONFIG_EXTENSION_HTTP_PORT: '2772', | ||||||||||||||||||||
AWS_APPCONFIG_EXTENSION_POLL_INTERVAL_SECONDS: '45', | ||||||||||||||||||||
AWS_APPCONFIG_EXTENSION_POLL_TIMEOUT_MILLIS: '3000' | ||||||||||||||||||||
}, | ||||||||||||||||||||
layers: [ | ||||||||||||||||||||
LayerVersion.fromLayerVersionArn(this, 'AppConfigLambdaExtension', 'arn:aws:lambda:us-east-1:027255383542:layer:AWS-AppConfig-Extension:1') | ||||||||||||||||||||
] | ||||||||||||||||||||
}); | ||||||||||||||||||||
|
||||||||||||||||||||
sampleAppConfigLambda.addToRolePolicy( | ||||||||||||||||||||
new PolicyStatement({ | ||||||||||||||||||||
resources: [ | ||||||||||||||||||||
`arn:aws:appconfig:${this.region}:${this.account}:application/${application.ref}*` | ||||||||||||||||||||
], | ||||||||||||||||||||
actions: ['appconfig:GetConfiguration'], | ||||||||||||||||||||
effect: Effect.ALLOW | ||||||||||||||||||||
}) | ||||||||||||||||||||
); | ||||||||||||||||||||
} | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
const app = new cdk.App(); | ||||||||||||||||||||
new AppConfigHostedConfigurationStack(app, 'AppConfigHostedConfigurationStack'); | ||||||||||||||||||||
app.synth(); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
{ | ||
"name": "appconfig-hosted-configuration", | ||
"version": "1.0.0", | ||
"description": "Use of AWS AppConfig with a hosted configuration", | ||
"private": true, | ||
"scripts": { | ||
"build": "tsc", | ||
"watch": "tsc -w", | ||
"cdk": "cdk" | ||
}, | ||
"author": { | ||
"name": "Amazon Web Services", | ||
"url": "https://aws.amazon.com", | ||
"organization": true | ||
}, | ||
"license": "Apache-2.0", | ||
"devDependencies": { | ||
"@types/node": "10.17.27", | ||
"typescript": "~3.9.7" | ||
}, | ||
"dependencies": { | ||
"@aws-cdk/aws-appconfig": "*", | ||
"@aws-cdk/aws-iam": "*", | ||
"@aws-cdk/aws-lambda": "*", | ||
"@aws-cdk/core": "*" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,67 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import * as http from 'http'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
export const handler = async (): Promise<any> => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// retrieve AppConfig configuration data from Lambda extension | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const res: any = await new Promise((resolve) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
http.get( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
`http://localhost:2772/applications/AppConfigSampleApplication/environments/AppConfigSampleLambdaDevelopmentEnvironment/configurations/AppConfigSampleConfigurationProfile`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe I'm missing something, but I dont see where this URL is coming from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe this only works with Lambda/ECS/Fargate workloads, normally you would acquire these config values via the AWS SDK: https://docs.aws.amazon.com/appconfig/latest/userguide/appconfig-integration-lambda-extensions.html#appconfig-integration-lambda-extensions-enabling There's also this blog post that references the localhost URL proxy: https://aws.amazon.com/blogs/mt/introducing-aws-appconfig-lambda-extension-deploying-application-configuration-serverless/ |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
resolve | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let configData: any = await new Promise((resolve, reject) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let data = ''; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
res.on('data', (chunk: any) => data += chunk); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
res.on('error', (err: any) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
console.log(err); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
reject(err); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
res.on('end', () => resolve(data)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let result: {name: String}[] = getServices(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const parsedConfigData = JSON.parse(configData); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// implement feature toggle that filters results using configuration data | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if ( (parsedConfigData.boolEnableLimitResults) && parsedConfigData.intResultLimit ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
result = result.splice(0, parsedConfigData.intResultLimit); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return result; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const getServices = () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
name: 'AWS AppConfig' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
name: 'Amazon SageMaker Studio' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
name: 'Amazon Kendra' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
name: 'Amazon CodeGuru' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
name: 'Amazon Fraud Detector' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
name: 'Amazon EKS on AWS Fargate' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
name: 'AWS Outposts' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
name: 'AWS Wavelength' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
name: 'AWS Transit Gateway' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
name: 'Amazon Detective' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+35
to
+66
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can make this a little more condensed while still obeying the eslint rules.
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
{ | ||
"compilerOptions": { | ||
"target":"ES2018", | ||
"module": "commonjs", | ||
"lib": ["es2016", "es2017.object", "es2017.string"], | ||
"strict": true, | ||
"noImplicitAny": true, | ||
"strictNullChecks": true, | ||
"noImplicitThis": true, | ||
"alwaysStrict": true, | ||
"noUnusedLocals": true, | ||
"noUnusedParameters": true, | ||
"noImplicitReturns": true, | ||
"noFallthroughCasesInSwitch": false, | ||
"inlineSourceMap": true, | ||
"inlineSources": true, | ||
"experimentalDecorators": true, | ||
"strictPropertyInitialization":false | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This actually isn't considered a "stable" module since only the L1 containing Cfn resources exists. You can see the banner here and explanation of reasoning here