<?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>DEV Community: Okesanya Odunayo </title>
    <description>The latest articles on DEV Community by Okesanya Odunayo  (@drintech).</description>
    <link>https://dev.to/drintech</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%2F1027219%2F39d2ef1c-27cd-4317-a0c9-76db6cff7ade.jpg</url>
      <title>DEV Community: Okesanya Odunayo </title>
      <link>https://dev.to/drintech</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/drintech"/>
    <language>en</language>
    <item>
      <title>Secure and Fast Static Website Deployment on AWS using Pulumi</title>
      <dc:creator>Okesanya Odunayo </dc:creator>
      <pubDate>Mon, 07 Apr 2025 03:30:49 +0000</pubDate>
      <link>https://dev.to/drintech/secure-and-fast-static-website-deployment-on-aws-using-pulumi-3bi5</link>
      <guid>https://dev.to/drintech/secure-and-fast-static-website-deployment-on-aws-using-pulumi-3bi5</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;In this project, I used Pulumi as an Infrastructure as Code (IaC) tool to create a secure and scalable static website infrastructure on AWS. The solution sets up an Amazon S3 bucket to host static site files and integrates it with Amazon CloudFront for fast, global content delivery. To ensure secure HTTPS access, I restricted permissions to only CloudFront, obtained an SSL certificate using AWS Certificate Manager (ACM), and set up a custom domain with Route 53.&lt;/p&gt;

&lt;p&gt;Everything was written in Python using pulumi's Python SDK, which makes the codebase easy to read and maintain. The result is a modern react static website setup that is ready for production, cost-effective, and secure, all fully automated with pulumi.&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%2Fzisrw0dp3dstekplh6hy.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%2Fzisrw0dp3dstekplh6hy.png" alt="A snippet of my fast static website"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;CloudFront Links&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://d8fuic0sibn8l.cloudfront.net/" rel="noopener noreferrer"&gt;https://d8fuic0sibn8l.cloudfront.net/&lt;/a&gt;&lt;br&gt;
&lt;a href="https://d8fuic0sibn8l.cloudfront.net/nonexistent-page" rel="noopener noreferrer"&gt;https://d8fuic0sibn8l.cloudfront.net/nonexistent-page&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Custom Domain Links&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://challenge.drintech.online" rel="noopener noreferrer"&gt;https://challenge.drintech.online&lt;/a&gt;&lt;br&gt;
&lt;a href="https://challenge.drintech.online/nonexistent-page" rel="noopener noreferrer"&gt;https://challenge.drintech.online/nonexistent-page&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Project Repo
&lt;/h2&gt;




&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/DrInTech22" rel="noopener noreferrer"&gt;
        DrInTech22
      &lt;/a&gt; / &lt;a href="https://github.com/DrInTech22/Pulumi_Static_Website" rel="noopener noreferrer"&gt;
        Pulumi_Static_Website
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Secure and Fast Static Website Deployment on AWS using Pulumi&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;This is a repository to set up a secure and fast static website deployment using pulumi.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Table of content&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/DrInTech22/Pulumi_Static_Website#overview" rel="noopener noreferrer"&gt;Overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/DrInTech22/Pulumi_Static_Website#objectives" rel="noopener noreferrer"&gt;Objectives&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/DrInTech22/Pulumi_Static_Website#technologies" rel="noopener noreferrer"&gt;Technologies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/DrInTech22/Pulumi_Static_Website#project-architecture" rel="noopener noreferrer"&gt;Project Architecture&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/DrInTech22/Pulumi_Static_Website#project-structure" rel="noopener noreferrer"&gt;Project Structure&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/DrInTech22/Pulumi_Static_Website#prerequisites" rel="noopener noreferrer"&gt;Prerequisites&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/DrInTech22/Pulumi_Static_Website#pulumi-installation" rel="noopener noreferrer"&gt;Pulumi Installation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/DrInTech22/Pulumi_Static_Website#setup-project-template" rel="noopener noreferrer"&gt;Setup Project Template&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/DrInTech22/Pulumi_Static_Website#deploy-the-static-website" rel="noopener noreferrer"&gt;Deploy the Static Website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/DrInTech22/Pulumi_Static_Website/secure-the-website-deployment" rel="noopener noreferrer"&gt;Secure the website deployment&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/DrInTech22/Pulumi_Static_Website#setup-a-custom-domain" rel="noopener noreferrer"&gt;Setup a Custom Doman&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/DrInTech22/Pulumi_Static_Website#best-practices" rel="noopener noreferrer"&gt;Best Practices&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Overview&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;This project uses Pulumi as an IAC tool to set up a secure and fast static website deployment. Pulumi is a unique IAC tool that lets you define your infrastructure using your favorite programming language. With Pulumi, we'll set up the infrastructure (S3 bucket, CloudFront, and Route 53) on AWS and securely deploy the static website files to this infrastructure using Python.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Technologies&lt;/h2&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Pulumi&lt;/strong&gt;: An Infrastructure as Code (IaC) tool that provisions and manages cloud resources using Python.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Python&lt;/strong&gt;: A programming language that provides modules to interact with Pulumi and AWS…&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/DrInTech22/Pulumi_Static_Website" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


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

&lt;p&gt;I started this project by exploring the Pulumi documentation since it was my first time using Pulumi. It turned out to be a great experience because the Pulumi documentation is developer-friendly and easy to follow. Being a fan of Python and quite familiar with it, I chose to use the Pulumi Python SDK to set up the static website infrastructure and deploy a static React website to it.&lt;/p&gt;

&lt;p&gt;My development journey was rewarding because I gained a better understanding of the technologies, but it was also filled with unexpected challenges. At one point, I even thought I had downloaded malware on my machine, lol. I began the journey using the helpful static website template provided by the Pulumi documentation. Let's go over the steps and the challenges I faced during development.&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary of Technologies Used
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Pulumi&lt;/strong&gt;: An Infrastructure as Code (IaC) tool that provisions and manages cloud resources using Python.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Python&lt;/strong&gt;: A programming language that provides modules to interact with Pulumi and AWS resources.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;S3 Bucket&lt;/strong&gt;: An object storage service that stores static website files and assets, serving as the origin for CloudFront.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;CloudFront&lt;/strong&gt;: A Content Delivery Network (CDN) that caches and securely delivers website content globally with low latency. These contents are cached at edge locations closer to your users.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Route 53&lt;/strong&gt;: A service that manages DNS records, enabling the use of a custom domain for the website.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Architecture
&lt;/h3&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%2F5jj9j7i1myodokv3hpmd.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%2F5jj9j7i1myodokv3hpmd.png" alt="architecture diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;p&gt;To get started, the following are required:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Python3, pip, and python3-venv installed&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A basic understanding of Python&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;AWS CLI installed and configured   &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 1: Pulumi Installation
&lt;/h3&gt;

&lt;p&gt;The command below installs pulumi on linux machine, refreshes the shell so the changes can reflect, and confirms the installation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://get.pulumi.com | sh
&lt;span class="nb"&gt;source&lt;/span&gt; ~/.bashrc
pulumi version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Setup Project Template
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Next, I created a new folder &lt;code&gt;website/&lt;/code&gt; and setup the static website template. The static website template bundles sample files and configurations to deploy static website with S3 and CloudFront.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir website &amp;amp;&amp;amp; cd website
pulumi new static-website-aws-python
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since I'm using the &lt;code&gt;pulumi new&lt;/code&gt; command for the first time, I needed to sign in to Pulumi Cloud to authenticate my CLI. I found this interesting because, after authentication, I could immediately start building the infrastructure. Pulumi automatically manages the state file remotely for me, unlike other IaC tools where I have to manually set up the backend file to manage the state file remotely before focusing on automation.&lt;br&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%2F0g0l0y6k3eiskfu5pcrr.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%2F0g0l0y6k3eiskfu5pcrr.png" alt="Pulumi console sign-in"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;After authentication, I configured the project and stack for the website deployment. This include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;setting the project name, description and stack name.&lt;/li&gt;
&lt;li&gt;selecting pip as the toolchain for installing dependencies.&lt;/li&gt;
&lt;li&gt;Setting &lt;code&gt;us-east-1&lt;/code&gt; as the region to deploy our aws resources such as S3 buckets.&lt;/li&gt;
&lt;li&gt;confirming the index, error document and website path.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After completing the configuration, pulumi proceeds to install the dependencies defined in the &lt;code&gt;requirements.txt&lt;/code&gt; but fails due to python env error. I did not have python3-venv installed. Luckily for me, python provided me with the right command to install the correct version in the error message.&lt;br&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%2Fm9nbvx52iuq5vmxyhvgn.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%2Fm9nbvx52iuq5vmxyhvgn.png" alt="python3-venv error"&gt;&lt;/a&gt;&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%2F36a30v7pyrfz5isu83xq.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%2F36a30v7pyrfz5isu83xq.png" alt="python dependencies installation"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pulumi manages the infrastructure setup as project and stack. Projects represent the entire infrastructure definition. They include metadata, runtime, and some configuration to manage and execute your infrastructure code. A project is defined in the &lt;code&gt;pulumi.yaml&lt;/code&gt; file. &lt;/p&gt;

&lt;p&gt;A stack is an instance of the project with its own configuration and state. Stacks include environment-specific configurations that allow us to deploy the infrastructure to different environments such as dev, staging, and prod. A stack is defined in the &lt;code&gt;pulumi.dev.yaml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;After setting up the project and stack, pulumi generated the following files for me.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;pulumi.yaml&lt;/code&gt;: This represents the project. It includes metadata, runtime and configs to manage the infrastructure code.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;website&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;A Python program to deploy a static website on AWS&lt;/span&gt;
&lt;span class="na"&gt;runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;python&lt;/span&gt;
  &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;toolchain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pip&lt;/span&gt;
    &lt;span class="na"&gt;virtualenv&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;venv&lt;/span&gt;
&lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pulumi:tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;pulumi:template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;static-website-aws-python&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;pulumi.dev.yaml&lt;/code&gt;: This file represents the stack for the development environment. It includes environment-specific configurations such as the AWS region, the path to static files, and HTML documents.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;aws:region&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;us-east-1&lt;/span&gt;
  &lt;span class="na"&gt;website:indexDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;index.html&lt;/span&gt;
  &lt;span class="na"&gt;website:errorDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;error.html&lt;/span&gt;
  &lt;span class="na"&gt;website:path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./www&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;__main__.py&lt;/code&gt;: This is the main file that contains the code defining the infrastructure. It specifies the resources to be deployed and their configurations.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;requirements.txt&lt;/code&gt;: The Python dependencies needed to deploy the infrastructure using Pulumi are listed here.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pulumi&amp;gt;=3.0.0,&amp;lt;4.0.0
pulumi-aws&amp;gt;=6.0.2,&amp;lt;7.0.0
pulumi-synced-folder&amp;gt;=0.0.0,&amp;lt;1.0.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;venv/&lt;/code&gt;: This folder manages the Python dependencies for this project in an isolated virtual environment.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;www/&lt;/code&gt;: This folder contains sample static web files, including &lt;code&gt;index.html&lt;/code&gt; and &lt;code&gt;error.html&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;.gitignore&lt;/code&gt;: Files listed here are not tracked by Git. This prevents us from pushing dependencies to our repository.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;A breakdown of the &lt;code&gt;__main__.py&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This file is where all the main operations happen. The breakdown highlights different parts of the code that work together to set up the static website infrastructure and how I address deprecated aspects of the code. After the detailed breakdown, I will adjust these components to deploy a more secure, personalized, and fast static website infrastructure.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Import the required modules
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pulumi&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pulumi_aws&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pulumi_synced_folder&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;synced_folder&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This imports the necessary modules to create AWS resources and sync files to an S3 bucket.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read Configuration Settings
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pulumi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;path&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./www&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;index_document&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;indexDocument&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;index.html&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;error_document&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;errorDocument&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error.html&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This reads environment-specific settings from the stack using &lt;code&gt;pulumi.Config&lt;/code&gt; and passes them as variables.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create an S3 Bucket &amp;amp; Enable Static Website Hosting
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;BucketV2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bucket&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;# website={
&lt;/span&gt;    &lt;span class="c1"&gt;#     "index_document": index_document,
&lt;/span&gt;    &lt;span class="c1"&gt;#     "error_document": error_document,
&lt;/span&gt;    &lt;span class="c1"&gt;# },
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;bucket_website&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;BucketWebsiteConfigurationV2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bucket&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;index_document&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;suffix&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;index_document&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;error_document&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;error_document&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This creates an S3 bucket and sets it up for static website hosting. It also specifies the index and error documents. The &lt;code&gt;bucketV2&lt;/code&gt; class no longer accepts the &lt;code&gt;website&lt;/code&gt; argument for configuring the bucket as a website, as it would cause errors. Instead, the &lt;code&gt;BucketWebsiteConfigurationV2&lt;/code&gt; class is used to configure the bucket for website hosting.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set Bucket Ownership Controls
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;ownership_controls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;BucketOwnershipControls&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ownership-controls&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;rule&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;object_ownership&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ObjectWriter&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This sets permissions that define who owns newly uploaded objects. "ObjectWriter" means the uploader retains ownership. I will modify this later on for enhanced security.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Allow Public Access to Objects
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;public_access_block&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;BucketPublicAccessBlock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;public-access-block&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;block_public_acls&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This allows public read access to objects by turning off the bucket public ACL blocking. In other words, it makes the bucket public so anyone can access it. However, this is not recommended for security best practices and it will be adjusted later because it lets anyone access our static files insecurely over HTTP.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Upload static files to S3 bucket
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;bucket_folder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;synced_folder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;S3BucketFolder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bucket-folder&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;acl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;public-read&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;bucket_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;pulumi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ResourceOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;depends_on&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ownership_controls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;public_access_block&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This uses &lt;code&gt;pulumi_synced_folder&lt;/code&gt; to upload the static files located at &lt;code&gt;path&lt;/code&gt; to the S3 bucket. It applies the public-read ACL settings to the uploaded objects, allowing public users to access the website files. It ensures that &lt;code&gt;ownership_controls&lt;/code&gt; and &lt;code&gt;public_access_block&lt;/code&gt; are set first. This order is important to avoid permission conflicts and ensure that the object ACL aligns with the bucket ownership settings.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a CloudFront CDN for Caching &amp;amp; Distribution
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;cdn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cloudfront&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Distribution&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cdn&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;enabled&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;origins&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;origin_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;domain_name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;bucket_website&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;website_endpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;custom_origin_config&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;origin_protocol_policy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http-only&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http_port&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https_port&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;443&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;origin_ssl_protocols&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;TLSv1.2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This creates a CloudFront distribution to serve the website using a CDN. The &lt;strong&gt;origin&lt;/strong&gt; (source) is set to the &lt;strong&gt;S3 bucket website endpoint&lt;/strong&gt; and uses "http-only" because S3 website hosting does not support HTTPS natively. This means CloudFront will internally fetch the static files from S3 over HTTP.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Configure CloudFront Cache Behavior
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;default_cache_behavior&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;target_origin_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;viewer_protocol_policy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;redirect-to-https&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;allowed_methods&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;HEAD&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OPTIONS&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cached_methods&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;HEAD&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OPTIONS&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;default_ttl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;max_ttl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;min_ttl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;forwarded_values&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;query_string&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cookies&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;forward&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;all&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This setup forces users to always access the website through CloudFront using HTTPS (redirect-to-https). It also includes request methods, cookies, caching rules, and TTL (cache time) settings. The default TTL caches your static files for 600 seconds (10 minutes), which is suitable for content that changes often. &lt;/p&gt;

&lt;p&gt;For example, if your static website is still in the early stages of development and you release several builds a day, this setting works well. Use 3600 seconds (1 hour) or 86400 seconds (24 hours) if your static website is more stable and rarely updated.&lt;/p&gt;

&lt;p&gt;If you're using a longer TTL and you update any or all objects in the bucket, CloudFront offers an option to invalidate the cache for specific or all objects. This ensures that your latest changes are visible to users.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Configure CloudFront Error Handling &amp;amp; Security
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;price_class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PriceClass_100&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;custom_error_responses&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error_code&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;response_code&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;response_page_path&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;error_document&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="n"&gt;restrictions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;geo_restriction&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;restriction_type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;none&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="n"&gt;viewer_certificate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cloudfront_default_certificate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;code&gt;PriceClass_100&lt;/code&gt; limits the CloudFront distribution to use edge locations in the U.S., Canada, and Europe to save on costs. If you need wider coverage for your users, you can look into other CloudFront price classes.&lt;/p&gt;

&lt;p&gt;404 errors from the S3 bucket are managed by serving the &lt;code&gt;errorDocument&lt;/code&gt; (error.html). You can use geo_restriction to control regional access or keep your content available worldwide. CloudFront uses its default SSL certificate, enabling public users to securely access your content over HTTPS.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Export Public URLs &amp;amp; Endpoint
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;pulumi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;export&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;originURL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pulumi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bucket_website&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;website_endpoint&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;pulumi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;export&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;originHostname&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;website_endpoint&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;pulumi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;export&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cdnURL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pulumi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cdn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;domain_name&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;pulumi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;export&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cdnHostname&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cdn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;domain_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This exports the necessary URL and hostname for S3 and CloudFront to access the newly deployed static website.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 3: Deploy the static website
&lt;/h3&gt;

&lt;p&gt;Now that we understand how the infrastructure code works, let's deploy the infrastructure and access the sample static website.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Run &lt;code&gt;pulumi up&lt;/code&gt;. This command validates and sets up the infrastructure. After a successful validation, it confirms if I would like to deploy the infrastructure.&lt;br&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%2Fo6uuidiypys4mru2iqvd.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%2Fo6uuidiypys4mru2iqvd.png" alt="pulumi cli"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;After I select 'yes', pulumi deploys the infrastructure. I can track the deployed stack on the Pulumi console. The console offers an easy-to-use dashboard where I can monitor deployments, view logs (updates), and see the resources deployed to a stack.&lt;br&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%2Fyaa0bqmkdunojrsryj87.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%2Fyaa0bqmkdunojrsryj87.png" alt="pulumi cli"&gt;&lt;/a&gt;&lt;br&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%2F8qsfukf6k69lwgtommed.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%2F8qsfukf6k69lwgtommed.png" alt="pulumi console"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The CloudFront distribution took the longest time to create. Once the deployment is complete, I will be able to access the sample static website over HTTP using the S3 &lt;code&gt;originURL&lt;/code&gt; and over HTTPS using the CloudFront &lt;code&gt;cdnURL&lt;/code&gt;.&lt;br&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%2Fm81lixb8gqnl7vhoy202.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%2Fm81lixb8gqnl7vhoy202.png" alt="sample static website"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The the error page is viewed by adding a nonexistent path to the URL, for example, &lt;code&gt;https://cloudfronturl.net/nonexistent-page&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;During deployment, Pulumi displayed a warning: "website_endpoint deprecated." The &lt;code&gt;website_endpoint&lt;/code&gt; is an output property that is no longer supported. Pulumi Co-Pilot, available on the console, was helpful in fixing the warning. &lt;/p&gt;

&lt;p&gt;To resolve it, I replaced the &lt;code&gt;website_endpoint&lt;/code&gt; property with the &lt;code&gt;bucket_regional_domain_name&lt;/code&gt; property, as suggested by Pulumi Co-Pilot, in the CDN configuration and export section at the bottom of the code. Then I used the &lt;code&gt;pulumi preview&lt;/code&gt; command to confirm that the warning was resolved.&lt;/p&gt;

&lt;p&gt;To deploy any custom website to S3, you need to update the path in the stack file (&lt;code&gt;pulumi.dev.yaml&lt;/code&gt;) to point to the location of the static website files.&lt;/p&gt;

&lt;p&gt;I wanted to make things more interesting by deploying a React application I built to the S3 bucket. I moved the &lt;code&gt;react_website/&lt;/code&gt; folder to the root of the repository. The production build, which contains the static files, is located in the &lt;code&gt;react_website/build/&lt;/code&gt; directory. The configuration in the dev stack is updated to deploy the React application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;aws:region&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;us-east-1&lt;/span&gt;
  &lt;span class="na"&gt;website:indexDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;index.html&lt;/span&gt;
  &lt;span class="na"&gt;website:errorDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;error.html&lt;/span&gt;
  &lt;span class="na"&gt;website:path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./react_website/build&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;pulumi up&lt;/code&gt; command deploys the React application. The newly deployed static website can be accessed using any of the provided URL links. I tested the error page by add a non-existent path to the URL and it works fine. The next phase is were things gets tricky and almost messy.&lt;/li&gt;
&lt;/ul&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%2Fallgf3qn58o0938i8bvq.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%2Fallgf3qn58o0938i8bvq.png" alt="static react website"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Secure the website deployment
&lt;/h3&gt;

&lt;p&gt;This step was the most challenging, requiring the most troubleshooting. I frequently switched between Pulumi documentation and AWS console to resolve unexpected issues.&lt;/p&gt;

&lt;p&gt;Currently, the website can be accessed via HTTP using the bucket endpoint and via HTTPS using the cdnURL. Disabling &lt;code&gt;BucketPublicAccessBlock&lt;/code&gt; makes the bucket and its objects publicly accessible, which poses a security risk. I set up a secure deployment to ensure users can only access the static website securely through the CloudFront URL. &lt;/p&gt;

&lt;p&gt;To enhance security, I restricted the bucket permissions so that only CloudFront can fetch objects from the bucket. I modified the &lt;code&gt;public_access_block&lt;/code&gt; settings to block all public access to the S3 bucket. This ensures that no one can access the S3 bucket or read the objects unless explicitly permitted. In other words, the bucket is now private.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;public_access_block&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;BucketPublicAccessBlock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;public-access-block&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;block_public_acls&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;block_public_policy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ignore_public_acls&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;restrict_public_buckets&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Next, I set &lt;code&gt;object_ownership&lt;/code&gt; to &lt;code&gt;BucketOwnerEnforced&lt;/code&gt;. This setting disables ACLs and ensures the bucket owner is the owner of all objects. With this configuration, access to the objects can only be explicitly granted using bucket policies.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;ownership_controls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;BucketOwnershipControls&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ownership-controls&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;rule&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;object_ownership&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BucketOwnerEnforced&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;I created CloudFront Origin Access Identity (OAI). An OAI is a virtual identity that can be associated with a CloudFront distribution to secure access to private S3 bucket.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;oai&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cloudfront&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;OriginAccessIdentity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;oai&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Then I attached the OAI to the CloudFront distribution by switching from &lt;code&gt;custom_origin_config&lt;/code&gt; to &lt;code&gt;s3_origin_config&lt;/code&gt;. The &lt;code&gt;s3_origin_config&lt;/code&gt; sets up CloudFront to access the S3 bucket through the OAI.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;cdn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cloudfront&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Distribution&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="bp"&gt;...&lt;/span&gt;
    &lt;span class="n"&gt;origins&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;origin_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;domain_name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bucket_regional_domain_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;s3_origin_config&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;origin_access_identity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;oai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cloudfront_access_identity_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;I Created an S3 bucket policy that allows only the CloudFront OAI to access the S3 bucket. The policy grants limited permission, allowing CloudFront to only fetch objects from the bucket. The code below creates the policy and attaches it to the bucket.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;bucket_policy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;BucketPolicy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bucketPolicy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;policy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;pulumi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;oai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;iam_arn&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;{{
        &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Version&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2012-10-17&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,
        &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Statement&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: [
            {{
                &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Effect&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Allow&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,
                &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Principal&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: {{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AWS&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;}},
                &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Action&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;s3:GetObject&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,
                &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Resource&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/*&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;
            }}
        ]
    }}&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;When I ran &lt;code&gt;pulumi up&lt;/code&gt; to test the new security changes, I encountered an '&lt;strong&gt;AccessControlListNotSupported: The bucket does not allow ACLs&lt;/strong&gt;' error for all the objects. This error occurred because the &lt;code&gt;synced_folder&lt;/code&gt; module attaches ACLs to objects and uploads them to the S3 bucket. However, the &lt;code&gt;BucketOwnerEnforced&lt;/code&gt; setting disables ACLs, which conflicts with &lt;code&gt;synced_folder&lt;/code&gt;. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Although the Pulumi documentation specified that the &lt;code&gt;synced_folder&lt;/code&gt; ACL property can be set to &lt;code&gt;None&lt;/code&gt;, I still encountered errors asking for the value when I executed the code. I manually wrote the code to upload objects to the S3 bucket using the &lt;code&gt;bucketObject&lt;/code&gt; class from the S3 submodule and a nested &lt;code&gt;for&lt;/code&gt; loop to iterate through all the files. Pulumi Co-Pilot was helpful for this.&lt;/p&gt;

&lt;p&gt;I then replaced the &lt;code&gt;synced_folder&lt;/code&gt; code with the new one generated.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;walk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;file_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;relpath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;BucketObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;pulumi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;FileAsset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;pulumi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ResourceOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;depends_on&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ownership_controls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;public_access_block&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the code above, I used the &lt;code&gt;os&lt;/code&gt; module to get the path of the static files on my machine. I imported the module at the beginning of the code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, when I ran &lt;code&gt;pulumi up&lt;/code&gt; and tried to access the website, I got an access denied error. Checking my S3 bucket, it looked like a disaster had struck—nothing was there. So, I ran &lt;code&gt;pulumi destroy &amp;amp;&amp;amp; pulumi up&lt;/code&gt; to rebuild the infrastructure from scratch. While this brought the objects back, the access denied error was still there.&lt;/p&gt;

&lt;p&gt;I reviewed all the configurations in the code and double-checked the settings for CloudFront and the S3 bucket in the console, but everything seemed correct. While going through the CloudFront distribution dashboard for the third time, I noticed that the &lt;code&gt;default root object&lt;/code&gt; option was not set. It didn't seem like a problem because it was optional, and the setup worked fine without it until I set the OAI. When you set OAI, some optional settings become mandatory.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;default root object&lt;/code&gt; tells CloudFront which object to return when users access the CloudFront URL, and in this case, it's &lt;code&gt;index.html&lt;/code&gt;. I asked Pulumi Copilot for the right argument to set it up in the CDN configuration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;cdn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cloudfront&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Distribution&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cdn&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;enabled&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;default_root_object&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;index.html&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I deployed the new changes, expecting everything to work smoothly. But when I tried accessing the website using the CloudFront URL, a weird file named "download" with no extension got downloaded to my computer. I deleted it right away. For a moment, I worried that I had downloaded malware and that my secure website was under attack, after just setting up enhanced security for it.&lt;/p&gt;

&lt;p&gt;I realized I needed to see what was happening in my infrastructure to answer questions like: Did my request reach the CloudFront distribution? Was it successful? Did it return an error, and if so, what was the error?&lt;/p&gt;

&lt;p&gt;I switched to the CloudFront dashboard, navigated to the logging tab, and set up a logging destination for CloudFront to send logs to a CloudWatch log group I had created for a Lambda function in my previous 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%2Fs6vmi00mbjao0u3wi867.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%2Fs6vmi00mbjao0u3wi867.png" alt="AWS CloudFront console"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then I tried accessing the link again so the request and response would be logged, allowing me to gather information to troubleshoot this unexpected behavior. I accessed the log from CloudWatch, but I couldn't understand it right away. I pasted it into Pulumi Copilot for analysis.&lt;/p&gt;

&lt;p&gt;With the response from Pulumi Copilot, I realized that the code I used to upload objects didn't set the correct file type for them. Instead, it set the file type for all objects to &lt;code&gt;application/octet-stream&lt;/code&gt; instead of &lt;code&gt;text/html&lt;/code&gt;, which is needed to display the index.html. &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%2Fp2opok426jkhu29sbazi.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%2Fp2opok426jkhu29sbazi.png" alt="pulumi co-pilot console"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This explains why a strange file was downloaded, which I suspected was my index.html, and I could confidently open it to confirm.&lt;/p&gt;

&lt;p&gt;I asked Pulumi Copilot again for a solution to set the file type in the nested &lt;code&gt;for&lt;/code&gt; loop code that uploads the objects.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;mimetypes&lt;/span&gt;
&lt;span class="bp"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;walk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;file_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;relpath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;content_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mimetypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;guess_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Uploading: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; as &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;BucketObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;pulumi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;FileAsset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;content_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;content_type&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;application/octet-stream&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;pulumi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ResourceOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;depends_on&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ownership_controls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;public_access_block&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The nested loop now uses the &lt;code&gt;mimetypes&lt;/code&gt; module to set the correct file type for each uploaded object. I ran &lt;code&gt;pulumi up&lt;/code&gt;, and it finally succeeded, or so I thought. My React static website is now securely accessible through the CloudFront URL link. When I tried the origin URL, it returned an access denied error, which was expected. However, when I tried to access the error page using the CloudFront URL, I also received an access denied error, which was unexpected.&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%2F9bxtbdvahr61kctomnv6.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%2F9bxtbdvahr61kctomnv6.png" alt="Access denied page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, I turned to the trusty old troubleshooting library (Stack Overflow). I discovered that because CloudFront and the S3 bucket are secured with OAI, the S3 bucket sends a 403 error code to CloudFront for pages that don't exist, instead of a 404. I tweaked the CloudFront settings to show the error page and send a 404 error response to users when it receives a 403 error code from the S3 bucket.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="bp"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;custom_error_responses&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error_code&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;response_code&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;response_page_path&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;error_document&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I deployed the latest changes, and now I can access both the main page and the error page of the static website. The next step is to personalize it with a custom domain.&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%2Foxb87crhkx5jz2aufprk.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%2Foxb87crhkx5jz2aufprk.png" alt="static website error page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup a Custom Domain
&lt;/h3&gt;

&lt;p&gt;Sharing the CloudFront URL with my friends to access my secure, fast, and beautiful static website felt boring. I wanted a custom domain that reflects a bit about me and what the website is about. I followed the steps in the Pulumi documentation to set up a custom domain for the static website.&lt;/p&gt;

&lt;p&gt;The steps include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Adding the domain and subdomain configuration to the stack.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Importing the new configurations into my code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Creating and validating a new SSL/TLS certificate with ACM. I used a DNS validation method, which involves creating a DNS record in the hosted zone to confirm domain ownership.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Adjusting the CloudFront code to handle requests for the custom domain by using the &lt;code&gt;aliases&lt;/code&gt; argument.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Creating a Route 53 alias A record that maps our subdomain to the CloudFront domain.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You might be wondering why we use an alias A record instead of a CNAME record, which is commonly used for mapping one domain to another. The Route 53 alias A record is recommended for mapping domains to AWS services like CloudFront. With an alias A record, DNS queries are resolved internally, making them fast and more efficient.&lt;/p&gt;

&lt;p&gt;After setting up the custom domain, I was able to access my static website using the personalized domain. When I create a new production build for the React application, all I need to do is run &lt;code&gt;pulumi up&lt;/code&gt;. Pulumi handles the rest by automating the deployment to the secure S3 bucket.&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%2Fmhqidqdd0qwa65zz0u3q.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%2Fmhqidqdd0qwa65zz0u3q.png" alt="static website with custom domain"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;This is my first time using Pulumi as an IAC tool, and I find it amazing. Pulumi lets me use Python, which is my favorite programming language. This made Pulumi feel familiar to me, like it was a part of my workflow. Like other IAC tools, Pulumi saves me the hassle of clicking through the AWS console to set up infrastructure and allows me to develop repeatable infrastructure. But unlike other IAC tools, Pulumi lets me automate right away without first manually configuring the remote state file.&lt;/p&gt;

&lt;p&gt;The Pulumi console and Pulumi Copilot (my personal assistant for this project) were very helpful in troubleshooting, referencing, and successfully completing this project. The Pulumi console provided updates (logs) on the changes I made, making it easy to track changes and reference past execution outputs. Pulumi Copilot, my intelligent personal assistant, helped me fix deprecated arguments, generate bucket policies, analyze logs, and provided possible solutions. &lt;/p&gt;

&lt;p&gt;In fact, when I wanted to secure my static website infrastructure, I copied the entire code provided by the template and gave prompts to Pulumi Copilot to set up a secure website infrastructure. This simplified the process, allowing me to focus on adjusting and reviewing the code using the pulumi documentation to make a few tweaks. It's incredible to have an AI assistant that understands the infrastructure tool I'm using. It makes infrastructure provisioning easy.&lt;/p&gt;

&lt;p&gt;Below are some of my key prompts:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prompt 1&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;can&lt;/span&gt; &lt;span class="n"&gt;you&lt;/span&gt; &lt;span class="n"&gt;adjust&lt;/span&gt; &lt;span class="n"&gt;this&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;each&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt; &lt;span class="n"&gt;uploaded&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt; 

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;walk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;file_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;relpath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;BucketObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;pulumi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;FileAsset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Prompt 2&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="n"&gt;option&lt;/span&gt; &lt;span class="n"&gt;acl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;working&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="n"&gt;It&lt;/span&gt; &lt;span class="n"&gt;still&lt;/span&gt; &lt;span class="n"&gt;ask&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;acl&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="n"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="n"&gt;Is&lt;/span&gt; &lt;span class="n"&gt;there&lt;/span&gt; &lt;span class="nb"&gt;any&lt;/span&gt; &lt;span class="n"&gt;good&lt;/span&gt; &lt;span class="n"&gt;alternative&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;S3BucketFolder&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt; 

&lt;span class="n"&gt;bucket_folder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;synced_folder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;S3BucketFolder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bucket-folder&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;acl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;bucket_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;pulumi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ResourceOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;depends_on&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ownership_controls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;public_access_block&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Prompt 3&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;this&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;cloudfront&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="n"&gt;I&lt;/span&gt; &lt;span class="n"&gt;got&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;cloudwatch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt; &lt;span class="n"&gt;reason&lt;/span&gt; &lt;span class="n"&gt;why&lt;/span&gt; &lt;span class="n"&gt;I&lt;/span&gt; &lt;span class="n"&gt;can&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;t access my static files via cloudfront URL after using OAI?

{
    &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;date&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2025-04-02&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,
    &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;time&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;18:01:10&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,
    &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;x-edge-location&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;LOS50-P2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,
    &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sc-bytes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;594&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,
    &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;c-ip&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;102.89.44.119&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,
    &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cs-method&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,
    &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cs(Host)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;d39ymmysanotpr.cloudfront.net&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,
    &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cs-uri-stem&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,
    &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sc-status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;200&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,
    &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cs(Referer)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,
    &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cs(User-Agent)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Mozilla/5.0%20(Windows%20NT%2010.0;%20Win64;%20x64)%20AppleWebKit/537.36%20(KHTML,%20like%20Gecko)%20Chrome/135.0.0.0%20Safari/537.36&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,
    &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cs-uri-query&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,
    &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cs(Cookie)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,
    &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;x-edge-result-type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Miss&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,
    &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;x-edge-request-id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-lB3Ff7SJ8HEdpSZeS8d8AwV8kjflOVlQdgaI_-yqDYQ-TenMjY2nA==&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,
    &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;x-host-header&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;d39ymmysanotpr.cloudfront.net&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,
    &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cs-protocol&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,
    &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cs-bytes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;486&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,
    &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;time-taken&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.480&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,
    &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;x-forwarded-for&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,
    &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ssl-protocol&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;TLSv1.3&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,
    &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ssl-cipher&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;TLS_AES_128_GCM_SHA256&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,
    &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;x-edge-response-result-type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Miss&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,
    &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cs-protocol-version&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;HTTP/2.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,
    &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fle-status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,
    &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fle-encrypted-fields&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,
    &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;c-port&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;17055&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,
    &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;time-to-first-byte&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.480&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,
    &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;x-edge-detailed-result-type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Miss&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,
    &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sc-content-type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;application/octet-stream&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,
    &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sc-content-len&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;238&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,
    &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sc-range-start&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,
    &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sc-range-end&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Kindly note this submission was published April 06, 8:42PM PDT.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Reference Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/templates/static-website/aws/" rel="noopener noreferrer"&gt;Pulumi docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://depascalematteo.medium.com/optimizing-content-delivery-the-complete-guide-through-s3-caching-and-cloudfront-df64d1b7536a" rel="noopener noreferrer"&gt;Optimizing content delivery through S3 caching&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks for reading. Your suggestions and feedbacks are highly welcome. Kindly drop them in the comment section.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>pulumichallenge</category>
      <category>webdev</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Perfect Development Workflow with Mirrord: Every Developer’s Hero</title>
      <dc:creator>Okesanya Odunayo </dc:creator>
      <pubDate>Tue, 20 Aug 2024 20:20:08 +0000</pubDate>
      <link>https://dev.to/drintech/perfect-development-workflow-with-mirrord-every-developers-hero-396m</link>
      <guid>https://dev.to/drintech/perfect-development-workflow-with-mirrord-every-developers-hero-396m</guid>
      <description>&lt;p&gt;&lt;strong&gt;Introducing mirrord, a tool designed to streamline developers' workflows. With mirrord, you can test your code in the cloud in seconds!&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Introduction&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Remember the long, tedious process of building an application, probably dockerizing it, then deploying to staging for testing, only to encounter bugs that are only realized in the cloud environment? These are the incidents that inevitably slow down the deployment process.&lt;/p&gt;

&lt;p&gt;Well, what if you could catch those bugs before your code reaches the final testing stage and avoid occasionally breaking the staging server with failed tests?&lt;/p&gt;

&lt;p&gt;The hero, mirrord, is here. Less troubles! mirrord enhances your development workflow by bringing the cloud environment closer to your local setup. This lets you test your code in the context of a cloud environment throughout the development process.&lt;/p&gt;

&lt;p&gt;Yes, you heard it right. Mirrord is bringing the cloud environment to your local setup!&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The Pain Points of Development Environments&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;When developing a Kubernetes application, the primary challenges usually stem from setting up a local Kubernetes environment and &lt;a href="https://mirrord.dev/docs/overview/introduction/#why" rel="noopener noreferrer"&gt;passing your code through a lengthy CI/CD pipeline,&lt;/a&gt; followed by waiting for the code to be deployed to the cloud before testing can commence.&lt;/p&gt;

&lt;p&gt;To circumvent this frustrating delay and lengthy process, developers are often tempted to save cloud testing for the final stages of development. However, this approach piles up bugs and issues that need to be addressed later in the cloud. This scenario can be best avoided by testing in the cloud from the very beginning of development.&lt;/p&gt;

&lt;p&gt;There are still more challenges developers face in their development workflow. Let's discuss these issues in more detail.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hurdles of dockerization and deployment&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Developers often need to dockerize their applications and go through a lengthy CI process before they can start testing in the cloud. These steps can be time-consuming and sometimes complex, diverting focus from actual development tasks. This shift in focus consumes valuable time that could be better spent on developing the application.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Discrepancy between the local and cloud environment&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When you deploy your app to the cloud, it faces a different environment. It must handle real-time traffic, interact with external services, and work with new configurations. This can lead to unexpected bugs that may break the application and make the server unusable for a period of time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Challenges with shared staging servers&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In many development teams, a shared staging server is used for testing code before it goes live. When one developer's code causes the staging server to crash, it creates a bottleneck.&lt;/p&gt;

&lt;p&gt;Other developers are forced to wait until the server is back up and running before they can continue their work. This not only slows down individual progress but also disrupts the entire team's workflow, leading to frustration and decreased productivity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Debugging&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Debugging in a cloud environment can be challenging. When you encounter a bug on the shared server, you have two options.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Bring a copy of the cloud environment down to your local environment to replicate the bug.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Start debugging on the remote server.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's consider how these options will serve us well in an environment of about 300 microservices.&lt;/p&gt;

&lt;p&gt;The first option requires you to replicate over 300 services and manage their individual dependencies and configurations. Beside the technical difficulty, such a massive workload can fry your laptop. I don't think you would love to tick this option.&lt;/p&gt;

&lt;p&gt;The second option lets you start debugging right away, but there's a downside. Your work can be disrupted when another developer's code breaks the server. Additionally, spending too much time on the shared server can slow down other developers who also need to test their code, causing delays for the entire team.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;How Mirrord Saves the Day!&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Mirrord provides solutions to the seemingly endless troubles developers face in their workflow. You can get your code running in your cloud environment with just a single button click.&lt;/p&gt;

&lt;p&gt;It provides your code with everything it needs to operate like it's on the cloud—access to other applications, databases, configurations, and most importantly, real-time traffic.&lt;/p&gt;

&lt;p&gt;Mirrord operates in the memory of your local process in your friendly environment and as an agent pod in the cloud. The agent pod duplicates incoming requests to the target pod (the cloud version of your code) and sends a copy to your local process. Your code interacts with this traffic, and you can take actions against the results —tweak, modify, and test your code again.&lt;/p&gt;

&lt;p&gt;From your local IDE, your code operates like it's living in the staging server. However, the server still handles requests and sends out responses seamlessly. It doesn't even know you were there!&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Core advantages of mirrord&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Mirrord makes developers' lives easier. Let’s take a look at what you will enjoy from using mirrord.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Simplicity&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;mirrord makes things easy for developers by offering a straightforward setup process and user-friendly commands. With just a few steps, you can configure mirrord to integrate seamlessly into your development environment. Get started quickly with minimal effort.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Improved Productivity&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;mirrord abstracts away the need for frequent git pushes and the delays associated with CI/CD pipelines, reducing both the time and stress involved in the development process. Instead of waiting for code to be pushed, built, and deployed, you can focus directly on writing and testing your code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Faster Deployment&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With the cloud environment integrated into the developer’s workflow, and the complete elimination of containerization and zero stress of CI process, you now experience faster development workflow which equals faster time to market of applications.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Efficient debugging&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No more reading logs to tackle remote bugs. With mirrord, you can replicate remote bugs directly in your local environment, allowing you to debug them more effectively. Debugging now becomes a seamless part of your development process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Shift left on cloud testing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You no longer have to save cloud testing for the final step. Test your code in the cloud continuously as you develop for faster iterations and improvements. This allows you to catch and fix issues earlier in the development cycle, leading to higher quality code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Saves the production-like environment&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;mirrord helps save the staging server. By allowing you to test your code on the staging server without actually deploying it, mirrord removes the experience of frequent disruptions and downtimes in the shared staging server.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Collaboration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;mirrord makes team collaboration much easier. With the new feature, &lt;a href="https://mirrord.dev/docs/overview/teams/" rel="noopener noreferrer"&gt;&lt;strong&gt;mirrord for teams&lt;/strong&gt;&lt;/a&gt;, you can now mirror traffic from the same target pod. This means multiple team members can test their code against similar traffic at the same time, producing more consistent and reliable results&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Unique features of mirrord&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Easy to use plugins&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Mirrord offers extra flexibility with its powerful plugins, which can be easily integrated into your local Integrated Development Environment (IDE). These plugins are designed to enhance your development experience by offering a range of functionalities that simplify your workflow. Begin testing with just a single click.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Regulate traffic flow&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Mirrord lets you control how traffic is directed to your local service. You can choose to mirror traffic to the target pod and send a copy to your local process, or you can configure your local process to handle requests exclusively by stealing the traffic if you are confident in your code.&lt;/p&gt;

&lt;p&gt;You can also streamline your testing by exposing your code to specific traffic using filters. These flexible options enable you to test your code under different configurations, ensuring that it performs well in the context of the cloud.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Granular configuration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;mirrord offers flexible configurations for how your local process responds to traffic. For example, if both the target pod and your local process write to the database, it can result in duplicate data. You can configure your local process to write to the remote database or let the target pod handle it while your local process writes to a local instance of your database.&lt;/p&gt;

&lt;p&gt;This level of control helps you tailor the development environment to your specific needs and manage how your local process accesses certain resources in your cluster.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resource cleanup&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Mirrord is quite efficient and reliable. It does a few things in the cluster to bring the remote functionalities to your cluster environment but unlike other development tools, it doesn't leave your cluster messy with stale pods or resources.&lt;/p&gt;

&lt;p&gt;When you terminate a mirrord session, it properly cleans up its resources in your cluster, ensuring that no resources are left behind that could disrupt your environment or accidentally incur costs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Customer centric&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Facing a problem connecting your code to the cloud? No worries. Mirrord team is available to support you and assist in resolving any problems you may face. They provide assistance through various channels, including email, public Discord, support tickets, private Discord, Slack, or Teams.&lt;/p&gt;

&lt;p&gt;You can also &lt;a href="https://calendly.com/eyal-metalbear/mirrord-session" rel="noopener noreferrer"&gt;schedule a live demo&lt;/a&gt;. Mirrord team will guide you in fully utilizing the functionalities of mirrord within your organization and assist in resolving any issues you may encounter.&lt;/p&gt;

&lt;p&gt;Additionally, Mirrord values user suggestions. If there is a feature you would like to see implemented, just let them know. They are absolutely committed to enhancing your development experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;In summary, mirrord saves developers troubles in their development workflow by making it easier to test their code in a cloud environment from the comfort of their local IDE. This significantly saves time, improves developer productivity, and reduces the chances of bugs sneaking into the production-like environment.&lt;/p&gt;

&lt;p&gt;On the business side, this tremendously improves the time-to-market of products. But it doesn't end there!&lt;/p&gt;

&lt;p&gt;Don't forget, mirrord is every developer's hero, and collaboration is key to every business's success. You wouldn't want to limit this tremendous capability of mirrord to a single person on your team. Empower your team with a better and faster workflow and promote collaboration with the new feature - &lt;a href="https://mirrord.dev/docs/overview/teams/" rel="noopener noreferrer"&gt;&lt;strong&gt;mirrord for teams&lt;/strong&gt;&lt;/a&gt;, which recently launched on the 17th, June. Wondering what is so amazing about this new feature?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Mirrord for teams&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;extends the capabilities of mirrord by providing additional functionalities such as concurrent use, enhanced security and deployment-level mirroring. Register&lt;/em&gt; &lt;a href="https://identity.metalbear.co/oauth/account/login" rel="noopener noreferrer"&gt;&lt;em&gt;here&lt;/em&gt;&lt;/a&gt; &lt;em&gt;to get started.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You want to hear more hero stories of mirrord in action? Read the &lt;a href="https://mirrord.dev/stories/" rel="noopener noreferrer"&gt;user stories&lt;/a&gt; to see how Mirrord helped them.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>The Problem Docker Solves: Beginner's Introduction to Docker</title>
      <dc:creator>Okesanya Odunayo </dc:creator>
      <pubDate>Thu, 16 May 2024 11:54:03 +0000</pubDate>
      <link>https://dev.to/drintech/the-problem-docker-solves-beginners-introduction-to-docker-2bfa</link>
      <guid>https://dev.to/drintech/the-problem-docker-solves-beginners-introduction-to-docker-2bfa</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;The recent talk of the town - a revolution to how applications are deployed. When developers build and successfully run an application, most times the application fails on deployment to different environments.&lt;/p&gt;

&lt;p&gt;Docker takes an application and everything it needs (dependencies and runtime), then puts them in an isolated environment. This makes the application independent of other processes and enables it to run smoothly across different environments (servers). Interesting, isn't it? Let’s dig deeper into that!&lt;/p&gt;

&lt;p&gt;This article explains docker, the problems it solves, basic concepts and benefits of using docker.&lt;/p&gt;

&lt;h2&gt;
  
  
  The PROBLEM
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Redundant configurations&lt;/li&gt;
&lt;li&gt;Conflicting dependencies (Dependency hell issue)&lt;/li&gt;
&lt;li&gt;Slow deployment&lt;/li&gt;
&lt;li&gt;Scaling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After an application is built, the next step is to deploy to production environments. Each production server is manually configured - installing application dependencies, libraries, which makes the deployment process slow and cumbersome.&lt;/p&gt;

&lt;p&gt;To make matters worse, some applications usually require similar dependencies but of different versions leaving us with the hunt of compatible versions of dependencies for these applications. In several scenarios, we’re left with no other option but to use a deprecated version that can support both applications. This issue is popularly known as the Dependency Matrix Hell.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F70u3jxlxeq4zzn7s47pp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F70u3jxlxeq4zzn7s47pp.png" alt="Fire on applications, with no hope of similar dependencies" width="652" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Also, applications that perfectly work on a developer’s machine might fail to run on a production environment. Some underlying processes and applications running on the production server makes the production environment incompatible and different, causing the application to fail. This makes the developer yell “It works on my machine!!”.&lt;/p&gt;

&lt;h2&gt;
  
  
  The SOLUTION - DOCKER
&lt;/h2&gt;

&lt;p&gt;Docker is an open-source containerization platform. The free availability of docker to a wider audience contributed to the vast adoption of this containerization technology which positively transformed the deployment game in the industry.&lt;/p&gt;

&lt;p&gt;Docker allows developers to package an application together with its dependencies, libraries and runtime into lightweight, isolated, self-contained units (containers).&lt;/p&gt;

&lt;p&gt;This isolated and self-dependent functionality prevents conflicts with underlying processes (other applications and system processes), and ensures the application runs the same way in any environment. Thereby eliminating the popular “it works on my machine” problem - healing to every developer's headache.&lt;/p&gt;

&lt;p&gt;Since the application is packaged with its configuration requirements and runtime, this further eliminates the need to manually configure each production server. All you need on the production server is docker installed!&lt;/p&gt;

&lt;h2&gt;
  
  
  Basic Docker Terminologies
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Image
&lt;/h3&gt;

&lt;p&gt;A docker image is a lightweight, independent and standalone package. It contains the application code, dependencies, libraries and runtime. With a docker image, you can easily deploy several containers.&lt;/p&gt;

&lt;p&gt;Most images are built upon existing images or parent images. For example, a front-end application image can be built on a httpd-server image which provides server functionality and configurations to the front-end application.&lt;/p&gt;

&lt;p&gt;Images are immutable and cannot be modified after creation. Images are built using a dockerfile. Dockerfile is a file with defined steps (instructions) to create a docker image.&lt;/p&gt;

&lt;h3&gt;
  
  
  Container
&lt;/h3&gt;

&lt;p&gt;A container is a lightweight, self-sustained unit that runs the application code and the dependencies. It is the running instance of the application created from the docker image.&lt;/p&gt;

&lt;p&gt;Containers pick up only the necessary components needed for execution of the application from the docker image. This makes them lightweight, portable, and easy to run across different environments (servers).&lt;/p&gt;

&lt;h3&gt;
  
  
  Docker registry
&lt;/h3&gt;

&lt;p&gt;A docker registry is a remote storage that enables distribution of docker images. It serves as a central storage for uploading and downloading images. Docker Hub is the official public registry for Docker images.&lt;/p&gt;

&lt;p&gt;Docker Hub provides two types of repositories (storage types):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Public repositories&lt;/strong&gt;: Images in this repo are generally available to the public. They are free and open-source images.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Private repositories&lt;/strong&gt;: Private repositories are usually created by an organization. Images found here are only accessible by members of the organization.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Containers Vs Virtual Machines
&lt;/h2&gt;

&lt;p&gt;Both containers and virtual machines are capable of running applications in an isolated environment, but it is important to understand their key differences.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fin5jtjrqafjs2g9g0ol5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fin5jtjrqafjs2g9g0ol5.png" alt="comparison of utilization of resources in containers to virtual machines" width="798" height="466"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Virtual machines are virtual computers within your physical computer that run a &lt;strong&gt;full guest operating system&lt;/strong&gt;. Each virtual machine shares the underlying hardware resources with the host operating system (your personal OS) and other virtual machines. This makes them heavy and compute-resource demanding.&lt;/p&gt;

&lt;p&gt;Containers are lightweight, isolated environments that include only the application and required dependencies. Each container &lt;strong&gt;shares the operating system kernel&lt;/strong&gt; with the host and other containers. This makes them light-weight and easy to deploy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benefits of Docker
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Consistency&lt;/strong&gt;: With docker, applications run successfully and behave the same way across different environments from development to production.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Portability&lt;/strong&gt;: Containers are lightweight, this makes it easier to deploy applications to several environments and even store the image in docker registries.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Isolation&lt;/strong&gt;: Isolation of each container prevents conflicts between containers and host machine, eliminating the occurrence of dependency matrix hell - where different applications require similar dependencies but of different versions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Collaboration&lt;/strong&gt;: since the application runs successfully across all platforms, this will foster collaboration between teams, such as the developers and operations team.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability&lt;/strong&gt;: Docker empowers developers to create or destroy several instances of the application in response to user demands.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;With the elimination of ‘it works on my machine problem’ and ‘dependency hell issue’, organizations now experience consistent deployment and efficient scalability, thanks to docker. As Docker clears obstacles in your deployment journey, your applications are only a few commands away from a successful deployment.&lt;/p&gt;

&lt;p&gt;I have also written a short story to further help visualize the problem-solving feature of Docker on X (formerly twitter). You can check it out &lt;a href="https://x.com/DrInTech22/status/1702760614970499338"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>containers</category>
      <category>docker</category>
      <category>cicd</category>
      <category>devops</category>
    </item>
    <item>
      <title>Evolution of DevOps! How it started</title>
      <dc:creator>Okesanya Odunayo </dc:creator>
      <pubDate>Sun, 05 May 2024 11:20:19 +0000</pubDate>
      <link>https://dev.to/drintech/evolution-of-devops-how-it-started-1ipd</link>
      <guid>https://dev.to/drintech/evolution-of-devops-how-it-started-1ipd</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;To develop an application, there are set of steps that mints the project requirements into a live application. The need for an organized approach towards software development led to the adoption of two major software development practices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Waterfall model (traditional)&lt;/li&gt;
&lt;li&gt;Agile methodology (classical)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These software development approaches are also known as SDLC (Software Development Life Cycle).&lt;/p&gt;

&lt;p&gt;In this article, we'll look at traditional software development approaches, the issues they imposed, and how we transitioned from these traditional practices to DevOps practices(modern).&lt;/p&gt;

&lt;h2&gt;
  
  
  Waterfall Model
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9kohh1mqtv85sqnd16ww.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9kohh1mqtv85sqnd16ww.jpeg" alt="Waterfall model step by step image" width="800" height="533"&gt;&lt;/a&gt;&lt;br&gt;
Waterfall Model is also referred to as linear-sequential life cycle. It is a step-by-step approach towards software development where each phase must be completed before moving on to the next one.&lt;/p&gt;

&lt;p&gt;It is the earliest SDLC practice. Each current phase is independent of the next phase leading to minimal collaboration between the separate teams and any required changes to final product will require restarting the SDLC.&lt;/p&gt;

&lt;p&gt;A case study to visualize this will be an e-commerce web app built for a client.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzn29hrj7ueldl03jhjp2.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzn29hrj7ueldl03jhjp2.jpeg" alt="client explaining an e-commerce web app" width="800" height="800"&gt;&lt;/a&gt;&lt;br&gt;
Following the waterfall model, the steps to follow would be:&lt;/p&gt;

&lt;p&gt;Requirements -  Design - Development - Testing - Deployment - Maintenance&lt;/p&gt;

&lt;p&gt;Requirements are gathered from the client, the web app is built, then taken to the testing phase to be tested, and finally deployed. After that, it is reviewed by the client. What if our client wants some adjustments like more functionality, animations or a different layout to attract customers to products on the e-commerce web app?&lt;/p&gt;

&lt;p&gt;That would be a lot of mess because we just followed a linear approach, where going back to the previous phase/step is not allowed. The only feasible solution is to restart the web app development right from step one, which is the requirement then integrating client required changes in the requirement.&lt;/p&gt;

&lt;p&gt;This shows how the waterfall model is an inefficient, time-consuming and rigid process that is not adaptable to changes into products/software.&lt;/p&gt;

&lt;h2&gt;
  
  
  Waterfall Model Design
&lt;/h2&gt;

&lt;p&gt;The waterfall approach divides software development into pre-defined phases. The outcome of one phase acts as a sort of input for the next phase.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5rsrrfbg07s8rtmfpn80.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5rsrrfbg07s8rtmfpn80.png" alt="waterfall model image" width="500" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Advantages of the Waterfall Model
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;The waterfall model gives a clear, defined set of steps to follow.&lt;/li&gt;
&lt;li&gt;It is good for smaller projects where requirements are well-defined with no need for future adjustment.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Disadvantages of the Waterfall Model
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Inflexibility: The linear structure of the waterfall model makes it difficult to incorporate a change once a phase is completed as it’s not easy to make changes along the way. Changes to the software would necessitate restarting the software development process.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Delay: Since each phase must be completed before going to the next one, there’s a period of idleness for team members in other phases.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We became very uncomfortable with this time-consuming linear approach, we wanted a system that is adaptable to any change our client desires, where we wouldn't have to start over to implement changes and that led us to the Agile Methodology.&lt;/p&gt;

&lt;h2&gt;
  
  
  Agile Methodology
&lt;/h2&gt;

&lt;p&gt;Agile methodology is an iterative approach toward the software development life cycle making it more flexible and adaptable to changes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdv8xv1nj91s29imrnpfy.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdv8xv1nj91s29imrnpfy.jpeg" alt="agile step by step image" width="800" height="800"&gt;&lt;/a&gt;&lt;br&gt;
Unlike the sequential process of waterfall, Agile breaks the entire SDLC into a loop of small series of steps (plan- build - test - deploy) known as sprint. The product is developed bit by bit, feature by feature. At the end of each sprint, review is gotten from client and required changes are integrated at the beginning of the next sprint.&lt;/p&gt;

&lt;p&gt;This way, Agile breaches the gap between clients and developers. Now let’s visualize Agile in action with our client's e-commerce app.&lt;/p&gt;

&lt;p&gt;Following the Agile method, the e-commerce app is built into small chunks, which go through the development cycle of packaging, testing and deployment known as sprint. At the end of each sprint, feedback is gotten from users which is leveraged to integrate changes into the app (chunks of code). Then the app goes through the sprint again.&lt;/p&gt;

&lt;p&gt;This loop process allows us to effectively integrate changes requested by our clients from every feedback into the development lifecycle while also shortening the time it takes for benefits to be delivered to users.&lt;/p&gt;

&lt;h2&gt;
  
  
  Some benefits of Agile Methodology
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Increased flexibility and adaptability.&lt;/li&gt;
&lt;li&gt;Faster delivery of software or products.&lt;/li&gt;
&lt;li&gt;Improved quality of software.&lt;/li&gt;
&lt;li&gt;Customer satisfaction.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Birth of DevOps
&lt;/h2&gt;

&lt;p&gt;Now we’re able to deliver quality software faster but there’s still one issue left unaddressed.&lt;/p&gt;

&lt;p&gt;Each team focuses solely on their tasks and sequentially moves the software (web app) to the next phase without any form of collaboration to ensure the software's success in the next phase. The next phase has little visibility of how the software was built in the previous phase. Most of the time, we say that there are silos (barriers) between these teams.&lt;/p&gt;

&lt;p&gt;If a bug occurs in production mode, the operations team in charge of deployment and monitoring will blame the testing team for not thoroughly testing the app, while the testing team will point fingers at the developer's team for producing poor-quality code.&lt;/p&gt;

&lt;p&gt;We can almost hear the dev teams saying, "We spent a sleepless night reviewing and testing our codes, the operations team is simply not doing their job." From this point, we can see that no team wants to take the blame; this is commonly known as the BLAME GAME.&lt;/p&gt;

&lt;p&gt;This blame causes delays in products and the teams forget they’re all responsible for ensuring the product is a success. So, we decided to bring Development and the Operations team (including testing team) together, where these teams can together oversee the process in each phase and collaborate with enhanced communication at every phase in the development cycle. And that was how we birth DevOps.&lt;/p&gt;

&lt;p&gt;DevOps bridged the gap between developers and operators. It is an improvement of Agile. It employs continuous integration, continuous delivery, continuous deployment and automated testing to release software more quickly with greater efficiency.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lifecycle of DevOps and Tools.
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz1oawyqxdq4ztfm61fha.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz1oawyqxdq4ztfm61fha.jpeg" alt="Image of lifecycle of devops" width="800" height="533"&gt;&lt;/a&gt;&lt;br&gt;
The DevOps lifecycle is a set of phases involved in the software development process that incorporates DevOps practices to ensure delivery of high-quality software applications.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Plan: This stage involves planning the software development process. This includes identifying the objectives, requirements and resources needed for the project. Project management tools like Jira, Asana are used in this phase.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Code: In this stage, the development team writes code for the application and makes use of version control tools like git,svn or bitbucket to keep track of changes and manage the codebase.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Build: This stage involves compiling the code from codebases and building the application with the help of build automation tools such as Jenkins, Travis CI, or CircleCI to automate the build process.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Test: In this stage, the application is tested to ensure it meets the requirements, functions correctly and is free of bugs. Automated testing tools such as Selenium, JMeter, and Appium are used to automate the testing process.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Release: In this stage, the application is prepared for release to the production environment. Release automation tools such as Ansible, Puppet, or Chef to automate the deployment process.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Deploy:  In this stage, the application is deployed to the production environment. This stage employs containerization and orchestration tools such as Docker, Kubernetes, or Mesos to manage and deploy the application in a scalable and efficient manner.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Operate: This stage focuses on application monitoring and maintenance. Monitoring and logging tools such as Nagios, ELK Stack, or Splunk are used to monitor the application's performance.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Monitor: In this stage, the team analyzes application performance and user feedback to identify areas for improvement. Tools used in this stage include monitoring tools like Prometheus, Grafana, and others.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Benefits of DevOps
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Enhanced collaboration between developers and operations team.&lt;/li&gt;
&lt;li&gt;Increased efficiency by automating processes and reducing manual tasks.&lt;/li&gt;
&lt;li&gt;Greater scalability and availability.&lt;/li&gt;
&lt;li&gt;Increased productivity of business.&lt;/li&gt;
&lt;li&gt;Lowers the failure rate of new releases.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this article, you’ve learned about traditional software development practices, their benefits, how we conceived DevOps from traditional practices, lifecycle and benefits of DevOps.&lt;/p&gt;

&lt;p&gt;If you enjoyed reading, you could give a heart (up to 10). You can also connect with me on &lt;a href="https://twitter.com/DrInTech22"&gt;X (formerly twitter)&lt;/a&gt;. Adios - DrIntech✍️&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
