lib/ide-services-cloudfront.ts (124 lines of code) (raw):
import * as cdk from "aws-cdk-lib";
import {CustomResource} from "aws-cdk-lib";
import {Construct} from "constructs";
import * as cloudfront from 'aws-cdk-lib/aws-cloudfront';
import {OriginProtocolPolicy} from 'aws-cdk-lib/aws-cloudfront';
import * as eks from "aws-cdk-lib/aws-eks";
import {HelmChart} from "aws-cdk-lib/aws-eks";
import * as cloudfront_origins from 'aws-cdk-lib/aws-cloudfront-origins';
import * as elasticloadbalancingv2 from 'aws-cdk-lib/aws-elasticloadbalancingv2';
import * as cr from 'aws-cdk-lib/custom-resources';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as logs from 'aws-cdk-lib/aws-logs';
interface IdeServicesCloudfrontProps extends cdk.NestedStackProps {
cluster: eks.Cluster;
ideServicesChart: HelmChart;
}
export class IdeServicesCloudfront extends cdk.NestedStack {
deploymentUrl: string;
constructor(scope: Construct, id: string, props: IdeServicesCloudfrontProps) {
super(scope, id, props);
const {cluster, ideServicesChart} = props;
const vpc = cluster.vpc;
const findLoadBalancerFunction = this.createFindLoadBalancerFunction();
// Provision a custom resource provider framework
const provider = new cr.Provider(this, 'FindLoadBalancerProvider', {
onEventHandler: findLoadBalancerFunction,
logGroup: new logs.LogGroup(this, 'FindLoadBalancerLogs', {
retention: logs.RetentionDays.ONE_DAY,
}),
});
// Create a custom resource to look up the ALB by its DNS name
const ingressNamePrefix = "k8s-ideservicesgroup-"
const findLoadBalancer = new CustomResource(this , 'FindLoadBalancer', {
serviceToken: provider.serviceToken,
properties: {
namePrefix: ingressNamePrefix,
},
});
findLoadBalancer.node.addDependency(ideServicesChart)
// Extract the load balancer ARN and security group ID from the Lambda function's response
const loadBalancerArn = findLoadBalancer.getAttString('loadBalancerArn');
const loadBalancerDnsName = findLoadBalancer.getAttString('dnsName');
const securityGroupId = findLoadBalancer.getAttString('securityGroupId');
// Import the ALB using its attributes
const alb = elasticloadbalancingv2.ApplicationLoadBalancer.fromApplicationLoadBalancerAttributes(
this,
'ImportedAlb',
{
loadBalancerArn: loadBalancerArn,
loadBalancerDnsName: loadBalancerDnsName,
securityGroupId: securityGroupId,
vpc: vpc,
}
);
// An Application Load Balancer as a VPC origin
const albOrigin = cloudfront_origins.VpcOrigin.withApplicationLoadBalancer(alb, {
// Optional VPC origin configurations
domainName: loadBalancerDnsName,
readTimeout: cdk.Duration.seconds(30),
keepaliveTimeout: cdk.Duration.seconds(5),
protocolPolicy: OriginProtocolPolicy.HTTP_ONLY,
});
// Create CloudFront distribution with ALB as VPC origin
const distribution = new cloudfront.Distribution(this, 'AlbCloudFrontDistribution', {
defaultBehavior: {
origin: albOrigin,
originRequestPolicy: cloudfront.OriginRequestPolicy.ALL_VIEWER,
allowedMethods: cloudfront.AllowedMethods.ALLOW_ALL,
cachePolicy: cloudfront.CachePolicy.CACHING_DISABLED,
viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
},
priceClass: cloudfront.PriceClass.PRICE_CLASS_100, // Use only North America and Europe
enabled: true,
comment: 'CloudFront distribution with VPC origin for ALB',
});
this.deploymentUrl = `https://${distribution.distributionDomainName}`
}
private createFindLoadBalancerFunction(): lambda.Function {
// Create a Lambda function to find the ALB by its DNS name
const fn = new lambda.Function(this, 'FindLoadBalancerFunction', {
runtime: lambda.Runtime.NODEJS_22_X,
handler: 'index.handler',
code: lambda.Code.fromInline(`
// Import AWS SDK v3 modules
const { ElasticLoadBalancingV2Client, DescribeLoadBalancersCommand } = require('@aws-sdk/client-elastic-load-balancing-v2');
const { EC2Client, DescribeSecurityGroupsCommand } = require('@aws-sdk/client-ec2');
exports.handler = async (event) => {
console.log('Event:', JSON.stringify(event));
const namePrefix = event.ResourceProperties.namePrefix;
if (!namePrefix) {
throw new Error('ALB name prefix is required');
}
// Initialize AWS clients
const elbv2Client = new ElasticLoadBalancingV2Client();
const ec2Client = new EC2Client();
try {
// Get all load balancers
const describeLoadBalancersCommand = new DescribeLoadBalancersCommand({});
const loadBalancersResponse = await elbv2Client.send(describeLoadBalancersCommand);
console.log('Load balancers:', JSON.stringify(loadBalancersResponse));
// Find the load balancer with the matching DNS name
const loadBalancer = loadBalancersResponse.LoadBalancers.find(lb => lb.LoadBalancerName.startsWith(namePrefix));
if (!loadBalancer) {
throw new Error(\`Load balancer with name \${namePrefix} not found\`);
}
return {
PhysicalResourceId: loadBalancer.LoadBalancerArn,
Data: {
loadBalancerArn: loadBalancer.LoadBalancerArn,
securityGroupId: loadBalancer.SecurityGroups[0],
dnsName: loadBalancer.DNSName,
}
};
} catch (error) {
console.error('Error:', error);
throw error;
}
};
`),
timeout: cdk.Duration.minutes(5),
});
// Grant the Lambda function permission to describe load balancers and security groups
fn.addToRolePolicy(new iam.PolicyStatement({
actions: [
'elasticloadbalancing:DescribeLoadBalancers',
'ec2:DescribeSecurityGroups'
],
resources: ['*'],
}));
return fn;
}
}