How we build this website

image of author

Richard Hillmann

This week is the Hackathon at AutoScout24. It is amazing how many ideas came up this year! One of those ideas was to revamp a for our tech organization. With the help of a UX designer, three engineers and of course technology we were able to ramp up the first iteration of this website within a day!

All about standards #

Scaffolding a new project is very easy at AutoScout24. We used to have project templates for almost all common project styles and programming languages.
A typical project template contains all necessary components like pipeline as code, deployment scripts and starter templates for languages and frameworks.

With the help of our open sourced frontend library ShowCar UI we can quickly start by re-using existing components and keep the brand design across all AutoScout24 products.

The blog engine #

To keep things simple and fast we usually generate static websites wherever is possible. To achieve this there are a lot of frameworks out in the wild like Hugo or Next.js.

This time we decided to give Eleventy (11lty) a chance. Eleventy has a flexible and independent template engine so you can decide how to provide and build your content.
Currently we use a mix of Nunjuck for the page rendering and Markdown for our blog articles. Writers can easily provide a new article in Markdown via Pull Requests to our github repository and it is easy to review articles as we do not need to face with syntax issues.

How we deploy the website with AWS CDK #

At AutoScout24 we love Infrastructure as Code. When we started our journey with AWS several years ago, every resource had always to be declared with AWS CloudFormation.

Writing plain CloudFormation templates became a tedious work with a lot of forth and back during deployments. Copy and paste culture, outdated best practices, wrong resource definitions and non-standardized tags were only a few of the problems during development.

Last year we started adopting AWS CDK as replacement for CloudFormation templates. It helps us to define and share common components or best practices within our organization.
Our engineers like the way to define their infrastructure in a programmatic way as it fits in their normal workflow writing code.

For this project, AWS CDK helped us to quickly ramp up the required infrastructure with least effort.
The little helpers shipped within the core CDK library provides everything you need to deploy a static website with serverless components.

Typically AWS S3 and Amazon CloudFront is everything you need to deliver you static assets.
AWS Certificate Manager and Route 53 rounds up the setup with a free HTTPS certificate and proper domain setup.

A common challenge when deploying a new CloudFront distribution in another region than us-east-1 is the association of a certificate. As we have very strict compliance rules forcing us to avoid deploying resources outside of Europe this can become a challenge.

To solve this problem we deploy two CloudFormation stacks: A certificate stack deployed in us-east-1 and our application stack in eu-west-1.
The SSM Parameter Store and the custom lambda helpers us to share the certificate ARN across regions natively within the CDK only:

Please note there is also a native solution which does not require a separate stack in a different region. As long the Route 53 hosted zone lives in the same account you can also use the acm.DnsValidatedCertificate construct which performs automated DNS validation.

// Certificate stack deployed in us-east-1
export class CloudfrontCertificate extends cdk.Stack {
constructor(app: cdk.App, id: string, props: cdk.StackProps) {
super(app, id, props);
const cert = new acm.Certificate(this, "certificate", {
domainName: props.domain,
// we have some automation in AutoScout24 which approves the validation request
validation: acm.CertificateValidation.fromEmail({
"your.sub.domain.tld": "domain.tld",
}),
});
new ssm.StringParameter(this, "parameter", {
parameterName: `/stack/${this.stackName}/certificate-arn`,
stringValue: cert.certificateArn,
});
}
}

The AWS CDK library provides a very nice helper construct to perform arbitrary API calls, even cross region. It creates a custom resource and returns the response as resource parameter:

import {AwsCustomResource, PhysicalResourceId} from '@aws-cdk/custom-resources'
export class Service extends cdk.Stack {
constructor(app: cdk.App, id: string, props: cdk.StackProps) {
super(app, id, props)
const certificateStackName = `certificate-stack-us-east-1`;
// workaround by receiving the certificate ARN from the us-east-1 stack via SSM
const certificate = new AwsCustomResource(this, 'getCertificateArn', {
onUpdate: {
service: 'SSM',
action: 'getParameter',
parameters: {
Name: `/stack/${certificateStackName}/certificate-arn`,
},
region: 'us-east-1',
physicalResourceId: PhysicalResourceId.of(Date.now().toString()), // Update physical id to always fetch the latest version
},
policy: {
statements: [
new iam.PolicyStatement({
resources: ['*'],
actions: ['ssm:GetParameter'],
effect: iam.Effect.ALLOW,
}),
],
},
})
const cert = acm.Certificate.fromCertificateArn(this, 'cert', certificate.getResponseField('Parameter.Value'))
}
}

Hosting a static website with a proper domain and caching in between is fairly easy with S3 and CloudFront. The CDK created the necessary resources and configuration out of the box :

    // The S3 bucket hosting our website
const contentBucket = new s3.Bucket(this, 'bucket', {
websiteIndexDocument: 'index.html',
websiteErrorDocument: '404.html',
encryption: s3.BucketEncryption.S3_MANAGED,
})
// s3 website needs to be accessible by cloudfront as HTTP origin, so we need to allow public access.
contentBucket.grantPublicAccess()

// CloudFront distribution that provides HTTPS
const distribution = new cloudfront.Distribution(this, 'cdn', {
priceClass: cloudfront.PriceClass.PRICE_CLASS_100,
certificate: cert,
domainNames: ['your.sub.domain.tld'],
defaultBehavior: {
origin: new origins.S3Origin(contentBucket),
viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
},
})

Great! We have now a working infrastructure, but the s3 bucket is still empty. Fortunately CDK can take care of deploying our website to the bucket. We even do not need to write any scripts to sync the content or invalidate the cache on cloudfront:

    new s3deploy.BucketDeployment(this, 'DeployWithInvalidation', {
sources: [s3deploy.Source.asset('../path/to/distribution/assets')],
destinationBucket: contentBucket,
distribution,
distributionPaths: ['/*'],
})

AWS CDK can take a lof of work away from people by defining re-usable patterns which can be shared easily by a library.

Stay tuned to learn more how we use CDK at AutoScout24!


image of author
Written by Richard Hillmann

Senior Platform Engineer at Autoscout24