Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ec2): CloudFormation-init support #9065

Merged
merged 8 commits into from
Aug 13, 2020
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 85 additions & 17 deletions packages/@aws-cdk/aws-ec2/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
## Amazon EC2 Construct Library
njlynch marked this conversation as resolved.
Show resolved Hide resolved

<!--BEGIN STABILITY BANNER-->
---

Expand Down Expand Up @@ -48,10 +49,9 @@ distinguishes three different subnet types:
connected to from other instances in the same VPC. A default VPC configuration
will not include isolated subnets,


A default VPC configuration will create public and **private** subnets. However, if
`natGateways:0` **and** `subnetConfiguration` is undefined, default VPC configuration
will create public and **isolated** subnets. See [*Advanced Subnet Configuration*](#advanced-subnet-configuration)
`natGateways:0` **and** `subnetConfiguration` is undefined, default VPC configuration
will create public and **isolated** subnets. See [*Advanced Subnet Configuration*](#advanced-subnet-configuration)
below for information on how to change the default subnet configuration.

Constructs using the VPC will "launch instances" (or more accurately, create
Expand All @@ -68,7 +68,6 @@ created by setting the `natGateways` property to a lower value (the default
is one NAT gateway per availability zone). Be aware that this may have
availability implications for your application.


[Read more about
subnets](https://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_Subnets.html).

Expand Down Expand Up @@ -438,7 +437,9 @@ examples of things you might want to use:
> [Runtime Context](https://docs.aws.amazon.com/cdk/latest/guide/context.html) in the CDK
> developer guide.

## VPN connections to a VPC
## Special VPC configurations

### VPN connections to a VPC

Create your VPC with VPN connections by specifying the `vpnConnections` props (keys are construct `id`s):

Expand Down Expand Up @@ -468,6 +469,7 @@ const vpc = new ec2.Vpc(this, 'MyVpc', {
```

VPN connections can then be added:

```ts fixture=with-vpc
vpc.addVpnConnection('Dynamic', {
ip: '1.2.3.4'
Expand All @@ -491,14 +493,15 @@ const vpnConnection = vpc.addVpnConnection('Dynamic', {
const state = vpnConnection.metricTunnelState();
```

## VPC endpoints
### VPC endpoints

A VPC endpoint enables you to privately connect your VPC to supported AWS services and VPC endpoint services powered by PrivateLink without requiring an internet gateway, NAT device, VPN connection, or AWS Direct Connect connection. Instances in your VPC do not require public IP addresses to communicate with resources in the service. Traffic between your VPC and the other service does not leave the Amazon network.

Endpoints are virtual devices. They are horizontally scaled, redundant, and highly available VPC components that allow communication between instances in your VPC and services without imposing availability risks or bandwidth constraints on your network traffic.

[example of setting up VPC endpoints](test/integ.vpc-endpoint.lit.ts)

By default, CDK will place a VPC endpoint in one subnet per AZ. If you wish to override the AZs CDK places the VPC endpoint in,
By default, CDK will place a VPC endpoint in one subnet per AZ. If you wish to override the AZs CDK places the VPC endpoint in,
use the `subnets` parameter as follows:

```ts
Expand Down Expand Up @@ -528,7 +531,8 @@ new InterfaceVpcEndpoint(stack, 'VPC Endpoint', {
});
```

### Security groups for interface VPC endpoints
#### Security groups for interface VPC endpoints

By default, interface VPC endpoints create a new security group and traffic is **not**
automatically allowed from the VPC CIDR.

Expand All @@ -540,7 +544,8 @@ myEndpoint.connections.allowDefaultPortFromAnyIpv4();

Alternatively, existing security groups can be used by specifying the `securityGroups` prop.

## VPC endpoint services
### VPC endpoint services

A VPC endpoint service enables you to expose a Network Load Balancer(s) as a provider service to consumers, who connect to your service over a VPC endpoint. You can restrict access to your service via whitelisted principals (anything that extends ArnPrincipal), and require that new connections be manually accepted.

```ts
Expand All @@ -551,17 +556,73 @@ new VpcEndpointService(this, 'EndpointService', {
});
```

## Bastion Hosts
## Instances

You can use the `Instance` class to start up a single EC2 instance. For production setups, we recommend
you use an `AutoScalingGroup` from the `aws-autoscaling` module instead, as AutoScalingGroups will take
care of restarting your instance if it ever fails.
nija-at marked this conversation as resolved.
Show resolved Hide resolved

### Configuring Instances using CloudFormation Init (cfn-init)

CloudFormation Init allows you to configure your instances by writing files to them, installing software
packages, starting services and running arbitrary commands. By default, if any of the instance setup
commands throw an error, the deployment will fail and roll back to the previously known good state.
The following documentation also applies to `AutoScalingGroup`s.

For the full set of capabilities of this system, see the documentation for
[`AWS::CloudFormation::Init`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-init.html).
Here is an example of applying some configuration to an instance:

```ts
new ec2.Instance(this, 'Instance', {
init: ec2.CloudFormationInit.fromElements(
ec2.InitCommand.shellCommand('/bin/true'),
),
initOptions: {
// Optional, which configsets to activate (['default'] by default)
configSets: ['default'],
njlynch marked this conversation as resolved.
Show resolved Hide resolved

// Optional, how long the installation is expected to take (5 minutes by default)
timeout: Duration.minutes(30),
},

// Optional but recommended: starts a new instance with the new config
// if the config changes.
userDataCausesReplacement: true,
njlynch marked this conversation as resolved.
Show resolved Hide resolved
njlynch marked this conversation as resolved.
Show resolved Hide resolved
});
```

You can have services restarted after the init process has made changes to the system.
To do that, instantiate an `InitServiceRestartHandle` and pass it to the config elements
that need to trigger the restart and the service itself. For example, the following
config installs nginx through a custom script, and then
restarts nginx so that it picks up the new config and files:

```ts
const handle = new ec2.InitServiceRestartHandle();
njlynch marked this conversation as resolved.
Show resolved Hide resolved

ec2.CloudFormationInit.fromElements(
ec2.InitCommand.shellCommand('/usr/bin/custom-nginx-install.sh', { serviceRestartHandles: [handle] }),
ec2.InitService.enable('nginx', {
njlynch marked this conversation as resolved.
Show resolved Hide resolved
serviceRestartHandle: handle,
})
);
```

### Bastion Hosts

A bastion host functions as an instance used to access servers and resources in a VPC without open up the complete VPC on a network level.
You can use bastion hosts using a standard SSH connection targetting port 22 on the host. As an alternative, you can connect the SSH connection
feature of AWS Systems Manager Session Manager, which does not need an opened security group. (https://aws.amazon.com/about-aws/whats-new/2019/07/session-manager-launches-tunneling-support-for-ssh-and-scp/)
feature of AWS Systems Manager Session Manager, which does not need an opened security group. (<https://aws.amazon.com/about-aws/whats-new/2019/07/session-manager-launches-tunneling-support-for-ssh-and-scp/>)
njlynch marked this conversation as resolved.
Show resolved Hide resolved

A default bastion host for use via SSM can be configured like:

```ts fixture=with-vpc
const host = new ec2.BastionHostLinux(this, 'BastionHost', { vpc });
```

If you want to connect from the internet using SSH, you need to place the host into a public subnet. You can then configure allowed source hosts.

```ts fixture=with-vpc
const host = new ec2.BastionHostLinux(this, 'BastionHost', {
vpc,
Expand All @@ -574,6 +635,7 @@ As there are no SSH public keys deployed on this machine, you need to use [EC2 I
with the command `aws ec2-instance-connect send-ssh-public-key` to provide your SSH public key.

EBS volume for the bastion host can be encrypted like:

```ts
const host = new ec2.BastionHostLinux(stack, 'BastionHost', {
vpc,
Expand All @@ -586,7 +648,7 @@ EBS volume for the bastion host can be encrypted like:
});
```

## Block Devices
### Block Devices

To add EBS block device mappings, specify the `blockDeviceMappings` property. The follow example sets the EBS-backed
root device (`/dev/sda1`) size to 50 GiB, and adds another EBS-backed device mapped to `/dev/sdm` that is 100 GiB in
Expand All @@ -609,7 +671,7 @@ new ec2.Instance(this, 'Instance', {

```

## Volumes
### Volumes

Whereas a `BlockDeviceVolume` is an EBS volume that is created and destroyed as part of the creation and destruction of a specific instance. A `Volume` is for when you want an EBS volume separate from any particular instance. A `Volume` is an EBS block device that can be attached to, or detached from, any instance at any time. Some types of `Volume`s can also be attached to multiple instances at the same time to allow you to have shared storage between those instances.

Expand All @@ -633,7 +695,7 @@ const volume = new ec2.Volume(this, 'Volume', {
volume.grantAttachVolume(role, [instance]);
```

### Instances Attaching Volumes to Themselves
#### Instances Attaching Volumes to Themselves

If you need to grant an instance the ability to attach/detach an EBS volume to/from itself, then using `grantAttachVolume` and `grantDetachVolume` as outlined above
will lead to an unresolvable circular reference between the instance role and the instance. In this case, use `grantAttachVolumeByResourceTag` and `grantDetachVolumeByResourceTag` as follows:
Expand All @@ -650,7 +712,7 @@ const attachGrant = volume.grantAttachVolumeByResourceTag(instance.grantPrincipa
const detachGrant = volume.grantDetachVolumeByResourceTag(instance.grantPrincipal, [instance]);
```

### Attaching Volumes
#### Attaching Volumes

The Amazon EC2 documentation for
[Linux Instances](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AmazonEBS.html) and
Expand Down Expand Up @@ -678,7 +740,8 @@ instance.userData.addCommands(
```

## VPC Flow Logs
VPC Flow Logs is a feature that enables you to capture information about the IP traffic going to and from network interfaces in your VPC. Flow log data can be published to Amazon CloudWatch Logs and Amazon S3. After you've created a flow log, you can retrieve and view its data in the chosen destination. (https://docs.aws.amazon.com/vpc/latest/userguide/flow-logs.html).

VPC Flow Logs is a feature that enables you to capture information about the IP traffic going to and from network interfaces in your VPC. Flow log data can be published to Amazon CloudWatch Logs and Amazon S3. After you've created a flow log, you can retrieve and view its data in the chosen destination. (<https://docs.aws.amazon.com/vpc/latest/userguide/flow-logs.html>).

By default a flow log will be created with CloudWatch Logs as the destination.

Expand All @@ -689,6 +752,7 @@ new ec2.FlowLog(this, 'FlowLog', {
resourceType: ec2.FlowLogResourceType.fromVpc(vpc)
})
```

Or you can add a Flow Log to a VPC by using the addFlowLog method like this:

```ts
Expand Down Expand Up @@ -718,6 +782,7 @@ the log group. In the case of an S3 destination, it will create the S3 bucket.
If you want to customize any of the destination resources you can provide your own as part of the `destination`.

*CloudWatch Logs*

```ts
const logGroup = new logs.LogGroup(this, 'MyCustomLogGroup');

Expand All @@ -732,6 +797,7 @@ new ec2.FlowLog(this, 'FlowLog', {
```

*S3*

```ts

const bucket = new s3.Bucket(this, 'MyCustomBucket');
Expand All @@ -743,10 +809,12 @@ new ec2.FlowLog(this, 'FlowLog', {
```

## User Data

User data enables you to run a script when your instances start up. In order to configure these scripts you can add commands directly to the script
or you can use the UserData's convenience functions to aid in the creation of your script.

A user data could be configured to run a script found in an asset through the following:

```ts
const asset = new Asset(this, 'Asset', {path: path.join(__dirname, 'configure.sh')});
const instance = new ec2.Instance(this, 'Instance', {
Expand Down Expand Up @@ -783,4 +851,4 @@ const subnet = Subnet.fromSubnetAttributes(this, 'SubnetFromAttributes', {

// Supply only subnet id
const subnet = Subnet.fromSubnetId(this, 'SubnetFromId', 's-1234');
```
```
Loading