Skip to content

Commit

Permalink
Merge pull request #691 from aws/dev
Browse files Browse the repository at this point in the history
chore: release 1.2
  • Loading branch information
philasmar authored Aug 24, 2022
2 parents 5c57ebd + 531621d commit 66fb1e3
Show file tree
Hide file tree
Showing 43 changed files with 936 additions and 159 deletions.
27 changes: 18 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,26 @@
# AWS .NET deployment tool [![nuget](https://img.shields.io/nuget/v/AWS.Deploy.Tools.svg) ![downloads](https://img.shields.io/nuget/dt/AWS.Deploy.Tools.svg)](https://www.nuget.org/packages/AWS.Deploy.Tools/)

This repository contains the AWS .NET deployment tool for .NET CLI - the opinionated tooling that simplifies deployment of .NET applications. The tool suggests the right AWS compute service to deploy your application to. It then builds and packages your application as required by the chosen compute service, generates the deployment infrastructure, deploys your application by using the appropriate deployment engine (Cloud Development Kit (CDK) or native service APIs), and displays the endpoint.
## Overview
This repository contains the AWS Deploy Tool for .NET CLI - the opinionated tooling that simplifies deployment of .NET applications. The tool suggests the right AWS compute service to deploy your application to. It then builds and packages your application as required by the chosen compute service, generates the deployment infrastructure, deploys your application by using the appropriate deployment engine (Cloud Development Kit (CDK) or native service APIs), and displays the endpoint.

The tool assumes minimal knowledge of AWS. It is designed to guide you through the deployment process and provides suggested defaults. The tool will show you all compute service options available to deploy your application, and will recommend a default with information about why it was chosen. The other compute service options will be shown with an explanation of their differences. If the selected compute option does not match your needs, you can select a different compute service.

The goal of the deployment tool is to deploy cloud-native .NET applications that are built with .NET Core 3.1 and above. A cloud-native .NET application is written in .NET with the intent to deploy to Linux. It is not tied to any Windows specific technology such as Windows registry, IIS or MSMQ, and can be deployed on virtualized compute. The tool **cannot** be used to deploy .NET Framework, Desktop, Xamarin, or other applications that do not fit the "cloud-native" criteria.

## Project Status
We are looking for feedback on the type of applications users want to deploy to AWS and what features are important to them. Please provide your feedback by opening an [issue in this repository](https://github.com/aws/aws-dotnet-deploy/issues).
We welcome your feedback! Please let us know what you think by opening an [issue](https://github.com/aws/aws-dotnet-deploy/issues).


## Useful Links
* [Complete Documentation Guide on GitHub.io](https://aws.github.io/aws-dotnet-deploy/)
* [AWS Deploy Tool for .NET on NuGet](https://www.nuget.org/packages/AWS.Deploy.Tools)
* Blog posts:
* [AWS Streamlines Deployment experience for .NET applications](https://aws.amazon.com/blogs/developer/aws-announces-a-streamlined-deployment-experience-for-net-applications/)
* [Reimagining the AWS .NET deployment experience](http://aws.amazon.com/blogs/developer/reimagining-the-aws-net-deployment-experience/)
* [Update on our new AWS .NET Deployment Experience](https://aws.amazon.com/blogs/developer/update-new-net-deployment-experience/)
* [Deployment Projects with the new AWS .NET Deployment Experience](https://aws.amazon.com/blogs/developer/dotnet-deployment-projects/)
* Youtube videos:
* [AWS On Air ft. New .NET Deployment Experience - Command Line](https://www.youtube.com/watch?v=5uyL8MXxljc)
* [Re:Invent 2021: “What’s new with .NET development and deployment on AWS”](https://www.youtube.com/watch?v=UvTJ_Inb634)

## Pre-requisites

Expand All @@ -24,6 +37,7 @@ To take advantage of this library you’ll need:
* Used when deploying to a container based service like Amazon Elastic Container Service (Amazon ECS)
* (optional) The zip cli tool
* Mac / Linux only. Used when creating zip packages for deployment bundles. The zip cli is used to maintain Linux file permissions.

## Getting started

The deployment tool is distributed as a .NET Tool from NuGet.org. The installation of the tool is managed with the `dotnet` CLI.
Expand Down Expand Up @@ -135,15 +149,10 @@ We welcome community contributions and pull requests. See [CONTRIBUTING](https:/

## Additional Resources

* [AWS Developer Guide](https://docs.aws.amazon.com/sdk-for-net/v3/developer-guide/deployment-tool.html) Find additional information about developing, deploying, and maintaining your applications using the AWS .Net deployment tool.
* [AWS Developer Center - Explore .NET on AWS](https://aws.amazon.com/developer/language/net/) Find all the .NET code samples, step-by-step guides, videos, blog content, tools, and information about live events that you need in one place.
* [AWS Developer Blog - .NET](https://aws.amazon.com/blogs/developer/category/programing-language/dot-net/) Come see what .NET developers at AWS are up to! Learn about new .NET software announcements, guides, and how-to's.
* [AWS re:Invent 2021 - What’s new with .NET development and deployment on AWS](https://www.youtube.com/watch?v=UvTJ_Inb634) New deployment tooling incorporates best AWS practices right from the start, providing you with recommendations and the optimal deployment option for your .NET application.
* [@dotnetonaws](https://twitter.com/dotnetonaws) Follow us on twitter!
* Deployment tool blog posts
* [Reimagining the AWS .NET deployment experience](http://aws.amazon.com/blogs/developer/reimagining-the-aws-net-deployment-experience/)
* [Update on our new AWS .NET Deployment Experience](https://aws.amazon.com/blogs/developer/update-new-net-deployment-experience/)
* [Deployment Projects with the new AWS .NET Deployment Experience](https://aws.amazon.com/blogs/developer/dotnet-deployment-projects/)


## License

Expand Down
2 changes: 2 additions & 0 deletions src/AWS.Deploy.CLI/Commands/DeployCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,8 @@ private async Task<Recommendation> GetSelectedRecommendationFromPreviousDeployme
else
previousSettings = await _deployedApplicationQueryer.GetPreviousSettings(deployedApplication);

await orchestrator.ApplyAllReplacementTokens(selectedRecommendation, deployedApplication.Name);

selectedRecommendation = await orchestrator.ApplyRecommendationPreviousSettings(selectedRecommendation, previousSettings);

var header = $"Loading {deployedApplication.DisplayName} settings:";
Expand Down
190 changes: 190 additions & 0 deletions src/AWS.Deploy.CLI/Commands/TypeHints/ElasticBeanstalkVpcCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Amazon.EC2.Model;
using Amazon.ECS.Model;
using AWS.Deploy.CLI.Extensions;
using AWS.Deploy.CLI.TypeHintResponses;
using AWS.Deploy.Common;
using AWS.Deploy.Common.Data;
using AWS.Deploy.Common.Recipes;
using AWS.Deploy.Common.TypeHintData;
using AWS.Deploy.Orchestration;
using AWS.Deploy.Orchestration.Data;
using Newtonsoft.Json;

namespace AWS.Deploy.CLI.Commands.TypeHints
{
/// <summary>
/// The <see cref="ElasticBeanstalkVpcCommand"/> type hint orchestrates the VPC object in Elastic Beanstalk environments.
/// </summary>
public class ElasticBeanstalkVpcCommand : ITypeHintCommand
{
private readonly IAWSResourceQueryer _awsResourceQueryer;
private readonly IConsoleUtilities _consoleUtilities;
private readonly IToolInteractiveService _toolInteractiveService;
private readonly IOptionSettingHandler _optionSettingHandler;

public ElasticBeanstalkVpcCommand(IAWSResourceQueryer awsResourceQueryer, IConsoleUtilities consoleUtilities, IToolInteractiveService toolInteractiveService, IOptionSettingHandler optionSettingHandler)
{
_awsResourceQueryer = awsResourceQueryer;
_consoleUtilities = consoleUtilities;
_toolInteractiveService = toolInteractiveService;
_optionSettingHandler = optionSettingHandler;
}

private async Task<List<Vpc>> GetData()
{
return await _awsResourceQueryer.GetListOfVpcs();
}

public async Task<TypeHintResourceTable> GetResources(Recommendation recommendation, OptionSettingItem optionSetting)
{
var vpcs = await GetData();
var resourceTable = new TypeHintResourceTable();

resourceTable.Rows = vpcs.ToDictionary(x => x.VpcId, x => x.GetDisplayableVpc()).Select(x => new TypeHintResource(x.Key, x.Value)).ToList();

return resourceTable;
}

public async Task<object> Execute(Recommendation recommendation, OptionSettingItem optionSetting)
{
_toolInteractiveService.WriteLine();

// Ask user if they want to Use a VPC
var useVpcOptionSetting = optionSetting.ChildOptionSettings.First(x => x.Id.Equals("UseVPC"));
var useVpcValue = _optionSettingHandler.GetOptionSettingValue<string>(recommendation, useVpcOptionSetting) ?? "false";
var useVpcAnswer = _consoleUtilities.AskYesNoQuestion(useVpcOptionSetting.Description, useVpcValue);
var useVpc = useVpcAnswer == YesNo.Yes;

// If user doesn't want to Use VPC, no need to continue
if (!useVpc)
return new ElasticBeanstalkVpcTypeHintResponse()
{
UseVPC = false
};

// Retrieve all the available VPCs
var vpcs = await GetData();

// If there are no VPCs, create a new one
if (!vpcs.Any())
{
_toolInteractiveService.WriteLine();
_toolInteractiveService.WriteLine("There are no VPCs in the selected account. The only option is to create a new one.");
return new ElasticBeanstalkVpcTypeHintResponse
{
UseVPC = true,
CreateNew = true
};
}

// Ask user to select a VPC from the available ones
_toolInteractiveService.WriteLine();
var currentVpcTypeHintResponse = optionSetting.GetTypeHintData<ElasticBeanstalkVpcTypeHintResponse>();
var vpcOptionSetting = optionSetting.ChildOptionSettings.First(x => x.Id.Equals("VpcId"));
var currentVpcValue = _optionSettingHandler.GetOptionSettingValue(recommendation, vpcOptionSetting).ToString();
var userInputConfigurationVPCs = new UserInputConfiguration<Vpc>(
idSelector: vpc => vpc.VpcId,
displaySelector: vpc => vpc.GetDisplayableVpc(),
defaultSelector: vpc =>
!string.IsNullOrEmpty(currentVpcTypeHintResponse?.VpcId)
? vpc.VpcId == currentVpcTypeHintResponse.VpcId
: vpc.IsDefault)
{
CanBeEmpty = false,
CreateNew = true
};
var vpc = _consoleUtilities.AskUserToChooseOrCreateNew<Vpc>(vpcs, "Select a VPC:", userInputConfigurationVPCs);

// Create a new VPC if the user wants to do that
if (vpc.CreateNew)
return new ElasticBeanstalkVpcTypeHintResponse
{
UseVPC = true,
CreateNew = true
};

// If for some reason an option was not selected, don't use a VPC
if (vpc.SelectedOption == null)
return new ElasticBeanstalkVpcTypeHintResponse
{
UseVPC = false
};

// Retrieve available Subnets based on the selected VPC
var availableSubnets = (await _awsResourceQueryer.DescribeSubnets(vpc.SelectedOption.VpcId)).OrderBy(x => x.SubnetId).ToList();

// If there are no subnets, don't use a VPC
if (!availableSubnets.Any())
{
_toolInteractiveService.WriteLine();
_toolInteractiveService.WriteLine("The selected VPC does not have any Subnets. Please select a VPC with Subnets.");
return new ElasticBeanstalkVpcTypeHintResponse
{
UseVPC = false
};
}

// Ask user to select subnets based on the selected VPC
var userInputConfigurationSubnets = new UserInputConfiguration<Subnet>(
idSelector: subnet => subnet.SubnetId,
displaySelector: subnet => $"{subnet.SubnetId.PadRight(24)} | {subnet.VpcId.PadRight(21)} | {subnet.AvailabilityZone}",
defaultSelector: subnet => false)
{
CanBeEmpty = false,
CreateNew = false
};
var subnetsOptionSetting = optionSetting.ChildOptionSettings.First(x => x.Id.Equals("Subnets"));
_toolInteractiveService.WriteLine($"{subnetsOptionSetting.Id}:");
_toolInteractiveService.WriteLine(subnetsOptionSetting.Description);
var subnets = _consoleUtilities.AskUserForList<Subnet>(userInputConfigurationSubnets, availableSubnets, subnetsOptionSetting, recommendation);

// Retrieve available security groups based on the selected VPC
var availableSecurityGroups = (await _awsResourceQueryer.DescribeSecurityGroups(vpc.SelectedOption.VpcId)).OrderBy(x => x.VpcId).ToList();
if (!availableSecurityGroups.Any())
return new ElasticBeanstalkVpcTypeHintResponse
{
UseVPC = true,
CreateNew = false,
VpcId = vpc.SelectedOption.VpcId,
Subnets = subnets
};

// Get the length of the longest group name to do padding when displaying the security groups
var groupNamePadding = 0;
availableSecurityGroups.ForEach(x =>
{
if (x.GroupName.Length > groupNamePadding)
groupNamePadding = x.GroupName.Length;
});

// Ask user to select security groups
var userInputConfigurationSecurityGroups = new UserInputConfiguration<SecurityGroup>(
idSelector: securityGroup => securityGroup.GroupId,
displaySelector: securityGroup => $"{securityGroup.GroupName.PadRight(groupNamePadding)} | {securityGroup.GroupId.PadRight(20)} | {securityGroup.VpcId}",
defaultSelector: securityGroup => false)
{
CanBeEmpty = false,
CreateNew = false
};
var securityGroupsOptionSetting = optionSetting.ChildOptionSettings.First(x => x.Id.Equals("SecurityGroups"));
_toolInteractiveService.WriteLine($"{securityGroupsOptionSetting.Id}:");
_toolInteractiveService.WriteLine(securityGroupsOptionSetting.Description);
var securityGroups = _consoleUtilities.AskUserForList<SecurityGroup>(userInputConfigurationSecurityGroups, availableSecurityGroups, securityGroupsOptionSetting, recommendation);

return new ElasticBeanstalkVpcTypeHintResponse
{
UseVPC = true,
CreateNew = false,
VpcId = vpc.SelectedOption.VpcId,
Subnets = subnets,
SecurityGroups = securityGroups
};
}
}
}
36 changes: 6 additions & 30 deletions src/AWS.Deploy.CLI/Commands/TypeHints/ExistingVpcCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Linq;
using System.Threading.Tasks;
using Amazon.EC2.Model;
using AWS.Deploy.CLI.Extensions;
using AWS.Deploy.Common;
using AWS.Deploy.Common.Data;
using AWS.Deploy.Common.Recipes;
Expand All @@ -13,6 +14,9 @@

namespace AWS.Deploy.CLI.Commands.TypeHints
{
/// <summary>
/// The <see cref="ExistingVpcCommand"/> type hint lists existing VPC in an account for an option setting of type <see cref="OptionSettingValueType.String"/>.
/// </summary>
public class ExistingVpcCommand : ITypeHintCommand
{
private readonly IAWSResourceQueryer _awsResourceQueryer;
Expand All @@ -37,21 +41,7 @@ public async Task<TypeHintResourceTable> GetResources(Recommendation recommendat

var resourceTable = new TypeHintResourceTable();

resourceTable.Rows = vpcs.ToDictionary(x => x.VpcId, x =>
{
var name = x.Tags?.FirstOrDefault(x => x.Key == "Name")?.Value ?? string.Empty;
var namePart =
string.IsNullOrEmpty(name)
? ""
: $" ({name}) ";

var isDefaultPart =
x.IsDefault
? " *** Account Default VPC ***"
: "";

return $"{x.VpcId}{namePart}{isDefaultPart}";
}).Select(x => new TypeHintResource(x.Key, x.Value)).ToList();
resourceTable.Rows = vpcs.ToDictionary(x => x.VpcId, x => x.GetDisplayableVpc()).Select(x => new TypeHintResource(x.Key, x.Value)).ToList();

return resourceTable;
}
Expand All @@ -63,21 +53,7 @@ public async Task<object> Execute(Recommendation recommendation, OptionSettingIt

var userInputConfig = new UserInputConfiguration<Vpc>(
idSelector: vpc => vpc.VpcId,
displaySelector: vpc =>
{
var name = vpc.Tags?.FirstOrDefault(x => x.Key == "Name")?.Value ?? string.Empty;
var namePart =
string.IsNullOrEmpty(name)
? ""
: $" ({name}) ";

var isDefaultPart =
vpc.IsDefault
? " *** Account Default VPC ***"
: "";

return $"{vpc.VpcId}{namePart}{isDefaultPart}";
},
displaySelector: vpc => vpc.GetDisplayableVpc(),
defaultSelector: vpc =>
!string.IsNullOrEmpty(currentVpcValue)
? vpc.VpcId == currentVpcValue
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ public TypeHintCommandFactory(IServiceProvider serviceProvider, IToolInteractive
{ OptionSettingTypeHint.ExistingSecurityGroups, ActivatorUtilities.CreateInstance<ExistingSecurityGroupsCommand>(serviceProvider) },
{ OptionSettingTypeHint.VPCConnector, ActivatorUtilities.CreateInstance<VPCConnectorCommand>(serviceProvider) },
{ OptionSettingTypeHint.FilePath, ActivatorUtilities.CreateInstance<FilePathCommand>(serviceProvider) },
{ OptionSettingTypeHint.ElasticBeanstalkVpc, ActivatorUtilities.CreateInstance<ElasticBeanstalkVpcCommand>(serviceProvider) },
};
}

Expand Down
Loading

0 comments on commit 66fb1e3

Please sign in to comment.