AWS CDK - Connect a Network Load Balancer and a Neptune Cluster Endpoint together - amazon-web-services

For the past two days I've been struggling with exposing a Neptune endpoint to the public using an NLB in a single stack. The architecture was inspired by this document.
For the life of me I haven't been able to figure out how to obtain the IP address of the Neptune endpoint to use as the target of NLB's listener. The main issue resides in the conversion of the Neptune hostname to an IP address as required by NLB's target group IPTarget and how CDK synthesizes stacks before deployment.
I explored the use of CustomResources to no avail due to my limited familiarity with the topic (day 5 of my aws journey), and was hoping someone could point me in the right direction.
Here's my stack (CDK app repo here):
import { Construct } from "constructs";
import { Stack } from "aws-cdk-lib";
import * as ec2 from "aws-cdk-lib/aws-ec2";
import * as elbv2 from "aws-cdk-lib/aws-elasticloadbalancingv2";
import * as neptune from "#aws-cdk/aws-neptune-alpha";
import { Props } from "../../_config";
import createVPC from "../helpers/createVPC";
import createNeptuneCluster from "../helpers/createNeptuneCluster";
import createNLB from "../helpers/createNLB";
export class ABCGraphStack extends Stack {
public readonly vpc: ec2.Vpc;
public readonly subnets: {
public: ec2.ISubnet[];
private: ec2.ISubnet[];
isolated: ec2.ISubnet[];
};
public readonly neptuneCluster: neptune.DatabaseCluster;
public readonly neptuneReadEndpoint: neptune.Endpoint;
public readonly neptuneWriteEndpoint: neptune.Endpoint;
public readonly nlb: elbv2.NetworkLoadBalancer;
constructor(scope: Construct, id: string, props: Props) {
super(scope, id, props);
// Create VPC for use with Neptune
const { vpc, subnets } = createVPC(props, this);
this.vpc = vpc;
this.subnets = subnets;
// Create Neptune Cluster
this.neptuneCluster = createNeptuneCluster(
props,
this,
this.vpc,
this.subnets
);
// Update Neptune Security Group to allow-all-in
this.neptuneCluster.connections.allowDefaultPortFromAnyIpv4(
"Allow All Inbound to Neptune"
);
// Add an ordering dependency on VPC.
this.neptuneCluster.node.addDependency(this.vpc);
// Output the Neptune read/write addresses
this.neptuneReadEndpoint = this.neptuneCluster.clusterReadEndpoint;
this.neptuneWriteEndpoint = this.neptuneCluster.clusterEndpoint;
// HOW TO GET IP ADDRESS OF this.neptuneWriteEndpoint.hostname?
// Create Network Load Balancer
this.nlb = createNLB(props, this, this.vpc, "????????", 8182);
this.nlb.node.addDependency(this.neptuneCluster);
}
}

Related

How to list the Elastic IP (EIP) addresses via CDK

How to get all the Elastic IP (EIP) addresses present in a subnet via CDK ?
My purpose is to whitelist those elastic ips in another account through the CDK.
You can try to find a construct with the logical name of EIP in the public subnets of your VPC. Afterwards you should be able to sue Ref to export the EIP address:
const eips = vpc.publicSubnets.map(subnet => (subnet as PublicSubnet).node.findChild('EIP') as CfnEIP);
this.exportValue(Fn.join(",", eips.map(n => n.ref)), {
name: "elastic-ips"
})
// or one by one
new CfnOutput(this, 'EIP1', {
value: eips[0].ref
})
new CfnOutput(this, 'EIP2', {
value: eips[1].ref
})
I am using CDKv2 and even though PublicSubnet has as Child with id = EIP, .findChild doesn't return anything and cause errors in stack synthesis.
Here is my version.
const eips = vpc.publicSubnets
.map((subnet) =>
(subnet as PublicSubnet).node.children
.filter((child) => child.node.id == 'EIP')
.map((child) => child as CfnEIP),
)
.flat();
for (let eip of eips) {
// Release EIPs on stack removal
eip.applyRemovalPolicy(RemovalPolicy.DESTROY);
}

How can you reference the primary network interface of an EC2 instance in AWS CDK?

In AWS-CDK I am trying to create a Traffic Mirroring Session. The EC2 machines are created in previous stacks and passed down as props to the new stack. However, while I am able to reference an ENI that was created explicitly (sniffing interface), I cannot find a way of referencing the EC2 primary network interface as the traffic mirror source
class TrafficMirringStack extends cdk.Stack {
constructor(scope, id, props) {
super(scope,id,props)
const {
suricataInstance,
sniffingInterface,
targetInstance
} = props;
const mirrorTarget = new ec2.CfnTrafficMirrorTarget(this, 'TrafficMirrorTarget', {
description:' This is the traffic mirror target',
networkInterfaceId: sniffingInterface.ref,
});
const mirrorFilter = new ec2.CfnTrafficMirrorFilter(this, 'TrafficMirrorFilter', {
description: 'This filter allows all traffic from the target machine to be redirected to the sniffing interface',
networkServices:[],
});
const allowAllInboundRule = new ec2.CfnTrafficMirrorFilterRule(this, 'InboundMirrorFilter', {
destinationCidrBlock : '0.0.0.0/0',
sourceCidrBlock:'0.0.0.0/0',
trafficDirection: 'ingress',
ruleAction: 'accept',
ruleNumber:100,
trafficMirrorFilterId: mirrorFilter.ref
});
const allowAllOutboundRule = new ec2.CfnTrafficMirrorFilterRule(this, 'OutboundMirrorFilter', {
destinationCidrBlock : '0.0.0.0/0',
sourceCidrBlock:'0.0.0.0/0',
trafficDirection: 'egress',
ruleAction: 'accept',
ruleNumber:200,
trafficMirrorFilterId: mirrorFilter.ref
});
const mirrorSession = new ec2.CfnTrafficMirrorSession(this, 'TrafficMirrorSession', {
sessionNumber: 1,
networkInterfaceId: targetInstance.instance.networkInterfaceId,
trafficMirrorFilterId: mirrorFilter.ref,
trafficMirrorTargetId: mirrorTarget.ref
})
}
}
and I get the following error
Error: TrafficMirroringStack/TrafficMirrorSession [AWS::EC2::TrafficMirrorSession] is missing required property: networkInterfaceId
try to use:
instance.networkInterfaces
and find the interface you want.
see docs:
networkInterfaces - aws cdk doc

Amazon EC2 client builder without region

I am trying to connect to a client's AWS using the secret key, access key, and role-arn in a java application.
Using AmazonEC2ClientBuilder requires the region to be specified.
Is there any way or any API support where I can connect to a client's AWS without the need for the region ?
Thanks.
When you use the Amazon EC2 Java API (V2), you specify a region to tell the code which region your EC2 instances are you located in.
That is, if I have my EC2 instances in USA West 2, then I need to specify this region when creating a Service Client, as shown in this Java V2 code example that finds running Amazon EC2 instances in USA West 2. You code still works without a region, but will default to a region. The default region for an SDK is US-EAST-1
package com.example.ec2;
// snippet-start:[ec2.java2.running_instances.import]
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.ec2.Ec2Client;
import software.amazon.awssdk.services.ec2.model.Filter;
import software.amazon.awssdk.services.ec2.model.DescribeInstancesRequest;
import software.amazon.awssdk.services.ec2.model.DescribeInstancesResponse;
import software.amazon.awssdk.services.ec2.model.Reservation;
import software.amazon.awssdk.services.ec2.model.Instance;
import software.amazon.awssdk.services.ec2.model.Ec2Exception;
// snippet-end:[ec2.java2.running_instances.import]
/**
* To run this Java V2 code example, ensure that you have setup your development environment, including your credentials.
*
* For information, see this documentation topic:
*
* https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html
*/
public class FindRunningInstances {
public static void main(String[] args) {
Region region = Region.US_WEST_2;
Ec2Client ec2 = Ec2Client.builder()
.region(region)
.build();
findRunningEC2Instances(ec2);
ec2.close();
}
// snippet-start:[ec2.java2.running_instances.main]
// This method creates a Filter to find all running instances
public static void findRunningEC2Instances(Ec2Client ec2) {
try {
String nextToken = null;
do {
Filter filter = Filter.builder()
.name("instance-state-name")
.values("running")
.build();
DescribeInstancesRequest request = DescribeInstancesRequest.builder()
.filters(filter)
.build();
DescribeInstancesResponse response = ec2.describeInstances(request);
for (Reservation reservation : response.reservations()) {
for (Instance instance : reservation.instances()) {
System.out.printf(
"Found Reservation with id %s, " +
"AMI %s, " +
"type %s, " +
"state %s " +
"and monitoring state %s",
instance.instanceId(),
instance.imageId(),
instance.instanceType(),
instance.state().name(),
instance.monitoring().state());
System.out.println("");
}
}
nextToken = response.nextToken();
} while (nextToken != null);
} catch (Ec2Exception e) {
System.err.println(e.awsErrorDetails().errorMessage());
System.exit(1);
}
}
// snippet-end:[ec2.java2.running_instances.main]
}

AWS CDK Java - Template format error: Mappings element count 0 should be greater than 0

I would like to use AWS CDK with Java to create one simple VPC, with a public subnet, a security group and a EC2 instance. The Java class is the following, very simple:
public class CDKStack extends Stack {
public CDKStack(final Construct scope, final String id) {
this(scope, id, null);
}
public CDKStack(final Construct scope, final String id, final StackProps props) {
super(scope, id, props);
// Create public subnet
SubnetConfiguration publicSubnet = SubnetConfiguration.builder()
.name("public-subnet")
.subnetType(SubnetType.PUBLIC)
.cidrMask(24)
.build();
List<SubnetConfiguration> subnetList = new ArrayList<>();
subnetList.add(publicSubnet);
// Create VPC with subnet above
Vpc vpc = new Vpc(this, "vpc-from-ckd", VpcProps.builder()
.cidr("10.0.0.0/16")
.maxAzs(1)
.subnetConfiguration(subnetList)
.build());
// Create the Security Group inside the VPC
SecurityGroup securityGroup = new SecurityGroup(this, "sg-cdk-java", SecurityGroupProps.builder()
.vpc(vpc)
.allowAllOutbound(true)
.build());
// Create image and EC2 instance
final GenericLinuxImage genericLinuxImage = new GenericLinuxImage(Collections.emptyMap());
Instance.Builder.create(this, "EC2 from CDK")
.instanceType(new InstanceType("t2.micro"))
.machineImage(genericLinuxImage)
.securityGroup(securityGroup)
.vpc(vpc)
.build();
}
}
Though, when I run cdk deploy (on Windows 10) I get the following error that I do not understand:
CDKStack: deploying...
CDKStack: creating CloudFormation changeset...
❌ CDKStack failed: Error [ValidationError]: Template format error: Mappings element count 0 should be greater than 0
Any help that could clarify what I'm doing wrong would be really appreciated, as I am going through samples and API documentation, but I cannot figure it out.
After some more search, I paid more attention to the error message and realized that Mappings refers to the Cloudformation Mappings (sounds obvious now that I think about it, but from the error message it is absolutely not clear, it seems more a verb in a sentence) and from their example in the official doc I thought it could be related to the environment variables.
So, I set them explicitly in the Java class and now it works:
.env(Environment.builder()
.account("1234567890")
.region("aws-region-name")
.build())

How do I create an AWS App Mesh using AWS CDK

I am attempting to create a stack for (currently) 9 .NET Core microservices to run in ECS Fargate and communicate with each other via App Mesh. I plan on creating an Infrastructure stack which creates the App Mesh resource and the ECS Cluster and a Microservice stack that creates the resources for each service and adds them to the App Mesh and ECS cluster.
I currently have this code:
Vpc = Amazon.CDK.AWS.EC2.Vpc.FromLookup(this, "vpc", new VpcLookupOptions
{
VpcId = "xxxxxxxxxxxx"
});
DefaultCloudMapNamespace = new CloudMapNamespaceOptions
{
Vpc = Vpc,
Name = dnsNamespace,
Type = NamespaceType.DNS_PRIVATE,
};
EcsCluster = new Cluster(this, $"{Env}-linux-cluster", new ClusterProps
{
Vpc = Vpc,
ClusterName = $"{Env}-linux-cluster",
DefaultCloudMapNamespace = DefaultCloudMapNamespace
});
This seems to be okay - it creates a hosted zone in Route53.
When I am creating the Service for Cloud Map, I'm using this code:
var cloudMapService = new Service(this, serviceName, new ServiceProps
{
Namespace = new PrivateDnsNamespace(this, $"{serviceNameHyphen}-cm-namespace", new PrivateDnsNamespaceProps
{
Vpc = infrastructureStack.Vpc,
Name = $"{serviceName}.dev",
}),
DnsRecordType = DnsRecordType.SRV,
DnsTtl = Duration.Seconds(60),
RoutingPolicy = RoutingPolicy.MULTIVALUE,
Name = serviceName
});
This is the first time I'm working with App Mesh & Cloud Map, but I would expect to use the same private hosted zone for both the Cloud Map namespace and the Cloud Map Service namespace.
Is this the correct approach?
My approach:
I create Namespace first
cloud_map = sds.PrivateDnsNamespace(
self,
"PrivateNameSpace",
vpc=vpcObject,
description=' '.join(["Private DNS for", self.node.try_get_context('EnvironmentName')]),
name=service_domain
)
Then when create Virtual Service I use same domain for it
vservice = mesh.VirtualService(
self,
"VirtualService",
virtual_service_name='.'.join([node_name, service_domain]),
virtual_service_provider=mesh.VirtualServiceProvider.virtual_node(vnode)
)
Then call it when create ECS service
ecs_service = ecs.Ec2Service(
self,
"ECSService",
task_definition=ecs_task,
placement_strategies=[
ecs.PlacementStrategy.spread_across_instances()
],
desired_count=desiredCount,
cluster=clusterObject,
security_groups=[sgObject],
vpc_subnets=ec2.SubnetSelection(
subnet_type=ec2.SubnetType.PRIVATE
),
enable_ecs_managed_tags=True,
health_check_grace_period=cdk.Duration.seconds(120),
max_healthy_percent=200,
min_healthy_percent=50,
cloud_map_options=ecs.CloudMapOptions(
cloud_map_namespace=cloud_map,
dns_record_type=cm.DnsRecordType.A,
dns_ttl=cdk.Duration.seconds(300),
failure_threshold=1,
name=node_name
),
)