This is a submission for the Pulumi Deploy and Document Challenge: Fast Static Website Deployment
What I Built
I deployed an Angular application (SkillSwap) to AWS using infrastructure as code with Pulumi. The solution includes:
- S3 bucket for static website hosting
- CloudFront CDN for global content delivery with HTTPS
- SPA routing support for Angular's client-side routing
- Automated deployment process with a single command
SkillSwap is a skill-sharing platform where users can exchange services based on their skills. The Angular application features user profiles, messaging, skill search, and ratings.
Live Demo Link
SkillSwap Application (Example CloudFront URL - actual URL will be available after deployment)
Project Repo
The project repository contains both the Angular application and the infrastructure code:
skillswap/ # Angular application
skillswap-infra/ # Pulumi infrastructure code
Repository Structure
The infrastructure portion includes:
-
index.ts
- Main Pulumi infrastructure definition -
deploy.js
- Automated deployment script -
README.md
- Setup and usage instructions -
architecture.puml
- Architecture diagram source
My Journey
Choosing Technologies
For this challenge, I selected:
- Angular as my static website framework for its robust TypeScript support and component architecture
- AWS (S3 + CloudFront) as my cloud provider for its reliability and global presence
- Pulumi as my IaC tool for its use of familiar programming languages (TypeScript)
Setup & Planning
I started by setting up a dedicated infrastructure directory separate from the Angular application code. This separation of concerns helps maintain clean architecture and allows the infrastructure to evolve independently.
mkdir skillswap-infra
cd skillswap-infra
npm init -y
npm install @pulumi/pulumi @pulumi/aws typescript @types/node mime @types/mime
npx tsc --init
Infrastructure Definition
The core infrastructure is defined in index.ts
, where I created:
- S3 Bucket configured for static website hosting:
const siteBucket = new aws.s3.Bucket("skillswap-website-bucket", {
website: {
indexDocument: "index.html",
errorDocument: "index.html",
},
});
- Public access policy to allow website visitors to access content:
const bucketPolicy = new aws.s3.BucketPolicy("bucketPolicy", {
bucket: siteBucket.id,
policy: siteBucket.id.apply(bucketId => JSON.stringify({
Version: "2012-10-17",
Statement: [{
Effect: "Allow",
Principal: "*",
Action: ["s3:GetObject"],
Resource: [`arn:aws:s3:::${bucketId}/*`]
}]
}))
});
- CloudFront distribution for global content delivery and HTTPS:
const cdn = new aws.cloudfront.Distribution("skillswap-cdn", {
enabled: true,
origins: [{
originId: siteBucket.arn,
domainName: siteBucket.websiteEndpoint,
customOriginConfig: {
originProtocolPolicy: "http-only",
httpPort: 80,
httpsPort: 443,
originSslProtocols: ["TLSv1.2"],
},
}],
// ... additional configuration ...
});
- SPA routing support for Angular's client-side routing:
customErrorResponses: [
{
errorCode: 404,
responseCode: 200,
responsePagePath: "/index.html",
},
],
Challenges Faced
- SPA Routing Configuration
One of the main challenges was configuring CloudFront to properly handle Angular's client-side routing. When users navigate to a route directly (e.g., /profile/123
), the server would typically return a 404 since there's no physical file at that path.
Solution: I configured CloudFront to redirect 404 errors to index.html
, allowing Angular's router to handle the navigation:
customErrorResponses: [
{
errorCode: 404,
responseCode: 200,
responsePagePath: "/index.html",
},
],
- File Upload Mechanism
Another challenge was efficiently syncing the Angular build files to S3 while preserving the correct content types.
Solution: I created a recursive directory walking function that:
- Discovers all files in the build directory
- Determines the correct MIME type for each file
- Creates S3 bucket objects with appropriate metadata
- Handles Windows path separators by converting to URL-compatible format
- Deployment Automation
To simplify the deployment process, I wanted a single command that would build the Angular app if needed and deploy the infrastructure.
Solution: I created a deploy.js
script that:
- Checks if the Angular app is already built
- Builds the app if necessary
- Dynamically updates the Pulumi code to enable file uploads
- Runs the Pulumi deployment
Performance Optimizations
I implemented several performance optimizations:
- CloudFront Caching: Configured TTLs for different content types to optimize caching behavior
- Compression: Enabled gzip/Brotli compression for text-based assets
- Region Selection: CloudFront automatically uses edge locations closest to users
Using Pulumi
Pulumi provided several key advantages for this project:
Familiar Language: Using TypeScript meant I could leverage the same language for both my application and infrastructure, reducing context switching.
Programmatic Abstractions: I could create reusable functions for tasks like file uploading, making the code more maintainable.
Resource Relationships: Pulumi's programming model made it easy to define relationships between resources (like the S3 bucket and CloudFront distribution).
State Management: Pulumi handles the state management, tracking what resources are created and their properties, making updates safer.
Preview Capabilities: The
pulumi preview
command let me see what changes would be made before applying them.
Key Benefits Over Traditional Methods
Compared to manual deployment or using CloudFormation/Terraform, Pulumi offered:
- Easier Debugging: Using a full programming language made debugging more straightforward
- Richer Abstractions: I could use loops, conditionals, and functions to generate resources
- Better Developer Experience: IntelliSense and type checking helped prevent errors
- Easier Integration: Working with the Angular build process was seamless
Architecture Diagram
The following diagram illustrates the architecture of the deployed solution:
[User] --> [CloudFront CDN] --> [S3 Bucket]
^ ^
| |
+---------- [Pulumi] -----------+
Next Steps
Future improvements could include:
- Custom domain configuration with SSL certificates
- CI/CD pipeline integration
- Environment-specific configurations
- Performance monitoring and analytics
Conclusion
This project demonstrates how Pulumi can be used to deploy a static website to AWS efficiently. The infrastructure-as-code approach provides repeatability, maintainability, and automation, while leveraging familiar programming languages and tools.
Top comments (0)