Courses / AWS in Action: AI edition

Session 3 of 10

Employee chat page: private S3 + CloudFront + HTTPS, whole replies

Notes

Student Notes — Chapter 3: Employee chat page: private S3 + CloudFront + HTTPS, whole replies

AWS in Action: AI edition · Chapter 3

Before you start

Nothing is needed to read this chapter or to run its offline checks beyond Python and the development tools listed in the course repository’s requirements-dev.txt. The offline checks read local files, so they need no AWS account and cost nothing. The live steps (deploying the stack, uploading the page and asking the real model) need an AWS account of your own; they create real resources and cost money, so you delete the stack at the end. You can follow these steps from the console, with CloudShell, a terminal in your browser, as the alternative, so there is nothing to install. Sign in as an IAM or IAM Identity Center user, not the root user: as the root user, a step can stop with AccessDeniedException ... :root is not authorized. Check these first: the top bar of the console shows your user name; its Region is one the template lists (RegionRoute); your budget alert is listed under Budgets; Amazon Nova Lite is shown in the Bedrock Model catalog; and template.yaml and app/web/index.html open from chapters/03-chat-page/ on your own computer, because the console uploads both from there. Upload the page into the bucket before you open the chat address, and empty the bucket before you delete the stack. The one-time setup is in SETUP.md at the top of the course repository, and this chapter’s own table is in chapters/03-chat-page/README.md under “Before you try”. This chapter builds on chapter 2: you should know what an API endpoint, a Lambda function, a template and a stack are, and the next section restates what you need. Have paper or a notes app open for the Flashcards and the Model answer.

Three ways to follow this chapter, the same as chapter 2, and you can mix them:

  • Watch: read the chapter and follow the build on screen. Nothing needed.
  • Copy and run: copy the folder chapters/03-chat-page/ and run its checks.
  • Build with AI: open chapters/03-chat-page/prompts/, run each prompt in an AI coding tool, then run that prompt’s check. The checks for the whole chapter are in chapters/03-chat-page/prompts/CHECK.md.

Summary

This chapter gives the helpdesk a front door: a chat page that employees open in a browser at one HTTPS address. The page is a single file kept in a private S3 bucket, and CloudFront, using an origin access control, is the reader the bucket’s policy names. The page calls /ask on its own address, so CloudFront forwards the question to the API you built in chapter 2 and no extra browser rules are needed. Replies arrive whole, not word by word, because a Python Lambda function returns one complete answer and the page shows it when it lands. You extend chapter 2’s template with four resources, try the page in a browser, empty the bucket and delete the stack.

Key concepts

Term Definition
Streamed reply A reply shown piece by piece while the model is still writing it.
Whole reply A reply that arrives once, complete, and is shown at once. This chapter’s page shows whole replies.
Bucket A container for files stored in Amazon S3. Each file in it is an object. A bucket is private until you grant access.
Block Public Access Four bucket settings that stop permissions and policies from opening the bucket to the public. Our template switches them on.
CloudFront The AWS service that delivers web content to browsers through a network of data centers, fetching it from an origin such as a bucket.
Distribution One CloudFront setup, with its own address, its origins and its rules for which path goes where.
Origin Where CloudFront gets the real content from. We have two: the private bucket and the helpdesk API.
Origin access control A CloudFront setting that signs each request to the bucket, so the bucket can tell it came from your distribution.
Bucket policy A permission attached to a bucket. Ours lets the CloudFront service read the files when the request is made for this one distribution.
HTTPS The web’s encrypted way to talk, so what the browser and the site send cannot be read on the way.
Certificate A site’s proof of who it is, which the browser checks for HTTPS. CloudFront supplies one for the address it assigns.
Same-origin A page and a service share one address, so the page calls a short path such as /ask and the browser needs no cross-origin rules.

Explanations

Orientation: where this chapter sits

Chapter 2 built the engine: a function behind an address that you call with curl. This chapter builds the door. In the finished helpdesk an employee uses a chat page, which calls the helpdesk API, which reaches a hosted model:

The finished helpdesk assistant: an employee uses a chat page that calls a helpdesk API in front of a hosted model, which reaches an IT knowledge base, ticket tools and a triage agent, with sign-in, deployment and monitoring around the API.

Today you build the front of the picture. Recap from chapter 2: POST /ask takes a question as JSON and returns an answer as JSON; a Lambda function named handler does the work; a template describes the resources and a stack is what the template builds, created and deleted as one unit.

Try this first: employees need a web page for the helpdesk. Do you keep the page file in a bucket opened to the public, in a private bucket that one delivery service may read, or on an engineer’s laptop that emails it to everyone? Pick one and say why. The chapter builds one of them.

By the end you can:

  • explain how an employee’s browser gets the page and how its question reaches the model;
  • read the new template pieces and say what each is for;
  • deploy the page, try it, and delete the stack cleanly, including the bucket.

Part 1. The goal: a chat page at one address

You walk into a shop that has one entrance. You do not need to know where the stock room is or how deliveries arrive; you use the door. The chat page is that door. An employee opens one HTTPS address, sees a box and a Send button, types a question and sees the answer.

Behind that one address there are two things: the page itself, which is one file in a private bucket, and the /ask endpoint from chapter 2. The code for this chapter is in the course repository under chapters/03-chat-page/. The page is chapters/03-chat-page/app/web/index.html, its tests are chapters/03-chat-page/tests/test_web.py, and the stack description is chapters/03-chat-page/template.yaml. Chapter 2’s function, tests and endpoint are still there, unchanged. A prompts/ folder holds one prompt per build step with a check for each, and a prompt is finished when its check passes.

Part 2. Why replies arrive whole

Think of a tap and a jug. A streamed reply is water running into your glass: you see it arrive bit by bit. A whole reply is the jug set down full. A live caption fills in word by word; a letter lands finished. Many chat products stream. Ours does not.

First, the failure an employee notices. Situation 1: the quiet wait. An employee presses Send and nothing seems to happen while the model works.

Employee presses Send   -> Send is greyed out
The whole reply arrives -> shown, Send is back

The cause is that the reply comes as one piece, so there is nothing to show until it is complete. The page handles it: the question appears at once, Send is switched off so a second click does not send the same question twice, and when the whole reply arrives it is shown and Send is switched back on.

A streamed reply is shown piece by piece while the model is still writing it; a whole reply arrives once, complete. The managed Python runtime for AWS Lambda does not stream a reply by itself, so the function returns one whole answer. The AWS documentation says Lambda’s managed runtimes support response streaming on Node.js, and that other languages, Python included, need a custom runtime or an adapter. Streaming is a possible later upgrade; this chapter keeps one request and one answer. The page reads the whole reply like this:

const data = await response.json();
if (response.ok) add("bot", data.answer);
else add("err", data.error || "Something went wrong. Try again.");

response.json() waits until the whole answer has arrived. The answer is then added to the page as text with textContent, not as markup, because the model’s answer is not trusted. The page’s question box also carries a length limit that matches the function’s 1000-character cap from chapter 2.

Part 3. A private bucket for the page

A drawer in a filing cabinet holds the files and has a lock. A left-luggage locker keeps your things yours until you hand over the ticket. An Amazon S3 bucket is a container like that for files stored in AWS; each file in it is an object. Our page is one object, index.html. The AWS documentation says buckets and the objects in them are private by default: access stays yours until you grant more.

Here is the shortcut that goes wrong. Situation 2: the public bucket. The page does not load, so someone opens the whole bucket to the public.

Bucket opened "to make the page load"
Anyone with the address can read it

The whole contents of the bucket become readable, not just the page. The cause is mixing two jobs: letting people see the page, and letting people read the storage. The template keeps the bucket private with four settings, called Block Public Access, switched on:

PublicAccessBlockConfiguration:
  BlockPublicAcls: true
  BlockPublicPolicy: true
  IgnorePublicAcls: true
  RestrictPublicBuckets: true

These settings override permissions and policies that would open the bucket, so a later mistake does not make it public. The bucket also has no website setting: it is a plain bucket, which is what origin access control needs. Two further settings in the template, default encryption and an ownership rule that turns off per-file access lists, complete the bucket.

Part 4. CloudFront and origin access control

A reception desk sits in front of a warehouse. Visitors talk to the desk; the desk fetches from the stock room. A chain of shops stocks the same catalogue, so most visits do not reach the warehouse. Amazon CloudFront works like that: it delivers web content to browsers through a worldwide network of data centers, fetching it from an origin and keeping copies. A distribution is one CloudFront setup, with its own address and its own rules. Ours has two origins: the private bucket and the helpdesk API.

But how does the desk get into a locked stock room? A courier carries a signed delivery note, and the stock room hands over goods against the note. A staff badge opens a side door for the badge, not for a stranger who knocks. Origin access control is that note: CloudFront signs each request it sends to the bucket, so the bucket can tell it came from your distribution. The AWS documentation recommends origin access control over the older origin access identity, and the template uses it.

A browser sends every request to one CloudFront address. CloudFront reads the chat page from a private bucket with a signed read, and forwards requests for /ask to the helpdesk API and function.

Read the picture from left to right. The browser asks CloudFront for the page (1). CloudFront reads it from the private bucket with a signed request (2). The page then posts the question to the same address (3), and CloudFront sends /ask to the API and function (4). In this design the browser talks to one address, CloudFront.

The bucket still has to agree. A bucket policy names who may read it. Ours names the CloudFront service and the action of reading files, and adds a condition that the request must be on behalf of this one distribution:

Principal:
  Service: cloudfront.amazonaws.com
Action: s3:GetObject
# …

The elided condition compares the request’s source ARN with this distribution’s ARN, which the template builds from the stack. So read access goes to this distribution and not to CloudFront in general. With origin access control set to always sign, the traffic from CloudFront to the bucket also travels over HTTPS.

Part 5. HTTPS and the same address

A postcard can be read by anyone who handles it; an envelope is sealed. HTTPS is the sealed envelope of the web: the browser and the site scramble what they send. A shop licence on the wall shows the shop is who it claims to be. A certificate is that proof for a website, and the browser checks it. On the address CloudFront assigns to a distribution, CloudFront provides the certificate, so the template does not buy or set one. A request made with plain http is redirected to the https address by the viewer rule in the template.

Now the address problem. Situation 3: the mixed-up address. The page sits on one address and calls the API on another.

Page on address A  calls  API on address B
Browser: wants cross-origin rules first

The browser treats the two addresses as different origins and wants extra permission rules before it lets the call through. Think of a colleague at the next desk, whom you simply ask, against another company’s switchboard, which wants approval first. Our design avoids the problem. A page and a service are same-origin when they share one address, so the page calls /ask with no full address, and CloudFront forwards the call to the API. The page does not name another host.

The distribution decides by path which origin answers:

Request for Goes to Kept as a copy?
The page, and anything that is not /ask The private bucket Yes
/ask The helpdesk API and its function No: each question is answered fresh
An address typed with plain http A redirect to the https address Not applicable

For the page, the template uses a cache policy that keeps copies; for /ask it uses one that keeps no copies, and a request policy that leaves out the viewer’s host name, because the API expects its own. These are AWS managed policies, chosen by their ids in the template.

You have now seen three situations: the quiet wait (the page waits for one whole reply), the public bucket (a private bucket and one trusted reader), and the mixed-up address (one address for the page and /ask). What each leaves for later:

What is still open What the helpdesk will do Built in
Anyone with the API’s own address can still call it Sign-in and roles Chapter 7
The model can still make things up Answer from the company’s own pages, cited Chapter 4
The reply still appears in one piece Streaming, as a later upgrade After this course’s build
Nobody watches cost or errors Logs, cost and an alarm Chapter 9

Part 6. Extend the chapter 2 template

A blueprint for an extension keeps the original rooms and adds new ones. Templates in this course are cumulative: chapter 3 holds the resources chapter 2 held, plus what it adds. Chapter 2’s eight resources stay as they were: AskFunctionLogGroup, AskFunctionRole, AskFunction, HelpdeskApi, AskIntegration, AskRoute, DefaultStage and AskInvokePermission. The file is chapters/03-chat-page/template.yaml, and it adds four resources:

Piece What it is for Name in the template
Site bucket Holds the chat page, private SiteBucket
Origin access control Signs CloudFront’s requests to the bucket SiteOriginAccessControl
Distribution The HTTPS address; sends the page and /ask where they belong SiteDistribution
Bucket policy Lets this one distribution read the files SiteBucketPolicy

The template also adds two outputs you use: ChatUrl, the address to open, and SiteBucketName, where the page is uploaded. Resources that can carry tags are tagged chapter: session-03 plus your project and environment. As in chapter 2, Project and Environment start every name you choose (helpdesk-dev-ask, helpdesk-dev-site), and nothing has a Retain deletion policy.

You can also build this with an AI coding tool. The prompts chapters/03-chat-page/prompts/01-chat-page.md and chapters/03-chat-page/prompts/02-page-tests.md write the page and its tests, 03-private-bucket.md adds the bucket and the access control, and 04-distribution.md adds the distribution and the bucket policy. cfn-lint template.yaml and pytest -q are their checks.

Part 7. Try it, then delete it

Costs money. The live steps create real resources and call a real model. Deploy, try and delete in one sitting.

aws cloudformation deploy --stack-name helpdesk-dev-ch3 --template-file template.yaml --capabilities CAPABILITY_IAM --parameter-overrides Project=helpdesk Environment=dev
BUCKET=$(aws cloudformation describe-stacks --stack-name helpdesk-dev-ch3 --query "Stacks[0].Outputs[?OutputKey=='SiteBucketName'].OutputValue" --output text)
SITE=$(aws cloudformation describe-stacks --stack-name helpdesk-dev-ch3 --query "Stacks[0].Outputs[?OutputKey=='ChatUrl'].OutputValue" --output text)
aws s3 cp app/web/index.html "s3://$BUCKET/index.html" --content-type text/html

The first command creates the stack; it needs --capabilities CAPABILITY_IAM because the template creates a role, and --parameter-overrides sets Project and Environment, which start every resource name. The next two read the bucket name and the page address from the stack’s outputs. The last copies the page into the bucket as index.html. Open the address in $SITE in a browser and ask a question. From a terminal, the same address serves the page, and /ask on it returns an answer:

curl -s -o /dev/null -w "%{http_code}\n" "$SITE/"
curl -s -X POST "$SITE/ask" -H "content-type: application/json" -d '{"question": "How do I reset my password?"}'

The first prints 200. The chapter’s check file also asks the bucket for the page directly, not through CloudFront, and expects a refusal with status 403. These commands are also in chapters/03-chat-page/README.md, and the whole live check is in chapters/03-chat-page/prompts/CHECK.md.

A hotel guest checks out and the room is cleared; a room left occupied keeps being charged. A stack you do not delete keeps costing money. This time the bucket holds the page, and a bucket can be deleted only when it has no objects in it. So empty it first:

aws s3 rm "s3://$BUCKET" --recursive
aws cloudformation delete-stack --stack-name helpdesk-dev-ch3
aws cloudformation wait stack-delete-complete --stack-name helpdesk-dev-ch3

The wait command polls until the stack is gone. Deleted stacks are hidden from the console’s stack list by default, so a list that no longer shows helpdesk-dev-ch3 confirms it. If you skip the first command, the delete fails on the bucket, the stack shows a failed-delete state, and you read the reason, empty the bucket and try again.

Practice: which piece handles it?

Decide each one before reading the call: which piece handles it, and what happens.

Problem Our call and reason
A stranger asks the bucket directly for the page The bucket refuses it: it is private, and its policy trusts this one distribution
An employee types the address with plain http CloudFront redirects it to the https address
The page has to reach /ask The page calls /ask on its own address and CloudFront forwards it to the API
The model takes a while to answer The page greys out Send and waits for the whole reply; streaming is a later upgrade
You finish the chapter and the bucket still holds the page Empty the bucket, delete the stack, and wait until it is gone

Flashcards

Say the answer aloud before revealing it. Speaking is a different skill from reading, so practise both.

1. What is the difference between a streamed reply and a whole reply? A streamed reply is shown piece by piece while the model is still writing, like a tap running into a glass. A whole reply arrives once, complete, like a jug set down full. This chapter's page shows whole replies.
2. Why does the helpdesk return whole replies? The managed Python runtime for Lambda does not stream a reply by itself, so the function returns one complete answer and the page shows it when it arrives. Streaming is a possible later upgrade, not a setting this chapter turns on.
3. What is an S3 bucket, and what is an object? A bucket is a container for files stored in AWS, like a drawer in a filing cabinet. Each file in it is an object. Our chat page is one object, index.html, and the bucket is private until access is granted.
4. What do the four Block Public Access settings do? They override permissions and policies that would open the bucket to the public, so a later mistake does not make it public. The template switches them on, and the bucket has no website setting.
5. What is CloudFront, and what is a distribution? CloudFront delivers web content to browsers through a network of data centers, fetching it from an origin such as a bucket. A distribution is one CloudFront setup with its own address, its origins and its path rules. Like a reception desk in front of a warehouse.
6. What is origin access control? A CloudFront setting that signs each request it sends to the bucket, like a courier carrying a signed delivery note. The bucket can then tell the request came from your distribution. It is the recommended replacement for the older origin access identity.
7. What does the bucket policy say? It lets the CloudFront service read files from the bucket when the request is made on behalf of this one distribution, using a condition on the distribution's ARN. Read access goes to this distribution, not to CloudFront in general.
8. What are HTTPS and a certificate? HTTPS is the web's encrypted way to talk, like a sealed envelope instead of a postcard. A certificate is the site's proof of who it is, which the browser checks. On the address CloudFront assigns, CloudFront provides the certificate.
9. What does same-origin mean, and why does the page call /ask? A page and a service are same-origin when they share one address. The page calls the short path /ask with no full address, so it stays on its own address and the browser needs no cross-origin rules. CloudFront forwards the call to the API.
10. How does CloudFront decide where a request goes? By path. A request for /ask goes to the helpdesk API and function with no copy kept; the page and anything else goes to the private bucket, where copies may be kept. A plain http address is redirected to https.
11. What does chapter 3 add to the chapter 2 template? Four resources: SiteBucket, SiteOriginAccessControl, SiteDistribution and SiteBucketPolicy, plus the outputs ChatUrl and SiteBucketName. Chapter 2's eight resources stay as they were, because templates are cumulative.
12. Why must you empty the bucket before deleting the stack, and how do you know it is gone? A bucket can be deleted only when it has no objects, and the page is still in it. Run the bucket empty command, then delete-stack, then wait stack-delete-complete; the console list no longer shows the stack.

Model answer

The core question this chapter answers: “The helpdesk has a chat page now. How does an employee reach it safely over the web, and how does their question get to the model?”

Four things a colleague listens for, in this order: private bucket · CloudFront · one HTTPS address · whole reply. Skip any of these four and the answer is incomplete.

  1. Private bucket — the page is one file in a bucket with public access blocked
  2. CloudFront — the front door, with origin access control and a bucket policy for this one distribution
  3. One HTTPS address — the page and /ask share it, so the call is same-origin
  4. Whole reply — the function returns the whole answer and the page shows it at once

The full answer, said aloud:

(Private bucket) “The chat page is one file in an S3 bucket with the four public-access settings on, so nobody can read the bucket directly.

(CloudFront) A CloudFront distribution is the front door. Origin access control makes it sign its reads, and the bucket policy lets this one distribution read the files and nobody else.

(One HTTPS address) The employee uses one address, over HTTPS with the certificate CloudFront supplies, and plain http is redirected. The page calls /ask on that same address, so the call is same-origin, and CloudFront forwards it to the API and function.

(Whole reply) The function sends the model’s question and returns the whole answer. The page greys out Send, waits, and shows the reply at once. Streaming would be a later upgrade.“

Missing a point is a failure state. The follow-up question lands where you went quiet: finish without the private bucket and the next question is whether the files are public, and finish without the one address and the next question is how the page reaches the API. Practise until the four come out without prompting, then stop talking and let the questions come.

Why this matters

In a conversation with a colleague: a team publishing an internal tool wants someone who can say plainly how the page is served, what is exposed, and why the answer does not stream. The four points leave no obvious gap, and the helpdesk is a specific example.

On the job: a developer publishing a page, a security review of a new web page, and an engineer tearing down a test environment ask versions of the same questions. The private bucket, the origin access control, the single address and the empty-then-delete order answer them.

In the rest of this course: chapter 4 puts policy pages into this same bucket and shows the cited page beside the answer, using the page and the address you built here. A reader who knows these seven parts can predict why each piece is there.

After this unit

Assignment. The chapter project asks you to deploy the stack, upload the page, ask the helpdesk from a browser, check that the bucket refuses a direct request, and delete it. Keep the template: the next chapter extends it. The brief itself is the project file.

What’s next. The next chapter makes the helpdesk answer from the company’s own IT policy pages and shows which page it used, beside the answer.

TechEazy Consulting training material — not affiliated with or endorsed by Amazon Web Services (AWS), Amazon Bedrock or any other company named in this course.