ec2: cross-app usage of VPC
See original GitHub issue❓ General Issue
The Question
I am trying to build a sample to pass vpcId
across stacks. It’s fine if I pass vpcId
as the stack property, however, the reality is, the vpc stack may be built by the infra team with native cloudformation templates and export the vpcId
in the Outputs and the App team may build application with CDK in that VPC. All the App team knows is the export name and have to build an app stack in that VPC.
In TypeScript
I can simply get the vpcId
as String
by cdk.Fn.importValue('ExportedVpcId')
import cdk = require('@aws-cdk/core');
import ec2 = require('@aws-cdk/aws-ec2');
import ecs = require('@aws-cdk/aws-ecs');
import ecsPatterns = require('@aws-cdk/aws-ecs-patterns');
import { ContainerImage } from '@aws-cdk/aws-ecs';
import { countResources } from '@aws-cdk/assert';
import { Vpc } from '@aws-cdk/aws-ec2';
export interface MyStackProps extends cdk.StackProps {
vpc?: ec2.Vpc
vpcId?: string
}
export class InfraStack extends cdk.Stack {
readonly vpc: ec2.Vpc
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
this.vpc = new ec2.Vpc(this, 'SharedVPC', {
natGateways: 1,
})
new cdk.CfnOutput(this, 'vpcId', {
value: this.vpc.vpcId,
exportName: 'ExportedVpcId'
})
}
}
/**
* This Fargate stack will be created within the ec2.Vpc we pass in from another stack
*/
export class FargateStack extends cdk.Stack {
readonly vpc: ec2.Vpc
constructor(scope: cdk.Construct, id: string, props: MyStackProps) {
super(scope, id, props);
this.vpc != props.vpc
const cluster = new ecs.Cluster(this, 'Cluster', {
vpc: this.vpc
})
const svc = new ecsPatterns.ApplicationLoadBalancedFargateService(this, 'FargateService', {
cluster,
image: ContainerImage.fromRegistry('nginx')
})
new cdk.CfnOutput(this, 'ServiceURL', {
value: `http://${svc.loadBalancer.loadBalancerDnsName}/`
})
}
}
/**
* This Fargate stack will be created within the vpcId we pass in from another stack
*/
export class FargateStack2 extends cdk.Stack {
readonly vpc: string
constructor(scope: cdk.Construct, id: string, props: MyStackProps) {
super(scope, id, props);
this.vpc != props.vpcId
const cluster = new ecs.Cluster(this, 'Cluster', {
vpc: Vpc.fromLookup(this, 'Vpc', {
vpcId: this.vpc
})
})
const svc = new ecsPatterns.ApplicationLoadBalancedFargateService(this, 'FargateService', {
cluster,
image: ContainerImage.fromRegistry('nginx')
})
new cdk.CfnOutput(this, 'ServiceURL', {
value: `http://${svc.loadBalancer.loadBalancerDnsName}/`
})
}
}
And
/**
* create our infra VPC
*/
const infra = new InfraStack(app, 'InfraStack', { env });
/**
* create our Fargate service in the VPC from InfraStack
*/
const svc = new FargateStack(app, 'FargateServiceStack', {
env,
vpc: infra.vpc
})
/**
* we can get the vpcId from the exported value from InfraStack
*/
const svc2 = new FargateStack2(app, 'FargateServiceStack2', {
env,
vpcId: cdk.Fn.importValue('ExportedVpcId')
})
However, in Python I got this error:
jsii.errors.JavaScriptError:
Error: All arguments to Vpc.fromLookup() must be concrete (no Tokens)
from aws_cdk import core, aws_ec2, aws_ecs, aws_ecs_patterns
class CdkPyCrossStackInfraStack(core.Stack):
def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
super().__init__(scope, id, **kwargs)
self.vpc = aws_ec2.Vpc(self, 'Vpc', nat_gateways=1)
core.CfnOutput(self, 'vpcId', value=self.vpc.vpc_id, export_name='ExportedVpcId')
class CdkPyCrossStackFargateStack(core.Stack):
def __init__(self, scope: core.Construct, id: str, vpc: aws_ec2.Vpc, **kwargs) -> None:
super().__init__(scope, id, **kwargs)
self.vpc = vpc
cluster = aws_ecs.Cluster(self, 'Cluster', vpc=self.vpc)
svc = aws_ecs_patterns.ApplicationLoadBalancedFargateService(
self, 'FargateService',
cluster=cluster,
image=aws_ecs.ContainerImage.from_registry('nginx'))
core.CfnOutput(self, 'ServiceURL', value='http://{}/'.format(svc.load_balancer.load_balancer_full_name))
class CdkPyCrossStackFargateStack2(core.Stack):
def __init__(self, scope: core.Construct, id: str, vpcId: str, **kwargs) -> None:
super().__init__(scope, id, **kwargs)
cluster = aws_ecs.Cluster(self, 'Cluster', vpc=aws_ec2.Vpc.from_lookup(self, 'Vpc', vpc_id=vpcId))
svc = aws_ecs_patterns.ApplicationLoadBalancedFargateService(
self, 'FargateService',
cluster=cluster,
image=aws_ecs.ContainerImage.from_registry('nginx'))
core.CfnOutput(self, 'ServiceURL', value='http://{}/'.format(svc.load_balancer.load_balancer_full_name))
infra_stack = CdkPyCrossStackInfraStack(app, "cdk-py-xstack-infra", env=AWS_ENV)
f1 = CdkPyCrossStackFargateStack(app, "cdk-py-xstack-fargate-svc1", env=AWS_ENV, vpc=infra_stack.vpc)
f2 = CdkPyCrossStackFargateStack2(app, "cdk-py-xstack-fargate-svc2",
env=AWS_ENV,
vpcId=core.Fn.import_value('ExportedVpcId')
)
app.synth()
It looks like cdk.Fn.importValue()
in TypeScript
returns String
but core.Fn.import_value()
in Python
returns Token
.
Not sure if this is a valid issue. Any guidance is highly appreciated.
Environment
- CDK CLI Version: 1.8.0
- Module Version: 1.8.0
- OS: OSX Mojave
- Language: Python
Other information
https://gitter.im/awslabs/aws-cdk?at=5d7f7b8636461106bb29e96e
Issue Analytics
- State:
- Created 4 years ago
- Reactions:27
- Comments:33 (5 by maintainers)
But how come it works if you hard-code the VPC-id, but doesn’t work when importing it? It would still not know anything about the remaining configuration?
And this use case can never really work. The reason is that we need to know more about a VPC than its VPC ID: we also need to know all of the subnets, routing tablets, etc, because there are a lot of things people want to do to VPCs and many of the things require detailed knowledge about the VPC layout.
The only way to get this going right now is for you infrastructure team to deploy the VPC completely, then use
fromLookup()
to find it in every region you intend to deploy to. I recommend using tags to identify the VPC.