diff --git a/.github/workflows/doc-builder.yml b/.github/workflows/doc-builder.yml index c2239ccfb..6ef8917ad 100644 --- a/.github/workflows/doc-builder.yml +++ b/.github/workflows/doc-builder.yml @@ -3,7 +3,6 @@ on: push: branches: - main - - dev # Allow the workflow to be triggered also manually. workflow_dispatch: diff --git a/buildtools/ci.template.yml b/buildtools/ci.template.yml index f967cf09d..33a371849 100644 --- a/buildtools/ci.template.yml +++ b/buildtools/ci.template.yml @@ -15,10 +15,6 @@ Parameters: Description: Name of the CodeBuild project. Default: "aws-dotnet-deploy-ci" Type: String - CodeBuildArtifactsBucketName: - Description: Name of the buckets where the CodeBuild artifacts will be stored. - Default: "aws-dotnet-deploy-codebuild-artifacts" - Type: String TestRunnerRoleArn: Description: Role to assume when running tests. This role must already exsit. Role can be a different account. Example arn:aws:iam:112233445566::role/awsdotnet-deploy-ci-test-runner Default: "" @@ -67,12 +63,6 @@ Resources: - logs:GetLogEvents Resource: - !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/${CodeBuildProjectName}:* - - Effect: Allow - Action: - - s3:GetObject - - s3:GetObjectVersion - Resource: - - !Sub "${CodeBuildArtifactsBucket.Arn}/*" GithubOidc: Type: AWS::IAM::OIDCProvider @@ -105,11 +95,7 @@ Resources: Location: !Sub https://github.com/${GitHubOrg}/${GitHubRepositoryName} BuildSpec: buildtools/ci.buildspec.yml Artifacts: - Type: S3 - Packaging: ZIP - Location: !GetAtt CodeBuildArtifactsBucket.Arn - OverrideArtifactName: true - + Type: NO_ARTIFACTS CodeBuildProjectRole: Type: AWS::IAM::Role @@ -150,17 +136,6 @@ Resources: Effect: Allow Resource: - !Sub arn:aws:codebuild:${AWS::Region}:${AWS::AccountId}:report-group/* - - Action: - - 's3:PutObject' - Effect: Allow - Resource: - - !Sub "${CodeBuildArtifactsBucket.Arn}/*" - - CodeBuildArtifactsBucket: - Type: 'AWS::S3::Bucket' - DeletionPolicy: Retain - Properties: - BucketName: !Ref CodeBuildArtifactsBucketName Outputs: OidcRole: diff --git a/mkdocs.yml b/mkdocs.yml index b75b360b1..6a6a4a796 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -18,12 +18,6 @@ nav: - Set up custom workspace: docs/getting-started/custom-workspace.md - Deploy "Hello World": docs/getting-started/run-tool.md - Support matrix: docs/support.md - # - Supported deployments: - # - Support matrix: docs/aws-computes/matrix.md - # - Amazon ECS: docs/aws-computes/ecs.md - # - AWS Elastic Beanstalk: docs/aws-computes/beanstalk.md - # - AWS App Runner: docs/aws-computes/app-runner.md - # - Amazon S3 and Amazon CloudFront: docs/aws-computes/s3.md - Commands: - deploy: docs/commands/deploy.md - list-deployments: docs/commands/list.md @@ -33,24 +27,13 @@ nav: - Docker support: - Dockerfile generation: docs/docker/docker-generation.md - Publishing a Docker image: docs/docker/publish-image.md - - Deployment recipes: docs/recipe.md - - Deployment projects: docs/project.md - # - Generating CDK project: docs/projects/overview.md - # - Customizing CDK project: docs/projects/custom-project.md + - Deployment Projects: + - Overview: docs/deployment-projects/index.md + - Recipe File: docs/deployment-projects/recipe-file.md + - CDK Project: docs/deployment-projects/cdk-project.md - Integrating with CI/CD: docs/cicd.md - # - Overview: docs/cicd/overview.md - # - Suppress prompts with --silent: docs/cicd/silent.md - # - Deployment settings file: docs/cicd/settings.md - - #- Tutorials: - # - Deploying ASP.NET Core Application: docs/tutorials/deploy-webapp.md - # - Deploying Blazor WebAssembly Application: docs/tutorials/deploy-blazorapp.md - # - Deploying Console Service: docs/tutorials/deploy-console-service.md - # - Deploying Console Task: docs/tutorials/deploy-console-task.md - # - Pushing Image to ECS: docs/tutorials/push-image-ecr.md - # - Automating Deployments: docs/tutorials/automate-deployments.md - # - Listing Deployments: docs/tutorials/list-deployments.md - # - Deleting Deployment: docs/tutorials/delete-deployment.md + - Tutorials: + - Customizing deployment projects: tutorials/custom-project.md - Troubleshooting Guide: - troubleshooting-guide/index.md - Missing Dependencies: troubleshooting-guide/missing-dependencies.md @@ -74,4 +57,4 @@ theme: markdown_extensions: - pymdownx.emoji: - emoji_index: !!python/name:materialx.emoji.twemoji \ No newline at end of file + emoji_index: !!python/name:materialx.emoji.twemoji diff --git a/site/content/assets/images/cliAfter.png b/site/content/assets/images/cliAfter.png new file mode 100644 index 000000000..365118f38 Binary files /dev/null and b/site/content/assets/images/cliAfter.png differ diff --git a/site/content/assets/images/cliAfterBorder.png b/site/content/assets/images/cliAfterBorder.png new file mode 100644 index 000000000..b2b688377 Binary files /dev/null and b/site/content/assets/images/cliAfterBorder.png differ diff --git a/site/content/assets/images/cliBefore.png b/site/content/assets/images/cliBefore.png new file mode 100644 index 000000000..995b0e320 Binary files /dev/null and b/site/content/assets/images/cliBefore.png differ diff --git a/site/content/assets/images/custom-deployment-project.PNG b/site/content/assets/images/custom-deployment-project.PNG new file mode 100644 index 000000000..c7d3faeee Binary files /dev/null and b/site/content/assets/images/custom-deployment-project.PNG differ diff --git a/site/content/assets/images/deployment-project-file-layout.png b/site/content/assets/images/deployment-project-file-layout.png new file mode 100644 index 000000000..e10fd8a89 Binary files /dev/null and b/site/content/assets/images/deployment-project-file-layout.png differ diff --git a/site/content/assets/images/solutionWithCDKProject.png b/site/content/assets/images/solutionWithCDKProject.png new file mode 100644 index 000000000..6e8626f2d Binary files /dev/null and b/site/content/assets/images/solutionWithCDKProject.png differ diff --git a/site/content/assets/images/vs-catagories.png b/site/content/assets/images/vs-catagories.png new file mode 100644 index 000000000..784439fdb Binary files /dev/null and b/site/content/assets/images/vs-catagories.png differ diff --git a/site/content/docs/cicd.md b/site/content/docs/cicd.md index 08d14d46d..e49292bf3 100644 --- a/site/content/docs/cicd.md +++ b/site/content/docs/cicd.md @@ -14,10 +14,6 @@ To specify the services to deploy and their configurations for your environment, Storing deployment settings in a JSON file also allows those settings to be version controlled. -This section defines the JSON definitions and syntax that you construct and use a deployment settings file. - -TODO - ### Invoking from CI/CD The `--apply` switch on deploy command allows you to specify a deployment settings file. diff --git a/site/content/docs/commands/project.md b/site/content/docs/commands/project.md index ce07fa4a2..40165ea6a 100644 --- a/site/content/docs/commands/project.md +++ b/site/content/docs/commands/project.md @@ -7,9 +7,14 @@ dotnet aws deployment-project generate [-d|--diagnostics] [-s|--silent] [--profile ] [--region ] [--project-path ] [--project-display-name ] ### Description -Generates and saves the deployment CDK project in a user provided directory path without proceeding with a deployment. Allows user to customize the CDK project before deploying the application. +Generates and saves the [deployment CDK project](../deployment-projects/index.md) in a user provided directory path without proceeding with a deployment. Allows user to customize the CDK project before deploying the application. + +* The `--output` switch sets the directory where the deployment project will be saved. +* The `--project-display-name` switch sets the name that will be shown when the .NET project is being deployed. ### Examples -``` -dotnet aws deployment-project generate --region us-west-2 -``` \ No newline at end of file + +This example creates a deployment project from the .NET project in the current directory. The deployment project will be saved to a sibling directory called CustomDeploymentProject. The name _"Team custom deployment project"_ will be displayed in the list of the available deployment options. + + dotnet aws deployment-project generate --output ../CustomDeploymentProject --project-display-name "Team custom deployment project" + diff --git a/site/content/docs/deployment-projects/cdk-project.md b/site/content/docs/deployment-projects/cdk-project.md new file mode 100644 index 000000000..dc9c18ba7 --- /dev/null +++ b/site/content/docs/deployment-projects/cdk-project.md @@ -0,0 +1,109 @@ +# CDK Project + +Each deployment project has a C# Cloud Development Kit (CDK) project. This CDK project is used to create the AWS infrastructure as a CloudFormation stack based on the user settings and to deploy your project to that infrastructure. + +Read our [tutorial](../../tutorials/custom-project.md) to see how you can customize this CDK project to add additional infrastructure resources for your deployments. + +### What is AWS CDK? + +The [AWS Cloud Development Kit (AWS CDK)](https://aws.amazon.com/cdk/) is an open-source software development framework to define your cloud application resources using familiar programming languages. + +The following links are useful resources to learn more about AWS CDK and how to write .NET CDK projects. + +* [AWS CDK Developer Guide](https://docs.aws.amazon.com/cdk/v2/guide/home.html) +* [.NET CDK Workshop](https://cdkworkshop.com/40-dotnet.html) +* [.NET CDK Reference](https://docs.aws.amazon.com/cdk/api/v2/dotnet/api/index.html) + +### Layout of a CDK deployment project + +The layout of the generated CDK project puts all the code that was used to create the AWS resources defined in the starting recipe in a directory called **Generated**. + +![Catagories in AWS Toolkit for Visual Studio](../../assets/images/deployment-project-file-layout.png) + +It is recommended to not modify the code in the `Generated` directory to make it easier to merge future changes from the starting recipe into your custom deployment project. If you do not intend to update your custom deployment project from the original built-in recipe you may modify the code or rearrange the directory layout. + +If you choose to not modify the `Generated` code it is recommended to customize the CDK project starting from the `AppStack` class. Here is the constructor of `AppStack`. + +``` +internal AppStack(Construct scope, IDeployToolStackProps props) + : base(scope, props.StackName, props) +{ + _configuration = props.RecipeProps.Settings; + + // Setup callback for generated construct to provide access to customize CDK properties before creating constructs. + CDKRecipeCustomizer.CustomizeCDKProps += CustomizeCDKProps; + + // Create custom CDK constructs here that might need to be referenced in the CustomizeCDKProps. For example if + // creating a DynamoDB table construct and then later using the CDK construct reference in CustomizeCDKProps to + // pass the table name as an environment variable to the container image. + + // Create the recipe defined CDK construct with all of its sub constructs. + var generatedRecipe = new Recipe(this, props.RecipeProps); + + // Create additional CDK constructs here. The recipe's constructs can be accessed as properties on + // the generatedRecipe variable. +} +``` + +The `var generatedRecipe = new Recipe(this, props.RecipeProps);` line of code creates all of the AWS resources from the `Generated` directory. Your customizations could create new AWS resources via CDK constructs before or after this line. Typically you would create new resources before this line if you want those resources to be connected to the resources defined in the `Recipe` type. If you need to create new resources that are connected to the resources defined in the `Recipe` then create them after this line. The instance of `Recipe` has public properties for all of the resources that were created in the `Recipe`. + +In this constructor a callback method called `CustomizeCDKProps` is setup. This callback method is called right before any constructs are created from the `Recipe`. This allows modifying the construct's property object before it is passed into the construct. + +The example below shows the `CustomizeCDKProps` callback that checks to see if the resource being created is the Beanstalk Environment. If it is, cast the property object to the appropriate property object and then make whatever customizations are needed. + +``` +private void CustomizeCDKProps(CustomizePropsEventArgs evnt) +{ + if (string.Equals(evnt.ResourceLogicalName, nameof(evnt.Construct.BeanstalkEnvironment))) + { + if (evnt.Props is CfnEnvironmentProps props) + { + Console.WriteLine("Customizing Beanstalk Environment"); + } + } +} +``` + +### Main method in Program.cs + +The `Main` method in `Program.cs` for the CDK deployment project must be coded in a certain style to ensure compatibility with the deploy tool. The AWS Deploy Tool relies on .NET's Configuration system to pass along settings from the deploy tool to the CDK project. In the example below the `ConfigurationBuilder().AddAWSDeployToolConfiguration(app)` method reads the settings that were passed into the project from the deploy tool. + +With the configuration read from the deploy tool, the CDK environment is set to the account and region the deploy tool was configured with. + +The other major difference from normal CDK projects is the call to `CDKRecipeSetup.RegisterStack`. This is required to stamp the CloudFormation stack with the recipe id that created the stack. Future redeployments can only update existing stacks that were created by the original recipe. It also serializes the settings collected from the deploy tool into the metadata for the CloudFormation stack so redeployments can use the previous settings used for deployment. + +``` +public static void Main(string[] args) +{ + var app = new App(); + + var builder = new ConfigurationBuilder().AddAWSDeployToolConfiguration(app); + var recipeProps = builder.Build().Get>(); + var appStackProps = new DeployToolStackProps(recipeProps) + { + Env = new Environment + { + Account = recipeProps.AWSAccountId, + Region = recipeProps.AWSRegion + } + }; + + // The RegisterStack method is used to set identifying information on the stack + // for the recipe used to deploy the application and preserve the settings used in the recipe + // to allow redeployment. The information is stored as CloudFormation tags and metadata inside + // the generated CloudFormation template. + CDKRecipeSetup.RegisterStack(new AppStack(app, appStackProps), appStackProps.RecipeProps); + + app.Synth(); +} +``` + +### Configuration options + +The option settings that are defined in the [recipe file](recipe-file.md) are passed into the CDK project and then deserialized into the `Configuration` object. + +When you add new settings to the recipe file, you also need to add the `Id` of the new settings to the `Configuration` object. If you added an `Object` setting with a collection of child settings, first create a new type representing that entire `Object`. For reach child setting id, add a property on the new type. Finally, add a new property on `Configuration` for your new type with the property name being the id of the `Object` setting. + +The `Configuration` object follows the same `Generated` directory pattern described above. Custom settings should be added to the partial `Configuration.cs` file outside of the `Generated` directory. + + diff --git a/site/content/docs/deployment-projects/index.md b/site/content/docs/deployment-projects/index.md new file mode 100644 index 000000000..838284c90 --- /dev/null +++ b/site/content/docs/deployment-projects/index.md @@ -0,0 +1,48 @@ +# Deployment Projects + +### What is a deployment recipe? + +The tool provides intelligent deployment recommendations that are tailored to your specific .NET application. The recommendation rules are defined in the `deployment recipes`. These recipes let you configure a pre-defined set of AWS resources related to your application such as IAM roles, virtual private clouds, load balancers, and other. + +There is a built-in recipe for [each supported project type](../support.md). All recipe definitions are available [on GitHub](https://github.com/aws/aws-dotnet-deploy/tree/main/src/AWS.Deploy.Recipes/RecipeDefinitions). + +### What is a deployment project? + +What if your application uses additional AWS resources that are not included in the built-in recipe, such as DynamoDB tables or SQS queues? In this case, you can extend one of the existing recipes and create a custom `deployment project` to manage additional AWS resources and services. + +Once you create and customize your deployment project, it will be displayed alongside the built-in recipes as a custom deployment option. + +** Before:** _The recommended deployment target is a built-in recipe for ASP.NET Core applications._ + +![Before](../../assets/images/cliBefore.png) + +** After:** _The recommended deployment target is a custom deployment project that manages a DynamoDB table in addition to the ASP.NET Core project._ + +![After](../../assets/images/cliAfterBorder.png) + +### Parts of a deployment project + +A deployment project consists of two parts. For details, refer to the full reference guides below. + +* [Recipe file](./recipe-file.md) - a JSON configuration file that drives the deployment experience. +* [.NET CDK project](./cdk-project.md) - a C# project that uses the [AWS Cloud Development Kit (CDK)](https://aws.amazon.com/cdk/) to define the infrastructure that will be created. + +### Creating a deployment project + +To create a custom deployment project, run this command in the directory of the .NET project you wish to deploy. See [`deployment-project` command](../commands/project.md) for more details. + + dotnet aws deployment-project generate --output --project-display-name + +The list of built-in recipes that are compatible with your .NET project will be displayed. Select the recipe that you will use to customize, and a deployment project will be created in the `--output` directory. + + > Note: It is important to add your deployment projects to source control. If your deployment project has been deleted, you will not be able to re-deploy your application to the same CloudFormation stack. + +Now you can begin customizing the deployment project. See our [step-by-step tutorial](../../tutorials/custom-project.md) for a step-by-step instructions. + +### Searching for deployment projects + +When you run the `dotnet aws deploy` command, the tool searches for deployment projects anywhere underneath the solution (.sln) directory of project you are deploying. It also ensures that each deployment project iscompatible with the .NET project being deployed. + +To point to a deployment project that is outside the solution directory, use the `--deployment-project` switch to pass in the path of the deployment project to use. This is common when sharing deployment projects across multiple solutions. + + diff --git a/site/content/docs/deployment-projects/recipe-file.md b/site/content/docs/deployment-projects/recipe-file.md new file mode 100644 index 000000000..0cdefc0e0 --- /dev/null +++ b/site/content/docs/deployment-projects/recipe-file.md @@ -0,0 +1,354 @@ +# Recipe file schema + +Each deployment project has a JSON file with a `.recipe` extension. This recipe file defines the type of .NET projects the recipe is compatible with and the settings that will be shown to users. + +Read our [tutorial](../../tutorials/custom-project.md) to see how you can modify this file to drive the custom deployment experience and display custom option settings to the users. The full schema for the recipe file can be found [here](https://github.com/aws/aws-dotnet-deploy/blob/main/src/AWS.Deploy.Recipes/RecipeDefinitions/aws-deploy-recipe-schema.json). + +### Top level settings + +This is the list of top level properties in the recipe definition. + +* **Id** - the Id of the deployment project. The CloudFormation stack will be tagged with this Id which is used to identify which stacks to update when redeploying. +* **Name** - the name of the deployment project, shown in the list of recommendations. +* **ShortDescription** - description used when showing the list of recommendations. +* **Description** - longer description shown when hovering over the recommendation in Visual Studio. +* **TargetService** - the main AWS service the application will be deployed to. Visual Studio uses this to provide visual indicators. +* **DeploymentType** - for deployment projects this value should always be `CdkProject`. +* **DeploymentBundle** - how the .NET project being deploying should be bundled. Allowed values are `Container` and `DotnetPublishZipFile`. +* **DisplayedResources** - the list of resources to display to users at the end of the deployment. +* **RecipePriority** - the priority of the recipe in the compatibility list. +* **RecommendationRules** - the list of rules to determine the recipe's compatibility with the project being deployed. +* **Categories** - the list of categories for the option settings. +* **OptionSettings** - the list of settings users can configure during deployment. + + +### Displayed Resources + +The `DisplayedResources` array contains a list of resources that the deployment tooling should display after deployment is complete. This is typically the primary resources for a deployment project like an Elastic Beanstalk Environment or a Load Balancer that allows the user to quickly see their application deployed to AWS. + +Here is an example for a recipe that wants to the Elastic Beanstalk environment to be displayed after deployment. + +``` + "DisplayedResources": [ + { + "LogicalId": "RecipeBeanstalkEnvironment83CC12DE", + "Description": "Application Endpoint" + } + ], +``` + +`DisplayedResources` objects have the following properties: + +* **LogicalId** - the CloudFormation logical id of the AWS resource. The CDK will generate a unique hash as the suffix of the logical resource id. The easiest way to figure out the value to set here is to do an initial deployment of the deployment project and then look up the logical id in the CloudFormation console. +* **Description** - a description for the resource shown to users in the list of display resources. + + +### Compatibility Rules + +The recipe file defines a collection of rules that the deployment tool executes against the .NET project being deployed to determine if the recipe is compatible with the .NET project. A rule is made up of a list of tests to run and the effect to apply. + +#### Tests + +The deploy tool supports a collection of tests that can be run against the .NET project being deployed. Here is an example of a test that checks the version of .NET the project is targeting. + +```json +{ + "Type": "MSProperty", + "Condition": { + "PropertyName": "TargetFramework", + "AllowedValues": [ + "netcoreapp3.1", + "net6.0" + ] + } +} +``` + +The `Type` property determines the type of test to run. The `Condition` contains the data needed to evaluate the test, in this case the MSBuild property to check and the allowed values. Each type of test can have a different set of properties for the `Condition`. + +#### Available Tests + +| Type | Description | Conditions properties | +| ---- | ----------- | ---------- | +| MSProjectSdkAttribute | Compares the `Sdk` attribute at the root element of the project file to a given value. |
  • Value - the expected value. For example `Microsoft.NET.Sdk.Web` for web projects.
| +| MSProperty | Compares the property in a project file property group. |
  • PropertyName - the property to compare.
  • AllowedValues - an array of possible values for the property.
| +| MSPropertyExists | Test to see if a property in a property group exists. |
  • PropertyName - the property test for existence.
| +| FileExists | Tests to see if a file exist. For example checking to see a `Dockerfile` exists. |
  • FileName - the file to test for existence.
| +| NuGetPackageReference | Test to see if the project has a `PackageReference` to a NuGet package. |
  • NuGetPackageName - the NuGet package to test if it is being referenced.
| + + +#### Effect + +A rules effect defines the behavior of the rule depending on if its test(s) pass or not. Effects can either mark the recipe as not included in the compatible list or adjust the priority for the recipe. + +Here is an example of a rule that checks the project is a web project and targets .NET Core 3.1 or .NET 6. If both tests pass the effect's `Pass` property instructs the deployment tooling to include the recipe. If either one of the two tests fail the `Fail` effect is run, removing the recipe from the included compatible list. + +```json + "RecommendationRules": [ + { + "Tests": [ + { + "Type": "MSProjectSdkAttribute", + "Condition": { + "Value": "Microsoft.NET.Sdk.Web" + } + }, + { + "Type": "MSProperty", + "Condition": { + "PropertyName": "TargetFramework", + "AllowedValues": [ + "netcoreapp3.1", + "net6.0" + ] + } + } + ], + "Effect": { + "Pass": { + "Include": true + }, + "Fail": { + "Include": false + } + } + } + ] +``` + +Here is another example that tests if a project contains a docker file. If it does not include a docker file it reduces the priority of the recipe. + +```json + "RecommendationRules": [ + ... + + { + "Tests": [ + { + "Type": "FileExists", + "Condition": { + "FileName": "Dockerfile" + } + } + ], + "Effect": { + "Fail": { + "PriorityAdjustment": -100, + "Include": true + } + } + }, + + ... + ] +``` + +A recipe is considered compatible if no rule ran an effect that set `Include` to false and if the priority is greater then 0. + +To simulate an **"or"** set of rules the starting priority in `RecipePriority` can be set to a negative value meaning it is not included by default. Then you can have a series of tests that adjust the priority to a positive amount. That way if any test rule passes it can adjust the priority to a positive number. + + +### Setting Categories + +The `Categories` property is an array of objects that define the categories for the settings. The AWS Toolkit for Visual Studio uses this array to build the list of categories in the UI for fast navigation to a group of settings. + +![Catagories in AWS Toolkit for Visual Studio](../../assets/images/vs-catagories.png) + +A category is defined with the following properties: + +* **Id** - the unique id within the recipe for the category. +* **DisplayName** - the name of the category displayed to users. +* **Order** - the order in the toolkit for the category. Categories are display in sorted descending order. + +Here is an example of defining a custom category that you could use to categorize additional settings added as application resources. + +``` + { + "Id": "AppResources", + "DisplayName": "Application Resources", + "Order": 15 + } +``` + +To assign a setting to a category, set the setting's `Category` property of to the `Id` of a category. Only top level settings can be assigned a category. Any top level settings that are not assigned a category will be placed in the `General` category. + +### Option Settings + +The settings that are shown to users and allows users to customize the deployment in either the CLI or Visual Studio are defined in the `OptionSettings` array. Settings have the following properties. + +* **Id** - the id of the setting. Once projects are deployed with the recipe, this id should not change because the id is saved into the CloudFormation stack's template. +* **Name** - the name of the setting shown to users. +* **Description** - the informational text shown to users for the setting. +* **Type** - the data type of the setting. +* **DefaultValue** - the default value for the setting. +* **TypeHint** - a hint to the deployment tooling what the meaning of the setting is. +* **TypeHintData** - additional information to pass into the type hint. +* **AdvancedSetting** - a boolean for whether this setting is an advanced use case. If true the setting might not be shown to users unless the request to see all settings. +* **Updatable** - a boolean that controls whether a setting can be modified during redeployment. It is recommended to set this to `false` for settings where deleting resources once they exist would make the application unavailable. +* **AllowedValues** - the list of possible values. +* **ValueMapping** - a mapping between values in the `AllowedValues` list to user friendly display names for each allowed value. +* **ChildOptionSettings** - if the `Type` property is set to `Object` this list contains a child option settings. +* **DependsOn** - a list of expressions to determine if this setting should be visible. +* **Validators** - a list of validators to run when values are set for the setting to ensure the value is valid. +* **ParentSettingId** - the id of a setting that represents the parent of this setting. For example a setting that allows picking EC2 subnets would set the `ParentSettingId` to the setting that defines the `VPC` to use for deployment. The type hint for the setting can use this field to filter the list of subnets that are valid for the selected VPC. + + +#### Setting Data Types + +Settings can have the following data types: + +* String +* Int +* Double +* Bool +* KeyValue +* List +* Object + +If the data type is set to `Object` the setting has child settings that are defined in the `ChildOptionSettings` array for the setting. + +#### TypeHint + +Type hints are used to control the user experience in the CLI or Visual Studio. For example if the `TypeHint` property is set to `ExistingECSCluster` the deployment tool knows this `String` data type setting should be set to the name of an existing ECS cluster and presents the list of existing clusters to the user. + +Some type hints require additional configuration which can specified in the `TypeHintData` property. For example if a setting has a type hint of `ExistingIAMRole` the tool should only show roles that can be assumed by the target service. Here is a example of configuring the `ServicePrincipal` for the type hint to filter the list of roles to pick from. + +``` + { + "Id": "RoleArn", + "Name": "Existing Role ARN", + "Description": "The ARN of the existing role to use.", + "Type": "String", + "TypeHint": "ExistingIAMRole", + "TypeHintData": { + "ServicePrincipal": "ecs-tasks.amazonaws.com" + }, + + ... +``` + +Here is the list of available type hints in the deployment tooling. + +| Type Hint | Type Hint Data | Notes | +| --------- | ----------- | ---- | +| IAMRole |
  • ServicePrincipal - the service principal the role can be assumed by.
| Set at `Object` level that controls whether to create a new role or select an existing role. | +| ExistingIAMRole |
  • ServicePrincipal - the service principal the role can be assumed by.
| Set at the `String` setting level for storing the existing IAM role arn. | +| ECSCluster | | Set at the `Object` setting level that controls whether to create or select an existing ECS cluster. | +| ExistingECSCluster | | Set at the `String` setting level for storing the existing ECS Cluster Name. | +| ECSService | | | +| ECSTaskSchedule | | | +| EC2KeyPair | | | +| Vpc | | Set at the `Object` level that controls whether to create a new VPC or select an existing VPC. | +| ExistingVpc | | Set at the `String` setting level for storing the existing VPC ID. | +| AppRunnerService | | | +| VPCConnector | | Set at the `Object` level for creating or selecting an App Runner VPC Connector. | +| ExistingVpcConnector | | Set at the `String` setting level for storing the existing VPC Connector id. | +| BeanstalkRollingUpdates | | | +| ExistingSubnets | | `ParentSettingId` should be set to a setting for the VPC. | +| ExistingSecurityGroups | | `ParentSettingId` should be set to a setting for the VPC. | +| ExistingBeanstalkApplication | | | +| ExistingApplicationLoadBalancer | | | +| InstanceType | | | +| WindowsInstanceType | | | +| S3BucketName |
  • AllowNoValue - determines whether to allow no value to be set.
| | +| DynamoDBTableName |
  • AllowNoValue - determines whether to allow no value to be set.
| | +| SQSQueueUrl |
  • AllowNoValue - determines whether to allow no value to be set.
| | +| SNSTopicArn |
  • AllowNoValue - determines whether to allow no value to be set.
| | +| FilePath |
  • Filter - the filter that appears in the file dialog box.
  • DefaultExtension - the default extension used if the user specifies a file name without an extension.
  • Title - the title of the file dialog box.
  • CheckFileExists - if true the deployment tooling ensures the file exists.
  • AllowEmpty - controls whether an empty value is allowed.
| | +| DotnetBeanstalkPlatformArn | | | +| DotnetWindowsBeanstalkPlatformArn | | | +| DotnetPublishSelfContainedBuild | | Used internally to validate build configuration. | +| DotnetPublishBuildConfiguration | | Used internally to validate build configuration. | +| DotnetPublishAdditionalBuildArguments | | Used internally to validate build configuration. | +| DockerExecutionDirectory | | Used internally to validate build configuration. | +| DockerBuildArgs | | Used internally to validate build configuration. | + + +#### DependsOn + +The `DependsOn` property is an array of conditions to determine if a setting should be visible based on the values for other setting(s). For example if a user selects the setting to create a new IAM role then the setting for the selecting an existing IAM role should not be displayed. + +Each item in the `DependsOn` array has the following properties. + +* **Id** - the id of the setting compare. The id should be full id including all parent object id settings. The format is .. +* **Operation** - the operation to run. Allowed values are `NotEmpty` and `Equals`. If operation is not set then the default is `Equals`. +* **Value** - the value to compare when the operation is `Equals`. + +Here is an example for a setting used for storing an existing IAM role to use. The value is displayed if the setting `ApplicationIAMRole.CreateNew` is set false. +``` + { + "Id": "RoleArn", + "Name": "Existing Role ARN", + "Description": "The ARN of the existing role to use.", + "Type": "String", + "TypeHint": "ExistingIAMRole", + "TypeHintData": { + "ServicePrincipal": "tasks.apprunner.amazonaws.com" + }, + "AdvancedSetting": false, + "Updatable": true, + "DependsOn": [ + { + "Id": "ApplicationIAMRole.CreateNew", + "Value": false + } + ], + + ... + } +``` + +#### Validators + +Validators allow telling the user when a setting has an invalid value before starting the deployment. Each setting can have a list of validators specified in the `Validators` array. Validators can do simple checks like making sure string values are of a certain format as well as make AWS service calls to make sure the value of an existing resource exists. + +Here is an example of a validator for a port setting that ensures the value is within the range 0 to 51200. + +```json + ... + + { + "Id": "Port", + "Name": "Port", + "Category": "General", + "Description": "The port the container is listening for requests on.", + "Type": "Int", + "DefaultValue": 80, + "AdvancedSetting": false, + "Updatable": true, + "Validators": [ + { + "ValidatorType": "Range", + "Configuration": { + "Min": 0, + "Max": 51200 + } + } + ] + } + + ... +``` + +The `ValidatorType` property determines the type of validator to run. The `Configuration` contains the data needed to evaluate the validator, in this case the min and max values. Each type of validator can have a different set of properties for the `Configuration`. + +| Validator Type | Description | Configuration properties | +| ---- | ----------- | ---------- | +| Range | For numeric settings define the min and max values. |
  • Min - the min value.
  • Max - the max value.
  • ValidationFailedMessage - the error message shown if the validator fails.
  • AllowEmptyString - boolean that if true allows empty values to be set.
| +| Regex | For string settings define a regular expression that the value must match. |
  • Regex - the regular expression to match against the value of the setting.
  • ValidationFailedMessage - the error message shown if the validator fails.
  • AllowEmptyString - boolean that if true allows empty values to be set.
| +| Required | A value is required for the setting. |
  • ValidationFailedMessage - the error message shown if the validator fails.
| +| DirectoryExists | For string settings representing a directory. Validates the directory exists. | | +| DockerBuildArgs | Ensures the value is valid for being passed to the `docker build` command. | | +| DotnetPublishArgs | Ensures the value is valid for being passed to the `dotnet publish` command. | | +| ExistingResource | Using the [AWS Cloud Control API GetResource](https://docs.aws.amazon.com/cloudcontrolapi/latest/APIReference/API_GetResource.html), ensure that the AWS resource exists. |
  • ResourceType - the AWS resource type to search for. For example `AWS::ECS::Cluster` for an ECS cluster.
| +| FileExists | For string settings representing a file path. Validates the file exists. |
  • ValidationFailedMessage - the error message shown if the validator fails.
  • AllowEmptyString - boolean that if true allows empty values to be set.
| +| StringLength | Validates the length of a string setting. |
  • MinLength - the min length for the string.
  • MaxLength - the max length for the string.
  • ValidationFailedMessage - the error message shown if the validator fails.
| +| InstanceType | Validates the string value is a valid Linux EC2 instance type. | | +| WindowsInstanceType | Validates the string value is a valid Windows EC2 instance type. | | +| SubnetsInVpc | Validates the subnets are in the configured VPC. |
  • VpcId - the id of the setting that has the VPC configured for.
  • ValidationFailedMessage - the error message shown if the validator fails.
| +| SecurityGroupsInVpc | Validates the security group are in the configured VPC. |
  • VpcId - the id of the setting that has the VPC configured for.
  • ValidationFailedMessage - the error message shown if the validator fails.
| +| Uri | Validates the value is a well-formed URI string. |
  • ValidationFailedMessage - the error message shown if the validator fails.
| +| Comparison | Compares this setting with another setting. |
  • SettingId - the setting to compare to.
  • Operation - the comparison operation. Allowed values are `GreaterThan`.
| +| VPCSubnetsInDifferentAZs | Validates that the selected VPC must have at least two subnets in two different Availability Zones |
  • ValidationFailedMessage - the error message shown if the validator fails.
| + + diff --git a/site/content/docs/project.md b/site/content/docs/project.md deleted file mode 100644 index 6a6ca2422..000000000 --- a/site/content/docs/project.md +++ /dev/null @@ -1,54 +0,0 @@ -# Deployment projects - -### What is a deployment project? - -A deployment project allows you to customize the resources you deploy to AWS. Instead of choosing one of the recipes supplied by AWS, you can generate a deployment project and modify it to fit your application needs. - -Each deployment project includes: - -* **.NET CDK project** - takes the collected settings from the user and performs the deployment using the AWS CDK. It’s a standard C# console project that uses NuGet packages containing CDK construct types to define the AWS infrastructure. -* **JSON metadata file** - a JSON configuration file that contains all of the settings that are needed to determine which deployment services to use and their configurations. Here is the [JSON file definition](https://github.com/aws/aws-dotnet-deploy/tree/main/src/AWS.Deploy.Recipes/RecipeDefinitions). - -### Generating a project - -You can generate deployment project into your workspace. The `--project-display-name` sets the name for the project that will be seen to you during the deployment. - - dotnet aws deployment-project generate --project-display-name MyCustomCDKProject - -You can also generate the deployment project in the directory of your choice by specifying the `--output` parameter. - - dotnet aws deployment-project generate --output ../myCustomCDKProject --project-display-name MyCustomCDKProject - -You can then customize the project and the deployment settings to fit your needs. - -### Customizing resources - -Now you can go ahead and add additional resources to the generated deployment project. Deployment projects enable you to change the deployment interface. - -The `AppStack class` is the recommended place to add new AWS resources or customize the resources created in the generated code. - - > Note: Most of the code is located in the generated folder. We don’t recommend you edit the files in there directly, and instead use it for reference. If you want to take updates from the original recipe the deployment project was created from, you can just copy the code into the generated folder. - -In the example below, we’ll show you how to add a DynamoDB table.... - -TODO - -### Customizing deployment settings -You can also add new settings that will be presented to the user during deployment. - -TODO - - -### Specifying external project - -Now that you created your custom deployment project, you can choose it over the default recipes supplied by AWS. - - By default, when you invoke the `dotnet aws deploy` command, it looks for all deployment projects under the directory where the solution file is at, or the root of the Git repository. - - If you have a deployment project you want to use but it’s in a separate workspace, possibly a separate repository, then use the `--deployment-project` switch to pass in the path of the shared deployment project. - - dotnet aws deploy --deployment-project [PATH_TO_CUSTOM_DEPLOYMENT_PROJECT] - -### Sharing deployment projects - -It is important to save the generated deployment project in version control so that you can reuse the project for multiple applications that you want to deploy or share with the rest of your team. diff --git a/site/content/docs/recipe.md b/site/content/docs/recipe.md deleted file mode 100644 index 1577159a1..000000000 --- a/site/content/docs/recipe.md +++ /dev/null @@ -1,16 +0,0 @@ -# Deployment Recipes - -#### Recommendation engine -At the heart of the tool there is a recommendation engine. When you run the tool, the recommendation engine inspects your .NET project and suggests the right deployment service that is most appropriate for your project. - -This allows the engine to provide intelligent recommendations that are tailored to the type of .NET application that is being deployed. The recommendation rules are defined in the deployment recipes. - -#### Deployment Recipes -The recommendation rules are defined in the deployment recipes. The deployment tool turns these recipes into recommendations for a given .NET project. - -There is a recipe for each project type. All recipes can be found on [GitHub](https://github.com/aws/aws-dotnet-deploy/tree/1344e9e8e5485d7d38af524657178facf27ec973/src/AWS.Deploy.Recipes/RecipeDefinitions). Each recipe contains the following: - -1. **JSON Metadata File** - this file contains all of the metadata the deployment tool uses to drive the experience. This includes rules used in the recommendation engine to determine if the recipe is compatible with a project. It also has all of the settings that the deploy tool CLI, and eventually Visual Studio, uses to allow users to customize the experience. The schema for this file can be found [here](https://github.com/aws/aws-dotnet-deploy/blob/main/src/AWS.Deploy.Recipes/RecipeDefinitions/aws-deploy-recipe-schema.json) -2. **.NET CDK project template** - a recipe also contains a .NET project template that will be used to generate an [AWS Cloud Development Kit (CDK)](https://aws.amazon.com/cdk/) deployment project. - - > Note: There are a couple special case recipes that don't have CDK project templates. (Push to ECR and Deploy to existing Beanstalk). diff --git a/site/content/docs/tutorials/automate-deployments.md b/site/content/docs/tutorials/automate-deployments.md deleted file mode 100644 index 85a4aa015..000000000 --- a/site/content/docs/tutorials/automate-deployments.md +++ /dev/null @@ -1 +0,0 @@ -### TODO \ No newline at end of file diff --git a/site/content/docs/tutorials/delete-deployment.md b/site/content/docs/tutorials/delete-deployment.md deleted file mode 100644 index 85a4aa015..000000000 --- a/site/content/docs/tutorials/delete-deployment.md +++ /dev/null @@ -1 +0,0 @@ -### TODO \ No newline at end of file diff --git a/site/content/docs/tutorials/deploy-blazorapp.md b/site/content/docs/tutorials/deploy-blazorapp.md deleted file mode 100644 index 85a4aa015..000000000 --- a/site/content/docs/tutorials/deploy-blazorapp.md +++ /dev/null @@ -1 +0,0 @@ -### TODO \ No newline at end of file diff --git a/site/content/docs/tutorials/deploy-console-service.md b/site/content/docs/tutorials/deploy-console-service.md deleted file mode 100644 index 85a4aa015..000000000 --- a/site/content/docs/tutorials/deploy-console-service.md +++ /dev/null @@ -1 +0,0 @@ -### TODO \ No newline at end of file diff --git a/site/content/docs/tutorials/deploy-console-task.md b/site/content/docs/tutorials/deploy-console-task.md deleted file mode 100644 index 85a4aa015..000000000 --- a/site/content/docs/tutorials/deploy-console-task.md +++ /dev/null @@ -1 +0,0 @@ -### TODO \ No newline at end of file diff --git a/site/content/docs/tutorials/deploy-webapp.md b/site/content/docs/tutorials/deploy-webapp.md deleted file mode 100644 index 85a4aa015..000000000 --- a/site/content/docs/tutorials/deploy-webapp.md +++ /dev/null @@ -1 +0,0 @@ -### TODO \ No newline at end of file diff --git a/site/content/docs/tutorials/list-deployments.md b/site/content/docs/tutorials/list-deployments.md deleted file mode 100644 index 85a4aa015..000000000 --- a/site/content/docs/tutorials/list-deployments.md +++ /dev/null @@ -1 +0,0 @@ -### TODO \ No newline at end of file diff --git a/site/content/docs/tutorials/push-image-ecr.md b/site/content/docs/tutorials/push-image-ecr.md deleted file mode 100644 index 85a4aa015..000000000 --- a/site/content/docs/tutorials/push-image-ecr.md +++ /dev/null @@ -1 +0,0 @@ -### TODO \ No newline at end of file diff --git a/site/content/faq.md b/site/content/faq.md index 9d20270b2..042c389d5 100644 --- a/site/content/faq.md +++ b/site/content/faq.md @@ -20,3 +20,9 @@ The AWS Deploy Tool will show you all compute service options available to depl #### *FAQ: I have an application that has dependency on Windows technology, Can I use the AWS Deploy Tool to deploy it to AWS?* ASP.NET Core applications can be deployed to AWS Elastic Beanstalk picking the "ASP.NET Core App to AWS Elastic Beanstalk on Windows" recommendation. The deployment experience is very similar the "ASP.NET Core App to AWS Elastic Beanstalk on Linux" recommendation with additional settings for configuring the Internet Information Services (IIS) resource path and web site. + +#### *FAQ: Can I deploy my application from Visual Studio?* +Yes, you can deploy your application using the "Publish to AWS" feature in the AWS Toolkit for Visual Studio. This feature exposes the same functionality as the AWS Deploy Tool for .NET CLI. To learn more, go to [Publish to AWS](https://docs.aws.amazon.com/toolkit-for-visual-studio/latest/user-guide/publish-experience.html) in the AWS Toolkit for Visual Studio User Guide. + +#### *FAQ: Can I invoke AWS Deploy Tool from my CI/CD pipeline?* +Yes, you can. To learn more, go to [Integrating with CI/CD](docs/cicd.md) diff --git a/site/content/index.md b/site/content/index.md index 45791c707..135ae235a 100644 --- a/site/content/index.md +++ b/site/content/index.md @@ -1,26 +1,26 @@ -# About the AWS Deploy Tool +# About the AWS Deploy Tool for .NET -** AWS Deploy Tool** is an interactive tooling for the .NET CLI and the AWS Toolkit for Visual Studio that helps deploy .NET applications with minimum AWS knowledge, and with the fewest clicks or commands. It works by analyzing .NET projects and guiding developers to the right AWS service. It then selects the right deployment service, builds and packages your application, and creates the deployment infrastructure. It allows for a quick and easy Proof of concept (POC), smooth graduation to CI/CD, and a gradual ramp up on AWS knowledge. +** AWS Deploy Tool for .NET** is an interactive tooling for the .NET CLI and the AWS Toolkit for Visual Studio that helps deploy .NET applications with minimum AWS knowledge, and with the fewest clicks or commands. -## Key capabilities +### Key capabilities AWS Deploy Tool has the following capabilities: * **Compute recommendations for your application** – Get recommendations about the type of compute best suited for your application based on the application type. * **Dockerfile generation** - The tool will generate a Dockerfile if needed, otherwise an existing Dockerfile will be used. * **Auto packaging and deployment** – The tool builds the deployment artifacts, generates a deployment CDK project, provisions the infrastructure and deploys your application to the chosen AWS compute. -* **Repeatable and shareable deployments** –The tool persists configuration settings and AWS Cloud Development Kit (CDK) project used to deploy your application. You can also version control them and share with other team members for repeatable deployments. -* **Help with learning AWS CDK for .NET!** - Once you are ready to start exploring, the deployment tool will help you gradually learn AWS tools like CDK that are used under the hood. It generates well documented/organized CDK projects that you can easily start modifying to fit your specific use-case. +* **Repeatable and shareable deployments** – You can generate and modify AWS Cloud Development Kit (CDK) deployment projects to fit your specific use case. You can also version control your projects and share them with your team for repeatable deployments. +* **Help with learning AWS CDK for .NET!** - Gradually learn the underlying AWS tools that AWS Deploy Tool for .NET is built on, such as the AWS CDK. -## Availability -### ... in .NET CLI +### Availability +####... in .NET CLI -AWS Deploy tool is available for download as a NuGet package. See [How to install](docs/getting-started/installation.md) section. +AWS Deploy Tool for .NET is available for download as a NuGet package. See [How to install](docs/getting-started/installation.md) section. -### ... in AWS Toolkit for Visual Studio +#### ... in AWS Toolkit for Visual Studio The AWS Toolkit for Visual Studio exposes the same deployment functionality via **Publish to AWS** feature. For information about toolkit versions and using the feature, see [Publish to AWS](https://docs.aws.amazon.com/AWSToolkitVS/latest/UserGuide/publish-experience.html) in the [AWS Toolkit for Visual Studio User Guide](https://docs.aws.amazon.com/AWSToolkitVS/latest/UserGuide/). -## Additional Resources +### Additional Resources * The [aws-dotnet-deploy](https://github.com/aws/aws-dotnet-deploy) GitHub repo. * Blog Post: [Reimagining the AWS .NET deployment experience](http://aws.amazon.com/blogs/developer/reimagining-the-aws-net-deployment-experience/). diff --git a/site/content/tutorials/custom-project.md b/site/content/tutorials/custom-project.md new file mode 100644 index 000000000..b9a889c7c --- /dev/null +++ b/site/content/tutorials/custom-project.md @@ -0,0 +1,402 @@ +# Adding DynamoDB table to your deployment project + +In this tutorial, we will create and customize a deployment project to deploy a web application that uses AWS DynamoDB table as the backend store to Amazon Elastic Container Service (ECS). We will then add the deployment project to the source control and share with the team for future deployments. + +Tasks we will accomplish: + +1. Create a new web application called Acme.WebApp +2. Generate a deployment project using a built-in recipe. +3. Customize the deployment project's recipe file to allow the user to configure DynamoDB table. +4. Modify the CDK project to add the new DynamoDB setting options. +5. Modify the CDK project to create the DynamoDB table. +6. Pass the DynamoDB table name to the application code using the environment variable so that our application can read to know which table to use. +7. Add the deployment project to source control. +8. Deploy our Acme.WebApp application using custom deployment project. + + > **Note:** This tutorial is not concerned with the application logic. You can replace the sample Acme.WebApp used in this tutorial with any other web application.*** + +### Step 1: Create a new web application + +In your command prompt, run the following command to create your app: + + dotnet new webapp -o Acme.WebApp -f net6.0 + +### Step 2: Generate a deployment project + +Navigate to the Acme.WebApp project directory and run the following command to generate a deployment project: + + dotnet aws deployment-project generate --output ../Acme.WebApp.DeploymentProject --project-display-name "ASP.NET Core app with DynamoDB" + +The `--project-display-name` switch above configures the name of the recommendation that is shown in the deploy tool when deploying the application project. + +The AWS Deploy Tool will analyze the Acme.WebApp project and display which built-in recipes can be used as the starting point of the custom deployment project. + +``` +Recommended Deployment Option +----------------------------- +1: ASP.NET Core App to AWS Elastic Beanstalk on Linux +This ASP.NET Core application will be built and deployed to AWS Elastic Beanstalk on Linux. Recommended if you do not want to deploy your application as a container image. + +Additional Deployment Options +------------------------------ +2: ASP.NET Core App to Amazon ECS using Fargate +This ASP.NET Core application will be deployed to Amazon Elastic Container Service (Amazon ECS) with compute power managed by AWS Fargate compute engine. If your project does not contain a Dockerfile, it will be automatically generated. Recommended if you want to deploy your application as a container image on Linux. + +3: ASP.NET Core App to AWS App Runner +This ASP.NET Core application will be built as a container image and deployed to AWS App Runner. If your project does not contain a Dockerfile, it will be automatically generated. Recommended if you want to deploy your application as a container image on a fully managed environment. + +4: ASP.NET Core App to Existing AWS Elastic Beanstalk Environment +This ASP.NET Core application will be built and deployed to existing AWS Elastic Beanstalk environment. Recommended if you do not want to deploy your application as a container image. + +Choose deployment option (recommended default: 1) +``` + +Pick the option #2 that says "ASP.NET Core App to Amazon ECS using AWS Fargate". + +`Acme.WebApp.DeploymentProject` is created in a sibling directory to the application project. If you are using Visual Studio, add the new `Acme.WebApp.DeploymentProject` project to your solution. + +![Deployment Project](../assets/images/solutionWithCDKProject.png) + + +### Step 3: Add DynamoDB settings to the recipe file + +To give the team members, who will use our deployment project, the choice to either select an existing DynamoDB table or create a new table during deployment, we will add several new settings to our deployment project's recipe definition. + +Open the `Acme.WebApp.DeploymentProject.recipe` file located in the deployment project directory in your JSON editor of choice. + +Go to the `OptionSettings` section that contains the settings users can use to configure their project. + +Create a new "Object" option setting called `Backend` to group all of our new settings using the snippet below. When users configure their deployment, this option will be displayed in the "General" category. + +``` + "OptionSettings": [ + + ... + + { + "Id": "Backend", + "Name": "Backend", + "Category": "General", + "Description": "Configure the backend store.", + "Type": "Object", + "AdvancedSetting": false, + "Updatable": true, + "ChildOptionSettings": [ + + ] + }, + + + ... + } +``` + +Now we will create child option settings to configure DynamoDB settings. The first is a setting to determine if we should create a new table or not. This setting is a `Bool` type which is defaulted to `true`. As a best practice the `Updatable` setting is set to `false` to protect users from accidentally deleting the table when redeploying in the future. + +Add the following snippet to the `ChildOptionSettings` of the new `Backend` option we just greated: + +``` + "ChildOptionSettings": [ + { + "Id": "CreateNewTable", + "Name": "Create New DynamoDB Table", + "Description": "Do you want to create a new DynamoDB table for the backend store?", + "Type": "Bool", + "DefaultValue": true, + "AdvancedSetting": false, + "Updatable": false + }, + + ... + + ] + +``` + +If the user unchecks the `CreateNewTable` setting, we need to give them the choice to select an existing table. This `ExistingTableName` setting is a "String" type that will store the name of an existing DynamoDB table to use as the backend store. + + +Add the following snippet to the `ChildOptionSettings` of the new `Backend` option: +``` + "ChildOptionSettings": [ + + ... + + { + "Id": "ExistingTableName", + "Name": "Existing DynamoDB Table", + "Description": "Existing DynamoDB table to use as the backend store.", + "Type": "String", + "TypeHint": "DynamoDBTableName", + "DefaultValue": "", + "AdvancedSetting": false, + "Updatable": true, + "DependsOn": [ + { + "Id": "Backend.CreateNewTable", + "Value": false + } + ], + "Validators": [ + { + "ValidatorType": "Regex", + "Configuration": { + "Regex": "[a-zA-Z0-9_.-]+", + "ValidationFailedMessage": "Invalid table name." + } + }, + { + "ValidatorType": "StringLength", + "Configuration": { + "MinLength": 3, + "MaxLength": 255 + } + } + ] + } + ] +``` + +Let us take a deeper dive into the properties for the `ExistingTableName` setting. + +* **TypeHint** - Set to `DynamoDBTableName` which lets the deployment tool know this String type is for the name of a DynamoDB table. The deploy tool uses this information to show users a list of tables to pick from instead of a text-box. +* **Updatable** - Since modifying the name of an existing table is not a destructive change, we will allow this field to be updated during redeployments. +* **DependsOn** - This setting will only be visible if the previous `CreateNewTable` setting is set to `false`. Notice how the `Id` is the full name of the setting including the parent "Object" setting `Backend`. +* **Validators** - This attaches validators to make sure that the user-provided name matches the regex for valid table names and that the name meets the required minimum and maximum lengths. Adding validators provides feedback to users when invalid values are provided in either the CLI or Visual Studio. + +Here is the full snippet of the `Backend` Object option setting with the child settings: +``` + { + "Id": "Backend", + "Name": "Backend", + "Category": "General", + "Description": "Configure the backend store.", + "Type": "Object", + "AdvancedSetting": false, + "Updatable": true, + "ChildOptionSettings": [ + { + "Id": "CreateNewTable", + "Name": "Create New DynamoDB Table", + "Description": "Do you want to create a new DynamoDB table for the backend store?", + "Type": "Bool", + "DefaultValue": true, + "AdvancedSetting": false, + "Updatable": false + }, + { + "Id": "ExistingTableName", + "Name": "Existing DynamoDB Table", + "Description": "Existing DynamoDB table to use as the backend store.", + "Type": "String", + "TypeHint": "DynamoDBTableName", + "DefaultValue": "", + "AdvancedSetting": false, + "Updatable": true, + "DependsOn": [ + { + "Id": "Backend.CreateNewTable", + "Value": false + } + ], + "Validators": [ + { + "ValidatorType": "Regex", + "Configuration": { + "Regex": "[a-zA-Z0-9_.-]+", + "ValidationFailedMessage": "Invalid table name." + } + }, + { + "ValidatorType": "StringLength", + "Configuration": { + "MinLength": 3, + "MaxLength": 255 + } + } + ] + } + ] + } +``` + +### Step 4. Add new setting options to the CDK project + +When CDK project is executed, all settings collected from the user are passed and deserialized into the `Configuration` type. Now we need to customize the CDK project to store the new setting options by modifying the `Configuration` type. + +Create a new class called `BackendConfiguration` in the `Configurations` directory. Below is the code for this new type with the properties for `CreateNewTable` and `ExistingTableName`. + + > **Note:** The .NET CDK projects generated by the AWS Deploy Tool have the C# feature `Nullable` enabled in the project file by default. If you do not want this feature enabled, edit the .csproj file and remove the `Nullable` project from the PropertyGroup.*** + + +``` +namespace Acme.WebApp.DeploymentProject.Configurations +{ + public class BackendConfiguration + { + public bool CreateNewTable { get; set; } + + public string ExistingTableName { get; set; } + + + /// A parameterless constructor is needed for + /// or the classes will fail to initialize. + /// The warnings are disabled since a parameterless constructor will allow non-nullable properties to be initialized with null values. +#nullable disable warnings + public BackendConfiguration() + { + + } +#nullable restore warnings + + public BackendConfiguration( + bool createNewTable, + string existingTableName) + { + CreateNewTable = createNewTable; + ExistingTableName = existingTableName; + + } + } +} +``` + +In the `Configuration.cs` file, add a new property for our new backend settings. + +``` +namespace Acme.WebApp.DeploymentProject.Configurations +{ + public partial class Configuration + { + public BackendConfiguration Backend { get; set; } = new BackendConfiguration(); + } +} +``` + +Notice that the `Backend` property was added to the partial class that is **not** in the `Generated` directory. In both the `Configuration` and `BackendConfiguration` types, the property names match the setting ids used in the recipe file. This is important for the data to be property deserialized. + +### Step 5. Add DynamoDB table to the CDK project + + > **Note:** The `AppStack` class is the recommended place to customize the AWS resources. + +Modify the constructor of the `AppStack` class to check if `CreateNewTable` is set to true. Then use the `Amazon.CDK.AWS.DynamoDB` CDK construct to create a table as part of the CloudFormation stack. + +``` +using Amazon.CDK.AWS.DynamoDB; + +namespace Acme.WebApp.DeploymentProject +{ + + public class AppStack : Stack + { + private readonly Configuration _configuration; + private Table? _ddbBackend; + + internal AppStack(Construct scope, IDeployToolStackProps props) + : base(scope, props.StackName, props) + { + _configuration = props.RecipeProps.Settings; + + // Setup callback for generated construct to provide access to customize CDK properties before creating constructs. + CDKRecipeCustomizer.CustomizeCDKProps += CustomizeCDKProps; + + if(_configuration.Backend.CreateNewTable == true) + { + var backendProps = new TableProps + { + RemovalPolicy = RemovalPolicy.DESTROY, + PartitionKey = new Amazon.CDK.AWS.DynamoDB.Attribute { Name = "Id", Type = AttributeType.STRING }, + BillingMode = BillingMode.PAY_PER_REQUEST, + }; + _ddbBackend = new Table(this, "Backend", backendProps); + } + + var generatedRecipe = new Recipe(this, props.RecipeProps); + } +``` + +Notice that in the snippet above the table is created before the] `Recipe` construct. The `Recipe` construct has all AWS resources that are part of the original built-in ECS recipe that the deployment project was created from. + +### Step 6: Pass the DynamoDB table name to the application code + +Now that we have our DynamoDB table, we need to pass the table name into our application code. We will do it by setting an environment variable that the application code will read in the `CustomizeCDKProps` of the `AppStack` class. `CustomizeCDKProps` is a callback method that gets called for each AWS resource about to be created from the `Recipe` construct. + +To know which AWS resource is about to be created, compare the `evnt.ResourceLogicalName` property to the public property name on the `Recipe` construct. The built-in recipes are written to make sure the resource logical name is the same as the public property name. In our scenario we are looking to see if the `AppContainerDefinition` is about to be created. + +When we determine that the callback is for `AppContainerDefinition` then we cast the `evnt.Props` to the corresponding property object for `AppContainerDefinition`, in this case `ContainerDefinitionOptions`. From `ContainerDefinitionOptions` we can set the table name in an environment variable. + +``` +private void CustomizeCDKProps(CustomizePropsEventArgs evnt) +{ + // Example of how to customize the container image definition to include environment variables to the running applications. + // + if (string.Equals(evnt.ResourceLogicalName, nameof(evnt.Construct.AppContainerDefinition))) + { + if (evnt.Props is ContainerDefinitionOptions props) + { + if (props.Environment == null) + props.Environment = new Dictionary(); + + + if(_ddbBackend != null) + { + props.Environment["BACKEND_TABLE"] = _ddbBackend.TableName; + } + else + { + props.Environment["BACKEND_TABLE"] = _configuration.Backend.ExistingTableName; + } + } + } +} +``` + +### Step 6: Add deployment project to the source control + +Check your customized deployment project in to your source control. This is required to re-deploy your application to existing CloudFormation stacks that were created using custom deployment projects. + +### Step 7: Deploy Acme.WebApp application + +In your command prompt, run the following command to deploy your application: + + dotnet aws deploy --project-path . + +The custom deployment project will be displayed as the recommended option. + +``` +Recommended Deployment Option +----------------------------- +1: ASP.NET Core app with DynamoDB +This ASP.NET Core application will be deployed to Amazon Elastic Container Service (Amazon ECS) with compute power managed by AWS Fargate compute engine. If your project does not contain a Dockerfile, it will be automatically generated, otherwise an existing Dockerfile will be used. Recommended if you want to deploy your application as a container image on Linux. + +Additional Deployment Options +------------------------------ +2: ASP.NET Core App to Amazon ECS using AWS Fargate +This ASP.NET Core application will be deployed to Amazon Elastic Container Service (Amazon ECS) with compute power managed by AWS Fargate compute engine. If your project does not contain a Dockerfile, it will be automatically generated, otherwise an existing Dockerfile will be used. Recommended if you want to deploy your application as a container image on Linux. + +... + +``` + +Select deployment option #1. You can now see Backend settings option we customized. When you navigate to the `Backend` settings, you will be able to choose between using a new table or picking an existing table. + +``` +... + +Current settings (select number to change its value) +---------------------------------------------------- +1. ECS Cluster: AcmeWebApp +2. ECS Service Name: AcmeWebApp-service +3. Backend: + Create New DynamoDB Table: True +4. Desired Task Count: 3 +5. Application IAM Role: *** Create new *** +6. Virtual Private Cloud (VPC): *** Default *** +7. Environment Variables: +8. ECR Repository Name: acmewebapp + +... + +``` + + > **Note:** The AWS Toolkit for Visual Studio will also recognize the custom deployment project. The deployment project will show up as the highest recommended option and the user will also be able to choose between creating a new table or choosing from a drop-down list of available tables in the account that is being deployed to. diff --git a/src/AWS.Deploy.Common/Exceptions.cs b/src/AWS.Deploy.Common/Exceptions.cs index f5c0a1a23..085a69f7e 100644 --- a/src/AWS.Deploy.Common/Exceptions.cs +++ b/src/AWS.Deploy.Common/Exceptions.cs @@ -123,7 +123,8 @@ public enum DeployToolErrorCode FailedToCreateDeepCopy = 10010100, FailedToGetOptionSettingValue = 10010200, ECRRepositoryNameIsNull = 10010300, - FailedToReadCdkBootstrapVersion = 10010400 + FailedToReadCdkBootstrapVersion = 10010400, + UnsupportedOptionSettingType = 10010500 } public class ProjectFileNotFoundException : DeployToolException @@ -220,6 +221,14 @@ public class ValidationFailedException : DeployToolException public ValidationFailedException(DeployToolErrorCode errorCode, string message, Exception? innerException = null) : base(errorCode, message, innerException) { } } + /// + /// Thrown if is setting a value with an incompatible type. + /// + public class UnsupportedOptionSettingType : DeployToolException + { + public UnsupportedOptionSettingType(DeployToolErrorCode errorCode, string message, Exception? innerException = null) : base(errorCode, message, innerException) { } + } + /// /// Thrown if Option Setting Item Validator has missing or invalid configuration. /// diff --git a/src/AWS.Deploy.Common/Recipes/OptionSettingItem.ValueOverride.cs b/src/AWS.Deploy.Common/Recipes/OptionSettingItem.ValueOverride.cs index 92ec8bac9..6b4697b47 100644 --- a/src/AWS.Deploy.Common/Recipes/OptionSettingItem.ValueOverride.cs +++ b/src/AWS.Deploy.Common/Recipes/OptionSettingItem.ValueOverride.cs @@ -125,45 +125,57 @@ public async Task SetValue(IOptionSettingHandler optionSettingHandler, object va Validation.ValidationMessage = string.Empty; Validation.InvalidValue = null; - if (valueOverride is bool || valueOverride is int || valueOverride is long || valueOverride is double || valueOverride is Dictionary || valueOverride is SortedSet) + try { - _value = valueOverride; - } - else if (Type.Equals(OptionSettingValueType.KeyValue)) - { - var deserialized = JsonConvert.DeserializeObject>(valueOverride?.ToString() ?? ""); - _value = deserialized; - } - else if (Type.Equals(OptionSettingValueType.List)) - { - var deserialized = JsonConvert.DeserializeObject>(valueOverride?.ToString() ?? ""); - _value = deserialized; - } - else if (valueOverride is string valueOverrideString) - { - if (bool.TryParse(valueOverrideString, out var valueOverrideBool)) + if (valueOverride is bool || valueOverride is int || valueOverride is long || valueOverride is double || valueOverride is Dictionary || valueOverride is SortedSet) { - _value = valueOverrideBool; + _value = valueOverride; } - else if (int.TryParse(valueOverrideString, out var valueOverrideInt)) + else if (Type.Equals(OptionSettingValueType.KeyValue)) { - _value = valueOverrideInt; + var deserialized = JsonConvert.DeserializeObject>(valueOverride?.ToString() ?? ""); + _value = deserialized; } - else + else if (Type.Equals(OptionSettingValueType.List)) { - _value = valueOverrideString; + var deserialized = JsonConvert.DeserializeObject>(valueOverride?.ToString() ?? ""); + _value = deserialized; } - } - else - { - var deserialized = JsonConvert.DeserializeObject>(JsonConvert.SerializeObject(valueOverride)); - foreach (var childOptionSetting in ChildOptionSettings) + else if (valueOverride is string valueOverrideString) { - if (deserialized?.TryGetValue(childOptionSetting.Id, out var childValueOverride) ?? false) + if (bool.TryParse(valueOverrideString, out var valueOverrideBool)) + { + _value = valueOverrideBool; + } + else if (int.TryParse(valueOverrideString, out var valueOverrideInt)) { - await optionSettingHandler.SetOptionSettingValue(recommendation, childOptionSetting, childValueOverride, skipValidation: skipValidation); + _value = valueOverrideInt; + } + else + { + _value = valueOverrideString; } } + else + { + var deserialized = JsonConvert.DeserializeObject>(JsonConvert.SerializeObject(valueOverride)); + foreach (var childOptionSetting in ChildOptionSettings) + { + if (deserialized?.TryGetValue(childOptionSetting.Id, out var childValueOverride) ?? false) + { + await optionSettingHandler.SetOptionSettingValue(recommendation, childOptionSetting, childValueOverride, skipValidation: skipValidation); + } + } + } + } + catch (JsonReaderException ex) + { + Validation.ValidationStatus = ValidationStatus.Invalid; + Validation.ValidationMessage = $"The value you are trying to set is invalid."; + Validation.InvalidValue = valueOverride; + throw new UnsupportedOptionSettingType(DeployToolErrorCode.UnsupportedOptionSettingType, + $"The value you are trying to set for the option setting '{Name}' is invalid.", + ex); } } diff --git a/src/AWS.Deploy.Orchestration/CDK/CDKBootstrapTemplate.yaml b/src/AWS.Deploy.Orchestration/CDK/CDKBootstrapTemplate.yaml index 46a61726e..a24888f21 100644 --- a/src/AWS.Deploy.Orchestration/CDK/CDKBootstrapTemplate.yaml +++ b/src/AWS.Deploy.Orchestration/CDK/CDKBootstrapTemplate.yaml @@ -198,8 +198,6 @@ Resources: Type: AWS::ECR::Repository Properties: ImageTagMutability: IMMUTABLE - ImageScanningConfiguration: - ScanOnPush: true RepositoryName: Fn::If: - HasCustomContainerAssetsRepositoryName @@ -490,7 +488,7 @@ Resources: Type: String Name: Fn::Sub: /cdk-bootstrap/${Qualifier}/version - Value: "13" + Value: "14" Outputs: BucketName: Description: The name of the S3 bucket owned by the CDK toolkit stack @@ -521,3 +519,4 @@ Outputs: - CdkBootstrapVersion - Value + \ No newline at end of file diff --git a/src/AWS.Deploy.Orchestration/Orchestrator.cs b/src/AWS.Deploy.Orchestration/Orchestrator.cs index f97e3c020..0237c48e0 100644 --- a/src/AWS.Deploy.Orchestration/Orchestrator.cs +++ b/src/AWS.Deploy.Orchestration/Orchestrator.cs @@ -151,6 +151,8 @@ public async Task ApplyRecommendationPreviousSettings(Recommenda { if (_optionSettingHandler == null) throw new InvalidOperationException($"{nameof(_optionSettingHandler)} is null as part of the orchestartor object"); + if (_interactiveService == null) + throw new InvalidOperationException($"{nameof(_interactiveService)} is null as part of the orchestrator object"); var recommendationCopy = recommendation.DeepCopy(); recommendationCopy.IsExistingCloudApplication = true; @@ -159,7 +161,15 @@ public async Task ApplyRecommendationPreviousSettings(Recommenda { if (previousSettings.TryGetValue(optionSetting.Id, out var value)) { - await _optionSettingHandler.SetOptionSettingValue(recommendationCopy, optionSetting, value, skipValidation: true); + try + { + await _optionSettingHandler.SetOptionSettingValue(recommendationCopy, optionSetting, value, skipValidation: true); + } + catch (UnsupportedOptionSettingType ex) + { + _interactiveService.LogErrorMessage($"Unable to retrieve value of '{optionSetting.Name}' from previous deployment. Make sure to set it again prior to redeployment."); + _interactiveService.LogDebugMessage(ex.Message); + } } } diff --git a/src/AWS.Deploy.Recipes/RecipeDefinitions/aws-deploy-recipe-schema.json b/src/AWS.Deploy.Recipes/RecipeDefinitions/aws-deploy-recipe-schema.json index 37d57821a..503328a1a 100644 --- a/src/AWS.Deploy.Recipes/RecipeDefinitions/aws-deploy-recipe-schema.json +++ b/src/AWS.Deploy.Recipes/RecipeDefinitions/aws-deploy-recipe-schema.json @@ -323,6 +323,29 @@ } } } + }, + + { + "if": { + "properties": { "Type": { "const": "NuGetPackageReference" } } + }, + "then": { + "properties": { + "Condition": { + "type": "object", + "additionalProperties": false, + "required": [ "NuGetPackageName" ], + "properties": { + "NuGetPackageName": { + "type": "string", + "title": "NuGet Package Exists", + "description": "The name of the NuGet package to check if it being referenced by the project.", + "minLength": 1 + } + } + } + } + } } ] }, @@ -501,6 +524,7 @@ "EC2KeyPair", "Vpc", "DotnetBeanstalkPlatformArn", + "DotnetWindowsBeanstalkPlatformArn", "DotnetPublishSelfContainedBuild", "DotnetPublishBuildConfiguration", "DotnetPublishAdditionalBuildArguments", @@ -516,7 +540,11 @@ "ExistingVpcConnector", "ExistingECSCluster", "ExistingApplicationLoadBalancer", - "S3BucketName" + "S3BucketName", + "DynamoDBTableName", + "SQSQueueUrl", + "SNSTopicArn", + "FilePath" ] }, "DefaultValue": { diff --git a/test/AWS.Deploy.CLI.IntegrationTests/ServerModeTests.cs b/test/AWS.Deploy.CLI.IntegrationTests/ServerModeTests.cs index 749356852..7d6e350a0 100644 --- a/test/AWS.Deploy.CLI.IntegrationTests/ServerModeTests.cs +++ b/test/AWS.Deploy.CLI.IntegrationTests/ServerModeTests.cs @@ -5,6 +5,8 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Net; +using System.Net.Http; using System.Security.Cryptography; using System.Text; using System.Threading; @@ -62,6 +64,39 @@ public Task ResolveCredentials() return Task.FromResult(testCredentials); } + /// + /// ServerMode must only be connectable from 127.0.0.1 or localhost. This test confirms that connect attempts using + /// the host name fail. + /// + /// + [Fact] + public async Task ConfirmLocalhostOnly() + { + var portNumber = 4900; + var serverCommand = new ServerModeCommand(_serviceProvider.GetRequiredService(), portNumber, null, true); + var cancelSource = new CancellationTokenSource(); + + _ = serverCommand.ExecuteAsync(cancelSource.Token); + try + { + var restClient = new RestAPIClient($"http://localhost:{portNumber}/", ServerModeHttpClientFactory.ConstructHttpClient(ResolveCredentials)); + await WaitTillServerModeReady(restClient); + + using var client = new HttpClient(); + + var localhostUrl = $"http://localhost:{portNumber}/api/v1/Health"; + await client.GetStringAsync(localhostUrl); + + var host = Dns.GetHostName(); + var hostnameUrl = $"http://{host}:{portNumber}/api/v1/Health"; + await Assert.ThrowsAsync(async () => await client.GetStringAsync(hostnameUrl)); + } + finally + { + cancelSource.Cancel(); + } + } + [Fact] public async Task GetRecommendations() { diff --git a/test/AWS.Deploy.CLI.UnitTests/ApplyPreviousSettingsTests.cs b/test/AWS.Deploy.CLI.UnitTests/ApplyPreviousSettingsTests.cs index 0adf317aa..19b8dd508 100644 --- a/test/AWS.Deploy.CLI.UnitTests/ApplyPreviousSettingsTests.cs +++ b/test/AWS.Deploy.CLI.UnitTests/ApplyPreviousSettingsTests.cs @@ -33,7 +33,7 @@ public class ApplyPreviousSettingsTests private readonly Orchestrator _orchestrator; private readonly Mock _serviceProvider; private readonly IDeploymentManifestEngine _deploymentManifestEngine; - private readonly IOrchestratorInteractiveService _orchestratorInteractiveService; + private readonly TestToolOrchestratorInteractiveService _orchestratorInteractiveService; private readonly IDirectoryManager _directoryManager; private readonly IFileManager _fileManager; private readonly IRecipeHandler _recipeHandler; @@ -55,7 +55,7 @@ public ApplyPreviousSettingsTests() var optionSettingHandler = new OptionSettingHandler(validatorFactory); _recipeHandler = new RecipeHandler(_deploymentManifestEngine, _orchestratorInteractiveService, _directoryManager, _fileManager, optionSettingHandler, validatorFactory); _optionSettingHandler = new OptionSettingHandler(new ValidatorFactory(_serviceProvider.Object)); - _orchestrator = new Orchestrator(null, null, null, null, null, null, null, null, null, null, null, null, null, _optionSettingHandler, null); + _orchestrator = new Orchestrator(null, _orchestratorInteractiveService, null, null, null, null, null, null, null, null, null, null, null, _optionSettingHandler, null); } private async Task BuildRecommendationEngine(string testProjectName) @@ -76,6 +76,31 @@ await parser.Parse(fullPath), return new RecommendationEngine(session, _recipeHandler); } + [Fact] + public async Task ApplyPreviousSettings_InvalidType() + { + var engine = await BuildRecommendationEngine("WebAppWithDockerFile"); + + var recommendations = await engine.ComputeRecommendations(); + + var fargateRecommendation = recommendations.First(r => r.Recipe.Id == Constants.ASPNET_CORE_ASPNET_CORE_FARGATE_RECIPE_ID); + + + var serializedSettings = @$" + {{ + ""AdditionalECSServiceSecurityGroups"": ""sg-1234abcd"" + }}"; + + var settings = JsonConvert.DeserializeObject>(serializedSettings); + + fargateRecommendation = await _orchestrator.ApplyRecommendationPreviousSettings(fargateRecommendation, settings); + + var additionalECSServiceSecurityGroupsOptionSetting = fargateRecommendation.Recipe.OptionSettings.First(optionSetting => optionSetting.Id.Equals("AdditionalECSServiceSecurityGroups")); + + Assert.Contains($"Unable to retrieve value of '{additionalECSServiceSecurityGroupsOptionSetting.Name}' from previous deployment. Make sure to set it again prior to redeployment.", + _orchestratorInteractiveService.ErrorMessages); + } + [Theory] [InlineData(true, null)] [InlineData(false, "arn:aws:iam::123456789012:group/Developers")] diff --git a/test/AWS.Deploy.CLI.UnitTests/SetOptionSettingTests.cs b/test/AWS.Deploy.CLI.UnitTests/SetOptionSettingTests.cs index bdbe0bf0d..174a7a986 100644 --- a/test/AWS.Deploy.CLI.UnitTests/SetOptionSettingTests.cs +++ b/test/AWS.Deploy.CLI.UnitTests/SetOptionSettingTests.cs @@ -142,7 +142,7 @@ public async Task SetOptionSettingTests_KeyValueType_Error() var recommendation = _recommendations.First(r => r.Recipe.Id == Constants.ASPNET_CORE_BEANSTALK_LINUX_RECIPE_ID); var optionSetting = recommendation.Recipe.OptionSettings.First(x => x.Id.Equals("ElasticBeanstalkEnvironmentVariables")); - await Assert.ThrowsAsync(async () => await _optionSettingHandler.SetOptionSettingValue(recommendation, optionSetting, "string")); + await Assert.ThrowsAsync(async () => await _optionSettingHandler.SetOptionSettingValue(recommendation, optionSetting, "string")); } /// diff --git a/version.json b/version.json index 3d784fe69..ea546f012 100644 --- a/version.json +++ b/version.json @@ -1,6 +1,6 @@ { "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", - "version": "1.0", + "version": "1.1", "publicReleaseRefSpec": [ ".*" ],