<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Joshua</title>
    <description>The latest articles on Forem by Joshua (@joshlite).</description>
    <link>https://forem.com/joshlite</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1467505%2F3cfdaf00-274c-4ff0-921b-09046112b3e3.png</url>
      <title>Forem: Joshua</title>
      <link>https://forem.com/joshlite</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/joshlite"/>
    <language>en</language>
    <item>
      <title>Fast Static Website Deployment with Pulumi</title>
      <dc:creator>Joshua</dc:creator>
      <pubDate>Tue, 01 Apr 2025 13:01:48 +0000</pubDate>
      <link>https://forem.com/joshlite/fast-static-website-deployment-with-pulumi-8pe</link>
      <guid>https://forem.com/joshlite/fast-static-website-deployment-with-pulumi-8pe</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/pulumi"&gt;Pulumi Deploy and Document Challenge&lt;/a&gt;: Fast Static Website Deployment&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;The goal of this project is to use AWS services, notably S3 and CloudFront, and &lt;a href="https://www.pulumi.com/" rel="noopener noreferrer"&gt;Pulumi&lt;/a&gt; to deploy a static website. Infrastructure as code (IaC) makes managing cloud resources easier while guaranteeing scalability, security, and effectiveness.&lt;/p&gt;

&lt;h2&gt;
  
  
  Live Demo Link
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://d3hiuxztivmq3z.cloudfront.net/index.html" rel="noopener noreferrer"&gt;https://d3hiuxztivmq3z.cloudfront.net/index.html&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Repo
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/dey-yemi/pulumi-static-website" rel="noopener noreferrer"&gt;https://github.com/dey-yemi/pulumi-static-website&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  My Journey
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Technologies used
&lt;/h2&gt;

&lt;p&gt;This project utilizes the following technologies:&lt;br&gt;
&lt;strong&gt;Cloud provider:&lt;/strong&gt; AWS&lt;br&gt;
&lt;strong&gt;Key services:&lt;/strong&gt; S3 Bucket, CloudFront&lt;br&gt;
&lt;strong&gt;Programming language:&lt;/strong&gt; Python 3.x&lt;/p&gt;
&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before getting started, ensure you have the following:&lt;br&gt;
&lt;strong&gt;·&lt;/strong&gt; An AWS account and a basic understanding of AWS services and python&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;·&lt;/strong&gt; Familiarity with linux commands&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;·&lt;/strong&gt; A Pulumi account to access the free &lt;a href="https://www.pulumi.com/product/copilot/" rel="noopener noreferrer"&gt;Pulumi copilot&lt;/a&gt;, which can assist you throughout the project.&lt;/p&gt;
&lt;h2&gt;
  
  
  Installing Pulumi
&lt;/h2&gt;

&lt;p&gt;Open PowerShell in administrator mode and use the Chocolatey package manager to install Pulumi. Run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;choco install pulumi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will install Pulumi along with all required dependencies, as illustrated in the image below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faa55n7uylwm52wt3v49s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faa55n7uylwm52wt3v49s.png" alt="Image description" width="800" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One issue I encountered during installation was not running powershell in administrator mode. This led to errors preventing Pulumi from being installed. To avoid this mistake, always launch powershell as an administrator before proceeding with the installation.&lt;/p&gt;

&lt;p&gt;Next, create a &lt;a href="https://www.pulumi.com/product/copilot/" rel="noopener noreferrer"&gt;Pulumi account&lt;/a&gt; and explore its services, products, documentation, and solutions. This will give you a clear understanding of what Pulumi offers and how it can support your infrastructure needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring iAM roles
&lt;/h2&gt;

&lt;p&gt;Log in to your Aws management console and navigate to the IAM service. Under users, click add user and enter a username - I used &lt;code&gt;pulumi-deploy-user&lt;/code&gt; as an example.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9cwizkfwexcvhkccy2xp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9cwizkfwexcvhkccy2xp.png" alt="Image description" width="800" height="248"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, select attach existing policies, search for AdministratorAccess and AmazonS3FullAccess, and attach them to the user.&lt;/p&gt;

&lt;p&gt;Once the user is created, you'll receive an access key id and secret Access Key. Keep these credentials secure and download the &lt;code&gt;.csv&lt;/code&gt; file for safekeeping.&lt;/p&gt;

&lt;p&gt;Next, open your VS Code terminal and run the &lt;code&gt;aws configure&lt;/code&gt; command. &lt;br&gt;
Your access key ID, secret access key, AWS region, and other necessary information will be requested.&lt;/p&gt;

&lt;p&gt;Be sure to keep your credentials private and never share them with anyone to maintain security.&lt;/p&gt;
&lt;h2&gt;
  
  
  Setting up Pulumi
&lt;/h2&gt;

&lt;p&gt;Run the &lt;code&gt;pulumi login&lt;/code&gt; command to authenticate your Pulumi cli with the Pulumi cloud or a self-hosted backend. &lt;/p&gt;

&lt;p&gt;This step is crucial for storing your infrastructure state and managing deployments efficiently. Before running this command, ensure you've created a directory for your project within your VS code terminal. In this case, the directory is named &lt;code&gt;static-website-pulumi&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Next, initialize a new Pulumi project by executing the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pulumi new aws-python
pip install pulumi pulumi-aws
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By initialising a new Pulumi project specifically designed for AWS using Python, the &lt;code&gt;pulumi new aws-python&lt;/code&gt; tool creates the necessary configuration files and directory structure.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb4blf6rk44er5tqf5ctk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb4blf6rk44er5tqf5ctk.png" alt="Image description" width="738" height="322"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Pulumi SDK and aws provider are then installed by running &lt;code&gt;pip install pulumi pulumi-aws&lt;/code&gt;, enabling smooth infrastructure provisioning and management for your project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu1iwd8xpqx1uxnrmxpyx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu1iwd8xpqx1uxnrmxpyx.png" alt="Image description" width="800" height="252"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploy your website
&lt;/h2&gt;

&lt;p&gt;Create a directory for your website files. Download your website template using the &lt;a href="https://www.tooplate.com/free-templates/2" rel="noopener noreferrer"&gt;provided link&lt;/a&gt;, then extract the zip file. Move all extracted files and folders into the "website" directory you created earlier.&lt;/p&gt;

&lt;p&gt;Next, update your&lt;code&gt;__main__.py&lt;/code&gt; file by replacing its content with the Python code provided below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import os
import mimetypes
import pulumi
import pulumi_aws as aws

# Create an S3 bucket for the website
site_bucket = aws.s3.Bucket("static-website-bucket",
    website=aws.s3.BucketWebsiteArgs(
        index_document="index.html",
        error_document="error.html",
    ))

# Create a bucket policy access
bucket_policy = aws.s3.BucketPolicy("bucket-policy",
    bucket=site_bucket.id,
    policy=site_bucket.id.apply(
        lambda id: f"""{{
            "Version": "2012-10-17",
            "Statement": [{{
                "Effect": "Allow",
                "Principal": "*",
                "Action": ["s3:GetObject"],
                "Resource": ["arn:aws:s3:::{id}/*"]
            }}]
        }}"""
    ))

# Helper function to recursively upload files
def upload_directory(directory_path, parent_path=""):
    for item in os.listdir(directory_path):
        item_path = os.path.join(directory_path, item)
        if os.path.isdir(item_path):
            # Recursively upload subdirectories
            upload_directory(item_path, os.path.join(parent_path, item))
        else:
            # Create an S3 object for each file
            file_path = os.path.join(parent_path, item)
            content_type = mimetypes.guess_type(item_path)[0] or "application/octet-stream"

            aws.s3.BucketObject(
                file_path.replace("\\", "/"),
                bucket=site_bucket.id,
                key=file_path.replace("\\", "/"),
                source=pulumi.FileAsset(item_path),
                content_type=content_type
            )

# Upload website files to S3
website_dir = "./website"
if os.path.exists(website_dir):
    upload_directory(website_dir)
else:
    print(f"Error: Website directory {website_dir} does not exist")

# CloudFront distribution for better performance
cdn = aws.cloudfront.Distribution("website-cdn",
    enabled=True,
    origins=[aws.cloudfront.DistributionOriginArgs(
        origin_id=site_bucket.arn,
        domain_name=site_bucket.website_endpoint,
        custom_origin_config=aws.cloudfront.DistributionOriginCustomOriginConfigArgs(
            http_port=80,
            https_port=443,
            origin_protocol_policy="http-only",
            origin_ssl_protocols=["TLSv1.2"],
        ),
    )],
    default_cache_behavior=aws.cloudfront.DistributionDefaultCacheBehaviorArgs(
        target_origin_id=site_bucket.arn,
        viewer_protocol_policy="redirect-to-https",
        allowed_methods=["GET", "HEAD", "OPTIONS"],
        cached_methods=["GET", "HEAD", "OPTIONS"],
        forwarded_values=aws.cloudfront.DistributionDefaultCacheBehaviorForwardedValuesArgs(
            query_string=False,
            cookies=aws.cloudfront.DistributionDefaultCacheBehaviorForwardedValuesCookiesArgs(
                forward="none",
            ),
        ),
        min_ttl=0,
        default_ttl=3600,
        max_ttl=86400,
    ),
    price_class="PriceClass_100",
    restrictions=aws.cloudfront.DistributionRestrictionsArgs(
        geo_restriction=aws.cloudfront.DistributionRestrictionsGeoRestrictionArgs(
            restriction_type="none",
        ),
    ),
    viewer_certificate=aws.cloudfront.DistributionViewerCertificateArgs(
        cloudfront_default_certificate=True,
    ))

# Export the URLs
pulumi.export("website_url", site_bucket.website_endpoint)
pulumi.export("cdn_url", cdn.domain_name)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run &lt;code&gt;pulumi preview&lt;/code&gt; to simulate the deployment and verify the expected changes.&lt;/p&gt;

&lt;p&gt;If everything looks correct, proceed with deploying your website by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pulumi up 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F01l6dv0r6eu3bg9koa45.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F01l6dv0r6eu3bg9koa45.png" alt="Image description" width="800" height="265"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While running &lt;code&gt;pulumi up&lt;/code&gt;, I encountered an error as seen above. Initially, I thought it was due to not granting S3BucketFullAccess policy, but the actual issue was that S3 Block public access was enabled. &lt;/p&gt;

&lt;p&gt;To ensure your website runs smoothly on S3 and cloudFront, make sure to disable Block public access settings for your S3 bucket if it's enabled.&lt;/p&gt;

&lt;p&gt;Rerun &lt;code&gt;pulumi up&lt;/code&gt;, then verify that your website is live by opening the provided &lt;a href="https://d3hiuxztivmq3z.cloudfront.net/index.html" rel="noopener noreferrer"&gt;CloudFront&lt;/a&gt; or &lt;a href="http://static-website-bucket-3167d8f.s3-website-us-east-1.amazonaws.com/" rel="noopener noreferrer"&gt;S3 bucket URL&lt;/a&gt; in your browser.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fchv20zmrsuv471ja4gqg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fchv20zmrsuv471ja4gqg.png" alt="Image description" width="800" height="398"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Cloud deployments with Pulumi
&lt;/h2&gt;

&lt;p&gt;Using code to specify and automate deployments, Pulumi simplifies infrastructure administration while deploying a static website.&lt;/p&gt;

&lt;p&gt;Scalable, secure, and affordable hosting solutions may be easily created by combining AWS services like S3 and CloudFront.&lt;/p&gt;

&lt;p&gt;Correctly creating IAM roles, modifying S3 public access settings, and properly configuring Pulumi are essential stages for a smooth deployment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Pulumi
&lt;/h2&gt;

&lt;p&gt;In this project, I used Pulumi to deploy a static website on AWS using Python. Pulumi simplified the Infrastructure as Code (IaC) process, allowing me to define and provision AWS resources efficiently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pulumi Copilot Usage:&lt;/strong&gt;&lt;br&gt;
I leveraged Pulumi Copilot to generate efficient infrastructure definitions, resolve policy issues, and automate configuration. Some key prompts included:&lt;/p&gt;

&lt;p&gt;· How do I create an S3 bucket for a static website?&lt;/p&gt;

&lt;p&gt;· What’s the correct way to attach an IAM policy for public read access to an S3 bucket in Pulumi?&lt;/p&gt;

&lt;p&gt;· How do I troubleshoot 'Access Denied' errors when hosting a static site on S3&lt;/p&gt;

&lt;p&gt;· Set up a CloudFront distribution with an S3 origin in Pulumi.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>pulumichallenge</category>
      <category>webdev</category>
      <category>cloud</category>
    </item>
  </channel>
</rss>
