Deploy a Secure S3 CloudFront Portfolio
Build a responsive portfolio securely through CloudFront from private S3.
Introduction
30 Second Summary
A personal portfolio can be the first place a recruiter or collaborator meets your work. A clear page that opens securely on any screen makes that first look count.
In this project, you will build a responsive single-page portfolio. You will then publish it from a private Amazon S3 origin through Amazon CloudFront.
What You'll Build
You'll open your public HTTPS address to a polished portfolio where every section works across wide desktop screens and narrow mobile views.
By the end of this project, you'll have:
- A clear navigation that moves visitors between Home, About Me, Education, Skills, Projects, Resume, and Contact.
- A responsive presentation whose project cards collapse to one column on narrow screens. Its footer shows the current year automatically.
- A secure delivery path where visitors open the portfolio through CloudFront. Direct Amazon S3 requests remain blocked.
- Secret Mission: Publish a fourth Secure AWS Portfolio project card. Then refresh the cached /index.html page with a CloudFront invalidation.
Are there any prerequisites?
You need a Windows computer with internet access. An active AWS account is required before cloud deployment. Account activation can take up to 24 hours.
Before We Start
Before the hands-on work begins, take a moment to define who your secure personal portfolio serves. A clear audience gives every later design choice a purpose.
Picture one visitor opening your portfolio. Decide what you want that person to understand about your skills or goals after reading it.
Verify Your AWS Account and Windows Tools
You have defined who your portfolio serves. Before you build it, you need secure access to the AWS Management Console plus a clear view of your account's billing status.
These checks prevent routine root use. They also reveal account activation issues before deployment.
Your local workspace needs the right tools too. Visual Studio Code will hold your source files. Microsoft Edge will display the portfolio during local testing.
In this step, get ready to:
- Review your AWS account access and billing status.
- Establish secure console access for routine project work.
- Prepare the local portfolio workspace on Windows.
Verify AWS access and billing
An active AWS account gives you access to the cloud services used later. Checking the account plan now gives you a baseline for spotting future charges.
- Open https://console.aws.amazon.com/ in your browser.
- Attempt to sign in to your AWS account.
- Choose the tab that matches what you see.
✔️ Console Home loads
Your account is active. Console Home confirms that you can access the services needed later.
ⓧ I need an AWS account
An active account is required before deployment. The sign-up process asks for contact information. It also requires phone verification plus a valid payment method.
- Open the official AWS account setup guide.
- Complete the required account details.
- Complete the phone verification.
- Provide the required payment method.
- Choose Complete sign up.
Account activation usually takes a few minutes. It can take up to 24 hours, so a longer wait does not mean the sign-up failed.
- Return to the AWS Management Console after receiving the activation confirmation.
- Confirm that Console Home loads.
ⓧ My activation is pending
AWS can take up to 24 hours to activate a new account. Continue after you receive the activation confirmation.
- Return to https://console.aws.amazon.com/ after activation.
- Complete the sign-in process.
- Confirm that Console Home loads.
Your account plan determines which pricing conditions apply. This review creates no AWS resources.
- Open Billing and Cost Management from the AWS Management Console.
- Open the Free Tier page or your account plan.
- Identify whether the account is currently Free or Paid.
Good progress. You now have a billing baseline before the project creates anything that can incur charges.
Why check billing now?
Amazon S3 charges for storage plus requests. Amazon CloudFront can also incur charges when usage exceeds its monthly allowance.
Knowing your account status makes later cost decisions easier to evaluate.
Secure the project identity
The root user controls the entire AWS account. Routine work should use an IAM Identity Center identity or an IAM identity protected by MFA.
Identity settings can feel high-stakes. This flow strengthens console access without creating access keys or cloud resources.
Microsoft Authenticator is available as a mobile app for iOS or Android. A passkey is another supported option for protecting the root user.
- Check the account menu to determine whether you are signed in as the root user.
- Avoid creating access keys for this console-only project.
- Choose the tab that matches your current identity.
✔️ I am using non-root access
- Confirm that the account menu does not identify you as the root user.
- Confirm the MFA status for this identity.
- Confirm that the identity has permission to manage this lab.
- Continue through your approved AWS access portal or IAM sign-in page.
Your routine project work is separated from the root credentials. Keep using this identity throughout the project.
ⓧ I am using root with another identity available
- Check whether MFA is already assigned to the root user.
- Choose the account name if root MFA is missing.
- Choose Security credentials.
- Choose Assign MFA device.
- Choose Authenticator app or Passkey or Security Key.
- Follow the registration prompts for the selected method.
The root user now has an extra sign-in check. Routine work can move to the separate identity.
- Sign out of the root user.
- Open your approved AWS access portal or IAM sign-in page.
- Sign in with the non-root identity.
- Complete the MFA prompt.
- Confirm that Console Home loads.
ⓧ Root is my only identity
Protect the root user before creating a separate identity. The new identity becomes your routine console entry point.
- Check whether MFA is already assigned to the root user.
- Choose the account name if root MFA is missing.
- Choose Security credentials.
- Choose Assign MFA device.
- Choose Authenticator app or Passkey or Security Key.
- Follow the registration prompts for the selected method.
Your root credentials now have an additional sign-in check. The next actions create the separate administrative identity.
- Open the IAM Identity Center administrative-user quick start.
- Choose Users.
- Choose Add user.
- Complete the new user's details.
- Assign the user to the AWS account with the predefined AdministratorAccess permission set.
- Accept the invitation sent to your email address.
- Register MFA for the new identity.
- Sign out of the root user.
The separate identity is ready to become your normal entry point.
- Enter the AWS Management Console through the AWS access portal.
- Confirm that Console Home loads.
Why does the quick start use broad access?
The predefined AdministratorAccess permission set supports the initial administration of a standalone learning account. It keeps routine work away from the root credentials.
This bootstrap permission should not be described as least privilege. Its purpose is to establish a safer human sign-in path.
Having trouble with secure sign-in?
Check whether you are using a root sign-in page or an AWS access portal. Each route expects a different identity.
Ask the account administrator for the approved portal if another person manages the account.
Help me choose the correct AWS sign-in path.
Prepare the Windows workspace
The portfolio needs one named folder inside your Windows Documents directory. Keeping all three source files there makes the next local test predictable.
- Press the Windows key to open Windows search.
- Type Visual Studio Code.
- Press Enter.
✔️ Visual Studio Code opens
Visual Studio Code is available. Its welcome screen or editor window confirms that the local editor is ready.
ⓧ Visual Studio Code is missing
The Windows User setup installs under your Windows user account. It does not require administrator permissions.
- Open the official Visual Studio Code Windows setup page.
- Download the Windows User setup.
- Open your Downloads folder.
- Run VSCodeUserSetup-{version}.exe.
- Complete the installation prompts.
- Search for Visual Studio Code from Windows search again.
Visual Studio Code should now open to its welcome screen or editor window.
Visual Studio Code still missing?
Confirm that you downloaded the Windows User setup. Run the installer again if it did not reach its completion screen.
Help me install Visual Studio Code on Windows.
- Press the Windows key to open Windows search.
- Type Microsoft Edge.
- Press Enter.
✔️ Microsoft Edge opens
Microsoft Edge is available. Keep it ready for the local portfolio tests in the next step.
ⓧ Microsoft Edge is missing
- Open the official Microsoft Edge download page.
- Choose Download Edge for Windows.
- Run the downloaded installer.
- Complete the installation prompts.
- Search for Microsoft Edge from Windows search again.
Microsoft Edge should now open to a browser window.
The two local tools are ready. You can now create the folder that keeps the portfolio files together.
- Press the Windows key to open Windows search.
- Type File Explorer.
- Press Enter.
You should see a File Explorer window with your Windows folders.
- Select Documents in the File Explorer sidebar.
- Select the folder creation control in the File Explorer toolbar.
- Enter portfolio-website as the folder name.
You should see portfolio-website inside Documents. Its full location is C:\Users\<your-windows-username>\Documents\portfolio-website.
- Switch back to Visual Studio Code.
- Click File in the top menu.
- Choose Open Folder.
You should see the Windows folder picker.
- Select the Documents directory.
- Select the portfolio-website folder.
- Confirm the folder selection.
The Visual Studio Code Explorer should now show portfolio-website as the open folder.
- Click the new-file icon in the Explorer toolbar.
- Enter index.html in the filename field.
- Press Enter.
You should see index.html beneath the portfolio folder in Explorer.
- Click the new-file icon in the Explorer toolbar.
- Enter style.css in the filename field.
- Press Enter.
You should see style.css beneath index.html in Explorer.
- Click the new-file icon in the Explorer toolbar.
- Enter script.js in the filename field.
- Press Enter.
You should now see all three source files in Explorer. Each file should open to a blank editor.
✔️ Awesome, I've got everything!
Keep the portfolio-website folder open in Visual Studio Code. Confirm that all three files remain empty.
ⓧ I'd like to double check the full code
The target code for this setup step is blank. Each source file should contain zero lines of code.
- Select index.html in Explorer.
- Confirm that its editor contains no text.
- Select style.css in Explorer.
- Confirm that its editor contains no text.
- Select script.js in Explorer.
- Confirm that its editor contains no text.
Before the final check, which three empty filenames do you expect to see in Explorer?
- Return to the AWS Management Console.
- Confirm that the account menu identifies a non-root identity where possible.
- Confirm that you have reviewed the account plan or Free Tier status.
- Switch back to Visual Studio Code.
- Confirm that index.html appears in Explorer.
- Confirm that style.css appears in Explorer.
- Confirm that script.js appears in Explorer.
Explorer should show the open portfolio-website folder with three empty files. This confirms that the local workspace matches the required starting state.
Your account checks are complete. Your empty workspace is ready for the portfolio structure you will build next.
Build the Portfolio Structure
Your secure workspace is ready. The empty files in Visual Studio Code now need a page structure that a browser can understand.
A page can contain the right information while still being hard to scan. Building a working Semantic HTML baseline helps you separate structural problems from visual design problems.
In this step, get ready to:
- Add the complete portfolio structure to index.html.
- Open the portfolio locally in Microsoft Edge.
- Test every navigation link against its matching section.
Create the page foundation
The document head connects style.css plus script.js to the page. The body uses semantic regions to organize the header, main content, seven sections, and footer.
- Select index.html in the Visual Studio Code Explorer.
- Replace its empty contents with the complete page from the second tab below:
✔️ Awesome, I've got everything!
Great. Double check that index.html contains the complete portfolio structure.
ⓧ I'd like to double check the full code
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="A personal portfolio and resume website for Your Name.">
<title>Your Name | Portfolio</title>
<link rel="stylesheet" href="style.css">
<script src="script.js" defer></script>
</head>
<body>
<header class="site-header">
<div class="container header-layout">
<a class="brand" href="#home">Your Name</a>
<nav aria-label="Primary navigation">
<ul class="nav-list">
<li><a href="#home">Home</a></li>
<li><a href="#about">About</a></li>
<li><a href="#education">Education</a></li>
<li><a href="#skills">Skills</a></li>
<li><a href="#projects">Projects</a></li>
<li><a href="#resume">Resume</a></li>
<li><a href="#contact">Contact</a></li>
</ul>
</nav>
</div>
</header>
<main>
<section id="home" class="hero">
<div class="container hero-grid">
<div>
<p class="eyebrow">Student and Aspiring Developer</p>
<h1>Hello, I am Your Name.</h1>
<p class="hero-text">
Welcome to my portfolio. I enjoy learning new technologies and building
practical projects that solve real problems.
</p>
<div class="hero-actions">
<a class="button button-primary" href="#projects">View my projects</a>
<a class="button button-secondary" href="#contact">Contact me</a>
</div>
</div>
<aside class="hero-card" aria-label="Portfolio summary">
<span class="avatar" aria-hidden="true">YN</span>
<h2>Your Name</h2>
<p>Your Course</p>
<p>Your City, Country</p>
</aside>
</div>
</section>
<section id="about" class="section">
<div class="container">
<div class="section-heading">
<p class="eyebrow">About Me</p>
<h2>A short introduction</h2>
</div>
<p class="lead">
I am a student with an interest in software development, cloud computing,
and continuous learning. I like turning ideas into simple, useful projects
and improving my skills through hands-on practice.
</p>
</div>
</section>
<section id="education" class="section section-alt">
<div class="container">
<div class="section-heading">
<p class="eyebrow">Education</p>
<h2>My academic background</h2>
</div>
<article class="card">
<p class="card-label">2023 to Present</p>
<h3>Your College Name</h3>
<p>Your Course or Degree</p>
<p>Relevant subjects: Web Development, Programming, Databases, and Cloud Fundamentals.</p>
</article>
</div>
</section>
<section id="skills" class="section">
<div class="container">
<div class="section-heading">
<p class="eyebrow">Skills</p>
<h2>Tools and strengths</h2>
</div>
<div class="grid grid-three">
<article class="card">
<h3>Programming</h3>
<ul class="skill-list">
<li>HTML</li>
<li>CSS</li>
<li>JavaScript</li>
</ul>
</article>
<article class="card">
<h3>Technical</h3>
<ul class="skill-list">
<li>Amazon S3</li>
<li>Amazon CloudFront</li>
<li>Responsive Design</li>
</ul>
</article>
<article class="card">
<h3>Professional</h3>
<ul class="skill-list">
<li>Problem Solving</li>
<li>Communication</li>
<li>Teamwork</li>
</ul>
</article>
</div>
</div>
</section>
<section id="projects" class="section section-alt">
<div class="container">
<div class="section-heading">
<p class="eyebrow">Projects</p>
<h2>Things I have built</h2>
</div>
<div class="grid grid-three">
<article class="card project-card">
<p class="card-label">School Project</p>
<h3>Student Record Page</h3>
<p>A simple webpage that organizes student information in a clear layout.</p>
<p class="tag-list">HTML · CSS</p>
</article>
<article class="card project-card">
<p class="card-label">Personal Project</p>
<h3>Study Planner</h3>
<p>A small browser-based planner for organizing subjects and weekly tasks.</p>
<p class="tag-list">HTML · CSS · JavaScript</p>
</article>
<article class="card project-card">
<p class="card-label">Practice Project</p>
<h3>Responsive Landing Page</h3>
<p>A mobile-friendly landing page built to practice layout and visual hierarchy.</p>
<p class="tag-list">CSS Grid · Responsive Design</p>
</article>
</div>
</div>
</section>
<section id="resume" class="section">
<div class="container">
<div class="section-heading">
<p class="eyebrow">Resume</p>
<h2>Quick professional summary</h2>
</div>
<div class="resume-layout">
<article class="card">
<h3>Profile</h3>
<p>
Motivated student seeking opportunities to apply web development and
cloud fundamentals in practical projects.
</p>
</article>
<article class="card">
<h3>Highlights</h3>
<ul class="resume-list">
<li>Currently studying Your Course at Your College Name.</li>
<li>Comfortable creating responsive pages with HTML and CSS.</li>
<li>Building practical experience with JavaScript and AWS.</li>
</ul>
</article>
</div>
</div>
</section>
<section id="contact" class="section section-alt">
<div class="container">
<div class="section-heading">
<p class="eyebrow">Contact</p>
<h2>Let us connect</h2>
</div>
<div class="contact-card">
<p>Email: your.email@example.com</p>
<p>LinkedIn: your-linkedin-profile</p>
<p>GitHub: your-github-username</p>
<p>Location: Your City, Country</p>
</div>
</div>
</section>
</main>
<footer class="site-footer">
<div class="container">
<p>Copyright <span id="current-year"></span> Your Name. Built with HTML, CSS, JavaScript, and AWS.</p>
</div>
</footer>
</body>
</html>
How Is the Page Structured?
- The document head links style.css so the same structure can receive its visual design later.
- The deferred script.js link lets the browser finish reading the page before the script runs.
- Each navigation link points to the matching section identifier.
- The current-year span gives JavaScript a precise place to insert the year later.
Open the unstyled page
A local test proves the portfolio works without creating an AWS resource. This also gives you a clear baseline for judging the page in its current form.
Before you open the page, do you think structure alone creates a clear visual hierarchy?
- Save index.html by pressing Ctrl+S.
- Open File Explorer from the Windows taskbar.
- Navigate to C:\Users\<your-windows-username>\Documents\portfolio-website.
- Use the context menu for index.html to open the file with Microsoft Edge.
You'll see all seven sections in order. The content works, but the page is plain and difficult to scan.
Why Test Without Styling?
This plain version proves the page structure works independently of CSS. The missing visual hierarchy gives you a concrete problem to solve in the next step.
Page Missing Sections?
Confirm that you saved index.html after pasting the complete file. Unsaved HTML leaves Microsoft Edge showing an older version.
Check that the file begins with <!doctype html> and ends with </html>.
Help me find why my portfolio sections are missing.
Test every navigation target
Each navigation link uses a fragment target such as #projects. The browser scrolls to the section carrying the matching id.
Before you test the links, which part of the page do you expect each one to reach?
- Select each navigation link in the header one at a time.
- Scroll through the page from Home to Contact.
Each link should land on its matching section. Home begins the sequence.
About Me, Education, Skills, Projects, Resume, and Contact follow in that order.
That is a solid first win: your seven-section portfolio now works as one local page. Next, you'll turn this plain structure into a responsive design and add its dynamic year.
Make the Portfolio Responsive and Interactive
Your HTML now gives every portfolio section a clear purpose. However, the unstyled page is difficult to scan and does not present your work professionally.
In this step, you'll use CSS to add visual hierarchy and responsive layouts. You'll also use JavaScript to display the current year in the footer.
In this step, get ready to:
- Add the complete responsive design to style.css.
- Add the current-year enhancement to script.js.
- Test the portfolio at wide and narrow browser widths.
Add the responsive design
A responsive design reshapes the same content for different screen widths. The stylesheet starts with shared colors and spacing before building the header, cards, grids, and mobile layout.
- Return to Visual Studio Code.
- Select style.css in the Explorer sidebar.
- Add the color system and page foundation by pasting this code:
:root {
--background: #f8fafc;
--surface: #ffffff;
--surface-alt: #eef2ff;
--text: #172033;
--muted: #5f6b7a;
--primary: #3157d5;
--primary-dark: #203ea3;
--border: #dce3ef;
--header: #111827;
--shadow: 0 18px 45px rgba(23, 32, 51, 0.1);
}
* {
box-sizing: border-box;
}
html {
scroll-behavior: smooth;
}
body {
margin: 0;
background: var(--background);
color: var(--text);
font-family: "Segoe UI", Arial, sans-serif;
line-height: 1.6;
}
What does this code do?
- The custom properties keep the portfolio's colors and shadow in one place.
- The universal selector makes element dimensions include their padding and borders.
- The page foundation removes the default body margin and sets readable typography.
- Save style.css with Ctrl+S.
- Switch back to Microsoft Edge.
- Refresh the portfolio.
You should see a pale page background with darker text and updated typography. The smooth scrolling behavior also applies when you use the navigation links.
Still seeing the original page style?
Confirm that you saved style.css inside the existing portfolio-website folder. Check that index.html still links to the same stylesheet filename.
Help me find why my stylesheet is not changing the page.
- Return to style.css.
- Add the main header layout below the existing code by pasting:
a {
color: inherit;
}
.container {
width: min(1100px, calc(100% - 2rem));
margin-inline: auto;
}
.site-header {
position: sticky;
top: 0;
z-index: 10;
background: var(--header);
color: #ffffff;
box-shadow: 0 4px 18px rgba(0, 0, 0, 0.18);
}
.header-layout {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
min-height: 72px;
}
How does the header layout work?
The .container class keeps content centered with space at both edges. The sticky header stays visible while a visitor moves between sections.
Flexbox places the portfolio name beside the navigation. The gap preserves space between those areas.
- Save style.css.
- Refresh the portfolio in Microsoft Edge.
You should see a dark header spanning the page. The name and navigation now share one horizontal layout.
Header still looks plain?
Check that the selectors include the leading periods in .site-header and .header-layout. Confirm that both closing braces are present.
Help me troubleshoot the portfolio header styles.
- Return to the end of style.css.
- Style the brand and navigation by adding:
.brand {
font-size: 1.15rem;
font-weight: 800;
text-decoration: none;
white-space: nowrap;
}
.nav-list {
display: flex;
flex-wrap: wrap;
justify-content: flex-end;
gap: 0.35rem 1rem;
margin: 0;
padding: 0;
list-style: none;
}
.nav-list a {
color: #dbe4ff;
font-size: 0.93rem;
text-decoration: none;
}
.nav-list a:hover,
.nav-list a:focus-visible {
color: #ffffff;
}
What changes in the navigation?
The list becomes a flexible row that can wrap when space gets tight. Removing the list markers keeps the navigation compact.
Hover and keyboard focus turn each link white. Visitors get a visible response before following a link.
- Save style.css.
- Refresh the portfolio in Microsoft Edge.
- Move the pointer over a navigation link.
You should see compact navigation links without bullet markers. The link under the pointer should change from pale blue to white.
Navigation still shows bullet markers?
Confirm that list-style: none; sits inside the .nav-list block. Check that the class in index.html uses the same spelling.
Help me fix the navigation list styling.
- Return to the end of style.css.
- Build the hero layout by adding:
.hero {
display: grid;
min-height: 76vh;
align-items: center;
padding: 5rem 0;
background: linear-gradient(135deg, #e8edff 0%, #f8fafc 55%, #dff7f2 100%);
}
.hero-grid {
display: grid;
grid-template-columns: 1.5fr 0.8fr;
align-items: center;
gap: 3rem;
}
.eyebrow {
margin: 0 0 0.5rem;
color: var(--primary);
font-size: 0.82rem;
font-weight: 800;
letter-spacing: 0.12em;
text-transform: uppercase;
}
How does the hero take shape?
The hero uses a two-column grid for the introduction and summary card. The first column receives more room because it contains the main message.
The gradient separates the opening area from the rest of the page. Eyebrow labels create a consistent visual cue above major headings.
- Save style.css.
- Refresh the portfolio in Microsoft Edge.
You should see a spacious hero with a pale gradient. Its introduction and summary card should sit in two columns at a wide browser width.
Hero content remains in one column?
Check that .hero-grid includes both display: grid; and grid-template-columns: 1.5fr 0.8fr;. Confirm that the browser window is wide enough for the desktop layout.
Help me diagnose the hero grid layout.
- Return to the end of style.css.
- Set the heading and introductory text hierarchy by adding:
h1,
h2,
h3,
p {
margin-top: 0;
}
h1 {
max-width: 760px;
margin-bottom: 1rem;
font-size: clamp(2.4rem, 7vw, 4.7rem);
line-height: 1.05;
}
h2 {
font-size: clamp(1.7rem, 4vw, 2.5rem);
line-height: 1.2;
}
h3 {
margin-bottom: 0.6rem;
}
.hero-text,
.lead {
max-width: 720px;
color: var(--muted);
font-size: 1.08rem;
}
Why use fluid heading sizes?
The clamp() values let headings grow with the screen while preserving readable limits. Muted introductory text keeps the main heading visually dominant.
- Save style.css.
- Refresh the portfolio in Microsoft Edge.
You should see a larger main heading with tighter line spacing. Introductory text should appear in a softer gray.
Headings have not changed size?
Check the comma after each selector in the opening group. Confirm that every clamp() value includes its closing parenthesis.
Help me find the heading style error.
- Return to the end of style.css.
- Style the hero actions by adding:
.hero-actions {
display: flex;
flex-wrap: wrap;
gap: 0.8rem;
margin-top: 1.6rem;
}
.button {
display: inline-block;
padding: 0.78rem 1.1rem;
border: 1px solid var(--primary);
border-radius: 0.65rem;
font-weight: 700;
text-decoration: none;
}
.button-primary {
background: var(--primary);
color: #ffffff;
}
.button-primary:hover,
.button-primary:focus-visible {
background: var(--primary-dark);
}
How do links become buttons?
The shared .button class gives both links the same shape and spacing. The primary variation adds the filled color used for the main action.
- Save style.css.
- Refresh the portfolio in Microsoft Edge.
- Move the pointer over View my projects.
You should see two rounded hero actions. The primary button should become darker under the pointer.
Hero links still look like plain text?
Confirm that the shared class selector is .button. Check that the hero links in index.html already include their button classes.
Help me fix the hero button styling.
- Return to the end of style.css.
- Add the secondary button and shared card surfaces by pasting:
.button-secondary {
background: transparent;
color: var(--primary);
}
.hero-card,
.card,
.contact-card {
border: 1px solid var(--border);
border-radius: 1rem;
background: var(--surface);
box-shadow: var(--shadow);
}
.hero-card {
padding: 2rem;
text-align: center;
}
Why share the card styles?
The selector group gives every card the same border, surface, and shadow. This repetition helps the visitor recognize related pieces of content.
The secondary button stays transparent so the primary action remains more prominent.
- Save style.css.
- Refresh the portfolio in Microsoft Edge.
You should see white card surfaces with rounded corners and soft shadows. The Contact me action should have a transparent background.
Cards have no visible surface?
Check that each selector in the shared group ends with a comma except .contact-card. Confirm that the group ends with one complete declaration block.
Help me repair the shared card selector.
- Return to the end of style.css.
- Finish the hero card details by adding:
.avatar {
display: grid;
width: 96px;
height: 96px;
margin: 0 auto 1rem;
place-items: center;
border-radius: 50%;
background: var(--primary);
color: #ffffff;
font-size: 1.7rem;
font-weight: 800;
}
.hero-card p,
.card p,
.contact-card p {
color: var(--muted);
}
What creates the avatar?
The equal width and height create a square. A circular border radius turns that square into an avatar that centers the initials.
Muted card text separates supporting details from headings. The contrast makes each card easier to scan.
- Save style.css.
- Refresh the portfolio in Microsoft Edge.
You should see the initials centered in a blue circle. Supporting card text should use the softer gray color.
Avatar is not circular?
Confirm that the .avatar width and height are both 96px. Check that border-radius: 50%; remains inside the same block.
Help me fix the avatar shape and alignment.
- Return to the end of style.css.
- Add the section spacing and reusable grids by pasting:
.section {
padding: 5rem 0;
scroll-margin-top: 72px;
}
.section-alt {
background: var(--surface-alt);
}
.section-heading {
margin-bottom: 1.8rem;
}
.section-heading h2 {
margin-bottom: 0;
}
.grid {
display: grid;
gap: 1.2rem;
}
.grid-three {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
How do sections become easier to scan?
Generous vertical padding gives each section a clear boundary. Alternating backgrounds separate neighboring sections without adding more markup.
The three-column grid aligns Skills and Projects cards at wide widths. Consistent gaps keep the cards visually connected.
- Save style.css.
- Refresh the portfolio in Microsoft Edge.
You should see more space between sections and alternating pale backgrounds. Skills and Projects should each display three cards across a wide window.
Cards do not form three columns?
Check that .grid sets the grid display. Confirm that .grid-three contains the complete column declaration.
Help me troubleshoot the three-column card grid.
- Return to the end of style.css.
- Style the content cards and resume layout by adding:
.card {
padding: 1.5rem;
}
.card-label,
.tag-list {
color: var(--primary) !important;
font-size: 0.85rem;
font-weight: 800;
}
.skill-list,
.resume-list {
margin-bottom: 0;
padding-left: 1.2rem;
color: var(--muted);
}
.resume-layout {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1.2rem;
}
How does the content hierarchy work?
Card labels and technology tags use the primary color to identify metadata. Skill and resume lists use the muted color so their section headings stay prominent.
The resume uses two equal columns at wide widths. Its profile and highlights receive the same visual weight.
- Save style.css.
- Refresh the portfolio in Microsoft Edge.
You should see blue labels above project details and balanced spacing inside every card. The Resume section should display two cards side by side.
Resume cards remain stacked?
Confirm that .resume-layout uses display: grid;. Check that its column declaration contains two 1fr values.
Help me fix the resume card layout.
- Return to the end of style.css.
- Finish the contact card and footer by adding:
.contact-card {
max-width: 680px;
padding: 1.6rem;
}
.contact-card p:last-child {
margin-bottom: 0;
}
.site-footer {
padding: 1.5rem 0;
background: var(--header);
color: #dbe4ff;
text-align: center;
}
.site-footer p {
margin: 0;
}
What completes the desktop design?
The contact card stays narrow enough to read comfortably. The footer repeats the header color to give the page a clear ending.
- Save style.css.
- Refresh the portfolio in Microsoft Edge.
- Scroll to the Contact section.
You should see the contact details inside a padded white card. A centered dark footer should appear beneath it.
Footer does not match the header?
Check that .site-footer uses var(--header) for its background. Confirm that the custom property remains defined at the top of the file.
Help me fix the contact card or footer styling.
The desktop design now has a clear visual hierarchy. The final CSS rules reshape that layout when the browser becomes narrow.
- Return to the end of style.css.
- Add the first part of the responsive media query by pasting the code below.
- Keep the file open without saving until you add the second part.
@media (max-width: 760px) {
.header-layout {
align-items: flex-start;
flex-direction: column;
padding: 1rem 0;
}
.nav-list {
justify-content: flex-start;
}
.hero {
min-height: auto;
padding: 4rem 0;
}
.hero-grid,
.grid-three,
.resume-layout {
grid-template-columns: 1fr;
}
What does the first responsive part cover?
These rules activate when the viewport is no wider than 760px. The header stacks vertically while each multi-column layout becomes one column.
This first part deliberately remains attached to the closing rules below. Add the next snippet before saving the file.
- Complete the responsive media query by adding this code directly below the first part:
.hero-grid {
gap: 2rem;
}
.section {
padding: 3.5rem 0;
scroll-margin-top: 145px;
}
}
How does the responsive query finish?
The smaller hero gap keeps the opening section compact. The larger scroll margin leaves room for the taller wrapped header when a navigation link moves to a section.
- Save style.css.
- Refresh the portfolio in Microsoft Edge.
- Narrow the browser window to a mobile-like width.
You should see the header stack vertically. The hero, Skills, Projects, and Resume layouts should each collapse to one column.
Layout stays in multiple columns?
Confirm that the media query begins with @media (max-width: 760px). Check that the final closing brace is present after the .section block.
Help me troubleshoot the responsive media query.
✔️ Awesome, I've got everything!
Great. Double check that you saved style.css and that the portfolio responds when you resize Microsoft Edge.
ⓧ I'd like to double check the full code
:root {
--background: #f8fafc;
--surface: #ffffff;
--surface-alt: #eef2ff;
--text: #172033;
--muted: #5f6b7a;
--primary: #3157d5;
--primary-dark: #203ea3;
--border: #dce3ef;
--header: #111827;
--shadow: 0 18px 45px rgba(23, 32, 51, 0.1);
}
* {
box-sizing: border-box;
}
html {
scroll-behavior: smooth;
}
body {
margin: 0;
background: var(--background);
color: var(--text);
font-family: "Segoe UI", Arial, sans-serif;
line-height: 1.6;
}
a {
color: inherit;
}
.container {
width: min(1100px, calc(100% - 2rem));
margin-inline: auto;
}
.site-header {
position: sticky;
top: 0;
z-index: 10;
background: var(--header);
color: #ffffff;
box-shadow: 0 4px 18px rgba(0, 0, 0, 0.18);
}
.header-layout {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
min-height: 72px;
}
.brand {
font-size: 1.15rem;
font-weight: 800;
text-decoration: none;
white-space: nowrap;
}
.nav-list {
display: flex;
flex-wrap: wrap;
justify-content: flex-end;
gap: 0.35rem 1rem;
margin: 0;
padding: 0;
list-style: none;
}
.nav-list a {
color: #dbe4ff;
font-size: 0.93rem;
text-decoration: none;
}
.nav-list a:hover,
.nav-list a:focus-visible {
color: #ffffff;
}
.hero {
display: grid;
min-height: 76vh;
align-items: center;
padding: 5rem 0;
background: linear-gradient(135deg, #e8edff 0%, #f8fafc 55%, #dff7f2 100%);
}
.hero-grid {
display: grid;
grid-template-columns: 1.5fr 0.8fr;
align-items: center;
gap: 3rem;
}
.eyebrow {
margin: 0 0 0.5rem;
color: var(--primary);
font-size: 0.82rem;
font-weight: 800;
letter-spacing: 0.12em;
text-transform: uppercase;
}
h1,
h2,
h3,
p {
margin-top: 0;
}
h1 {
max-width: 760px;
margin-bottom: 1rem;
font-size: clamp(2.4rem, 7vw, 4.7rem);
line-height: 1.05;
}
h2 {
font-size: clamp(1.7rem, 4vw, 2.5rem);
line-height: 1.2;
}
h3 {
margin-bottom: 0.6rem;
}
.hero-text,
.lead {
max-width: 720px;
color: var(--muted);
font-size: 1.08rem;
}
.hero-actions {
display: flex;
flex-wrap: wrap;
gap: 0.8rem;
margin-top: 1.6rem;
}
.button {
display: inline-block;
padding: 0.78rem 1.1rem;
border: 1px solid var(--primary);
border-radius: 0.65rem;
font-weight: 700;
text-decoration: none;
}
.button-primary {
background: var(--primary);
color: #ffffff;
}
.button-primary:hover,
.button-primary:focus-visible {
background: var(--primary-dark);
}
.button-secondary {
background: transparent;
color: var(--primary);
}
.hero-card,
.card,
.contact-card {
border: 1px solid var(--border);
border-radius: 1rem;
background: var(--surface);
box-shadow: var(--shadow);
}
.hero-card {
padding: 2rem;
text-align: center;
}
.avatar {
display: grid;
width: 96px;
height: 96px;
margin: 0 auto 1rem;
place-items: center;
border-radius: 50%;
background: var(--primary);
color: #ffffff;
font-size: 1.7rem;
font-weight: 800;
}
.hero-card p,
.card p,
.contact-card p {
color: var(--muted);
}
.section {
padding: 5rem 0;
scroll-margin-top: 72px;
}
.section-alt {
background: var(--surface-alt);
}
.section-heading {
margin-bottom: 1.8rem;
}
.section-heading h2 {
margin-bottom: 0;
}
.grid {
display: grid;
gap: 1.2rem;
}
.grid-three {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.card {
padding: 1.5rem;
}
.card-label,
.tag-list {
color: var(--primary) !important;
font-size: 0.85rem;
font-weight: 800;
}
.skill-list,
.resume-list {
margin-bottom: 0;
padding-left: 1.2rem;
color: var(--muted);
}
.resume-layout {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1.2rem;
}
.contact-card {
max-width: 680px;
padding: 1.6rem;
}
.contact-card p:last-child {
margin-bottom: 0;
}
.site-footer {
padding: 1.5rem 0;
background: var(--header);
color: #dbe4ff;
text-align: center;
}
.site-footer p {
margin: 0;
}
@media (max-width: 760px) {
.header-layout {
align-items: flex-start;
flex-direction: column;
padding: 1rem 0;
}
.nav-list {
justify-content: flex-start;
}
.hero {
min-height: auto;
padding: 4rem 0;
}
.hero-grid,
.grid-three,
.resume-layout {
grid-template-columns: 1fr;
}
.hero-grid {
gap: 2rem;
}
.section {
padding: 3.5rem 0;
scroll-margin-top: 145px;
}
}
Add the JavaScript enhancement
The footer already contains an empty element with the ID current-year. JavaScript can read the visitor's local date and place its four-digit year inside that element.
- Return to Visual Studio Code.
- Select script.js in the Explorer sidebar.
- Add the current-year behavior by pasting:
"use strict";
const currentYear = document.getElementById("current-year");
currentYear.textContent = new Date().getFullYear();
What does this code do?
- The currentYear constant stores the footer element whose ID is current-year.
- The date expression reads the current year from the visitor's device.
- Assigning that value to textContent fills the empty footer span.
- Save script.js with Ctrl+S.
- Refresh the portfolio in Microsoft Edge.
- Scroll to the footer.
You should see the current four-digit year between Copyright and Your Name. That visible value confirms the browser loaded script.js successfully.
Footer year remains empty?
Confirm that the ID uses the exact spelling current-year in both files. Check that script.js is saved in the same folder as index.html.
Help me debug the missing footer year.
✔️ Awesome, I've got everything!
Excellent. Your footer now updates its year from the visitor's device whenever the page loads.
ⓧ I'd like to double check the full code
"use strict";
const currentYear = document.getElementById("current-year");
currentYear.textContent = new Date().getFullYear();
Test before deployment
A responsive page needs to remain readable between its widest and narrowest layouts. This final check confirms that styling, navigation, and JavaScript work together before the files leave your computer.
Before you test, how do you expect the cards and navigation to respond as the Microsoft Edge window becomes narrow?
- Return to the portfolio in Microsoft Edge.
- Refresh the page.
- Narrow the browser window to a mobile-like width.
You should see the navigation wrap cleanly and the project cards form one column. Text should remain readable with no horizontal scrollbar.
- Widen the Microsoft Edge window to a desktop-like width.
- Select the Home navigation link.
- Select the Projects navigation link.
- Select the Resume navigation link.
- Select the Contact navigation link.
- Scroll to the footer.
Each navigation link should land on its matching section. The wide layout should restore multiple columns while the footer continues to show the current four-digit year.
Something breaks at a narrow width?
Compare the media query at the bottom of style.css with the full-code tab. A missing closing brace can prevent every responsive rule from applying.
Help me debug the final responsive portfolio check.
That's the local portfolio finished: it now has a professional layout, responsive cards, working navigation, and a dynamic footer year. Next, you'll store these three files in a private Amazon S3 bucket.
Store the Website in a Private S3 Bucket
Your portfolio now works locally. Files on your computer still have no address that other people can visit.
In this step, you'll store the three site files in a private Amazon S3 bucket. An anonymous browser check will reveal whether the privacy settings hold.
In this step, get ready to:
- Create a private S3 bucket with the required ownership controls.
- Upload the three portfolio files to the bucket root.
- Prove that anonymous visitors cannot read the files directly from S3.
Create the private bucket
An S3 bucket is a named container for stored objects. Its Region determines where AWS stores those objects.
The Bucket owner enforced setting disables access control lists for ownership. Block Public Access preserves the bucket's restrictive access settings.
Keep S3 Costs Small
Creating this bucket can incur usage-based S3 storage charges. S3 requests can also incur charges.
This lab uses only three small files. The cleanup section gives you options for removing the resources when you finish.
- Keep only index.html, style.css, and script.js in the bucket.
- Leave versioning disabled.
- Leave logging disabled.
- Leave Static website hosting disabled.
- In the AWS Management Console from earlier, open Amazon S3 using the S3 console.
- Choose the Region selector.
- Select US East (N. Virginia).
- Select General purpose buckets.
- Choose Create bucket.
- Enter a globally unique lowercase name without periods in Bucket name.
- Save the exact bucket name for later: portfolio-yourname-123456.
Choose a Unique Name
S3 bucket names must be globally unique. Add your name plus a short number if your first choice is already in use.
- Keep Object Ownership set to Bucket owner enforced.
- Keep all four Block Public Access settings enabled.
- Leave the remaining settings at their defaults.
- Choose Create bucket.
- Confirm that General purpose buckets lists portfolio-yourname-123456.
Your private storage container is ready. The bucket now has the ownership settings needed for the website files.
Upload the portfolio files
Each file uploaded to S3 becomes an object. Keeping the three objects together at the bucket root preserves the relative file references in index.html.
The filenames must remain index.html, style.css, and script.js. Those names match the stylesheet link plus the deferred script reference in your HTML.
- Select portfolio-yourname-123456 from General purpose buckets.
- Select the Objects tab.
- Choose Upload.
- Choose Add files.
- Return to the portfolio-website folder from earlier in the Windows file picker.
- Select the three files named index.html, style.css, and script.js.
- Choose Upload.
- Wait for the Upload: status page to show success for all three objects.
That upload puts the complete portfolio into private cloud storage. All three local files now have matching S3 objects.
- Return to the bucket's Objects tab.
You should see index.html, style.css, and script.js at the bucket root.
Missing One of the Files?
- Return to Upload if one of the three filenames is missing.
- Select the missing file from the portfolio-website folder.
- Confirm that the filename matches the local file exactly.
Ask for help if the upload status does not show success: Help me troubleshoot an Amazon S3 console upload that did not add all three website files to the bucket root.
Confirm that direct access is blocked
Your signed-in console identity can manage the bucket. An InPrivate browser window has no AWS session, which makes it useful for testing anonymous access.
This test isolates the storage boundary from your approved identity. The result shows what an ordinary visitor can reach through the direct S3 address.
- Select the bucket's Permissions tab.
- Confirm that Block public access (bucket settings) shows On.
- Confirm that Object Ownership shows Bucket owner enforced.
Before you test, do you expect the anonymous request to open your portfolio or reach a blocked response?
- Open an InPrivate window in Microsoft Edge.
- Enter https://portfolio-yourname-123456.s3.amazonaws.com/index.html in the address bar.
You'll see 403 Forbidden or AccessDenied instead of the portfolio. That blocked page is the security win you were aiming for.
Why This Failure Matters
The AWS console request uses your approved identity. The InPrivate request has no permission to read the private object.
The access-denied response proves that uploading the website did not make the bucket public. Your storage boundary is working.
- Keep all four Block Public Access settings enabled.
- Leave the bucket without a public-read ACL or public bucket policy.
Seeing a Different Result?
- Compare the bucket name in the browser address with portfolio-yourname-123456.
- Confirm that index.html appears in the bucket's Objects tab.
- Confirm that all four Block Public Access settings remain enabled.
Ask for help with the result you see: Help me diagnose why my private Amazon S3 object test is not returning an access-denied response.
You've stored the complete portfolio without exposing the files directly. Next, you'll give visitors a secure HTTPS path to this private origin.
Deliver the Private Website Through CloudFront
Your portfolio is now stored in a private Amazon S3 bucket. The blocked object URL proves that anonymous visitors cannot read its files.
Amazon CloudFront now becomes the public HTTPS entry point. Origin Access Control gives only this distribution permission to read the private files.
In this step, get ready to:
- Create a CloudFront distribution that can read the private S3 bucket through OAC.
- Configure the distribution to serve index.html at its root over HTTPS.
- Compare the public CloudFront page with the blocked S3 object URL.
Create the distribution with OAC
A CloudFront distribution is a CDN configuration that delivers your files through edge locations. OAC signs requests from CloudFront to the private S3 origin.
Review the cost settings
This action creates a usage-based CloudFront resource. The pay-as-you-go allowance includes 1 TB of data transfer out each month.
It also includes 10,000,000 HTTP/HTTPS requests each month. Usage above either allowance is billed.
Paid extras stay outside this project. These extras include AWS WAF, logging, Origin Shield, custom domains, and dedicated-IP SSL.
- Return to the AWS Management Console from earlier.
- Open Amazon CloudFront at https://console.aws.amazon.com/cloudfront/v4/home.
- Choose Create distribution.
The distribution setup flow is now ready for your website details.
- Enter portfolio-distribution in Distribution name.
- Choose Single website or app.
The wizard now identifies this resource as a single website distribution.
- Choose Next.
- Choose Next on the following screen.
You'll reach the origin configuration for the files CloudFront needs to serve.
- Choose Amazon S3 for Origin type.
- Choose Browse S3.
The bucket picker now lists the S3 buckets available to your approved identity.
- Select the private portfolio bucket from Step 4.
- Choose Use recommended origin settings.
The origin summary now points to your private portfolio bucket with the recommended access configuration.
How does OAC keep S3 private?
OAC lets CloudFront sign each request to the regular S3 origin. Anonymous browser requests still lack permission to read the objects directly.
CloudFront creates the OAC when you use the recommended origin settings. It also updates the S3 bucket policy for this distribution.
- Choose pay-as-you-go pricing.
- Keep AWS WAF security protections unenabled.
The selected pricing path keeps the distribution focused on the private portfolio delivery flow.
- Leave the remaining optional settings at their defaults.
- Choose Create distribution.
CloudFront now deploys the distribution across its network. The page can remain on Deploying while that work finishes.
- Wait until Last modified changes from Deploying to a date and time.
Your distribution now exists. Its OAC can request the three website objects through the updated bucket policy.
Distribution creation did not complete?
- Confirm that the selected origin is the regular private S3 bucket from Step 4.
- Confirm that your approved AWS identity has permission to create the distribution and update the bucket policy.
- Return to the origin settings if the recommended OAC configuration was skipped.
Ask for help with a CloudFront distribution that will not deploy.
Configure the root page and HTTPS
A default root object tells CloudFront which file to serve when a visitor opens the domain without a file path. The viewer protocol policy moves browser traffic onto an encrypted connection.
These controls sit on two separate tabs, so the split can feel fiddly. Configure the root file on General before updating the default behavior.
- Select the distribution named portfolio-distribution.
- Open General.
The General tab shows the distribution-wide settings for the portfolio.
- Choose Edit.
- Enter index.html in Default root object without a leading slash.
The root object field now points to the uploaded portfolio page.
- Choose Save changes.
- Wait until Last modified shows a date and time again.
The distribution now lists index.html as its default root object.
- Open Behaviors.
- Select the default behavior.
The selected behavior controls how visitor requests reach this S3 origin.
- Choose Edit.
- Set Viewer protocol policy to Redirect HTTP to HTTPS.
The behavior now sends unencrypted visitor requests toward the secure HTTPS address.
- Choose Save changes.
- Wait until Last modified shows a date and time again.
This second deployment can also take a while because CloudFront must publish the behavior change.
Why do both settings matter?
- The Default root object setting serves index.html when the URL ends at the domain root.
- The Redirect HTTP to HTTPS policy protects the browser connection with HTTPS.
- An HTTP redirect produces one HTTP request plus the resulting HTTPS request.
Test both access paths
The strongest proof comes from comparing the CloudFront address with the direct S3 object address. One path should deliver the portfolio through OAC.
Before you test both paths, which address do you expect to load the portfolio? Which address should remain blocked?
- On the distribution's General tab, record the Distribution domain name here: d111111abcdef8.cloudfront.net.
- In Microsoft Edge, enter https:// followed by d111111abcdef8.cloudfront.net and / in the address bar.
You'll see the fully styled portfolio at the root address. The address bar uses HTTPS.
- Select the Projects link in the portfolio header.
Microsoft Edge moves to the Projects section with its three styled project cards.
- Scroll to the portfolio footer.
You'll see the current four-digit year in the copyright line. This confirms that script.js loaded through CloudFront.
That's the secure delivery path working. Visitors can now reach your complete portfolio through CloudFront.
- Return to the InPrivate browser window from Step 4.
- Refresh the direct S3 index.html object URL.
You'll still see 403 Forbidden or AccessDenied. Together, these results prove that CloudFront is the public delivery path while S3 remains private.
Portfolio missing through CloudFront?
- Return to General if the root URL fails while /index.html works.
- Confirm that Default root object contains index.html without a leading slash.
- Review the distribution's S3 origin if CloudFront reports denied access.
- Confirm that style.css and script.js remain under Objects at the bucket root if the page lacks styling or the year.
Ask for help with a private S3 portfolio that does not load through CloudFront.
Your portfolio is live over HTTPS through CloudFront. Its source files remain protected inside the private S3 bucket.
Secret mission
Publish an Update and Refresh the Cache
Your portfolio is already live through Amazon CloudFront, but a cached page can remain unchanged after you update its Amazon S3 object. In this challenge, you will publish a fourth Secure AWS Portfolio card and invalidate only `/index.html` so visitors receive the new page.
Clean Up Your Resources
Clean Up Your Resources
Your live portfolio uses Amazon S3 storage and Amazon CloudFront delivery, so usage-based charges can apply. Decide whether to keep your resources running, pause public delivery, or delete everything.
Cost warning
Keeping the site online leaves S3 storage and requests subject to account pricing. CloudFront traffic above the account's monthly allowance is billed.
Pausing stops CloudFront delivery charges. Your S3 files remain stored and can still incur storage charges.
Deleting removes the distribution and bucket. It prevents further charges from these resources.
Resources you used:
- Amazon CloudFront distribution portfolio-distribution using Origin Access Control (OAC) to reach the private origin.
- Private Amazon S3 bucket portfolio-yourname-123456 containing index.html, style.css, and script.js.
- Local Windows folder C:\Users\<your-windows-username>\Documents\portfolio-website containing the complete source files.
Keep everything running
No action is needed. Choose this if you want visitors to keep accessing your portfolio through its HTTPS CloudFront address.
- Your CloudFront URL remains available to visitors.
- Your S3 bucket remains private from anonymous direct requests.
- Your local source remains in C:\Users\<your-windows-username>\Documents\portfolio-website.
- S3 storage and requests remain subject to account pricing.
- CloudFront traffic above the account's monthly allowance can incur charges.
Pause - I'll come back to this later
Disable public delivery while keeping the private bucket and local files. CloudFront needs time to deploy this change, so a temporary Deploying status is expected.
- Return to the Amazon CloudFront console from earlier.
- Select portfolio-distribution.
- Choose Disable.
- Confirm with Yes, Disable.
- Wait until Last modified updates to a date and time.
Your public portfolio is now offline through CloudFront. The private S3 files and local source remain ready for later.
What Stays Billable?
A disabled distribution does not accrue CloudFront charges. The S3 bucket continues to incur any applicable storage charges until you delete it.
Delete - I don't want to use this again
This option permanently removes the cloud deployment and local source folder. The order below closes the public delivery path before removing its private origin.
Disable and delete CloudFront
- Return to the Amazon CloudFront console from earlier.
- Select portfolio-distribution.
- Choose Disable.
- Confirm with Yes, Disable.
- Wait until Last modified updates to a date and time.
- Choose Delete.
- Confirm the deletion in the prompt.
You should no longer see portfolio-distribution in your list of distributions.
Empty the S3 bucket
Emptying the bucket permanently removes all three deployed website objects. Your local files remain available until the final cleanup below.
- Return to the Amazon S3 console from earlier.
- Open General purpose buckets.
- Select portfolio-yourname-123456.
- Choose Empty.
- Enter portfolio-yourname-123456.
- Choose Empty.
The bucket should now contain no objects.
Delete the S3 bucket
- Return to General purpose buckets.
- Select the empty portfolio-yourname-123456 bucket.
- Choose Delete.
- Enter portfolio-yourname-123456.
- Choose Delete bucket.
You should no longer see portfolio-yourname-123456 under General purpose buckets.
Delete the local folder
Deleting the local folder removes your final copy of the portfolio source. Keep a separate copy if you want to reuse the site later.
- Close Visual Studio Code.
- Launch File Explorer through Windows search.
- Browse to C:\Users\<your-windows-username>\Documents.
- Select portfolio-website.
- Press Shift+Delete to permanently delete the selected folder.
- Confirm the deletion in the Windows prompt.
You should no longer see portfolio-website in your Documents folder. That's the cleanup complete.
Still See a Resource?
Wait for CloudFront to finish deploying its disabled state before trying to delete the distribution. Confirm that the S3 bucket is empty before deleting it.
If Windows keeps the local folder open, close any editor or File Explorer window using it. Help me safely finish this cleanup.
Nice Work!
Nice Work!
You did it. Your responsive portfolio now loads over HTTPS through Amazon CloudFront. Its Amazon S3 files stay private behind Origin Access Control.
You've learned how to:
- Built a semantic single-page portfolio with all seven required sections. Added a current footer year with JavaScript.
- Created a responsive CSS layout. Confirmed that the project cards collapse to one column at narrow widths.
- Delivered the portfolio over HTTPS through Amazon CloudFront. Kept anonymous access to Amazon S3 blocked through OAC.
- Secret Mission: Published the fourth Secure AWS Portfolio card. Refreshed the live page with a targeted CloudFront invalidation for /index.html.
Ready to quiz yourself?