feat: Add AWS CDK project and Helm charts for Beckn-Onix deployment on AWS cloud

This commit is contained in:
Mozammil Khan
2024-09-23 22:57:34 +05:30
parent 5d5e363ccd
commit c683ec3d74
114 changed files with 10018 additions and 0 deletions

View File

@@ -0,0 +1,67 @@
import * as dotenv from "dotenv";
import path = require("path");
dotenv.config({ path: path.resolve(__dirname, "../.env") });
export type ConfigProps = {
REGION: string,
ACCOUNT: string,
REPOSITORY: string,
REGISTRY_RELEASE_NAME: string;
GATEWAY_RELEASE_NAME: string;
BAP_RELEASE_NAME: string;
BPP_RELEASE_NAME: string,
RDS_USER: string,
CERT_ARN: string,
REGISTRY_URL: string,
MAX_AZS: number,
EKS_CLUSTER_NAME: string,
CIDR: string,
EC2_NODES_COUNT: number;
EC2_INSTANCE_TYPE: string;
ROLE_ARN: string;
DOCDB_PASSWORD: string;
RABBITMQ_PASSWORD: string;
NAMESPACE: string;
BAP_PUBLIC_KEY: string;
BAP_PRIVATE_KEY: string;
BPP_PUBLIC_KEY: string;
BPP_PRIVATE_KEY: string;
REGISTRY_EXTERNAL_DOMAIN: string,
GATEWAY_EXTERNAL_DOMAIN: string;
BAP_EXTERNAL_DOMAIN: string;
BPP_EXTERNAL_DOMAIN: string;
};
export const getConfig = (): ConfigProps => ({
REGION: process.env.REGION || "ap-south-1",
ACCOUNT: process.env.ACCOUNT || "",
REPOSITORY: process.env.BECKN_ONIX_HELM_REPOSITORY || "",
MAX_AZS: Number(process.env.MAZ_AZs) || 2,
REGISTRY_RELEASE_NAME: "beckn-onix-registry",
GATEWAY_RELEASE_NAME: "beckn-onix-gateway",
BAP_RELEASE_NAME: "beckn-onix-bap",
BPP_RELEASE_NAME: "beckn-onix-bpp",
RDS_USER: process.env.RDS_USER || "postgres",
CERT_ARN: process.env.CERT_ARN || "", // user must provide it
REGISTRY_URL: process.env.REGISTRY_URL || "", // beckn-onix reg url
EKS_CLUSTER_NAME: process.env.EKS_CLUSTER_NAME || "beckn-onix",
CIDR: process.env.CIDR || "10.20.0.0/16",
EC2_NODES_COUNT: Number(process.env.EC2_NODES_COUNT) || 2,
EC2_INSTANCE_TYPE: process.env.EC2_INSTANCE_TYPE || "t3.large",
ROLE_ARN: process.env.ROLE_ARN || "",
DOCDB_PASSWORD: process.env.DOCDB_PASSWORD || "",
RABBITMQ_PASSWORD: process.env.RABBITMQ_PASSWORD || "",
NAMESPACE: "-common-services",
BAP_PUBLIC_KEY: process.env.BAP_PUBLIC_KEY || "",
BAP_PRIVATE_KEY: process.env.BAP_PRIVATE_KEY || "",
BPP_PUBLIC_KEY: process.env.BPP_PUBLIC_KEY || "",
BPP_PRIVATE_KEY: process.env.BPP_PRIVATE_KEY || "",
REGISTRY_EXTERNAL_DOMAIN: process.env.REGISTRY_EXTERNAL_DOMAIN || "", // user must provide it
GATEWAY_EXTERNAL_DOMAIN: process.env.GATEWAY_EXTERNAL_DOMAIN || "", // user must provide it
BAP_EXTERNAL_DOMAIN: process.env.BAP_EXTERNAL_DOMAIN || "", // user must provide it
BPP_EXTERNAL_DOMAIN: process.env.BPP_EXTERNAL_DOMAIN || "", // user must provide it
});

View File

@@ -0,0 +1,64 @@
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as docdb from 'aws-cdk-lib/aws-docdb';
import * as dotenv from 'dotenv';
import { ConfigProps } from './config';
// Load environment variables from .env file
dotenv.config();
interface DocumentDbStackProps extends cdk.StackProps {
config: ConfigProps;
vpc: ec2.Vpc;
}
export class DocumentDbStack extends cdk.Stack {
constructor(scope: Construct, id: string, props: DocumentDbStackProps) {
super(scope, id, props);
// Use environment variable from .env file or fallback to a default value
const docDbPassword = new cdk.CfnParameter(this, 'DocDbPassword', {
type: 'String',
description: 'The password for the DocumentDB cluster admin user',
noEcho: true,
default: props.config.DOCDB_PASSWORD || '', // Use environment variable
});
// Security group for DocumentDB
const docDbSecurityGroup = new ec2.SecurityGroup(this, 'DocDbSecurityGroup', {
vpc: props.vpc,
description: 'Security group for DocumentDB',
allowAllOutbound: true,
});
docDbSecurityGroup.addIngressRule(ec2.Peer.ipv4(props.vpc.vpcCidrBlock), ec2.Port.tcp(27017), 'Allow DocumentDB traffic on port 27017');
// DocumentDB subnet group
const docDbSubnetGroup = new docdb.CfnDBSubnetGroup(this, 'DocDbSubnetGroup', {
dbSubnetGroupDescription: 'Subnet group for DocumentDB',
subnetIds: props.vpc.selectSubnets({ subnetType: ec2.SubnetType.PRIVATE_WITH_NAT }).subnetIds,
});
// DocumentDB cluster
const docDbCluster = new docdb.CfnDBCluster(this, 'DocDbCluster', {
masterUsername: 'beckn',
masterUserPassword: docDbPassword.valueAsString, // Password entered by the user
dbClusterIdentifier: 'MyDocDbCluster',
engineVersion: '4.0.0',
vpcSecurityGroupIds: [docDbSecurityGroup.securityGroupId],
dbSubnetGroupName: docDbSubnetGroup.ref,
});
// Create 2 DocumentDB instances
new docdb.CfnDBInstance(this, 'DocDbInstance1', {
dbClusterIdentifier: docDbCluster.ref,
dbInstanceClass: 'db.r5.large',
});
new docdb.CfnDBInstance(this, 'DocDbInstance2', {
dbClusterIdentifier: docDbCluster.ref,
dbInstanceClass: 'db.r5.large',
});
}
}

View File

@@ -0,0 +1,149 @@
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as eks from 'aws-cdk-lib/aws-eks';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as cdk from 'aws-cdk-lib';
import { KubectlV30Layer } from '@aws-cdk/lambda-layer-kubectl-v30';
// import { CfnAutoScalingGroup } from 'aws-cdk-lib/aws-autoscaling';
import { Construct } from 'constructs';
import { ConfigProps } from './config';
export interface EksStackProps extends cdk.StackProps {
config: ConfigProps;
vpc: ec2.Vpc;
}
export class EksStack extends cdk.Stack {
public readonly cluster: eks.Cluster;
public readonly eksSecGrp: ec2.SecurityGroup;
constructor(scope: Construct, id: string, props: EksStackProps) {
super(scope, id, props);
const config = props.config;
const vpc = props.vpc;
const cidr = config.CIDR; // from config file
const EKS_CLUSTER_NAME = config.EKS_CLUSTER_NAME; // take it from config file
// const ROLE_ARN = 'ROLE_ARN'; // take form config file
const ROLE_ARN = config.ROLE_ARN;
const securityGroupEKS = new ec2.SecurityGroup(this, "EKSSecurityGroup", {
vpc: vpc,
allowAllOutbound: true,
description: "Security group for EKS",
});
securityGroupEKS.addIngressRule(
ec2.Peer.ipv4(cidr),
ec2.Port.allTraffic(),
"Allow EKS traffic"
);
// securityGroupEKS.addIngressRule(
// ec2.Peer.securityGroupId(securityGroupEKS.securityGroupId),
// ec2.Port.allTraffic(),
// "Allow EKS traffic"
// );
const iamRole = iam.Role.fromRoleArn(this, "MyIAMRole", ROLE_ARN);
// Create the EKS cluster
this.cluster = new eks.Cluster(this, 'EksCluster', {
vpc: vpc,
vpcSubnets: [{ subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS }],
defaultCapacity: 0,
// defaultCapacityInstance: new ec2.InstanceType(config.EC2_INSTANCE_TYPE),
kubectlLayer: new KubectlV30Layer(this, 'KubectlLayer'),
version: eks.KubernetesVersion.V1_30,
securityGroup: securityGroupEKS,
endpointAccess: eks.EndpointAccess.PUBLIC_AND_PRIVATE,
ipFamily: eks.IpFamily.IP_V4,
clusterName: EKS_CLUSTER_NAME,
mastersRole: iamRole, // Assign the admin role to the cluster
outputClusterName: true,
outputConfigCommand: true,
authenticationMode: eks.AuthenticationMode.API_AND_CONFIG_MAP,
bootstrapClusterCreatorAdminPermissions: true,
albController: {
version: eks.AlbControllerVersion.V2_8_1,
repository: "public.ecr.aws/eks/aws-load-balancer-controller",
},
});
const key1 = this.cluster.openIdConnectProvider.openIdConnectProviderIssuer;
const stringEquals = new cdk.CfnJson(this, 'ConditionJson', {
value: {
[`${key1}:sub`]: ['system:serviceaccount:kube-system:ebs-csi-controller-sa', 'system:serviceaccount:kube-system:efs-csi-controller-sa'],
[`${key1}:aud`]: 'sts.amazonaws.com'
},
})
const oidcEKSCSIRole = new iam.Role(this, "OIDCRole", {
assumedBy: new iam.FederatedPrincipal(
`arn:aws:iam::${this.account}:oidc-provider/${this.cluster.clusterOpenIdConnectIssuer}`,
{
StringEquals: stringEquals,
},
"sts:AssumeRoleWithWebIdentity"
),
});
// Attach a managed policy to the role
oidcEKSCSIRole.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName("service-role/AmazonEBSCSIDriverPolicy"))
oidcEKSCSIRole.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName("service-role/AmazonEFSCSIDriverPolicy"))
const ebscsi = new eks.CfnAddon(this, "addonEbsCsi",
{
addonName: "aws-ebs-csi-driver",
clusterName: this.cluster.clusterName,
serviceAccountRoleArn: oidcEKSCSIRole.roleArn
}
);
const efscsi = new eks.CfnAddon(this, "addonEfsCsi",
{
addonName: "aws-efs-csi-driver",
clusterName: this.cluster.clusterName,
serviceAccountRoleArn: oidcEKSCSIRole.roleArn
}
);
new cdk.CfnOutput(this, String("OIDC-issuer"), {
value: this.cluster.clusterOpenIdConnectIssuer,
});
new cdk.CfnOutput(this, String("OIDC-issuerURL"), {
value: this.cluster.clusterOpenIdConnectIssuerUrl,
});
new cdk.CfnOutput(this, "EKS Cluster Name", {
value: this.cluster.clusterName,
});
new cdk.CfnOutput(this, "EKS Cluster Arn", {
value: this.cluster.clusterArn,
});
const launchTemplate = new ec2.CfnLaunchTemplate(this, 'MyLaunchTemplate', {
launchTemplateData: {
instanceType: config.EC2_INSTANCE_TYPE,
securityGroupIds: [this.cluster.clusterSecurityGroupId, securityGroupEKS.securityGroupId],
}
});
// Create node group using the launch template
this.cluster.addNodegroupCapacity('CustomNodeGroup', {
amiType: eks.NodegroupAmiType.AL2_X86_64,
desiredSize: config.EC2_NODES_COUNT,
launchTemplateSpec: {
id: launchTemplate.ref,
version: launchTemplate.attrLatestVersionNumber,
},
subnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS },
});
this.eksSecGrp = securityGroupEKS;
}
}

View File

@@ -0,0 +1,113 @@
import * as cdk from 'aws-cdk-lib';
import * as eks from 'aws-cdk-lib/aws-eks';
import * as helm from 'aws-cdk-lib/aws-eks';
import { Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { ConfigProps } from './config';
import * as efs from 'aws-cdk-lib/aws-efs';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as iam from 'aws-cdk-lib/aws-iam';
interface HelmBapStackProps extends StackProps {
config: ConfigProps;
eksCluster: eks.Cluster;
isSandbox: boolean;
eksSecGrp: ec2.SecurityGroup;
vpc: ec2.Vpc;
}
export class HelmBapStack extends Stack {
constructor(scope: Construct, id: string, props: HelmBapStackProps) {
super(scope, id, props);
const eksCluster = props.eksCluster;
const externalDomain = props.config.BAP_EXTERNAL_DOMAIN;
const certArn = props.config.CERT_ARN;
const releaseName = props.config.BAP_RELEASE_NAME;
const repository = props.config.REPOSITORY;
const registryUrl = props.config.REGISTRY_URL;
const bapPrivateKey = props.config.BAP_PRIVATE_KEY;
const bapPublicKey = props.config.BAP_PUBLIC_KEY;
const isSandbox = props.isSandbox;
const myFileSystemPolicy = new iam.PolicyDocument({
statements: [new iam.PolicyStatement({
actions: [
'elasticfilesystem:ClientRootAccess',
'elasticfilesystem:ClientWrite',
'elasticfilesystem:ClientMount',
],
principals: [new iam.ArnPrincipal('*')],
resources: ['*'],
conditions: {
Bool: {
'elasticfilesystem:AccessedViaMountTarget': 'true',
},
},
})],
});
const efsBapFileSystemId = new efs.FileSystem(this, 'Beckn-Onix-Bap', {
vpc: props.vpc,
securityGroup: props.eksSecGrp,
fileSystemPolicy: myFileSystemPolicy,
});
// let efsBapFileSystemId: string | undefined;
// const existingFileSystemId = cdk.Fn.importValue('EfsBapFileSystemId');
// if(existingFileSystemId){
// efsBapFileSystemId = existingFileSystemId;
// } else{
// const efsBapFileSystem = new efs.FileSystem(this, 'Beckn-Onix-Bap', {
// vpc: props.vpc,
// securityGroup: props.eksSecGrp,
// });
// efsBapFileSystemId = efsBapFileSystem.fileSystemId;
// new cdk.CfnOutput(this, 'EfsBapFileSystemId', {
// value: efsBapFileSystemId,
// exportName: 'EfsBapFileSystemId',
// })
// }
// const efsBapFileSystemId = new efs.FileSystem(this, 'Beckn-Onix-Bap', {
// vpc: props.vpc,
// });
new helm.HelmChart(this, 'baphelm', {
cluster: eksCluster,
chart: 'beckn-onix-bap',
release: releaseName,
wait: false,
repository: repository,
values: {
global: {
isSandbox: isSandbox,
externalDomain: externalDomain,
registry_url: registryUrl,
bap: {
privateKey: bapPrivateKey,
publicKey: bapPublicKey,
},
efs: {
fileSystemId: efsBapFileSystemId.fileSystemId,
},
ingress: {
tls: {
certificateArn: certArn,
},
},
},
},
}
);
new cdk.CfnOutput(this, String("EksFileSystemId"), {
value: efsBapFileSystemId.fileSystemId,
});
}
}

View File

@@ -0,0 +1,90 @@
import * as cdk from 'aws-cdk-lib';
import * as eks from 'aws-cdk-lib/aws-eks';
import * as helm from 'aws-cdk-lib/aws-eks';
import { Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { ConfigProps } from './config';
import * as crypto from 'crypto';
interface HelmCommonServicesStackProps extends StackProps {
config: ConfigProps;
eksCluster: eks.Cluster;
service: string,
}
export class HelmCommonServicesStack extends Stack {
constructor(scope: Construct, id: string, props: HelmCommonServicesStackProps) {
super(scope, id, props);
const eksCluster = props.eksCluster;
const service = props.service;
const repository = "https://charts.bitnami.com/bitnami";
const namespace = props.config.NAMESPACE;
const generateRandomPassword = (length: number) => {
return crypto.randomBytes(length).toString('hex').slice(0, length);
};
const rabbitMQPassword = generateRandomPassword(12);
new helm.HelmChart(this, "RedisHelmChart", {
cluster: eksCluster,
chart: "redis",
namespace: service + namespace,
release: "redis",
wait: false,
repository: repository,
values: {
auth: {
enabled: false
},
replica: {
replicaCount: 0
},
master: {
persistence: {
storageClass: "gp2"
}
}
}
});
new helm.HelmChart(this, "MongoDBHelmChart", {
cluster: eksCluster,
chart: "mongodb",
namespace: service + namespace,
release: "mongodb",
wait: false,
repository: repository,
values: {
persistence: {
storageClass: "gp2"
}
}
});
new helm.HelmChart(this, "RabbitMQHelmChart", {
cluster: eksCluster,
chart: "rabbitmq",
namespace: service + namespace,
release: "rabbitmq",
wait: false,
repository: repository,
values: {
persistence: {
enabled: true,
storageClass: "gp2"
},
auth: {
username: "beckn",
password: "beckn1234"
}
}
});
// new cdk.CfnOutput(this, String("RabbimqPassword"), {
// value: rabbitMQPassword,
// });
}
}

View File

@@ -0,0 +1,89 @@
import * as cdk from 'aws-cdk-lib';
import * as eks from 'aws-cdk-lib/aws-eks';
import * as helm from 'aws-cdk-lib/aws-eks';
import { Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { ConfigProps } from './config';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as efs from 'aws-cdk-lib/aws-efs';
import * as iam from 'aws-cdk-lib/aws-iam';
interface HelmBppStackProps extends StackProps {
config: ConfigProps;
vpc: ec2.Vpc;
isSandbox: boolean;
eksSecGrp: ec2.SecurityGroup;
eksCluster: eks.Cluster;
}
export class HelmBppStack extends Stack {
constructor(scope: Construct, id: string, props: HelmBppStackProps) {
super(scope, id, props);
const eksCluster = props.eksCluster;
const externalDomain = props.config.BPP_EXTERNAL_DOMAIN;
const certArn = props.config.CERT_ARN;
const releaseName = props.config.BPP_RELEASE_NAME;
const repository = props.config.REPOSITORY;
const registryUrl = props.config.REGISTRY_URL;
const bppPrivateKey = props.config.BPP_PRIVATE_KEY;
const bppPublicKey = props.config.BPP_PUBLIC_KEY;
const isSandbox = props.isSandbox;
const myFileSystemPolicy = new iam.PolicyDocument({
statements: [new iam.PolicyStatement({
actions: [
'elasticfilesystem:ClientRootAccess',
'elasticfilesystem:ClientWrite',
'elasticfilesystem:ClientMount',
],
principals: [new iam.ArnPrincipal('*')],
resources: ['*'],
conditions: {
Bool: {
'elasticfilesystem:AccessedViaMountTarget': 'true',
},
},
})],
});
const efsBppFileSystemId = new efs.FileSystem(this, 'Beckn-Onix-Bpp', {
vpc: props.vpc,
securityGroup: props.eksSecGrp,
fileSystemPolicy: myFileSystemPolicy,
});
new helm.HelmChart(this, 'Bpphelm', {
cluster: eksCluster,
chart: 'beckn-onix-bpp',
release: releaseName,
wait: false,
repository: repository,
values: {
global: {
isSandbox: isSandbox,
externalDomain: externalDomain,
registry_url: registryUrl,
bpp: {
privateKey: bppPrivateKey,
publicKey: bppPublicKey,
},
efs: {
fileSystemId: efsBppFileSystemId.fileSystemId,
},
ingress: {
tls: {
certificateArn: certArn,
},
},
},
},
}
);
new cdk.CfnOutput(this, String("EksFileSystemId"), {
value: efsBppFileSystemId.fileSystemId,
});
}
}

View File

@@ -0,0 +1,54 @@
import * as cdk from 'aws-cdk-lib';
import * as eks from 'aws-cdk-lib/aws-eks';
import * as helm from 'aws-cdk-lib/aws-eks';
import { Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { ConfigProps } from './config';
interface HelmGAtewayStackProps extends cdk.StackProps {
config: ConfigProps;
eksCluster: eks.Cluster;
rdsHost: string;
rdsPassword: string;
}
export class HelmGatewayStack extends Stack {
constructor(scope: Construct, id: string, props: HelmGAtewayStackProps) {
super(scope, id, props);
const eksCluster = props.eksCluster;
const externalDomain = props.config.GATEWAY_EXTERNAL_DOMAIN;
const certArn = props.config.CERT_ARN;
const registryUrl = props.config.REGISTRY_URL;
const releaseName = props.config.GATEWAY_RELEASE_NAME;
const repository = props.config.REPOSITORY;
const rdsHost = props.rdsHost;
const rdsPassword = props.rdsPassword;
new helm.HelmChart(this, "gatewayhelm", {
cluster: eksCluster,
chart: "beckn-onix-gateway",
release: releaseName,
wait: false,
repository: repository,
values: {
externalDomain: externalDomain,
registry_url: registryUrl,
database: {
host: rdsHost,
password: rdsPassword,
},
ingress: {
tls:
{
certificateArn: certArn,
},
},
}
});
}
}

View File

@@ -0,0 +1,50 @@
import * as cdk from 'aws-cdk-lib';
import * as eks from 'aws-cdk-lib/aws-eks';
import * as helm from 'aws-cdk-lib/aws-eks';
import { Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { ConfigProps } from './config';
interface HelmRegistryStackProps extends StackProps {
config: ConfigProps;
eksCluster: eks.Cluster;
rdsHost: string;
rdsPassword: string;
}
export class HelmRegistryStack extends Stack {
constructor(scope: Construct, id: string, props: HelmRegistryStackProps) {
super(scope, id, props);
const eksCluster = props.eksCluster;
const externalDomain = props.config.REGISTRY_EXTERNAL_DOMAIN;
const certArn = props.config.CERT_ARN;
const releaseName = props.config.REGISTRY_RELEASE_NAME;
const repository = props.config.REPOSITORY;
const rdsHost = props.rdsHost;
const rdsPassword = props.rdsPassword;
new helm.HelmChart(this, "registryhelm", {
cluster: eksCluster,
chart: "beckn-onix-registry",
release: releaseName,
wait: false,
repository: repository,
values: {
externalDomain: externalDomain,
database: {
host: rdsHost,
password: rdsPassword
},
ingress: {
tls:
{
certificateArn: certArn,
},
},
}
});
}
}

View File

@@ -0,0 +1,66 @@
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as amazonmq from 'aws-cdk-lib/aws-amazonmq';
import * as dotenv from 'dotenv';
import { ConfigProps } from './config';
// Load environment variables from .env file
dotenv.config();
interface RabbitMqStackProps extends cdk.StackProps {
config: ConfigProps;
vpc: ec2.Vpc;
}
export class RabbitMqStack extends cdk.Stack {
constructor(scope: Construct, id: string, props: RabbitMqStackProps) {
super(scope, id, props);
// Prompt for the RabbitMQ admin password using environment variable
const rabbitMqPassword = new cdk.CfnParameter(this, 'RabbitMqPassword', {
type: 'String',
description: 'The password for the RabbitMQ broker admin user',
noEcho: true, // Ensure the password is hidden from the console
default: props.config.RABBITMQ_PASSWORD || '', // Use the password from .env or set a fallback
});
// Security group for RabbitMQ
const rabbitMqSecurityGroup = new ec2.SecurityGroup(this, 'RabbitMqSecurityGroup', {
vpc: props.vpc,
description: 'Security group for RabbitMQ broker',
allowAllOutbound: true,
});
rabbitMqSecurityGroup.addIngressRule(ec2.Peer.ipv4(props.vpc.vpcCidrBlock), ec2.Port.tcp(5672), 'Allow RabbitMQ traffic on port 5672');
rabbitMqSecurityGroup.addIngressRule(ec2.Peer.ipv4(props.vpc.vpcCidrBlock), ec2.Port.tcp(15672), 'Allow RabbitMQ management traffic');
// Select a single private subnet for the RabbitMQ Broker
const privateSubnets = props.vpc.selectSubnets({ subnetType: ec2.SubnetType.PRIVATE_WITH_NAT }).subnets;
// Ensure there's at least one subnet, and use the first one
if (privateSubnets.length === 0) {
throw new Error('No private subnets found in the VPC');
}
const selectedSubnet = privateSubnets[0]; // Use the first subnet
// RabbitMQ Broker
new amazonmq.CfnBroker(this, 'RabbitMqBroker', {
brokerName: 'MyRabbitMqBroker',
engineType: 'RABBITMQ',
engineVersion: '3.10.25',
deploymentMode: 'SINGLE_INSTANCE',
publiclyAccessible: false,
hostInstanceType: 'mq.m5.large', // Adjust the instance type as needed
subnetIds: [selectedSubnet.subnetId], // Pass a single subnet
securityGroups: [rabbitMqSecurityGroup.securityGroupId],
users: [
{
username: 'becknadmin', // Fixed username
password: rabbitMqPassword.valueAsString, // Password entered by the user or set from the .env file
},
],
});
}
}

View File

@@ -0,0 +1,50 @@
import * as cdk from 'aws-cdk-lib';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as rds from 'aws-cdk-lib/aws-rds';
import { Construct } from 'constructs';
import { ConfigProps } from './config';
import cluster from 'cluster';
export interface RdsStackProps extends cdk.StackProps {
config: ConfigProps;
vpc: ec2.Vpc;
}
export class RdsStack extends cdk.Stack {
public readonly rdsSecret: string;
public readonly rdsHost: string;
constructor(scope: Construct, id: string, props: RdsStackProps) {
super(scope, id, props);
// Security group for RDS
const dbSecurityGroup = new ec2.SecurityGroup(this, 'DatabaseSecurityGroup', {
vpc: props.vpc,
description: 'Security group for Aurora PostgreSQL database',
allowAllOutbound: true,
});
dbSecurityGroup.addIngressRule(ec2.Peer.ipv4(props.vpc.vpcCidrBlock), ec2.Port.tcp(5432), 'Allow Postgres access');
// Create Aurora PostgreSQL database cluster
const cluster = new rds.DatabaseCluster(this, 'AuroraCluster', {
engine: rds.DatabaseClusterEngine.auroraPostgres({
version: rds.AuroraPostgresEngineVersion.VER_13_15,
}),
instances: 2,
instanceProps: {
vpc: props.vpc,
vpcSubnets: {
subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
},
securityGroups: [dbSecurityGroup],
instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE3, ec2.InstanceSize.MEDIUM),
},
credentials: rds.Credentials.fromGeneratedSecret('dbadmin'),
defaultDatabaseName: 'MyDatabase',
removalPolicy: cdk.RemovalPolicy.DESTROY, // Destroy cluster when stack is deleted (useful for development)
});
this.rdsHost = cluster.clusterEndpoint.hostname;
}
}

View File

@@ -0,0 +1,84 @@
import * as cdk from 'aws-cdk-lib';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as rds from 'aws-cdk-lib/aws-rds';
import { Construct } from 'constructs';
import { ConfigProps } from './config';
import cluster from 'cluster';
import { Secret } from 'aws-cdk-lib/aws-secretsmanager';
export interface RdsStackProps extends cdk.StackProps {
config: ConfigProps;
envC: string;
vpc: ec2.Vpc;
}
export class RdsStack extends cdk.Stack {
public readonly rdsSecret: string;
public readonly rdsHost: string;
public readonly rdsPassword: string;
constructor(scope: Construct, id: string, props: RdsStackProps) {
super(scope, id, props);
const vpc = props.vpc;
const dbName = props.envC;
const rdsUser = props.config.RDS_USER; // take input from user / make it
const rdsPassword = this.createPassword();
const rdsSecGrpIngress = props.config.CIDR;
const securityGroupRDS = new ec2.SecurityGroup(this, 'RdsSecurityGroup', {
vpc: vpc,
allowAllOutbound: true,
description: 'Security group for Aurora PostgreSQL database',
});
securityGroupRDS.addIngressRule(
ec2.Peer.ipv4(rdsSecGrpIngress),
ec2.Port.tcp(5432),
"Allow Postgress Access"
);
const creds = new Secret(this, "rdsSecret", {
secretObjectValue: {
username: cdk.SecretValue.unsafePlainText(rdsUser.toString()),
password: cdk.SecretValue.unsafePlainText(rdsPassword.toString()),
},
});
const cluster = new rds.DatabaseCluster(this, 'AuroraCluster', {
engine: rds.DatabaseClusterEngine.auroraPostgres({
version: rds.AuroraPostgresEngineVersion.VER_14_6,
}),
credentials: rds.Credentials.fromSecret(creds),
instances: 1,
instanceProps: {
vpc: props.vpc,
vpcSubnets: {
subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
},
securityGroups: [securityGroupRDS],
instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE3, ec2.InstanceSize.MEDIUM),
},
defaultDatabaseName: dbName,
});
this.rdsSecret = creds.secretArn;
this.rdsHost = cluster.clusterEndpoint.hostname;
this.rdsPassword = rdsPassword;
new cdk.CfnOutput(this, 'RDSPasswordOutput', {
value: rdsPassword,
exportName: `RDSPassword-${dbName}`,
})
}
//generate password function
private createPassword(length: number = 12): string {
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%&()*+,-.:;<=>?[]^_`{|}~';
let password = '';
for (let i = 0; i < length; i++) {
password += characters.charAt(Math.floor(Math.random() * characters.length));
}
return password;
}
}

View File

@@ -0,0 +1,38 @@
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as elasticache from 'aws-cdk-lib/aws-elasticache';
interface RedisStackProps extends cdk.StackProps {
vpc: ec2.Vpc;
}
export class RedisStack extends cdk.Stack {
constructor(scope: Construct, id: string, props: RedisStackProps) {
super(scope, id, props);
// Security group for ElastiCache
const elasticacheSecurityGroup = new ec2.SecurityGroup(this, 'ElastiCacheSecurityGroup', {
vpc: props.vpc,
description: 'Security group for Redis',
allowAllOutbound: true,
});
elasticacheSecurityGroup.addIngressRule(ec2.Peer.ipv4(props.vpc.vpcCidrBlock), ec2.Port.tcp(6379), 'Allow Redis traffic');
// Redis subnet group
const redisSubnetGroup = new elasticache.CfnSubnetGroup(this, 'RedisSubnetGroup', {
description: 'Subnet group for Redis cluster',
subnetIds: props.vpc.selectSubnets({ subnetType: ec2.SubnetType.PRIVATE_WITH_NAT }).subnetIds,
});
// Redis Cluster
new elasticache.CfnCacheCluster(this, 'RedisCluster', {
cacheNodeType: 'cache.t3.medium', // Adjust the node type based on your needs
engine: 'redis',
numCacheNodes: 1,
vpcSecurityGroupIds: [elasticacheSecurityGroup.securityGroupId],
cacheSubnetGroupName: redisSubnetGroup.ref,
});
}
}

View File

@@ -0,0 +1,76 @@
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as elb from 'aws-cdk-lib/aws-elasticloadbalancingv2';
import { ConfigProps } from './config';
export interface VpcStackProps extends cdk.StackProps {
config: ConfigProps;
}
export class VpcStack extends cdk.Stack {
public readonly vpc: ec2.Vpc;
// public readonly alb: elb.ApplicationLoadBalancer;
constructor(scope: Construct, id: string, props: VpcStackProps) {
super(scope, id, props);
const config = props.config;
// Create a new VPC
this.vpc = new ec2.Vpc(this, 'beckn-onix-vpc', {
maxAzs: config.MAX_AZS, // Maximum number of availability zones
cidr: config.CIDR,
natGateways: 1, // Single NAT Gateway in the public subnet
subnetConfiguration: [
{
cidrMask: 24,
name: 'Public',
subnetType: ec2.SubnetType.PUBLIC,
},
{
cidrMask: 24,
name: 'AppLayer',
subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS, // Use the newer "PRIVATE_WITH_EGRESS" instead of PRIVATE_WITH_NAT
},
{
cidrMask: 24,
name: 'DatabaseLayer',
subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
}
]
});
// Output the VPC CIDR block for other stacks to reference
new cdk.CfnOutput(this, 'VpcCidrBlock', {
value: this.vpc.vpcCidrBlock,
exportName: 'VpcCidrBlock-env', // Export name to reference in other stacks
});
// Output the VPC ID for other stacks
new cdk.CfnOutput(this, 'VpcId', {
value: this.vpc.vpcId,
exportName: 'VpcId', // Export name to reference in other stacks
});
// Output the Public Subnet IDs
new cdk.CfnOutput(this, 'PublicSubnetIds', {
value: this.vpc.publicSubnets.map(subnet => subnet.subnetId).join(','),
exportName: 'PublicSubnetIds', // Export name to reference in other stacks
});
// Output the App Layer Subnet IDs (for application instances or services)
new cdk.CfnOutput(this, 'AppLayerSubnetIds', {
value: this.vpc.selectSubnets({ subnetGroupName: 'AppLayer' }).subnetIds.join(','),
exportName: 'AppLayerSubnetIds', // Export name to reference in other stacks
});
// Output the Database Layer Subnet IDs (for database instances)
new cdk.CfnOutput(this, 'DatabaseSubnetIds', {
value: this.vpc.selectSubnets({ subnetGroupName: 'DatabaseLayer' }).subnetIds.join(','),
exportName: 'DatabaseSubnetIds', // Export name to reference in other stacks
});
}
}