When trying to create an EKS Cluster and use an existing VPC, I get the error: There are no 'Public' subnet groups in this VPC. Available types.
The referenced VPC has been created using the following snippet:
new ec2.Vpc(this, 'VPC1-Default', {
cidr: '10.0.0.0/20',
maxAzs: 3
});
This creates a VPC with 2 public and 2 private subnets.
Now I tried the EKS example, where a vpc and an eks are created within the same stack. This works. Now I tried creating a second EKS cluster in a separate stack, referencing that same vpc that the other eks cluster is using and I'm getting the same error.
const vpc = new ec2.Vpc(this, 'EKSVpc');
eksCluster = new eks.Cluster(this, 'Cluster1', {
clusterName: `cluster1`,
version: 1.14,
vpc: vpc
}
This works fine. Then I create a second eks in a separate stack:
const vpc = ec2.Vpc.fromVpcAttributes(this, 'EKSVPC', {
vpcId: 'vpc-xxxxxxxxxx', // this is the vpc-id from the first stack
availabilityZones: ['eu-central-1b', 'eu-central-1c']
})
eksCluster = new eks.Cluster(this, 'Cluster2', {
clusterName: `cluster2`,
version: 1.14,
vpc: vpc
}
This does not work!!!
What worked at the end was explicitly providing the subnet ids and route table Ids, like this:
const vpc = ec2.Vpc.fromVpcAttributes(this, 'EKSVPC', {
vpcId: 'vpc-xxxxxxxxxx', // this is the vpc-id from the first stack
availabilityZones: ['eu-central-1b', 'eu-central-1c']
})
eksCluster = new eks.Cluster(this, 'Cluster2', {
clusterName: `cluster2`,
version: 1.14,
vpc: vpc,
vpcSubnets: [
{
subnets: [
ec2.Subnet.fromSubnetAttributes(this, 'PublicSubnet1a', {
availabilityZone: "eu-central-1a",
subnetId: cdk.Fn.importValue("VPC1PublicSubnet1a"),
routeTableId: cdk.Fn.importValue("VPC1PublicSubnet1aRouteTable")
}),
.....
]
}]
}
There are no 'Public' subnet groups in this VPC. Available types:
This seems like a bug to me, since providing a vpc id can (should) be the only thing I need to reference a VPC. In this case the user is saying just create it here and I don't care where
This is :bug: Bug Report
@moatazelmasry2 you need to provide the subnets as well when using the Vpc.fromVpcAttributes() method.
Might I suggest simply passing the VPC object you have in the first stack to the second stack?
Thanks,
Adam
@skinny85 what finally worked like you said referencing the subnet and the routing table IDs. But this is far from optimal.... If I'm providing the vpc id which is unique, I should actually be able to find and load those information...
Might I suggest simply passing the VPC object you have in the first stack to the second stack?
I am a TypeScript noob. Could you please show me a minimal example?
@skinny85 what finally worked like you said referencing the subnet and the routing table IDs. But this is far from optimal.... If I'm providing the vpc id which is unique, I should actually be able to find and load those information...
OK. Now I understand what the confusion is :).
The CDK has 2 ways of referencing existing resources. The first one are methods like fromXyzName, fromXyzArn, fromXyzAttributes. Those simply assume the thing you've passed us exist, and don't do any checks, nor any service calls. Vpc.fromVpcAttributes() that you used belongs to this family. That's why you have to give us the subnet IDs. Another way of thinking about it is the information is strictly at compile time.
The other methods are those that are called fromLookup. How that works, is that the first time you use it, it will require AWS credentials to be present, and it will perform actual service calls to discover your resources. It will then cache that information in the cdk.context.json file, which you are expected to commit to version control. If the information is cached, no further network calls are required.
However, since you're creating and using the VPC in the same app, that is not the best way to do that. You should simply pass the VPC object to the other stack.
Might I suggest simply passing the VPC object you have in the first stack to the second stack?
I am a TypeScript noob. Could you please show me a minimal example?
Sure:
import { App, Construct, Stack, StackProps } from '@aws-cdk/core';
import ec2 = require('@aws-cdk/aws-ec2');
class Stack1 extends Stack {
public readonly vpc: ec2.IVpc;
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
this.vpc = new ec2.Vpc(this, 'Vpc');
}
}
interface Stack2Props extends StackProps {
readonly vpc: ec2.IVpc;
}
class Stack2 extends Stack {
constructor(scope: Construct, id: string, props: Stack2Props) {
super(scope, id, props);
// use props.vpc here...
}
}
And you use them like so in your CDK entry point:
const app = new App();
const stack1 = new Stack1(app, 'Stack1');
new Stack2(app, 'Stack2', {
vpc: stack1.vpc,
});
app.synth();
Hope this helps!
Thanks,
Adam
Wow many thanks for the great help and the good example. This really helps!!!.
What I kinda don't like from a beginners' perspective are the following:
Maybe I can submit a PR enhancing 2. but I need your opinion on number 1. why is there a vpc id attribute at all when calling fromVpcAttributes() ?
why is there a vpc id attribute at all when calling fromVpcAttributes() ?
Because we need to know the ID to fulfill the contract of the IVpc interface.
ok, but if I have the subnet IDs I can get the vpc ID and vice versa. Why do I need both? or is this because cdk synth/deploy does everything first offline with no knowledge about my account?
Sorry about the beginners questions...
Why do I need both? or is this because cdk synth/deploy does everything first offline with no knowledge about my account?
Yes. Let me quote myself:
The CDK has 2 ways of referencing existing resources. The first one are methods like
fromXyzName,fromXyzArn,fromXyzAttributes. Those simply assume the thing you've passed us exist, and don't do any checks, nor any service calls.Vpc.fromVpcAttributes()that you used belongs to this family. That's why you have to give us the subnet IDs. Another way of thinking about it is the information is strictly at compile time.
ok. Thanks for the explanation. I'll try to create some documentation PRs the next couple of days.
Most helpful comment
OK. Now I understand what the confusion is :).
The CDK has 2 ways of referencing existing resources. The first one are methods like
fromXyzName,fromXyzArn,fromXyzAttributes. Those simply assume the thing you've passed us exist, and don't do any checks, nor any service calls.Vpc.fromVpcAttributes()that you used belongs to this family. That's why you have to give us the subnet IDs. Another way of thinking about it is the information is strictly at compile time.The other methods are those that are called
fromLookup. How that works, is that the first time you use it, it will require AWS credentials to be present, and it will perform actual service calls to discover your resources. It will then cache that information in thecdk.context.jsonfile, which you are expected to commit to version control. If the information is cached, no further network calls are required.However, since you're creating and using the VPC in the same app, that is not the best way to do that. You should simply pass the VPC object to the other stack.
Sure:
And you use them like so in your CDK entry point:
Hope this helps!
Thanks,
Adam