diff --git a/packages/@aws-cdk/aws-appconfig-alpha/.eslintrc.js b/packages/@aws-cdk/aws-appconfig-alpha/.eslintrc.js new file mode 100644 index 0000000000000..39d029a634437 --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/.eslintrc.js @@ -0,0 +1,7 @@ +const baseConfig = require('@aws-cdk/cdk-build-tools/config/eslintrc'); +baseConfig.parserOptions.project = __dirname + '/tsconfig.json'; + +baseConfig.rules['import/order'] = 'off'; +baseConfig.rules['@aws-cdk/no-literal-partition'] = 'off'; + +module.exports = baseConfig; diff --git a/packages/@aws-cdk/aws-appconfig-alpha/.gitignore b/packages/@aws-cdk/aws-appconfig-alpha/.gitignore new file mode 100644 index 0000000000000..3e895fc51317c --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/.gitignore @@ -0,0 +1,23 @@ +*.js +*.js.map +*.d.ts +tsconfig.json +node_modules +*.generated.ts +dist +.jsii + +.LAST_BUILD +.nyc_output +coverage +nyc.config.js +.LAST_PACKAGE +*.snk +!.eslintrc.js +!jest.config.js + +junit.xml +!**/*.snapshot/**/asset.*/*.js +!**/*.snapshot/**/asset.*/*.d.ts + +!**/*.snapshot/**/asset.*/** diff --git a/packages/@aws-cdk/aws-appconfig-alpha/.npmignore b/packages/@aws-cdk/aws-appconfig-alpha/.npmignore new file mode 100644 index 0000000000000..22661f119c020 --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/.npmignore @@ -0,0 +1,31 @@ +# Don't include original .ts files when doing `npm pack` +*.ts +!*.d.ts +coverage +.nyc_output +*.tgz + +dist +.LAST_PACKAGE +.LAST_BUILD +!*.js + +# Include .jsii +!.jsii + +*.snk + +*.tsbuildinfo + +tsconfig.json +.eslintrc.js +jest.config.js + +# exclude cdk artifacts +**/cdk.out +junit.xml +!*.lit.ts +**/*.snapshot + +# exclude test +test/ diff --git a/packages/@aws-cdk/aws-appconfig-alpha/LICENSE b/packages/@aws-cdk/aws-appconfig-alpha/LICENSE new file mode 100644 index 0000000000000..9b722c65c5481 --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018-2023 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. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/@aws-cdk/aws-appconfig-alpha/NOTICE b/packages/@aws-cdk/aws-appconfig-alpha/NOTICE new file mode 100644 index 0000000000000..a27b7dd317649 --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/NOTICE @@ -0,0 +1,2 @@ +AWS Cloud Development Kit (AWS CDK) +Copyright 2018-2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. diff --git a/packages/@aws-cdk/aws-appconfig-alpha/README.md b/packages/@aws-cdk/aws-appconfig-alpha/README.md new file mode 100644 index 0000000000000..c20878cbbad24 --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/README.md @@ -0,0 +1,443 @@ +# AWS AppConfig Construct Library + + + +--- + +![cdk-constructs: Experimental](https://img.shields.io/badge/cdk--constructs-experimental-important.svg?style=for-the-badge) + +> The APIs of higher level constructs in this module are experimental and under active development. +> They are subject to non-backward compatible changes or removal in any future version. These are +> not subject to the [Semantic Versioning](https://semver.org/) model and breaking changes will be +> announced in the release notes. 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. + +--- + + + +This module is part of the [AWS Cloud Development Kit](https://github.com/aws/aws-cdk) project. + +Use AWS AppConfig, a capability of AWS Systems Manager, to create, manage, and quickly deploy application configurations. A configuration is a collection of settings that influence the behavior of your application. You can use AWS AppConfig with applications hosted on Amazon Elastic Compute Cloud (Amazon EC2) instances, AWS Lambda, containers, mobile applications, or IoT devices. To view examples of the types of configurations you can manage by using AWS AppConfig, see [Example configurations](https://docs.aws.amazon.com/appconfig/latest/userguide/appconfig-creating-configuration-and-profile.html#appconfig-creating-configuration-and-profile-examples). + +## Application + +In AWS AppConfig, an application is simply an organizational construct like a folder. This organizational construct has a relationship with some unit of executable code. For example, you could create an application called MyMobileApp to organize and manage configuration data for a mobile application installed by your users. Configurations and environments are associated with the application. + +The name and description of an application are optional. + +Create a simple application: + +```ts +new appconfig.Application(this, 'MyApplication'); +``` + +Create an application with a name and description: + +```ts +new appconfig.Application(this, 'MyApplication', { + name: 'App1', + description: 'This is my application created through CDK.', +}); +``` + +## Deployment Strategy + +A deployment strategy defines how a configuration will roll out. The roll out is defined by four parameters: deployment type, step percentage, deployment time, and bake time. +See: https://docs.aws.amazon.com/appconfig/latest/userguide/appconfig-creating-deployment-strategy.html + +Deployment strategy with predefined values: + +```ts +new appconfig.DeploymentStrategy(this, 'MyDeploymentStrategy', { + rolloutStrategy: appconfig.RolloutStrategy.CANARY_10_PERCENT_20_MINUTES, +}); +``` + +Deployment strategy with custom values: + +```ts +new appconfig.DeploymentStrategy(this, 'MyDeploymentStrategy', { + rolloutStrategy: appconfig.RolloutStrategy.linear({ + growthFactor: 20, + deploymentDuration: Duration.minutes(30), + finalBakeTime: Duration.minutes(30), + }), +}); +``` + +## Configuration + +A configuration is a higher-level construct that can either be a `HostedConfiguration` (stored internally through AWS AppConfig) or a `SourcedConfiguration` (stored in an Amazon S3 bucket, AWS Secrets Manager secrets, Systems Manager (SSM) Parameter Store parameters, SSM documents, or AWS CodePipeline). This construct manages deployments on creation. + +### HostedConfiguration + +A hosted configuration represents configuration stored in the AWS AppConfig hosted configuration store. A hosted configuration takes in the configuration content and associated AWS AppConfig application. On construction of a hosted configuration, the configuration is deployed. + +```ts +declare const application: appconfig.Application; + +new appconfig.HostedConfiguration(this, 'MyHostedConfiguration', { + application, + content: appconfig.ConfigurationContent.fromInline('This is my configuration content.'), +}); +``` + +AWS AppConfig supports the following types of configuration profiles. + +* **Feature flag**: Use a feature flag configuration to turn on new features that require a timely deployment, such as a product launch or announcement. +* **Freeform**: Use a freeform configuration to carefully introduce changes to your application. + +A hosted configuration with type: + +```ts +declare const application: appconfig.Application; + +new appconfig.HostedConfiguration(this, 'MyHostedConfiguration', { + application, + content: appconfig.ConfigurationContent.fromInline('This is my configuration content.'), + type: appconfig.ConfigurationType.FEATURE_FLAGS, +}); +``` + +When you create a configuration and configuration profile, you can specify up to two validators. A validator ensures that your configuration data is syntactically and semantically correct. You can create validators in either JSON Schema or as an AWS Lambda function. +See [About validators](https://docs.aws.amazon.com/appconfig/latest/userguide/appconfig-creating-configuration-and-profile.html#appconfig-creating-configuration-and-profile-validators) for more information. + +A hosted configuration with validators: + +```ts +declare const application: appconfig.Application; +declare const fn: lambda.Function; + +new appconfig.HostedConfiguration(this, 'MyHostedConfiguration', { + application, + content: appconfig.ConfigurationContent.fromInline('This is my configuration content.'), + validators: [ + appconfig.JsonSchemaValidator.fromFile('schema.json'), + appconfig.LambdaValidator.fromFunction(fn), + ], +}); +``` + +You can attach a deployment strategy (as described in the previous section) to your configuration to specify how you want your configuration to roll out. + +A hosted configuration with a deployment strategy: + +```ts +declare const application: appconfig.Application; + +new appconfig.HostedConfiguration(this, 'MyHostedConfiguration', { + application, + content: appconfig.ConfigurationContent.fromInline('This is my configuration content.'), + deploymentStrategy: new appconfig.DeploymentStrategy(this, 'MyDeploymentStrategy', { + rolloutStrategy: appconfig.RolloutStrategy.linear({ + growthFactor: 15, + deploymentDuration: Duration.minutes(30), + finalBakeTime: Duration.minutes(15), + }), + }), +}); +``` + +The `deployTo` parameter is used to specify which environments to deploy the configuration to. If this parameter is not specified, there will only be a deployment if there is one environment associated to the AWS AppConfig application. + +A hosted configuration with `deployTo`: + +```ts +declare const application: appconfig.Application; +declare const env: appconfig.Environment; + +new appconfig.HostedConfiguration(this, 'MyHostedConfiguration', { + application, + content: appconfig.ConfigurationContent.fromInline('This is my configuration content.'), + deployTo: [env], +}); +``` + +### SourcedConfiguration + +A sourced configuration represents configuration stored in an Amazon S3 bucket, AWS Secrets Manager secret, Systems Manager (SSM) Parameter Store parameter, SSM document, or AWS CodePipeline. A sourced configuration takes in the location source construct and optionally a version number to deploy. On construction of a sourced configuration, the configuration is deployed only if a version number is specified. + +### S3 + +Use an Amazon S3 bucket to store a configuration. + +```ts +declare const application: appconfig.Application; + +const bucket = new s3.Bucket(this, 'MyBucket', { + versioned: true, +}); + +new appconfig.SourcedConfiguration(this, 'MySourcedConfiguration', { + application, + location: appconfig.ConfigurationSource.fromBucket(bucket, 'path/to/file.json'), +}); +``` + +Use an encrypted bucket: + +```ts +declare const application: appconfig.Application; + +const bucket = new s3.Bucket(this, 'MyBucket', { + versioned: true, + encryption: s3.BucketEncryption.KMS, +}); + +new appconfig.SourcedConfiguration(this, 'MySourcedConfiguration', { + application, + location: appconfig.ConfigurationSource.fromBucket(bucket, 'path/to/file.json'), +}); +``` + +### AWS Secrets Manager secret + +Use a Secrets Manager secret to store a configuration. + +```ts +declare const application: appconfig.Application; +declare const secret: secrets.Secret; + +new appconfig.SourcedConfiguration(this, 'MySourcedConfiguration', { + application, + location: appconfig.ConfigurationSource.fromSecret(secret), +}); +``` + +### SSM Parameter Store parameter + +Use an SSM parameter to store a configuration. + +```ts +declare const application: appconfig.Application; +declare const parameter: ssm.StringParameter; + +new appconfig.SourcedConfiguration(this, 'MySourcedConfiguration', { + application, + location: appconfig.ConfigurationSource.fromParameter(parameter), + versionNumber: '1', +}); +``` + +### SSM document + +Use an SSM document to store a configuration. + +```ts +declare const application: appconfig.Application; +declare const document: ssm.CfnDocument; + +new appconfig.SourcedConfiguration(this, 'MySourcedConfiguration', { + application, + location: appconfig.ConfigurationSource.fromCfnDocument(document), +}); +``` + +### AWS CodePipeline + +Use an AWS CodePipeline pipeline to store a configuration. + +```ts +declare const application: appconfig.Application; +declare const pipeline: codepipeline.Pipeline; + +new appconfig.SourcedConfiguration(this, 'MySourcedConfiguration', { + application, + location: appconfig.ConfigurationSource.fromPipeline(pipeline), +}); +``` + +Similar to a hosted configuration, a sourced configuration can optionally take in a type, validators, a `deployTo` parameter, and a deployment strategy. + +A sourced configuration with type: + +```ts +declare const application: appconfig.Application; +declare const bucket: s3.Bucket; + +new appconfig.SourcedConfiguration(this, 'MySourcedConfiguration', { + application, + location: appconfig.ConfigurationSource.fromBucket(bucket, 'path/to/file.json'), + type: appconfig.ConfigurationType.FEATURE_FLAGS, + name: 'MyConfig', + description: 'This is my sourced configuration from CDK.', +}); +``` + +A sourced configuration with validators: + +```ts +declare const application: appconfig.Application; +declare const bucket: s3.Bucket; +declare const fn: lambda.Function; + +new appconfig.SourcedConfiguration(this, 'MySourcedConfiguration', { + application, + location: appconfig.ConfigurationSource.fromBucket(bucket, 'path/to/file.json'), + validators: [ + appconfig.JsonSchemaValidator.fromFile('schema.json'), + appconfig.LambdaValidator.fromFunction(fn), + ], +}); +``` + +A sourced configuration with a deployment strategy: + +```ts +declare const application: appconfig.Application; +declare const bucket: s3.Bucket; + +new appconfig.SourcedConfiguration(this, 'MySourcedConfiguration', { + application, + location: appconfig.ConfigurationSource.fromBucket(bucket, 'path/to/file.json'), + deploymentStrategy: new appconfig.DeploymentStrategy(this, 'MyDeploymentStrategy', { + rolloutStrategy: appconfig.RolloutStrategy.linear({ + growthFactor: 15, + deploymentDuration: Duration.minutes(30), + finalBakeTime: Duration.minutes(15), + }), + }), +}); +``` + +The `deployTo` parameter is used to specify which environments to deploy the configuration to. If this parameter is not specified, there will only be a deployment if there is one environment associated to the AWS AppConfig application. + +A sourced configuration with `deployTo`: + +```ts +declare const application: appconfig.Application; +declare const bucket: s3.Bucket; +declare const env: appconfig.Environment; + +new appconfig.SourcedConfiguration(this, 'MySourcedConfiguration', { + application, + location: appconfig.ConfigurationSource.fromBucket(bucket, 'path/to/file.json'), + deployTo: [env], +}); +``` + +## Environment + +For each AWS AppConfig application, you define one or more environments. An environment is a logical deployment group of AWS AppConfig targets, such as applications in a Beta or Production environment. You can also define environments for application subcomponents such as the Web, Mobile, and Back-end components for your application. You can configure Amazon CloudWatch alarms for each environment. The system monitors alarms during a configuration deployment. If an alarm is triggered, the system rolls back the configuration. + +Basic environment with monitors: + +```ts +declare const application: appconfig.Application; +declare const alarm: cloudwatch.Alarm; + +new appconfig.Environment(this, 'MyEnvironment', { + application, + monitors: [ + {alarm}, + ] +}); +``` + +## Extension + +An extension augments your ability to inject logic or behavior at different points during the AWS AppConfig workflow of creating or deploying a configuration. +See: https://docs.aws.amazon.com/appconfig/latest/userguide/working-with-appconfig-extensions.html + +### AWS Lambda destination + +Use an AWS Lambda as the event destination for an extension. + +```ts +declare const fn: lambda.Function; + +new appconfig.Extension(this, 'MyExtension', { + actions: [ + new appconfig.Action({ + actionPoints: [appconfig.ActionPoint.ON_DEPLOYMENT_START], + eventDestination: new appconfig.LambdaDestination(fn), + }), + ], +}); +``` + +Lambda extension with parameters: + +```ts +declare const fn: lambda.Function; + +new appconfig.Extension(this, 'MyExtension', { + actions: [ + new appconfig.Action({ + actionPoints: [appconfig.ActionPoint.ON_DEPLOYMENT_START], + eventDestination: new appconfig.LambdaDestination(fn), + }), + ], + parameters: [ + appconfig.Parameter.required('testParam', 'true'), + appconfig.Parameter.notRequired('testNotRequiredParam'), + ] +}); +``` + + +### Amazon Simple Queue Service (SQS) destination + +Use a queue as the event destination for an extension. + +```ts +declare const queue: sqs.Queue; + +new appconfig.Extension(this, 'MyExtension', { + actions: [ + new appconfig.Action({ + actionPoints: [appconfig.ActionPoint.ON_DEPLOYMENT_START], + eventDestination: new appconfig.SqsDestination(queue), + }), + ], +}); +``` + +### Amazon Simple Notification Service (SNS) destination + +Use an SNS topic as the event destination for an extension. + +```ts +declare const topic: sns.Topic; + +new appconfig.Extension(this, 'MyExtension', { + actions: [ + new appconfig.Action({ + actionPoints: [appconfig.ActionPoint.ON_DEPLOYMENT_START], + eventDestination: new appconfig.SnsDestination(topic), + }), + ], +}); +``` + +### Amazon EventBridge destination + +Use the default event bus as the event destination for an extension. + +```ts +const bus = events.EventBus.fromEventBusName(this, 'MyEventBus', 'default'); + +new appconfig.Extension(this, 'MyExtension', { + actions: [ + new appconfig.Action({ + actionPoints: [appconfig.ActionPoint.ON_DEPLOYMENT_START], + eventDestination: new appconfig.EventBridgeDestination(bus), + }), + ], +}); +``` + +You can also add extensions and their associations directly by calling `onDeploymentComplete()` or any other action point method on the AWS AppConfig application, configuration, or environment resource. To add an association to an existing extension, you can call `addExtension()` on the resource. + +Adding an association to an AWS AppConfig application: + +```ts +declare const application: appconfig.Application; +declare const extension: appconfig.Extension; +declare const lambdaDestination: appconfig.LambdaDestination; + +application.addExtension(extension); +application.onDeploymentComplete(lambdaDestination); +``` + diff --git a/packages/@aws-cdk/aws-appconfig-alpha/jest.config.js b/packages/@aws-cdk/aws-appconfig-alpha/jest.config.js new file mode 100644 index 0000000000000..3a2fd93a1228a --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/jest.config.js @@ -0,0 +1,2 @@ +const baseConfig = require('@aws-cdk/cdk-build-tools/config/jest.config'); +module.exports = baseConfig; diff --git a/packages/@aws-cdk/aws-appconfig-alpha/lib/application.ts b/packages/@aws-cdk/aws-appconfig-alpha/lib/application.ts new file mode 100644 index 0000000000000..9ad5cafd5d599 --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/lib/application.ts @@ -0,0 +1,396 @@ +import * as cdk from 'aws-cdk-lib'; +import { Names } from 'aws-cdk-lib'; +import { CfnApplication } from 'aws-cdk-lib/aws-appconfig'; +import * as ecs from 'aws-cdk-lib/aws-ecs'; +import { Construct } from 'constructs'; +import { HostedConfiguration, HostedConfigurationOptions, SourcedConfiguration, SourcedConfigurationOptions } from './configuration'; +import { Environment, EnvironmentOptions, IEnvironment } from './environment'; +import { ActionPoint, IEventDestination, ExtensionOptions, IExtension, IExtensible, ExtensibleBase } from './extension'; + +/** + * Defines the platform for the AWS AppConfig Lambda extension. + */ +export enum Platform { + X86_64 = 'x86-64', + ARM_64 = 'ARM64', +} + +export interface IApplication extends cdk.IResource { + /** + * The description of the application. + */ + readonly description?: string; + + /** + * The name of the application. + */ + readonly name?: string; + + /** + * The ID of the application. + */ + readonly applicationId: string; + + /** + * The Amazon Resource Name (ARN) of the application. + */ + readonly applicationArn: string; + + /** + * Adds an environment. + * + * @param id The name of the environment construct + * @param options The options for the environment construct + */ + addEnvironment(id: string, options?: EnvironmentOptions): IEnvironment; + + /** + * Adds a hosted configuration. + * + * @param id The name of the hosted configuration construct + * @param options The options for the hosted configuration construct + */ + addHostedConfiguration(id: string, options: HostedConfigurationOptions): HostedConfiguration; + + /** + * Adds a sourced configuration. + * + * @param id The name of the sourced configuration construct + * @param options The options for the sourced configuration construct + */ + addSourcedConfiguration(id: string, options: SourcedConfigurationOptions): SourcedConfiguration; + + /** + * Adds an existing environment. + * + * @param environment The environment + */ + addExistingEnvironment(environment: IEnvironment): void; + + /** + * Returns the list of associated environments. + */ + get environments(): IEnvironment[]; +} + +export interface ApplicationProps { + /** + * The name of the application. + * + * @default - A name is generated. + */ + readonly name?: string; + + /** + * The description for the application. + * + * @default - No description. + */ + readonly description?: string; +} + +abstract class ApplicationBase extends cdk.Resource implements IApplication, IExtensible { + public abstract applicationId: string; + public abstract applicationArn: string; + private _environments: IEnvironment[] = []; + protected abstract extensible: ExtensibleBase; + + public addEnvironment(id: string, options: EnvironmentOptions = {}): IEnvironment { + return new Environment(this, id, { + application: this, + ...options, + }); + } + + public addHostedConfiguration(id: string, options: HostedConfigurationOptions): HostedConfiguration { + return new HostedConfiguration(this, id, { + application: this, + ...options, + }); + } + + public addSourcedConfiguration(id: string, options: SourcedConfigurationOptions): SourcedConfiguration { + return new SourcedConfiguration(this, id, { + application: this, + ...options, + }); + } + + public addExistingEnvironment(environment: IEnvironment) { + this._environments.push(environment); + } + + get environments(): IEnvironment[] { + return this._environments; + } + + /** + * Adds an extension defined by the action point and event destination + * and also creates an extension association to an application. + * + * @param actionPoint The action point which triggers the event + * @param eventDestination The event that occurs during the extension + * @param options Options for the extension + */ + public on(actionPoint: ActionPoint, eventDestination: IEventDestination, options?: ExtensionOptions) { + this.extensible.on(actionPoint, eventDestination, options); + } + + /** + * Adds a PRE_CREATE_HOSTED_CONFIGURATION_VERSION extension with the + * provided event destination and also creates an extension association to an application. + * + * @param eventDestination The event that occurs during the extension + * @param options Options for the extension + */ + public preCreateHostedConfigurationVersion(eventDestination: IEventDestination, options?: ExtensionOptions) { + this.extensible.preCreateHostedConfigurationVersion(eventDestination, options); + } + + /** + * Adds a PRE_START_DEPLOYMENT extension with the provided event destination and + * also creates an extension association to an application. + * + * @param eventDestination The event that occurs during the extension + * @param options Options for the extension + */ + public preStartDeployment(eventDestination: IEventDestination, options?: ExtensionOptions) { + this.extensible.preStartDeployment(eventDestination, options); + } + + /** + * Adds an ON_DEPLOYMENT_START extension with the provided event destination and + * also creates an extension association to an application. + * + * @param eventDestination The event that occurs during the extension + * @param options Options for the extension + */ + public onDeploymentStart(eventDestination: IEventDestination, options?: ExtensionOptions) { + this.extensible.onDeploymentStart(eventDestination, options); + } + + /** + * Adds an ON_DEPLOYMENT_STEP extension with the provided event destination and + * also creates an extension association to an application. + * + * @param eventDestination The event that occurs during the extension + * @param options Options for the extension + */ + public onDeploymentStep(eventDestination: IEventDestination, options?: ExtensionOptions) { + this.extensible.onDeploymentStep(eventDestination, options); + } + + /** + * Adds an ON_DEPLOYMENT_BAKING extension with the provided event destination and + * also creates an extension association to an application. + * + * @param eventDestination The event that occurs during the extension + * @param options Options for the extension + */ + public onDeploymentBaking(eventDestination: IEventDestination, options?: ExtensionOptions) { + this.extensible.onDeploymentBaking(eventDestination, options); + } + + /** + * Adds an ON_DEPLOYMENT_COMPLETE extension with the provided event destination and + * also creates an extension association to an application. + * + * @param eventDestination The event that occurs during the extension + * @param options Options for the extension + */ + public onDeploymentComplete(eventDestination: IEventDestination, options?: ExtensionOptions) { + this.extensible.onDeploymentComplete(eventDestination, options); + } + + /** + * Adds an ON_DEPLOYMENT_ROLLED_BACK extension with the provided event destination and + * also creates an extension association to an application. + * + * @param eventDestination The event that occurs during the extension + * @param options Options for the extension + */ + public onDeploymentRolledBack(eventDestination: IEventDestination, options?: ExtensionOptions) { + this.extensible.onDeploymentRolledBack(eventDestination, options); + } + + /** + * Adds an extension association to the application. + * + * @param extension The extension to create an association for + */ + public addExtension(extension: IExtension) { + this.extensible.addExtension(extension); + } +} + +/** + * An AWS AppConfig application. + * + * @resource AWS::AppConfig::Application + * @see https://docs.aws.amazon.com/appconfig/latest/userguide/appconfig-creating-application.html + */ +export class Application extends ApplicationBase { + /** + * Imports an AWS AppConfig application into the CDK using its Amazon Resource Name (ARN). + * + * @param scope The parent construct + * @param id The name of the application construct + * @param applicationArn The Amazon Resource Name (ARN) of the application + */ + public static fromApplicationArn(scope: Construct, id: string, applicationArn: string): IApplication { + const parsedArn = cdk.Stack.of(scope).splitArn(applicationArn, cdk.ArnFormat.SLASH_RESOURCE_NAME); + const applicationId = parsedArn.resourceName; + if (!applicationId) { + throw new Error('Missing required application id from application ARN'); + } + + class Import extends ApplicationBase { + public readonly applicationId = applicationId!; + public readonly applicationArn = applicationArn; + protected readonly extensible = new ExtensibleBase(scope, this.applicationArn); + } + + return new Import(scope, id); + } + + /** + * Imports an AWS AppConfig application into the CDK using its ID. + * + * @param scope The parent construct + * @param id The name of the application construct + * @param applicationId The ID of the application + */ + public static fromApplicationId(scope: Construct, id: string, applicationId: string): IApplication { + const stack = cdk.Stack.of(scope); + const applicationArn = stack.formatArn({ + service: 'appconfig', + resource: 'application', + resourceName: applicationId, + }); + + class Import extends ApplicationBase { + public readonly applicationId = applicationId; + public readonly applicationArn = applicationArn; + protected readonly extensible = new ExtensibleBase(scope, this.applicationArn); + } + + return new Import(scope, id); + } + + /** + * Retrieves the Lambda layer version Amazon Resource Name (ARN) for the AWS AppConfig Lambda extension. + * + * @param region The region for the Lambda layer (for example, 'us-east-1') + * @param platform The platform for the Lambda layer (default is Platform.X86_64) + * @returns Lambda layer version ARN + */ + public static getLambdaLayerVersionArn(region: string, platform?: Platform): string { + return lambdaLayerVersions[platform || Platform.X86_64][region]; + } + + /** + * Adds the AWS AppConfig Agent as a container to the provided ECS task definition. + * + * @param taskDef The ECS task definition + */ + public static addAgentToEcs(taskDef: ecs.TaskDefinition) { + taskDef.addContainer('AppConfigAgentContainer', { + image: ecs.ContainerImage.fromRegistry('public.ecr.aws/aws-appconfig/aws-appconfig-agent:latest'), + containerName: 'AppConfigAgentContainer', + }); + } + + /** + * The description of the application. + */ + public readonly description?: string; + + /** + * The name of the application. + */ + public readonly name?: string; + + /** + * The ID of the application. + */ + public readonly applicationId: string; + + /** + * The Amazon Resource Name (ARN) of the application. + */ + public readonly applicationArn: string; + + private _application: CfnApplication; + protected extensible: ExtensibleBase; + + constructor(scope: Construct, id: string, props: ApplicationProps = {}) { + super(scope, id); + + this.description = props.description; + this.name = props.name || Names.uniqueResourceName(this, { + maxLength: 64, + separator: '-', + }); + + this._application = new CfnApplication(this, 'Resource', { + name: this.name, + description: this.description, + }); + this.applicationId = this._application.ref; + this.applicationArn = cdk.Stack.of(this).formatArn({ + service: 'appconfig', + resource: 'application', + resourceName: this.applicationId, + }); + + this.extensible = new ExtensibleBase(scope, this.applicationArn, this.name); + } +} + +const lambdaLayerVersions: {[key: string]: {[key: string]: string}} = { + [Platform.X86_64]: { + 'us-east-1': 'arn:aws:lambda:us-east-1:027255383542:layer:AWS-AppConfig-Extension:110', + 'us-east-2': 'arn:aws:lambda:us-east-2:728743619870:layer:AWS-AppConfig-Extension:79', + 'us-west-1': 'arn:aws:lambda:us-west-1:958113053741:layer:AWS-AppConfig-Extension:121', + 'us-west-2': 'arn:aws:lambda:us-west-2:359756378197:layer:AWS-AppConfig-Extension:143', + 'ca-central-1': 'arn:aws:lambda:ca-central-1:039592058896:layer:AWS-AppConfig-Extension:79', + 'eu-central-1': 'arn:aws:lambda:eu-central-1:066940009817:layer:AWS-AppConfig-Extension:91', + 'eu-central-2': 'arn:aws:lambda:eu-central-2:758369105281:layer:AWS-AppConfig-Extension:29', + 'eu-west-1': 'arn:aws:lambda:eu-west-1:434848589818:layer:AWS-AppConfig-Extension:108', + 'eu-west-2': 'arn:aws:lambda:eu-west-2:282860088358:layer:AWS-AppConfig-Extension:79', + 'eu-west-3': 'arn:aws:lambda:eu-west-3:493207061005:layer:AWS-AppConfig-Extension:80', + 'eu-north-1': 'arn:aws:lambda:eu-north-1:646970417810:layer:AWS-AppConfig-Extension:139', + 'eu-south-1': 'arn:aws:lambda:eu-south-1:203683718741:layer:AWS-AppConfig-Extension:71', + 'eu-south-2': 'arn:aws:lambda:eu-south-2:586093569114:layer:AWS-AppConfig-Extension:26', + 'cn-north-1': 'arn:aws-cn:lambda:cn-north-1:615057806174:layer:AWS-AppConfig-Extension:66', + 'cn-northwest-1': 'arn:aws-cn:lambda:cn-northwest-1:615084187847:layer:AWS-AppConfig-Extension:66', + 'ap-east-1': 'arn:aws:lambda:ap-east-1:630222743974:layer:AWS-AppConfig-Extension:71', + 'ap-northeast-1': 'arn:aws:lambda:ap-northeast-1:980059726660:layer:AWS-AppConfig-Extension:82', + 'ap-northeast-2': 'arn:aws:lambda:ap-northeast-2:826293736237:layer:AWS-AppConfig-Extension:91', + 'ap-northeast-3': 'arn:aws:lambda:ap-northeast-3:706869817123:layer:AWS-AppConfig-Extension:84', + 'ap-southeast-1': 'arn:aws:lambda:ap-southeast-1:421114256042:layer:AWS-AppConfig-Extension:89', + 'ap-southeast-2': 'arn:aws:lambda:ap-southeast-2:080788657173:layer:AWS-AppConfig-Extension:91', + 'ap-southeast-3': 'arn:aws:lambda:ap-southeast-3:418787028745:layer:AWS-AppConfig-Extension:60', + 'ap-southeast-4': 'arn:aws:lambda:ap-southeast-4:307021474294:layer:AWS-AppConfig-Extension:2', + 'ap-south-1': 'arn:aws:lambda:ap-south-1:554480029851:layer:AWS-AppConfig-Extension:92', + 'ap-south-2': 'arn:aws:lambda:ap-south-2:489524808438:layer:AWS-AppConfig-Extension:29', + 'sa-east-1': 'arn:aws:lambda:sa-east-1:000010852771:layer:AWS-AppConfig-Extension:110', + 'af-south-1': 'arn:aws:lambda:af-south-1:574348263942:layer:AWS-AppConfig-Extension:71', + 'me-central-1': 'arn:aws:lambda:me-central-1:662846165436:layer:AWS-AppConfig-Extension:31', + 'me-south-1': 'arn:aws:lambda:me-south-1:559955524753:layer:AWS-AppConfig-Extension:71', + 'us-gov-east-1': 'arn:aws-us-gov:lambda:us-gov-east-1:946561847325:layer:AWS-AppConfig-Extension:44', + 'us-gov-west-1': 'arn:aws-us-gov:lambda:us-gov-west-1:946746059096:layer:AWS-AppConfig-Extension:44', + }, + [Platform.ARM_64]: { + 'us-east-1': 'arn:aws:lambda:us-east-1:027255383542:layer:AWS-AppConfig-Extension-Arm64:43', + 'us-east-2': 'arn:aws:lambda:us-east-2:728743619870:layer:AWS-AppConfig-Extension-Arm64:31', + 'us-west-2': 'arn:aws:lambda:us-west-2:359756378197:layer:AWS-AppConfig-Extension-Arm64:45', + 'eu-central-1': 'arn:aws:lambda:eu-central-1:066940009817:layer:AWS-AppConfig-Extension-Arm64:34', + 'eu-west-1': 'arn:aws:lambda:eu-west-1:434848589818:layer:AWS-AppConfig-Extension-Arm64:46', + 'eu-west-2': 'arn:aws:lambda:eu-west-2:282860088358:layer:AWS-AppConfig-Extension-Arm64:31', + 'ap-northeast-1': 'arn:aws:lambda:ap-northeast-1:980059726660:layer:AWS-AppConfig-Extension-Arm64:35', + 'ap-southeast-1': 'arn:aws:lambda:ap-southeast-1:421114256042:layer:AWS-AppConfig-Extension-Arm64:41', + 'ap-southeast-2': 'arn:aws:lambda:ap-southeast-2:080788657173:layer:AWS-AppConfig-Extension-Arm64:34', + 'ap-south-1': 'arn:aws:lambda:ap-south-1:554480029851:layer:AWS-AppConfig-Extension-Arm64:34', + }, +}; \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/lib/configuration.ts b/packages/@aws-cdk/aws-appconfig-alpha/lib/configuration.ts new file mode 100644 index 0000000000000..0681f73487e08 --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/lib/configuration.ts @@ -0,0 +1,904 @@ +import * as fs from 'fs'; +import { PhysicalName, Stack, ArnFormat, Names, RemovalPolicy } from 'aws-cdk-lib'; +import { CfnConfigurationProfile, CfnDeployment, CfnHostedConfigurationVersion } from 'aws-cdk-lib/aws-appconfig'; +import * as cp from 'aws-cdk-lib/aws-codepipeline'; +import * as iam from 'aws-cdk-lib/aws-iam'; +import * as kms from 'aws-cdk-lib/aws-kms'; +import * as lambda from 'aws-cdk-lib/aws-lambda'; +import * as s3 from 'aws-cdk-lib/aws-s3'; +import * as sm from 'aws-cdk-lib/aws-secretsmanager'; +import * as ssm from 'aws-cdk-lib/aws-ssm'; +import { Construct, IConstruct } from 'constructs'; +import { IApplication } from './application'; +import { DeploymentStrategy, IDeploymentStrategy, RolloutStrategy } from './deployment-strategy'; +import { IEnvironment } from './environment'; +import { ActionPoint, IEventDestination, ExtensionOptions, IExtension, IExtensible, ExtensibleBase } from './extension'; +import { getHash } from './private/hash'; + +export interface ConfigurationOptions { + /** + * The deployment strategy for the configuration. + * + * @default - A deployment strategy with the rollout strategy set to + * RolloutStrategy.CANARY_10_PERCENT_20_MINUTES + */ + readonly deploymentStrategy?: IDeploymentStrategy; + + /** + * The name of the configuration. + * + * @default - A name is generated. + */ + readonly name?: string; + + /** + * The validators for the configuration. + * + * @default - No validators. + */ + readonly validators?: IValidator[]; + + /** + * The description of the configuration. + * + * @default - No description. + */ + readonly description?: string; + + /** + * The type of configuration. + * + * @default ConfigurationType.FREEFORM + */ + readonly type?: ConfigurationType; + + /** + * The list of environments to deploy the configuration to. + * + * If this parameter is not specified and there is only one environment + * associated to the application, then we will deploy to that one. Otherwise, + * there will be no deployment. + * + * @default - None. + */ + readonly deployTo?: IEnvironment[]; + + /** + * The deployment key of the configuration. + * + * @default - None. + */ + readonly deploymentKey?: kms.IKey; +} + +export interface ConfigurationProps extends ConfigurationOptions { + /** + * The application associated with the configuration. + */ + readonly application: IApplication; +} + +export interface IConfiguration extends IConstruct { + /** + * The deployment strategy for the configuration. + */ + readonly deploymentStrategy?: IDeploymentStrategy; + + /** + * The configuration version number. + */ + readonly versionNumber?: string; + + /** + * The application associated with the configuration. + */ + readonly application: IApplication; + + /** + * The name of the configuration. + */ + readonly name?: string; + + /** + * The validators for the configuration. + */ + readonly validators?: IValidator[]; + + /** + * The description of the configuration. + */ + readonly description?: string; + + /** + * The configuration type. + */ + readonly type?: ConfigurationType; + + /** + * The environments to deploy to. + */ + readonly deployTo?: IEnvironment[]; + + /** + * The deployment key for the configuration. + */ + readonly deploymentKey?: kms.IKey; + + /** + * The ID of the configuration profile. + */ + readonly configurationProfileId: string; +} + +abstract class ConfigurationBase extends Construct implements IConfiguration, IExtensible { + public abstract readonly versionNumber?: string; + public abstract readonly configurationProfileId: string; + + /** + * The application associated with the configuration. + */ + public readonly application: IApplication; + + /** + * The environments to deploy to. + */ + public readonly deployTo?: IEnvironment[]; + + /** + * The name of the configuration. + */ + public readonly name?: string; + + /** + * The validators for the configuration. + */ + public readonly validators?: IValidator[]; + + /** + * The description of the configuration. + */ + public readonly description?: string; + + /** + * The configuration type. + */ + public readonly type?: ConfigurationType; + + /** + * The deployment key for the configuration. + */ + public readonly deploymentKey?: kms.IKey; + + /** + * The deployment strategy for the configuration. + */ + readonly deploymentStrategy?: IDeploymentStrategy; + + protected applicationId: string; + protected extensible!: ExtensibleBase; + + constructor(scope: Construct, id: string, props: ConfigurationProps) { + super(scope, id); + + this.name = props.name || Names.uniqueResourceName(this, { + maxLength: 128, + separator: '-', + }); + this.application = props.application; + this.applicationId = this.application.applicationId; + this.type = props.type; + this.validators = props.validators; + this.description = props.description; + this.deployTo = props.deployTo; + this.deploymentKey = props.deploymentKey; + this.deploymentStrategy = props.deploymentStrategy || new DeploymentStrategy(this, 'DeploymentStrategy', { + rolloutStrategy: RolloutStrategy.CANARY_10_PERCENT_20_MINUTES, + }); + } + + protected abstract getDeploymentHash(environment: IEnvironment): string; + + /** + * Adds an extension defined by the action point and event destination + * and also creates an extension association to the configuration profile. + * + * @param actionPoint The action point which triggers the event + * @param eventDestination The event that occurs during the extension + * @param options Options for the extension + */ + public on(actionPoint: ActionPoint, eventDestination: IEventDestination, options?: ExtensionOptions) { + this.extensible.on(actionPoint, eventDestination, options); + } + + /** + * Adds a PRE_CREATE_HOSTED_CONFIGURATION_VERSION extension with the + * provided event destination and also creates an extension association to the configuration profile. + * + * @param eventDestination The event that occurs during the extension + * @param options Options for the extension + */ + public preCreateHostedConfigurationVersion(eventDestination: IEventDestination, options?: ExtensionOptions) { + this.extensible.preCreateHostedConfigurationVersion(eventDestination, options); + } + + /** + * Adds a PRE_START_DEPLOYMENT extension with the provided event destination + * and also creates an extension association to the configuration profile. + * + * @param eventDestination The event that occurs during the extension + * @param options Options for the extension + */ + public preStartDeployment(eventDestination: IEventDestination, options?: ExtensionOptions) { + this.extensible.preStartDeployment(eventDestination, options); + } + + /** + * Adds an ON_DEPLOYMENT_START extension with the provided event destination + * and also creates an extension association to the configuration profile. + * + * @param eventDestination The event that occurs during the extension + * @param options Options for the extension + */ + public onDeploymentStart(eventDestination: IEventDestination, options?: ExtensionOptions) { + this.extensible.onDeploymentStart(eventDestination, options); + } + + /** + * Adds an ON_DEPLOYMENT_STEP extension with the provided event destination + * and also creates an extension association to the configuration profile. + * + * @param eventDestination The event that occurs during the extension + * @param options Options for the extension + */ + public onDeploymentStep(eventDestination: IEventDestination, options?: ExtensionOptions) { + this.extensible.onDeploymentStep(eventDestination, options); + } + + /** + * Adds an ON_DEPLOYMENT_BAKING extension with the provided event destination and + * also creates an extension association to the configuration profile. + * + * @param eventDestination The event that occurs during the extension + * @param options Options for the extension + */ + public onDeploymentBaking(eventDestination: IEventDestination, options?: ExtensionOptions) { + this.extensible.onDeploymentBaking(eventDestination, options); + } + + /** + * Adds an ON_DEPLOYMENT_COMPLETE extension with the provided event destination + * and also creates an extension association to the configuration profile. + * + * @param eventDestination The event that occurs during the extension + * @param options Options for the extension + */ + public onDeploymentComplete(eventDestination: IEventDestination, options?: ExtensionOptions) { + this.extensible.onDeploymentComplete(eventDestination, options); + } + + /** + * Adds an ON_DEPLOYMENT_ROLLED_BACK extension with the provided event destination + * and also creates an extension association to the configuration profile. + * + * @param eventDestination The event that occurs during the extension + * @param options Options for the extension + */ + public onDeploymentRolledBack(eventDestination: IEventDestination, options?: ExtensionOptions) { + this.extensible.onDeploymentRolledBack(eventDestination, options); + } + + /** + * Adds an extension association to the configuration profile. + * + * @param extension The extension to create an association for + */ + public addExtension(extension: IExtension) { + this.extensible.addExtension(extension); + } + + protected addExistingEnvironmentsToApplication() { + this.deployTo?.forEach((environment) => { + if (!this.application.environments.includes(environment)) { + this.application.addExistingEnvironment(environment); + } + }); + } + + protected deployConfigToEnvironments() { + if (this.application.environments.length == 0) { + this.application.addEnvironment('Environment', { + description: this.description, + }); + } + + if ((!this.deployTo && this.application.environments.length > 1) || !this.versionNumber) { + return; + } + + this.application.environments.forEach((environment) => { + if ((this.deployTo && !this.deployTo.includes(environment))) { + return; + } + const logicalId = `Deployment${this.getDeploymentHash(environment)}`; + if (this.node.tryFindChild(logicalId)) { + return; + } + new CfnDeployment(this, logicalId, { + applicationId: this.application.applicationId, + configurationProfileId: this.configurationProfileId, + deploymentStrategyId: this.deploymentStrategy!.deploymentStrategyId, + environmentId: environment.environmentId, + configurationVersion: this.versionNumber!, + description: this.description, + kmsKeyIdentifier: this.deploymentKey?.keyArn, + }); + }); + } +} + +export interface HostedConfigurationOptions extends ConfigurationOptions { + /** + * The content of the hosted configuration. + */ + readonly content: ConfigurationContent; + + /** + * The content type of the hosted configuration. + */ + readonly contentType?: string; + + /** + * The latest version number of the hosted configuration. + */ + readonly latestVersionNumber?: number; + + /** + * The version label of the hosted configuration. + */ + readonly versionLabel?: string; +} + +export interface HostedConfigurationProps extends ConfigurationProps { + /** + * The content of the hosted configuration. + */ + readonly content: ConfigurationContent; + + /** + * The content type of the hosted configuration. + */ + readonly contentType?: string; + + /** + * The latest version number of the hosted configuration. + */ + readonly latestVersionNumber?: number; + + /** + * The version label of the hosted configuration. + */ + readonly versionLabel?: string; +} + +export class HostedConfiguration extends ConfigurationBase { + /** + * The content of the hosted configuration. + */ + public readonly content: string; + + /** + * The content type of the hosted configuration. + */ + public readonly contentType?: string; + + /** + * The latest version number of the hosted configuration. + */ + public readonly latestVersionNumber?: number; + + /** + * The version label of the hosted configuration. + */ + public readonly versionLabel?: string; + + /** + * The version number of the hosted configuration. + */ + public readonly versionNumber?: string; + + /** + * The Amazon Resource Name (ARN) of the hosted configuration version. + */ + public readonly hostedConfigurationVersionArn: string; + + /** + * The ID of the configuration profile. + */ + public readonly configurationProfileId: string; + + /** + * The Amazon Resource Name (ARN) of the configuration profile. + */ + public readonly configurationProfileArn: string; + + private readonly _cfnConfigurationProfile: CfnConfigurationProfile; + private readonly _cfnHostedConfigurationVersion: CfnHostedConfigurationVersion; + + constructor(scope: Construct, id: string, props: HostedConfigurationProps) { + super(scope, id, props); + + this._cfnConfigurationProfile = new CfnConfigurationProfile(this, 'ConfigurationProfile', { + applicationId: this.applicationId, + locationUri: 'hosted', + name: this.name!, + description: this.description, + type: this.type, + validators: this.validators, + }); + this.configurationProfileId = this._cfnConfigurationProfile.ref; + this.configurationProfileArn = Stack.of(this).formatArn({ + service: 'appconfig', + resource: 'application', + resourceName: `${this.applicationId}/configurationprofile/${this.configurationProfileId}`, + }); + this.extensible = new ExtensibleBase(scope, this.configurationProfileArn, this.name); + + this.content = props.content.content; + this.contentType = props.contentType || 'application/json'; + this.latestVersionNumber = props.latestVersionNumber; + this.versionLabel = props.versionLabel; + this._cfnHostedConfigurationVersion = new CfnHostedConfigurationVersion(this, 'Resource', { + applicationId: this.applicationId, + configurationProfileId: this.configurationProfileId, + content: this.content, + contentType: this.contentType, + description: this.description, + latestVersionNumber: this.latestVersionNumber, + versionLabel: this.versionLabel, + }); + this._cfnHostedConfigurationVersion.applyRemovalPolicy(RemovalPolicy.RETAIN); + + this.versionNumber = this._cfnHostedConfigurationVersion.ref; + this.hostedConfigurationVersionArn = Stack.of(this).formatArn({ + service: 'appconfig', + resource: 'application', + resourceName: `${this.applicationId}/configurationprofile/${this.configurationProfileId}/hostedconfigurationversion/${this.versionNumber}`, + }); + + this.addExistingEnvironmentsToApplication(); + this.deployConfigToEnvironments(); + } + + protected getDeploymentHash(environment: IEnvironment): string { + const combinedString = ` + ${this.application!.name!} + ${this.name!} + ${environment.name!} + ${this.content} + `; + return getHash(combinedString); + } +} + +export interface SourcedConfigurationOptions extends ConfigurationOptions { + /** + * The location where the configuration is stored. + */ + readonly location: ConfigurationSource; + + /** + * The version number of the sourced configuration to deploy. If this is not specified, + * then there will be no deployment. + * + * @default - None. + */ + readonly versionNumber?: string; + + /** + * The IAM role to retrieve the configuration. + * + * @default - A role is generated. + */ + readonly retrievalRole?: iam.IRole; +} + +export interface SourcedConfigurationProps extends ConfigurationProps { + /** + * The location where the configuration is stored. + */ + readonly location: ConfigurationSource; + + /** + * The version number of the sourced configuration to deploy. If this is not specified, + * then there will be no deployment. + * + * @default - None. + */ + readonly versionNumber?: string; + + /** + * The IAM role to retrieve the configuration. + * + * @default - A role is generated. + */ + readonly retrievalRole?: iam.IRole; +} + +export class SourcedConfiguration extends ConfigurationBase { + /** + * The location where the configuration is stored. + */ + public readonly location: ConfigurationSource; + + /** + * The version number of the configuration to deploy. + */ + public readonly versionNumber?: string; + + /** + * The IAM role to retrieve the configuration. + */ + public readonly retrievalRole?: iam.IRole; + + /** + * The key to decrypt the configuration if applicable. This key + * can be used when storing configuration in AWS Secrets Manager, Systems Manager Parameter Store, + * or Amazon S3. + */ + public readonly sourceKey?: kms.IKey; + + /** + * The ID of the configuration profile. + */ + public readonly configurationProfileId: string; + + /** + * The Amazon Resource Name (ARN) of the configuration profile. + */ + public readonly configurationProfileArn: string; + + private readonly locationUri: string; + private readonly _cfnConfigurationProfile: CfnConfigurationProfile; + + constructor (scope: Construct, id: string, props: SourcedConfigurationProps) { + super(scope, id, props); + + this.location = props.location; + this.locationUri = this.location.locationUri; + this.versionNumber = props.versionNumber; + this.sourceKey = this.location.key; + this.retrievalRole = props.retrievalRole || this.location.type != ConfigurationSourceType.CODE_PIPELINE + ? new iam.Role(this, 'Role', { + roleName: PhysicalName.GENERATE_IF_NEEDED, + assumedBy: new iam.ServicePrincipal('appconfig.amazonaws.com'), + inlinePolicies: { + ['AllowAppConfigReadFromSourcePolicy']: this.getPolicyForRole(), + }, + }) + : undefined; + + this._cfnConfigurationProfile = new CfnConfigurationProfile(this, 'Resource', { + applicationId: this.applicationId, + locationUri: this.locationUri, + name: this.name!, + description: this.description, + retrievalRoleArn: this.retrievalRole?.roleArn, + type: this.type, + validators: this.validators, + }); + + this.configurationProfileId = this._cfnConfigurationProfile.ref; + this.configurationProfileArn = Stack.of(this).formatArn({ + service: 'appconfig', + resource: 'application', + resourceName: `${this.applicationId}/configurationprofile/${this.configurationProfileId}`, + }); + this.extensible = new ExtensibleBase(scope, this.configurationProfileArn, this.name); + + this.addExistingEnvironmentsToApplication(); + this.deployConfigToEnvironments(); + } + + protected getDeploymentHash(environment: IEnvironment): string { + const combinedString = ` + ${this.application!.name!} + ${this.name!} + ${environment.name!} + ${this.versionNumber} + ${this.location.type} + `; + return getHash(combinedString); + } + + private getPolicyForRole(): iam.PolicyDocument { + const policy = new iam.PolicyStatement({ + effect: iam.Effect.ALLOW, + }); + const document = new iam.PolicyDocument({ + statements: [policy], + }); + + if (this.location.type == ConfigurationSourceType.SSM_PARAMETER) { + policy.addActions('ssm:GetParameter'); + policy.addResources(this.locationUri); + } else if (this.location.type == ConfigurationSourceType.SSM_DOCUMENT) { + policy.addActions('ssm:GetDocument'); + policy.addResources(Stack.of(this).formatArn({ + service: 'ssm', + resource: 'document', + resourceName: this.locationUri.split('://')[1], + })); + } else if (this.location.type == ConfigurationSourceType.S3) { + const bucketAndObjectKey = this.locationUri.split('://')[1]; + const sep = bucketAndObjectKey.search('/'); + const bucketName = bucketAndObjectKey.substring(0, sep); + const objectKey = bucketAndObjectKey.substring(sep + 1); + policy.addActions( + 's3:GetObject', + 's3:GetObjectMetadata', + 's3:GetObjectVersion', + ); + policy.addResources(Stack.of(this).formatArn({ + region: '', + account: '', + service: 's3', + arnFormat: ArnFormat.NO_RESOURCE_NAME, + resource: `${bucketName}/${objectKey}`, + })); + const bucketPolicy = new iam.PolicyStatement({ + effect: iam.Effect.ALLOW, + actions: [ + 's3:GetBucketLocation', + 's3:GetBucketVersioning', + 's3:ListBucket', + 's3:ListBucketVersions', + ], + resources: [ + Stack.of(this).formatArn({ + region: '', + account: '', + service: 's3', + arnFormat: ArnFormat.NO_RESOURCE_NAME, + resource: bucketName, + }), + ], + }); + const listBucketsPolicy = new iam.PolicyStatement({ + effect: iam.Effect.ALLOW, + actions: ['s3:ListAllMyBuckets'], + resources: ['*'], + }); + document.addStatements(bucketPolicy, listBucketsPolicy); + } else { + policy.addActions('secretsmanager:GetSecretValue'); + policy.addResources(this.locationUri); + } + + if (this.sourceKey) { + const keyPolicy = new iam.PolicyStatement({ + effect: iam.Effect.ALLOW, + actions: ['kms:Decrypt'], + resources: [this.sourceKey.keyArn], + }); + document.addStatements(keyPolicy); + } + + return document; + } +} + +/** + * The configuration type. + */ +export enum ConfigurationType { + FREEFORM = 'AWS.Freeform', + FEATURE_FLAGS = 'AWS.AppConfig.FeatureFlags', +} + +/** + * The validator type. + */ +export enum ValidatorType { + JSON_SCHEMA = 'JSON_SCHEMA', + LAMBDA = 'LAMBDA', +} + +/** + * The configuration source type. + */ +export enum ConfigurationSourceType { + S3 = 'S3', + SECRETS_MANAGER = 'SECRETS_MANAGER', + SSM_PARAMETER = 'SSM_PARAMETER', + SSM_DOCUMENT = 'SSM_DOCUMENT', + CODE_PIPELINE = 'CODE_PIPELINE' +} + +export interface IValidator { + /** + * The content of the validator. + */ + readonly content: string; + + /** + * The type of validator. + */ + readonly type: ValidatorType; +} + +/** + * Defines a JSON Schema validator. + */ +export abstract class JsonSchemaValidator implements IValidator { + /** + * Defines a JSON Schema validator from a file. + * + * @param path The path to the file that defines the validator + */ + public static fromFile(path: string): JsonSchemaValidator { + return { + content: fs.readFileSync(path).toString(), + type: ValidatorType.JSON_SCHEMA, + }; + } + + /** + * Defines a JSON Schema validator from inline code. + * + * @param code The inline code that defines the validator + */ + public static fromInline(code: string): JsonSchemaValidator { + return { + content: code, + type: ValidatorType.JSON_SCHEMA, + }; + } + + public abstract readonly content: string; + public abstract readonly type: ValidatorType; +} + +/** + * Defines an AWS Lambda validator. + */ +export abstract class LambdaValidator implements IValidator { + /** + * Defines an AWS Lambda validator from a Lambda function. This will call + * `addPermission` to your function to grant AWS AppConfig permissions. + * + * @param func The function that defines the validator + */ + public static fromFunction(func: lambda.Function): LambdaValidator { + if (!func.permissionsNode.tryFindChild('AppConfigPermission')) { + func.addPermission('AppConfigPermission', { + principal: new iam.ServicePrincipal('appconfig.amazonaws.com'), + }); + } + return { + content: func.functionArn, + type: ValidatorType.LAMBDA, + }; + } + + public abstract readonly content: string; + public abstract readonly type: ValidatorType; +} + +/** + * Defines the hosted configuration content. + */ +export abstract class ConfigurationContent { + /** + * Defines the hosted configuration content from a file. + * + * @param path The path to the file that defines configuration content + */ + public static fromFile(path: string): ConfigurationContent { + return { + content: fs.readFileSync(path).toString(), + }; + } + + /** + * Defines the hosted configuration content from inline code. + * + * @param content The inline code that defines the configuration content + */ + public static fromInline(content: string): ConfigurationContent { + return { + content, + }; + } + + /** + * The configuration content. + */ + public abstract readonly content: string; +} + +/** + * Defines the integrated configuration sources. + */ +export abstract class ConfigurationSource { + /** + * Defines configuration content from an Amazon S3 bucket. + * + * @param bucket The S3 bucket where the configuration is stored + * @param objectKey The path to the configuration + * @param key The KMS Key that the bucket is encrypted with + */ + public static fromBucket(bucket: s3.IBucket, objectKey: string, key?: kms.IKey): ConfigurationSource { + return { + locationUri: bucket.s3UrlForObject(objectKey), + type: ConfigurationSourceType.S3, + key, + }; + } + + /** + * Defines configuration content from an AWS Secrets Manager secret. + * + * @param secret The secret where the configuration is stored + */ + public static fromSecret(secret: sm.ISecret): ConfigurationSource { + return { + locationUri: secret.secretArn, + type: ConfigurationSourceType.SECRETS_MANAGER, + key: secret.encryptionKey, + }; + } + + /** + * Defines configuration content from a Systems Manager (SSM) Parameter Store parameter. + * + * @param parameter The parameter where the configuration is stored + * @param key The KMS Key that the secure string is encrypted with + */ + public static fromParameter(parameter: ssm.IParameter, key?: kms.IKey): ConfigurationSource { + return { + locationUri: parameter.parameterArn, + type: ConfigurationSourceType.SSM_PARAMETER, + key, + }; + } + + /** + * Defines configuration content from a Systems Manager (SSM) document. + * + * @param document The SSM document where the configuration is stored + */ + public static fromCfnDocument(document: ssm.CfnDocument): ConfigurationSource { + return { + locationUri: `ssm-document://${document.ref}`, + type: ConfigurationSourceType.SSM_DOCUMENT, + }; + } + + /** + * Defines configuration content from AWS CodePipeline. + * + * @param pipeline The pipeline where the configuration is stored + * @returns + */ + public static fromPipeline(pipeline: cp.IPipeline): ConfigurationSource { + return { + locationUri: `codepipeline://${pipeline.pipelineName}`, + type: ConfigurationSourceType.CODE_PIPELINE, + }; + } + + /** + * The URI of the configuration source. + */ + public abstract readonly locationUri: string; + + /** + * The type of the configuration source. + */ + public abstract readonly type: ConfigurationSourceType; + + /** + * The KMS Key that encrypts the configuration. + */ + public abstract readonly key?: kms.IKey; +} diff --git a/packages/@aws-cdk/aws-appconfig-alpha/lib/deployment-strategy.ts b/packages/@aws-cdk/aws-appconfig-alpha/lib/deployment-strategy.ts new file mode 100644 index 0000000000000..3bc454de41f91 --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/lib/deployment-strategy.ts @@ -0,0 +1,318 @@ +import { Resource, IResource, Stack, ArnFormat, Names, Duration } from 'aws-cdk-lib'; +import { CfnDeploymentStrategy } from 'aws-cdk-lib/aws-appconfig'; +import { Construct } from 'constructs'; + +export interface DeploymentStrategyProps { + /** + * The rollout strategy for the deployment strategy. You can use predefined deployment + * strategies, such as RolloutStrategy.ALL_AT_ONCE, RolloutStrategy.LINEAR_50_PERCENT_EVERY_30_SECONDS, + * or RolloutStrategy.CANARY_10_PERCENT_20_MINUTES. + */ + readonly rolloutStrategy: RolloutStrategy; + + /** + * A name for the deployment strategy. + * + * @default - A name is generated. + */ + readonly name?: string; + + /** + * A description of the deployment strategy. + * + * @default - No description. + */ + readonly description?: string; +} + +/** + * An AWS AppConfig deployment strategy. + * + * @resource AWS::AppConfig::DeploymentStrategy + * @see https://docs.aws.amazon.com/appconfig/latest/userguide/appconfig-creating-deployment-strategy.html + */ +export class DeploymentStrategy extends Resource implements IDeploymentStrategy { + /** + * Imports a deployment strategy into the CDK using its Amazon Resource Name (ARN). + * + * @param scope The parent construct + * @param id The name of the deployment strategy construct + * @param deploymentStrategyArn The Amazon Resource Name (ARN) of the deployment strategy + */ + public static fromDeploymentStrategyArn(scope: Construct, id: string, deploymentStrategyArn: string): IDeploymentStrategy { + const parsedArn = Stack.of(scope).splitArn(deploymentStrategyArn, ArnFormat.SLASH_RESOURCE_NAME); + const deploymentStrategyId = parsedArn.resourceName; + if (!deploymentStrategyId) { + throw new Error('Missing required deployment strategy id from deployment strategy ARN'); + } + + class Import extends Resource implements IDeploymentStrategy { + public readonly deploymentStrategyId = deploymentStrategyId!; + public readonly deploymentStrategyArn = deploymentStrategyArn; + } + + return new Import(scope, id, { + environmentFromArn: deploymentStrategyArn, + }); + } + + /** + * Imports a deployment strategy into the CDK using its ID. + * + * @param scope The parent construct + * @param id The name of the deployment strategy construct + * @param deploymentStrategyId The ID of the deployment strategy + */ + public static fromDeploymentStrategyId(scope: Construct, id: string, deploymentStrategyId: string): IDeploymentStrategy { + const stack = Stack.of(scope); + const deploymentStrategyArn = stack.formatArn({ + service: 'appconfig', + resource: 'deploymentstrategy', + resourceName: deploymentStrategyId, + }); + + class Import extends Resource implements IDeploymentStrategy { + public readonly deploymentStrategyId = deploymentStrategyId; + public readonly deploymentStrategyArn = deploymentStrategyArn; + } + + return new Import(scope, id, { + environmentFromArn: deploymentStrategyArn, + }); + } + + /** + * The name of the deployment strategy. + */ + public readonly name?: string; + + /** + * The deployment duration in minutes of the deployment strategy. + */ + public readonly deploymentDurationInMinutes?: number; + + /** + * The growth factor of the deployment strategy. + */ + public readonly growthFactor?: number; + + /** + * The description of the deployment strategy. + */ + public readonly description?: string; + + /** + * The final bake time in minutes of the deployment strategy. + */ + public readonly finalBakeTimeInMinutes?: number; + + /** + * The growth type of the deployment strategy. + */ + public readonly growthType?: GrowthType; + + /** + * The ID of the deployment strategy. + */ + public readonly deploymentStrategyId: string; + + /** + * The Amazon Resource Name (ARN) of the deployment strategy. + */ + public readonly deploymentStrategyArn: string; + + private readonly _cfnDeploymentStrategy: CfnDeploymentStrategy; + + constructor(scope: Construct, id: string, props: DeploymentStrategyProps) { + super(scope, id, { + physicalName: props.name, + }); + + this.deploymentDurationInMinutes = props.rolloutStrategy.deploymentDuration.toMinutes(); + this.growthFactor = props.rolloutStrategy.growthFactor; + this.description = props.description; + this.finalBakeTimeInMinutes = props.rolloutStrategy.finalBakeTime?.toMinutes(); + this.growthType = props.rolloutStrategy.growthType; + this.name = props.name || Names.uniqueResourceName(this, { + maxLength: 64, + separator: '-', + }); + + const resource = new CfnDeploymentStrategy(this, 'Resource', { + name: this.name, + deploymentDurationInMinutes: this.deploymentDurationInMinutes, + growthFactor: this.growthFactor, + replicateTo: 'NONE', + description: this.description, + finalBakeTimeInMinutes: this.finalBakeTimeInMinutes, + growthType: this.growthType, + }); + this._cfnDeploymentStrategy = resource; + + this.deploymentStrategyId = this._cfnDeploymentStrategy.ref; + this.deploymentStrategyArn = this.stack.formatArn({ + service: 'appconfig', + resource: 'deploymentstrategy', + resourceName: this.deploymentStrategyId, + }); + } +} + +/** + * Defines the growth type of the deployment strategy. + */ +export enum GrowthType { + LINEAR = 'LINEAR', + EXPONENTIAL = 'EXPONENTIAL', +} + +/** + * Defines the deployment strategy ID's of AWS AppConfig predefined strategies. + */ +export enum PredefinedDeploymentStrategyId { + CANARY_10_PERCENT_20_MINUTES = 'AppConfig.Canary10Percent20Minutes', + LINEAR_50_PERCENT_EVERY_30_SECONDS = 'AppConfig.Linear50PercentEvery30Seconds', + LINEAR_20_PERCENT_EVERY_6_MINUTES = 'AppConfig.Linear20PercentEvery6Minutes', + ALL_AT_ONCE = 'AppConfig.AllAtOnce', +} + +export interface RolloutStrategyProps { + /** + * The growth factor of the deployment strategy. This defines + * the percentage of targets to receive a deployed configuration + * during each interval. + */ + readonly growthFactor: number; + + /** + * The deployment duration of the deployment strategy. This defines + * the total amount of time for a deployment to last. + */ + readonly deploymentDuration: Duration; + + /** + * The final bake time of the deployment strategy. + * + * This setting specifies the amount of time AWS AppConfig monitors for Amazon + * CloudWatch alarms after the configuration has been deployed to + * 100% of its targets, before considering the deployment to be complete. + * If an alarm is triggered during this time, AWS AppConfig rolls back + * the deployment. + * + * @default Duration.minutes(0) + */ + readonly finalBakeTime?: Duration; +} + +/** + * Defines the rollout strategy for a deployment strategy and includes the growth factor, + * deployment duration, growth type, and optionally final bake time. + */ +export abstract class RolloutStrategy { + public static readonly CANARY_10_PERCENT_20_MINUTES = RolloutStrategy.exponential({ + growthFactor: 10, + deploymentDuration: Duration.minutes(20), + finalBakeTime: Duration.minutes(10), + }); + public static readonly LINEAR_50_PERCENT_EVERY_30_SECONDS = RolloutStrategy.linear({ + growthFactor: 50, + deploymentDuration: Duration.minutes(1), + finalBakeTime: Duration.minutes(1), + }); + public static readonly LINEAR_20_PERCENT_EVERY_6_MINUTES = RolloutStrategy.linear({ + growthFactor: 20, + deploymentDuration: Duration.minutes(30), + finalBakeTime: Duration.minutes(30), + }); + public static readonly ALL_AT_ONCE = RolloutStrategy.linear({ + growthFactor: 100, + deploymentDuration: Duration.minutes(0), + finalBakeTime: Duration.minutes(10), + }); + + /** + * @returns A linear rollout strategy. + */ + public static linear(props: RolloutStrategyProps): RolloutStrategy { + return { + growthFactor: props.growthFactor, + deploymentDuration: props.deploymentDuration, + growthType: GrowthType.LINEAR, + finalBakeTime: props.finalBakeTime, + }; + } + + /** + * @returns An exponential rollout strategy. + */ + public static exponential(props: RolloutStrategyProps): RolloutStrategy { + return { + growthFactor: props.growthFactor, + deploymentDuration: props.deploymentDuration, + growthType: GrowthType.EXPONENTIAL, + finalBakeTime: props.finalBakeTime, + }; + } + + /** + * The growth factor of the rollout strategy. + */ + public abstract readonly growthFactor: number; + + /** + * The deployment duration of the rollout strategy. + */ + public abstract readonly deploymentDuration: Duration; + + /** + * The growth type of the rollout strategy. + */ + public abstract readonly growthType?: GrowthType; + + /** + * The final bake time of the deployment strategy. + */ + public abstract readonly finalBakeTime?: Duration; +} + +export interface IDeploymentStrategy extends IResource { + /** + * The name of the deployment strategy. + */ + readonly name?: string; + + /** + * The deployment duration in minutes. + */ + readonly deploymentDurationInMinutes?: number; + + /** + * The growth factor of the deployment strategy. + */ + readonly growthFactor?: number; + + /** + * The description of the deployment strategy. + */ + readonly description?: string; + + /** + * The final bake time in minutes. + */ + readonly finalBakeTimeInMinutes?: number; + + /** + * The growth type of the deployment strategy. + */ + readonly growthType?: GrowthType; + + /** + * The ID of the deployment strategy. + */ + readonly deploymentStrategyId: string; + + /** + * The Amazon Resource Name (ARN) of the deployment strategy. + */ + readonly deploymentStrategyArn: string; +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/lib/environment.ts b/packages/@aws-cdk/aws-appconfig-alpha/lib/environment.ts new file mode 100644 index 0000000000000..cd59cfa4265a5 --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/lib/environment.ts @@ -0,0 +1,411 @@ +import { Resource, IResource, Stack, ArnFormat, PhysicalName, Names } from 'aws-cdk-lib'; +import { CfnEnvironment } from 'aws-cdk-lib/aws-appconfig'; +import * as cloudwatch from 'aws-cdk-lib/aws-cloudwatch'; +import * as iam from 'aws-cdk-lib/aws-iam'; +import { Construct } from 'constructs'; +import { IApplication } from './application'; +import { ActionPoint, IEventDestination, ExtensionOptions, IExtension, IExtensible, ExtensibleBase } from './extension'; + +/** + * Attributes of an existing AWS AppConfig environment to import it. + */ +export interface EnvironmentAttributes { + /** + * The application associated with the environment. + */ + readonly application: IApplication; + + /** + * The ID of the environment. + */ + readonly environmentId: string; + + /** + * The name of the environment. + */ + readonly name?: string; + + /** + * The description of the environment. + */ + readonly description?: string; + + /** + * The monitors for the environment. + */ + readonly monitors?: Monitor[]; +} + +abstract class EnvironmentBase extends Resource implements IEnvironment, IExtensible { + public abstract applicationId: string; + public abstract environmentId: string; + public abstract environmentArn: string; + protected extensible!: ExtensibleBase; + + public on(actionPoint: ActionPoint, eventDestination: IEventDestination, options?: ExtensionOptions) { + this.extensible.on(actionPoint, eventDestination, options); + } + + public preCreateHostedConfigurationVersion(eventDestination: IEventDestination, options?: ExtensionOptions) { + this.extensible.preCreateHostedConfigurationVersion(eventDestination, options); + } + + public preStartDeployment(eventDestination: IEventDestination, options?: ExtensionOptions) { + this.extensible.preStartDeployment(eventDestination, options); + } + + public onDeploymentStart(eventDestination: IEventDestination, options?: ExtensionOptions) { + this.extensible.onDeploymentStart(eventDestination, options); + } + + public onDeploymentStep(eventDestination: IEventDestination, options?: ExtensionOptions) { + this.extensible.onDeploymentStep(eventDestination, options); + } + + public onDeploymentBaking(eventDestination: IEventDestination, options?: ExtensionOptions) { + this.extensible.onDeploymentBaking(eventDestination, options); + } + + public onDeploymentComplete(eventDestination: IEventDestination, options?: ExtensionOptions) { + this.extensible.onDeploymentComplete(eventDestination, options); + } + + public onDeploymentRolledBack(eventDestination: IEventDestination, options?: ExtensionOptions) { + this.extensible.onDeploymentRolledBack(eventDestination, options); + } + + public addExtension(extension: IExtension) { + this.extensible.addExtension(extension); + } +} + +export interface EnvironmentOptions { + /** + * The name of the environment. + * + * @default - A name is generated. + */ + readonly name?: string; + + /** + * The description of the environment. + * + * @default - No description. + */ + readonly description?: string; + + /** + * The monitors for the environment. + * + * @default - No monitors. + */ + readonly monitors?: Monitor[]; +} + +export interface EnvironmentProps extends EnvironmentOptions { + /** + * The application to be associated with the environment. + */ + readonly application: IApplication; +} + +/** + * An AWS AppConfig environment. + * + * @resource AWS::AppConfig::Environment + * @see https://docs.aws.amazon.com/appconfig/latest/userguide/appconfig-creating-environment.html + */ +export class Environment extends EnvironmentBase { + /** + * Imports an environment into the CDK using its Amazon Resource Name (ARN). + * + * @param scope The parent construct + * @param id The name of the environment construct + * @param environmentArn The Amazon Resource Name (ARN) of the environment + */ + public static fromEnvironmentArn(scope: Construct, id: string, environmentArn: string): IEnvironment { + const parsedArn = Stack.of(scope).splitArn(environmentArn, ArnFormat.SLASH_RESOURCE_NAME); + if (!parsedArn.resourceName) { + throw new Error(`Missing required /$/{applicationId}/environment//$/{environmentId} from environment ARN: ${parsedArn.resourceName}`); + } + + const resourceName = parsedArn.resourceName.split('/'); + if (resourceName.length != 3 || !resourceName[0] || !resourceName[2]) { + throw new Error('Missing required parameters for environment ARN: format should be /$/{applicationId}/environment//$/{environmentId}'); + } + + const applicationId = resourceName[0]; + const environmentId = resourceName[2]; + + class Import extends EnvironmentBase { + public readonly applicationId = applicationId; + public readonly environmentId = environmentId; + public readonly environmentArn = environmentArn; + } + + return new Import(scope, id, { + environmentFromArn: environmentArn, + }); + } + + /** + * Imports an environment into the CDK from its attributes. + * + * @param scope The parent construct + * @param id The name of the environment construct + * @param attr The attributes of the environment + */ + public static fromEnvironmentAttributes(scope: Construct, id: string, attr: EnvironmentAttributes): IEnvironment { + const applicationId = attr.application.applicationId; + const environmentId = attr.environmentId; + + const stack = Stack.of(scope); + const environmentArn = stack.formatArn({ + service: 'appconfig', + resource: 'application', + resourceName: `${applicationId}/environment/${environmentId}`, + }); + + class Import extends EnvironmentBase { + public readonly application = attr.application; + public readonly applicationId = attr.application.applicationId; + public readonly name = attr.name; + public readonly environmentId = environmentId; + public readonly environmentArn = environmentArn; + public readonly description = attr.description; + public readonly monitors = attr.monitors; + } + + return new Import(scope, id, { + environmentFromArn: environmentArn, + }); + } + + /** + * The application associated with the environment. + */ + public readonly application?: IApplication; + + /** + * The name of the environment. + */ + public readonly name?: string; + + /** + * The description of the environment. + */ + public readonly description?: string; + + /** + * The monitors for the environment. + */ + public readonly monitors?: Monitor[]; + + /** + * The ID of the environment. + */ + public readonly environmentId: string; + + /** + * The Amazon Resource Name (ARN) of the environment. + */ + public readonly environmentArn: string; + + /** + * The ID of the environment. + */ + public readonly applicationId: string; + + private readonly _cfnEnvironment: CfnEnvironment; + + constructor(scope: Construct, id: string, props: EnvironmentProps) { + super(scope, id, { + physicalName: props.name, + }); + + this.name = props.name || Names.uniqueResourceName(this, { + maxLength: 64, + separator: '-', + }); + this.application = props.application; + this.applicationId = this.application.applicationId; + this.description = props.description; + this.monitors = props.monitors; + + const resource = new CfnEnvironment(this, 'Resource', { + applicationId: this.applicationId, + name: this.name, + description: this.description, + monitors: this.monitors?.map((monitor) => { + return { + alarmArn: monitor.alarm.alarmArn, + alarmRoleArn: monitor.alarmRole?.roleArn || this.createAlarmRole(monitor.alarm.alarmArn).roleArn, + }; + }), + }); + this._cfnEnvironment = resource; + + this.environmentId = this._cfnEnvironment.ref; + this.environmentArn = this.stack.formatArn({ + service: 'appconfig', + resource: 'application', + resourceName: `${this.applicationId}/environment/${this.environmentId}`, + }); + this.extensible = new ExtensibleBase(scope, this.environmentArn, this.name); + + this.application.addExistingEnvironment(this); + } + + private createAlarmRole(alarmArn: string): iam.IRole { + const policy = new iam.PolicyStatement({ + effect: iam.Effect.ALLOW, + actions: ['cloudwatch:DescribeAlarms'], + resources: [alarmArn], + }); + const document = new iam.PolicyDocument({ + statements: [policy], + }); + const role = new iam.Role(this, 'Role', { + roleName: PhysicalName.GENERATE_IF_NEEDED, + assumedBy: new iam.ServicePrincipal('appconfig.amazonaws.com'), + inlinePolicies: { + ['AllowAppConfigMonitorAlarmPolicy']: document, + }, + }); + return role; + } +} + +/** + * Defines monitors that will be associated with an AWS AppConfig environment. + */ +export interface Monitor { + /** + * The Amazon CloudWatch alarm. + */ + readonly alarm: cloudwatch.IAlarm; + + /** + * The IAM role for AWS AppConfig to view the alarm state. + * + * @default - A role is generated. + */ + readonly alarmRole?: iam.IRole; +} + +export interface IEnvironment extends IResource { + /** + * The application associated with the environment. + */ + readonly application?: IApplication; + + /** + * The ID of the application associated to the environment. + */ + readonly applicationId: string; + + /** + * The name of the environment. + */ + readonly name?: string; + + /** + * The description of the environment. + */ + readonly description?: string; + + /** + * The monitors for the environment. + */ + readonly monitors?: Monitor[]; + + /** + * The ID of the environment. + */ + readonly environmentId: string; + + /** + * The Amazon Resource Name (ARN) of the environment. + */ + readonly environmentArn: string; + + /** + * Adds an extension defined by the action point and event destination and also + * creates an extension association to the environment. + * + * @param actionPoint The action point which triggers the event + * @param eventDestination The event that occurs during the extension + * @param options Options for the extension + */ + on(actionPoint: ActionPoint, eventDestination: IEventDestination, options?: ExtensionOptions): void; + + /** + * Adds a PRE_CREATE_HOSTED_CONFIGURATION_VERSION extension with the provided event destination + * and also creates an extension association to the environment. + * + * @param eventDestination The event that occurs during the extension + * @param options Options for the extension + */ + preCreateHostedConfigurationVersion(eventDestination: IEventDestination, options?: ExtensionOptions): void; + + /** + * Adds a PRE_START_DEPLOYMENT extension with the provided event destination and also creates + * an extension association to the environment. + * + * @param eventDestination The event that occurs during the extension + * @param options Options for the extension + */ + preStartDeployment(eventDestination: IEventDestination, options?: ExtensionOptions): void; + + /** + * Adds an ON_DEPLOYMENT_START extension with the provided event destination and also creates + * an extension association to the environment. + * + * @param eventDestination The event that occurs during the extension + * @param options Options for the extension + */ + onDeploymentStart(eventDestination: IEventDestination, options?: ExtensionOptions): void; + + /** + * Adds an ON_DEPLOYMENT_STEP extension with the provided event destination and also + * creates an extension association to the environment. + * + * @param eventDestination The event that occurs during the extension + * @param options Options for the extension + */ + onDeploymentStep(eventDestination: IEventDestination, options?: ExtensionOptions): void; + + /** + * Adds an ON_DEPLOYMENT_BAKING extension with the provided event destination and + * also creates an extension association to the environment. + * + * @param eventDestination The event that occurs during the extension + * @param options Options for the extension + */ + onDeploymentBaking(eventDestination: IEventDestination, options?: ExtensionOptions): void; + + /** + * Adds an ON_DEPLOYMENT_COMPLETE extension with the provided event destination and + * also creates an extension association to the environment. + * + * @param eventDestination The event that occurs during the extension + * @param options Options for the extension + */ + onDeploymentComplete(eventDestination: IEventDestination, options?: ExtensionOptions): void; + + /** + * Adds an ON_DEPLOYMENT_ROLLED_BACK extension with the provided event destination and + * also creates an extension association to the environment. + * + * @param eventDestination The event that occurs during the extension + * @param options Options for the extension + */ + onDeploymentRolledBack(eventDestination: IEventDestination, options?: ExtensionOptions): void; + + /** + * Adds an extension association to the environment. + * + * @param extension The extension to create an association for + */ + addExtension(extension: IExtension): void; +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/lib/extension.ts b/packages/@aws-cdk/aws-appconfig-alpha/lib/extension.ts new file mode 100644 index 0000000000000..307f4537bd407 --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/lib/extension.ts @@ -0,0 +1,758 @@ +import { ArnFormat, IResource, Names, PhysicalName, Resource, Stack } from 'aws-cdk-lib'; +import { CfnExtension, CfnExtensionAssociation } from 'aws-cdk-lib/aws-appconfig'; +import * as events from 'aws-cdk-lib/aws-events'; +import * as iam from 'aws-cdk-lib/aws-iam'; +import * as lambda from 'aws-cdk-lib/aws-lambda'; +import * as sns from 'aws-cdk-lib/aws-sns'; +import * as sqs from 'aws-cdk-lib/aws-sqs'; +import { Construct } from 'constructs'; +import { getHash, stringifyObjects } from './private/hash'; + +/** + * Defines Extension action points. + * + * @see https://docs.aws.amazon.com/appconfig/latest/userguide/working-with-appconfig-extensions-about.html#working-with-appconfig-extensions-how-it-works-step-2 + */ +export enum ActionPoint { + PRE_CREATE_HOSTED_CONFIGURATION_VERSION = 'PRE_CREATE_HOSTED_CONFIGURATION_VERSION', + PRE_START_DEPLOYMENT = 'PRE_START_DEPLOYMENT', + ON_DEPLOYMENT_START = 'ON_DEPLOYMENT_START', + ON_DEPLOYMENT_STEP = 'ON_DEPLOYMENT_STEP', + ON_DEPLOYMENT_BAKING = 'ON_DEPLOYMENT_BAKING', + ON_DEPLOYMENT_COMPLETE = 'ON_DEPLOYMENT_COMPLETE', + ON_DEPLOYMENT_ROLLED_BACK = 'ON_DEPLOYMENT_ROLLED_BACK', +} + +export enum SourceType { + LAMBDA = 'lambda', + SQS = 'sqs', + SNS = 'sns', + EVENTS = 'events', +} + +/** + * Implemented by allowed extension event destinations. + */ +export interface IEventDestination { + /** + * The URI of the extension event destination. + */ + readonly extensionUri: string; + + /** + * The type of the extension event destination. + */ + readonly type: SourceType; + + /** + * The IAM policy document to invoke the event destination. + */ + readonly policyDocument?: iam.PolicyDocument; +} + +/** + * Use an AWS Lambda function as an event destination. + */ +export class LambdaDestination implements IEventDestination { + public readonly extensionUri: string; + public readonly type: SourceType; + public readonly policyDocument?: iam.PolicyDocument; + + constructor(func: lambda.IFunction) { + this.extensionUri = func.functionArn; + this.type = SourceType.LAMBDA; + const policy = new iam.PolicyStatement({ + effect: iam.Effect.ALLOW, + resources: [this.extensionUri], + actions: [ + 'lambda:InvokeFunction', + 'lambda:InvokeAsync', + ], + }); + this.policyDocument = new iam.PolicyDocument({ + statements: [policy], + }); + } +} + +/** + * Use an Amazon SQS queue as an event destination. + */ +export class SqsDestination implements IEventDestination { + public readonly extensionUri: string; + public readonly type: SourceType; + public readonly policyDocument?: iam.PolicyDocument; + + constructor(queue: sqs.IQueue) { + this.extensionUri = queue.queueArn; + this.type = SourceType.SQS; + const policy = new iam.PolicyStatement({ + effect: iam.Effect.ALLOW, + resources: [this.extensionUri], + actions: ['sqs:SendMessage'], + }); + this.policyDocument = new iam.PolicyDocument({ + statements: [policy], + }); + } +} + +/** + * Use an Amazon SNS topic as an event destination. + */ +export class SnsDestination implements IEventDestination { + public readonly extensionUri: string; + public readonly type: SourceType; + public readonly policyDocument?: iam.PolicyDocument; + + constructor(topic: sns.ITopic) { + this.extensionUri = topic.topicArn; + this.type = SourceType.SNS; + const policy = new iam.PolicyStatement({ + effect: iam.Effect.ALLOW, + resources: [this.extensionUri], + actions: ['sns:Publish'], + }); + this.policyDocument = new iam.PolicyDocument({ + statements: [policy], + }); + } +} + +/** + * Use an Amazon EventBridge event bus as an event destination. + */ +export class EventBridgeDestination implements IEventDestination { + public readonly extensionUri: string; + public readonly type: SourceType; + + constructor(bus: events.IEventBus) { + this.extensionUri = bus.eventBusArn; + this.type = SourceType.EVENTS; + } +} + +export interface ActionProps { + /** + * The action points that will trigger the extension action. + */ + readonly actionPoints: ActionPoint[]; + + /** + * The event destination for the action. + */ + readonly eventDestination: IEventDestination; + + /** + * The name for the action. + * + * @default - A name is generated. + */ + readonly name?: string; + + /** + * The execution role for the action. + * + * @default - A role is generated. + */ + readonly executionRole?: iam.IRole; + + /** + * The description for the action. + * + * @default - No description. + */ + readonly description?: string; + + /** + * The flag that specifies whether or not to create the execution role. + * + * If set to true, then the role will not be auto-generated under the assumption + * there is already the corresponding resource-based policy attached to the event + * destination. If false, the execution role will be generated if not provided. + * + * @default false + */ + readonly invokeWithoutExecutionRole?: boolean; +} + +/** + * Defines an action for an extension. + */ +export class Action { + /** + * The action points that will trigger the extension action. + */ + public readonly actionPoints: ActionPoint[]; + + /** + * The event destination for the action. + */ + public readonly eventDestination: IEventDestination; + + /** + * The name for the action. + */ + public readonly name?: string; + + /** + * The execution role for the action. + */ + public readonly executionRole?: iam.IRole; + + /** + * The description for the action. + */ + public readonly description?: string; + + /** + * The flag that specifies whether to create the execution role. + */ + readonly invokeWithoutExecutionRole?: boolean; + + public constructor(props: ActionProps) { + this.actionPoints = props.actionPoints; + this.eventDestination = props.eventDestination; + this.name = props.name; + this.executionRole = props.executionRole; + this.description = props.description; + this.invokeWithoutExecutionRole = props.invokeWithoutExecutionRole || false; + } +} + +/** + * Defines a parameter for an extension. + */ +export class Parameter { + /** + * A required parameter for an extension. + * + * @param name The name of the parameter + * @param value The value of the parameter + * @param description A description for the parameter + */ + public static required(name: string, value: string, description?: string): Parameter { + return new Parameter(name, true, value, description); + } + + /** + * An optional parameter for an extension. + * + * @param name The name of the parameter + * @param value The value of the parameter + * @param description A description for the parameter + */ + public static notRequired(name: string, value?: string, description?: string): Parameter { + return new Parameter(name, false, value, description); + } + + /** + * The name of the parameter. + */ + public readonly name: string; + + /** + * A boolean that indicates if the parameter is required or optional. + */ + public readonly isRequired: boolean; + + /** + * The value of the parameter. + */ + public readonly value?: string; + + /** + * The description of the parameter. + */ + public readonly description?: string; + + private constructor(name: string, isRequired: boolean, value?: string, description?: string) { + this.name = name; + this.isRequired = isRequired; + this.value = value; + this.description = description; + } +} + +/** + * Attributes of an existing AWS AppConfig extension to import. + */ +export interface ExtensionAttributes { + /** + * The ID of the extension. + */ + readonly extensionId: string; + + /** + * The version number of the extension. + */ + readonly extensionVersionNumber: number; + + /** + * The Amazon Resource Name (ARN) of the extension. + */ + readonly extensionArn?: string; + + /** + * The actions of the extension. + */ + readonly actions?: Action[]; + + /** + * The name of the extension. + */ + readonly name?: string; + + /** + * The description of the extension. + */ + readonly description?: string; +} + +export interface ExtensionOptions { + /** + * The name of the extension. + * + * @default - A name is generated. + */ + readonly name?: string; + + /** + * A description of the extension + * + * @default - No description. + */ + readonly description?: string; + + /** + * The latest version number of the extension. When you create a new version, + * specify the most recent current version number. For example, you create version 3, + * enter 2 for this field. + * + * @default - None. + */ + readonly latestVersionNumber?: number; + + /** + * The parameters accepted for the extension. + * + * @default - None. + */ + readonly parameters?: Parameter[]; +} + +export interface ExtensionProps extends ExtensionOptions { + /** + * The actions for the extension. + */ + readonly actions: Action[]; +} + +/** + * An AWS AppConfig extension. + * + * @resource AWS::AppConfig::Extension + * @see https://docs.aws.amazon.com/appconfig/latest/userguide/working-with-appconfig-extensions.html + */ +export class Extension extends Resource implements IExtension { + /** + * Imports an extension into the CDK using its Amazon Resource Name (ARN). + * + * @param scope The parent construct + * @param id The name of the extension construct + * @param extensionArn The Amazon Resource Name (ARN) of the extension + */ + public static fromExtensionArn(scope: Construct, id: string, extensionArn: string): IExtension { + const parsedArn = Stack.of(scope).splitArn(extensionArn, ArnFormat.SLASH_RESOURCE_NAME); + if (!parsedArn.resourceName) { + throw new Error(`Missing required /$/{extensionId}//$/{extensionVersionNumber} from configuration profile ARN: ${parsedArn.resourceName}`); + } + + const resourceName = parsedArn.resourceName.split('/'); + if (resourceName.length != 2 || !resourceName[0] || !resourceName[1]) { + throw new Error('Missing required parameters for extension ARN: format should be /$/{extensionId}//$/{extensionVersionNumber}'); + } + + const extensionId = resourceName[0]; + const extensionVersionNumber = resourceName[1]; + + class Import extends Resource implements IExtension { + public readonly extensionId = extensionId; + public readonly extensionVersionNumber = parseInt(extensionVersionNumber); + public readonly extensionArn = extensionArn; + } + + return new Import(scope, id, { + environmentFromArn: extensionArn, + }); + } + + /** + * Imports an extension into the CDK using its attributes. + * + * @param scope The parent construct + * @param id The name of the extension construct + * @param attr The attributes of the extension + */ + public static fromExtensionAttributes(scope: Construct, id: string, attr: ExtensionAttributes): IExtension { + const stack = Stack.of(scope); + const extensionArn = attr.extensionArn || stack.formatArn({ + service: 'appconfig', + resource: 'extension', + resourceName: `${attr.extensionId}/${attr.extensionVersionNumber}`, + }); + + class Import extends Resource implements IExtension { + public readonly extensionId = attr.extensionId; + public readonly extensionVersionNumber = attr.extensionVersionNumber; + public readonly extensionArn = extensionArn; + public readonly name = attr.name; + public readonly actions = attr.actions; + public readonly description = attr.description; + } + + return new Import(scope, id, { + environmentFromArn: extensionArn, + }); + } + + /** + * The actions for the extension. + */ + public readonly actions?: Action[]; + + /** + * The name of the extension. + */ + public readonly name?: string; + + /** + * The description of the extension. + */ + public readonly description?: string; + + /** + * The latest version number of the extension. + */ + public readonly latestVersionNumber?: number; + + /** + * The parameters of the extension. + */ + public readonly parameters?: Parameter[]; + + /** + * The Amazon Resource Name (ARN) of the extension. + */ + public readonly extensionArn: string; + + /** + * The ID of the extension. + */ + public readonly extensionId: string; + + /** + * The version number of the extension. + */ + public readonly extensionVersionNumber: number; + + private readonly _cfnExtension: CfnExtension; + private executionRole?: iam.IRole; + + constructor(scope: Construct, id: string, props: ExtensionProps) { + super(scope, id, { + physicalName: props.name, + }); + + this.actions = props.actions; + this.name = props.name || Names.uniqueResourceName(this, { + maxLength: 64, + separator: '-', + }); + this.description = props.description; + this.latestVersionNumber = props.latestVersionNumber; + this.parameters = props.parameters; + + const resource = new CfnExtension(this, 'Resource', { + actions: this.actions.reduce((acc: {[key: string]: {[key: string]: string}[]}, cur: Action) => { + const extensionUri = cur.eventDestination.extensionUri; + const sourceType = cur.eventDestination.type; + this.executionRole = cur.executionRole; + cur.actionPoints.forEach((actionPoint) => { + acc[actionPoint] = [ + { + Name: cur.name || Names.uniqueResourceName(this, { + maxLength: 64, + separator: '-', + }), + Uri: extensionUri, + ...(sourceType === SourceType.EVENTS || cur.invokeWithoutExecutionRole + ? {} + : { RoleArn: this.executionRole?.roleArn || this.getExecutionRole(cur.eventDestination).roleArn }), + ...(cur.description ? { Description: cur.description } : {}), + }, + ]; + }); + return acc; + }, {}), + name: this.name, + description: this.description, + latestVersionNumber: this.latestVersionNumber, + parameters: this.parameters?.reduce((acc: {[key: string]: CfnExtension.ParameterProperty}, cur: Parameter) => { + acc[cur.name] = { + required: cur.isRequired, + description: cur.description, + }; + return acc; + }, {}), + }); + this._cfnExtension = resource; + + this.extensionId = this._cfnExtension.attrId; + this.extensionVersionNumber = this._cfnExtension.attrVersionNumber; + this.extensionArn = this.getResourceArnAttribute(this._cfnExtension.attrArn, { + service: 'appconfig', + resource: 'extension', + resourceName: `${this.extensionId}/${this.extensionVersionNumber}`, + }); + } + + private getExecutionRole(eventDestination: IEventDestination): iam.IRole { + this.executionRole = new iam.Role(this, `Role${getHash(eventDestination.extensionUri)}`, { + roleName: PhysicalName.GENERATE_IF_NEEDED, + assumedBy: new iam.ServicePrincipal('appconfig.amazonaws.com'), + inlinePolicies: { + ['AllowAppConfigInvokeExtensionEventSourcePolicy']: eventDestination.policyDocument!, + }, + }); + + return this.executionRole; + } +} + +export interface IExtension extends IResource { + /** + * The actions for the extension. + */ + readonly actions?: Action[]; + + /** + * The name of the extension. + */ + readonly name?: string; + + /** + * The description of the extension. + */ + readonly description?: string; + + /** + * The latest version number of the extension. + */ + readonly latestVersionNumber?: number; + + /** + * The parameters of the extension. + */ + readonly parameters?: Parameter[]; + + /** + * The Amazon Resource Name (ARN) of the extension. + */ + readonly extensionArn: string; + + /** + * The ID of the extension. + */ + readonly extensionId: string; + + /** + * The version number of the extension. + */ + readonly extensionVersionNumber: number; +} + +/** + * This class is meant to be used by AWS AppConfig resources (application, + * configuration profile, environment) directly. There is currently no use + * for this class outside of the AWS AppConfig construct implementation. It is + * intended to be used with the resources since there is currently no way to + * inherit from two classes (at least within JSII constraints). + */ +export class ExtensibleBase implements IExtensible { + private resourceArn: string; + private resourceName?: string; + private scope: Construct; + + public constructor(scope: Construct, resourceArn: string, resourceName?: string) { + this.resourceArn = resourceArn; + this.resourceName = resourceName; + this.scope = scope; + } + + public on(actionPoint: ActionPoint, eventDestination: IEventDestination, options?: ExtensionOptions) { + this.getExtensionForActionPoint(eventDestination, actionPoint, options); + } + + public preCreateHostedConfigurationVersion(eventDestination: IEventDestination, options?: ExtensionOptions) { + this.getExtensionForActionPoint(eventDestination, ActionPoint.PRE_CREATE_HOSTED_CONFIGURATION_VERSION, options); + } + + public preStartDeployment(eventDestination: IEventDestination, options?: ExtensionOptions) { + this.getExtensionForActionPoint(eventDestination, ActionPoint.PRE_START_DEPLOYMENT, options); + } + + public onDeploymentStart(eventDestination: IEventDestination, options?: ExtensionOptions) { + this.getExtensionForActionPoint(eventDestination, ActionPoint.ON_DEPLOYMENT_START, options); + } + + public onDeploymentStep(eventDestination: IEventDestination, options?: ExtensionOptions) { + this.getExtensionForActionPoint(eventDestination, ActionPoint.ON_DEPLOYMENT_STEP, options); + } + + public onDeploymentBaking(eventDestination: IEventDestination, options?: ExtensionOptions) { + this.getExtensionForActionPoint(eventDestination, ActionPoint.ON_DEPLOYMENT_BAKING, options); + } + + public onDeploymentComplete(eventDestination: IEventDestination, options?: ExtensionOptions) { + this.getExtensionForActionPoint(eventDestination, ActionPoint.ON_DEPLOYMENT_COMPLETE, options); + } + + public onDeploymentRolledBack(eventDestination: IEventDestination, options?: ExtensionOptions) { + this.getExtensionForActionPoint(eventDestination, ActionPoint.ON_DEPLOYMENT_ROLLED_BACK, options); + } + + public addExtension(extension: IExtension) { + this.addExtensionAssociation(extension, { + parameters: extension.parameters, + }); + } + + private getExtensionForActionPoint(eventDestination: IEventDestination, actionPoint: ActionPoint, options?: ExtensionOptions) { + const extension = new Extension(this.scope, `Extension${this.getExtensionHash(eventDestination, actionPoint, options)}`, { + actions: [ + new Action({ + eventDestination, + actionPoints: [ + actionPoint, + ], + }), + ], + ...(options?.description ? { description: options.description } : {}), + ...(options?.latestVersionNumber ? { latestVersionNumber: options.latestVersionNumber } : {}), + ...(options?.name ? { name: options.name } : {}), + ...(options?.parameters ? { parameters: options.parameters } : {}), + }); + this.addExtensionAssociation(extension, options); + } + + private addExtensionAssociation(extension: IExtension, options?: ExtensionOptions) { + new CfnExtensionAssociation(this.scope, `AssociationResource${this.getExtensionAssociationHash(extension)}`, { + extensionIdentifier: extension.extensionId, + resourceIdentifier: this.resourceArn, + extensionVersionNumber: extension.extensionVersionNumber, + parameters: options?.parameters?.reduce((acc: {[key: string]: string}, cur: Parameter) => { + if (cur.value) { + acc[cur.name] = cur.value; + } + return acc; + }, {}), + }); + } + + private getExtensionHash(eventDestination: IEventDestination, actionPoint: ActionPoint, options?: ExtensionOptions) { + const combinedString = stringifyObjects(eventDestination, actionPoint, options); + return getHash(combinedString); + } + + private getExtensionAssociationHash(extension: IExtension) { + const resourceIdentifier = this.resourceName ? this.resourceName : this.resourceArn; + const combinedString = stringifyObjects(resourceIdentifier, extension.name, extension.extensionVersionNumber); + return getHash(combinedString); + } +} + +/** + * Defines the extensible base implementation for extension association resources. + */ +export interface IExtensible { + /** + * Adds an extension defined by the action point and event destination and + * also creates an extension association to the derived resource. + * + * @param actionPoint The action point which triggers the event + * @param eventDestination The event that occurs during the extension + * @param options Options for the extension + */ + on(actionPoint: ActionPoint, eventDestination: IEventDestination, options?: ExtensionOptions): void; + + /** + * Adds a PRE_CREATE_HOSTED_CONFIGURATION_VERSION extension with the provided event + * destination and also creates an extension association to the derived resource. + * + * @param eventDestination The event that occurs during the extension + * @param options Options for the extension + */ + preCreateHostedConfigurationVersion(eventDestination: IEventDestination, options?: ExtensionOptions): void; + + /** + * Adds a PRE_START_DEPLOYMENT extension with the provided event destination and + * also creates an extension association to the derived resource. + * + * @param eventDestination The event that occurs during the extension + * @param options Options for the extension + */ + preStartDeployment(eventDestination: IEventDestination, options?: ExtensionOptions): void; + + /** + * Adds an ON_DEPLOYMENT_START extension with the provided event destination and + * also creates an extension association to the derived resource. + * + * @param eventDestination The event that occurs during the extension + * @param options Options for the extension + */ + onDeploymentStart(eventDestination: IEventDestination, options?: ExtensionOptions): void; + + /** + * Adds an ON_DEPLOYMENT_STEP extension with the provided event destination and + * also creates an extension association to the derived resource. + * + * @param eventDestination The event that occurs during the extension + * @param options Options for the extension + */ + onDeploymentStep(eventDestination: IEventDestination, options?: ExtensionOptions): void; + + /** + * Adds an ON_DEPLOYMENT_BAKING extension with the provided event destination and + * also creates an extension association to the derived resource. + * + * @param eventDestination The event that occurs during the extension + * @param options Options for the extension + */ + onDeploymentBaking(eventDestination: IEventDestination, options?: ExtensionOptions): void; + + /** + * Adds an ON_DEPLOYMENT_COMPLETE extension with the provided event destination and + * also creates an extension association to the derived resource. + * + * @param eventDestination The event that occurs during the extension + * @param options Options for the extension + */ + onDeploymentComplete(eventDestination: IEventDestination, options?: ExtensionOptions): void; + + /** + * Adds an ON_DEPLOYMENT_ROLLED_BACK extension with the provided event destination and + * also creates an extension association to the derived resource. + * + * @param eventDestination The event that occurs during the extension + * @param options Options for the extension + */ + onDeploymentRolledBack(eventDestination: IEventDestination, options?: ExtensionOptions): void; + + /** + * Adds an extension association to the derived resource. + * + * @param extension The extension to create an association for + */ + addExtension(extension: IExtension): void; +} diff --git a/packages/@aws-cdk/aws-appconfig-alpha/lib/index.ts b/packages/@aws-cdk/aws-appconfig-alpha/lib/index.ts new file mode 100644 index 0000000000000..33118b5873456 --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/lib/index.ts @@ -0,0 +1,5 @@ +export * from './environment'; +export * from './deployment-strategy'; +export * from './extension'; +export * from './application'; +export * from './configuration'; diff --git a/packages/@aws-cdk/aws-appconfig-alpha/lib/private/hash.ts b/packages/@aws-cdk/aws-appconfig-alpha/lib/private/hash.ts new file mode 100644 index 0000000000000..c496ea174cee2 --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/lib/private/hash.ts @@ -0,0 +1,12 @@ +import * as crypto from 'crypto'; + +export function getHash(stringToHash: string): string { + const hash = crypto.createHash('sha256').update(stringToHash).digest('hex'); + const truncatedHash = hash.substring(0, 5).toUpperCase(); + return truncatedHash; +} + +export function stringifyObjects(...objects: any[]): string { + const combinedObject = Object.assign({}, ...objects); + return JSON.stringify(combinedObject); +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/package.json b/packages/@aws-cdk/aws-appconfig-alpha/package.json new file mode 100644 index 0000000000000..0c6b1342a7064 --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/package.json @@ -0,0 +1,119 @@ +{ + "name": "@aws-cdk/aws-appconfig-alpha", + "private": false, + "version": "0.0.0", + "description": "An experimental construct library for AWS AppConfig.", + "main": "lib/index.js", + "types": "lib/index.d.ts", + "jsii": { + "outdir": "dist", + "targets": { + "dotnet": { + "namespace": "Amazon.CDK.AWS.AppConfig.Alpha", + "packageId": "Amazon.CDK.AWS.AppConfig.Alpha", + "iconUrl": "https://raw.githubusercontent.com/aws/aws-cdk/main/logo/default-256-dark.png" + }, + "java": { + "package": "software.amazon.awscdk.services.appconfig.alpha", + "maven": { + "groupId": "software.amazon.awscdk", + "artifactId": "appconfig-alpha" + } + }, + "python": { + "distName": "aws-cdk.aws-appconfig-alpha", + "module": "aws_cdk.aws_appconfig_alpha", + "classifiers": [ + "Framework :: AWS CDK", + "Framework :: AWS CDK :: 2" + ] + }, + "go": { + "moduleName": "github.com/aws/aws-cdk-go", + "packageName": "awscdkappconfigalpha" + } + }, + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } + }, + "repository": { + "type": "git", + "url": "https://github.com/aws/aws-cdk.git", + "directory": "packages/@aws-cdk/aws-appconfig-alpha" + }, + "scripts": { + "build": "cdk-build", + "watch": "cdk-watch", + "lint": "cdk-lint", + "test": "cdk-test", + "integ": "integ-runner", + "pkglint": "pkglint -f", + "package": "cdk-package", + "awslint": "cdk-awslint", + "build+test": "yarn build && yarn test", + "build+test+package": "yarn build+test && yarn package", + "compat": "cdk-compat", + "rosetta:extract": "yarn --silent jsii-rosetta extract", + "build+extract": "yarn build && yarn rosetta:extract", + "build+test+extract": "yarn build+test && yarn rosetta:extract" + }, + "keywords": [ + "aws", + "cdk", + "constructs", + "appconfig" + ], + "author": { + "name": "Amazon Web Services", + "url": "https://aws.amazon.com", + "organization": true + }, + "license": "Apache-2.0", + "devDependencies": { + "aws-cdk-lib": "0.0.0", + "@aws-cdk/cdk-build-tools": "0.0.0", + "@aws-cdk/integ-runner": "0.0.0", + "@aws-cdk/pkglint": "0.0.0", + "@aws-cdk/integ-tests-alpha": "0.0.0", + "@types/jest": "^29.5.3", + "constructs": "^10.0.0", + "jest": "^29.6.2" + }, + "dependencies": { + "aws-cdk-lib": "0.0.0", + "constructs": "^10.0.0" + }, + "homepage": "https://github.com/aws/aws-cdk", + "peerDependencies": { + "aws-cdk-lib": "0.0.0", + "constructs": "^10.0.0" + }, + "separate-module": false, + "engines": { + "node": ">= 14.15.0" + }, + "stability": "experimental", + "maturity": "experimental", + "awslint": { + "exclude": [ + "*:*" + ] + }, + "awscdkio": { + "announce": false + }, + "publishConfig": { + "tag": "latest" + }, + "cdk-build": { + "env": { + "AWSLINT_BASE_CONSTRUCT": true + } + } +} diff --git a/packages/@aws-cdk/aws-appconfig-alpha/rosetta/default.ts-fixture b/packages/@aws-cdk/aws-appconfig-alpha/rosetta/default.ts-fixture new file mode 100644 index 0000000000000..088052b1423f5 --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/rosetta/default.ts-fixture @@ -0,0 +1,21 @@ +// Fixture with packages imported, but nothing else +import { Construct } from 'constructs'; +import { Stack, Duration } from 'aws-cdk-lib'; +import * as lambda from 'aws-cdk-lib/aws-lambda'; +import * as appconfig from '@aws-cdk/aws-appconfig-alpha'; +import * as s3 from 'aws-cdk-lib/aws-s3'; +import * as ssm from 'aws-cdk-lib/aws-ssm'; +import * as secrets from 'aws-cdk-lib/aws-secretsmanager'; +import * as sqs from 'aws-cdk-lib/aws-sqs'; +import * as sns from 'aws-cdk-lib/aws-sns'; +import * as codepipeline from 'aws-cdk-lib/aws-codepipeline'; +import * as events from 'aws-cdk-lib/aws-events'; +import * as cloudwatch from 'aws-cdk-lib/aws-cloudwatch'; + +class Fixture extends Stack { + constructor(scope: Construct, id: string) { + super(scope, id); + + /// here + } +} diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/application.test.ts b/packages/@aws-cdk/aws-appconfig-alpha/test/application.test.ts new file mode 100644 index 0000000000000..a94427b770681 --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/application.test.ts @@ -0,0 +1,531 @@ +import * as cdk from 'aws-cdk-lib'; +import { Template } from 'aws-cdk-lib/assertions'; +import { FargateTaskDefinition } from 'aws-cdk-lib/aws-ecs'; +import { Code, Function, Runtime } from 'aws-cdk-lib/aws-lambda'; +import { + Application, + Platform, + LambdaDestination, + Parameter, + ActionPoint, +} from '../lib'; + +describe('appconfig', () => { + test('basic appconfig', () => { + const stack = new cdk.Stack(); + new Application(stack, 'MyAppConfig'); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::Application', { + Name: 'MyAppConfig', + }); + }); + + test('appconfig with name', () => { + const stack = new cdk.Stack(); + new Application(stack, 'MyAppConfig', { + name: 'TestApp', + }); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::Application', { + Name: 'TestApp', + }); + }); + + test('appconfig with description', () => { + const stack = new cdk.Stack(); + new Application(stack, 'MyAppConfig', { + description: 'This is my description', + }); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::Application', { + Name: 'MyAppConfig', + Description: 'This is my description', + }); + }); + + test('get lambda layer arn', () => { + expect(Application.getLambdaLayerVersionArn('us-east-1')).toEqual('arn:aws:lambda:us-east-1:027255383542:layer:AWS-AppConfig-Extension:110'); + expect(Application.getLambdaLayerVersionArn('us-east-1', Platform.ARM_64)).toEqual('arn:aws:lambda:us-east-1:027255383542:layer:AWS-AppConfig-Extension-Arm64:43'); + }); + + test('add agent to ecs', () => { + const stack = new cdk.Stack(); + const taskDef = new FargateTaskDefinition(stack, 'TaskDef'); + Application.addAgentToEcs(taskDef); + + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { + ContainerDefinitions: [ + { + Image: 'public.ecr.aws/aws-appconfig/aws-appconfig-agent:latest', + Name: 'AppConfigAgentContainer', + Essential: true, + }, + ], + }); + }); + + test('pre create hosted configuration version', () => { + const stack = new cdk.Stack(); + const appconfig = new Application(stack, 'MyAppConfig'); + const func = new Function(stack, 'MyFunc', { + handler: 'index.handler', + runtime: Runtime.PYTHON_3_7, + code: Code.fromInline('# this is my code'), + }); + Object.defineProperty(func, 'functionArn', { + value: 'arn:lambda:us-east-1:123456789012:function:my-function', + }); + appconfig.on(ActionPoint.ON_DEPLOYMENT_STEP, new LambdaDestination(func)); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::Extension', { + Name: 'Extension28486', + Actions: { + ON_DEPLOYMENT_STEP: [ + { + Name: 'Extension28486', + RoleArn: { 'Fn::GetAtt': ['Extension28486RoleFD36712B5D791', 'Arn'] }, + Uri: 'arn:lambda:us-east-1:123456789012:function:my-function', + }, + ], + }, + }); + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::ExtensionAssociation', { + ExtensionIdentifier: { + 'Fn::GetAtt': ['Extension28486EB468E25', 'Id'], + }, + ExtensionVersionNumber: { + 'Fn::GetAtt': ['Extension28486EB468E25', 'VersionNumber'], + }, + ResourceIdentifier: { + 'Fn::Join': [ + '', + [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':appconfig:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':application/', + { Ref: 'MyAppConfigB4B63E75' }, + ], + ], + }, + }); + }); + + test('pre create hosted configuration version', () => { + const stack = new cdk.Stack(); + const appconfig = new Application(stack, 'MyAppConfig'); + const func = new Function(stack, 'MyFunc', { + handler: 'index.handler', + runtime: Runtime.PYTHON_3_7, + code: Code.fromInline('# this is my code'), + }); + Object.defineProperty(func, 'functionArn', { + value: 'arn:lambda:us-east-1:123456789012:function:my-function', + }); + appconfig.preCreateHostedConfigurationVersion(new LambdaDestination(func), { + description: 'This is my description', + name: 'MyExtension', + latestVersionNumber: 1, + parameters: [ + Parameter.required('myparam', 'val'), + ], + }); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::Extension', { + Name: 'MyExtension', + Description: 'This is my description', + LatestVersionNumber: 1, + Actions: { + PRE_CREATE_HOSTED_CONFIGURATION_VERSION: [ + { + Name: 'Extension8D9D7', + RoleArn: { 'Fn::GetAtt': ['Extension8D9D7RoleFD367F4FA01C5', 'Arn'] }, + Uri: 'arn:lambda:us-east-1:123456789012:function:my-function', + }, + ], + }, + Parameters: { + myparam: { Required: true }, + }, + }); + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::ExtensionAssociation', { + ExtensionIdentifier: { + 'Fn::GetAtt': ['Extension8D9D75657615A', 'Id'], + }, + ExtensionVersionNumber: { + 'Fn::GetAtt': ['Extension8D9D75657615A', 'VersionNumber'], + }, + Parameters: { + myparam: 'val', + }, + ResourceIdentifier: { + 'Fn::Join': [ + '', + [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':appconfig:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':application/', + { Ref: 'MyAppConfigB4B63E75' }, + ], + ], + }, + }); + }); + + test('pre start deployment', () => { + const stack = new cdk.Stack(); + const appconfig = new Application(stack, 'MyAppConfig'); + const func = new Function(stack, 'MyFunc', { + handler: 'index.handler', + runtime: Runtime.PYTHON_3_7, + code: Code.fromInline('# this is my code'), + }); + Object.defineProperty(func, 'functionArn', { + value: 'arn:lambda:us-east-1:123456789012:function:my-function', + }); + appconfig.preStartDeployment(new LambdaDestination(func)); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::Extension', { + Name: 'Extension6253E', + Actions: { + PRE_START_DEPLOYMENT: [ + { + Name: 'Extension6253E', + RoleArn: { 'Fn::GetAtt': ['Extension6253ERoleFD367F586E17D', 'Arn'] }, + Uri: 'arn:lambda:us-east-1:123456789012:function:my-function', + }, + ], + }, + }); + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::ExtensionAssociation', { + ExtensionIdentifier: { + 'Fn::GetAtt': ['Extension6253ED4CE66CE', 'Id'], + }, + ExtensionVersionNumber: { + 'Fn::GetAtt': ['Extension6253ED4CE66CE', 'VersionNumber'], + }, + ResourceIdentifier: { + 'Fn::Join': [ + '', + [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':appconfig:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':application/', + { Ref: 'MyAppConfigB4B63E75' }, + ], + ], + }, + }); + }); + + test('on deployment start', () => { + const stack = new cdk.Stack(); + const appconfig = new Application(stack, 'MyAppConfig'); + Object.defineProperty(appconfig, 'applicationArn', { + value: 'arn:aws:appconfig:us-east-1:123456789012:application/abc123', + }); + const func = new Function(stack, 'MyFunc', { + handler: 'index.handler', + runtime: Runtime.PYTHON_3_7, + code: Code.fromInline('# this is my code'), + }); + Object.defineProperty(func, 'functionArn', { + value: 'arn:lambda:us-east-1:123456789012:function:my-function', + }); + appconfig.onDeploymentStart(new LambdaDestination(func)); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::Extension', { + Name: 'ExtensionB65DC', + Actions: { + ON_DEPLOYMENT_START: [ + { + Name: 'ExtensionB65DC', + RoleArn: { 'Fn::GetAtt': ['ExtensionB65DCRoleFD3677AFA6FE0', 'Arn'] }, + Uri: 'arn:lambda:us-east-1:123456789012:function:my-function', + }, + ], + }, + }); + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::ExtensionAssociation', { + ExtensionIdentifier: { + 'Fn::GetAtt': ['ExtensionB65DC00D22C6E', 'Id'], + }, + ExtensionVersionNumber: { + 'Fn::GetAtt': ['ExtensionB65DC00D22C6E', 'VersionNumber'], + }, + ResourceIdentifier: { + 'Fn::Join': [ + '', + [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':appconfig:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':application/', + { Ref: 'MyAppConfigB4B63E75' }, + ], + ], + }, + }); + }); + + test('on deployment step', () => { + const stack = new cdk.Stack(); + const appconfig = new Application(stack, 'MyAppConfig'); + const func = new Function(stack, 'MyFunc', { + handler: 'index.handler', + runtime: Runtime.PYTHON_3_7, + code: Code.fromInline('# this is my code'), + }); + Object.defineProperty(func, 'functionArn', { + value: 'arn:lambda:us-east-1:123456789012:function:my-function', + }); + appconfig.onDeploymentStep(new LambdaDestination(func)); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::Extension', { + Name: 'Extension28486', + Actions: { + ON_DEPLOYMENT_STEP: [ + { + Name: 'Extension28486', + RoleArn: { 'Fn::GetAtt': ['Extension28486RoleFD36712B5D791', 'Arn'] }, + Uri: 'arn:lambda:us-east-1:123456789012:function:my-function', + }, + ], + }, + }); + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::ExtensionAssociation', { + ExtensionIdentifier: { + 'Fn::GetAtt': ['Extension28486EB468E25', 'Id'], + }, + ExtensionVersionNumber: { + 'Fn::GetAtt': ['Extension28486EB468E25', 'VersionNumber'], + }, + ResourceIdentifier: { + 'Fn::Join': [ + '', + [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':appconfig:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':application/', + { Ref: 'MyAppConfigB4B63E75' }, + ], + ], + }, + }); + }); + + test('on deployment complete', () => { + const stack = new cdk.Stack(); + const appconfig = new Application(stack, 'MyAppConfig'); + const func = new Function(stack, 'MyFunc', { + handler: 'index.handler', + runtime: Runtime.PYTHON_3_7, + code: Code.fromInline('# this is my code'), + }); + Object.defineProperty(func, 'functionArn', { + value: 'arn:lambda:us-east-1:123456789012:function:my-function', + }); + appconfig.onDeploymentComplete(new LambdaDestination(func)); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::Extension', { + Name: 'Extension32166', + Actions: { + ON_DEPLOYMENT_COMPLETE: [ + { + Name: 'Extension32166', + RoleArn: { 'Fn::GetAtt': ['Extension32166RoleFD367EE1FF117', 'Arn'] }, + Uri: 'arn:lambda:us-east-1:123456789012:function:my-function', + }, + ], + }, + }); + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::ExtensionAssociation', { + ExtensionIdentifier: { + 'Fn::GetAtt': ['Extension32166E58405A0', 'Id'], + }, + ExtensionVersionNumber: { + 'Fn::GetAtt': ['Extension32166E58405A0', 'VersionNumber'], + }, + ResourceIdentifier: { + 'Fn::Join': [ + '', + [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':appconfig:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':application/', + { Ref: 'MyAppConfigB4B63E75' }, + ], + ], + }, + }); + }); + + test('on deployment bake', () => { + const stack = new cdk.Stack(); + const appconfig = new Application(stack, 'MyAppConfig'); + const func = new Function(stack, 'MyFunc', { + handler: 'index.handler', + runtime: Runtime.PYTHON_3_7, + code: Code.fromInline('# this is my code'), + }); + Object.defineProperty(func, 'functionArn', { + value: 'arn:lambda:us-east-1:123456789012:function:my-function', + }); + appconfig.onDeploymentBaking(new LambdaDestination(func)); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::Extension', { + Name: 'Extension1CAD4', + Actions: { + ON_DEPLOYMENT_BAKING: [ + { + Name: 'Extension1CAD4', + RoleArn: { 'Fn::GetAtt': ['Extension1CAD4RoleFD367FC09E8DE', 'Arn'] }, + Uri: 'arn:lambda:us-east-1:123456789012:function:my-function', + }, + ], + }, + }); + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::ExtensionAssociation', { + ExtensionIdentifier: { + 'Fn::GetAtt': ['Extension1CAD47F07C609', 'Id'], + }, + ExtensionVersionNumber: { + 'Fn::GetAtt': ['Extension1CAD47F07C609', 'VersionNumber'], + }, + ResourceIdentifier: { + 'Fn::Join': [ + '', + [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':appconfig:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':application/', + { Ref: 'MyAppConfigB4B63E75' }, + ], + ], + }, + }); + }); + + test('on deployment rolled back', () => { + const stack = new cdk.Stack(); + const appconfig = new Application(stack, 'MyAppConfig'); + const func = new Function(stack, 'MyFunc', { + handler: 'index.handler', + runtime: Runtime.PYTHON_3_7, + code: Code.fromInline('# this is my code'), + }); + Object.defineProperty(func, 'functionArn', { + value: 'arn:lambda:us-east-1:123456789012:function:my-function', + }); + appconfig.onDeploymentRolledBack(new LambdaDestination(func)); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::Extension', { + Name: 'ExtensionC8347', + Actions: { + ON_DEPLOYMENT_ROLLED_BACK: [ + { + Name: 'ExtensionC8347', + RoleArn: { 'Fn::GetAtt': ['ExtensionC8347RoleFD36716A1DE61', 'Arn'] }, + Uri: 'arn:lambda:us-east-1:123456789012:function:my-function', + }, + ], + }, + }); + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::ExtensionAssociation', { + ExtensionIdentifier: { + 'Fn::GetAtt': ['ExtensionC83470CE85F6C', 'Id'], + }, + ExtensionVersionNumber: { + 'Fn::GetAtt': ['ExtensionC83470CE85F6C', 'VersionNumber'], + }, + ResourceIdentifier: { + 'Fn::Join': [ + '', + [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':appconfig:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':application/', + { Ref: 'MyAppConfigB4B63E75' }, + ], + ], + }, + }); + }); + + test('create same extension twice', () => { + const stack = new cdk.Stack(); + const appconfig = new Application(stack, 'MyAppConfig'); + const func = new Function(stack, 'MyFunc', { + handler: 'index.handler', + runtime: Runtime.PYTHON_3_7, + code: Code.fromInline('# this is my code'), + }); + Object.defineProperty(func, 'functionArn', { + value: 'arn:lambda:us-east-1:123456789012:function:my-function', + }); + appconfig.preStartDeployment(new LambdaDestination(func)); + + expect(() => { + appconfig.preStartDeployment(new LambdaDestination(func)); + }).toThrow(); + }); + + test('from application arn', () => { + const stack = new cdk.Stack(); + const app = Application.fromApplicationArn(stack, 'Application', + 'arn:aws:appconfig:us-west-2:123456789012:application/abc123'); + + expect(app.applicationId).toEqual('abc123'); + }); + + test('from application arn with no resource name', () => { + const stack = new cdk.Stack(); + expect(() => { + Application.fromApplicationArn(stack, 'Application', + 'arn:aws:appconfig:us-west-2:123456789012:application/'); + }).toThrow('Missing required application id from application ARN'); + }); + + test('from application id', () => { + const cdkApp = new cdk.App(); + const stack = new cdk.Stack(cdkApp, 'Stack', { + env: { + region: 'us-west-2', + account: '123456789012', + }, + }); + const app = Application.fromApplicationId(stack, 'Application', 'abc123'); + + expect(app.applicationId).toEqual('abc123'); + }); +}); \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/config.json b/packages/@aws-cdk/aws-appconfig-alpha/test/config.json new file mode 100644 index 0000000000000..5c2ecbd6e6cf7 --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/config.json @@ -0,0 +1,3 @@ +{ + "content": "This is the configuration content" +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/configuration.test.ts b/packages/@aws-cdk/aws-appconfig-alpha/test/configuration.test.ts new file mode 100644 index 0000000000000..6022720431473 --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/configuration.test.ts @@ -0,0 +1,1162 @@ +import * as cdk from 'aws-cdk-lib'; +import { Template } from 'aws-cdk-lib/assertions'; +import { Artifact, Pipeline } from 'aws-cdk-lib/aws-codepipeline'; +import { S3DeployAction, S3SourceAction } from 'aws-cdk-lib/aws-codepipeline-actions'; +import * as iam from 'aws-cdk-lib/aws-iam'; +import { Key } from 'aws-cdk-lib/aws-kms'; +import { Bucket } from 'aws-cdk-lib/aws-s3'; +import { Secret } from 'aws-cdk-lib/aws-secretsmanager'; +import { CfnDocument, StringParameter } from 'aws-cdk-lib/aws-ssm'; +import { + Application, + HostedConfiguration, + ConfigurationSource, + SourcedConfiguration, + DeploymentStrategy, + ConfigurationType, + ValidatorType, + JsonSchemaValidator, + ConfigurationContent, + RolloutStrategy, +} from '../lib'; + +describe('configuration', () => { + test('configuration with no environments and no deployTo prop', () => { + const stack = new cdk.Stack(); + const app = new Application(stack, 'MyAppConfig'); + new HostedConfiguration(stack, 'MyHostedConfig', { + content: ConfigurationContent.fromInline('This is my content'), + application: app, + deploymentStrategy: new DeploymentStrategy(stack, 'MyDeploymentStrategy', { + rolloutStrategy: RolloutStrategy.linear({ + growthFactor: 15, + deploymentDuration: cdk.Duration.minutes(30), + }), + }), + }); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::Environment', { + Name: 'MyAppConfig-Environment-CF46384A', + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + }); + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::ConfigurationProfile', { + Name: 'MyHostedConfig', + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + }); + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::HostedConfigurationVersion', { + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + ConfigurationProfileId: { + Ref: 'MyHostedConfigConfigurationProfile2E1A2BBC', + }, + Content: 'This is my content', + ContentType: 'application/json', + }); + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::Deployment', { + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + EnvironmentId: { + Ref: 'MyAppConfigEnvironment833A9182', + }, + ConfigurationVersion: { + Ref: 'MyHostedConfig51D3877D', + }, + ConfigurationProfileId: { + Ref: 'MyHostedConfigConfigurationProfile2E1A2BBC', + }, + DeploymentStrategyId: { + Ref: 'MyDeploymentStrategy60D31FB0', + }, + }); + }); + + test('configuration with environments and no deployTo prop', () => { + const stack = new cdk.Stack(); + const app = new Application(stack, 'MyAppConfig', { + name: 'MyApplication', + }); + app.addEnvironment('MyEnv1'); + app.addEnvironment('MyEnv2'); + new HostedConfiguration(stack, 'MyHostedConfig', { + content: ConfigurationContent.fromInline('This is my content'), + application: app, + deploymentStrategy: new DeploymentStrategy(stack, 'MyDeploymentStrategy', { + rolloutStrategy: RolloutStrategy.linear({ + growthFactor: 15, + deploymentDuration: cdk.Duration.minutes(30), + }), + }), + }); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::ConfigurationProfile', { + Name: 'MyHostedConfig', + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + }); + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::HostedConfigurationVersion', { + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + ConfigurationProfileId: { + Ref: 'MyHostedConfigConfigurationProfile2E1A2BBC', + }, + Content: 'This is my content', + ContentType: 'application/json', + }); + Template.fromStack(stack).resourceCountIs('AWS::AppConfig::Environment', 2); + Template.fromStack(stack).resourceCountIs('AWS::AppConfig::Deployment', 0); + }); + + test('configuration with environments and deployTo prop', () => { + const stack = new cdk.Stack(); + const app = new Application(stack, 'MyAppConfig', { + name: 'MyApplication', + }); + app.addEnvironment('MyEnv1'); + const env = app.addEnvironment('MyEnv2'); + new HostedConfiguration(stack, 'MyHostedConfig', { + content: ConfigurationContent.fromInline('This is my content'), + application: app, + deployTo: [env], + deploymentStrategy: new DeploymentStrategy(stack, 'MyDeploymentStrategy', { + rolloutStrategy: RolloutStrategy.linear({ + growthFactor: 15, + deploymentDuration: cdk.Duration.minutes(30), + }), + }), + }); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::ConfigurationProfile', { + Name: 'MyHostedConfig', + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + }); + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::HostedConfigurationVersion', { + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + ConfigurationProfileId: { + Ref: 'MyHostedConfigConfigurationProfile2E1A2BBC', + }, + Content: 'This is my content', + ContentType: 'application/json', + }); + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::Deployment', { + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + EnvironmentId: { + Ref: 'MyAppConfigMyEnv2350437D6', + }, + ConfigurationVersion: { + Ref: 'MyHostedConfig51D3877D', + }, + ConfigurationProfileId: { + Ref: 'MyHostedConfigConfigurationProfile2E1A2BBC', + }, + DeploymentStrategyId: { + Ref: 'MyDeploymentStrategy60D31FB0', + }, + }); + Template.fromStack(stack).resourceCountIs('AWS::AppConfig::Environment', 2); + Template.fromStack(stack).resourceCountIs('AWS::AppConfig::Deployment', 1); + }); + + test('configuration with two configurations specified', () => { + const stack = new cdk.Stack(); + const app = new Application(stack, 'MyAppConfig', { + name: 'MyApplication', + }); + const env1 = app.addEnvironment('MyEnv1'); + const env2 = app.addEnvironment('MyEnv2'); + const bucket = new Bucket(stack, 'MyBucket'); + new HostedConfiguration(stack, 'MyHostedConfig', { + content: ConfigurationContent.fromInline('This is my content'), + application: app, + deployTo: [env1], + deploymentStrategy: new DeploymentStrategy(stack, 'MyDeploymentStrategy1', { + rolloutStrategy: RolloutStrategy.linear({ + growthFactor: 15, + deploymentDuration: cdk.Duration.minutes(30), + }), + }), + }); + new SourcedConfiguration(stack, 'MySourcedConfig', { + versionNumber: '1', + location: ConfigurationSource.fromBucket(bucket, 'path/to/object'), + application: app, + deployTo: [env2], + deploymentStrategy: new DeploymentStrategy(stack, 'MyDeploymentStrategy2', { + rolloutStrategy: RolloutStrategy.linear({ + growthFactor: 15, + deploymentDuration: cdk.Duration.minutes(30), + }), + }), + }); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::ConfigurationProfile', { + Name: 'MyHostedConfig', + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + LocationUri: 'hosted', + }); + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::HostedConfigurationVersion', { + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + ConfigurationProfileId: { + Ref: 'MyHostedConfigConfigurationProfile2E1A2BBC', + }, + Content: 'This is my content', + ContentType: 'application/json', + }); + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::ConfigurationProfile', { + Name: 'MySourcedConfig', + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + LocationUri: { + 'Fn::Join': [ + '', + [ + 's3://', + { Ref: 'MyBucketF68F3FF0' }, + '/path/to/object', + ], + ], + }, + RetrievalRoleArn: { 'Fn::GetAtt': ['MySourcedConfigRole249449B1', 'Arn'] }, + }); + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::DeploymentStrategy', { + Name: 'MyDeploymentStrategy1', + DeploymentDurationInMinutes: 30, + GrowthFactor: 15, + ReplicateTo: 'NONE', + }); + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::DeploymentStrategy', { + Name: 'MyDeploymentStrategy2', + DeploymentDurationInMinutes: 30, + GrowthFactor: 15, + ReplicateTo: 'NONE', + }); + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::Deployment', { + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + EnvironmentId: { + Ref: 'MyAppConfigMyEnv1B9120FA1', + }, + ConfigurationVersion: { + Ref: 'MyHostedConfig51D3877D', + }, + ConfigurationProfileId: { + Ref: 'MyHostedConfigConfigurationProfile2E1A2BBC', + }, + DeploymentStrategyId: { + Ref: 'MyDeploymentStrategy178099446', + }, + }); + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::Deployment', { + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + EnvironmentId: { + Ref: 'MyAppConfigMyEnv2350437D6', + }, + ConfigurationVersion: '1', + ConfigurationProfileId: { + Ref: 'MySourcedConfig5455C47C', + }, + DeploymentStrategyId: { + Ref: 'MyDeploymentStrategy202B80715', + }, + }); + Template.fromStack(stack).resourceCountIs('AWS::AppConfig::ConfigurationProfile', 2); + Template.fromStack(stack).resourceCountIs('AWS::AppConfig::Environment', 2); + Template.fromStack(stack).resourceCountIs('AWS::AppConfig::Deployment', 2); + }); + + test('configuration with two configurations and no deployment strategy specified', () => { + const stack = new cdk.Stack(); + const app = new Application(stack, 'MyAppConfig', { + name: 'MyApplication', + }); + const bucket = new Bucket(stack, 'MyBucket'); + new HostedConfiguration(stack, 'MyHostedConfig', { + content: ConfigurationContent.fromInline('This is my content'), + application: app, + }); + new SourcedConfiguration(stack, 'MySourcedConfig', { + versionNumber: '1', + location: ConfigurationSource.fromBucket(bucket, 'path/to/object'), + application: app, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::DeploymentStrategy', { + Name: 'MyHostedConfig-DeploymentStrategy-A5936E60', + DeploymentDurationInMinutes: 20, + GrowthFactor: 10, + ReplicateTo: 'NONE', + FinalBakeTimeInMinutes: 10, + }); + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::DeploymentStrategy', { + Name: 'MySourcedConfig-DeploymentStrategy-7A104657', + DeploymentDurationInMinutes: 20, + GrowthFactor: 10, + ReplicateTo: 'NONE', + FinalBakeTimeInMinutes: 10, + }); + }); + + test('deploy secret with kms key', () => { + const stack = new cdk.Stack(); + const key = new Key(stack, 'MyKey'); + const secret = new Secret(stack, 'MySecret'); + const app = new Application(stack, 'MyAppConfig'); + new SourcedConfiguration(stack, 'MySourcedConfig', { + versionNumber: '1', + location: ConfigurationSource.fromSecret(secret), + deploymentKey: key, + application: app, + deploymentStrategy: new DeploymentStrategy(stack, 'MyDeploymentStrategy', { + rolloutStrategy: RolloutStrategy.linear({ + growthFactor: 15, + deploymentDuration: cdk.Duration.minutes(30), + }), + }), + }); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::Deployment', { + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + EnvironmentId: { + Ref: 'MyAppConfigEnvironment833A9182', + }, + ConfigurationVersion: '1', + ConfigurationProfileId: { + Ref: 'MySourcedConfig5455C47C', + }, + DeploymentStrategyId: { + Ref: 'MyDeploymentStrategy60D31FB0', + }, + KmsKeyIdentifier: { 'Fn::GetAtt': ['MyKey6AB29FA6', 'Arn'] }, + }); + }); + + test('default configuration from inline', () => { + const stack = new cdk.Stack(); + const app = new Application(stack, 'MyAppConfig'); + new HostedConfiguration(stack, 'MyConfiguration', { + application: app, + content: ConfigurationContent.fromInline('This is my content'), + deploymentStrategy: new DeploymentStrategy(stack, 'MyDeploymentStrategy', { + rolloutStrategy: RolloutStrategy.linear({ + growthFactor: 15, + deploymentDuration: cdk.Duration.minutes(30), + }), + }), + }); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::ConfigurationProfile', { + Name: 'MyConfiguration', + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + LocationUri: 'hosted', + }); + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::HostedConfigurationVersion', { + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + ConfigurationProfileId: { + Ref: 'MyConfigurationConfigurationProfileEE0ECA85', + }, + Content: 'This is my content', + ContentType: 'application/json', + }); + Template.fromStack(stack).resourceCountIs('AWS::AppConfig::Deployment', 1); + }); + + // test('default configuration from asset', () => { + // const stack = new cdk.Stack(); + // const app = new AppConfig(stack, 'MyAppConfig', { + // deploymentStrategy: new DeploymentStrategy(stack, 'MyDeploymentStrategy', { + // rolloutStrategy: RolloutStrategy.linear(15, cdk.Duration.minutes(30)), + // }), + // }); + // new HostedConfiguration(stack, 'MyConfiguration', { + // appConfig: app, + // content: ConfigurationContent.fromAsset('/Users/chenjane/Documents/appconfig-l2-constructs/test/config.json'), + // }); + + // Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::ConfigurationProfile', { + // Name: 'MyConfiguration', + // ApplicationId: { + // Ref: 'MyAppConfigB4B63E75', + // }, + // LocationUri: 'hosted', + // }); + // Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::HostedConfigurationVersion', { + // ApplicationId: { + // Ref: 'MyAppConfigB4B63E75', + // }, + // ConfigurationProfileId: { + // Ref: 'MyConfigurationConfigurationProfileEE0ECA85', + // }, + // Content: '{\n "content": "This is the configuration content"\n}', + // ContentType: 'application/json', + // }); + // }); + + test('configuration profile with name', () => { + const stack = new cdk.Stack(); + const app = new Application(stack, 'MyAppConfig'); + new HostedConfiguration(stack, 'MyConfigurationProfile', { + name: 'TestConfigProfile', + application: app, + content: ConfigurationContent.fromInline('This is my content'), + deploymentStrategy: new DeploymentStrategy(stack, 'MyDeploymentStrategy', { + rolloutStrategy: RolloutStrategy.linear({ + growthFactor: 15, + deploymentDuration: cdk.Duration.minutes(30), + }), + }), + }); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::ConfigurationProfile', { + Name: 'TestConfigProfile', + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + LocationUri: 'hosted', + }); + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::HostedConfigurationVersion', { + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + ConfigurationProfileId: { + Ref: 'MyConfigurationProfile33A97163', + }, + Content: 'This is my content', + ContentType: 'application/json', + }); + Template.fromStack(stack).resourceCountIs('AWS::AppConfig::Deployment', 1); + }); + + test('configuration profile with type', () => { + const stack = new cdk.Stack(); + const app = new Application(stack, 'MyAppConfig'); + new HostedConfiguration(stack, 'MyConfigurationProfile', { + name: 'TestConfigProfile', + application: app, + type: ConfigurationType.FEATURE_FLAGS, + content: ConfigurationContent.fromInline('This is my content'), + deploymentStrategy: new DeploymentStrategy(stack, 'MyDeploymentStrategy', { + rolloutStrategy: RolloutStrategy.linear({ + growthFactor: 15, + deploymentDuration: cdk.Duration.minutes(30), + }), + }), + }); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::ConfigurationProfile', { + Name: 'TestConfigProfile', + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + Type: 'AWS.AppConfig.FeatureFlags', + LocationUri: 'hosted', + }); + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::HostedConfigurationVersion', { + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + ConfigurationProfileId: { + Ref: 'MyConfigurationProfile33A97163', + }, + Content: 'This is my content', + ContentType: 'application/json', + }); + Template.fromStack(stack).resourceCountIs('AWS::AppConfig::Deployment', 1); + }); + + test('configuration profile with description', () => { + const stack = new cdk.Stack(); + const app = new Application(stack, 'MyAppConfig'); + new HostedConfiguration(stack, 'MyConfigurationProfile', { + name: 'TestConfigProfile', + application: app, + content: ConfigurationContent.fromInline('This is my content'), + description: 'This is my description', + deploymentStrategy: new DeploymentStrategy(stack, 'MyDeploymentStrategy', { + rolloutStrategy: RolloutStrategy.linear({ + growthFactor: 15, + deploymentDuration: cdk.Duration.minutes(30), + }), + }), + }); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::ConfigurationProfile', { + Name: 'TestConfigProfile', + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + Description: 'This is my description', + LocationUri: 'hosted', + }); + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::HostedConfigurationVersion', { + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + ConfigurationProfileId: { + Ref: 'MyConfigurationProfile33A97163', + }, + Content: 'This is my content', + ContentType: 'application/json', + Description: 'This is my description', + }); + Template.fromStack(stack).resourceCountIs('AWS::AppConfig::Deployment', 1); + }); + + test('configuration profile with validator', () => { + const stack = new cdk.Stack(); + const app = new Application(stack, 'MyAppConfig'); + new HostedConfiguration(stack, 'MyConfigurationProfile', { + name: 'TestConfigProfile', + application: app, + content: ConfigurationContent.fromInline('This is my content'), + validators: [ + { + content: 'dummy validator', + type: ValidatorType.JSON_SCHEMA, + }, + ], + deploymentStrategy: new DeploymentStrategy(stack, 'MyDeploymentStrategy', { + rolloutStrategy: RolloutStrategy.linear({ + growthFactor: 15, + deploymentDuration: cdk.Duration.minutes(30), + }), + }), + }); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::ConfigurationProfile', { + Name: 'TestConfigProfile', + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + Validators: [ + { + Type: 'JSON_SCHEMA', + Content: 'dummy validator', + }, + ], + LocationUri: 'hosted', + }); + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::HostedConfigurationVersion', { + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + ConfigurationProfileId: { + Ref: 'MyConfigurationProfile33A97163', + }, + Content: 'This is my content', + ContentType: 'application/json', + }); + Template.fromStack(stack).resourceCountIs('AWS::AppConfig::Deployment', 1); + }); + + test('configuration profile with inline json schema validator', () => { + const stack = new cdk.Stack(); + const app = new Application(stack, 'MyAppConfig'); + const validatorContent = ` + { + "type": "object", + "properties": { + "computeResource": { + "type": "object", + "properties": { + "ComputeAL1ImageId": { + "type": "object", + "properties": { + "me-south-1": { "type": "string" }, + "ap-east-1": { "type": "string" }, + "ap-northeast-1": { "type": "string" }, + "ap-northeast-2": { "type": "string" }, + "ap-south-1": { "type": "string" }, + "ap-southeast-1": { "type": "string" }, + "ap-southeast-2": { "type": "string" }, + "ca-central-1": { "type": "string" }, + "cn-north-1": { "type": "string" }, + "cn-northwest-1": { "type": "string" }, + "eu-central-1": { "type": "string" }, + "eu-north-1": { "type": "string" }, + "eu-west-1": { "type": "string" }, + "eu-west-2": { "type": "string" }, + "eu-west-3": { "type": "string" }, + "sa-east-1": { "type": "string" }, + "us-east-1": { "type": "string" }, + "us-east-2": { "type": "string" }, + "us-gov-west-1": { "type": "string" }, + "us-gov-east-1": { "type": "string" }, + "us-west-1": { "type": "string" }, + "us-west-2": { "type": "string" }, + "eu-south-1": { "type": "string" }, + "ap-northeast-3": { "type": "string" }, + "af-south-1": { "type": "string" } + } + }, + "GPUImageId": { + "type": "object", + "properties": { + "me-south-1": { "type": "string" }, + "ap-east-1": { "type": "string" }, + "ap-northeast-1": { "type": "string" }, + "ap-northeast-2": { "type": "string" }, + "ap-south-1": { "type": "string" }, + "ap-southeast-1": { "type": "string" }, + "ap-southeast-2": { "type": "string" }, + "ca-central-1": { "type": "string" }, + "cn-north-1": { "type": "string" }, + "cn-northwest-1": { "type": "string" }, + "eu-central-1": { "type": "string" }, + "eu-north-1": { "type": "string" }, + "eu-west-1": { "type": "string" }, + "eu-west-2": { "type": "string" }, + "eu-west-3": { "type": "string" }, + "sa-east-1": { "type": "string" }, + "us-east-1": { "type": "string" }, + "us-east-2": { "type": "string" }, + "us-gov-west-1": { "type": "string" }, + "us-gov-east-1": { "type": "string" }, + "us-west-1": { "type": "string" }, + "us-west-2": { "type": "string" }, + "eu-south-1": { "type": "string" }, + "ap-northeast-3": { "type": "string" }, + "af-south-1": { "type": "string" } + } + }, + "ARMImageId": { + "type": "object", + "properties": { + "me-south-1": { "type": "string" }, + "ap-east-1": { "type": "string" }, + "ap-northeast-1": { "type": "string" }, + "ap-northeast-2": { "type": "string" }, + "ap-south-1": { "type": "string" }, + "ap-southeast-1": { "type": "string" }, + "ap-southeast-2": { "type": "string" }, + "ca-central-1": { "type": "string" }, + "cn-north-1": { "type": "string" }, + "cn-northwest-1": { "type": "string" }, + "eu-central-1": { "type": "string" }, + "eu-north-1": { "type": "string" }, + "eu-west-1": { "type": "string" }, + "eu-west-2": { "type": "string" }, + "eu-west-3": { "type": "string" }, + "sa-east-1": { "type": "string" }, + "us-east-1": { "type": "string" }, + "us-east-2": { "type": "string" }, + "us-gov-west-1": { "type": "string" }, + "us-gov-east-1": { "type": "string" }, + "us-west-1": { "type": "string" }, + "us-west-2": { "type": "string" }, + "eu-south-1": { "type": "string" }, + "ap-northeast-3": { "type": "string" }, + "af-south-1": { "type": "string" } + } + }, + "ComputeAL2ImageId": { + "type": "object", + "properties": { + "me-south-1": { "type": "string" }, + "ap-east-1": { "type": "string" }, + "ap-northeast-1": { "type": "string" }, + "ap-northeast-2": { "type": "string" }, + "ap-south-1": { "type": "string" }, + "ap-southeast-1": { "type": "string" }, + "ap-southeast-2": { "type": "string" }, + "ca-central-1": { "type": "string" }, + "cn-north-1": { "type": "string" }, + "cn-northwest-1": { "type": "string" }, + "eu-central-1": { "type": "string" }, + "eu-north-1": { "type": "string" }, + "eu-west-1": { "type": "string" }, + "eu-west-2": { "type": "string" }, + "eu-west-3": { "type": "string" }, + "sa-east-1": { "type": "string" }, + "us-east-1": { "type": "string" }, + "us-east-2": { "type": "string" }, + "us-gov-west-1": { "type": "string" }, + "us-gov-east-1": { "type": "string" }, + "us-west-1": { "type": "string" }, + "us-west-2": { "type": "string" }, + "eu-south-1": { "type": "string" }, + "ap-northeast-3": { "type": "string" }, + "af-south-1": { "type": "string" } + } + } + } + } + } + }`; + new HostedConfiguration(stack, 'MyConfiguration', { + name: 'TestConfigProfile', + application: app, + validators: [ + JsonSchemaValidator.fromInline(validatorContent), + ], + content: ConfigurationContent.fromInline('This is my content'), + deploymentStrategy: new DeploymentStrategy(stack, 'MyDeploymentStrategy', { + rolloutStrategy: RolloutStrategy.linear({ + growthFactor: 15, + deploymentDuration: cdk.Duration.minutes(30), + }), + }), + }); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::ConfigurationProfile', { + Name: 'TestConfigProfile', + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + Validators: [ + { + Type: 'JSON_SCHEMA', + Content: '\n {\n \"type\": \"object\",\n \"properties\": {\n \"computeResource\": {\n \"type\": \"object\",\n \"properties\": {\n \"ComputeAL1ImageId\": {\n \"type\": \"object\",\n \"properties\": {\n \"me-south-1\": { \"type\": \"string\" },\n \"ap-east-1\": { \"type\": \"string\" },\n \"ap-northeast-1\": { \"type\": \"string\" },\n \"ap-northeast-2\": { \"type\": \"string\" },\n \"ap-south-1\": { \"type\": \"string\" },\n \"ap-southeast-1\": { \"type\": \"string\" },\n \"ap-southeast-2\": { \"type\": \"string\" },\n \"ca-central-1\": { \"type\": \"string\" },\n \"cn-north-1\": { \"type\": \"string\" },\n \"cn-northwest-1\": { \"type\": \"string\" },\n \"eu-central-1\": { \"type\": \"string\" },\n \"eu-north-1\": { \"type\": \"string\" },\n \"eu-west-1\": { \"type\": \"string\" },\n \"eu-west-2\": { \"type\": \"string\" },\n \"eu-west-3\": { \"type\": \"string\" },\n \"sa-east-1\": { \"type\": \"string\" },\n \"us-east-1\": { \"type\": \"string\" },\n \"us-east-2\": { \"type\": \"string\" },\n \"us-gov-west-1\": { \"type\": \"string\" },\n \"us-gov-east-1\": { \"type\": \"string\" },\n \"us-west-1\": { \"type\": \"string\" },\n \"us-west-2\": { \"type\": \"string\" },\n \"eu-south-1\": { \"type\": \"string\" },\n \"ap-northeast-3\": { \"type\": \"string\" },\n \"af-south-1\": { \"type\": \"string\" }\n }\n },\n \"GPUImageId\": {\n \"type\": \"object\",\n \"properties\": {\n \"me-south-1\": { \"type\": \"string\" },\n \"ap-east-1\": { \"type\": \"string\" },\n \"ap-northeast-1\": { \"type\": \"string\" },\n \"ap-northeast-2\": { \"type\": \"string\" },\n \"ap-south-1\": { \"type\": \"string\" },\n \"ap-southeast-1\": { \"type\": \"string\" },\n \"ap-southeast-2\": { \"type\": \"string\" },\n \"ca-central-1\": { \"type\": \"string\" },\n \"cn-north-1\": { \"type\": \"string\" },\n \"cn-northwest-1\": { \"type\": \"string\" },\n \"eu-central-1\": { \"type\": \"string\" },\n \"eu-north-1\": { \"type\": \"string\" },\n \"eu-west-1\": { \"type\": \"string\" },\n \"eu-west-2\": { \"type\": \"string\" },\n \"eu-west-3\": { \"type\": \"string\" },\n \"sa-east-1\": { \"type\": \"string\" },\n \"us-east-1\": { \"type\": \"string\" },\n \"us-east-2\": { \"type\": \"string\" },\n \"us-gov-west-1\": { \"type\": \"string\" },\n \"us-gov-east-1\": { \"type\": \"string\" },\n \"us-west-1\": { \"type\": \"string\" },\n \"us-west-2\": { \"type\": \"string\" },\n \"eu-south-1\": { \"type\": \"string\" },\n \"ap-northeast-3\": { \"type\": \"string\" },\n \"af-south-1\": { \"type\": \"string\" }\n }\n },\n \"ARMImageId\": {\n \"type\": \"object\",\n \"properties\": {\n \"me-south-1\": { \"type\": \"string\" },\n \"ap-east-1\": { \"type\": \"string\" },\n \"ap-northeast-1\": { \"type\": \"string\" },\n \"ap-northeast-2\": { \"type\": \"string\" },\n \"ap-south-1\": { \"type\": \"string\" },\n \"ap-southeast-1\": { \"type\": \"string\" },\n \"ap-southeast-2\": { \"type\": \"string\" },\n \"ca-central-1\": { \"type\": \"string\" },\n \"cn-north-1\": { \"type\": \"string\" },\n \"cn-northwest-1\": { \"type\": \"string\" },\n \"eu-central-1\": { \"type\": \"string\" },\n \"eu-north-1\": { \"type\": \"string\" },\n \"eu-west-1\": { \"type\": \"string\" },\n \"eu-west-2\": { \"type\": \"string\" },\n \"eu-west-3\": { \"type\": \"string\" },\n \"sa-east-1\": { \"type\": \"string\" },\n \"us-east-1\": { \"type\": \"string\" },\n \"us-east-2\": { \"type\": \"string\" },\n \"us-gov-west-1\": { \"type\": \"string\" },\n \"us-gov-east-1\": { \"type\": \"string\" },\n \"us-west-1\": { \"type\": \"string\" },\n \"us-west-2\": { \"type\": \"string\" },\n \"eu-south-1\": { \"type\": \"string\" },\n \"ap-northeast-3\": { \"type\": \"string\" },\n \"af-south-1\": { \"type\": \"string\" }\n }\n },\n \"ComputeAL2ImageId\": {\n \"type\": \"object\",\n \"properties\": {\n \"me-south-1\": { \"type\": \"string\" },\n \"ap-east-1\": { \"type\": \"string\" },\n \"ap-northeast-1\": { \"type\": \"string\" },\n \"ap-northeast-2\": { \"type\": \"string\" },\n \"ap-south-1\": { \"type\": \"string\" },\n \"ap-southeast-1\": { \"type\": \"string\" },\n \"ap-southeast-2\": { \"type\": \"string\" },\n \"ca-central-1\": { \"type\": \"string\" },\n \"cn-north-1\": { \"type\": \"string\" },\n \"cn-northwest-1\": { \"type\": \"string\" },\n \"eu-central-1\": { \"type\": \"string\" },\n \"eu-north-1\": { \"type\": \"string\" },\n \"eu-west-1\": { \"type\": \"string\" },\n \"eu-west-2\": { \"type\": \"string\" },\n \"eu-west-3\": { \"type\": \"string\" },\n \"sa-east-1\": { \"type\": \"string\" },\n \"us-east-1\": { \"type\": \"string\" },\n \"us-east-2\": { \"type\": \"string\" },\n \"us-gov-west-1\": { \"type\": \"string\" },\n \"us-gov-east-1\": { \"type\": \"string\" },\n \"us-west-1\": { \"type\": \"string\" },\n \"us-west-2\": { \"type\": \"string\" },\n \"eu-south-1\": { \"type\": \"string\" },\n \"ap-northeast-3\": { \"type\": \"string\" },\n \"af-south-1\": { \"type\": \"string\" }\n }\n }\n }\n }\n }\n }', + }, + ], + LocationUri: 'hosted', + }); + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::HostedConfigurationVersion', { + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + ConfigurationProfileId: { + Ref: 'MyConfigurationConfigurationProfileEE0ECA85', + }, + Content: 'This is my content', + ContentType: 'application/json', + }); + Template.fromStack(stack).resourceCountIs('AWS::AppConfig::Deployment', 1); + }); + + test('configuration profile with ssm parameter', () => { + const stack = new cdk.Stack(); + const app = new Application(stack, 'MyAppConfig'); + const parameter = new StringParameter(stack, 'MyParameter', { + stringValue: 'This is the content stored in ssm parameter', + parameterName: 'my-parameter', + }); + new SourcedConfiguration(stack, 'MyConfiguration', { + name: 'TestConfigProfile', + location: ConfigurationSource.fromParameter(parameter), + versionNumber: '1', + application: app, + deploymentStrategy: new DeploymentStrategy(stack, 'MyDeploymentStrategy', { + rolloutStrategy: RolloutStrategy.linear({ + growthFactor: 15, + deploymentDuration: cdk.Duration.minutes(30), + }), + }), + }); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::ConfigurationProfile', { + Name: 'TestConfigProfile', + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + LocationUri: { + 'Fn::Join': [ + '', + [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':ssm:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':parameter/', + { Ref: 'MyParameter18BA547D' }, + ], + ], + }, + }); + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { + Policies: [ + { + PolicyDocument: { + Statement: [ + { + Effect: iam.Effect.ALLOW, + Resource: { + 'Fn::Join': [ + '', + [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':ssm:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':parameter/', + { Ref: 'MyParameter18BA547D' }, + ], + ], + }, + Action: 'ssm:GetParameter', + }, + ], + }, + PolicyName: 'AllowAppConfigReadFromSourcePolicy', + }, + ], + }); + Template.fromStack(stack).resourceCountIs('AWS::AppConfig::Deployment', 1); + }); + + test('configuration profile with ssm document', () => { + const stack = new cdk.Stack(); + const document = new CfnDocument(stack, 'MyDocument', { + content: { + mainSteps: [ + { + action: 'aws:runShellScript', + }, + ], + }, + name: 'TestDocumentName', + }); + const app = new Application(stack, 'MyAppConfig'); + new SourcedConfiguration(stack, 'MyConfiguration', { + name: 'TestConfigProfile', + location: ConfigurationSource.fromCfnDocument(document), + versionNumber: '1', + application: app, + deploymentStrategy: new DeploymentStrategy(stack, 'MyDeploymentStrategy', { + rolloutStrategy: RolloutStrategy.linear({ + growthFactor: 15, + deploymentDuration: cdk.Duration.minutes(30), + }), + }), + }); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::ConfigurationProfile', { + Name: 'TestConfigProfile', + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + LocationUri: { + 'Fn::Join': [ + '', + [ + 'ssm-document://', + { Ref: 'MyDocument' }, + ], + ], + }, + }); + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { + Policies: [ + { + PolicyDocument: { + Statement: [ + { + Effect: iam.Effect.ALLOW, + Resource: { + 'Fn::Join': [ + '', + [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':ssm:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':document/', + { Ref: 'MyDocument' }, + ], + ], + }, + Action: 'ssm:GetDocument', + }, + ], + }, + PolicyName: 'AllowAppConfigReadFromSourcePolicy', + }, + ], + }); + Template.fromStack(stack).resourceCountIs('AWS::AppConfig::Deployment', 1); + }); + + test('configuration profile with s3 object', () => { + const stack = new cdk.Stack(); + const bucket = new Bucket(stack, 'MyBucket', { + bucketName: 'bucket', + }); + const app = new Application(stack, 'MyAppConfig'); + new SourcedConfiguration(stack, 'MyConfiguration', { + name: 'TestConfigProfile', + location: ConfigurationSource.fromBucket(bucket, 'hello/file.txt'), + versionNumber: '1', + application: app, + deploymentStrategy: new DeploymentStrategy(stack, 'MyDeploymentStrategy', { + rolloutStrategy: RolloutStrategy.linear({ + growthFactor: 15, + deploymentDuration: cdk.Duration.minutes(30), + }), + }), + }); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::ConfigurationProfile', { + Name: 'TestConfigProfile', + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + LocationUri: { + 'Fn::Join': [ + '', + [ + 's3://', + { Ref: 'MyBucketF68F3FF0' }, + '/hello/file.txt', + ], + ], + }, + }); + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { + Policies: [ + { + PolicyDocument: { + Statement: [ + { + Effect: iam.Effect.ALLOW, + Resource: { + 'Fn::Join': [ + '', + [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':s3:::', + { Ref: 'MyBucketF68F3FF0' }, + '/hello/file.txt', + ], + ], + }, + Action: [ + 's3:GetObject', + 's3:GetObjectMetadata', + 's3:GetObjectVersion', + ], + }, + { + Effect: iam.Effect.ALLOW, + Resource: { + 'Fn::Join': [ + '', + [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':s3:::', + { Ref: 'MyBucketF68F3FF0' }, + ], + ], + }, + Action: [ + 's3:GetBucketLocation', + 's3:GetBucketVersioning', + 's3:ListBucket', + 's3:ListBucketVersions', + ], + }, + { + Effect: iam.Effect.ALLOW, + Resource: '*', + Action: 's3:ListAllMyBuckets', + }, + ], + }, + PolicyName: 'AllowAppConfigReadFromSourcePolicy', + }, + ], + }); + Template.fromStack(stack).resourceCountIs('AWS::AppConfig::Deployment', 1); + }); + + test('configuration profile with codepipeline', () => { + const stack = new cdk.Stack(); + const bucket = new Bucket(stack, 'MyBucket'); + const sourceAction = new S3SourceAction({ + actionName: 'Source', + bucket: bucket, + bucketKey: 'hello/world/codepipeline.txt', + output: new Artifact('SourceOutput'), + }); + const deployAction = new S3DeployAction({ + actionName: 'Deploy', + input: Artifact.artifact('SourceOutput'), + bucket: bucket, + extract: true, + }); + const pipeline = new Pipeline(stack, 'MyPipeline', { + stages: [ + { + stageName: 'beta', + actions: [sourceAction], + }, + { + stageName: 'prod', + actions: [deployAction], + }, + ], + }); + const app = new Application(stack, 'MyAppConfig'); + new SourcedConfiguration(stack, 'MyConfiguration', { + name: 'TestConfigProfile', + location: ConfigurationSource.fromPipeline(pipeline), + application: app, + deploymentStrategy: new DeploymentStrategy(stack, 'MyDeploymentStrategy', { + rolloutStrategy: RolloutStrategy.linear({ + growthFactor: 15, + deploymentDuration: cdk.Duration.minutes(30), + }), + }), + }); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::ConfigurationProfile', { + Name: 'TestConfigProfile', + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + LocationUri: { + 'Fn::Join': [ + '', + [ + 'codepipeline://', + { Ref: 'MyPipelineAED38ECF' }, + ], + ], + }, + }); + + Template.fromStack(stack).resourceCountIs('AWS::IAM::Role', 3); + Template.fromStack(stack).resourceCountIs('AWS::IAM::Policy', 3); + Template.fromStack(stack).resourceCountIs('AWS::AppConfig::Deployment', 0); + }); + + test('configuration profile with secretsmanager', () => { + const stack = new cdk.Stack(); + const secret = new Secret(stack, 'MySecret', { + secretStringValue: cdk.SecretValue.unsafePlainText('This is the content stored in secrets manager'), + secretName: 'secret', + }); + Object.defineProperty(secret, 'secretArn', { + value: 'arn:aws:secretsmanager:us-east-1:123456789012:secret:my-secret', + }); + const app = new Application(stack, 'MyAppConfig'); + new SourcedConfiguration(stack, 'MyConfiguration', { + name: 'TestConfigProfile', + location: ConfigurationSource.fromSecret(secret), + versionNumber: '1', + application: app, + deploymentStrategy: new DeploymentStrategy(stack, 'MyDeploymentStrategy', { + rolloutStrategy: RolloutStrategy.linear({ + growthFactor: 15, + deploymentDuration: cdk.Duration.minutes(30), + }), + }), + }); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::ConfigurationProfile', { + Name: 'TestConfigProfile', + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + LocationUri: 'arn:aws:secretsmanager:us-east-1:123456789012:secret:my-secret', + }); + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { + Policies: [ + { + PolicyDocument: { + Statement: [ + { + Effect: iam.Effect.ALLOW, + Resource: 'arn:aws:secretsmanager:us-east-1:123456789012:secret:my-secret', + Action: 'secretsmanager:GetSecretValue', + }, + ], + }, + PolicyName: 'AllowAppConfigReadFromSourcePolicy', + }, + ], + }); + Template.fromStack(stack).resourceCountIs('AWS::AppConfig::Deployment', 1); + }); + + test('configuration profile with secretsmanager and kms', () => { + const stack = new cdk.Stack(); + const key = new Key(stack, 'MyKey'); + const secret = new Secret(stack, 'MySecret', { + secretStringValue: cdk.SecretValue.unsafePlainText('This is the content stored in secrets manager'), + secretName: 'secret', + encryptionKey: key, + }); + Object.defineProperty(secret, 'secretArn', { + value: 'arn:aws:secretsmanager:us-east-1:123456789012:secret:my-secret', + }); + const app = new Application(stack, 'MyAppConfig'); + new SourcedConfiguration(stack, 'MyConfiguration', { + name: 'TestConfigProfile', + location: ConfigurationSource.fromSecret(secret), + versionNumber: '1', + application: app, + deploymentStrategy: new DeploymentStrategy(stack, 'MyDeploymentStrategy', { + rolloutStrategy: RolloutStrategy.linear({ + growthFactor: 15, + deploymentDuration: cdk.Duration.minutes(30), + }), + }), + }); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::ConfigurationProfile', { + Name: 'TestConfigProfile', + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + LocationUri: 'arn:aws:secretsmanager:us-east-1:123456789012:secret:my-secret', + }); + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { + Policies: [ + { + PolicyDocument: { + Statement: [ + { + Effect: iam.Effect.ALLOW, + Resource: 'arn:aws:secretsmanager:us-east-1:123456789012:secret:my-secret', + Action: 'secretsmanager:GetSecretValue', + }, + { + Effect: iam.Effect.ALLOW, + Resource: { + 'Fn::GetAtt': ['MyKey6AB29FA6', 'Arn'], + }, + Action: 'kms:Decrypt', + }, + ], + }, + PolicyName: 'AllowAppConfigReadFromSourcePolicy', + }, + ], + }); + Template.fromStack(stack).resourceCountIs('AWS::AppConfig::Deployment', 1); + }); +}); diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/deployment-strategy.test.ts b/packages/@aws-cdk/aws-appconfig-alpha/test/deployment-strategy.test.ts new file mode 100644 index 0000000000000..14e4e1875af25 --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/deployment-strategy.test.ts @@ -0,0 +1,284 @@ +import * as cdk from 'aws-cdk-lib'; +import { App } from 'aws-cdk-lib'; +import { Template } from 'aws-cdk-lib/assertions'; +import { DeploymentStrategy, PredefinedDeploymentStrategyId, RolloutStrategy } from '../lib'; + +describe('deployment strategy', () => { + test('default deployment strategy', () => { + const stack = new cdk.Stack(); + new DeploymentStrategy(stack, 'MyDeploymentStrategy', { + rolloutStrategy: RolloutStrategy.linear({ + growthFactor: 10, + deploymentDuration: cdk.Duration.minutes(10), + }), + }); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::DeploymentStrategy', { + Name: 'MyDeploymentStrategy', + DeploymentDurationInMinutes: 10, + GrowthFactor: 10, + ReplicateTo: 'NONE', + GrowthType: 'LINEAR', + }); + }); + + test('deployment strategy with name', () => { + const stack = new cdk.Stack(); + new DeploymentStrategy(stack, 'MyDeploymentStrategy', { + name: 'TestDeploymentStrategy', + rolloutStrategy: RolloutStrategy.linear({ + growthFactor: 10, + deploymentDuration: cdk.Duration.minutes(10), + }), + }); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::DeploymentStrategy', { + Name: 'TestDeploymentStrategy', + DeploymentDurationInMinutes: 10, + GrowthFactor: 10, + ReplicateTo: 'NONE', + GrowthType: 'LINEAR', + }); + }); + + test('deployment strategy duration in seconds', () => { + const stack = new cdk.Stack(); + new DeploymentStrategy(stack, 'MyDeploymentStrategy', { + name: 'TestDeploymentStrategy', + rolloutStrategy: RolloutStrategy.linear({ + growthFactor: 10, + deploymentDuration: cdk.Duration.seconds(120), + }), + }); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::DeploymentStrategy', { + Name: 'TestDeploymentStrategy', + DeploymentDurationInMinutes: 2, + GrowthFactor: 10, + ReplicateTo: 'NONE', + GrowthType: 'LINEAR', + }); + }); + + test('deployment strategy with description', () => { + const stack = new cdk.Stack(); + new DeploymentStrategy(stack, 'MyDeploymentStrategy', { + name: 'TestDeploymentStrategy', + rolloutStrategy: RolloutStrategy.linear({ + growthFactor: 10, + deploymentDuration: cdk.Duration.minutes(10), + }), + description: 'This is my description', + }); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::DeploymentStrategy', { + Name: 'TestDeploymentStrategy', + DeploymentDurationInMinutes: 10, + GrowthFactor: 10, + ReplicateTo: 'NONE', + Description: 'This is my description', + GrowthType: 'LINEAR', + }); + }); + + test('deployment strategy with final bake time', () => { + const stack = new cdk.Stack(); + new DeploymentStrategy(stack, 'MyDeploymentStrategy', { + name: 'TestDeploymentStrategy', + rolloutStrategy: RolloutStrategy.linear({ + growthFactor: 10, + deploymentDuration: cdk.Duration.minutes(10), + finalBakeTime: cdk.Duration.minutes(30), + }), + }); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::DeploymentStrategy', { + Name: 'TestDeploymentStrategy', + DeploymentDurationInMinutes: 10, + GrowthFactor: 10, + ReplicateTo: 'NONE', + FinalBakeTimeInMinutes: 30, + GrowthType: 'LINEAR', + }); + }); + + test('deployment strategy with growth type', () => { + const stack = new cdk.Stack(); + new DeploymentStrategy(stack, 'MyDeploymentStrategy', { + name: 'TestDeploymentStrategy', + rolloutStrategy: RolloutStrategy.exponential({ + growthFactor: 10, + deploymentDuration: cdk.Duration.minutes(10), + }), + }); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::DeploymentStrategy', { + Name: 'TestDeploymentStrategy', + DeploymentDurationInMinutes: 10, + GrowthFactor: 10, + ReplicateTo: 'NONE', + GrowthType: 'EXPONENTIAL', + }); + }); + + test('deployment strategy with replicate to', () => { + const stack = new cdk.Stack(); + new DeploymentStrategy(stack, 'MyDeploymentStrategy', { + name: 'TestDeploymentStrategy', + rolloutStrategy: RolloutStrategy.linear({ + growthFactor: 10, + deploymentDuration: cdk.Duration.minutes(10), + }), + }); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::DeploymentStrategy', { + Name: 'TestDeploymentStrategy', + DeploymentDurationInMinutes: 10, + GrowthFactor: 10, + ReplicateTo: 'NONE', + GrowthType: 'LINEAR', + }); + }); + + test('from deployment strategy arn', () => { + const stack = new cdk.Stack(); + const deploymentStrategy = DeploymentStrategy.fromDeploymentStrategyArn(stack, 'MyDeploymentStrategy', + 'arn:aws:appconfig:us-west-2:123456789012:deploymentstrategy/abc123'); + + expect(deploymentStrategy.deploymentStrategyId).toEqual('abc123'); + expect(deploymentStrategy.env.account).toEqual('123456789012'); + expect(deploymentStrategy.env.region).toEqual('us-west-2'); + }); + + test('from deployment strategy arn with no resource name', () => { + const stack = new cdk.Stack(); + expect(() => { + DeploymentStrategy.fromDeploymentStrategyArn(stack, 'MyDeploymentStrategy', + 'arn:aws:appconfig:us-west-2:123456789012:deploymentstrategy/'); + }).toThrow('Missing required deployment strategy id from deployment strategy ARN'); + }); + + test('from deployment strategy id', () => { + const cdkApp = new App(); + const stack = new cdk.Stack(cdkApp, 'Stack', { + env: { + region: 'us-west-2', + account: '123456789012', + }, + }); + const deploymentStrategy = DeploymentStrategy.fromDeploymentStrategyId(stack, 'MyDeploymentStrategy', 'abc123'); + + expect(deploymentStrategy.deploymentStrategyId).toEqual('abc123'); + expect(deploymentStrategy.env.account).toEqual('123456789012'); + expect(deploymentStrategy.env.region).toEqual('us-west-2'); + }); + + test('from predefined all at once deployment strategy id', () => { + const cdkApp = new App(); + const stack = new cdk.Stack(cdkApp, 'Stack', { + env: { + region: 'us-west-2', + account: '123456789012', + }, + }); + const deploymentStrategy = DeploymentStrategy.fromDeploymentStrategyId(stack, 'MyDeploymentStrategy', PredefinedDeploymentStrategyId.ALL_AT_ONCE); + + expect(deploymentStrategy.deploymentStrategyId).toEqual('AppConfig.AllAtOnce'); + expect(deploymentStrategy.env.account).toEqual('123456789012'); + expect(deploymentStrategy.env.region).toEqual('us-west-2'); + }); + + test('from predefined canary deployment strategy id', () => { + const cdkApp = new App(); + const stack = new cdk.Stack(cdkApp, 'Stack', { + env: { + region: 'us-west-2', + account: '123456789012', + }, + }); + const deploymentStrategy = DeploymentStrategy.fromDeploymentStrategyId(stack, 'MyDeploymentStrategy', PredefinedDeploymentStrategyId.CANARY_10_PERCENT_20_MINUTES); + + expect(deploymentStrategy.deploymentStrategyId).toEqual('AppConfig.Canary10Percent20Minutes'); + expect(deploymentStrategy.env.account).toEqual('123456789012'); + expect(deploymentStrategy.env.region).toEqual('us-west-2'); + }); + + test('from predefined linear deployment strategy id', () => { + const cdkApp = new App(); + const stack = new cdk.Stack(cdkApp, 'Stack', { + env: { + region: 'us-west-2', + account: '123456789012', + }, + }); + const deploymentStrategy = DeploymentStrategy.fromDeploymentStrategyId(stack, 'MyDeploymentStrategy', PredefinedDeploymentStrategyId.LINEAR_50_PERCENT_EVERY_30_SECONDS); + + expect(deploymentStrategy.deploymentStrategyId).toEqual('AppConfig.Linear50PercentEvery30Seconds'); + expect(deploymentStrategy.env.account).toEqual('123456789012'); + expect(deploymentStrategy.env.region).toEqual('us-west-2'); + }); + + test('all at once deployment strategy with no bake time', () => { + const stack = new cdk.Stack(); + new DeploymentStrategy(stack, 'MyDeploymentStrategy', { + rolloutStrategy: RolloutStrategy.ALL_AT_ONCE, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::DeploymentStrategy', { + Name: 'MyDeploymentStrategy', + DeploymentDurationInMinutes: 0, + GrowthFactor: 100, + FinalBakeTimeInMinutes: 10, + ReplicateTo: 'NONE', + GrowthType: 'LINEAR', + }); + }); + + test('all at once deployment strategy with bake time', () => { + const stack = new cdk.Stack(); + new DeploymentStrategy(stack, 'MyDeploymentStrategy', { + rolloutStrategy: RolloutStrategy.ALL_AT_ONCE, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::DeploymentStrategy', { + Name: 'MyDeploymentStrategy', + DeploymentDurationInMinutes: 0, + GrowthFactor: 100, + FinalBakeTimeInMinutes: 10, + ReplicateTo: 'NONE', + GrowthType: 'LINEAR', + }); + }); + + test('canary deployment strategy with no bake time', () => { + const stack = new cdk.Stack(); + new DeploymentStrategy(stack, 'MyDeploymentStrategy', { + rolloutStrategy: RolloutStrategy.CANARY_10_PERCENT_20_MINUTES, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::DeploymentStrategy', { + Name: 'MyDeploymentStrategy', + DeploymentDurationInMinutes: 20, + GrowthFactor: 10, + FinalBakeTimeInMinutes: 10, + ReplicateTo: 'NONE', + GrowthType: 'EXPONENTIAL', + }); + }); + + test('linear deployment strategy with no bake time', () => { + const stack = new cdk.Stack(); + new DeploymentStrategy(stack, 'MyDeploymentStrategy', { + rolloutStrategy: RolloutStrategy.LINEAR_50_PERCENT_EVERY_30_SECONDS, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::DeploymentStrategy', { + Name: 'MyDeploymentStrategy', + DeploymentDurationInMinutes: 1, + GrowthFactor: 50, + FinalBakeTimeInMinutes: 1, + ReplicateTo: 'NONE', + GrowthType: 'LINEAR', + }); + }); +}); \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/environment.test.ts b/packages/@aws-cdk/aws-appconfig-alpha/test/environment.test.ts new file mode 100644 index 0000000000000..e0950fa0ed564 --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/environment.test.ts @@ -0,0 +1,242 @@ +import * as cdk from 'aws-cdk-lib'; +import { App } from 'aws-cdk-lib'; +import { Template } from 'aws-cdk-lib/assertions'; +import { Alarm, Metric } from 'aws-cdk-lib/aws-cloudwatch'; +import * as iam from 'aws-cdk-lib/aws-iam'; +import { Application, Environment } from '../lib'; + +describe('environment', () => { + test('default environment', () => { + const stack = new cdk.Stack(); + const app = new Application(stack, 'MyAppConfig'); + new Environment(stack, 'MyEnvironment', { + application: app, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::Environment', { + Name: 'MyEnvironment', + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + }); + }); + + test('environment with name', () => { + const stack = new cdk.Stack(); + const app = new Application(stack, 'MyAppConfig'); + new Environment(stack, 'MyEnvironment', { + name: 'TestEnv', + application: app, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::Environment', { + Name: 'TestEnv', + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + }); + }); + + test('environment with description', () => { + const stack = new cdk.Stack(); + const app = new Application(stack, 'MyAppConfig'); + new Environment(stack, 'MyEnvironment', { + name: 'TestEnv', + application: app, + description: 'This is my description', + }); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::Environment', { + Name: 'TestEnv', + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + Description: 'This is my description', + }); + }); + + test('environment with monitors with alarm and alarmRole', () => { + const stack = new cdk.Stack(); + const app = new Application(stack, 'MyAppConfig'); + const env = new Environment(stack, 'MyEnvironment', { + name: 'TestEnv', + application: app, + monitors: [ + { + alarm: new Alarm(stack, 'Alarm', { + threshold: 5, + evaluationPeriods: 5, + metric: new Metric( + { + namespace: 'aws', + metricName: 'myMetric', + }, + ), + }), + alarmRole: new iam.Role(stack, 'Role', { + assumedBy: new iam.ServicePrincipal('appconfig.amazonaws.com'), + }), + }, + ], + }); + + Template.fromStack(stack).resourceCountIs('AWS::CloudWatch::Alarm', 1); + Template.fromStack(stack).resourceCountIs('AWS::IAM::Role', 1); + expect(env).toBeDefined(); + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::Environment', { + Name: 'TestEnv', + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + Monitors: [ + { + AlarmArn: { + 'Fn::GetAtt': [ + 'Alarm7103F465', + 'Arn', + ], + }, + AlarmRoleArn: { + 'Fn::GetAtt': [ + 'Role1ABCC5F0', + 'Arn', + ], + }, + }, + ], + }); + }); + + test('environment with monitors with only alarm', () => { + const stack = new cdk.Stack(); + const alarm = new Alarm(stack, 'Alarm', { + threshold: 5, + evaluationPeriods: 5, + metric: new Metric( + { + namespace: 'aws', + metricName: 'myMetric', + }, + ), + }); + const app = new Application(stack, 'MyAppConfig'); + const env = new Environment(stack, 'MyEnvironment', { + name: 'TestEnv', + application: app, + monitors: [ + { + alarm, + }, + ], + }); + + expect(env).toBeDefined(); + Template.fromStack(stack).resourceCountIs('AWS::CloudWatch::Alarm', 1); + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::Environment', { + Name: 'TestEnv', + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + Monitors: [ + { + AlarmArn: { + 'Fn::GetAtt': [ + 'Alarm7103F465', + 'Arn', + ], + }, + AlarmRoleArn: { + 'Fn::GetAtt': [ + 'MyEnvironmentRoleC08961D3', + 'Arn', + ], + }, + }, + ], + }); + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { + Policies: [ + { + PolicyDocument: { + Statement: [ + { + Effect: iam.Effect.ALLOW, + Resource: { + 'Fn::GetAtt': [ + 'Alarm7103F465', + 'Arn', + ], + }, + Action: 'cloudwatch:DescribeAlarms', + }, + ], + }, + PolicyName: 'AllowAppConfigMonitorAlarmPolicy', + }, + ], + }); + }); + + test('from environment arn', () => { + const stack = new cdk.Stack(); + const env = Environment.fromEnvironmentArn(stack, 'MyEnvironment', + 'arn:aws:appconfig:us-west-2:123456789012:application/abc123/environment/def456'); + + expect(env.applicationId).toEqual('abc123'); + expect(env.environmentId).toEqual('def456'); + expect(env.env.account).toEqual('123456789012'); + expect(env.env.region).toEqual('us-west-2'); + }); + + test('from environment arn with no resource name', () => { + const stack = new cdk.Stack(); + expect(() => { + Environment.fromEnvironmentArn(stack, 'MyEnvironment', + 'arn:aws:appconfig:us-west-2:123456789012:application/'); + }).toThrow('Missing required /$/{applicationId}/environment//$/{environmentId} from environment ARN:'); + }); + + test('from environment arn with missing slash', () => { + const stack = new cdk.Stack(); + expect(() => { + Environment.fromEnvironmentArn(stack, 'MyEnvironment', + 'arn:aws:appconfig:us-west-2:123456789012:application/abc123environment/def456'); + }).toThrow('Missing required parameters for environment ARN: format should be /$/{applicationId}/environment//$/{environmentId}'); + }); + + test('from environment arn with no application id', () => { + const stack = new cdk.Stack(); + expect(() => { + Environment.fromEnvironmentArn(stack, 'MyEnvironment', + 'arn:aws:appconfig:us-west-2:123456789012:application//environment/def456'); + }).toThrow('Missing required parameters for environment ARN: format should be /$/{applicationId}/environment//$/{environmentId}'); + }); + + test('from environment arn with no environment id', () => { + const stack = new cdk.Stack(); + expect(() => { + Environment.fromEnvironmentArn(stack, 'MyEnvironment', + 'arn:aws:appconfig:us-west-2:123456789012:application/abc123/environment/'); + }).toThrow('Missing required parameters for environment ARN: format should be /$/{applicationId}/environment//$/{environmentId}'); + }); + + test('from environment attributes', () => { + const app = new App(); + const stack = new cdk.Stack(app, 'Stack', { + env: { + region: 'us-west-2', + account: '123456789012', + }, + }); + const appConfigApp = new Application(stack, 'MyAppConfig'); + const env = Environment.fromEnvironmentAttributes(stack, 'MyEnvironment', { + application: appConfigApp, + environmentId: 'def456', + }); + + expect(env.environmentId).toEqual('def456'); + expect(env.applicationId).toBeDefined(); + expect(env.env.account).toEqual('123456789012'); + expect(env.env.region).toEqual('us-west-2'); + }); +}); diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/extension.test.ts b/packages/@aws-cdk/aws-appconfig-alpha/test/extension.test.ts new file mode 100644 index 0000000000000..c4faed3acb4c3 --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/extension.test.ts @@ -0,0 +1,664 @@ +import * as cdk from 'aws-cdk-lib'; +import { App } from 'aws-cdk-lib'; +import { Template } from 'aws-cdk-lib/assertions'; +import { EventBus } from 'aws-cdk-lib/aws-events'; +import { Effect, Role, ServicePrincipal } from 'aws-cdk-lib/aws-iam'; +import * as lambda from 'aws-cdk-lib/aws-lambda'; +import { Topic } from 'aws-cdk-lib/aws-sns'; +import { Queue } from 'aws-cdk-lib/aws-sqs'; +import { + Action, + ActionPoint, + Application, + ConfigurationContent, + Extension, + HostedConfiguration, + Parameter, + LambdaDestination, + SqsDestination, + SnsDestination, + EventBridgeDestination, +} from '../lib'; + +describe('extension', () => { + test('simple extension with lambda', () => { + const stack = new cdk.Stack(); + const func = new lambda.Function(stack, 'MyFunction', { + runtime: lambda.Runtime.PYTHON_3_8, + code: lambda.Code.fromInline('# dummy func'), + handler: 'index.handler', + }); + Object.defineProperty(func, 'functionArn', { + value: 'arn:lambda:us-east-1:123456789012:function:my-function', + }); + new Extension(stack, 'MyExtension', { + actions: [ + new Action({ + actionPoints: [ + ActionPoint.ON_DEPLOYMENT_COMPLETE, + ActionPoint.ON_DEPLOYMENT_ROLLED_BACK, + ], + eventDestination: new LambdaDestination(func), + }), + ], + }); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::Extension', { + Name: 'MyExtension', + Actions: { + ON_DEPLOYMENT_COMPLETE: [ + { + Name: 'MyExtension', + RoleArn: { 'Fn::GetAtt': ['MyExtensionRoleFD367BEA2AE12', 'Arn'] }, + Uri: 'arn:lambda:us-east-1:123456789012:function:my-function', + }, + ], + ON_DEPLOYMENT_ROLLED_BACK: [ + { + Name: 'MyExtension', + RoleArn: { 'Fn::GetAtt': ['MyExtensionRoleFD367BEA2AE12', 'Arn'] }, + Uri: 'arn:lambda:us-east-1:123456789012:function:my-function', + }, + ], + }, + }); + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { + Policies: [ + { + PolicyDocument: { + Statement: [ + { + Effect: Effect.ALLOW, + Resource: 'arn:lambda:us-east-1:123456789012:function:my-function', + Action: [ + 'lambda:InvokeFunction', + 'lambda:InvokeAsync', + ], + }, + ], + }, + PolicyName: 'AllowAppConfigInvokeExtensionEventSourcePolicy', + }, + ], + }); + }); + + test('simple extension with two lambdas', () => { + const stack = new cdk.Stack(); + const func1 = new lambda.Function(stack, 'MyFunction1', { + runtime: lambda.Runtime.PYTHON_3_8, + code: lambda.Code.fromInline('# dummy func'), + handler: 'index.handler', + }); + const func2 = new lambda.Function(stack, 'MyFunction2', { + runtime: lambda.Runtime.PYTHON_3_8, + code: lambda.Code.fromInline('# dummy func'), + handler: 'index.handler', + }); + Object.defineProperty(func1, 'functionArn', { + value: 'arn:lambda:us-east-1:123456789012:function:my-function', + }); + Object.defineProperty(func2, 'functionArn', { + value: 'arn:lambda:us-east-1:123456789012:function:my-diff-function', + }); + new Extension(stack, 'MyExtension', { + actions: [ + new Action({ + actionPoints: [ + ActionPoint.ON_DEPLOYMENT_COMPLETE, + ], + eventDestination: new LambdaDestination(func1), + }), + new Action({ + actionPoints: [ + ActionPoint.ON_DEPLOYMENT_ROLLED_BACK, + ], + eventDestination: new LambdaDestination(func2), + }), + ], + }); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::Extension', { + Name: 'MyExtension', + Actions: { + ON_DEPLOYMENT_COMPLETE: [ + { + Name: 'MyExtension', + RoleArn: { 'Fn::GetAtt': ['MyExtensionRoleFD367BEA2AE12', 'Arn'] }, + Uri: 'arn:lambda:us-east-1:123456789012:function:my-function', + }, + ], + ON_DEPLOYMENT_ROLLED_BACK: [ + { + Name: 'MyExtension', + RoleArn: { 'Fn::GetAtt': ['MyExtensionRole78071F4E7B10A', 'Arn'] }, + Uri: 'arn:lambda:us-east-1:123456789012:function:my-diff-function', + }, + ], + }, + }); + Template.fromStack(stack).resourcePropertiesCountIs('AWS::IAM::Role', { + Policies: [ + { + PolicyDocument: { + Statement: [ + { + Effect: Effect.ALLOW, + Resource: 'arn:lambda:us-east-1:123456789012:function:my-function', + Action: [ + 'lambda:InvokeFunction', + 'lambda:InvokeAsync', + ], + }, + ], + }, + PolicyName: 'AllowAppConfigInvokeExtensionEventSourcePolicy', + }, + ], + }, 1); + Template.fromStack(stack).resourcePropertiesCountIs('AWS::IAM::Role', { + Policies: [ + { + PolicyDocument: { + Statement: [ + { + Effect: Effect.ALLOW, + Resource: 'arn:lambda:us-east-1:123456789012:function:my-diff-function', + Action: [ + 'lambda:InvokeFunction', + 'lambda:InvokeAsync', + ], + }, + ], + }, + PolicyName: 'AllowAppConfigInvokeExtensionEventSourcePolicy', + }, + ], + }, 1); + }); + + test('extension with all props using lambda', () => { + const stack = new cdk.Stack(); + const func = new lambda.Function(stack, 'MyFunction', { + runtime: lambda.Runtime.PYTHON_3_8, + code: lambda.Code.fromInline('# dummy func'), + handler: 'index.handler', + }); + Object.defineProperty(func, 'functionArn', { + value: 'arn:lambda:us-east-1:123456789012:function:my-function', + }); + const appconfig = new Application(stack, 'MyApplication', { + name: 'MyApplication', + }); + const ext = new Extension(stack, 'MyExtension', { + actions: [ + new Action({ + actionPoints: [ + ActionPoint.ON_DEPLOYMENT_COMPLETE, + ], + eventDestination: new LambdaDestination(func), + }), + ], + name: 'TestExtension', + description: 'This is my extension', + parameters: [ + Parameter.required('testVariable', 'hello'), + Parameter.notRequired('testNotRequiredVariable'), + ], + latestVersionNumber: 1, + }); + appconfig.addExtension(ext); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::Extension', { + Name: 'TestExtension', + Actions: { + ON_DEPLOYMENT_COMPLETE: [ + { + Name: 'MyExtension', + RoleArn: { 'Fn::GetAtt': ['MyExtensionRoleFD367BEA2AE12', 'Arn'] }, + Uri: 'arn:lambda:us-east-1:123456789012:function:my-function', + }, + ], + }, + Description: 'This is my extension', + Parameters: { + testVariable: { Required: true }, + testNotRequiredVariable: { Required: false }, + }, + LatestVersionNumber: 1, + }); + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::ExtensionAssociation', { + ExtensionIdentifier: { + 'Fn::GetAtt': ['MyExtension89A915D0', 'Id'], + }, + ExtensionVersionNumber: { + 'Fn::GetAtt': ['MyExtension89A915D0', 'VersionNumber'], + }, + Parameters: { + testVariable: 'hello', + }, + ResourceIdentifier: { + 'Fn::Join': [ + '', + [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':appconfig:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':application/', + { Ref: 'MyApplication5C63EC1D' }, + ], + ], + }, + }); + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { + Policies: [ + { + PolicyDocument: { + Statement: [ + { + Effect: Effect.ALLOW, + Resource: 'arn:lambda:us-east-1:123456789012:function:my-function', + Action: [ + 'lambda:InvokeFunction', + 'lambda:InvokeAsync', + ], + }, + ], + }, + PolicyName: 'AllowAppConfigInvokeExtensionEventSourcePolicy', + }, + ], + }); + }); + + test('extension with queue', () => { + const stack = new cdk.Stack(); + const queue = new Queue(stack, 'MyQueue'); + Object.defineProperty(queue, 'queueArn', { + value: 'arn:sqs:us-east-1:123456789012:my-queue', + }); + new Extension(stack, 'MyExtension', { + actions: [ + new Action({ + actionPoints: [ + ActionPoint.ON_DEPLOYMENT_ROLLED_BACK, + ], + eventDestination: new SqsDestination(queue), + name: 'ActionTestName', + description: 'This is my action description', + }), + ], + }); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::Extension', { + Name: 'MyExtension', + Actions: { + ON_DEPLOYMENT_ROLLED_BACK: [ + { + Description: 'This is my action description', + Name: 'ActionTestName', + RoleArn: { 'Fn::GetAtt': ['MyExtensionRoleC3C234A0DDBAB', 'Arn'] }, + Uri: 'arn:sqs:us-east-1:123456789012:my-queue', + }, + ], + }, + }); + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { + Policies: [ + { + PolicyDocument: { + Statement: [ + { + Effect: Effect.ALLOW, + Resource: 'arn:sqs:us-east-1:123456789012:my-queue', + Action: 'sqs:SendMessage', + }, + ], + }, + PolicyName: 'AllowAppConfigInvokeExtensionEventSourcePolicy', + }, + ], + }); + }); + + test('extension with topic', () => { + const stack = new cdk.Stack(); + const topic = new Topic(stack, 'MyTopic'); + Object.defineProperty(topic, 'topicArn', { + value: 'arn:sns:us-east-1:123456789012:my-topic', + }); + new Extension(stack, 'MyExtension', { + actions: [ + new Action({ + actionPoints: [ + ActionPoint.ON_DEPLOYMENT_BAKING, + ], + eventDestination: new SnsDestination(topic), + }), + ], + }); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::Extension', { + Name: 'MyExtension', + Actions: { + ON_DEPLOYMENT_BAKING: [ + { + Name: 'MyExtension', + RoleArn: { 'Fn::GetAtt': ['MyExtensionRole600FCFE1621BF', 'Arn'] }, + Uri: 'arn:sns:us-east-1:123456789012:my-topic', + }, + ], + }, + }); + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { + Policies: [ + { + PolicyDocument: { + Statement: [ + { + Effect: Effect.ALLOW, + Resource: 'arn:sns:us-east-1:123456789012:my-topic', + Action: 'sns:Publish', + }, + ], + }, + PolicyName: 'AllowAppConfigInvokeExtensionEventSourcePolicy', + }, + ], + }); + }); + + test('extension with bus', () => { + const stack = new cdk.Stack(); + const bus = new EventBus(stack, 'MyEventBus'); + Object.defineProperty(bus, 'eventBusArn', { + value: 'arn:aws:events:us-east-1:123456789012:event-bus/aws.partner/PartnerName/acct1/repo1', + }); + new Extension(stack, 'MyExtension', { + actions: [ + new Action({ + actionPoints: [ + ActionPoint.ON_DEPLOYMENT_BAKING, + ], + eventDestination: new EventBridgeDestination(bus), + }), + ], + }); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::Extension', { + Name: 'MyExtension', + Actions: { + ON_DEPLOYMENT_BAKING: [ + { + Name: 'MyExtension', + Uri: 'arn:aws:events:us-east-1:123456789012:event-bus/aws.partner/PartnerName/acct1/repo1', + }, + ], + }, + }); + Template.fromStack(stack).resourceCountIs('AWS::IAM::Role', 0); + }); + + test('extension with associated environment', () => { + const stack = new cdk.Stack(); + const func = new lambda.Function(stack, 'MyFunction', { + runtime: lambda.Runtime.PYTHON_3_8, + code: lambda.Code.fromInline('# dummy func'), + handler: 'index.handler', + }); + Object.defineProperty(func, 'functionArn', { + value: 'arn:lambda:us-east-1:123456789012:function:my-function', + }); + const app = new Application(stack, 'MyApplication'); + const env = app.addEnvironment('MyEnvironment'); + const ext = new Extension(stack, 'MyExtension', { + actions: [ + new Action({ + actionPoints: [ + ActionPoint.ON_DEPLOYMENT_COMPLETE, + ], + eventDestination: new LambdaDestination(func), + }), + ], + }); + env.addExtension(ext); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::Extension', { + Name: 'MyExtension', + Actions: { + ON_DEPLOYMENT_COMPLETE: [ + { + Name: 'MyExtension', + RoleArn: { 'Fn::GetAtt': ['MyExtensionRoleFD367BEA2AE12', 'Arn'] }, + Uri: 'arn:lambda:us-east-1:123456789012:function:my-function', + }, + ], + }, + }); + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::ExtensionAssociation', { + ExtensionIdentifier: { + 'Fn::GetAtt': ['MyExtension89A915D0', 'Id'], + }, + ExtensionVersionNumber: { + 'Fn::GetAtt': ['MyExtension89A915D0', 'VersionNumber'], + }, + ResourceIdentifier: { + 'Fn::Join': [ + '', + [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':appconfig:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':application/', + { Ref: 'MyApplication5C63EC1D' }, + '/environment/', + { Ref: 'MyApplicationMyEnvironment10D94356' }, + ], + ], + }, + }); + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { + Policies: [ + { + PolicyDocument: { + Statement: [ + { + Effect: Effect.ALLOW, + Resource: 'arn:lambda:us-east-1:123456789012:function:my-function', + Action: [ + 'lambda:InvokeFunction', + 'lambda:InvokeAsync', + ], + }, + ], + }, + PolicyName: 'AllowAppConfigInvokeExtensionEventSourcePolicy', + }, + ], + }); + }); + + test('extension with associated configuration profile', () => { + const stack = new cdk.Stack(); + const func = new lambda.Function(stack, 'MyFunction', { + runtime: lambda.Runtime.PYTHON_3_8, + code: lambda.Code.fromInline('# dummy func'), + handler: 'index.handler', + }); + Object.defineProperty(func, 'functionArn', { + value: 'arn:lambda:us-east-1:123456789012:function:my-function', + }); + const app = new Application(stack, 'MyApplication'); + const configProfile = new HostedConfiguration(stack, 'MyConfiguration', { + application: app, + content: ConfigurationContent.fromInline('This is my content.'), + }); + const ext = new Extension(stack, 'MyExtension', { + actions: [ + new Action({ + actionPoints: [ + ActionPoint.ON_DEPLOYMENT_COMPLETE, + ], + eventDestination: new LambdaDestination(func), + }), + ], + }); + configProfile.addExtension(ext); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::Extension', { + Name: 'MyExtension', + Actions: { + ON_DEPLOYMENT_COMPLETE: [ + { + Name: 'MyExtension', + RoleArn: { 'Fn::GetAtt': ['MyExtensionRoleFD367BEA2AE12', 'Arn'] }, + Uri: 'arn:lambda:us-east-1:123456789012:function:my-function', + }, + ], + }, + }); + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::ExtensionAssociation', { + ExtensionIdentifier: { + 'Fn::GetAtt': ['MyExtension89A915D0', 'Id'], + }, + ExtensionVersionNumber: { + 'Fn::GetAtt': ['MyExtension89A915D0', 'VersionNumber'], + }, + ResourceIdentifier: { + 'Fn::Join': [ + '', + [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':appconfig:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':application/', + { Ref: 'MyApplication5C63EC1D' }, + '/configurationprofile/', + { Ref: 'MyConfigurationConfigurationProfileEE0ECA85' }, + ], + ], + }, + }); + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { + Policies: [ + { + PolicyDocument: { + Statement: [ + { + Effect: Effect.ALLOW, + Resource: 'arn:lambda:us-east-1:123456789012:function:my-function', + Action: [ + 'lambda:InvokeFunction', + 'lambda:InvokeAsync', + ], + }, + ], + }, + PolicyName: 'AllowAppConfigInvokeExtensionEventSourcePolicy', + }, + ], + }); + }); + + test('extension with execution role', () => { + const stack = new cdk.Stack(); + const func = new lambda.Function(stack, 'MyFunction', { + runtime: lambda.Runtime.PYTHON_3_8, + code: lambda.Code.fromInline('# dummy func'), + handler: 'index.handler', + }); + Object.defineProperty(func, 'functionArn', { + value: 'arn:lambda:us-east-1:123456789012:function:my-function', + }); + const role = new Role(stack, 'MyRole', { + assumedBy: new ServicePrincipal('appconfig.amazonaws.com'), + }); + new Extension(stack, 'MyExtension', { + actions: [ + new Action({ + actionPoints: [ + ActionPoint.ON_DEPLOYMENT_COMPLETE, + ], + eventDestination: new LambdaDestination(func), + executionRole: role, + }), + ], + }); + + Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::Extension', { + Name: 'MyExtension', + Actions: { + ON_DEPLOYMENT_COMPLETE: [ + { + Name: 'MyExtension', + RoleArn: { 'Fn::GetAtt': ['MyRoleF48FFE04', 'Arn'] }, + Uri: 'arn:lambda:us-east-1:123456789012:function:my-function', + }, + ], + }, + }); + }); + + test('from extension arn', () => { + const stack = new cdk.Stack(); + const extension = Extension.fromExtensionArn(stack, 'MyExtension', + 'arn:aws:appconfig:us-west-2:123456789012:extension/abc123/1'); + + expect(extension.extensionId).toEqual('abc123'); + expect(extension.extensionVersionNumber).toEqual(1); + expect(extension.env.account).toEqual('123456789012'); + expect(extension.env.region).toEqual('us-west-2'); + }); + + test('from extension arn with no resource name', () => { + const stack = new cdk.Stack(); + expect(() => { + Extension.fromExtensionArn(stack, 'MyExtension', + 'arn:aws:appconfig:us-west-2:123456789012:extension/'); + }).toThrow('Missing required /$/{extensionId}//$/{extensionVersionNumber} from configuration profile ARN:'); + }); + + test('from extension arn with no extension id', () => { + const stack = new cdk.Stack(); + expect(() => { + Extension.fromExtensionArn(stack, 'MyExtension', + 'arn:aws:appconfig:us-west-2:123456789012:extension//1'); + }).toThrow('Missing required parameters for extension ARN: format should be /$/{extensionId}//$/{extensionVersionNumber}'); + }); + + test('from extension arn with no extension version number', () => { + const stack = new cdk.Stack(); + expect(() => { + Extension.fromExtensionArn(stack, 'MyExtension', + 'arn:aws:appconfig:us-west-2:123456789012:extension/abc123/'); + }).toThrow('Missing required parameters for extension ARN: format should be /$/{extensionId}//$/{extensionVersionNumber}'); + }); + + test('from extension id', () => { + const cdkApp = new App(); + const stack = new cdk.Stack(cdkApp, 'Stack', { + env: { + region: 'us-west-2', + account: '123456789012', + }, + }); + const extension = Extension.fromExtensionAttributes(stack, 'Extension', { + extensionId: 'abc123', + extensionVersionNumber: 1, + }); + + expect(extension.extensionId).toEqual('abc123'); + expect(extension.extensionVersionNumber).toEqual(1); + expect(extension.env.account).toEqual('123456789012'); + expect(extension.env.region).toEqual('us-west-2'); + }); +}); \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.application.js.snapshot/appconfigapplicationDefaultTestDeployAssertD6537C40.assets.json b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.application.js.snapshot/appconfigapplicationDefaultTestDeployAssertD6537C40.assets.json new file mode 100644 index 0000000000000..b6f615178fa21 --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.application.js.snapshot/appconfigapplicationDefaultTestDeployAssertD6537C40.assets.json @@ -0,0 +1,19 @@ +{ + "version": "32.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "appconfigapplicationDefaultTestDeployAssertD6537C40.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.application.js.snapshot/appconfigapplicationDefaultTestDeployAssertD6537C40.template.json b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.application.js.snapshot/appconfigapplicationDefaultTestDeployAssertD6537C40.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.application.js.snapshot/appconfigapplicationDefaultTestDeployAssertD6537C40.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.application.js.snapshot/aws-appconfig-application.assets.json b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.application.js.snapshot/aws-appconfig-application.assets.json new file mode 100644 index 0000000000000..264d0b0f3253f --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.application.js.snapshot/aws-appconfig-application.assets.json @@ -0,0 +1,19 @@ +{ + "version": "32.0.0", + "files": { + "1bbcd83a6b27b680c911215e6afe359b98571c3d9d715d5ebf40ae0522a31c33": { + "source": { + "path": "aws-appconfig-application.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "1bbcd83a6b27b680c911215e6afe359b98571c3d9d715d5ebf40ae0522a31c33.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.application.js.snapshot/aws-appconfig-application.template.json b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.application.js.snapshot/aws-appconfig-application.template.json new file mode 100644 index 0000000000000..6cbd018c1b9d1 --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.application.js.snapshot/aws-appconfig-application.template.json @@ -0,0 +1,87 @@ +{ + "Resources": { + "MyAppConfigB4B63E75": { + "Type": "AWS::AppConfig::Application", + "Properties": { + "Description": "This is my application for testing", + "Name": "awsappconfigapplication-MyAppConfig-5BFACBE9" + } + }, + "MyTaskDefTaskRole727F9D3B": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "ecs-tasks.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "MyTaskDef01F0D39B": { + "Type": "AWS::ECS::TaskDefinition", + "Properties": { + "ContainerDefinitions": [ + { + "Essential": true, + "Image": "public.ecr.aws/aws-appconfig/aws-appconfig-agent:latest", + "Name": "AppConfigAgentContainer" + } + ], + "Cpu": "256", + "Family": "awsappconfigapplicationMyTaskDef7372410D", + "Memory": "512", + "NetworkMode": "awsvpc", + "RequiresCompatibilities": [ + "FARGATE" + ], + "TaskRoleArn": { + "Fn::GetAtt": [ + "MyTaskDefTaskRole727F9D3B", + "Arn" + ] + } + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.application.js.snapshot/cdk.out b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.application.js.snapshot/cdk.out new file mode 100644 index 0000000000000..f0b901e7c06e5 --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.application.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"32.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.application.js.snapshot/integ.json b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.application.js.snapshot/integ.json new file mode 100644 index 0000000000000..02a72ca75410e --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.application.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "32.0.0", + "testCases": { + "appconfig-application/DefaultTest": { + "stacks": [ + "aws-appconfig-application" + ], + "assertionStack": "appconfig-application/DefaultTest/DeployAssert", + "assertionStackName": "appconfigapplicationDefaultTestDeployAssertD6537C40" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.application.js.snapshot/manifest.json b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.application.js.snapshot/manifest.json new file mode 100644 index 0000000000000..bc03046086754 --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.application.js.snapshot/manifest.json @@ -0,0 +1,123 @@ +{ + "version": "32.0.0", + "artifacts": { + "aws-appconfig-application.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "aws-appconfig-application.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "aws-appconfig-application": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "aws-appconfig-application.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/1bbcd83a6b27b680c911215e6afe359b98571c3d9d715d5ebf40ae0522a31c33.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "aws-appconfig-application.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "aws-appconfig-application.assets" + ], + "metadata": { + "/aws-appconfig-application/MyAppConfig/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyAppConfigB4B63E75" + } + ], + "/aws-appconfig-application/MyTaskDef/TaskRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyTaskDefTaskRole727F9D3B" + } + ], + "/aws-appconfig-application/MyTaskDef/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyTaskDef01F0D39B" + } + ], + "/aws-appconfig-application/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/aws-appconfig-application/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "aws-appconfig-application" + }, + "appconfigapplicationDefaultTestDeployAssertD6537C40.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "appconfigapplicationDefaultTestDeployAssertD6537C40.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "appconfigapplicationDefaultTestDeployAssertD6537C40": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "appconfigapplicationDefaultTestDeployAssertD6537C40.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "appconfigapplicationDefaultTestDeployAssertD6537C40.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "appconfigapplicationDefaultTestDeployAssertD6537C40.assets" + ], + "metadata": { + "/appconfig-application/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/appconfig-application/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "appconfig-application/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.application.js.snapshot/tree.json b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.application.js.snapshot/tree.json new file mode 100644 index 0000000000000..9d9934f4ae1b5 --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.application.js.snapshot/tree.json @@ -0,0 +1,220 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "aws-appconfig-application": { + "id": "aws-appconfig-application", + "path": "aws-appconfig-application", + "children": { + "MyAppConfig": { + "id": "MyAppConfig", + "path": "aws-appconfig-application/MyAppConfig", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-appconfig-application/MyAppConfig/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::Application", + "aws:cdk:cloudformation:props": { + "description": "This is my application for testing", + "name": "awsappconfigapplication-MyAppConfig-5BFACBE9" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appconfig.CfnApplication", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "MyTaskDef": { + "id": "MyTaskDef", + "path": "aws-appconfig-application/MyTaskDef", + "children": { + "TaskRole": { + "id": "TaskRole", + "path": "aws-appconfig-application/MyTaskDef/TaskRole", + "children": { + "ImportTaskRole": { + "id": "ImportTaskRole", + "path": "aws-appconfig-application/MyTaskDef/TaskRole/ImportTaskRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-appconfig-application/MyTaskDef/TaskRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "ecs-tasks.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-appconfig-application/MyTaskDef/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::ECS::TaskDefinition", + "aws:cdk:cloudformation:props": { + "containerDefinitions": [ + { + "essential": true, + "image": "public.ecr.aws/aws-appconfig/aws-appconfig-agent:latest", + "name": "AppConfigAgentContainer" + } + ], + "cpu": "256", + "family": "awsappconfigapplicationMyTaskDef7372410D", + "memory": "512", + "networkMode": "awsvpc", + "requiresCompatibilities": [ + "FARGATE" + ], + "taskRoleArn": { + "Fn::GetAtt": [ + "MyTaskDefTaskRole727F9D3B", + "Arn" + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ecs.CfnTaskDefinition", + "version": "0.0.0" + } + }, + "AppConfigAgentContainer": { + "id": "AppConfigAgentContainer", + "path": "aws-appconfig-application/MyTaskDef/AppConfigAgentContainer", + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ecs.ContainerDefinition", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ecs.FargateTaskDefinition", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "aws-appconfig-application/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "aws-appconfig-application/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + }, + "appconfig-application": { + "id": "appconfig-application", + "path": "appconfig-application", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "appconfig-application/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "appconfig-application/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "appconfig-application/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "appconfig-application/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "appconfig-application/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.application.ts b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.application.ts new file mode 100644 index 0000000000000..92f6830918ccd --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.application.ts @@ -0,0 +1,19 @@ +import { Stack, App } from 'aws-cdk-lib'; +import { FargateTaskDefinition } from 'aws-cdk-lib/aws-ecs'; +import { Application } from '../lib'; +import { IntegTest } from '@aws-cdk/integ-tests-alpha'; + +const app = new App(); + +const stack = new Stack(app, 'aws-appconfig-application'); + +new Application(stack, 'MyAppConfig', { + description: 'This is my application for testing', +}); + +const taskDef = new FargateTaskDefinition(stack, 'MyTaskDef'); +Application.addAgentToEcs(taskDef); + +new IntegTest(app, 'appconfig-application', { + testCases: [stack], +}); \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.configuration.js.snapshot/appconfigconfigurationDefaultTestDeployAssert6752CD38.assets.json b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.configuration.js.snapshot/appconfigconfigurationDefaultTestDeployAssert6752CD38.assets.json new file mode 100644 index 0000000000000..e88b36a4a2c59 --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.configuration.js.snapshot/appconfigconfigurationDefaultTestDeployAssert6752CD38.assets.json @@ -0,0 +1,19 @@ +{ + "version": "32.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "appconfigconfigurationDefaultTestDeployAssert6752CD38.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.configuration.js.snapshot/appconfigconfigurationDefaultTestDeployAssert6752CD38.template.json b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.configuration.js.snapshot/appconfigconfigurationDefaultTestDeployAssert6752CD38.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.configuration.js.snapshot/appconfigconfigurationDefaultTestDeployAssert6752CD38.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.configuration.js.snapshot/asset.9b7590a121e3b39547297cbf009beb8a27e1cf9daaa760cb82c42bd530d5ef8f/hello/world/codepipeline.txt b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.configuration.js.snapshot/asset.9b7590a121e3b39547297cbf009beb8a27e1cf9daaa760cb82c42bd530d5ef8f/hello/world/codepipeline.txt new file mode 100644 index 0000000000000..167d0deaf2dcc --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.configuration.js.snapshot/asset.9b7590a121e3b39547297cbf009beb8a27e1cf9daaa760cb82c42bd530d5ef8f/hello/world/codepipeline.txt @@ -0,0 +1 @@ +This is the content stored in code pipeline \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.configuration.js.snapshot/asset.9eb41a5505d37607ac419321497a4f8c21cf0ee1f9b4a6b29aa04301aea5c7fd/index.py b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.configuration.js.snapshot/asset.9eb41a5505d37607ac419321497a4f8c21cf0ee1f9b4a6b29aa04301aea5c7fd/index.py new file mode 100644 index 0000000000000..95c458826a0b0 --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.configuration.js.snapshot/asset.9eb41a5505d37607ac419321497a4f8c21cf0ee1f9b4a6b29aa04301aea5c7fd/index.py @@ -0,0 +1,319 @@ +import contextlib +import json +import logging +import os +import shutil +import subprocess +import tempfile +from urllib.request import Request, urlopen +from uuid import uuid4 +from zipfile import ZipFile + +import boto3 + +logger = logging.getLogger() +logger.setLevel(logging.INFO) + +cloudfront = boto3.client('cloudfront') +s3 = boto3.client('s3') + +CFN_SUCCESS = "SUCCESS" +CFN_FAILED = "FAILED" +ENV_KEY_MOUNT_PATH = "MOUNT_PATH" +ENV_KEY_SKIP_CLEANUP = "SKIP_CLEANUP" + +AWS_CLI_CONFIG_FILE = "/tmp/aws_cli_config" +CUSTOM_RESOURCE_OWNER_TAG = "aws-cdk:cr-owned" + +os.putenv('AWS_CONFIG_FILE', AWS_CLI_CONFIG_FILE) + +def handler(event, context): + + def cfn_error(message=None): + logger.error("| cfn_error: %s" % message) + cfn_send(event, context, CFN_FAILED, reason=message, physicalResourceId=event.get('PhysicalResourceId', None)) + + + try: + # We are not logging ResponseURL as this is a pre-signed S3 URL, and could be used to tamper + # with the response CloudFormation sees from this Custom Resource execution. + logger.info({ key:value for (key, value) in event.items() if key != 'ResponseURL'}) + + # cloudformation request type (create/update/delete) + request_type = event['RequestType'] + + # extract resource properties + props = event['ResourceProperties'] + old_props = event.get('OldResourceProperties', {}) + physical_id = event.get('PhysicalResourceId', None) + + try: + source_bucket_names = props['SourceBucketNames'] + source_object_keys = props['SourceObjectKeys'] + source_markers = props.get('SourceMarkers', None) + dest_bucket_name = props['DestinationBucketName'] + dest_bucket_prefix = props.get('DestinationBucketKeyPrefix', '') + extract = props.get('Extract', 'true') == 'true' + retain_on_delete = props.get('RetainOnDelete', "true") == "true" + distribution_id = props.get('DistributionId', '') + user_metadata = props.get('UserMetadata', {}) + system_metadata = props.get('SystemMetadata', {}) + prune = props.get('Prune', 'true').lower() == 'true' + exclude = props.get('Exclude', []) + include = props.get('Include', []) + sign_content = props.get('SignContent', 'false').lower() == 'true' + + # backwards compatibility - if "SourceMarkers" is not specified, + # assume all sources have an empty market map + if source_markers is None: + source_markers = [{} for i in range(len(source_bucket_names))] + + default_distribution_path = dest_bucket_prefix + if not default_distribution_path.endswith("/"): + default_distribution_path += "/" + if not default_distribution_path.startswith("/"): + default_distribution_path = "/" + default_distribution_path + default_distribution_path += "*" + + distribution_paths = props.get('DistributionPaths', [default_distribution_path]) + except KeyError as e: + cfn_error("missing request resource property %s. props: %s" % (str(e), props)) + return + + # configure aws cli options after resetting back to the defaults for each request + if os.path.exists(AWS_CLI_CONFIG_FILE): + os.remove(AWS_CLI_CONFIG_FILE) + if sign_content: + aws_command("configure", "set", "default.s3.payload_signing_enabled", "true") + + # treat "/" as if no prefix was specified + if dest_bucket_prefix == "/": + dest_bucket_prefix = "" + + s3_source_zips = list(map(lambda name, key: "s3://%s/%s" % (name, key), source_bucket_names, source_object_keys)) + s3_dest = "s3://%s/%s" % (dest_bucket_name, dest_bucket_prefix) + old_s3_dest = "s3://%s/%s" % (old_props.get("DestinationBucketName", ""), old_props.get("DestinationBucketKeyPrefix", "")) + + + # obviously this is not + if old_s3_dest == "s3:///": + old_s3_dest = None + + logger.info("| s3_dest: %s" % s3_dest) + logger.info("| old_s3_dest: %s" % old_s3_dest) + + # if we are creating a new resource, allocate a physical id for it + # otherwise, we expect physical id to be relayed by cloudformation + if request_type == "Create": + physical_id = "aws.cdk.s3deployment.%s" % str(uuid4()) + else: + if not physical_id: + cfn_error("invalid request: request type is '%s' but 'PhysicalResourceId' is not defined" % request_type) + return + + # delete or create/update (only if "retain_on_delete" is false) + if request_type == "Delete" and not retain_on_delete: + if not bucket_owned(dest_bucket_name, dest_bucket_prefix): + aws_command("s3", "rm", s3_dest, "--recursive") + + # if we are updating without retention and the destination changed, delete first + if request_type == "Update" and not retain_on_delete and old_s3_dest != s3_dest: + if not old_s3_dest: + logger.warn("cannot delete old resource without old resource properties") + return + + aws_command("s3", "rm", old_s3_dest, "--recursive") + + if request_type == "Update" or request_type == "Create": + s3_deploy(s3_source_zips, s3_dest, user_metadata, system_metadata, prune, exclude, include, source_markers, extract) + + if distribution_id: + cloudfront_invalidate(distribution_id, distribution_paths) + + cfn_send(event, context, CFN_SUCCESS, physicalResourceId=physical_id, responseData={ + # Passing through the ARN sequences dependencees on the deployment + 'DestinationBucketArn': props.get('DestinationBucketArn'), + 'SourceObjectKeys': props.get('SourceObjectKeys'), + }) + except KeyError as e: + cfn_error("invalid request. Missing key %s" % str(e)) + except Exception as e: + logger.exception(e) + cfn_error(str(e)) + +#--------------------------------------------------------------------------------------------------- +# populate all files from s3_source_zips to a destination bucket +def s3_deploy(s3_source_zips, s3_dest, user_metadata, system_metadata, prune, exclude, include, source_markers, extract): + # list lengths are equal + if len(s3_source_zips) != len(source_markers): + raise Exception("'source_markers' and 's3_source_zips' must be the same length") + + # create a temporary working directory in /tmp or if enabled an attached efs volume + if ENV_KEY_MOUNT_PATH in os.environ: + workdir = os.getenv(ENV_KEY_MOUNT_PATH) + "/" + str(uuid4()) + os.mkdir(workdir) + else: + workdir = tempfile.mkdtemp() + + logger.info("| workdir: %s" % workdir) + + # create a directory into which we extract the contents of the zip file + contents_dir=os.path.join(workdir, 'contents') + os.mkdir(contents_dir) + + try: + # download the archive from the source and extract to "contents" + for i in range(len(s3_source_zips)): + s3_source_zip = s3_source_zips[i] + markers = source_markers[i] + + if extract: + archive=os.path.join(workdir, str(uuid4())) + logger.info("archive: %s" % archive) + aws_command("s3", "cp", s3_source_zip, archive) + logger.info("| extracting archive to: %s\n" % contents_dir) + logger.info("| markers: %s" % markers) + extract_and_replace_markers(archive, contents_dir, markers) + else: + logger.info("| copying archive to: %s\n" % contents_dir) + aws_command("s3", "cp", s3_source_zip, contents_dir) + + # sync from "contents" to destination + + s3_command = ["s3", "sync"] + + if prune: + s3_command.append("--delete") + + if exclude: + for filter in exclude: + s3_command.extend(["--exclude", filter]) + + if include: + for filter in include: + s3_command.extend(["--include", filter]) + + s3_command.extend([contents_dir, s3_dest]) + s3_command.extend(create_metadata_args(user_metadata, system_metadata)) + aws_command(*s3_command) + finally: + if not os.getenv(ENV_KEY_SKIP_CLEANUP): + shutil.rmtree(workdir) + +#--------------------------------------------------------------------------------------------------- +# invalidate files in the CloudFront distribution edge caches +def cloudfront_invalidate(distribution_id, distribution_paths): + invalidation_resp = cloudfront.create_invalidation( + DistributionId=distribution_id, + InvalidationBatch={ + 'Paths': { + 'Quantity': len(distribution_paths), + 'Items': distribution_paths + }, + 'CallerReference': str(uuid4()), + }) + # by default, will wait up to 10 minutes + cloudfront.get_waiter('invalidation_completed').wait( + DistributionId=distribution_id, + Id=invalidation_resp['Invalidation']['Id']) + +#--------------------------------------------------------------------------------------------------- +# set metadata +def create_metadata_args(raw_user_metadata, raw_system_metadata): + if len(raw_user_metadata) == 0 and len(raw_system_metadata) == 0: + return [] + + format_system_metadata_key = lambda k: k.lower() + format_user_metadata_key = lambda k: k.lower() + + system_metadata = { format_system_metadata_key(k): v for k, v in raw_system_metadata.items() } + user_metadata = { format_user_metadata_key(k): v for k, v in raw_user_metadata.items() } + + flatten = lambda l: [item for sublist in l for item in sublist] + system_args = flatten([[f"--{k}", v] for k, v in system_metadata.items()]) + user_args = ["--metadata", json.dumps(user_metadata, separators=(',', ':'))] if len(user_metadata) > 0 else [] + + return system_args + user_args + ["--metadata-directive", "REPLACE"] + +#--------------------------------------------------------------------------------------------------- +# executes an "aws" cli command +def aws_command(*args): + aws="/opt/awscli/aws" # from AwsCliLayer + logger.info("| aws %s" % ' '.join(args)) + subprocess.check_call([aws] + list(args)) + +#--------------------------------------------------------------------------------------------------- +# sends a response to cloudformation +def cfn_send(event, context, responseStatus, responseData={}, physicalResourceId=None, noEcho=False, reason=None): + + responseUrl = event['ResponseURL'] + + responseBody = {} + responseBody['Status'] = responseStatus + responseBody['Reason'] = reason or ('See the details in CloudWatch Log Stream: ' + context.log_stream_name) + responseBody['PhysicalResourceId'] = physicalResourceId or context.log_stream_name + responseBody['StackId'] = event['StackId'] + responseBody['RequestId'] = event['RequestId'] + responseBody['LogicalResourceId'] = event['LogicalResourceId'] + responseBody['NoEcho'] = noEcho + responseBody['Data'] = responseData + + body = json.dumps(responseBody) + logger.info("| response body:\n" + body) + + headers = { + 'content-type' : '', + 'content-length' : str(len(body)) + } + + try: + request = Request(responseUrl, method='PUT', data=bytes(body.encode('utf-8')), headers=headers) + with contextlib.closing(urlopen(request)) as response: + logger.info("| status code: " + response.reason) + except Exception as e: + logger.error("| unable to send response to CloudFormation") + logger.exception(e) + + +#--------------------------------------------------------------------------------------------------- +# check if bucket is owned by a custom resource +# if it is then we don't want to delete content +def bucket_owned(bucketName, keyPrefix): + tag = CUSTOM_RESOURCE_OWNER_TAG + if keyPrefix != "": + tag = tag + ':' + keyPrefix + try: + request = s3.get_bucket_tagging( + Bucket=bucketName, + ) + return any((x["Key"].startswith(tag)) for x in request["TagSet"]) + except Exception as e: + logger.info("| error getting tags from bucket") + logger.exception(e) + return False + +# extract archive and replace markers in output files +def extract_and_replace_markers(archive, contents_dir, markers): + with ZipFile(archive, "r") as zip: + zip.extractall(contents_dir) + + # replace markers for this source + for file in zip.namelist(): + file_path = os.path.join(contents_dir, file) + if os.path.isdir(file_path): continue + replace_markers(file_path, markers) + +def replace_markers(filename, markers): + # convert the dict of string markers to binary markers + replace_tokens = dict([(k.encode('utf-8'), v.encode('utf-8')) for k, v in markers.items()]) + + outfile = filename + '.new' + with open(filename, 'rb') as fi, open(outfile, 'wb') as fo: + for line in fi: + for token in replace_tokens: + line = line.replace(token, replace_tokens[token]) + fo.write(line) + + # # delete the original file and rename the new one to the original + os.remove(filename) + os.rename(outfile, filename) diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.configuration.js.snapshot/asset.9f148cad39f325035dfec935c0a2e907135f10cb64e7c4ef78c2320fc9ad24ae/hello/world/file.txt b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.configuration.js.snapshot/asset.9f148cad39f325035dfec935c0a2e907135f10cb64e7c4ef78c2320fc9ad24ae/hello/world/file.txt new file mode 100644 index 0000000000000..9c4249ba50142 --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.configuration.js.snapshot/asset.9f148cad39f325035dfec935c0a2e907135f10cb64e7c4ef78c2320fc9ad24ae/hello/world/file.txt @@ -0,0 +1 @@ +This is the content stored in S3 \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.configuration.js.snapshot/asset.e2277687077a2abf9ae1af1cc9565e6715e2ebb62f79ec53aa75a1af9298f642.zip b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.configuration.js.snapshot/asset.e2277687077a2abf9ae1af1cc9565e6715e2ebb62f79ec53aa75a1af9298f642.zip new file mode 100644 index 0000000000000..90a8a2a6e2615 Binary files /dev/null and b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.configuration.js.snapshot/asset.e2277687077a2abf9ae1af1cc9565e6715e2ebb62f79ec53aa75a1af9298f642.zip differ diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.configuration.js.snapshot/aws-appconfig-configuration.assets.json b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.configuration.js.snapshot/aws-appconfig-configuration.assets.json new file mode 100644 index 0000000000000..e245698a2ce83 --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.configuration.js.snapshot/aws-appconfig-configuration.assets.json @@ -0,0 +1,71 @@ +{ + "version": "32.0.0", + "files": { + "e2277687077a2abf9ae1af1cc9565e6715e2ebb62f79ec53aa75a1af9298f642": { + "source": { + "path": "asset.e2277687077a2abf9ae1af1cc9565e6715e2ebb62f79ec53aa75a1af9298f642.zip", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "e2277687077a2abf9ae1af1cc9565e6715e2ebb62f79ec53aa75a1af9298f642.zip", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + }, + "9eb41a5505d37607ac419321497a4f8c21cf0ee1f9b4a6b29aa04301aea5c7fd": { + "source": { + "path": "asset.9eb41a5505d37607ac419321497a4f8c21cf0ee1f9b4a6b29aa04301aea5c7fd", + "packaging": "zip" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "9eb41a5505d37607ac419321497a4f8c21cf0ee1f9b4a6b29aa04301aea5c7fd.zip", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + }, + "9f148cad39f325035dfec935c0a2e907135f10cb64e7c4ef78c2320fc9ad24ae": { + "source": { + "path": "asset.9f148cad39f325035dfec935c0a2e907135f10cb64e7c4ef78c2320fc9ad24ae", + "packaging": "zip" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "9f148cad39f325035dfec935c0a2e907135f10cb64e7c4ef78c2320fc9ad24ae.zip", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + }, + "9b7590a121e3b39547297cbf009beb8a27e1cf9daaa760cb82c42bd530d5ef8f": { + "source": { + "path": "asset.9b7590a121e3b39547297cbf009beb8a27e1cf9daaa760cb82c42bd530d5ef8f", + "packaging": "zip" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "9b7590a121e3b39547297cbf009beb8a27e1cf9daaa760cb82c42bd530d5ef8f.zip", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + }, + "62a9b3aeb4a84560d1814dd4ac31c12a8f5047e3cde5873f6f4c351d7668af04": { + "source": { + "path": "aws-appconfig-configuration.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "62a9b3aeb4a84560d1814dd4ac31c12a8f5047e3cde5873f6f4c351d7668af04.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.configuration.js.snapshot/aws-appconfig-configuration.template.json b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.configuration.js.snapshot/aws-appconfig-configuration.template.json new file mode 100644 index 0000000000000..b1eda3c6e7502 --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.configuration.js.snapshot/aws-appconfig-configuration.template.json @@ -0,0 +1,1730 @@ +{ + "Resources": { + "MyAppConfigB4B63E75": { + "Type": "AWS::AppConfig::Application", + "Properties": { + "Name": "AppForConfigTest" + } + }, + "MyAppConfigHostedEnvF39C847E": { + "Type": "AWS::AppConfig::Environment", + "Properties": { + "ApplicationId": { + "Ref": "MyAppConfigB4B63E75" + }, + "Name": "awsappconfigconfiguration-MyAppConfig-HostedEnv-D1EED3BE" + } + }, + "MyAppConfigHostedEnvFromJson9E6E36C4": { + "Type": "AWS::AppConfig::Environment", + "Properties": { + "ApplicationId": { + "Ref": "MyAppConfigB4B63E75" + }, + "Name": "awsappconfigconfiguration-MyAppConfig-HostedEnvFromJson-140D2DDD" + } + }, + "MyAppConfigParameterEnvD769FB19": { + "Type": "AWS::AppConfig::Environment", + "Properties": { + "ApplicationId": { + "Ref": "MyAppConfigB4B63E75" + }, + "Name": "awsappconfigconfiguration-MyAppConfig-ParameterEnv-803E803A" + } + }, + "MyAppConfigDocumentEnv34B10223": { + "Type": "AWS::AppConfig::Environment", + "Properties": { + "ApplicationId": { + "Ref": "MyAppConfigB4B63E75" + }, + "Name": "awsappconfigconfiguration-MyAppConfig-DocumentEnv-5B0DCEC5" + } + }, + "MyAppConfigBucketEnvCBDFEF45": { + "Type": "AWS::AppConfig::Environment", + "Properties": { + "ApplicationId": { + "Ref": "MyAppConfigB4B63E75" + }, + "Name": "awsappconfigconfiguration-MyAppConfig-BucketEnv-1A81B6F5" + } + }, + "MyAppConfigSecretEnvE1102B63": { + "Type": "AWS::AppConfig::Environment", + "Properties": { + "ApplicationId": { + "Ref": "MyAppConfigB4B63E75" + }, + "Name": "awsappconfigconfiguration-MyAppConfig-SecretEnv-371FF6BB" + } + }, + "MyAppConfigSecretEnvWithKey9C93DA23": { + "Type": "AWS::AppConfig::Environment", + "Properties": { + "ApplicationId": { + "Ref": "MyAppConfigB4B63E75" + }, + "Name": "awsappconfigconfiguration-MyAppConfig-SecretEnvWithKey-8D4A2FFE" + } + }, + "MyDeployStrategy062CAEA2": { + "Type": "AWS::AppConfig::DeploymentStrategy", + "Properties": { + "DeploymentDurationInMinutes": 0, + "GrowthFactor": 100, + "GrowthType": "LINEAR", + "Name": "awsappconfigconfiguration-MyDeployStrategy-91730AE8", + "ReplicateTo": "NONE" + } + }, + "MyHostedConfigConfigurationProfile2E1A2BBC": { + "Type": "AWS::AppConfig::ConfigurationProfile", + "Properties": { + "ApplicationId": { + "Ref": "MyAppConfigB4B63E75" + }, + "LocationUri": "hosted", + "Name": "awsappconfigconfiguration-MyHostedConfig-4CF350AE", + "Validators": [ + { + "Content": "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"type\": \"string\"\n}", + "Type": "JSON_SCHEMA" + } + ] + } + }, + "MyHostedConfig51D3877D": { + "Type": "AWS::AppConfig::HostedConfigurationVersion", + "Properties": { + "ApplicationId": { + "Ref": "MyAppConfigB4B63E75" + }, + "ConfigurationProfileId": { + "Ref": "MyHostedConfigConfigurationProfile2E1A2BBC" + }, + "Content": "This is my configuration content.", + "ContentType": "application/json" + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "MyHostedConfigDeploymentE4742E8E64DF4": { + "Type": "AWS::AppConfig::Deployment", + "Properties": { + "ApplicationId": { + "Ref": "MyAppConfigB4B63E75" + }, + "ConfigurationProfileId": { + "Ref": "MyHostedConfigConfigurationProfile2E1A2BBC" + }, + "ConfigurationVersion": { + "Ref": "MyHostedConfig51D3877D" + }, + "DeploymentStrategyId": { + "Ref": "MyDeployStrategy062CAEA2" + }, + "EnvironmentId": { + "Ref": "MyAppConfigHostedEnvF39C847E" + } + } + }, + "MyHostedConfigFromJsonConfigurationProfile863E1E42": { + "Type": "AWS::AppConfig::ConfigurationProfile", + "Properties": { + "ApplicationId": { + "Ref": "MyAppConfigB4B63E75" + }, + "LocationUri": "hosted", + "Name": "awsappconfigconfiguration-MyHostedConfigFromJson-3E786E81" + } + }, + "MyHostedConfigFromJsonD8CF9BE4": { + "Type": "AWS::AppConfig::HostedConfigurationVersion", + "Properties": { + "ApplicationId": { + "Ref": "MyAppConfigB4B63E75" + }, + "ConfigurationProfileId": { + "Ref": "MyHostedConfigFromJsonConfigurationProfile863E1E42" + }, + "Content": "This is the configuration content", + "ContentType": "application/json" + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "MyHostedConfigFromJsonDeploymentCD82E3049E374": { + "Type": "AWS::AppConfig::Deployment", + "Properties": { + "ApplicationId": { + "Ref": "MyAppConfigB4B63E75" + }, + "ConfigurationProfileId": { + "Ref": "MyHostedConfigFromJsonConfigurationProfile863E1E42" + }, + "ConfigurationVersion": { + "Ref": "MyHostedConfigFromJsonD8CF9BE4" + }, + "DeploymentStrategyId": { + "Ref": "MyDeployStrategy062CAEA2" + }, + "EnvironmentId": { + "Ref": "MyAppConfigHostedEnvFromJson9E6E36C4" + } + } + }, + "MyValidatorFunctionServiceRole5CD02390": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "MyValidatorFunctionA679CB3C": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ZipFile": "\ndef handler(event, context):\n print('This is my dummy validator')\n" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "MyValidatorFunctionServiceRole5CD02390", + "Arn" + ] + }, + "Runtime": "python3.8" + }, + "DependsOn": [ + "MyValidatorFunctionServiceRole5CD02390" + ] + }, + "MyValidatorFunctionAppConfigPermission77CD11AC": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Fn::GetAtt": [ + "MyValidatorFunctionA679CB3C", + "Arn" + ] + }, + "Principal": "appconfig.amazonaws.com" + } + }, + "MyParameter18BA547D": { + "Type": "AWS::SSM::Parameter", + "Properties": { + "Type": "String", + "Value": "This is the content stored in ssm parameter" + } + }, + "MyConfigFromParameterRole4E40C8BC": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "appconfig.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": "ssm:GetParameter", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":ssm:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":parameter/", + { + "Ref": "MyParameter18BA547D" + } + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "AllowAppConfigReadFromSourcePolicy" + } + ] + } + }, + "MyConfigFromParameterDC204468": { + "Type": "AWS::AppConfig::ConfigurationProfile", + "Properties": { + "ApplicationId": { + "Ref": "MyAppConfigB4B63E75" + }, + "Description": "This is a configuration profile used for integ testing", + "LocationUri": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":ssm:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":parameter/", + { + "Ref": "MyParameter18BA547D" + } + ] + ] + }, + "Name": "TestConfigProfileStoredAsParamater", + "RetrievalRoleArn": { + "Fn::GetAtt": [ + "MyConfigFromParameterRole4E40C8BC", + "Arn" + ] + }, + "Validators": [ + { + "Content": { + "Fn::GetAtt": [ + "MyValidatorFunctionA679CB3C", + "Arn" + ] + }, + "Type": "LAMBDA" + } + ] + } + }, + "MyConfigFromParameterDeployment0C84B4B3BAD6B": { + "Type": "AWS::AppConfig::Deployment", + "Properties": { + "ApplicationId": { + "Ref": "MyAppConfigB4B63E75" + }, + "ConfigurationProfileId": { + "Ref": "MyConfigFromParameterDC204468" + }, + "ConfigurationVersion": "1", + "DeploymentStrategyId": { + "Ref": "MyDeployStrategy062CAEA2" + }, + "Description": "This is a configuration profile used for integ testing", + "EnvironmentId": { + "Ref": "MyAppConfigParameterEnvD769FB19" + } + } + }, + "MyDocument": { + "Type": "AWS::SSM::Document", + "Properties": { + "Content": { + "schemaVersion": "2.2", + "description": "Sample SSM Document", + "mainSteps": [ + { + "action": "aws:runShellScript", + "name": "step1", + "inputs": { + "runCommand": [ + "echo \"Hello, World!\"" + ] + } + } + ] + }, + "DocumentType": "Command", + "Name": "TestDocument" + } + }, + "MyConfigFromDocumentRole29FB4D3D": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "appconfig.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": "ssm:GetDocument", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":ssm:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":document/", + { + "Ref": "MyDocument" + } + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "AllowAppConfigReadFromSourcePolicy" + } + ] + } + }, + "MyConfigFromDocument79E66ABA": { + "Type": "AWS::AppConfig::ConfigurationProfile", + "Properties": { + "ApplicationId": { + "Ref": "MyAppConfigB4B63E75" + }, + "LocationUri": { + "Fn::Join": [ + "", + [ + "ssm-document://", + { + "Ref": "MyDocument" + } + ] + ] + }, + "Name": "awsappconfigconfiguration-MyConfigFromDocument-A15AC401", + "RetrievalRoleArn": { + "Fn::GetAtt": [ + "MyConfigFromDocumentRole29FB4D3D", + "Arn" + ] + } + } + }, + "MyConfigFromDocumentDeployment1520EE7C916D3": { + "Type": "AWS::AppConfig::Deployment", + "Properties": { + "ApplicationId": { + "Ref": "MyAppConfigB4B63E75" + }, + "ConfigurationProfileId": { + "Ref": "MyConfigFromDocument79E66ABA" + }, + "ConfigurationVersion": "1", + "DeploymentStrategyId": { + "Ref": "MyDeployStrategy062CAEA2" + }, + "EnvironmentId": { + "Ref": "MyAppConfigDocumentEnv34B10223" + } + } + }, + "MyBucketF68F3FF0": { + "Type": "AWS::S3::Bucket", + "Properties": { + "Tags": [ + { + "Key": "aws-cdk:cr-owned:2255b7ad", + "Value": "true" + } + ], + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "DeployConfigInBucketAwsCliLayerFC57D055": { + "Type": "AWS::Lambda::LayerVersion", + "Properties": { + "Content": { + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "S3Key": "e2277687077a2abf9ae1af1cc9565e6715e2ebb62f79ec53aa75a1af9298f642.zip" + }, + "Description": "/opt/awscli/aws" + } + }, + "DeployConfigInBucketCustomResource91997C5B": { + "Type": "Custom::CDKBucketDeployment", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C81C01536", + "Arn" + ] + }, + "SourceBucketNames": [ + { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + } + ], + "SourceObjectKeys": [ + "9f148cad39f325035dfec935c0a2e907135f10cb64e7c4ef78c2320fc9ad24ae.zip", + "9b7590a121e3b39547297cbf009beb8a27e1cf9daaa760cb82c42bd530d5ef8f.zip" + ], + "SourceMarkers": [ + {}, + {} + ], + "DestinationBucketName": { + "Ref": "MyBucketF68F3FF0" + }, + "RetainOnDelete": false, + "Prune": true + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRole89A01265": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRoleDefaultPolicy88902FDF": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "s3:GetBucket*", + "s3:GetObject*", + "s3:List*" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":s3:::", + { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "/*" + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":s3:::", + { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + } + ] + ] + } + ] + }, + { + "Action": [ + "s3:Abort*", + "s3:DeleteObject*", + "s3:GetBucket*", + "s3:GetObject*", + "s3:List*", + "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "MyBucketF68F3FF0", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "MyBucketF68F3FF0", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRoleDefaultPolicy88902FDF", + "Roles": [ + { + "Ref": "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRole89A01265" + } + ] + } + }, + "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C81C01536": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "S3Key": "9eb41a5505d37607ac419321497a4f8c21cf0ee1f9b4a6b29aa04301aea5c7fd.zip" + }, + "Environment": { + "Variables": { + "AWS_CA_BUNDLE": "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem" + } + }, + "Handler": "index.handler", + "Layers": [ + { + "Ref": "DeployConfigInBucketAwsCliLayerFC57D055" + } + ], + "Role": { + "Fn::GetAtt": [ + "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRole89A01265", + "Arn" + ] + }, + "Runtime": "python3.9", + "Timeout": 900 + }, + "DependsOn": [ + "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRoleDefaultPolicy88902FDF", + "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRole89A01265" + ] + }, + "MyConfigFromBucketDeploymentStrategy59D68C4F": { + "Type": "AWS::AppConfig::DeploymentStrategy", + "Properties": { + "DeploymentDurationInMinutes": 20, + "FinalBakeTimeInMinutes": 10, + "GrowthFactor": 10, + "GrowthType": "EXPONENTIAL", + "Name": "awsappconfigconfiguration-MyomBucket-DeploymentStrategy-8366DF86", + "ReplicateTo": "NONE" + } + }, + "MyConfigFromBucketRole5749669F": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "appconfig.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "s3:GetObject", + "s3:GetObjectMetadata", + "s3:GetObjectVersion" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":s3:::", + { + "Ref": "MyBucketF68F3FF0" + }, + "/hello/world/file.txt" + ] + ] + } + }, + { + "Action": [ + "s3:GetBucketLocation", + "s3:GetBucketVersioning", + "s3:ListBucket", + "s3:ListBucketVersions" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":s3:::", + { + "Ref": "MyBucketF68F3FF0" + } + ] + ] + } + }, + { + "Action": "s3:ListAllMyBuckets", + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "AllowAppConfigReadFromSourcePolicy" + } + ] + } + }, + "MyConfigFromBucket064B5420": { + "Type": "AWS::AppConfig::ConfigurationProfile", + "Properties": { + "ApplicationId": { + "Ref": "MyAppConfigB4B63E75" + }, + "Description": { + "Fn::Join": [ + "", + [ + "Sourced from ", + { + "Fn::Select": [ + 0, + { + "Fn::GetAtt": [ + "DeployConfigInBucketCustomResource91997C5B", + "SourceObjectKeys" + ] + } + ] + } + ] + ] + }, + "LocationUri": { + "Fn::Join": [ + "", + [ + "s3://", + { + "Ref": "MyBucketF68F3FF0" + }, + "/hello/world/file.txt" + ] + ] + }, + "Name": "awsappconfigconfiguration-MyConfigFromBucket-79972829", + "RetrievalRoleArn": { + "Fn::GetAtt": [ + "MyConfigFromBucketRole5749669F", + "Arn" + ] + } + } + }, + "MySecret8FE80B51": { + "Type": "AWS::SecretsManager::Secret", + "Properties": { + "SecretString": "This is the content stored in secrets manager" + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "MyConfigFromSecretDeploymentStrategy7E7398AA": { + "Type": "AWS::AppConfig::DeploymentStrategy", + "Properties": { + "DeploymentDurationInMinutes": 20, + "FinalBakeTimeInMinutes": 10, + "GrowthFactor": 10, + "GrowthType": "EXPONENTIAL", + "Name": "awsappconfigconfiguration-MyomSecret-DeploymentStrategy-2F6D94AB", + "ReplicateTo": "NONE" + } + }, + "MyConfigFromSecretRole77E11CE7": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "appconfig.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": "secretsmanager:GetSecretValue", + "Effect": "Allow", + "Resource": { + "Ref": "MySecret8FE80B51" + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "AllowAppConfigReadFromSourcePolicy" + } + ] + } + }, + "MyConfigFromSecret42EED9DC": { + "Type": "AWS::AppConfig::ConfigurationProfile", + "Properties": { + "ApplicationId": { + "Ref": "MyAppConfigB4B63E75" + }, + "LocationUri": { + "Ref": "MySecret8FE80B51" + }, + "Name": "awsappconfigconfiguration-MyConfigFromSecret-3BD4C031", + "RetrievalRoleArn": { + "Fn::GetAtt": [ + "MyConfigFromSecretRole77E11CE7", + "Arn" + ] + } + } + }, + "MyKey6AB29FA6": { + "Type": "AWS::KMS::Key", + "Properties": { + "KeyPolicy": { + "Statement": [ + { + "Action": "kms:*", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + }, + { + "Action": [ + "kms:CreateGrant", + "kms:Decrypt", + "kms:DescribeKey", + "kms:Encrypt", + "kms:GenerateDataKey*", + "kms:ReEncrypt*" + ], + "Condition": { + "StringEquals": { + "kms:ViaService": { + "Fn::Join": [ + "", + [ + "secretsmanager.", + { + "Ref": "AWS::Region" + }, + ".amazonaws.com" + ] + ] + } + } + }, + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + } + ], + "Version": "2012-10-17" + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "MySecretWithKey1F7B590B": { + "Type": "AWS::SecretsManager::Secret", + "Properties": { + "KmsKeyId": { + "Fn::GetAtt": [ + "MyKey6AB29FA6", + "Arn" + ] + }, + "SecretString": "This is the content stored in secrets manager" + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "MyConfigFromSecretWithKeyDeploymentStrategy9FAC9F62": { + "Type": "AWS::AppConfig::DeploymentStrategy", + "Properties": { + "DeploymentDurationInMinutes": 20, + "FinalBakeTimeInMinutes": 10, + "GrowthFactor": 10, + "GrowthType": "EXPONENTIAL", + "Name": "awsappconfigconfiguration-MytWithKey-DeploymentStrategy-70C32371", + "ReplicateTo": "NONE" + } + }, + "MyConfigFromSecretWithKeyRole3C7B494A": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "appconfig.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": "secretsmanager:GetSecretValue", + "Effect": "Allow", + "Resource": { + "Ref": "MySecretWithKey1F7B590B" + } + }, + { + "Action": "kms:Decrypt", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "MyKey6AB29FA6", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "AllowAppConfigReadFromSourcePolicy" + } + ] + } + }, + "MyConfigFromSecretWithKey6F08D343": { + "Type": "AWS::AppConfig::ConfigurationProfile", + "Properties": { + "ApplicationId": { + "Ref": "MyAppConfigB4B63E75" + }, + "LocationUri": { + "Ref": "MySecretWithKey1F7B590B" + }, + "Name": "awsappconfigconfiguration-MyConfigFromSecretWithKey-516D9EB2", + "RetrievalRoleArn": { + "Fn::GetAtt": [ + "MyConfigFromSecretWithKeyRole3C7B494A", + "Arn" + ] + } + } + }, + "MyPipelineArtifactsBucketEncryptionKey8BF0A7F3": { + "Type": "AWS::KMS::Key", + "Properties": { + "KeyPolicy": { + "Statement": [ + { + "Action": "kms:*", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + } + ], + "Version": "2012-10-17" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "MyPipelineArtifactsBucketEncryptionKeyAlias9D4F8C59": { + "Type": "AWS::KMS::Alias", + "Properties": { + "AliasName": "alias/codepipeline-aws-appconfig-configuration-mypipeline-893f29d4", + "TargetKeyId": { + "Fn::GetAtt": [ + "MyPipelineArtifactsBucketEncryptionKey8BF0A7F3", + "Arn" + ] + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "MyPipelineArtifactsBucket727923DD": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "KMSMasterKeyID": { + "Fn::GetAtt": [ + "MyPipelineArtifactsBucketEncryptionKey8BF0A7F3", + "Arn" + ] + }, + "SSEAlgorithm": "aws:kms" + } + } + ] + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "MyPipelineArtifactsBucketPolicyDFDA675B": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "MyPipelineArtifactsBucket727923DD" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "MyPipelineArtifactsBucket727923DD", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "MyPipelineArtifactsBucket727923DD", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + } + } + }, + "MyPipelineRoleC0D47CA4": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "codepipeline.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "MyPipelineRoleDefaultPolicy34F09EFA": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "s3:Abort*", + "s3:DeleteObject*", + "s3:GetBucket*", + "s3:GetObject*", + "s3:List*", + "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "MyPipelineArtifactsBucket727923DD", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "MyPipelineArtifactsBucket727923DD", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "kms:Decrypt", + "kms:DescribeKey", + "kms:Encrypt", + "kms:GenerateDataKey*", + "kms:ReEncrypt*" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "MyPipelineArtifactsBucketEncryptionKey8BF0A7F3", + "Arn" + ] + } + }, + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "MyPipelinebetaSourceCodePipelineActionRoleA59DCC4C", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "MyPipelineprodDeployCodePipelineActionRoleFAD07544", + "Arn" + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "MyPipelineRoleDefaultPolicy34F09EFA", + "Roles": [ + { + "Ref": "MyPipelineRoleC0D47CA4" + } + ] + } + }, + "MyPipelineAED38ECF": { + "Type": "AWS::CodePipeline::Pipeline", + "Properties": { + "ArtifactStore": { + "EncryptionKey": { + "Id": { + "Fn::GetAtt": [ + "MyPipelineArtifactsBucketEncryptionKey8BF0A7F3", + "Arn" + ] + }, + "Type": "KMS" + }, + "Location": { + "Ref": "MyPipelineArtifactsBucket727923DD" + }, + "Type": "S3" + }, + "RoleArn": { + "Fn::GetAtt": [ + "MyPipelineRoleC0D47CA4", + "Arn" + ] + }, + "Stages": [ + { + "Actions": [ + { + "ActionTypeId": { + "Category": "Source", + "Owner": "AWS", + "Provider": "S3", + "Version": "1" + }, + "Configuration": { + "S3Bucket": { + "Ref": "MyBucketF68F3FF0" + }, + "S3ObjectKey": "hello/world/codepipeline.txt" + }, + "Name": "Source", + "OutputArtifacts": [ + { + "Name": "SourceOutput" + } + ], + "RoleArn": { + "Fn::GetAtt": [ + "MyPipelinebetaSourceCodePipelineActionRoleA59DCC4C", + "Arn" + ] + }, + "RunOrder": 1 + } + ], + "Name": "beta" + }, + { + "Actions": [ + { + "ActionTypeId": { + "Category": "Deploy", + "Owner": "AWS", + "Provider": "S3", + "Version": "1" + }, + "Configuration": { + "BucketName": { + "Ref": "MyBucketF68F3FF0" + }, + "Extract": "true" + }, + "InputArtifacts": [ + { + "Name": "SourceOutput" + } + ], + "Name": "Deploy", + "RoleArn": { + "Fn::GetAtt": [ + "MyPipelineprodDeployCodePipelineActionRoleFAD07544", + "Arn" + ] + }, + "RunOrder": 1 + } + ], + "Name": "prod" + } + ] + }, + "DependsOn": [ + "MyPipelineRoleDefaultPolicy34F09EFA", + "MyPipelineRoleC0D47CA4" + ] + }, + "MyPipelinebetaSourceCodePipelineActionRoleA59DCC4C": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + } + } + ], + "Version": "2012-10-17" + } + } + }, + "MyPipelinebetaSourceCodePipelineActionRoleDefaultPolicy4F6DF82E": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "s3:GetBucket*", + "s3:GetObject*", + "s3:List*" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "MyBucketF68F3FF0", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "MyBucketF68F3FF0", + "Arn" + ] + }, + "/hello/world/codepipeline.txt" + ] + ] + } + ] + }, + { + "Action": [ + "s3:Abort*", + "s3:DeleteObject*", + "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "MyPipelineArtifactsBucket727923DD", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "MyPipelineArtifactsBucket727923DD", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "kms:Decrypt", + "kms:Encrypt", + "kms:GenerateDataKey*", + "kms:ReEncrypt*" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "MyPipelineArtifactsBucketEncryptionKey8BF0A7F3", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "MyPipelinebetaSourceCodePipelineActionRoleDefaultPolicy4F6DF82E", + "Roles": [ + { + "Ref": "MyPipelinebetaSourceCodePipelineActionRoleA59DCC4C" + } + ] + } + }, + "MyPipelineprodDeployCodePipelineActionRoleFAD07544": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + } + } + ], + "Version": "2012-10-17" + } + } + }, + "MyPipelineprodDeployCodePipelineActionRoleDefaultPolicyF1913519": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "s3:Abort*", + "s3:DeleteObject*", + "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "MyBucketF68F3FF0", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "MyBucketF68F3FF0", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "s3:GetBucket*", + "s3:GetObject*", + "s3:List*" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "MyPipelineArtifactsBucket727923DD", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "MyPipelineArtifactsBucket727923DD", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "kms:Decrypt", + "kms:DescribeKey" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "MyPipelineArtifactsBucketEncryptionKey8BF0A7F3", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "MyPipelineprodDeployCodePipelineActionRoleDefaultPolicyF1913519", + "Roles": [ + { + "Ref": "MyPipelineprodDeployCodePipelineActionRoleFAD07544" + } + ] + } + }, + "MyConfigFromPipelineDeploymentStrategyF07EE763": { + "Type": "AWS::AppConfig::DeploymentStrategy", + "Properties": { + "DeploymentDurationInMinutes": 20, + "FinalBakeTimeInMinutes": 10, + "GrowthFactor": 10, + "GrowthType": "EXPONENTIAL", + "Name": "awsappconfigconfiguration-MyPipeline-DeploymentStrategy-5F76004F", + "ReplicateTo": "NONE" + } + }, + "MyConfigFromPipelineEA8471A4": { + "Type": "AWS::AppConfig::ConfigurationProfile", + "Properties": { + "ApplicationId": { + "Ref": "MyAppConfigB4B63E75" + }, + "LocationUri": { + "Fn::Join": [ + "", + [ + "codepipeline://", + { + "Ref": "MyPipelineAED38ECF" + } + ] + ] + }, + "Name": "awsappconfigconfiguration-MyConfigFromPipeline-FD2B6B65" + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.configuration.js.snapshot/cdk.out b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.configuration.js.snapshot/cdk.out new file mode 100644 index 0000000000000..f0b901e7c06e5 --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.configuration.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"32.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.configuration.js.snapshot/integ.json b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.configuration.js.snapshot/integ.json new file mode 100644 index 0000000000000..7d30ed8fc2aa4 --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.configuration.js.snapshot/integ.json @@ -0,0 +1,19 @@ +{ + "version": "32.0.0", + "testCases": { + "appconfig-configuration/DefaultTest": { + "stacks": [ + "aws-appconfig-configuration" + ], + "cdkCommandOptions": { + "destroy": { + "args": { + "force": true + } + } + }, + "assertionStack": "appconfig-configuration/DefaultTest/DeployAssert", + "assertionStackName": "appconfigconfigurationDefaultTestDeployAssert6752CD38" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.configuration.js.snapshot/manifest.json b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.configuration.js.snapshot/manifest.json new file mode 100644 index 0000000000000..745926ca84296 --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.configuration.js.snapshot/manifest.json @@ -0,0 +1,447 @@ +{ + "version": "32.0.0", + "artifacts": { + "aws-appconfig-configuration.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "aws-appconfig-configuration.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "aws-appconfig-configuration": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "aws-appconfig-configuration.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/62a9b3aeb4a84560d1814dd4ac31c12a8f5047e3cde5873f6f4c351d7668af04.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "aws-appconfig-configuration.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "aws-appconfig-configuration.assets" + ], + "metadata": { + "/aws-appconfig-configuration/MyAppConfig/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyAppConfigB4B63E75" + } + ], + "/aws-appconfig-configuration/MyAppConfig/HostedEnv/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyAppConfigHostedEnvF39C847E" + } + ], + "/aws-appconfig-configuration/MyAppConfig/HostedEnvFromJson/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyAppConfigHostedEnvFromJson9E6E36C4" + } + ], + "/aws-appconfig-configuration/MyAppConfig/ParameterEnv/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyAppConfigParameterEnvD769FB19" + } + ], + "/aws-appconfig-configuration/MyAppConfig/DocumentEnv/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyAppConfigDocumentEnv34B10223" + } + ], + "/aws-appconfig-configuration/MyAppConfig/BucketEnv/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyAppConfigBucketEnvCBDFEF45" + } + ], + "/aws-appconfig-configuration/MyAppConfig/SecretEnv/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyAppConfigSecretEnvE1102B63" + } + ], + "/aws-appconfig-configuration/MyAppConfig/SecretEnvWithKey/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyAppConfigSecretEnvWithKey9C93DA23" + } + ], + "/aws-appconfig-configuration/MyDeployStrategy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyDeployStrategy062CAEA2" + } + ], + "/aws-appconfig-configuration/MyHostedConfig/ConfigurationProfile": [ + { + "type": "aws:cdk:logicalId", + "data": "MyHostedConfigConfigurationProfile2E1A2BBC" + } + ], + "/aws-appconfig-configuration/MyHostedConfig/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyHostedConfig51D3877D" + } + ], + "/aws-appconfig-configuration/MyHostedConfig/DeploymentE4742": [ + { + "type": "aws:cdk:logicalId", + "data": "MyHostedConfigDeploymentE4742E8E64DF4" + } + ], + "/aws-appconfig-configuration/MyHostedConfigFromJson/ConfigurationProfile": [ + { + "type": "aws:cdk:logicalId", + "data": "MyHostedConfigFromJsonConfigurationProfile863E1E42" + } + ], + "/aws-appconfig-configuration/MyHostedConfigFromJson/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyHostedConfigFromJsonD8CF9BE4" + } + ], + "/aws-appconfig-configuration/MyHostedConfigFromJson/DeploymentCD82E": [ + { + "type": "aws:cdk:logicalId", + "data": "MyHostedConfigFromJsonDeploymentCD82E3049E374" + } + ], + "/aws-appconfig-configuration/MyValidatorFunction/ServiceRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyValidatorFunctionServiceRole5CD02390" + } + ], + "/aws-appconfig-configuration/MyValidatorFunction/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyValidatorFunctionA679CB3C" + } + ], + "/aws-appconfig-configuration/MyValidatorFunction/AppConfigPermission": [ + { + "type": "aws:cdk:logicalId", + "data": "MyValidatorFunctionAppConfigPermission77CD11AC" + } + ], + "/aws-appconfig-configuration/MyParameter/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyParameter18BA547D" + } + ], + "/aws-appconfig-configuration/MyConfigFromParameter/Role/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyConfigFromParameterRole4E40C8BC" + } + ], + "/aws-appconfig-configuration/MyConfigFromParameter/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyConfigFromParameterDC204468" + } + ], + "/aws-appconfig-configuration/MyConfigFromParameter/Deployment0C84B": [ + { + "type": "aws:cdk:logicalId", + "data": "MyConfigFromParameterDeployment0C84B4B3BAD6B" + } + ], + "/aws-appconfig-configuration/MyDocument": [ + { + "type": "aws:cdk:logicalId", + "data": "MyDocument" + } + ], + "/aws-appconfig-configuration/MyConfigFromDocument/Role/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyConfigFromDocumentRole29FB4D3D" + } + ], + "/aws-appconfig-configuration/MyConfigFromDocument/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyConfigFromDocument79E66ABA" + } + ], + "/aws-appconfig-configuration/MyConfigFromDocument/Deployment1520E": [ + { + "type": "aws:cdk:logicalId", + "data": "MyConfigFromDocumentDeployment1520EE7C916D3" + } + ], + "/aws-appconfig-configuration/MyBucket/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyBucketF68F3FF0" + } + ], + "/aws-appconfig-configuration/DeployConfigInBucket/AwsCliLayer/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "DeployConfigInBucketAwsCliLayerFC57D055" + } + ], + "/aws-appconfig-configuration/DeployConfigInBucket/CustomResource/Default": [ + { + "type": "aws:cdk:logicalId", + "data": "DeployConfigInBucketCustomResource91997C5B" + } + ], + "/aws-appconfig-configuration/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/ServiceRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRole89A01265" + } + ], + "/aws-appconfig-configuration/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/ServiceRole/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRoleDefaultPolicy88902FDF" + } + ], + "/aws-appconfig-configuration/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C81C01536" + } + ], + "/aws-appconfig-configuration/MyConfigFromBucket/DeploymentStrategy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyConfigFromBucketDeploymentStrategy59D68C4F" + } + ], + "/aws-appconfig-configuration/MyConfigFromBucket/Role/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyConfigFromBucketRole5749669F" + } + ], + "/aws-appconfig-configuration/MyConfigFromBucket/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyConfigFromBucket064B5420" + } + ], + "/aws-appconfig-configuration/MySecret/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MySecret8FE80B51" + } + ], + "/aws-appconfig-configuration/MyConfigFromSecret/DeploymentStrategy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyConfigFromSecretDeploymentStrategy7E7398AA" + } + ], + "/aws-appconfig-configuration/MyConfigFromSecret/Role/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyConfigFromSecretRole77E11CE7" + } + ], + "/aws-appconfig-configuration/MyConfigFromSecret/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyConfigFromSecret42EED9DC" + } + ], + "/aws-appconfig-configuration/MyKey/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyKey6AB29FA6" + } + ], + "/aws-appconfig-configuration/MySecretWithKey/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MySecretWithKey1F7B590B" + } + ], + "/aws-appconfig-configuration/MyConfigFromSecretWithKey/DeploymentStrategy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyConfigFromSecretWithKeyDeploymentStrategy9FAC9F62" + } + ], + "/aws-appconfig-configuration/MyConfigFromSecretWithKey/Role/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyConfigFromSecretWithKeyRole3C7B494A" + } + ], + "/aws-appconfig-configuration/MyConfigFromSecretWithKey/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyConfigFromSecretWithKey6F08D343" + } + ], + "/aws-appconfig-configuration/MyPipeline/ArtifactsBucketEncryptionKey/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyPipelineArtifactsBucketEncryptionKey8BF0A7F3" + } + ], + "/aws-appconfig-configuration/MyPipeline/ArtifactsBucketEncryptionKeyAlias/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyPipelineArtifactsBucketEncryptionKeyAlias9D4F8C59" + } + ], + "/aws-appconfig-configuration/MyPipeline/ArtifactsBucket/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyPipelineArtifactsBucket727923DD" + } + ], + "/aws-appconfig-configuration/MyPipeline/ArtifactsBucket/Policy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyPipelineArtifactsBucketPolicyDFDA675B" + } + ], + "/aws-appconfig-configuration/MyPipeline/Role/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyPipelineRoleC0D47CA4" + } + ], + "/aws-appconfig-configuration/MyPipeline/Role/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyPipelineRoleDefaultPolicy34F09EFA" + } + ], + "/aws-appconfig-configuration/MyPipeline/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyPipelineAED38ECF" + } + ], + "/aws-appconfig-configuration/MyPipeline/beta/Source/CodePipelineActionRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyPipelinebetaSourceCodePipelineActionRoleA59DCC4C" + } + ], + "/aws-appconfig-configuration/MyPipeline/beta/Source/CodePipelineActionRole/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyPipelinebetaSourceCodePipelineActionRoleDefaultPolicy4F6DF82E" + } + ], + "/aws-appconfig-configuration/MyPipeline/prod/Deploy/CodePipelineActionRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyPipelineprodDeployCodePipelineActionRoleFAD07544" + } + ], + "/aws-appconfig-configuration/MyPipeline/prod/Deploy/CodePipelineActionRole/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyPipelineprodDeployCodePipelineActionRoleDefaultPolicyF1913519" + } + ], + "/aws-appconfig-configuration/MyConfigFromPipeline/DeploymentStrategy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyConfigFromPipelineDeploymentStrategyF07EE763" + } + ], + "/aws-appconfig-configuration/MyConfigFromPipeline/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyConfigFromPipelineEA8471A4" + } + ], + "/aws-appconfig-configuration/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/aws-appconfig-configuration/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "aws-appconfig-configuration" + }, + "appconfigconfigurationDefaultTestDeployAssert6752CD38.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "appconfigconfigurationDefaultTestDeployAssert6752CD38.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "appconfigconfigurationDefaultTestDeployAssert6752CD38": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "appconfigconfigurationDefaultTestDeployAssert6752CD38.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "appconfigconfigurationDefaultTestDeployAssert6752CD38.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "appconfigconfigurationDefaultTestDeployAssert6752CD38.assets" + ], + "metadata": { + "/appconfig-configuration/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/appconfig-configuration/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "appconfig-configuration/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.configuration.js.snapshot/tree.json b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.configuration.js.snapshot/tree.json new file mode 100644 index 0000000000000..0f0a957f37369 --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.configuration.js.snapshot/tree.json @@ -0,0 +1,2918 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "aws-appconfig-configuration": { + "id": "aws-appconfig-configuration", + "path": "aws-appconfig-configuration", + "children": { + "MyAppConfig": { + "id": "MyAppConfig", + "path": "aws-appconfig-configuration/MyAppConfig", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-appconfig-configuration/MyAppConfig/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::Application", + "aws:cdk:cloudformation:props": { + "name": "AppForConfigTest" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appconfig.CfnApplication", + "version": "0.0.0" + } + }, + "HostedEnv": { + "id": "HostedEnv", + "path": "aws-appconfig-configuration/MyAppConfig/HostedEnv", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-appconfig-configuration/MyAppConfig/HostedEnv/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::Environment", + "aws:cdk:cloudformation:props": { + "applicationId": { + "Ref": "MyAppConfigB4B63E75" + }, + "name": "awsappconfigconfiguration-MyAppConfig-HostedEnv-D1EED3BE" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appconfig.CfnEnvironment", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "HostedEnvFromJson": { + "id": "HostedEnvFromJson", + "path": "aws-appconfig-configuration/MyAppConfig/HostedEnvFromJson", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-appconfig-configuration/MyAppConfig/HostedEnvFromJson/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::Environment", + "aws:cdk:cloudformation:props": { + "applicationId": { + "Ref": "MyAppConfigB4B63E75" + }, + "name": "awsappconfigconfiguration-MyAppConfig-HostedEnvFromJson-140D2DDD" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appconfig.CfnEnvironment", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "ParameterEnv": { + "id": "ParameterEnv", + "path": "aws-appconfig-configuration/MyAppConfig/ParameterEnv", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-appconfig-configuration/MyAppConfig/ParameterEnv/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::Environment", + "aws:cdk:cloudformation:props": { + "applicationId": { + "Ref": "MyAppConfigB4B63E75" + }, + "name": "awsappconfigconfiguration-MyAppConfig-ParameterEnv-803E803A" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appconfig.CfnEnvironment", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "DocumentEnv": { + "id": "DocumentEnv", + "path": "aws-appconfig-configuration/MyAppConfig/DocumentEnv", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-appconfig-configuration/MyAppConfig/DocumentEnv/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::Environment", + "aws:cdk:cloudformation:props": { + "applicationId": { + "Ref": "MyAppConfigB4B63E75" + }, + "name": "awsappconfigconfiguration-MyAppConfig-DocumentEnv-5B0DCEC5" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appconfig.CfnEnvironment", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "BucketEnv": { + "id": "BucketEnv", + "path": "aws-appconfig-configuration/MyAppConfig/BucketEnv", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-appconfig-configuration/MyAppConfig/BucketEnv/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::Environment", + "aws:cdk:cloudformation:props": { + "applicationId": { + "Ref": "MyAppConfigB4B63E75" + }, + "name": "awsappconfigconfiguration-MyAppConfig-BucketEnv-1A81B6F5" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appconfig.CfnEnvironment", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "SecretEnv": { + "id": "SecretEnv", + "path": "aws-appconfig-configuration/MyAppConfig/SecretEnv", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-appconfig-configuration/MyAppConfig/SecretEnv/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::Environment", + "aws:cdk:cloudformation:props": { + "applicationId": { + "Ref": "MyAppConfigB4B63E75" + }, + "name": "awsappconfigconfiguration-MyAppConfig-SecretEnv-371FF6BB" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appconfig.CfnEnvironment", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "SecretEnvWithKey": { + "id": "SecretEnvWithKey", + "path": "aws-appconfig-configuration/MyAppConfig/SecretEnvWithKey", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-appconfig-configuration/MyAppConfig/SecretEnvWithKey/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::Environment", + "aws:cdk:cloudformation:props": { + "applicationId": { + "Ref": "MyAppConfigB4B63E75" + }, + "name": "awsappconfigconfiguration-MyAppConfig-SecretEnvWithKey-8D4A2FFE" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appconfig.CfnEnvironment", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "MyDeployStrategy": { + "id": "MyDeployStrategy", + "path": "aws-appconfig-configuration/MyDeployStrategy", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-appconfig-configuration/MyDeployStrategy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::DeploymentStrategy", + "aws:cdk:cloudformation:props": { + "deploymentDurationInMinutes": 0, + "growthFactor": 100, + "growthType": "LINEAR", + "name": "awsappconfigconfiguration-MyDeployStrategy-91730AE8", + "replicateTo": "NONE" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appconfig.CfnDeploymentStrategy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "MyHostedConfig": { + "id": "MyHostedConfig", + "path": "aws-appconfig-configuration/MyHostedConfig", + "children": { + "ConfigurationProfile": { + "id": "ConfigurationProfile", + "path": "aws-appconfig-configuration/MyHostedConfig/ConfigurationProfile", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::ConfigurationProfile", + "aws:cdk:cloudformation:props": { + "applicationId": { + "Ref": "MyAppConfigB4B63E75" + }, + "locationUri": "hosted", + "name": "awsappconfigconfiguration-MyHostedConfig-4CF350AE", + "validators": [ + { + "content": "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"type\": \"string\"\n}", + "type": "JSON_SCHEMA" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appconfig.CfnConfigurationProfile", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-appconfig-configuration/MyHostedConfig/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::HostedConfigurationVersion", + "aws:cdk:cloudformation:props": { + "applicationId": { + "Ref": "MyAppConfigB4B63E75" + }, + "configurationProfileId": { + "Ref": "MyHostedConfigConfigurationProfile2E1A2BBC" + }, + "content": "This is my configuration content.", + "contentType": "application/json" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appconfig.CfnHostedConfigurationVersion", + "version": "0.0.0" + } + }, + "DeploymentE4742": { + "id": "DeploymentE4742", + "path": "aws-appconfig-configuration/MyHostedConfig/DeploymentE4742", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::Deployment", + "aws:cdk:cloudformation:props": { + "applicationId": { + "Ref": "MyAppConfigB4B63E75" + }, + "configurationProfileId": { + "Ref": "MyHostedConfigConfigurationProfile2E1A2BBC" + }, + "configurationVersion": { + "Ref": "MyHostedConfig51D3877D" + }, + "deploymentStrategyId": { + "Ref": "MyDeployStrategy062CAEA2" + }, + "environmentId": { + "Ref": "MyAppConfigHostedEnvF39C847E" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appconfig.CfnDeployment", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + }, + "MyHostedConfigFromJson": { + "id": "MyHostedConfigFromJson", + "path": "aws-appconfig-configuration/MyHostedConfigFromJson", + "children": { + "ConfigurationProfile": { + "id": "ConfigurationProfile", + "path": "aws-appconfig-configuration/MyHostedConfigFromJson/ConfigurationProfile", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::ConfigurationProfile", + "aws:cdk:cloudformation:props": { + "applicationId": { + "Ref": "MyAppConfigB4B63E75" + }, + "locationUri": "hosted", + "name": "awsappconfigconfiguration-MyHostedConfigFromJson-3E786E81" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appconfig.CfnConfigurationProfile", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-appconfig-configuration/MyHostedConfigFromJson/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::HostedConfigurationVersion", + "aws:cdk:cloudformation:props": { + "applicationId": { + "Ref": "MyAppConfigB4B63E75" + }, + "configurationProfileId": { + "Ref": "MyHostedConfigFromJsonConfigurationProfile863E1E42" + }, + "content": "This is the configuration content", + "contentType": "application/json" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appconfig.CfnHostedConfigurationVersion", + "version": "0.0.0" + } + }, + "DeploymentCD82E": { + "id": "DeploymentCD82E", + "path": "aws-appconfig-configuration/MyHostedConfigFromJson/DeploymentCD82E", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::Deployment", + "aws:cdk:cloudformation:props": { + "applicationId": { + "Ref": "MyAppConfigB4B63E75" + }, + "configurationProfileId": { + "Ref": "MyHostedConfigFromJsonConfigurationProfile863E1E42" + }, + "configurationVersion": { + "Ref": "MyHostedConfigFromJsonD8CF9BE4" + }, + "deploymentStrategyId": { + "Ref": "MyDeployStrategy062CAEA2" + }, + "environmentId": { + "Ref": "MyAppConfigHostedEnvFromJson9E6E36C4" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appconfig.CfnDeployment", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + }, + "MyValidatorFunction": { + "id": "MyValidatorFunction", + "path": "aws-appconfig-configuration/MyValidatorFunction", + "children": { + "ServiceRole": { + "id": "ServiceRole", + "path": "aws-appconfig-configuration/MyValidatorFunction/ServiceRole", + "children": { + "ImportServiceRole": { + "id": "ImportServiceRole", + "path": "aws-appconfig-configuration/MyValidatorFunction/ServiceRole/ImportServiceRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-appconfig-configuration/MyValidatorFunction/ServiceRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "managedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-appconfig-configuration/MyValidatorFunction/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Lambda::Function", + "aws:cdk:cloudformation:props": { + "code": { + "zipFile": "\ndef handler(event, context):\n print('This is my dummy validator')\n" + }, + "handler": "index.handler", + "role": { + "Fn::GetAtt": [ + "MyValidatorFunctionServiceRole5CD02390", + "Arn" + ] + }, + "runtime": "python3.8" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_lambda.CfnFunction", + "version": "0.0.0" + } + }, + "AppConfigPermission": { + "id": "AppConfigPermission", + "path": "aws-appconfig-configuration/MyValidatorFunction/AppConfigPermission", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Lambda::Permission", + "aws:cdk:cloudformation:props": { + "action": "lambda:InvokeFunction", + "functionName": { + "Fn::GetAtt": [ + "MyValidatorFunctionA679CB3C", + "Arn" + ] + }, + "principal": "appconfig.amazonaws.com" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_lambda.CfnPermission", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_lambda.Function", + "version": "0.0.0" + } + }, + "MyParameter": { + "id": "MyParameter", + "path": "aws-appconfig-configuration/MyParameter", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-appconfig-configuration/MyParameter/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::SSM::Parameter", + "aws:cdk:cloudformation:props": { + "type": "String", + "value": "This is the content stored in ssm parameter" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ssm.CfnParameter", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ssm.StringParameter", + "version": "0.0.0" + } + }, + "MyConfigFromParameter": { + "id": "MyConfigFromParameter", + "path": "aws-appconfig-configuration/MyConfigFromParameter", + "children": { + "Role": { + "id": "Role", + "path": "aws-appconfig-configuration/MyConfigFromParameter/Role", + "children": { + "ImportRole": { + "id": "ImportRole", + "path": "aws-appconfig-configuration/MyConfigFromParameter/Role/ImportRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-appconfig-configuration/MyConfigFromParameter/Role/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "appconfig.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "policies": [ + { + "policyName": "AllowAppConfigReadFromSourcePolicy", + "policyDocument": { + "Statement": [ + { + "Action": "ssm:GetParameter", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":ssm:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":parameter/", + { + "Ref": "MyParameter18BA547D" + } + ] + ] + } + } + ], + "Version": "2012-10-17" + } + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-appconfig-configuration/MyConfigFromParameter/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::ConfigurationProfile", + "aws:cdk:cloudformation:props": { + "applicationId": { + "Ref": "MyAppConfigB4B63E75" + }, + "description": "This is a configuration profile used for integ testing", + "locationUri": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":ssm:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":parameter/", + { + "Ref": "MyParameter18BA547D" + } + ] + ] + }, + "name": "TestConfigProfileStoredAsParamater", + "retrievalRoleArn": { + "Fn::GetAtt": [ + "MyConfigFromParameterRole4E40C8BC", + "Arn" + ] + }, + "validators": [ + { + "content": { + "Fn::GetAtt": [ + "MyValidatorFunctionA679CB3C", + "Arn" + ] + }, + "type": "LAMBDA" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appconfig.CfnConfigurationProfile", + "version": "0.0.0" + } + }, + "Deployment0C84B": { + "id": "Deployment0C84B", + "path": "aws-appconfig-configuration/MyConfigFromParameter/Deployment0C84B", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::Deployment", + "aws:cdk:cloudformation:props": { + "applicationId": { + "Ref": "MyAppConfigB4B63E75" + }, + "configurationProfileId": { + "Ref": "MyConfigFromParameterDC204468" + }, + "configurationVersion": "1", + "deploymentStrategyId": { + "Ref": "MyDeployStrategy062CAEA2" + }, + "description": "This is a configuration profile used for integ testing", + "environmentId": { + "Ref": "MyAppConfigParameterEnvD769FB19" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appconfig.CfnDeployment", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + }, + "MyDocument": { + "id": "MyDocument", + "path": "aws-appconfig-configuration/MyDocument", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::SSM::Document", + "aws:cdk:cloudformation:props": { + "content": { + "schemaVersion": "2.2", + "description": "Sample SSM Document", + "mainSteps": [ + { + "action": "aws:runShellScript", + "name": "step1", + "inputs": { + "runCommand": [ + "echo \"Hello, World!\"" + ] + } + } + ] + }, + "documentType": "Command", + "name": "TestDocument" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ssm.CfnDocument", + "version": "0.0.0" + } + }, + "MyConfigFromDocument": { + "id": "MyConfigFromDocument", + "path": "aws-appconfig-configuration/MyConfigFromDocument", + "children": { + "Role": { + "id": "Role", + "path": "aws-appconfig-configuration/MyConfigFromDocument/Role", + "children": { + "ImportRole": { + "id": "ImportRole", + "path": "aws-appconfig-configuration/MyConfigFromDocument/Role/ImportRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-appconfig-configuration/MyConfigFromDocument/Role/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "appconfig.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "policies": [ + { + "policyName": "AllowAppConfigReadFromSourcePolicy", + "policyDocument": { + "Statement": [ + { + "Action": "ssm:GetDocument", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":ssm:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":document/", + { + "Ref": "MyDocument" + } + ] + ] + } + } + ], + "Version": "2012-10-17" + } + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-appconfig-configuration/MyConfigFromDocument/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::ConfigurationProfile", + "aws:cdk:cloudformation:props": { + "applicationId": { + "Ref": "MyAppConfigB4B63E75" + }, + "locationUri": { + "Fn::Join": [ + "", + [ + "ssm-document://", + { + "Ref": "MyDocument" + } + ] + ] + }, + "name": "awsappconfigconfiguration-MyConfigFromDocument-A15AC401", + "retrievalRoleArn": { + "Fn::GetAtt": [ + "MyConfigFromDocumentRole29FB4D3D", + "Arn" + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appconfig.CfnConfigurationProfile", + "version": "0.0.0" + } + }, + "Deployment1520E": { + "id": "Deployment1520E", + "path": "aws-appconfig-configuration/MyConfigFromDocument/Deployment1520E", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::Deployment", + "aws:cdk:cloudformation:props": { + "applicationId": { + "Ref": "MyAppConfigB4B63E75" + }, + "configurationProfileId": { + "Ref": "MyConfigFromDocument79E66ABA" + }, + "configurationVersion": "1", + "deploymentStrategyId": { + "Ref": "MyDeployStrategy062CAEA2" + }, + "environmentId": { + "Ref": "MyAppConfigDocumentEnv34B10223" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appconfig.CfnDeployment", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + }, + "MyBucket": { + "id": "MyBucket", + "path": "aws-appconfig-configuration/MyBucket", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-appconfig-configuration/MyBucket/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::S3::Bucket", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "aws-cdk:cr-owned:2255b7ad", + "value": "true" + } + ], + "versioningConfiguration": { + "status": "Enabled" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_s3.CfnBucket", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_s3.Bucket", + "version": "0.0.0" + } + }, + "DeployConfigInBucket": { + "id": "DeployConfigInBucket", + "path": "aws-appconfig-configuration/DeployConfigInBucket", + "children": { + "AwsCliLayer": { + "id": "AwsCliLayer", + "path": "aws-appconfig-configuration/DeployConfigInBucket/AwsCliLayer", + "children": { + "Code": { + "id": "Code", + "path": "aws-appconfig-configuration/DeployConfigInBucket/AwsCliLayer/Code", + "children": { + "Stage": { + "id": "Stage", + "path": "aws-appconfig-configuration/DeployConfigInBucket/AwsCliLayer/Code/Stage", + "constructInfo": { + "fqn": "aws-cdk-lib.AssetStaging", + "version": "0.0.0" + } + }, + "AssetBucket": { + "id": "AssetBucket", + "path": "aws-appconfig-configuration/DeployConfigInBucket/AwsCliLayer/Code/AssetBucket", + "constructInfo": { + "fqn": "aws-cdk-lib.aws_s3.BucketBase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_s3_assets.Asset", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-appconfig-configuration/DeployConfigInBucket/AwsCliLayer/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Lambda::LayerVersion", + "aws:cdk:cloudformation:props": { + "content": { + "s3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "s3Key": "e2277687077a2abf9ae1af1cc9565e6715e2ebb62f79ec53aa75a1af9298f642.zip" + }, + "description": "/opt/awscli/aws" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_lambda.CfnLayerVersion", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.lambda_layer_awscli.AwsCliLayer", + "version": "0.0.0" + } + }, + "CustomResourceHandler": { + "id": "CustomResourceHandler", + "path": "aws-appconfig-configuration/DeployConfigInBucket/CustomResourceHandler", + "constructInfo": { + "fqn": "aws-cdk-lib.aws_lambda.SingletonFunction", + "version": "0.0.0" + } + }, + "Asset1": { + "id": "Asset1", + "path": "aws-appconfig-configuration/DeployConfigInBucket/Asset1", + "children": { + "Stage": { + "id": "Stage", + "path": "aws-appconfig-configuration/DeployConfigInBucket/Asset1/Stage", + "constructInfo": { + "fqn": "aws-cdk-lib.AssetStaging", + "version": "0.0.0" + } + }, + "AssetBucket": { + "id": "AssetBucket", + "path": "aws-appconfig-configuration/DeployConfigInBucket/Asset1/AssetBucket", + "constructInfo": { + "fqn": "aws-cdk-lib.aws_s3.BucketBase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_s3_assets.Asset", + "version": "0.0.0" + } + }, + "CustomResource": { + "id": "CustomResource", + "path": "aws-appconfig-configuration/DeployConfigInBucket/CustomResource", + "children": { + "Default": { + "id": "Default", + "path": "aws-appconfig-configuration/DeployConfigInBucket/CustomResource/Default", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.CustomResource", + "version": "0.0.0" + } + }, + "Asset2": { + "id": "Asset2", + "path": "aws-appconfig-configuration/DeployConfigInBucket/Asset2", + "children": { + "Stage": { + "id": "Stage", + "path": "aws-appconfig-configuration/DeployConfigInBucket/Asset2/Stage", + "constructInfo": { + "fqn": "aws-cdk-lib.AssetStaging", + "version": "0.0.0" + } + }, + "AssetBucket": { + "id": "AssetBucket", + "path": "aws-appconfig-configuration/DeployConfigInBucket/Asset2/AssetBucket", + "constructInfo": { + "fqn": "aws-cdk-lib.aws_s3.BucketBase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_s3_assets.Asset", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_s3_deployment.BucketDeployment", + "version": "0.0.0" + } + }, + "Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C": { + "id": "Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C", + "path": "aws-appconfig-configuration/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C", + "children": { + "ServiceRole": { + "id": "ServiceRole", + "path": "aws-appconfig-configuration/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/ServiceRole", + "children": { + "ImportServiceRole": { + "id": "ImportServiceRole", + "path": "aws-appconfig-configuration/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/ServiceRole/ImportServiceRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-appconfig-configuration/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/ServiceRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "managedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + }, + "DefaultPolicy": { + "id": "DefaultPolicy", + "path": "aws-appconfig-configuration/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/ServiceRole/DefaultPolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-appconfig-configuration/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/ServiceRole/DefaultPolicy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": [ + "s3:GetBucket*", + "s3:GetObject*", + "s3:List*" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":s3:::", + { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "/*" + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":s3:::", + { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + } + ] + ] + } + ] + }, + { + "Action": [ + "s3:Abort*", + "s3:DeleteObject*", + "s3:GetBucket*", + "s3:GetObject*", + "s3:List*", + "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "MyBucketF68F3FF0", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "MyBucketF68F3FF0", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "policyName": "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRoleDefaultPolicy88902FDF", + "roles": [ + { + "Ref": "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRole89A01265" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnPolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Policy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" + } + }, + "Code": { + "id": "Code", + "path": "aws-appconfig-configuration/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/Code", + "children": { + "Stage": { + "id": "Stage", + "path": "aws-appconfig-configuration/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/Code/Stage", + "constructInfo": { + "fqn": "aws-cdk-lib.AssetStaging", + "version": "0.0.0" + } + }, + "AssetBucket": { + "id": "AssetBucket", + "path": "aws-appconfig-configuration/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/Code/AssetBucket", + "constructInfo": { + "fqn": "aws-cdk-lib.aws_s3.BucketBase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_s3_assets.Asset", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-appconfig-configuration/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Lambda::Function", + "aws:cdk:cloudformation:props": { + "code": { + "s3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "s3Key": "9eb41a5505d37607ac419321497a4f8c21cf0ee1f9b4a6b29aa04301aea5c7fd.zip" + }, + "environment": { + "variables": { + "AWS_CA_BUNDLE": "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem" + } + }, + "handler": "index.handler", + "layers": [ + { + "Ref": "DeployConfigInBucketAwsCliLayerFC57D055" + } + ], + "role": { + "Fn::GetAtt": [ + "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRole89A01265", + "Arn" + ] + }, + "runtime": "python3.9", + "timeout": 900 + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_lambda.CfnFunction", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_lambda.Function", + "version": "0.0.0" + } + }, + "MyConfigFromBucket": { + "id": "MyConfigFromBucket", + "path": "aws-appconfig-configuration/MyConfigFromBucket", + "children": { + "DeploymentStrategy": { + "id": "DeploymentStrategy", + "path": "aws-appconfig-configuration/MyConfigFromBucket/DeploymentStrategy", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-appconfig-configuration/MyConfigFromBucket/DeploymentStrategy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::DeploymentStrategy", + "aws:cdk:cloudformation:props": { + "deploymentDurationInMinutes": 20, + "finalBakeTimeInMinutes": 10, + "growthFactor": 10, + "growthType": "EXPONENTIAL", + "name": "awsappconfigconfiguration-MyomBucket-DeploymentStrategy-8366DF86", + "replicateTo": "NONE" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appconfig.CfnDeploymentStrategy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Role": { + "id": "Role", + "path": "aws-appconfig-configuration/MyConfigFromBucket/Role", + "children": { + "ImportRole": { + "id": "ImportRole", + "path": "aws-appconfig-configuration/MyConfigFromBucket/Role/ImportRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-appconfig-configuration/MyConfigFromBucket/Role/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "appconfig.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "policies": [ + { + "policyName": "AllowAppConfigReadFromSourcePolicy", + "policyDocument": { + "Statement": [ + { + "Action": [ + "s3:GetObject", + "s3:GetObjectMetadata", + "s3:GetObjectVersion" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":s3:::", + { + "Ref": "MyBucketF68F3FF0" + }, + "/hello/world/file.txt" + ] + ] + } + }, + { + "Action": [ + "s3:GetBucketLocation", + "s3:GetBucketVersioning", + "s3:ListBucket", + "s3:ListBucketVersions" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":s3:::", + { + "Ref": "MyBucketF68F3FF0" + } + ] + ] + } + }, + { + "Action": "s3:ListAllMyBuckets", + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + } + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-appconfig-configuration/MyConfigFromBucket/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::ConfigurationProfile", + "aws:cdk:cloudformation:props": { + "applicationId": { + "Ref": "MyAppConfigB4B63E75" + }, + "description": { + "Fn::Join": [ + "", + [ + "Sourced from ", + { + "Fn::Select": [ + 0, + { + "Fn::GetAtt": [ + "DeployConfigInBucketCustomResource91997C5B", + "SourceObjectKeys" + ] + } + ] + } + ] + ] + }, + "locationUri": { + "Fn::Join": [ + "", + [ + "s3://", + { + "Ref": "MyBucketF68F3FF0" + }, + "/hello/world/file.txt" + ] + ] + }, + "name": "awsappconfigconfiguration-MyConfigFromBucket-79972829", + "retrievalRoleArn": { + "Fn::GetAtt": [ + "MyConfigFromBucketRole5749669F", + "Arn" + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appconfig.CfnConfigurationProfile", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + }, + "MySecret": { + "id": "MySecret", + "path": "aws-appconfig-configuration/MySecret", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-appconfig-configuration/MySecret/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::SecretsManager::Secret", + "aws:cdk:cloudformation:props": { + "secretString": "This is the content stored in secrets manager" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_secretsmanager.CfnSecret", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_secretsmanager.Secret", + "version": "0.0.0" + } + }, + "MyConfigFromSecret": { + "id": "MyConfigFromSecret", + "path": "aws-appconfig-configuration/MyConfigFromSecret", + "children": { + "DeploymentStrategy": { + "id": "DeploymentStrategy", + "path": "aws-appconfig-configuration/MyConfigFromSecret/DeploymentStrategy", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-appconfig-configuration/MyConfigFromSecret/DeploymentStrategy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::DeploymentStrategy", + "aws:cdk:cloudformation:props": { + "deploymentDurationInMinutes": 20, + "finalBakeTimeInMinutes": 10, + "growthFactor": 10, + "growthType": "EXPONENTIAL", + "name": "awsappconfigconfiguration-MyomSecret-DeploymentStrategy-2F6D94AB", + "replicateTo": "NONE" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appconfig.CfnDeploymentStrategy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Role": { + "id": "Role", + "path": "aws-appconfig-configuration/MyConfigFromSecret/Role", + "children": { + "ImportRole": { + "id": "ImportRole", + "path": "aws-appconfig-configuration/MyConfigFromSecret/Role/ImportRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-appconfig-configuration/MyConfigFromSecret/Role/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "appconfig.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "policies": [ + { + "policyName": "AllowAppConfigReadFromSourcePolicy", + "policyDocument": { + "Statement": [ + { + "Action": "secretsmanager:GetSecretValue", + "Effect": "Allow", + "Resource": { + "Ref": "MySecret8FE80B51" + } + } + ], + "Version": "2012-10-17" + } + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-appconfig-configuration/MyConfigFromSecret/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::ConfigurationProfile", + "aws:cdk:cloudformation:props": { + "applicationId": { + "Ref": "MyAppConfigB4B63E75" + }, + "locationUri": { + "Ref": "MySecret8FE80B51" + }, + "name": "awsappconfigconfiguration-MyConfigFromSecret-3BD4C031", + "retrievalRoleArn": { + "Fn::GetAtt": [ + "MyConfigFromSecretRole77E11CE7", + "Arn" + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appconfig.CfnConfigurationProfile", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + }, + "MyKey": { + "id": "MyKey", + "path": "aws-appconfig-configuration/MyKey", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-appconfig-configuration/MyKey/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::KMS::Key", + "aws:cdk:cloudformation:props": { + "keyPolicy": { + "Statement": [ + { + "Action": "kms:*", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + }, + { + "Action": [ + "kms:CreateGrant", + "kms:Decrypt", + "kms:DescribeKey", + "kms:Encrypt", + "kms:GenerateDataKey*", + "kms:ReEncrypt*" + ], + "Condition": { + "StringEquals": { + "kms:ViaService": { + "Fn::Join": [ + "", + [ + "secretsmanager.", + { + "Ref": "AWS::Region" + }, + ".amazonaws.com" + ] + ] + } + } + }, + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_kms.CfnKey", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_kms.Key", + "version": "0.0.0" + } + }, + "MySecretWithKey": { + "id": "MySecretWithKey", + "path": "aws-appconfig-configuration/MySecretWithKey", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-appconfig-configuration/MySecretWithKey/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::SecretsManager::Secret", + "aws:cdk:cloudformation:props": { + "kmsKeyId": { + "Fn::GetAtt": [ + "MyKey6AB29FA6", + "Arn" + ] + }, + "secretString": "This is the content stored in secrets manager" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_secretsmanager.CfnSecret", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_secretsmanager.Secret", + "version": "0.0.0" + } + }, + "MyConfigFromSecretWithKey": { + "id": "MyConfigFromSecretWithKey", + "path": "aws-appconfig-configuration/MyConfigFromSecretWithKey", + "children": { + "DeploymentStrategy": { + "id": "DeploymentStrategy", + "path": "aws-appconfig-configuration/MyConfigFromSecretWithKey/DeploymentStrategy", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-appconfig-configuration/MyConfigFromSecretWithKey/DeploymentStrategy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::DeploymentStrategy", + "aws:cdk:cloudformation:props": { + "deploymentDurationInMinutes": 20, + "finalBakeTimeInMinutes": 10, + "growthFactor": 10, + "growthType": "EXPONENTIAL", + "name": "awsappconfigconfiguration-MytWithKey-DeploymentStrategy-70C32371", + "replicateTo": "NONE" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appconfig.CfnDeploymentStrategy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Role": { + "id": "Role", + "path": "aws-appconfig-configuration/MyConfigFromSecretWithKey/Role", + "children": { + "ImportRole": { + "id": "ImportRole", + "path": "aws-appconfig-configuration/MyConfigFromSecretWithKey/Role/ImportRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-appconfig-configuration/MyConfigFromSecretWithKey/Role/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "appconfig.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "policies": [ + { + "policyName": "AllowAppConfigReadFromSourcePolicy", + "policyDocument": { + "Statement": [ + { + "Action": "secretsmanager:GetSecretValue", + "Effect": "Allow", + "Resource": { + "Ref": "MySecretWithKey1F7B590B" + } + }, + { + "Action": "kms:Decrypt", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "MyKey6AB29FA6", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + } + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-appconfig-configuration/MyConfigFromSecretWithKey/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::ConfigurationProfile", + "aws:cdk:cloudformation:props": { + "applicationId": { + "Ref": "MyAppConfigB4B63E75" + }, + "locationUri": { + "Ref": "MySecretWithKey1F7B590B" + }, + "name": "awsappconfigconfiguration-MyConfigFromSecretWithKey-516D9EB2", + "retrievalRoleArn": { + "Fn::GetAtt": [ + "MyConfigFromSecretWithKeyRole3C7B494A", + "Arn" + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appconfig.CfnConfigurationProfile", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + }, + "MyPipeline": { + "id": "MyPipeline", + "path": "aws-appconfig-configuration/MyPipeline", + "children": { + "ArtifactsBucketEncryptionKey": { + "id": "ArtifactsBucketEncryptionKey", + "path": "aws-appconfig-configuration/MyPipeline/ArtifactsBucketEncryptionKey", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-appconfig-configuration/MyPipeline/ArtifactsBucketEncryptionKey/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::KMS::Key", + "aws:cdk:cloudformation:props": { + "keyPolicy": { + "Statement": [ + { + "Action": "kms:*", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_kms.CfnKey", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_kms.Key", + "version": "0.0.0" + } + }, + "ArtifactsBucketEncryptionKeyAlias": { + "id": "ArtifactsBucketEncryptionKeyAlias", + "path": "aws-appconfig-configuration/MyPipeline/ArtifactsBucketEncryptionKeyAlias", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-appconfig-configuration/MyPipeline/ArtifactsBucketEncryptionKeyAlias/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::KMS::Alias", + "aws:cdk:cloudformation:props": { + "aliasName": "alias/codepipeline-aws-appconfig-configuration-mypipeline-893f29d4", + "targetKeyId": { + "Fn::GetAtt": [ + "MyPipelineArtifactsBucketEncryptionKey8BF0A7F3", + "Arn" + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_kms.CfnAlias", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_kms.Alias", + "version": "0.0.0" + } + }, + "ArtifactsBucket": { + "id": "ArtifactsBucket", + "path": "aws-appconfig-configuration/MyPipeline/ArtifactsBucket", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-appconfig-configuration/MyPipeline/ArtifactsBucket/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::S3::Bucket", + "aws:cdk:cloudformation:props": { + "bucketEncryption": { + "serverSideEncryptionConfiguration": [ + { + "serverSideEncryptionByDefault": { + "sseAlgorithm": "aws:kms", + "kmsMasterKeyId": { + "Fn::GetAtt": [ + "MyPipelineArtifactsBucketEncryptionKey8BF0A7F3", + "Arn" + ] + } + } + } + ] + }, + "publicAccessBlockConfiguration": { + "blockPublicAcls": true, + "blockPublicPolicy": true, + "ignorePublicAcls": true, + "restrictPublicBuckets": true + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_s3.CfnBucket", + "version": "0.0.0" + } + }, + "Policy": { + "id": "Policy", + "path": "aws-appconfig-configuration/MyPipeline/ArtifactsBucket/Policy", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-appconfig-configuration/MyPipeline/ArtifactsBucket/Policy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::S3::BucketPolicy", + "aws:cdk:cloudformation:props": { + "bucket": { + "Ref": "MyPipelineArtifactsBucket727923DD" + }, + "policyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "MyPipelineArtifactsBucket727923DD", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "MyPipelineArtifactsBucket727923DD", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_s3.CfnBucketPolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_s3.BucketPolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_s3.Bucket", + "version": "0.0.0" + } + }, + "Role": { + "id": "Role", + "path": "aws-appconfig-configuration/MyPipeline/Role", + "children": { + "ImportRole": { + "id": "ImportRole", + "path": "aws-appconfig-configuration/MyPipeline/Role/ImportRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-appconfig-configuration/MyPipeline/Role/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "codepipeline.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + }, + "DefaultPolicy": { + "id": "DefaultPolicy", + "path": "aws-appconfig-configuration/MyPipeline/Role/DefaultPolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-appconfig-configuration/MyPipeline/Role/DefaultPolicy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": [ + "s3:Abort*", + "s3:DeleteObject*", + "s3:GetBucket*", + "s3:GetObject*", + "s3:List*", + "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "MyPipelineArtifactsBucket727923DD", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "MyPipelineArtifactsBucket727923DD", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "kms:Decrypt", + "kms:DescribeKey", + "kms:Encrypt", + "kms:GenerateDataKey*", + "kms:ReEncrypt*" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "MyPipelineArtifactsBucketEncryptionKey8BF0A7F3", + "Arn" + ] + } + }, + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "MyPipelinebetaSourceCodePipelineActionRoleA59DCC4C", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "MyPipelineprodDeployCodePipelineActionRoleFAD07544", + "Arn" + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "policyName": "MyPipelineRoleDefaultPolicy34F09EFA", + "roles": [ + { + "Ref": "MyPipelineRoleC0D47CA4" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnPolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Policy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-appconfig-configuration/MyPipeline/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::CodePipeline::Pipeline", + "aws:cdk:cloudformation:props": { + "artifactStore": { + "type": "S3", + "location": { + "Ref": "MyPipelineArtifactsBucket727923DD" + }, + "encryptionKey": { + "type": "KMS", + "id": { + "Fn::GetAtt": [ + "MyPipelineArtifactsBucketEncryptionKey8BF0A7F3", + "Arn" + ] + } + } + }, + "roleArn": { + "Fn::GetAtt": [ + "MyPipelineRoleC0D47CA4", + "Arn" + ] + }, + "stages": [ + { + "name": "beta", + "actions": [ + { + "name": "Source", + "outputArtifacts": [ + { + "name": "SourceOutput" + } + ], + "actionTypeId": { + "category": "Source", + "version": "1", + "owner": "AWS", + "provider": "S3" + }, + "configuration": { + "S3Bucket": { + "Ref": "MyBucketF68F3FF0" + }, + "S3ObjectKey": "hello/world/codepipeline.txt" + }, + "runOrder": 1, + "roleArn": { + "Fn::GetAtt": [ + "MyPipelinebetaSourceCodePipelineActionRoleA59DCC4C", + "Arn" + ] + } + } + ] + }, + { + "name": "prod", + "actions": [ + { + "name": "Deploy", + "inputArtifacts": [ + { + "name": "SourceOutput" + } + ], + "actionTypeId": { + "category": "Deploy", + "version": "1", + "owner": "AWS", + "provider": "S3" + }, + "configuration": { + "BucketName": { + "Ref": "MyBucketF68F3FF0" + }, + "Extract": "true" + }, + "runOrder": 1, + "roleArn": { + "Fn::GetAtt": [ + "MyPipelineprodDeployCodePipelineActionRoleFAD07544", + "Arn" + ] + } + } + ] + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_codepipeline.CfnPipeline", + "version": "0.0.0" + } + }, + "beta": { + "id": "beta", + "path": "aws-appconfig-configuration/MyPipeline/beta", + "children": { + "Source": { + "id": "Source", + "path": "aws-appconfig-configuration/MyPipeline/beta/Source", + "children": { + "CodePipelineActionRole": { + "id": "CodePipelineActionRole", + "path": "aws-appconfig-configuration/MyPipeline/beta/Source/CodePipelineActionRole", + "children": { + "ImportCodePipelineActionRole": { + "id": "ImportCodePipelineActionRole", + "path": "aws-appconfig-configuration/MyPipeline/beta/Source/CodePipelineActionRole/ImportCodePipelineActionRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-appconfig-configuration/MyPipeline/beta/Source/CodePipelineActionRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + } + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + }, + "DefaultPolicy": { + "id": "DefaultPolicy", + "path": "aws-appconfig-configuration/MyPipeline/beta/Source/CodePipelineActionRole/DefaultPolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-appconfig-configuration/MyPipeline/beta/Source/CodePipelineActionRole/DefaultPolicy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": [ + "s3:GetBucket*", + "s3:GetObject*", + "s3:List*" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "MyBucketF68F3FF0", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "MyBucketF68F3FF0", + "Arn" + ] + }, + "/hello/world/codepipeline.txt" + ] + ] + } + ] + }, + { + "Action": [ + "s3:Abort*", + "s3:DeleteObject*", + "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "MyPipelineArtifactsBucket727923DD", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "MyPipelineArtifactsBucket727923DD", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "kms:Decrypt", + "kms:Encrypt", + "kms:GenerateDataKey*", + "kms:ReEncrypt*" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "MyPipelineArtifactsBucketEncryptionKey8BF0A7F3", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "policyName": "MyPipelinebetaSourceCodePipelineActionRoleDefaultPolicy4F6DF82E", + "roles": [ + { + "Ref": "MyPipelinebetaSourceCodePipelineActionRoleA59DCC4C" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnPolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Policy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + }, + "prod": { + "id": "prod", + "path": "aws-appconfig-configuration/MyPipeline/prod", + "children": { + "Deploy": { + "id": "Deploy", + "path": "aws-appconfig-configuration/MyPipeline/prod/Deploy", + "children": { + "CodePipelineActionRole": { + "id": "CodePipelineActionRole", + "path": "aws-appconfig-configuration/MyPipeline/prod/Deploy/CodePipelineActionRole", + "children": { + "ImportCodePipelineActionRole": { + "id": "ImportCodePipelineActionRole", + "path": "aws-appconfig-configuration/MyPipeline/prod/Deploy/CodePipelineActionRole/ImportCodePipelineActionRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-appconfig-configuration/MyPipeline/prod/Deploy/CodePipelineActionRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + } + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + }, + "DefaultPolicy": { + "id": "DefaultPolicy", + "path": "aws-appconfig-configuration/MyPipeline/prod/Deploy/CodePipelineActionRole/DefaultPolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-appconfig-configuration/MyPipeline/prod/Deploy/CodePipelineActionRole/DefaultPolicy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": [ + "s3:Abort*", + "s3:DeleteObject*", + "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "MyBucketF68F3FF0", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "MyBucketF68F3FF0", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "s3:GetBucket*", + "s3:GetObject*", + "s3:List*" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "MyPipelineArtifactsBucket727923DD", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "MyPipelineArtifactsBucket727923DD", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "kms:Decrypt", + "kms:DescribeKey" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "MyPipelineArtifactsBucketEncryptionKey8BF0A7F3", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "policyName": "MyPipelineprodDeployCodePipelineActionRoleDefaultPolicyF1913519", + "roles": [ + { + "Ref": "MyPipelineprodDeployCodePipelineActionRoleFAD07544" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnPolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Policy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_codepipeline.Pipeline", + "version": "0.0.0" + } + }, + "MyConfigFromPipeline": { + "id": "MyConfigFromPipeline", + "path": "aws-appconfig-configuration/MyConfigFromPipeline", + "children": { + "DeploymentStrategy": { + "id": "DeploymentStrategy", + "path": "aws-appconfig-configuration/MyConfigFromPipeline/DeploymentStrategy", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-appconfig-configuration/MyConfigFromPipeline/DeploymentStrategy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::DeploymentStrategy", + "aws:cdk:cloudformation:props": { + "deploymentDurationInMinutes": 20, + "finalBakeTimeInMinutes": 10, + "growthFactor": 10, + "growthType": "EXPONENTIAL", + "name": "awsappconfigconfiguration-MyPipeline-DeploymentStrategy-5F76004F", + "replicateTo": "NONE" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appconfig.CfnDeploymentStrategy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-appconfig-configuration/MyConfigFromPipeline/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::ConfigurationProfile", + "aws:cdk:cloudformation:props": { + "applicationId": { + "Ref": "MyAppConfigB4B63E75" + }, + "locationUri": { + "Fn::Join": [ + "", + [ + "codepipeline://", + { + "Ref": "MyPipelineAED38ECF" + } + ] + ] + }, + "name": "awsappconfigconfiguration-MyConfigFromPipeline-FD2B6B65" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appconfig.CfnConfigurationProfile", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "aws-appconfig-configuration/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "aws-appconfig-configuration/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + }, + "appconfig-configuration": { + "id": "appconfig-configuration", + "path": "appconfig-configuration", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "appconfig-configuration/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "appconfig-configuration/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "appconfig-configuration/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "appconfig-configuration/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "appconfig-configuration/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.configuration.ts b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.configuration.ts new file mode 100644 index 0000000000000..5722d6ae23a7f --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.configuration.ts @@ -0,0 +1,208 @@ +import { App, Duration, Stack, RemovalPolicy, Fn, SecretValue } from 'aws-cdk-lib'; +import { + Application, + ConfigurationContent, + ConfigurationSource, + DeploymentStrategy, + HostedConfiguration, + JsonSchemaValidator, + LambdaValidator, + RolloutStrategy, + SourcedConfiguration, +} from '../lib'; +import { Key } from 'aws-cdk-lib/aws-kms'; +import { Code, Function, Runtime } from 'aws-cdk-lib/aws-lambda'; +import { Bucket } from 'aws-cdk-lib/aws-s3'; +import { Secret } from 'aws-cdk-lib/aws-secretsmanager'; +import { CfnDocument, StringParameter } from 'aws-cdk-lib/aws-ssm'; +import * as s3Deployment from 'aws-cdk-lib/aws-s3-deployment'; +import { Artifact, Pipeline } from 'aws-cdk-lib/aws-codepipeline'; +import { S3DeployAction, S3SourceAction } from 'aws-cdk-lib/aws-codepipeline-actions'; +import { IntegTest } from '@aws-cdk/integ-tests-alpha'; + +const SCHEMA_STR = +`{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "string" +}`; + +const LAMBDA_CODE = +` +def handler(event, context): + print('This is my dummy validator') +`; + +const app = new App(); + +const stack = new Stack(app, 'aws-appconfig-configuration'); + +// create application for config profile +const appConfigApp = new Application(stack, 'MyAppConfig', { + name: 'AppForConfigTest', +}); + +const deploymentStrategy = new DeploymentStrategy(stack, 'MyDeployStrategy', { + rolloutStrategy: RolloutStrategy.linear({ + growthFactor: 100, + deploymentDuration: Duration.minutes(0), + }), +}); + +// create basic config profile and add config version +const hostedEnv = appConfigApp.addEnvironment('HostedEnv'); +new HostedConfiguration(stack, 'MyHostedConfig', { + application: appConfigApp, + content: ConfigurationContent.fromInline('This is my configuration content.'), + deployTo: [hostedEnv], + validators: [ + JsonSchemaValidator.fromInline(SCHEMA_STR), + // JsonSchemaValidator.fromAsset('/Users/chenjane/Documents/appconfig-l2-constructs/test/schema.json'), + ], + deploymentStrategy, +}); + +// create basic config profile from add config version from file +const hostedEnvFromJson = appConfigApp.addEnvironment('HostedEnvFromJson'); +new HostedConfiguration(stack, 'MyHostedConfigFromJson', { + application: appConfigApp, + content: ConfigurationContent.fromInline('This is the configuration content'), + deployTo: [hostedEnvFromJson], + deploymentStrategy, +}); + +// ssm paramter as configuration source +const func = new Function(stack, 'MyValidatorFunction', { + runtime: Runtime.PYTHON_3_8, + handler: 'index.handler', + code: Code.fromInline(LAMBDA_CODE), +}); +const parameterEnv = appConfigApp.addEnvironment('ParameterEnv'); +const ssmParameter = new StringParameter(stack, 'MyParameter', { + stringValue: 'This is the content stored in ssm parameter', +}); +new SourcedConfiguration(stack, 'MyConfigFromParameter', { + name: 'TestConfigProfileStoredAsParamater', + application: appConfigApp, + description: 'This is a configuration profile used for integ testing', + location: ConfigurationSource.fromParameter(ssmParameter), + versionNumber: '1', + deployTo: [parameterEnv], + validators: [ + LambdaValidator.fromFunction(func), + ], + deploymentStrategy, +}); + +// ssm document as configuration source +const documentEnv = appConfigApp.addEnvironment('DocumentEnv'); +const ssmDocument = new CfnDocument(stack, 'MyDocument', { + content: { + schemaVersion: '2.2', + description: 'Sample SSM Document', + mainSteps: [ + { + action: 'aws:runShellScript', + name: 'step1', + inputs: { + runCommand: [ + 'echo "Hello, World!"', + ], + }, + }, + ], + }, + documentType: 'Command', + name: 'TestDocument', +}); +new SourcedConfiguration(stack, 'MyConfigFromDocument', { + application: appConfigApp, + location: ConfigurationSource.fromCfnDocument(ssmDocument), + versionNumber: '1', + deployTo: [documentEnv], + deploymentStrategy, +}); + +// S3 as configuration source +const bucketEnv = appConfigApp.addEnvironment('BucketEnv'); +const bucket = new Bucket(stack, 'MyBucket', { + versioned: true, +}); +bucket.applyRemovalPolicy(RemovalPolicy.DESTROY); +const deployment = new s3Deployment.BucketDeployment(stack, 'DeployConfigInBucket', { + sources: [s3Deployment.Source.data('hello/world/file.txt', 'This is the content stored in S3')], + destinationBucket: bucket, + retainOnDelete: false, +}); +new SourcedConfiguration(stack, 'MyConfigFromBucket', { + application: appConfigApp, + location: ConfigurationSource.fromBucket(bucket, 'hello/world/file.txt'), + description: `Sourced from ${Fn.select(0, deployment.objectKeys)}`, + deployTo: [bucketEnv], +}); + +// secrets manager as configuration source (without key) +const secretEnv = appConfigApp.addEnvironment('SecretEnv'); +const secret = new Secret(stack, 'MySecret', { + secretStringValue: SecretValue.unsafePlainText('This is the content stored in secrets manager'), +}); +new SourcedConfiguration(stack, 'MyConfigFromSecret', { + application: appConfigApp, + location: ConfigurationSource.fromSecret(secret), + deployTo: [secretEnv], +}); + +// secrets manager as configuration source (with key) +const secretWithKeyEnv = appConfigApp.addEnvironment('SecretEnvWithKey'); +const key = new Key(stack, 'MyKey'); +const secretWithKey = new Secret(stack, 'MySecretWithKey', { + secretStringValue: SecretValue.unsafePlainText('This is the content stored in secrets manager'), + encryptionKey: key, +}); +new SourcedConfiguration(stack, 'MyConfigFromSecretWithKey', { + location: ConfigurationSource.fromSecret(secretWithKey), + deploymentKey: key, + application: appConfigApp, + deployTo: [secretWithKeyEnv], +}); + +// code pipeline as configuration source +deployment.addSource(s3Deployment.Source.data('hello/world/codepipeline.txt', 'This is the content stored in code pipeline')); +const sourceAction = new S3SourceAction({ + actionName: 'Source', + bucket: bucket, + bucketKey: 'hello/world/codepipeline.txt', + output: new Artifact('SourceOutput'), +}); +const deployAction = new S3DeployAction({ + actionName: 'Deploy', + input: Artifact.artifact('SourceOutput'), + bucket: bucket, + extract: true, +}); +const pipeline = new Pipeline(stack, 'MyPipeline', { + stages: [ + { + stageName: 'beta', + actions: [sourceAction], + }, + { + stageName: 'prod', + actions: [deployAction], + }, + ], +}); +new SourcedConfiguration(stack, 'MyConfigFromPipeline', { + application: appConfigApp, + location: ConfigurationSource.fromPipeline(pipeline), +}); + +new IntegTest(app, 'appconfig-configuration', { + testCases: [stack], + cdkCommandOptions: { + destroy: { + args: { + force: true, + }, + }, + }, +}); \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.deployment-strategy.js.snapshot/appconfigdeploymentstrategyDefaultTestDeployAssert3CECE452.assets.json b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.deployment-strategy.js.snapshot/appconfigdeploymentstrategyDefaultTestDeployAssert3CECE452.assets.json new file mode 100644 index 0000000000000..22caef8fdee1b --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.deployment-strategy.js.snapshot/appconfigdeploymentstrategyDefaultTestDeployAssert3CECE452.assets.json @@ -0,0 +1,19 @@ +{ + "version": "32.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "appconfigdeploymentstrategyDefaultTestDeployAssert3CECE452.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.deployment-strategy.js.snapshot/appconfigdeploymentstrategyDefaultTestDeployAssert3CECE452.template.json b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.deployment-strategy.js.snapshot/appconfigdeploymentstrategyDefaultTestDeployAssert3CECE452.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.deployment-strategy.js.snapshot/appconfigdeploymentstrategyDefaultTestDeployAssert3CECE452.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.deployment-strategy.js.snapshot/aws-appconfig-deployment-strategy.assets.json b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.deployment-strategy.js.snapshot/aws-appconfig-deployment-strategy.assets.json new file mode 100644 index 0000000000000..4c181d5492583 --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.deployment-strategy.js.snapshot/aws-appconfig-deployment-strategy.assets.json @@ -0,0 +1,19 @@ +{ + "version": "32.0.0", + "files": { + "cfda20689bf14dbe95332f041046160d462f7705f59c5f25c14b30055c21a10e": { + "source": { + "path": "aws-appconfig-deployment-strategy.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "cfda20689bf14dbe95332f041046160d462f7705f59c5f25c14b30055c21a10e.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.deployment-strategy.js.snapshot/aws-appconfig-deployment-strategy.template.json b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.deployment-strategy.js.snapshot/aws-appconfig-deployment-strategy.template.json new file mode 100644 index 0000000000000..4d3c224d509fb --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.deployment-strategy.js.snapshot/aws-appconfig-deployment-strategy.template.json @@ -0,0 +1,48 @@ +{ + "Resources": { + "MyDeploymentStrategy60D31FB0": { + "Type": "AWS::AppConfig::DeploymentStrategy", + "Properties": { + "DeploymentDurationInMinutes": 5, + "GrowthFactor": 15, + "GrowthType": "LINEAR", + "Name": "awsappconfigdeploymentstrategy-MyDeploymentStrategy-9D0AC356", + "ReplicateTo": "NONE" + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.deployment-strategy.js.snapshot/cdk.out b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.deployment-strategy.js.snapshot/cdk.out new file mode 100644 index 0000000000000..f0b901e7c06e5 --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.deployment-strategy.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"32.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.deployment-strategy.js.snapshot/integ.json b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.deployment-strategy.js.snapshot/integ.json new file mode 100644 index 0000000000000..09fba1f5bd925 --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.deployment-strategy.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "32.0.0", + "testCases": { + "appconfig-deployment-strategy/DefaultTest": { + "stacks": [ + "aws-appconfig-deployment-strategy" + ], + "assertionStack": "appconfig-deployment-strategy/DefaultTest/DeployAssert", + "assertionStackName": "appconfigdeploymentstrategyDefaultTestDeployAssert3CECE452" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.deployment-strategy.js.snapshot/manifest.json b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.deployment-strategy.js.snapshot/manifest.json new file mode 100644 index 0000000000000..b0d6cfd05eb03 --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.deployment-strategy.js.snapshot/manifest.json @@ -0,0 +1,111 @@ +{ + "version": "32.0.0", + "artifacts": { + "aws-appconfig-deployment-strategy.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "aws-appconfig-deployment-strategy.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "aws-appconfig-deployment-strategy": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "aws-appconfig-deployment-strategy.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/cfda20689bf14dbe95332f041046160d462f7705f59c5f25c14b30055c21a10e.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "aws-appconfig-deployment-strategy.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "aws-appconfig-deployment-strategy.assets" + ], + "metadata": { + "/aws-appconfig-deployment-strategy/MyDeploymentStrategy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyDeploymentStrategy60D31FB0" + } + ], + "/aws-appconfig-deployment-strategy/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/aws-appconfig-deployment-strategy/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "aws-appconfig-deployment-strategy" + }, + "appconfigdeploymentstrategyDefaultTestDeployAssert3CECE452.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "appconfigdeploymentstrategyDefaultTestDeployAssert3CECE452.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "appconfigdeploymentstrategyDefaultTestDeployAssert3CECE452": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "appconfigdeploymentstrategyDefaultTestDeployAssert3CECE452.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "appconfigdeploymentstrategyDefaultTestDeployAssert3CECE452.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "appconfigdeploymentstrategyDefaultTestDeployAssert3CECE452.assets" + ], + "metadata": { + "/appconfig-deployment-strategy/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/appconfig-deployment-strategy/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "appconfig-deployment-strategy/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.deployment-strategy.js.snapshot/tree.json b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.deployment-strategy.js.snapshot/tree.json new file mode 100644 index 0000000000000..16d84ca6204f9 --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.deployment-strategy.js.snapshot/tree.json @@ -0,0 +1,129 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "aws-appconfig-deployment-strategy": { + "id": "aws-appconfig-deployment-strategy", + "path": "aws-appconfig-deployment-strategy", + "children": { + "MyDeploymentStrategy": { + "id": "MyDeploymentStrategy", + "path": "aws-appconfig-deployment-strategy/MyDeploymentStrategy", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-appconfig-deployment-strategy/MyDeploymentStrategy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::DeploymentStrategy", + "aws:cdk:cloudformation:props": { + "deploymentDurationInMinutes": 5, + "growthFactor": 15, + "growthType": "LINEAR", + "name": "awsappconfigdeploymentstrategy-MyDeploymentStrategy-9D0AC356", + "replicateTo": "NONE" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appconfig.CfnDeploymentStrategy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "aws-appconfig-deployment-strategy/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "aws-appconfig-deployment-strategy/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + }, + "appconfig-deployment-strategy": { + "id": "appconfig-deployment-strategy", + "path": "appconfig-deployment-strategy", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "appconfig-deployment-strategy/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "appconfig-deployment-strategy/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "appconfig-deployment-strategy/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "appconfig-deployment-strategy/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "appconfig-deployment-strategy/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.deployment-strategy.ts b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.deployment-strategy.ts new file mode 100644 index 0000000000000..0aadaeeab707e --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.deployment-strategy.ts @@ -0,0 +1,19 @@ +import { App, Duration, Stack } from 'aws-cdk-lib'; +import { DeploymentStrategy, RolloutStrategy } from '../lib'; +import { IntegTest } from '@aws-cdk/integ-tests-alpha'; + +const app = new App(); + +const stack = new Stack(app, 'aws-appconfig-deployment-strategy'); + +// create basic deployment strategy +new DeploymentStrategy(stack, 'MyDeploymentStrategy', { + rolloutStrategy: RolloutStrategy.linear({ + growthFactor: 15, + deploymentDuration: Duration.minutes(5), + }), +}); + +new IntegTest(app, 'appconfig-deployment-strategy', { + testCases: [stack], +}); diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.environment.js.snapshot/appconfigenvironmentDefaultTestDeployAssert75BD28E7.assets.json b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.environment.js.snapshot/appconfigenvironmentDefaultTestDeployAssert75BD28E7.assets.json new file mode 100644 index 0000000000000..e7f9aca08607f --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.environment.js.snapshot/appconfigenvironmentDefaultTestDeployAssert75BD28E7.assets.json @@ -0,0 +1,19 @@ +{ + "version": "32.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "appconfigenvironmentDefaultTestDeployAssert75BD28E7.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.environment.js.snapshot/appconfigenvironmentDefaultTestDeployAssert75BD28E7.template.json b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.environment.js.snapshot/appconfigenvironmentDefaultTestDeployAssert75BD28E7.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.environment.js.snapshot/appconfigenvironmentDefaultTestDeployAssert75BD28E7.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.environment.js.snapshot/aws-appconfig-environment.assets.json b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.environment.js.snapshot/aws-appconfig-environment.assets.json new file mode 100644 index 0000000000000..30512d7b22c0b --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.environment.js.snapshot/aws-appconfig-environment.assets.json @@ -0,0 +1,19 @@ +{ + "version": "32.0.0", + "files": { + "5a83c94c795349286c703ba61add79efe69c74c7adb8de98527fcf034b9683af": { + "source": { + "path": "aws-appconfig-environment.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "5a83c94c795349286c703ba61add79efe69c74c7adb8de98527fcf034b9683af.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.environment.js.snapshot/aws-appconfig-environment.template.json b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.environment.js.snapshot/aws-appconfig-environment.template.json new file mode 100644 index 0000000000000..78b08f894831d --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.environment.js.snapshot/aws-appconfig-environment.template.json @@ -0,0 +1,119 @@ +{ + "Resources": { + "MyApplicationForEnv1F597ED9": { + "Type": "AWS::AppConfig::Application", + "Properties": { + "Name": "AppForEnvTest" + } + }, + "MyAlarm696658B6": { + "Type": "AWS::CloudWatch::Alarm", + "Properties": { + "ComparisonOperator": "GreaterThanOrEqualToThreshold", + "EvaluationPeriods": 5, + "MetricName": "dummy name", + "Namespace": "aws", + "Period": 300, + "Statistic": "Average", + "Threshold": 10 + } + }, + "MyEnvironmentRoleC08961D3": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "appconfig.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": "cloudwatch:DescribeAlarms", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "MyAlarm696658B6", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "AllowAppConfigMonitorAlarmPolicy" + } + ] + } + }, + "MyEnvironment465E4DEA": { + "Type": "AWS::AppConfig::Environment", + "Properties": { + "ApplicationId": { + "Ref": "MyApplicationForEnv1F597ED9" + }, + "Description": "This is the environment for integ testing", + "Monitors": [ + { + "AlarmArn": { + "Fn::GetAtt": [ + "MyAlarm696658B6", + "Arn" + ] + }, + "AlarmRoleArn": { + "Fn::GetAtt": [ + "MyEnvironmentRoleC08961D3", + "Arn" + ] + } + } + ], + "Name": "awsappconfigenvironment-MyEnvironment-C8813182" + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.environment.js.snapshot/cdk.out b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.environment.js.snapshot/cdk.out new file mode 100644 index 0000000000000..f0b901e7c06e5 --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.environment.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"32.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.environment.js.snapshot/integ.json b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.environment.js.snapshot/integ.json new file mode 100644 index 0000000000000..4d4d0f1d20168 --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.environment.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "32.0.0", + "testCases": { + "appconfig-environment/DefaultTest": { + "stacks": [ + "aws-appconfig-environment" + ], + "assertionStack": "appconfig-environment/DefaultTest/DeployAssert", + "assertionStackName": "appconfigenvironmentDefaultTestDeployAssert75BD28E7" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.environment.js.snapshot/manifest.json b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.environment.js.snapshot/manifest.json new file mode 100644 index 0000000000000..0749986d2339c --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.environment.js.snapshot/manifest.json @@ -0,0 +1,129 @@ +{ + "version": "32.0.0", + "artifacts": { + "aws-appconfig-environment.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "aws-appconfig-environment.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "aws-appconfig-environment": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "aws-appconfig-environment.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/5a83c94c795349286c703ba61add79efe69c74c7adb8de98527fcf034b9683af.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "aws-appconfig-environment.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "aws-appconfig-environment.assets" + ], + "metadata": { + "/aws-appconfig-environment/MyApplicationForEnv/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyApplicationForEnv1F597ED9" + } + ], + "/aws-appconfig-environment/MyAlarm/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyAlarm696658B6" + } + ], + "/aws-appconfig-environment/MyEnvironment/Role/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyEnvironmentRoleC08961D3" + } + ], + "/aws-appconfig-environment/MyEnvironment/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyEnvironment465E4DEA" + } + ], + "/aws-appconfig-environment/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/aws-appconfig-environment/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "aws-appconfig-environment" + }, + "appconfigenvironmentDefaultTestDeployAssert75BD28E7.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "appconfigenvironmentDefaultTestDeployAssert75BD28E7.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "appconfigenvironmentDefaultTestDeployAssert75BD28E7": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "appconfigenvironmentDefaultTestDeployAssert75BD28E7.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "appconfigenvironmentDefaultTestDeployAssert75BD28E7.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "appconfigenvironmentDefaultTestDeployAssert75BD28E7.assets" + ], + "metadata": { + "/appconfig-environment/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/appconfig-environment/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "appconfig-environment/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.environment.js.snapshot/tree.json b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.environment.js.snapshot/tree.json new file mode 100644 index 0000000000000..f094a53e8edb1 --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.environment.js.snapshot/tree.json @@ -0,0 +1,262 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "aws-appconfig-environment": { + "id": "aws-appconfig-environment", + "path": "aws-appconfig-environment", + "children": { + "MyApplicationForEnv": { + "id": "MyApplicationForEnv", + "path": "aws-appconfig-environment/MyApplicationForEnv", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-appconfig-environment/MyApplicationForEnv/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::Application", + "aws:cdk:cloudformation:props": { + "name": "AppForEnvTest" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appconfig.CfnApplication", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "MyAlarm": { + "id": "MyAlarm", + "path": "aws-appconfig-environment/MyAlarm", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-appconfig-environment/MyAlarm/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::CloudWatch::Alarm", + "aws:cdk:cloudformation:props": { + "comparisonOperator": "GreaterThanOrEqualToThreshold", + "evaluationPeriods": 5, + "metricName": "dummy name", + "namespace": "aws", + "period": 300, + "statistic": "Average", + "threshold": 10 + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_cloudwatch.CfnAlarm", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_cloudwatch.Alarm", + "version": "0.0.0" + } + }, + "MyEnvironment": { + "id": "MyEnvironment", + "path": "aws-appconfig-environment/MyEnvironment", + "children": { + "Role": { + "id": "Role", + "path": "aws-appconfig-environment/MyEnvironment/Role", + "children": { + "ImportRole": { + "id": "ImportRole", + "path": "aws-appconfig-environment/MyEnvironment/Role/ImportRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-appconfig-environment/MyEnvironment/Role/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "appconfig.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "policies": [ + { + "policyName": "AllowAppConfigMonitorAlarmPolicy", + "policyDocument": { + "Statement": [ + { + "Action": "cloudwatch:DescribeAlarms", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "MyAlarm696658B6", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + } + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-appconfig-environment/MyEnvironment/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::Environment", + "aws:cdk:cloudformation:props": { + "applicationId": { + "Ref": "MyApplicationForEnv1F597ED9" + }, + "description": "This is the environment for integ testing", + "monitors": [ + { + "alarmArn": { + "Fn::GetAtt": [ + "MyAlarm696658B6", + "Arn" + ] + }, + "alarmRoleArn": { + "Fn::GetAtt": [ + "MyEnvironmentRoleC08961D3", + "Arn" + ] + } + } + ], + "name": "awsappconfigenvironment-MyEnvironment-C8813182" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appconfig.CfnEnvironment", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "aws-appconfig-environment/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "aws-appconfig-environment/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + }, + "appconfig-environment": { + "id": "appconfig-environment", + "path": "appconfig-environment", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "appconfig-environment/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "appconfig-environment/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "appconfig-environment/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "appconfig-environment/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "appconfig-environment/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.environment.ts b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.environment.ts new file mode 100644 index 0000000000000..2d19953e3d59c --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.environment.ts @@ -0,0 +1,36 @@ +import { IntegTest } from '@aws-cdk/integ-tests-alpha'; +import { App, Stack } from 'aws-cdk-lib'; +import { Application, Environment } from '../lib'; +import { Alarm, Metric } from 'aws-cdk-lib/aws-cloudwatch'; + +const app = new App(); + +const stack = new Stack(app, 'aws-appconfig-environment'); + +// create resources needed for environment +const appForEnv = new Application(stack, 'MyApplicationForEnv', { + name: 'AppForEnvTest', +}); +const alarm = new Alarm(stack, 'MyAlarm', { + metric: new Metric({ + namespace: 'aws', + metricName: 'dummy name', + }), + evaluationPeriods: 5, + threshold: 10, +}); + +// create environment with all props defined +new Environment(stack, 'MyEnvironment', { + application: appForEnv, + description: 'This is the environment for integ testing', + monitors: [ + { + alarm, + }, + ], +}); + +new IntegTest(app, 'appconfig-environment', { + testCases: [stack], +}); diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.extension.js.snapshot/aws-appconfig-extension.assets.json b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.extension.js.snapshot/aws-appconfig-extension.assets.json new file mode 100644 index 0000000000000..8336092ae5e5a --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.extension.js.snapshot/aws-appconfig-extension.assets.json @@ -0,0 +1,19 @@ +{ + "version": "32.0.0", + "files": { + "1b3fd18c823d863a4f13c60e5b79b85a1c059750ecd97aef1f9ea43c85d23de0": { + "source": { + "path": "aws-appconfig-extension.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "1b3fd18c823d863a4f13c60e5b79b85a1c059750ecd97aef1f9ea43c85d23de0.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.extension.js.snapshot/aws-appconfig-extension.template.json b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.extension.js.snapshot/aws-appconfig-extension.template.json new file mode 100644 index 0000000000000..9da058b2463bc --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.extension.js.snapshot/aws-appconfig-extension.template.json @@ -0,0 +1,454 @@ +{ + "Resources": { + "MyFunctionServiceRole3C357FF2": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "MyFunction3BAA72D1": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ZipFile": "def handler(event, context):\n\tprint('The function has been invoked.')" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "MyFunctionServiceRole3C357FF2", + "Arn" + ] + }, + "Runtime": "python3.8" + }, + "DependsOn": [ + "MyFunctionServiceRole3C357FF2" + ] + }, + "MyApplication5C63EC1D": { + "Type": "AWS::AppConfig::Application", + "Properties": { + "Name": "AppForExtensionTest" + } + }, + "MyApplicationMyEnv55DE3293": { + "Type": "AWS::AppConfig::Environment", + "Properties": { + "ApplicationId": { + "Ref": "MyApplication5C63EC1D" + }, + "Name": "awsappconfigextension-MyApplication-MyEnv-0FA5092F" + } + }, + "MyLambdaExtensionAFA1476A": { + "Type": "AWS::AppConfig::Extension", + "Properties": { + "Actions": { + "PRE_CREATE_HOSTED_CONFIGURATION_VERSION": [ + { + "Name": "awsappconfigextension-MyLambdaExtension-68C15290", + "Uri": { + "Fn::GetAtt": [ + "MyFunction3BAA72D1", + "Arn" + ] + } + } + ], + "ON_DEPLOYMENT_START": [ + { + "Name": "awsappconfigextension-MyLambdaExtension-68C15290", + "Uri": { + "Fn::GetAtt": [ + "MyFunction3BAA72D1", + "Arn" + ] + } + } + ] + }, + "Name": "awsappconfigextension-MyLambdaExtension-68C15290" + } + }, + "AssociationResource3FA55": { + "Type": "AWS::AppConfig::ExtensionAssociation", + "Properties": { + "ExtensionIdentifier": { + "Fn::GetAtt": [ + "MyLambdaExtensionAFA1476A", + "Id" + ] + }, + "ExtensionVersionNumber": { + "Fn::GetAtt": [ + "MyLambdaExtensionAFA1476A", + "VersionNumber" + ] + }, + "ResourceIdentifier": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":appconfig:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":application/", + { + "Ref": "MyApplication5C63EC1D" + } + ] + ] + } + } + }, + "MyQueueE6CA6235": { + "Type": "AWS::SQS::Queue", + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "MyQueueExtension872C5D27": { + "Type": "AWS::AppConfig::Extension", + "Properties": { + "Actions": { + "ON_DEPLOYMENT_START": [ + { + "Name": "awsappconfigextension-MyQueueExtension-EF6112FA", + "Uri": { + "Fn::GetAtt": [ + "MyQueueE6CA6235", + "Arn" + ] + } + } + ] + }, + "Name": "awsappconfigextension-MyQueueExtension-EF6112FA" + } + }, + "AssociationResourceBAC86": { + "Type": "AWS::AppConfig::ExtensionAssociation", + "Properties": { + "ExtensionIdentifier": { + "Fn::GetAtt": [ + "MyQueueExtension872C5D27", + "Id" + ] + }, + "ExtensionVersionNumber": { + "Fn::GetAtt": [ + "MyQueueExtension872C5D27", + "VersionNumber" + ] + }, + "ResourceIdentifier": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":appconfig:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":application/", + { + "Ref": "MyApplication5C63EC1D" + } + ] + ] + } + } + }, + "MyTopic86869434": { + "Type": "AWS::SNS::Topic" + }, + "MyTopicExtension9B6DF691": { + "Type": "AWS::AppConfig::Extension", + "Properties": { + "Actions": { + "ON_DEPLOYMENT_START": [ + { + "Name": "awsappconfigextension-MyTopicExtension-37440DA2", + "Uri": { + "Ref": "MyTopic86869434" + } + } + ] + }, + "Name": "awsappconfigextension-MyTopicExtension-37440DA2" + } + }, + "AssociationResource7F3E1": { + "Type": "AWS::AppConfig::ExtensionAssociation", + "Properties": { + "ExtensionIdentifier": { + "Fn::GetAtt": [ + "MyTopicExtension9B6DF691", + "Id" + ] + }, + "ExtensionVersionNumber": { + "Fn::GetAtt": [ + "MyTopicExtension9B6DF691", + "VersionNumber" + ] + }, + "ResourceIdentifier": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":appconfig:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":application/", + { + "Ref": "MyApplication5C63EC1D" + } + ] + ] + } + } + }, + "MyEventBusExtensionADFE2273": { + "Type": "AWS::AppConfig::Extension", + "Properties": { + "Actions": { + "ON_DEPLOYMENT_START": [ + { + "Name": "MyEventBusPreHostedConfigVersionAction", + "Uri": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":events:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":event-bus/default" + ] + ] + }, + "Description": "My event bus action" + } + ] + }, + "Name": "awsappconfigextension-MyEventBusExtension-39114CFE", + "Parameters": { + "testParam": { + "Required": true + }, + "testNotRequiredParam": { + "Required": false + } + } + } + }, + "AssociationResource689DE": { + "Type": "AWS::AppConfig::ExtensionAssociation", + "Properties": { + "ExtensionIdentifier": { + "Fn::GetAtt": [ + "MyEventBusExtensionADFE2273", + "Id" + ] + }, + "ExtensionVersionNumber": { + "Fn::GetAtt": [ + "MyEventBusExtensionADFE2273", + "VersionNumber" + ] + }, + "Parameters": { + "testParam": "true" + }, + "ResourceIdentifier": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":appconfig:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":application/", + { + "Ref": "MyApplication5C63EC1D" + } + ] + ] + } + } + }, + "MyDeployStrategy062CAEA2": { + "Type": "AWS::AppConfig::DeploymentStrategy", + "Properties": { + "DeploymentDurationInMinutes": 0, + "GrowthFactor": 100, + "GrowthType": "LINEAR", + "Name": "awsappconfigextension-MyDeployStrategy-61F99D9B", + "ReplicateTo": "NONE" + } + }, + "HostedConfigurationConfigurationProfile3AB17BEA": { + "Type": "AWS::AppConfig::ConfigurationProfile", + "Properties": { + "ApplicationId": { + "Ref": "MyApplication5C63EC1D" + }, + "LocationUri": "hosted", + "Name": "awsappconfigextension-HostedConfiguration-15AED6EE" + }, + "DependsOn": [ + "MyEventBusExtensionADFE2273", + "MyLambdaExtensionAFA1476A", + "MyQueueExtension872C5D27", + "MyTopicExtension9B6DF691" + ] + }, + "HostedConfiguration257746B4": { + "Type": "AWS::AppConfig::HostedConfigurationVersion", + "Properties": { + "ApplicationId": { + "Ref": "MyApplication5C63EC1D" + }, + "ConfigurationProfileId": { + "Ref": "HostedConfigurationConfigurationProfile3AB17BEA" + }, + "Content": "This is my configuration content", + "ContentType": "application/json" + }, + "DependsOn": [ + "MyEventBusExtensionADFE2273", + "MyLambdaExtensionAFA1476A", + "MyQueueExtension872C5D27", + "MyTopicExtension9B6DF691" + ], + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "HostedConfigurationDeploymentA86BAE6C77C59": { + "Type": "AWS::AppConfig::Deployment", + "Properties": { + "ApplicationId": { + "Ref": "MyApplication5C63EC1D" + }, + "ConfigurationProfileId": { + "Ref": "HostedConfigurationConfigurationProfile3AB17BEA" + }, + "ConfigurationVersion": { + "Ref": "HostedConfiguration257746B4" + }, + "DeploymentStrategyId": { + "Ref": "MyDeployStrategy062CAEA2" + }, + "EnvironmentId": { + "Ref": "MyApplicationMyEnv55DE3293" + } + }, + "DependsOn": [ + "MyEventBusExtensionADFE2273", + "MyLambdaExtensionAFA1476A", + "MyQueueExtension872C5D27", + "MyTopicExtension9B6DF691" + ] + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.extension.js.snapshot/awsappconfigextensionMyApplicationappconfigextensionDefaultTestDeployAssert64BA6C4E.assets.json b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.extension.js.snapshot/awsappconfigextensionMyApplicationappconfigextensionDefaultTestDeployAssert64BA6C4E.assets.json new file mode 100644 index 0000000000000..635d1e188e6e3 --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.extension.js.snapshot/awsappconfigextensionMyApplicationappconfigextensionDefaultTestDeployAssert64BA6C4E.assets.json @@ -0,0 +1,19 @@ +{ + "version": "32.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "awsappconfigextensionMyApplicationappconfigextensionDefaultTestDeployAssert64BA6C4E.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.extension.js.snapshot/awsappconfigextensionMyApplicationappconfigextensionDefaultTestDeployAssert64BA6C4E.template.json b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.extension.js.snapshot/awsappconfigextensionMyApplicationappconfigextensionDefaultTestDeployAssert64BA6C4E.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.extension.js.snapshot/awsappconfigextensionMyApplicationappconfigextensionDefaultTestDeployAssert64BA6C4E.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.extension.js.snapshot/cdk.out b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.extension.js.snapshot/cdk.out new file mode 100644 index 0000000000000..f0b901e7c06e5 --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.extension.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"32.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.extension.js.snapshot/integ.json b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.extension.js.snapshot/integ.json new file mode 100644 index 0000000000000..f615688162cdc --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.extension.js.snapshot/integ.json @@ -0,0 +1,19 @@ +{ + "version": "32.0.0", + "testCases": { + "aws-appconfig-extension/MyApplication/appconfig-extension/DefaultTest": { + "stacks": [ + "aws-appconfig-extension" + ], + "cdkCommandOptions": { + "destroy": { + "args": { + "force": true + } + } + }, + "assertionStack": "aws-appconfig-extension/MyApplication/appconfig-extension/DefaultTest/DeployAssert", + "assertionStackName": "awsappconfigextensionMyApplicationappconfigextensionDefaultTestDeployAssert64BA6C4E" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.extension.js.snapshot/manifest.json b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.extension.js.snapshot/manifest.json new file mode 100644 index 0000000000000..e5aeb013fdaa8 --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.extension.js.snapshot/manifest.json @@ -0,0 +1,213 @@ +{ + "version": "32.0.0", + "artifacts": { + "awsappconfigextensionMyApplicationappconfigextensionDefaultTestDeployAssert64BA6C4E.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "awsappconfigextensionMyApplicationappconfigextensionDefaultTestDeployAssert64BA6C4E.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "awsappconfigextensionMyApplicationappconfigextensionDefaultTestDeployAssert64BA6C4E": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "awsappconfigextensionMyApplicationappconfigextensionDefaultTestDeployAssert64BA6C4E.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "awsappconfigextensionMyApplicationappconfigextensionDefaultTestDeployAssert64BA6C4E.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "awsappconfigextensionMyApplicationappconfigextensionDefaultTestDeployAssert64BA6C4E.assets" + ], + "metadata": { + "/aws-appconfig-extension/MyApplication/appconfig-extension/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/aws-appconfig-extension/MyApplication/appconfig-extension/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "aws-appconfig-extension/MyApplication/appconfig-extension/DefaultTest/DeployAssert" + }, + "aws-appconfig-extension.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "aws-appconfig-extension.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "aws-appconfig-extension": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "aws-appconfig-extension.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/1b3fd18c823d863a4f13c60e5b79b85a1c059750ecd97aef1f9ea43c85d23de0.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "aws-appconfig-extension.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "aws-appconfig-extension.assets" + ], + "metadata": { + "/aws-appconfig-extension/MyFunction/ServiceRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyFunctionServiceRole3C357FF2" + } + ], + "/aws-appconfig-extension/MyFunction/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyFunction3BAA72D1" + } + ], + "/aws-appconfig-extension/MyApplication/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyApplication5C63EC1D" + } + ], + "/aws-appconfig-extension/MyApplication/MyEnv/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyApplicationMyEnv55DE3293" + } + ], + "/aws-appconfig-extension/MyLambdaExtension/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyLambdaExtensionAFA1476A" + } + ], + "/aws-appconfig-extension/AssociationResource3FA55": [ + { + "type": "aws:cdk:logicalId", + "data": "AssociationResource3FA55" + } + ], + "/aws-appconfig-extension/MyQueue/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyQueueE6CA6235" + } + ], + "/aws-appconfig-extension/MyQueueExtension/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyQueueExtension872C5D27" + } + ], + "/aws-appconfig-extension/AssociationResourceBAC86": [ + { + "type": "aws:cdk:logicalId", + "data": "AssociationResourceBAC86" + } + ], + "/aws-appconfig-extension/MyTopic/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyTopic86869434" + } + ], + "/aws-appconfig-extension/MyTopicExtension/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyTopicExtension9B6DF691" + } + ], + "/aws-appconfig-extension/AssociationResource7F3E1": [ + { + "type": "aws:cdk:logicalId", + "data": "AssociationResource7F3E1" + } + ], + "/aws-appconfig-extension/MyEventBusExtension/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyEventBusExtensionADFE2273" + } + ], + "/aws-appconfig-extension/AssociationResource689DE": [ + { + "type": "aws:cdk:logicalId", + "data": "AssociationResource689DE" + } + ], + "/aws-appconfig-extension/MyDeployStrategy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyDeployStrategy062CAEA2" + } + ], + "/aws-appconfig-extension/HostedConfiguration/ConfigurationProfile": [ + { + "type": "aws:cdk:logicalId", + "data": "HostedConfigurationConfigurationProfile3AB17BEA" + } + ], + "/aws-appconfig-extension/HostedConfiguration/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "HostedConfiguration257746B4" + } + ], + "/aws-appconfig-extension/HostedConfiguration/DeploymentA86BA": [ + { + "type": "aws:cdk:logicalId", + "data": "HostedConfigurationDeploymentA86BAE6C77C59" + } + ], + "/aws-appconfig-extension/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/aws-appconfig-extension/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "aws-appconfig-extension" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.extension.js.snapshot/tree.json b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.extension.js.snapshot/tree.json new file mode 100644 index 0000000000000..578580b4ce0e5 --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.extension.js.snapshot/tree.json @@ -0,0 +1,774 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "aws-appconfig-extension": { + "id": "aws-appconfig-extension", + "path": "aws-appconfig-extension", + "children": { + "MyFunction": { + "id": "MyFunction", + "path": "aws-appconfig-extension/MyFunction", + "children": { + "ServiceRole": { + "id": "ServiceRole", + "path": "aws-appconfig-extension/MyFunction/ServiceRole", + "children": { + "ImportServiceRole": { + "id": "ImportServiceRole", + "path": "aws-appconfig-extension/MyFunction/ServiceRole/ImportServiceRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-appconfig-extension/MyFunction/ServiceRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "managedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-appconfig-extension/MyFunction/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Lambda::Function", + "aws:cdk:cloudformation:props": { + "code": { + "zipFile": "def handler(event, context):\n\tprint('The function has been invoked.')" + }, + "handler": "index.handler", + "role": { + "Fn::GetAtt": [ + "MyFunctionServiceRole3C357FF2", + "Arn" + ] + }, + "runtime": "python3.8" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_lambda.CfnFunction", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_lambda.Function", + "version": "0.0.0" + } + }, + "MyApplication": { + "id": "MyApplication", + "path": "aws-appconfig-extension/MyApplication", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-appconfig-extension/MyApplication/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::Application", + "aws:cdk:cloudformation:props": { + "name": "AppForExtensionTest" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appconfig.CfnApplication", + "version": "0.0.0" + } + }, + "MyEnv": { + "id": "MyEnv", + "path": "aws-appconfig-extension/MyApplication/MyEnv", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-appconfig-extension/MyApplication/MyEnv/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::Environment", + "aws:cdk:cloudformation:props": { + "applicationId": { + "Ref": "MyApplication5C63EC1D" + }, + "name": "awsappconfigextension-MyApplication-MyEnv-0FA5092F" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appconfig.CfnEnvironment", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "appconfig-extension": { + "id": "appconfig-extension", + "path": "aws-appconfig-extension/MyApplication/appconfig-extension", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "aws-appconfig-extension/MyApplication/appconfig-extension/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "aws-appconfig-extension/MyApplication/appconfig-extension/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "aws-appconfig-extension/MyApplication/appconfig-extension/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "aws-appconfig-extension/MyApplication/appconfig-extension/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "aws-appconfig-extension/MyApplication/appconfig-extension/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "MyLambdaExtension": { + "id": "MyLambdaExtension", + "path": "aws-appconfig-extension/MyLambdaExtension", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-appconfig-extension/MyLambdaExtension/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::Extension", + "aws:cdk:cloudformation:props": { + "actions": { + "PRE_CREATE_HOSTED_CONFIGURATION_VERSION": [ + { + "Name": "awsappconfigextension-MyLambdaExtension-68C15290", + "Uri": { + "Fn::GetAtt": [ + "MyFunction3BAA72D1", + "Arn" + ] + } + } + ], + "ON_DEPLOYMENT_START": [ + { + "Name": "awsappconfigextension-MyLambdaExtension-68C15290", + "Uri": { + "Fn::GetAtt": [ + "MyFunction3BAA72D1", + "Arn" + ] + } + } + ] + }, + "name": "awsappconfigextension-MyLambdaExtension-68C15290" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appconfig.CfnExtension", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "AssociationResource3FA55": { + "id": "AssociationResource3FA55", + "path": "aws-appconfig-extension/AssociationResource3FA55", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::ExtensionAssociation", + "aws:cdk:cloudformation:props": { + "extensionIdentifier": { + "Fn::GetAtt": [ + "MyLambdaExtensionAFA1476A", + "Id" + ] + }, + "extensionVersionNumber": { + "Fn::GetAtt": [ + "MyLambdaExtensionAFA1476A", + "VersionNumber" + ] + }, + "resourceIdentifier": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":appconfig:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":application/", + { + "Ref": "MyApplication5C63EC1D" + } + ] + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appconfig.CfnExtensionAssociation", + "version": "0.0.0" + } + }, + "MyQueue": { + "id": "MyQueue", + "path": "aws-appconfig-extension/MyQueue", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-appconfig-extension/MyQueue/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::SQS::Queue", + "aws:cdk:cloudformation:props": {} + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_sqs.CfnQueue", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_sqs.Queue", + "version": "0.0.0" + } + }, + "MyQueueExtension": { + "id": "MyQueueExtension", + "path": "aws-appconfig-extension/MyQueueExtension", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-appconfig-extension/MyQueueExtension/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::Extension", + "aws:cdk:cloudformation:props": { + "actions": { + "ON_DEPLOYMENT_START": [ + { + "Name": "awsappconfigextension-MyQueueExtension-EF6112FA", + "Uri": { + "Fn::GetAtt": [ + "MyQueueE6CA6235", + "Arn" + ] + } + } + ] + }, + "name": "awsappconfigextension-MyQueueExtension-EF6112FA" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appconfig.CfnExtension", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "AssociationResourceBAC86": { + "id": "AssociationResourceBAC86", + "path": "aws-appconfig-extension/AssociationResourceBAC86", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::ExtensionAssociation", + "aws:cdk:cloudformation:props": { + "extensionIdentifier": { + "Fn::GetAtt": [ + "MyQueueExtension872C5D27", + "Id" + ] + }, + "extensionVersionNumber": { + "Fn::GetAtt": [ + "MyQueueExtension872C5D27", + "VersionNumber" + ] + }, + "resourceIdentifier": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":appconfig:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":application/", + { + "Ref": "MyApplication5C63EC1D" + } + ] + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appconfig.CfnExtensionAssociation", + "version": "0.0.0" + } + }, + "MyTopic": { + "id": "MyTopic", + "path": "aws-appconfig-extension/MyTopic", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-appconfig-extension/MyTopic/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::SNS::Topic", + "aws:cdk:cloudformation:props": {} + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_sns.CfnTopic", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_sns.Topic", + "version": "0.0.0" + } + }, + "MyTopicExtension": { + "id": "MyTopicExtension", + "path": "aws-appconfig-extension/MyTopicExtension", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-appconfig-extension/MyTopicExtension/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::Extension", + "aws:cdk:cloudformation:props": { + "actions": { + "ON_DEPLOYMENT_START": [ + { + "Name": "awsappconfigextension-MyTopicExtension-37440DA2", + "Uri": { + "Ref": "MyTopic86869434" + } + } + ] + }, + "name": "awsappconfigextension-MyTopicExtension-37440DA2" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appconfig.CfnExtension", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "AssociationResource7F3E1": { + "id": "AssociationResource7F3E1", + "path": "aws-appconfig-extension/AssociationResource7F3E1", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::ExtensionAssociation", + "aws:cdk:cloudformation:props": { + "extensionIdentifier": { + "Fn::GetAtt": [ + "MyTopicExtension9B6DF691", + "Id" + ] + }, + "extensionVersionNumber": { + "Fn::GetAtt": [ + "MyTopicExtension9B6DF691", + "VersionNumber" + ] + }, + "resourceIdentifier": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":appconfig:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":application/", + { + "Ref": "MyApplication5C63EC1D" + } + ] + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appconfig.CfnExtensionAssociation", + "version": "0.0.0" + } + }, + "MyEventBus": { + "id": "MyEventBus", + "path": "aws-appconfig-extension/MyEventBus", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "MyEventBusExtension": { + "id": "MyEventBusExtension", + "path": "aws-appconfig-extension/MyEventBusExtension", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-appconfig-extension/MyEventBusExtension/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::Extension", + "aws:cdk:cloudformation:props": { + "actions": { + "ON_DEPLOYMENT_START": [ + { + "Name": "MyEventBusPreHostedConfigVersionAction", + "Uri": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":events:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":event-bus/default" + ] + ] + }, + "Description": "My event bus action" + } + ] + }, + "name": "awsappconfigextension-MyEventBusExtension-39114CFE", + "parameters": { + "testParam": { + "required": true + }, + "testNotRequiredParam": { + "required": false + } + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appconfig.CfnExtension", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "AssociationResource689DE": { + "id": "AssociationResource689DE", + "path": "aws-appconfig-extension/AssociationResource689DE", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::ExtensionAssociation", + "aws:cdk:cloudformation:props": { + "extensionIdentifier": { + "Fn::GetAtt": [ + "MyEventBusExtensionADFE2273", + "Id" + ] + }, + "extensionVersionNumber": { + "Fn::GetAtt": [ + "MyEventBusExtensionADFE2273", + "VersionNumber" + ] + }, + "parameters": { + "testParam": "true" + }, + "resourceIdentifier": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":appconfig:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":application/", + { + "Ref": "MyApplication5C63EC1D" + } + ] + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appconfig.CfnExtensionAssociation", + "version": "0.0.0" + } + }, + "MyDeployStrategy": { + "id": "MyDeployStrategy", + "path": "aws-appconfig-extension/MyDeployStrategy", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-appconfig-extension/MyDeployStrategy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::DeploymentStrategy", + "aws:cdk:cloudformation:props": { + "deploymentDurationInMinutes": 0, + "growthFactor": 100, + "growthType": "LINEAR", + "name": "awsappconfigextension-MyDeployStrategy-61F99D9B", + "replicateTo": "NONE" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appconfig.CfnDeploymentStrategy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "HostedConfiguration": { + "id": "HostedConfiguration", + "path": "aws-appconfig-extension/HostedConfiguration", + "children": { + "ConfigurationProfile": { + "id": "ConfigurationProfile", + "path": "aws-appconfig-extension/HostedConfiguration/ConfigurationProfile", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::ConfigurationProfile", + "aws:cdk:cloudformation:props": { + "applicationId": { + "Ref": "MyApplication5C63EC1D" + }, + "locationUri": "hosted", + "name": "awsappconfigextension-HostedConfiguration-15AED6EE" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appconfig.CfnConfigurationProfile", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-appconfig-extension/HostedConfiguration/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::HostedConfigurationVersion", + "aws:cdk:cloudformation:props": { + "applicationId": { + "Ref": "MyApplication5C63EC1D" + }, + "configurationProfileId": { + "Ref": "HostedConfigurationConfigurationProfile3AB17BEA" + }, + "content": "This is my configuration content", + "contentType": "application/json" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appconfig.CfnHostedConfigurationVersion", + "version": "0.0.0" + } + }, + "DeploymentA86BA": { + "id": "DeploymentA86BA", + "path": "aws-appconfig-extension/HostedConfiguration/DeploymentA86BA", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::Deployment", + "aws:cdk:cloudformation:props": { + "applicationId": { + "Ref": "MyApplication5C63EC1D" + }, + "configurationProfileId": { + "Ref": "HostedConfigurationConfigurationProfile3AB17BEA" + }, + "configurationVersion": { + "Ref": "HostedConfiguration257746B4" + }, + "deploymentStrategyId": { + "Ref": "MyDeployStrategy062CAEA2" + }, + "environmentId": { + "Ref": "MyApplicationMyEnv55DE3293" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appconfig.CfnDeployment", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "aws-appconfig-extension/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "aws-appconfig-extension/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/integ.extension.ts b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.extension.ts new file mode 100755 index 0000000000000..65ff5986da603 --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/integ.extension.ts @@ -0,0 +1,125 @@ +import { Stack, App, Duration } from 'aws-cdk-lib'; +import { EventBus } from 'aws-cdk-lib/aws-events'; +import { Code, Function, Runtime } from 'aws-cdk-lib/aws-lambda'; +import { Topic } from 'aws-cdk-lib/aws-sns'; +import { IntegTest } from '@aws-cdk/integ-tests-alpha'; +import { Queue } from 'aws-cdk-lib/aws-sqs'; +import { + DeploymentStrategy, + Extension, + Action, + ActionPoint, + Parameter, + Application, + RolloutStrategy, + HostedConfiguration, + ConfigurationContent, + LambdaDestination, + SqsDestination, + SnsDestination, + EventBridgeDestination, +} from '../lib'; + +const cdkApp = new App(); + +const stack = new Stack(cdkApp, 'aws-appconfig-extension'); + +// create extension through lambda +const lambda = new Function(stack, 'MyFunction', { + runtime: Runtime.PYTHON_3_8, + handler: 'index.handler', + code: Code.fromInline('def handler(event, context):\n\tprint(\'The function has been invoked.\')'), +}); +const app = new Application(stack, 'MyApplication', { + name: 'AppForExtensionTest', +}); +const lambdaExtension = new Extension(stack, 'MyLambdaExtension', { + actions: [ + new Action({ + actionPoints: [ + ActionPoint.PRE_CREATE_HOSTED_CONFIGURATION_VERSION, + ActionPoint.ON_DEPLOYMENT_START, + ], + eventDestination: new LambdaDestination(lambda), + invokeWithoutExecutionRole: true, + }), + ], +}); +app.addExtension(lambdaExtension); + +// create extension through sqs queue +const queue = new Queue(stack, 'MyQueue'); +const queueExtension = new Extension(stack, 'MyQueueExtension', { + actions: [ + new Action({ + actionPoints: [ + ActionPoint.ON_DEPLOYMENT_START, + ], + eventDestination: new SqsDestination(queue), + invokeWithoutExecutionRole: true, + }), + ], +}); +app.addExtension(queueExtension); + +// create extension through sns topic +const topic = new Topic(stack, 'MyTopic'); +const topicExtension = new Extension(stack, 'MyTopicExtension', { + actions: [ + new Action({ + actionPoints: [ + ActionPoint.ON_DEPLOYMENT_START, + ], + eventDestination: new SnsDestination(topic), + invokeWithoutExecutionRole: true, + }), + ], +}); +app.addExtension(topicExtension); + +// create extension through event bus (with parameters) +const bus = EventBus.fromEventBusName(stack, 'MyEventBus', 'default'); +const busExtension = new Extension(stack, 'MyEventBusExtension', { + actions: [ + new Action({ + actionPoints: [ + ActionPoint.ON_DEPLOYMENT_START, + ], + eventDestination: new EventBridgeDestination(bus), + description: 'My event bus action', + name: 'MyEventBusPreHostedConfigVersionAction', + invokeWithoutExecutionRole: true, + }), + ], + parameters: [ + Parameter.required('testParam', 'true'), + Parameter.notRequired('testNotRequiredParam'), + ], +}); +app.addExtension(busExtension); + +// invoke the extension actions +const env = app.addEnvironment('MyEnv'); +const hostedConfig = new HostedConfiguration(stack, 'HostedConfiguration', { + application: app, + content: ConfigurationContent.fromInline('This is my configuration content'), + deployTo: [env], + deploymentStrategy: new DeploymentStrategy(stack, 'MyDeployStrategy', { + rolloutStrategy: RolloutStrategy.linear({ + growthFactor: 100, + deploymentDuration: Duration.minutes(0), + }), + }), +}); +hostedConfig.node.addDependency(lambdaExtension, topicExtension, busExtension, queueExtension); + +new IntegTest(app, 'appconfig-extension', { + testCases: [stack], + cdkCommandOptions: { + destroy: { + args: { + force: true, + }, + }, + }, +}); diff --git a/packages/@aws-cdk/aws-appconfig-alpha/test/schema.json b/packages/@aws-cdk/aws-appconfig-alpha/test/schema.json new file mode 100644 index 0000000000000..290e9cca57d08 --- /dev/null +++ b/packages/@aws-cdk/aws-appconfig-alpha/test/schema.json @@ -0,0 +1,4 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "string" +}