What is the best way to use the AWS CDK to get the CIDR of the VPC? - aws-cdk

I've started using the AWS CDK to stand up a new VPC, but I am struggling when trying to query other existing VPCs and their CIDR ranges - this is to ensure that my new VPC does not overlap with existing CIDR ranges. The return string is not something I can understand. Could you provide an example on how to query for a list of CIDR ranges in subnets?
Thanks.

If you are trying to reference an existing VPC into your CDK stack, you should use the VpcNetwork.import static method which doesn't require you to specify the CIDR blocks of the VPC.
You will need other information specified in VpcNetworkRefProps, which shouldn't be too hard to obtain from the AWS Console or the AWS CLI:
Something like:
const externalVpc = VpcNetwork.import(this, 'ExternalVpc', {
vpcId: 'vpc-bd5656d4',
availabilityZones: [ 'us-east1a', 'us-east-1b' ],
publicSubnetIds: [ 'subnet-1111aaaa', 'subnet-2222bbbb' ],
privateSubnetIds: [ 'subnet-8368fbce', 'subnet-8368abcc' ],
});
We are looking at making this easier (see #506)

Related

How to add PTR record for EC2 Instance's Private IP in CDK?

I've two private hosted zones created for populating A records and PTR records corresponding to my EC2 instance's private ip. Yes, it's the private ip that I need. This subnet is routed to our corporate data center, so we need non-cryptic hostnames and consistent reverse lookup on them within the account.
I've got the forward lookup working well, however I'm confused how exactly it should be for the reverse lookup on the IP. Assume, my CIDR is 192.168.10.0/24 where the EC2 instances will get created.
const fwdZone = new aws_route53.PrivateHostedZone(
this, "myFwdZone", {
zoneName: "example.com",
vpc: myVpc,
});
const revZone = new aws_route53.PrivateHostedZone(
this, "myRevZone", {
zoneName: "10.168.192.in-addr.arpa",
vpc: myVpc,
}
);
I'm later creating the A record by referencing the EC2 instance's privateIp property. This worked well.
const myEc2 = new aws_ec2.Instance(this, 'myEC2', {...})
new aws_route53.RecordSet(this, "fwdRecord", {
zone: fwdZone,
recordName: "myec2.example.com",
recordType: aws_route53.RecordType.A,
target: aws_route53.RecordTarget.fromIpAddresses(
myEc2.instancePrivateIp
),
});
However, when I try to create the PTR record for the same, I've got some trouble. I needed to extract the fourth octet and specify as the recordName
new aws_route53.RecordSet(this, "revRecord", {
zone: revZone,
recordName: myEc2.instancePrivateIp.split('.')[3],
recordType: aws_route53.RecordType.PTR,
target: aws_route53.RecordTarget.fromValues("myec2.example.com"),
});
The CDK synthesized CloudFormation template looks odd as well, especially the token syntax.
revRecordDEADBEEF:
Type: AWS::Route53::RecordSet
Properties:
Name: ${Token[TOKEN.10.168.192.in-addr.arpa.
Type: PTR
HostedZoneId: A12345678B00CDEFGHIJ3
ResourceRecords:
- myec2.example.com
TTL: "1800"
Is this the right way to achieve this ? If I specified the recordName as just the privateIp, then the synthesized template ends up doing something else, which I see is incorrect too.
revRecordDEADBEEF:
Type: AWS::Route53::RecordSet
Properties:
Name:
Fn::Join:
- ""
- - Fn::GetAtt:
- myEC2123A01BC
- PrivateIp
- .10.168.192.in-addr.arpa.
Type: PTR
HostedZoneId: A12345678B00CDEFGHIJ3
ResourceRecords:
- myec2.example.com
TTL: "1800"
Answering the CDK part of your question: the original error was because you were performing string manipulation on an unresolved token. Your CDK code runs before any resources are provisioned. This has to be the case, since it generates the CloudFormation template that will be submitted to CloudFormation to provision the resources. So when the code runs, the instance does not exist, and its IP address is not knowable.
CDK still allows you to access unresolved properties, returning a Token instead. You can pass this token around and it will be resolved to the actual value during deployment.
To perform string manipulation on a token, you can use CloudFormation's bult-in functions, since they run during deployment, after the token has been resolved.
Here's what it would look like:
recordName: Fn.select(0, Fn.split('.', myEc2.instancePrivateIp))
As you found out yourself, you were also selecting the wrong octet of the IP address, so the actual solution would include replacing 0 with 3 in the call.
References:
https://docs.aws.amazon.com/cdk/v2/guide/tokens.html
https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib-readme.html#intrinsic-functions-and-condition-expressions

Create CfnDBCluster in non-default VPC?

I'm trying to create a serverless aurora database with the AWS CDK (1.19.0). However, it will always be created in the default VPC of the region. If I specify a vpc_security_group_id cloudformation fails because the provided security group is in the vpc created in the same stack as the aurora db.
"The DB instance and EC2 security group are in different VPCs."
Here is my code sample:
from aws_cdk import (
core,
aws_rds as rds,
aws_ec2 as ec2
)
class CdkAuroraStack(core.Stack):
def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
super().__init__(scope, id, **kwargs)
# The code that defines your stack goes here
vpc = ec2.Vpc(self, "VPC")
sg = ec2.SecurityGroup(self, "SecurityGroup",
vpc = vpc,
allow_all_outbound = True
)
cluster = rds.CfnDBCluster(self, "AuroraDB",
engine="aurora",
engine_mode="serverless",
master_username="admin",
master_user_password="password",
database_name="databasename",
vpc_security_group_ids=[
sg.security_group_id
]
)
Do I miss something and it is possible to create the CfnDbCluster in a specific vpc or is this just not possible atm?
Thanks for any help and advice. Have a nice day!
You should create a DB subnet group and include only the subnets you want Amazon RDS to launch instances into. Amazon RDS creates a DB subnet group in default VPC if none is specified.
You can use db_subnet_group_name property to specify your subnets, however it is better to use high-level constructs. In this case, there is one called DatabaseCluster.
cluster = DatabaseCluster(
scope=self,
id="AuroraDB",
engine=DatabaseClusterEngine.AURORA,
master_user=rds.Login(
username="admin",
password="Do not put passwords in your CDK code directly"
),
instance_props={
"instance_type": ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE2, ec2.InstanceSize.SMALL),
"vpc_subnets": {
"subnet_type": ec2.SubnetType.PRIVATE
},
"vpc": vpc,
"security_group": sg
}
)
Do not specify password attribute for your database, CDK assigns a Secrets Manager generated password by default.
Just to note that this construct is still experimental, that means there might be a breaking change in the future.

aws cdk for Elasticache Redis Cluster

I have gone through the https://docs.aws.amazon.com/cdk/api/latest/python/aws_cdk.aws_elasticache.html.
How to create an Elasticache Redis template using AWS-CDK. It would be more helpful if you share the sample code.
sorry for late response but can be usefull for others.
CDK doesn't have an High Level Construct to create a Redis Cluster, but you can create it by using the low level construct api.
For Redis Cluster types you can take a look at this: https://aws.amazon.com/it/blogs/database/work-with-cluster-mode-on-amazon-elasticache-for-redis/
I've created a single Redis (no replication) Cluster using typescript like this:
const subnetGroup = new CfnSubnetGroup(
this,
"RedisClusterPrivateSubnetGroup",
{
cacheSubnetGroupName: "privata",
subnetIds: privateSubnets.subnetIds,
description: "subnet di sviluppo privata"
}
);
const redis = new CfnCacheCluster(this, `RedisCluster`, {
engine: "redis",
cacheNodeType: "cache.t2.small",
numCacheNodes: 1,
clusterName: "redis-sviluppo",
vpcSecurityGroupIds: [vpc.defaultSecurityGroup.securityGroupId],
cacheSubnetGroupName: subnetGroup.cacheSubnetGroupName
});
redis.addDependsOn(subnetGroup);
If you need a Redis (cluster enabled) Cluster you can you replication group
const redisSubnetGroup = new CfnSubnetGroup(
this,
"RedisClusterPrivateSubnetGroup",
{
cacheSubnetGroupName: "privata",
subnetIds: privateSubnets.subnetIds,
description: "subnet di produzione privata"
}
);
const redisReplication = new CfnReplicationGroup(
this,
`RedisReplicaGroup`,
{
engine: "redis",
cacheNodeType: "cache.m5.xlarge",
replicasPerNodeGroup: 1,
numNodeGroups: 3,
automaticFailoverEnabled: true,
autoMinorVersionUpgrade: true,
replicationGroupDescription: "cluster redis di produzione",
cacheSubnetGroupName: redisSubnetGroup.cacheSubnetGroupName
}
);
redisReplication.addDependsOn(redisSubnetGroup);
Hope this help.
I just struggled for hours creating a Redis cluster mode enabled with just one shard but two nodes. If you create a CfnReplicationGroup with num_cache_clusters=2, it will create a primary & replica node.
The trick is to create a CfnReplicationGroup with num_cache_clusters=2 and set the cache_parameter_group_name="default.redis6.x.cluster.on"
Then it will create a redis cache with cluster mode enable, one shard but two nodes

How to get PrivateIPAddress of VPC Endpoint in CDK?

I need create a VPC Endpoint and an ALB to target the VPC Endpoint in CDK.
I found InterfaceVpcEndpoint can return vpcEndpointNetworkInterfaceIds attribute. So it seems the missing part is how to get private IP address from these ENI IDs in a CDK way.
I found CDK has a custom-resource package, its example shows I can use AwsCustomResource to call an AWS API (EC2/DescribeNetworkInterfaces) to get the IP Address.
I tried write a custom resource like below:
eni = AwsCustomResource(
self, 'DescribeNetworkInterfaces',
on_create=custom_resources.AwsSdkCall(
service='ec2',
action='describeNetworkInterfaces',
parameters= {
'NetworkInterfaceId.N': [eni_id]
},
physical_resource_id=str(time.time())
)
)
ip = eni.get_data('NetworkInterfaces.0.PrivateIpAddress')
and pass ip into elbv2.IPTarget.
But it seems I missed something, so it complains it needs a scalar not reference?
(.env) ➜ base-stack (master) ✔ cdk synth base --no-staging > template.yaml
jsii.errors.JavaScriptError:
Error: Expected Scalar, got {"$jsii.byref":"#aws-cdk/core.Reference#10015"}
at Object.deserialize (/Volumes/DATA/ci/aws/base-stack/.env/lib/python3.7/site-packages/jsii/_embedded/jsii/jsii-runtime.js:12047:23)
at Kernel._toSandbox (/Volumes/DATA/ci/aws/base-stack/.env/lib/python3.7/site-packages/jsii/_embedded/jsii/jsii-runtime.js:7031:61)
at /Volumes/DATA/ci/aws/base-stack/.env/lib/python3.7/site-packages/jsii/_embedded/jsii/jsii-runtime.js:7084:33
at Array.map (<anonymous>)
at Kernel._boxUnboxParameters (/Volumes/DATA/ci/aws/base-stack/.env/lib/python3.7/site-packages/jsii/_embedded/jsii/jsii-runtime.js:7084:19)
at Kernel
....
Thanks!
The AwsCustomResource.get_data-method return a Reference object, which now causes the issue. To get the CloudFormation token (!GetAtt "DescribeNetworkInterfaces"."NetworkInterfaces.0.PrivateIpAddress") the Reference.to_string method must be used explicitly.
This:
ip = eni.get_data('NetworkInterfaces.0.PrivateIpAddress')
Becomes:
ip = eni.get_data('NetworkInterfaces.0.PrivateIpAddress').to_string()

How to use VCAP_APP_PORT

how do you get cloudfoundry to assign a port? I am adding applications and I'd like to have a different port for each but VCAP_APP_PORT is not set. VCAP_APP_HOST is set but VCAP_APP_PORT is not.
Take a look at http://show-env.cloudfoundry.com/
It's a node application I knocked together just to output the environment and the request headers when you call it, the code looks like this;
var http = require('http');
var util = require('util');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.write(util.inspect(process.env));
res.write("\n\n************\n\n");
res.end(util.inspect(req.headers));
}).listen(3000);
You can see the VCAP_APP_PORT in the output;
It would be handy to know which framework you are using, however, all these variables should be stored in the system environment so it shouldn't really matter.
Cloud Foundry will automatically assign each application instance an IP address and port and these values are accessible in the VCAP_* variable as Dan describes. You don't get to tell Cloud Foundry which port you prefer. Each instance of your app may receive a different IP address and port, so you should always interrogate the environment to find out what they are if you need that information.

Resources