Creating a robust infrastructure to serve web assets efficiently is crucial for modern applications. We'll explore how to leverage Pulumi, a powerful infrastructure as code tool, to set up an Amazon S3 bucket and CloudFront distribution. By combining S3's reliable storage with CloudFront's global content delivery network (CDN), we can significantly enhance the performance and availability of our web assets.
Pulumi simplifies the process of provisioning and managing cloud resources, allowing us to define our infrastructure using familiar programming languages. In this guide, we'll walk through the steps to create an S3 bucket for storing our assets and configure a CloudFront distribution to serve them. This approach enables us to take advantage of AWS's scalable and cost-effective services while maintaining full control over our infrastructure.
Setting Up AWS Credentials
To use Pulumi with AWS, we need to set up our AWS credentials. This allows Pulumi to authenticate and interact with AWS services on our behalf.
We can configure our AWS credentials in several ways:
- Using the AWS CLI with aws configure
- Setting environment variables (AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY)
- Using a shared credentials file (~/.aws/credentials)
For Pulumi, we recommend using the ~/.aws/credentials file. Here's an example of its contents:
[default]
aws_access_key_id = YOUR_ACCESS_KEY_ID
aws_secret_access_key = YOUR_SECRET_ACCESS_KEY
Once we've set up our credentials, Pulumi will automatically detect and use them when we run our infrastructure code.
Remember to keep these credentials secure and never commit them to version control.
Installing and Configuring Pulumi
Pulumi is a powerful infrastructure as code tool that allows us to define cloud resources using familiar programming languages. We'll walk through the essential steps to get Pulumi up and running for our AWS project.
Installing the Pulumi CLI
To begin using Pulumi, we need to install the Pulumi CLI. The installation process varies depending on your operating system.
Install PulumiCreating an S3 Bucket with Pulumi
To serve assets using CloudFront, we first need to set up an Amazon S3 bucket. Pulumi makes this process straightforward and allows us to define our infrastructure as code.
We start by importing the necessary Pulumi AWS package. In our Pulumi program, we create an S3 bucket using the aws.s3.Bucket resource
import { Bucket } from '@pulumi/aws/s3';
const bucket = new Bucket('cdn.example.com:s3:bucket', {
bucket: 'cdn.example.com',
acl: 'private',
forceDestroy: true,
tags: {
name: 'cdn.example.com',
},
});
Configuring S3 Bucket Policies
To ensure proper access to our bucket, we need to set up appropriate policies. We can define a bucket policy using Pulumi:
import { BucketPolicy } from '@pulumi/aws/s3';
import { OriginAccessIdentity } from '@pulumi/aws/cloudfront';
const oai = new OriginAccessIdentity('cdn.example.com:cloudfront:oai', {
comment: 'cdn.example.com',
});
const bucket_policy = new BucketPolicy('cdn.example.com:s3:bucket:policy', {
bucket: bucket.bucket,
policy: interpolate`{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity ${oai.id}"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::${bucket.bucket}/*"
}
]
}`,
});
This setup ensures that only CloudFront can access the S3 bucket directly
Setting Up a CloudFront Distribution
CloudFront distributions enable fast and secure content delivery to users worldwide. We'll explore how to define and configure a distribution using Pulumi, set up caching, add custom domains, implement SSL/TLS, and secure access with Origin Access Identity
Implementing SSL/TLS
To secure our distribution with HTTPS, we need an SSL/TLS certificate. We can use AWS Certificate Manager (ACM):
import { Zone } from '@pulumi/aws/route53';
const zone = new Zone('example.com:route53:zone', {
name: 'example.com',
});
Once we have the zone, we can create an ACM certificate for it:
import { Certificate, CertificateValidation } from '@pulumi/aws/acm';
import { Record } from '@pulumi/aws/route53';
const certificate = new Certificate('cdn.example.com:certificate', {
domainName: 'cdn.example.com',
subjectAlternativeNames: [
'www.cdn.example.com',
],
validationMethod: 'DNS',
tags: {
Name: 'cdn.example.com',
},
});
const validation = new Record('cdn.example.com:certificate:validation:dns', {
name: certificate.domainValidationOptions[0].resourceRecordName,
records: [certificate.domainValidationOptions[0].resourceRecordValue],
ttl: 60,
type: certificate.domainValidationOptions[0].resourceRecordType,
zoneId: zone.zoneId,
});
const validation_www = new Record('cdn.example.com:certificate:validation:dns:www', {
name: certificate.domainValidationOptions[1].resourceRecordName,
records: [certificate.domainValidationOptions[1].resourceRecordValue],
ttl: 60,
type: certificate.domainValidationOptions[1].resourceRecordType,
zoneId: zone.zoneId,
});
const certificate_validation = new CertificateValidation('cdn.example.com:certificate:validation', {
certificateArn: certificate.arn,
validationRecordFqdns: [
validation.fqdn,
validation_www.fqdn,
],
});
We can now create a CloudFront distribution using Pulumi:
import { Distribution } from '@pulumi/aws/cloudfront';
const distribution = new Distribution('cdn.example.com:cloudfront:distribution', {
origins: [
{
domainName: bucket.bucketRegionalDomainName,
originId: bucket.id,
s3OriginConfig: {
originAccessIdentity: oai.cloudfrontAccessIdentityPath,
},
},
],
defaultCacheBehavior: {
targetOriginId: bucket.id,
viewerProtocolPolicy: 'redirect-to-https',
allowedMethods: ['GET', 'HEAD', 'OPTIONS'],
cachedMethods: ['GET', 'HEAD', 'OPTIONS'],
forwardedValues: {
cookies: {
forward: 'none',
},
queryString: false,
},
minTtl: 0,
defaultTtl: 2678400,
maxTtl: 31536000,
},
priceClass: 'PriceClass_100',
enabled: true,
isIpv6Enabled: true,
restrictions: {
geoRestriction: {
restrictionType: 'none',
},
},
viewerCertificate: {
acmCertificateArn: certificate.arn,
sslSupportMethod: 'sni-only',
},
aliases: [
'cdn.example.com',
'www.cdn.example.com',
],
tags: {
Name: 'cdn.example.com',
},
});
This setup ensures that our CloudFront distribution serves our S3 bucket directly, sets up caching, adds custom domains, implements SSL/TLS, and secures access with Origin Access Identity
We can now create records in our DNS provider to point to our CloudFront distribution:
import { Record } from '@pulumi/aws/route53';
const main = new Record('cdn.example.com:dns:main', {
name: 'cdn.example.com',
zoneId: zone.zoneId,
type: 'A',
aliases: [
{
evaluateTargetHealth: true,
name: distribution.domainName,
zoneId: distribution.hostedZoneId,
},
],
});
const www = new Record('cdn.example.com:dns:www', {
name: 'www.cdn.example.com',
zoneId: zone.zoneId,
type: 'A',
aliases: [
{
evaluateTargetHealth: true,
name: distribution.domainName,
zoneId: distribution.hostedZoneId,
},
],
});