<?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: Gaurish Naik</title>
    <description>The latest articles on DEV Community by Gaurish Naik (@gaurishxjfk).</description>
    <link>https://dev.to/gaurishxjfk</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%2F2193592%2Fd705033a-dbbf-4afe-908e-4e2bc16c16ab.jpg</url>
      <title>DEV Community: Gaurish Naik</title>
      <link>https://dev.to/gaurishxjfk</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gaurishxjfk"/>
    <language>en</language>
    <item>
      <title>Deploy Static Web Apps to AWS S3 Using GitHub Actions</title>
      <dc:creator>Gaurish Naik</dc:creator>
      <pubDate>Sun, 13 Apr 2025 09:17:14 +0000</pubDate>
      <link>https://dev.to/gaurishxjfk/deploy-static-web-apps-to-aws-s3-using-github-actions-45lb</link>
      <guid>https://dev.to/gaurishxjfk/deploy-static-web-apps-to-aws-s3-using-github-actions-45lb</guid>
      <description>&lt;p&gt;This guide will help you securely deploy a static web application (e.g., Next.js, Vite) to an AWS S3 bucket using GitHub Actions and an IAM Role.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Create an IAM Role in AWS
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Log in to the AWS Management Console&lt;/strong&gt; and navigate to &lt;strong&gt;IAM &amp;gt; Roles&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Create Role&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Web Identity&lt;/strong&gt; as the trusted entity type.&lt;/li&gt;
&lt;li&gt;Under &lt;strong&gt;Identity Provider&lt;/strong&gt;, choose &lt;code&gt;token.actions.githubusercontent.com&lt;/code&gt;. If it doesn’t exist:

&lt;ul&gt;
&lt;li&gt;Click &lt;strong&gt;Create Provider&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Set &lt;strong&gt;Provider Type&lt;/strong&gt; to &lt;strong&gt;OpenID Connect&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Enter &lt;code&gt;token.actions.githubusercontent.com&lt;/code&gt; as the &lt;strong&gt;Provider URL&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Set &lt;strong&gt;sts.amazonaws.com&lt;/strong&gt; as the &lt;strong&gt;Audience&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Save&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Return to the &lt;strong&gt;Create Role&lt;/strong&gt; screen, select &lt;code&gt;token.actions.githubusercontent.com&lt;/code&gt;, and confirm &lt;strong&gt;sts.amazonaws.com&lt;/strong&gt; as the audience.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add Permissions&lt;/strong&gt;: Choose a policy like &lt;code&gt;AdministratorAccess&lt;/code&gt; (for simplicity) or a custom policy with least privilege for production.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set Trust Policy&lt;/strong&gt;: Ensure the policy matches the following (replace placeholders with your values):
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Principal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Federated"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:iam::&amp;lt;AWS_ACCOUNT_ID&amp;gt;:oidc-provider/token.actions.githubusercontent.com"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sts:AssumeRoleWithWebIdentity"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Condition"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"StringEquals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"token.actions.githubusercontent.com:aud"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sts.amazonaws.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"token.actions.githubusercontent.com:sub"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"repo:&amp;lt;GITHUB_USERNAME&amp;gt;/&amp;lt;REPO_NAME&amp;gt;:ref:refs/heads/&amp;lt;BRANCH_NAME&amp;gt;"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Name the Role&lt;/strong&gt; (e.g., &lt;code&gt;GitHubActionsRole&lt;/code&gt;) and click &lt;strong&gt;Create Role&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 2: Configure GitHub Repository Secrets
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Go to your &lt;strong&gt;GitHub repository&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Navigate to &lt;strong&gt;Settings &amp;gt; Secrets and Variables &amp;gt; Actions&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Add the following secrets:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;AWS_ROLE_ARN&lt;/code&gt;: The ARN of the IAM role (e.g., &lt;code&gt;arn:aws:iam::&amp;lt;AWS_ACCOUNT_ID&amp;gt;:role/GitHubActionsRole&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AWS_REGION&lt;/code&gt;: Your AWS region (e.g., &lt;code&gt;us-east-1&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;S3_BUCKET&lt;/code&gt;: The name of your S3 bucket (e.g., &lt;code&gt;my-app-bucket&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 3: Create a GitHub Actions Workflow
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;In your repository, create a file at &lt;code&gt;.github/workflows/deploy.yml&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Add the following YAML configuration to automate the deployment to S3:
&lt;/li&gt;
&lt;/ol&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;Deploy to S3&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;main&lt;/span&gt;

&lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;id-token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;
  &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&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;Configure AWS Credentials&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws-actions/configure-aws-credentials@v2&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;role-to-assume&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.AWS_ROLE_ARN }}&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;${{ secrets.AWS_REGION }}&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;Verify AWS Role Assumption&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws sts get-caller-identity&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;Install Dependencies&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm install&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;Build Application&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm run build&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;Deploy Next.js to S3&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;if [ -d "out" ]; then&lt;/span&gt;
            &lt;span class="s"&gt;echo "✅ Found 'out/' directory (Next.js export). Deploying..."&lt;/span&gt;
            &lt;span class="s"&gt;aws s3 sync out/ s3://${{ secrets.S3_BUCKET }}/ --delete --cache-control "public, max-age=0, must-revalidate"&lt;/span&gt;
          &lt;span class="s"&gt;else&lt;/span&gt;
            &lt;span class="s"&gt;echo "❌ 'out/' directory not found. Build may have failed."&lt;/span&gt;
            &lt;span class="s"&gt;exit 1&lt;/span&gt;
          &lt;span class="s"&gt;fi&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;Deploy Vite to S3&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;if [ -d "dist" ]; then&lt;/span&gt;
            &lt;span class="s"&gt;echo "✅ Found 'dist/' directory (Vite). Deploying..."&lt;/span&gt;
            &lt;span class="s"&gt;aws s3 sync dist/ s3://${{ secrets.S3_BUCKET }}/ --delete --cache-control "public, max-age=0, must-revalidate"&lt;/span&gt;
          &lt;span class="s"&gt;else&lt;/span&gt;
            &lt;span class="s"&gt;echo "❌ 'dist/' directory not found. Build may have failed."&lt;/span&gt;
            &lt;span class="s"&gt;exit 1&lt;/span&gt;
          &lt;span class="s"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Explanation of the Workflow
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Trigger&lt;/strong&gt;: The workflow is triggered when a push is made to the &lt;code&gt;main&lt;/code&gt; branch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Permissions&lt;/strong&gt;: Grants access to the GitHub OIDC token and repository contents.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Steps&lt;/strong&gt;:

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Checkout code&lt;/strong&gt;: Uses &lt;code&gt;actions/checkout&lt;/code&gt; to pull the latest code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configure AWS Credentials&lt;/strong&gt;: Uses &lt;code&gt;aws-actions/configure-aws-credentials&lt;/code&gt; to authenticate using the IAM role.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verify Role Assumption&lt;/strong&gt;: Confirms that the AWS role has been assumed successfully.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Install Dependencies&lt;/strong&gt;: Runs &lt;code&gt;npm install&lt;/code&gt; to install the necessary packages.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build Application&lt;/strong&gt;: Executes &lt;code&gt;npm run build&lt;/code&gt; to generate the production build.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deploy to S3&lt;/strong&gt;: Syncs the build output (either &lt;code&gt;out/&lt;/code&gt; for Next.js or &lt;code&gt;dist/&lt;/code&gt; for Vite) to the S3 bucket, ensuring the removal of outdated files and setting proper cache headers.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Notes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Replace placeholders&lt;/strong&gt;: Ensure that you replace &lt;code&gt;&amp;lt;AWS_ACCOUNT_ID&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;GITHUB_USERNAME&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;REPO_NAME&amp;gt;&lt;/code&gt;, and &lt;code&gt;&amp;lt;BRANCH_NAME&amp;gt;&lt;/code&gt; with your actual values.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Least-privilege policy&lt;/strong&gt;: For production environments, use a least-privilege IAM policy instead of &lt;code&gt;AdministratorAccess&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;S3 static website hosting&lt;/strong&gt;: If you’re serving the app directly from S3, make sure your S3 bucket is configured for static website hosting.&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>aws</category>
      <category>githubactions</category>
      <category>vite</category>
      <category>cicd</category>
    </item>
  </channel>
</rss>
