Amazon VPC Lattice L2 Construct
- Dominant language
- JavaScript
- Stars
- 572
- Forks
- 108
- Avg merge
- 2m
- Merged PRs (30d)
- 1
Description
## Description
This RFC proposes a new L2 module for CDK to support AWS VPC Lattice.
This RFC supercedes [#502](https://github.com/aws/aws-cdk-rfcs/issues/502#issue-1715838721)
## Roles
| Role | User
|---------------------|------------------------------
| Proposed by | @clopca
| Author(s) | @clopca, @aws-rafams
| API Bar Raiser | @TheRealAmazonKendra
| Stakeholders | @TBD
> See [RFC Process](https://github.com/aws/aws-cdk-rfcs#rfc-process) for details
## Workflow
- [x] Tracking issue created (label: `status/proposed`)
- [ ] API bar raiser assigned (ping us at [#aws-cdk-rfcs](https://cdk-dev.slack.com/archives/C025ZFGMUCD) if needed)
- [ ] Kick off meeting
- [ ] RFC pull request submitted (label: `status/review`)
- [ ] Community reach out (via Slack and/or Twitter)
- [ ] API signed-off (label `status/api-approved` applied to pull request)
- [ ] Final comments period (label: `status/final-comments-period`)
- [ ] Approved and merged (label: `status/approved`)
- [ ] Execution plan submitted (label: `status/planning`)
- [ ] Plan approved and merged (label: `status/implementing`)
- [ ] Implementation complete (label: `status/done`)
---
> Author is responsible to progress the RFC according to this checklist, and
apply the relevant labels to this issue so that the RFC table in README gets
updated.
## (vpclattice): L2 for Amazon VPC Lattice #25452
### USER STORIES
#### As Any User I would like to:
- Have a intuitive, low code user experience
- Abstract the unerlying complexities to allow different personas to use the same CDK Construct to deploy and manage aspects of the Amazon VPC Lattice configuration
- Integration between this construct and other CDK constructs
- Apply secure, best practice configurations as default
#### As a Service Owner I would like to:
- Publish my service for others to access
- Control Authentication and Authorisation for my services
- Control load distribution across my compute resources
- Hide the implementation detail of my services from consumers
#### As a Service Consumer I would like to:
- Be able have my resources use services that are available to me in my VPCs
- Authenticate with services
- Restrict access for services to security groups that I decide
#### As a Platform/Network Admin I would like to:
- Create Service Networks for Owners and Consumers to use
# README
## VpcLattice Constructs
- **Service**: An independently deployable unit of software that delivers a specific task or function. A service can run on EC2 instances or ECS containers, or as Lambda functions, within an account or a virtual private cloud (VPC). A VPC Lattice service has the following components: target groups, listeners, and rules.
- **Target Group**: A collection of resources, also known as targets, that run your application or service. Targets can be EC2 instances, IP addresses, Lambda functions, Application Load Balancers, or Kubernetes Pods. These are similar to the target groups provided by Elastic Load Balancing, but they are not interchangeable. A target group can be associated with a listener, and the listener can route requests to the targets in the target group.
- **Listener**: A process that checks for connection requests, and routes them to targets in a target group. A service can have up to two listeners, using the HTTP and HTTPS protocols and port numbers from 1 to 65535.
- **Rule**: A default component of a listener that forwards requests to the targets in a VPC Lattice target group. Each rule consists of a priority, one or more actions, and one or more conditions. Rules determines how the listener routes client requests.
- **Service Network**: A logical boundary for a collection of services. A client is any resource deployed in a VPC that is associated with the service network. To communicate with services in a certain service network, clients and other services must be associated with the same service network. They must also be authenticated and authorized by the service network or the specific service for this communication to take place.
- **ServiceNetwork Service Association**: A Service association is a relationship between a Service and a Service Network. When you associate a Service with a Service Network, it enables all the targets within that Service to be clients and communicate with other services associated to that same Service Network.
- **ServiceNetwork VPC Association**: A VPC association is a relationship between a VPC and a service network. When you associate a VPC with a service network, it enables all the targets within that VPC to be clients and communicate with other services associated to that same service network.
- **Auth Policy**: Fine-grained authorization policies that can be used to define access to services. You can attach separate auth policies to individual services or to the service network. For example, you can create a policy for how a payment service running on an auto scaling group of EC2 instances should interact with a billing service running in AWS Lambda.
- **AccessLogSubscription**: Enables access logs to be sent to Amazon CloudWatch, Amazon S3, and Amazon Kinesis Data Firehose. The service network owner can use the access logs to audit the services in the network. The service network owner can only see access logs from clients and services that are associated with their service network. Access log entries represent traffic originated from VPCs associated with that network. For more information, see Access logs in the Amazon VPC Lattice User Guide.
## Service
In VPC Lattice, we refer to a microservice as a service. It represents an independently deployable unit of software that delivers a specific task or function.
To create a service, use the `Service` Construct:
```ts nofixture
const logBucket = new Bucket(stack, 'LogsBucket', {});
new Service(stack, 'Service', {
authType: vpclattice.AuthType.AWS_IAM,
name: 'my-custom-name',
removalPolicy: cdk.RemovalPolicy.DESTROY,
loggingConfiguration: {
s3Bucket: logBucket
}
});
```
### Service access
Access settings enable you to configure and manage client access to a service.
Access settings include auth type and auth policies. Auth policies help you authenticate and authorize traffic flowing to services within VPC Lattice.
You can apply auth policies at the service network level, the service level, or both. At the service level, service owners can apply fine-grained controls, which can be more restrictive.
- You can chooose to not define an auth policy:
```ts
const testSvc = new Service(stack, 'Parking', {
...
authType: AuthType.NONE,
...
});
```
- You can chooose a predefined policy template:
```ts
const testSvc = new Service(stack, 'Parking', {
...
authType: AuthType.AWS_IAM,
authPolicy: AuthPolicyDocument.UNAUTHENTICATED,
...
});
```
- You can create a custom auth policy document using higher level statements:
```ts
const testSvc = new Service(stack, 'Parking', {
...
authType: AuthType.AWS_IAM,
authPolicy: new AuthPolicyDocument({
statements: [
AuthPolicyStatement.allowOnlyRole(...)
AuthPolicyStatement.allowOnlyOrganization(...)
]
}),
...
});
```
- You can create a custom auth policy document using low level statements:
```ts
const testSvc = new Service(stack, 'Parking', {
...
authType: AuthType.AWS_IAM,
authPolicy: new AuthPolicyDocument({
statements: [
new PolicyStatement({
effect: iam.Effect.ALLOW,
actions: ['vpc-lattice-svcs:*'],
resources: ['*'],
principals: [new iam.StarPrincipal()],
conditions: {
StringEquals: { 'vpc-lattice-svcs:RequestMethod': 'GET' },
},
}),
]
}),
...
});
```
## Target Groups
A VPC Lattice target group is a collection of targets, or compute resources, that run your application. Targets can be EC2 instances, IP addresses, Lambda functions, Application Load Balancers, or Kubernetes Pods. To register an EKS pod as a target, use the [AWS Gateway API Controller](https://www.gateway-api-controller.eks.aws.dev/), which gets the IP addresses from the Kubernetes service.
To create a target group use the appropriate construct according to the target type.
### Lambda Function
```ts
const lambdaFunction = new LambdaFunction(stack, 'LambdaTargetFunction', {
runtime: Runtime.NODEJS_18_X,
code: Code.fromInline(`
exports.handler = async (event) => {
return {
isBase64Encoded: false,
statusCode: 200,
body: JSON.stringify({ message: "Hello from Lambda!" }),
};
};
`),
handler: 'index.handler',
});
const tg1 = new LambdaTargetGroup(stack, 'LambdaTG', {
name: 'lambda-tg1',
target: lambdaFunction,
});
```
### Application Load Balancer
```ts
const albSvc = new ApplicationLoadBalancedFargateService(stack, 'Service', {
vpc,
memoryLimitMiB: 1024,
cpu: 512,
taskImageOptions: {
image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'),
containerPort: 80,
},
publicLoadBalancer: false,
});
new AlbTargetGroup(stack, 'ALBTG', {
vpc,
loadBalancer: albSvc.loadBalancer,
});
```
### IP Targets
```ts
const instance1 = new Instance(stack, 'Instance1', {
vpc,
instanceType: cdk.aws_ec2.InstanceType.of(cdk.aws_ec2.InstanceClass.T3, cdk.aws_ec2.InstanceSize.SMALL),
machineImage: cdk.aws_ec2.MachineImage.latestAmazonLinux2(),
});
const instance2 = new Instance(stack, 'Instance2', {
vpc,
instanceType: cdk.aws_ec2.InstanceType.of(cdk.aws_ec2.InstanceClass.T3, cdk.aws_ec2.InstanceSize.SMALL),
machineImage: cdk.aws_ec2.MachineImage.latestAmazonLinux2(),
});
const tg1 = new IpTargetGroup(stack, 'IpTG', {
name: 'ip-tg1',
vpc,
targets: [
{
ipAddress: instance1.instancePrivateIp,
port: 80,
},
],
});
tg1.addTarget({
ipAddress: instance2.instancePrivateIp,
port: 80,
});
```
### Instance
```ts
const instance1 = new Instance(stack, 'Instance1', {
vpc,
instanceType: cdk.aws_ec2.InstanceType.of(cdk.aws_ec2.InstanceClass.T3, cdk.aws_ec2.InstanceSize.SMALL),
machineImage: cdk.aws_ec2.MachineImage.latestAmazonLinux2(),
});
const instance2 = new Instance(stack, 'Instance2', {
vpc,
instanceType: cdk.aws_ec2.InstanceType.of(cdk.aws_ec2.InstanceClass.T3, cdk.aws_ec2.InstanceSize.SMALL),
machineImage: cdk.aws_ec2.MachineImage.latestAmazonLinux2(),
});
const tg1 = new InstanceTargetGroup(stack, 'Ec2TG', {
vpc,
instances: [
{
instance: instance1,
port: 80,
},
],
healthCheck: {
enabled: false,
protocol: HealthCheckProtocol.HTTP,
path: '/health',
port: 8080,
},
});
```
### Auto-scaling Group
```ts
const asg = new AutoScalingGroup(stack, 'ASG', {
vpc,
instanceType: cdk.aws_ec2.InstanceType.of(cdk.aws_ec2.InstanceClass.T2, cdk.aws_ec2.InstanceSize.MICRO),
machineImage: cdk.aws_ec2.MachineImage.latestAmazonLinux2(),
});
const tg1 = new InstanceTargetGroup(stack, 'ASG-TG', {
vpc,
autoScalingGroups: [asg],
healthCheck: {
enabled: false,
protocol: HealthCheckProtocol.HTTP,
path: '/health',
port: 8080,
},
});
```
## Service Network
Creates a service network. A service network is a logical boundary for a collection of services. You can associate services and VPCs with a service network. To communicate with services in a certain service network, clients and other services must be associated with the same service network. They must also be authenticated and authorized by the service network or the specific service for this communication to take place.
To create a service network use the `ServiceNetwork` construct.
```typescript
const myNetwork = new vpclattice.ServiceNetwork(this, 'myNetwork', {
name: 'myNetwork',
authType: vpclattice.AuthType.AWS_IAM,
});
```
### Service Network Access
Access settings enable you to control access to a service network. You can define an auth policy for the service network, which can either be active (when the attribute `authType` is set to `AWS_IAM`) or inactive (when the attribute `authType` is set to `NONE`). It works in the same way as the auth policy for a service.
```typescript
const myNetwork = new vpclattice.ServiceNetwork(this, 'myNetwork', {
name: 'myNetwork',
authType: vpclattice.AuthType.AWS_IAM,
authPolicy: AuthPolicyDocument.UNAUTHENTICATED,
...
});
```
### Service Network VPC associations
When you associate a VPC with a service network, it enables all the targets within that VPC to be clients and communicate with other services associated to that same service network.
You can optional make use of Security Groups to control the access of the VPC association, allowing some traffic segmentation before the traffic arrives to the Service Network.
#### Basic Use
Creating a new service network association for a VPC:
```typescript
const vpc = new ec2.vpc(this,'myVpc',{
})
const serviceNetwork = vpcLattice.ServiceNetwork.fromId('serviceNetworkId')
serviceNetwork.associateVPC(vpc)
```
We can also create a new service network association for a VPC:
```typescript
const vpc = new ec2.vpc(this,'myVpc',{})
const serviceNetwork = new vpclattice.ServiceNetwork(this, 'myNetwork', {
name: 'myNetwork',
authType: vpclattice.AuthType.AWS_IAM,
vpcAssociations: [{ vpc: vpc, }]
});
```
Security Groups can optionally be supplied to the association
```typescript
const vpc = new ec2.vpc(this,'myVpc',{})
const securityGroup = new ec2.SecurityGroup(this,'mySecurityGroup',{
vpc: vpc
})
const serviceNetwork = new vpclattice.ServiceNetwork(this, 'myNetwork', {
name: 'myNetwork',
authType: vpclattice.AuthType.AWS_IAM,
vpcAssociations: [{ vpc: vpc, securityGroups: [securityGroup] }]
});
```
You can create more than 1 VPC association per Service Network and more than 1 Service Network per VPC.
## Adding a VPC Lattice Service to Service Network
There are three ways to associate a service to a service network:
1. Add the Service to the Service Network
2. Add the Service Network to the Service
3. Create a Service Network Service Association
### Add the Service to the Service Network
#### Through the Service Network Properties
The Service Network can be created with the Service already associated to it. This is the simplest way to create a Service Network with a Service.
```typescript
const service = new vpclattice.Service(this, 'myService', {
name: 'myService',
});
const serviceNetwork = new vpclattice.ServiceNetwork(this, 'myNetwork', {
name: 'myNetwork',
authType: vpclattice.AuthType.AWS_IAM,
services: [service],
});
```
#### Through the associateService method
The Service can be created and then associated to the Service Network later.
```typescript
const service = new vpclattice.Service(this, 'myService', {
name: 'myService',
});
const serviceNetwork = new vpclattice.ServiceNetwork(this, 'myNetwork', {
name: 'myNetwork',
});
serviceNetwork.associateService(service)
```
### Add the Service Network to the Service
#### Through the Service Properties
The Service can be created with the Service Network already associated to it. This is the simplest way to create a Service with a Service Network.
```typescript
const serviceNetwork = new vpclattice.ServiceNetwork(this, 'myNetwork', {
name: 'myNetwork',
});
const service = new vpclattice.Service(this, 'myService', {
name: 'myService',
serviceNetwork: serviceNetwork,
});
```
#### Through the associateWithServiceNetwork method
The Service can be created and then associated to the Service Network later.
```typescript
const serviceNetwork = new vpclattice.ServiceNetwork(this, 'myNetwork', {
name: 'myNetwork',
});
const service = new vpclattice.Service(this, 'myService', {
name: 'myService',
});
serviceNetwork.associateService(service)
```
### Create a Service Network Service Association
This method allows to create a Service Network Service Association directly.
```typescript
const serviceNetwork = new vpclattice.ServiceNetwork(this, 'myNetwork', {
name: 'myNetwork',
authType: vpclattice.AuthType.AWS_IAM,
});
const service = new vpclattice.Service(this, 'myService', {
name: 'myService',
});
const serviceNetworkServiceAssociation = new vpclattice.ServiceNetworkServiceAssociation(this, 'myServiceNetworkServiceAssociation', {
serviceNetwork: serviceNetwork,
service: service,
});
```
## Complete example:
The following example demonstrates how to create a complete VPC Lattice setup with:
- A Service Network
- A Service with authentication
- Multiple logging destinations
- VPC association
- Lambda target group
```typescript
import * as cdk from 'aws-cdk-lib';
import { Vpc } from 'aws-cdk-lib/aws-ec2';
import { Role } from 'aws-cdk-lib/aws-iam';
import { Function, Runtime, Code } from 'aws-cdk-lib/aws-lambda';
import { LogGroup } from 'aws-cdk-lib/aws-logs';
import { Bucket } from 'aws-cdk-lib/aws-s3';
import { Construct } from 'constructs';
import { AuthPolicyDocument, AuthPolicyStatement, AuthType, HttpFixedResponse, LambdaTargetGroup, ListenerProtocol, RuleAction, Service, ServiceNetwork } from '../../../src';
export class VpcLatticeStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// Create VPCs
const clientVpc = new Vpc(this, 'ClientVPC');
// Create logging destinations
const logGroup = new LogGroup(this, 'LogGroup');
const logBucket = new Bucket(this, 'LogsBucket');
// Create a Lambda function as target
const lambdaFunction = new Function(this, 'Handler', {
runtime: Runtime.NODEJS_18_X,
handler: 'index.handler',
code: Code.fromInline(`
exports.handler = async () => {
return {
statusCode: 200,
body: JSON.stringify({ message: 'Hello from Lambda!' })
};
};
`),
});
// Create a Service
const service = new Service(this, 'MyService', {
name: 'my-service',
authType: AuthType.AWS_IAM,
loggingConfiguration: {
cloudwatchLogGroup: logGroup,
s3Bucket: logBucket,
},
authPolicy: new AuthPolicyDocument({
statements: [
AuthPolicyStatement.allowOnlyRole(Role.fromRoleArn(this, 'MyRole', 'arn:aws:iam::123456789012:role/my-role')),
],
}),
});
// Create a Lambda target group
const targetGroup = new LambdaTargetGroup(this, 'MyTargetGroup', {
target: lambdaFunction,
});
// Add a listener to the service
const listener = service.addListener({
port: 443,
protocol: ListenerProtocol.HTTPS,
defaultAction: RuleAction.fixedResponseAction(HttpFixedResponse.NOT_FOUND),
});
// Add a rule to the listener
listener.addRule({
name: 'my-rule',
priority: 10,
matchPath: {
match: {
path: '/api/*',
},
caseSensitive: true,
},
action: RuleAction.forwardAction(targetGroup),
});
// Create a Service Network
const serviceNetwork = new ServiceNetwork(this, 'MyServiceNetwork', {
name: 'my-network',
authType: AuthType.AWS_IAM,
loggingConfiguration: {
cloudwatchLogGroup: logGroup,
s3Bucket: logBucket,
},
services: [service],
vpcAssociations: [
{ vpc: clientVpc },
],
});
}
}
```
## Public FAQ
### What are we launching today?
We are launching the VPC Lattice construct in the CDK. This construct will allow users to deploy and manage VPC Lattice resources in their CDK stack.
### Why should I use this construct?
This CDK L2 Construct can be used to deploy resources from [Amazon VPC Lattice](https://docs.aws.amazon.com/vpc-lattice/latest/ug/what-is-vpc-lattice.html). VPC Lattice is a fully managed application networking service that you use to connect, secure, and monitor all your services across multiple accounts and virtual private clouds (VPCs).
This construct handles all the different resources you can use with VPC Lattice: Service Network, Service, Listeners, Listener Rules, Target Groups (and targets), and Associations (Service or VPC). You have the freedom to create the combination of resources you need, so in multi-AWS Account environments you can make use of the module as many times as needed (different providers) to create your application network architecture.
You can check common [Amazon VPC Lattice Reference Architectures](https://d1.awsstatic.com/architecture-diagrams/ArchitectureDiagrams/lattice-use-cases-ra.pdf) to understand the different use cases you can build with the AWS service.
- It simplifies the deployment of common patterns for AWS VPC Lattice
- It has been tested and implemented as part of a number of wider architectures
- It is extensible to support other patterns as they emerge
- It simplifies AWS VPC Lattice adoption and administration
- Allows you to integrate infrastructure deployment with your application code
- Reduces time to deploy and test AWS VPC Lattice
- Provides separation of concerns with a common interface for user personas
## Internal FAQ
### Why are we doing this?
- To provide a CDK native interface for AWS VPC Lattice
- Provide a way to deploy AWS VPC Lattice deterministically
### Why should we not do this?
- AWS Lattice is relatively new and has not yet had much time to develop its uses cases
- L1s exist that can do the job, (but are more work for developers and more code to maintain)
- This Module will need to be maintained over time, and extended as features evolve
### Is this a breaking change?
- No we aim to be compatible, and this is the first iteration of this module.
### What are the drawbacks of this solution?
- It is an opinionated pattern, however there are escapes to help customisation where needed.
- It is a new AWS Service and its common usecases and features may change and evolve
Ticking the box below indicates that the public API of this RFC has been signed-off by the API bar raiser (the api-approved label was applied to the RFC pull request):
``` markdown
[ ] Signed-off by API Bar Raiser @xxxxx
```
Contributor guide
Research direction
Start with the linked RFC Process and review the README's VpcLattice Constructs section, user stories, and workflow checklist. Trace the proposed Service, target group, and ServiceNetwork entry points described in the examples; the RFC is done when it completes the stated review, approval, planning, and implementation workflow.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- aws, typescript
- Domain
- cloud, networking
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100