One day, document checking processes had become a bottleneck at work, so I decided to build something on AWS – partly to organize my own thoughts.

What I’m building here is a checking system for internal blog posts. While it’s scoped to internal blog articles specifically, the design can be generalized for all kinds of checking workflows, so think of this as a prototype.

Here’s what the architecture looks like.

Architecture Diagram

System Architecture Diagram

Business Requirements

  • In practice, this will be used by people in the business division.
  • There are various checking rules for blog articles, combining programmatic checks with AI-based checks.
  • Users can create and modify checking logic through conversations with AI.
  • Checking logic is integrated into the system as plugins.
  • A database is set up, and the checking logic accesses it through a DB service layer. (Databases get expensive, so I might stub this out for now.)

That was a long preamble, but I want to document the journey of building the system above as a reference for myself.

A Note on AWS Service Selection

I haven’t gone through any formal, structured learning as an AWS Builder. I picked my services by consulting GPT-5.1, so there might be some choices that don’t follow AWS best practices. My plan is to build it first and then ask the AWS veterans for their input.

…Maybe I’ll ask at AWS AI Builders Day!

(1) Frontend / User Authentication

CloudFront + S3 + Cognito

Note: CloudFront is the entry point for external access, S3 is object storage, and Cognito is a managed service for user management.

Let’s start with the CloudFront setup.

I asked GPT-5.1 to create a Canvas and walked through the CloudFront build steps.

Originally, I was planning to implement user management from scratch, but I learned that Cognito handles that nicely. …AWS really does have a generous lineup of managed services.

While I’m at it, let me learn about GitHub CI pipelines too – so I can deploy from Git via CI.

By the way, when I ran git init, Git suggested changing the default branch name from master to main. Apparently, Git’s default used to be “master,” but in recent years there’s been growing awareness that the “master/slave” terminology evokes racial discrimination and slavery, which led to the movement to adopt the more neutral “main.”

Getting back on track – I managed to create a simple SPA and set up the repository. There weren’t any major stumbling blocks up to this point. I only had a bit of trouble with Cognito-based user authentication, but overall things went smoothly. Next time, I’ll start working on automated deployment.

Build Log 1

Build Log 2

Below is the build log (procedure notes) created with GPT-5.1.

Blog Article Check Engine – Build Log

This document serves as the work log for building the Blog Article Check Engine on AWS.

0. Prerequisites

  • Region: us-west-2 Oregon
  • System name: Blog Article Check Engine PoC

1. CloudFront Setup

1-0. Pre-checks

1-1. Creating a New CloudFront Distribution – Step 1: Get Started

  1. Open the CloudFront console, go to Distributions in the left menu, and click Create distribution
  2. On the “Get started” screen, configure the following:
    • Distribution name: something descriptive like blog-checker-web-dist
    • This is essentially a tag-like name and doesn’t affect the URL
    • Description: leave empty (add later if needed)
    • Distribution type: select Single website or app
    • Domain: leave empty for now (set later if using a custom domain via Route 53)
    • Tags: optional. Add something like Project = blog-checker-web if desired
  3. Click Next in the bottom right to proceed to Step 2: Specify Origin

1-2. Creating a New CloudFront Distribution – Step 2 Onward

  1. Step 2: Specify Origin
    • Origin type: select Web
    • Origin domain: select your S3 bucket blog-checker-web-XXXX (e.g., blog-checker-web-example) from the dropdown
    • Example: example-bucket.s3.us-west-2.amazonaws.com
    • If you see a message like “This S3 bucket is configured for S3 Web hosting. If you plan to use this distribution as a website, we recommend using the S3 website endpoint instead of the bucket endpoint” – no changes needed
    • Leave everything else at defaults and click Next
  2. Step 3: Enable Security
    • This step lets you choose whether to attach AWS WAF (Web Application Firewall) to CloudFront
    • For the Blog Article Check Engine PoC, select Do not enable security protections to keep costs and configuration simple
    • Don’t use “Use existing WAF configuration” or “Create new WAF”
    • Leave “Use monitor mode” off
    • WAF can be added later when needed
    • Click Next in the bottom right to proceed to Step 4: Configure Distribution
  3. Step 4: Configure Distribution
    • Under Default cache behavior, set the following:
    • Viewer protocol policy: select Redirect HTTP to HTTPS
    • Allowed HTTP methods: select GET, HEAD (sufficient for now)
    • Cache policy: select Managed-CachingOptimized
    • Compress objects automatically: leave enabled
    • Leave everything else at defaults and click Next
  4. Step 5: Get TLS Certificate
    • For now, select “Use the default CloudFront certificate”
    • If using a custom domain, set up an ACM certificate later
    • Click Next to go to the final review screen
  5. Step 6: Review and Create
    • Review your settings and click Create distribution if everything looks good

1-3. Distribution Settings and Default Cache Behavior

  1. In the Default cache behavior section:
    • Viewer protocol policy: select Redirect HTTP to HTTPS
    • Allowed HTTP methods: select GET, HEAD (sufficient for now)
    • Cache policy: select Managed-CachingOptimized
    • Compress objects automatically: leave enabled
  2. Leave other settings at defaults; fine-tuning can be done later

1-4. Domain and Certificate Configuration

  • For now, use the auto-generated CloudFront domain name xxxx.cloudfront.net as-is
  • If using a custom domain later, plan to configure:
    • Alternate domain name (CNAME)
    • ACM certificate

1-5. Creating the Distribution and Verifying It Works

  1. Click Create distribution at the bottom of the screen to start creation
  2. In the Distributions list, wait for the target distribution’s Status to change from Deploying to Enabled
  3. Once Status is Enabled, note the Domain name for that row
  4. In your browser, go to https://{CloudFront Domain name}/index.html and confirm that the login screen from the index.html uploaded to S3 is displayed
  5. If accessing https://{CloudFront Domain name}/ returns AccessDenied, set up CloudFront’s default root object as follows:
    1. Open the target distribution in the CloudFront console and click Edit on the General tab
    2. Enter index.html in the Default root object field
    3. Save the changes and wait a few minutes for the status to become Deployed
    4. Access https://{CloudFront Domain name}/ again and confirm that index.html is displayed

1-6. Updating the S3 Bucket Policy for OAC

  1. Open the target distribution in the CloudFront console and select the Origins tab
  2. Select the row for the S3 bucket configured as the origin and click Edit
  3. Copy the sample policy shown in the “Update bucket policy” section
  4. In a separate tab, open the S3 console, navigate to the target bucket -> Permissions -> Bucket policy
  5. Replace the existing policy with the sample policy from CloudFront and save
  6. Keep Block public access enabled – the setup ensures access only through CloudFront OAC
  7. If needed, disable public access to the S3 website endpoint (for production use)

2. Creating the S3 Static Hosting Bucket

2-1. Creating a Bucket for Web Hosting

  1. Confirm the region in the top-right of the AWS console is US West (Oregon) us-west-2
  2. Open S3 from the service list
  3. Click “Create bucket”
  4. General configuration
    • AWS Region: leave as US West (Oregon) us-west-2
    • Bucket name: enter a unique name like blog-checker-web-example (lowercase and hyphens only)
    • Copy settings from existing bucket: leave unselected
  5. Object Ownership
    • Keep “ACLs disabled (recommended)” selected
    • Object ownership stays as “Bucket owner” – that’s fine
  6. Block Public Access settings for this bucket
    • Keep all four checkboxes checked
    • It should show a note saying “Block all public access” – that’s correct
    • Access will be granted later through CloudFront OAC and bucket policy
  7. Scroll down; leave versioning, encryption, etc. at defaults (no changes needed for now)
  8. Click “Create bucket” at the bottom right
  9. Confirm the bucket name appears in the bucket list, and note the actual bucket name in the build log

2-2. Enabling Static Website Hosting

  1. Open S3 in the AWS console
  2. Click the bucket name link for the target bucket (e.g., blog-checker-web-example) in the bucket list
  3. Click the Properties tab at the top of the bucket detail screen
  4. Scroll down the Properties page and find the Static website hosting section
  5. Click the Edit button on the right side of the Static website hosting section
  6. Under Hosting type, select Host a static website
  7. Enter index.html for the Index document
  8. Error document can be left empty, or enter error.html if you plan to create one later
  9. Click Save changes at the bottom right
  10. The Static website hosting section will now show a Bucket website endpoint – note this URL in the build log

2-3. Uploading Initial Content

Here we’ll upload the index.html from the local blog-checker-web/dist/ directory to the S3 bucket.

  1. Confirm that blog-checker-web/dist/index.html exists locally
  2. Open S3 in the AWS console and click the bucket name link for the target bucket (e.g., blog-checker-web-example)
  3. Confirm the Objects tab is selected at the top
  4. Click the “Upload” button in the top right
  5. On the upload screen, click “Add files” and select your local dist/index.html
  6. Scroll to the bottom; leave the storage class at the default “Standard”
  7. Leave access permissions at defaults (public access will be handled later through CloudFront OAC)
  8. Click the “Upload” button at the bottom right
  9. Once you see the upload complete message, go back to the Objects tab and confirm that index.html appears

3. Creating a Cognito User Pool

Here we’ll create the Cognito User Pool used for login authentication in the Blog Article Check Engine. This covers the preparation needed to connect from the frontend (CloudFront + S3) login screen.

3-1. Creating a New User Pool

  1. Confirm the region in the top-right of the AWS console is US West (Oregon) us-west-2
  2. Open Cognito from the service list
  3. Select User pools from the left menu
  4. Click Create user pool in the top right
  5. On the “Configure sign-in experience” screen:
    • Select Cognito user pool (not using external IdP for now)
    • Sign-in options: check Email only (login with email address rather than username)
    • Click Next
  6. “Configure security requirements” screen
    • Password policy: leave at defaults (minimum 8 characters, etc.)
    • Multi-factor authentication (MFA): select No MFA (this is a PoC)
    • Leave other settings at defaults and click Next
  7. “Configure sign-up experience” screen
    • Self-service sign-up: turn ON if needed (if you want to sign up yourself)
    • Required attributes: set only email as Required
    • Attribute verification: check Email (email verification)
    • Click Next
  8. “Configure message delivery” screen
    • Use the default Cognito email provider (don’t configure SES)
    • Click Next
  9. “Integrate your app” screen
    • User pool name: something descriptive like blog-checker-user-pool
    • App client settings will be configured in 3-2, so just click Next
  10. On the Review screen, verify the settings and click Create user pool if everything looks good
  11. Note the created User Pool ID (e.g., <your_user_pool_id>)

3-2. Creating an App Client (for the Web Frontend)

  1. Open the user pool you created, then go to App integration -> App clients in the left menu
  2. Click Create app client
  3. Enter blog-checker-web-client or similar for the App client name
  4. Authorized client type:
    • Select Public client (no secret needed since it’s called directly from frontend JavaScript)
  5. Authentication flows:
    • Enable user password authentication flows like USER_PASSWORD or USER_SRP (defaults are fine)
  6. OAuth 2.0 settings can be skipped for now (configure later alongside the Hosted UI if needed)
  7. Leave other settings at defaults and click Create app client
  8. Note the created App client ID (e.g., <your_app_client_id>)

3-3. Domain Setup (Preparation for Hosted UI)

  1. Open App integration -> Domain name in the User Pool screen
  2. Enter a unique string like blog-checker-auth as the Cognito domain prefix
  3. Confirm that the domain like auth.us-west-2.amazoncognito.com is available and save
  4. Note the generated domain URL (e.g., https://<your-domain-prefix>.auth.us-west-2.amazoncognito.com)

3-4. Notes on Connecting with CloudFront / Frontend

Information needed to connect from the frontend (https://<your-cloudfront-domain>/) to Cognito:

  • Region: us-west-2
  • User Pool ID: <your_user_pool_id>
  • App client ID: <your_app_client_id>
  • Cognito domain: https://<your-domain-prefix>.auth.us-west-2.amazoncognito.com

These values will later be embedded as configuration in assets/js/app.js, used in the implementation that calls Cognito’s sign-in API from the login form.

If using the Hosted UI, the Callback URL and Sign out URL need to be registered with the CloudFront URL in the Cognito app client settings (a follow-up task).

4. Git / GitHub Initial Setup Notes

4-1. Creating a Local Repository

  1. Navigate to the blog-checker-web/ directory locally
  2. Run the following to initialize a Git repository:
cd blog-checker-web
git init
  1. Create a .gitignore to exclude dist/ and temporary files (can be updated later):
# .gitignore example
node_modules/
dist/
.DS_Store
.env
  1. Initial commit:
git add .
git commit -m "Initial commit: blog checker web skeleton"

4-2. Linking to a GitHub Repository

  1. Create a new repository on GitHub
    • Repository name: blog-checker-web-demo
    • Auto-generating README or .gitignore is fine either way (we’ll push from local this time)
  2. Following the git remote / push commands shown by GitHub, run the following locally:
git remote add origin git@github.com:<your-account>/blog-checker-web-demo.git
git branch -M main
git push -u origin main
  1. Running the above commands pushes the initial code to the <your-account>/blog-checker-web-demo repository on GitHub

4-3. Relationship to Deployment

  • If setting up automated deployment via GitHub Actions in the future, the plan is to use this blog-checker-web-demo repository as the trigger.
  • For now, uploading from local to S3 (manually or via shell script) is the approach, with GitHub serving as code history management and backup.

Upcoming Task Ideas

  • Create a local deploy script (deploy.sh) to automate dist/ -> S3 uploads and CloudFront cache invalidation
  • Add a GitHub Actions workflow (.github/workflows/deploy.yml) triggered by pushes to the main branch for automated deployment
  • Organize the blog article checking logic in a plugin-style architecture, designing it to align with future check plugin structures for other document types
  • Consider implementing Authorization header attachment using authenticated user info (id_token) for future API Gateway / Lambda integration

Note: Specific values such as CloudFront domain names, Cognito User Pool IDs / Client IDs, and GitHub account names have been replaced with placeholders (<your-...>, xxxx.cloudfront.net, etc.) for blog publication.