PS Bootcamp Lab Deployment Guide

Deploy the Cloudflare PS Bootcamp Lab environment

This page is the gateway for the Cloudflare PS Bootcamp Zero Trust and PS Bootcamp Application Security course labs.

This will provision three virtual machines and the AcmeCorp web stack in your own AWS account, in about 10 minutes. The labs themselves run in your Cloudflare dashboard account, which must carry the Zero Trust Premier Bundle entitlement. As a Cloudflare partner, you grant that to yourself through the Not For Resale (NFR) program.

Read the requirements below before you run it.

Is this automated installer right for you?

This installer only works on a fresh, empty AWS sandbox account. If you want to deploy the lab into a pre-existing AWS account that already has your own work in it, the installer's preflight will hard-block the deploy with no override. That's deliberate. It prevents the lab from conflicting with or damaging your existing infrastructure.

If you'd rather use your own pre-existing AWS account (or your own VMs, or your own non-AWS environment), stop here and follow the manual setup guide instead. It walks you through the same lab environment, but you build it yourself in whatever cloud or virtualization platform you choose.

Required: these conditions must be satisfied before attending class.

Each row below is required. The AWS account row is enforced by software. The bootstrap's preflight refuses to deploy without it. The two Cloudflare rows are not enforced, but the labs themselves fail in class if either is missing. Line all three up before you start the installer.

Requirement Why & how to get it
A Cloudflare Enterprise account with the Zero Trust Premier Bundle Premier Bundle is one entitlement that turns on everything the labs use: Access, Browser Isolation, CASB, Device management, DEX, DLP, Gateway, Internal DNS, Mesh, and Tunnel. Request from your Cloudflare account team or partner manager. Lead time can be weeks, so start early.
A registered public domain added to that Cloudflare account as an Enterprise zone Used from Lab 2 onward to publish services like web.yourdomain.com, ssh.yourdomain.com, intranet.yourdomain.com. Any registrar works; the domain just needs to be real, publicly resolvable, and have its nameservers pointing to Cloudflare on the Enterprise plan.
A fresh, empty AWS account The Terraform deploys three VPCs and three EC2 instances into your AWS account. The bootstrap's preflight hard-blocks the deploy with no override if the account has any of: existing resources (EC2, S3, IAM users, custom IAM roles, RDS, Lambda, load balancers, non-default VPCs); AWS Organizations membership; Transit Gateways, VPC peering, Direct Connect, or VPNs; or a production-sounding account alias (prod, corp, main, shared, enterprise, live).

1. Sign in to your Cloudflare account and verify everything works

The installer doesn't need Cloudflare access. It only talks to AWS. But the labs themselves (Labs 1 through 5) all happen in the Cloudflare Zero Trust dashboard. If your Cloudflare account is missing an entitlement or pointed at the wrong subscription, you'll discover it at 9 AM on Day 1 and lose hours. Run through the checklist below before the installer to confirm everything is ready.

  1. Open a new browser tab to dash.cloudflare.com and sign in with the Cloudflare account that has your Enterprise + Zero Trust Premier Bundle entitlements (the one you set up per the requirements at the top of this page).
  2. If your login can access more than one Cloudflare account, you'll land on the Accounts page listing each one and can select the correct one.
    • You'll usually only see this on a first sign-in or in Incognito. Otherwise, the dashboard drops you back where you last were, so use the account name in the left sidebar to switch.
    • Double-check the account ID in the URL or in the account profile. If your login has prod and lab accounts, this is the most common student mistake.

Then click through each of the following pages and confirm they load without an "Upgrade to Zero Trust" or "Get started with..." upsell screen:

Where to clickWhat should happen
Zero Trust dashboard (left sidebar → Zero Trust) Overview page renders with stats / widgets. Not an upsell page.
Zero Trust → Overview tab → Account details card (right side of the page) The card shows three values, each with a copy icon:
  • Team domain: the full https://<team-name>.cloudflareaccess.com URL
  • Team name
  • Account ID
  • Copy the team domain so you have it for the labs
Insights & Logs → Digital experience → Device overview tab DEX page loads. Not an upsell page.
Insights & Logs → Logs Page renders with 8 dataset cards: Admin activity logs, Access authentication logs, DNS query logs, Network logs, HTTP request logs, SSH command logs, SCIM provisioning logs, and Posture logs.
Access controls → Applications Applications list loads (empty is fine; you'll add apps in Lab 2+).
Traffic policies → Firewall policies Three tabs visible: DNS, Network, HTTP. No upsell.
Data loss prevention → Profiles Page shows the 12 predefined profiles (Credit Card Numbers, Social Security Numbers, etc.).
Browser isolation → Browser isolation settings Browser Isolation settings page loads. Not an upsell page.

Domains → Overview

Click "Back to <your account>" at the top of the left sidebar to get back out to the main dashboard.

If you have already registered a public domain you want to use for this class, it appears as a row in the table. If you have not created and registered a domain yet in the Cloudflare dashboard, this would be the time to do that. Some domains are as low as ~$5/year.

If any of those rows shows an upsell page or fails to load, stop here and contact your Cloudflare account team. An incomplete entitlement set is much easier to fix a week before class than during it.

2. Access AWS

2a. Create a new AWS Account

Skip this step if you already have an AWS account you want to use (it must be blank and not part of an AWS organization), and move on to step 2b.

If you don't have an AWS account yet, create one first. It takes about 10 minutes:

A credit card is required for signup, but new accounts get $100 in AWS Free Tier credits and 6 months of free access, enough for a multi-day lab. If you don't convert to a paid plan when the 6 months are up, AWS closes the account, so there's no risk of forgotten charges piling up.

  1. Open aws.amazon.com/account.
  2. Click Create an AWS Account.
  3. Enter an email address. A new free Gmail account can help keep this sandbox separate from any other AWS accounts you might have.
  4. When prompted for an AWS account name, enter cloudflare-lab. (AWS requires a name here. You can use this name, but it's only a suggestion.)
  5. Go through the rest of the signup.
  6. Choose the Basic Support - Free plan.
  7. When signup completes, sign in to console.aws.amazon.com as the root user.
  8. Skip Step 2b.

Brand-new AWS accounts can take up to 24 hours to fully activate, though most are usable in under 10 minutes. If preflight later complains about permissions or an API call fails inexplicably, give the account 30 minutes to finish provisioning and try again.

2b. If you already have an AWS account you want to use

3. Create an access key for the root user

  1. Click your account name in the top-right corner of the console.
    • Copy your account number to your clipboard. In just a moment you will paste it into a CSV file alongside your access key.
    • Click on Security credentials.
    AWS Console account dropdown menu, with Security credentials in the list of links
  2. Scroll to the Access keys section and click Create access key.
    My security credentials page showing the Access keys section and the Create access key button
  3. AWS opens a two-step wizard. The first step is called "Alternatives to root user access keys" and tries to talk you out of creating one.
    • For a 4-day lab environment this should be fine, so just check the "I understand" confirmation box and click Create access key.
    • If you would rather go through the process of creating an IAM user to limit credentials, feel free to do that at this time, but there is no walkthrough in this guide for that process.
    Alternatives to root user access keys page with the I understand checkbox ticked and the Create access key button visible
  4. The second step is called "Retrieve access key".

    Click the "Download .CSV file" button in the bottom-right corner.

    Retrieve access key page showing both values with the Download .csv file button in the bottom-right

    After it downloads, open the file in a text editor. It will look something like the image below:

    • The first value (before the comma) is the Access key ID; the value after the comma is the Secret access key. You'll need both of these values in a few moments.
    • Click Done.
    • Paste the account number from your clipboard into this text file for use in just a moment.
    rootkey.csv opened in a text editor, showing Access key ID and Secret access key separated by a comma

The secret access key cannot be retrieved a second time. If you lose it, delete this key in IAM and create a new one.

The rest of this page is available in two versions. Pick the one that matches how you like to work.

4. Run the installer

Pick your operating system in the tab strip below.

Tested on Windows 11 and Windows Server 2022 with PowerShell 5.1+.

  1. Open PowerShell as Administrator.
  2. Run the installer:
    irm https://psbc-lab-deploy.cloudflareacademy.com/win | iex
  3. When asked "Have you already deployed this lab to AWS before?", type N (you're deploying for the first time).
  4. Type yes at the disclosure.
  5. If you already have a Python 3 version other than 3.13.x installed, the installer warns you and asks Install pinned 3.13 / Continue with existing.
    • I is the preferred choice.
  6. Answer the prompts:
    • Account ID
    • AWS Access Key ID
    • Secret Access Key
    • Region (default us-east-1).
      • Press Enter to accept it unless you want to change the region.
    • Instance types
      • Just hit Enter to accept the defaults.
  7. Wait ~10-20 minutes for preflight + deploy + smoke test.
Troubleshooting Deployment
What you seeFix
irm : The term 'irm' is not recognizedYou're in Command Prompt, not PowerShell. Open PowerShell as Administrator instead.
The term 'winget' is not recognizedwinget isn't installed. Run this in your elevated PowerShell to install it, then re-run the installer:
if (Get-Command winget -ErrorAction SilentlyContinue) { Write-Host 'winget is already installed. No action needed.' -ForegroundColor Green } else { [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $tmp = "$env:TEMP\winget-setup"; New-Item -ItemType Directory -Force -Path $tmp | Out-Null; Invoke-WebRequest -Uri https://aka.ms/Microsoft.VCLibs.x64.14.00.Desktop.appx -OutFile "$tmp\VCLibs.appx"; Invoke-WebRequest -Uri https://aka.ms/getwinget -OutFile "$tmp\winget.msixbundle"; Add-AppxPackage -Path "$tmp\VCLibs.appx"; Add-AppxPackage -Path "$tmp\winget.msixbundle"; Write-Host 'winget installed. Close and reopen PowerShell, then re-run the installer.' -ForegroundColor Green }
python not recognized after installClose PowerShell, open a new elevated window, re-run from step 2.
The SSL connection could not be establishedOld PowerShell defaulting to weak encryption. Paste this in the same window, press Enter, then re-run the installer:
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
Accidentally closed PowerShell mid-installReopen elevated PowerShell, redo steps 1-2. Already-installed tools are skipped.

Tested on macOS 13+ (Ventura and later) on both Apple Silicon (M1/M2/M3/M4) and Intel.

  1. Open Terminal.
  2. If you don't have Homebrew, install it first, then close and reopen Terminal before continuing:
    /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
  3. Run the installer:
    curl -fsSL https://psbc-lab-deploy.cloudflareacademy.com/mac | bash
  4. When asked "Have you already deployed this lab to AWS before?", type N (you're deploying for the first time).
  5. Type yes at the disclosure.
  6. If you already have a Python 3 version other than 3.13.x installed, the installer warns you and asks Install pinned 3.13 / Continue with existing.
    • I is the preferred choice.
  7. Answer the prompts:
    • Account ID
    • AWS Access Key ID
    • Secret Access Key
    • Region (default us-east-1).
      • Press Enter to accept it unless you want to change the region.
    • Instance types
      • Just hit Enter to accept the defaults.
  8. Wait ~10-20 minutes for preflight + deploy + smoke test.
Troubleshooting Deployment
What you seeFix
command not found: brew immediately after installing HomebrewClose and reopen Terminal. Homebrew adds itself to PATH only for new shells. Then re-run the install command.
xcrun: error: invalid active developer pathPaste xcode-select --install, click Install on the pop-up, wait, then re-run.
Pop-up asking for your passwordNormal. Type your Mac login password (characters hidden) and press Enter.
Accidentally closed Terminal mid-installReopen Terminal, re-run the install command. Already-installed tools are skipped.

Ubuntu 22.04 / 24.04 / Debian 12+ tested. Other Linux/glibc distros likely work. The installer fetches a prebuilt CPython 3.13.14 from python-build-standalone, so distro Python version doesn't matter.

  1. Open a terminal.
  2. Run the installer (automatically checks for curl and prompts to install if needed):
    command -v curl >/dev/null 2>&1 || { echo "curl is required. Install it now? (y/n)"; read -r answer; [[ "$answer" == "y" ]] && sudo apt update && sudo apt install -y curl || exit 1; }; curl -fsSL https://psbc-lab-deploy.cloudflareacademy.com/linux | bash
  3. If prompted about curl, type y and press Enter, then enter your sudo password.
  4. When asked "Have you already deployed this lab to AWS before?", type N (you're deploying for the first time).
  5. Type yes at the disclosure.
  6. If you already have a Python 3 version other than 3.13.x installed, the installer warns you and asks Install pinned 3.13 / Continue with existing.
    • I is the preferred choice.
  7. Answer the prompts:
    • Account ID
    • AWS Access Key ID
    • Secret Access Key
    • Region (default us-east-1).
      • Press Enter to accept it unless you want to change the region.
    • Instance types
      • Just hit Enter to accept the defaults.
  8. Wait ~10-20 minutes for preflight + deploy + smoke test.
Troubleshooting Deployment
What you seeFix
command not found: curlPaste sudo apt-get update && sudo apt-get install -y curl then re-run.
apt-get not available on this LinuxNot Debian/Ubuntu. Install Python 3 with your distro's package manager, then re-run.
user is not in the sudoers fileAsk an admin to add you to the sudo group, then re-run.
Accidentally closed the terminal mid-installReopen terminal, re-run. Already-installed tools are skipped.
Installation complete

Your lab environment is deployed. The remaining sections are verification (Post Deployment Testing) and an optional permanent-copy walkthrough.

Here's what just happened

What the script did

  • Installed CPython 3.13.14 (the pinned, lab-tested version) via the official python.org installer.
  • Installed CPython 3.13.14 (the pinned, lab-tested version) via brew install python@3.13.
  • Installed CPython 3.13.14 (the pinned, lab-tested version) from a prebuilt python-build-standalone tarball.
  • Downloaded the lab source tarball from this site.
  • Installed AWS CLI v2.
  • Prompted for AWS Access Key ID, Secret Access Key (masked with asterisks), and region (default us-east-1). Wrote them straight to ~/.aws/credentials and ~/.aws/config.
  • Ran the AWS preflight check, which hard-blocks if the account isn't empty.
  • Installed Terraform (only after preflight passes).
  • Ran terraform apply (4-6 min) to create 3 VPCs and 3 EC2 instances. Everything Terraform creates is tracked in state; nothing is provisioned outside it.
  • Smoke-tested both Ubuntu VMs (website on :80, intranet on :8000, and /status on each). Ubuntu A should advertise "origin":"primary" and Ubuntu B "origin":"backup".
  • Printed a post-class reminder: run python setup.py destroy, then delete your root access key from the IAM Console.

What landed in your AWS account

  • 3 VPCs (10.90.0.0/16 Windows, 10.91.0.0/16 Ubuntu A primary, 10.92.0.0/16 Ubuntu B backup), with no peering between any of them.
  • 3 internet gateways, 3 subnets, 3 route tables
  • 3 security groups (RDP and SSH locked to your public IP; HTTP 80 / 8000 on both Ubuntus open to the world until Lab 11)
  • 1 EC2 key pair (shared by both Ubuntus, used by setup.py for emergency SSH access if password authentication fails)
  • 3 EC2 instances:
    • lab-windows: your Windows desktop (Windows Server 2022 at 10.90.0.90).
    • lab-ubuntu-a: the primary Ubuntu origin server (Ubuntu 24.04 at 10.91.0.91).
    • lab-ubuntu-b: the backup Ubuntu origin (Ubuntu 24.04 at 10.92.0.92). Used as the second Load Balancer pool member in the Application Security class; idle during the Zero Trust class.

New AWS accounts get $100 in AWS Free Tier credits and 6 months of free access, enough for a multi-day lab. Tear it all down with python setup.py destroy when class ends. If you don't convert to a paid plan when the 6 months are up, AWS closes the account.

5. Post Deployment Testing

Plug in your lab values

Type your AWS region first. The Console link further down on this page will auto-fill with it, so you can click into the AWS Console and find your VM IPs without typing anything. Saved in your browser (survives reloads).

Where to find your region: in the terminal window where you ran the install command, scroll up. Near the end of the output you'll see a line like aws_region = "us-east-1". Type that value (without quotes) into the box above.

The fastest way to find your VMs in the AWS Console: click the Copy button on the link below, paste it into your browser's address bar, and press Enter. The link was built from the AWS region you typed in above, so it drops you straight onto the EC2 Instances page in the correct region (no menu clicking). Sign in if AWS prompts you.

https://<region>.console.aws.amazon.com/ec2/home?region=<region>#Instances:

Once the page loads, you'll see three instance rows:

  • lab-windows: your Windows desktop.
  • lab-ubuntu-a: the primary Ubuntu origin server.
  • lab-ubuntu-b: the backup Ubuntu origin. Used as the second Load Balancer pool member in the Application Security class; idle during the Zero Trust class.

The Public IPv4 address column on each row is the value you'll paste below.

Don't want to use the link? Sign in to console.aws.amazon.com manually, use the region selector in the top-right to pick your region, then click EC2 → Instances.

Paste each VM's Public IPv4 address from the AWS Console below.

These need to work before day 1 of the class.

  • Once you have all three public IPs, run these nine checks.
  • If any fails, see the Troubleshooting box below.

SSH and RDP tests

1. SSH into Ubuntu A (primary origin)

From a PowerShell window on your laptop:

From Terminal on your laptop:

From any terminal on your laptop:

ssh cloudflare@<ubuntu-ip>

At the password prompt enter #Savetheinternet. You should land at a cloudflare@origin-primary:~$ shell prompt. Type exit to disconnect.

2. SSH into Ubuntu B (backup origin)

From a PowerShell window on your laptop:

From Terminal on your laptop:

From any terminal on your laptop:

ssh cloudflare@<ubuntu-b-ip>

Same password #Savetheinternet as Ubuntu A. You should land at a cloudflare@origin-backup:~$ shell prompt. Type exit to disconnect.

3. RDP into the Windows desktop

Using the built-in Remote Desktop client:

Install Microsoft Remote Desktop from the Mac App Store (free), then connect to the Windows public IP:

Install remmina (sudo apt-get install remmina remmina-plugin-rdp), then connect to the Windows public IP:

mstsc /v:<windows-ip>
<windows-ip>
<windows-ip>

Enter cloudflare / #Savetheinternet at the credentials prompt. You should see the Windows desktop logged in as the lab user.

HTTP tests

4. AcmeCorp website on Ubuntu A (port 80)

Open in your browser:

http://<ubuntu-ip>

Renders the AcmeCorp homepage.

5. AcmeCorp intranet on Ubuntu A (port 8000)

Same VM, different vhost:

http://<ubuntu-ip>:8000

Renders a visibly different intranet page.

6. Ubuntu A /status identifies as primary

Open in your browser, or run from any terminal:

http://<ubuntu-ip>/status

Returns {"status":"healthy","origin":"primary"}.

7. AcmeCorp website on Ubuntu B (port 80)

Open in your browser:

http://<ubuntu-b-ip>

Renders the same AcmeCorp homepage as Ubuntu A: identical content, served from Ubuntu B's nginx.

8. AcmeCorp intranet on Ubuntu B (port 8000)

Same VM, different vhost:

http://<ubuntu-b-ip>:8000

Renders the same intranet page as Ubuntu A.

9. Ubuntu B /status identifies as backup

Open in your browser, or run from any terminal:

http://<ubuntu-b-ip>/status

Returns {"status":"healthy","origin":"backup"}. This "origin" field is what makes the two pool members tell themselves apart at the curl level in the Application Security Lab 5 Load Balancer exercise.

Troubleshooting Tests

Click to expand
What you seeWhyFix
HTTP test: page doesn't load or times out The Ubuntu VM is still running cloud-init (nginx not started yet), OR your public IP has changed since deploy Wait 2-3 more minutes after the installer finished. If still failing, run:
python setup.py update-ip
ssh: connect to host <ip> port 22: Connection timed out Security group doesn't allow your current public IP, OR you're trying to SSH from the Windows VM (different source IP) SSH from your laptop, not from the Windows VM. From your laptop, run this, then retry SSH:
python setup.py update-ip
Connection refused on SSH sshd hadn't finished restarting after cloud-init wrote the password-auth config Wait 30 sec and retry. If still failing, see the break-glass section below.
Permission denied or password rejected The cloudflare user wasn't created yet, OR sshd didn't pick up the password-auth config Break-glass via the ubuntu user (below), then run:
sudo systemctl restart ssh
PowerShell says 'ssh' is not recognized Older Windows without OpenSSH client From elevated PowerShell, run this, then retry:
Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0
RDP: "Your credentials did not work" You typed the password wrong, or domain prefix snuck in Leave the username as just cloudflare (no .\, no domain). Password is #Savetheinternet case-sensitive.

Break-glass: SSH as the ubuntu user via your SSH key. The cloudflare user uses password auth; the ubuntu user has its own key-based path that works even if the password-auth setup is broken. The key is ~/.ssh/id_ed25519, either your existing key (if you already had one) or a fresh one that setup.py generated during install and registered with AWS. The same key authorizes both Ubuntus.

Ubuntu A (primary):

ssh -i $env:USERPROFILE\.ssh\id_ed25519 ubuntu@<ubuntu-ip>
ssh -i ~/.ssh/id_ed25519 ubuntu@<ubuntu-ip>
ssh -i ~/.ssh/id_ed25519 ubuntu@<ubuntu-ip>

Ubuntu B (backup):

ssh -i $env:USERPROFILE\.ssh\id_ed25519 ubuntu@<ubuntu-b-ip>
ssh -i ~/.ssh/id_ed25519 ubuntu@<ubuntu-b-ip>
ssh -i ~/.ssh/id_ed25519 ubuntu@<ubuntu-b-ip>

Once in (either VM), run this to confirm cloud-init finished:

sudo cloud-init status

Then, if password auth still isn't working, restart sshd:

sudo systemctl restart ssh

Optional Commands

Easiest path: just re-run the same one-liner you installed with. It asks "Have you already deployed this lab to AWS before?"; answer y and it auto-detects your AWS account (no account-ID prompt), pulls your lab state down from S3, and drops you into a short menu ([1] Update my IP, [2] Destroy lab). This works from any computer with the same AWS key, even one that never ran the deploy. You never type a file path.

The commands below are the power-user equivalents of those menu choices. Each uses the full path to setup.py, so it works in any shell window in any current directory. No need to cd into the lab folder first. Paste into PowerShell. Paste into Terminal. Paste into any shell.

Reprint the VM IPs, SSH / RDP commands, and every Terraform output:

python "$env:USERPROFILE\cloudflare-psbc-lab\setup.py" show
python3 ~/cloudflare-psbc-lab/setup.py show
python3 ~/cloudflare-psbc-lab/setup.py show

Refresh security groups when your public IP changes:

python "$env:USERPROFILE\cloudflare-psbc-lab\setup.py" update-ip
python3 ~/cloudflare-psbc-lab/setup.py update-ip
python3 ~/cloudflare-psbc-lab/setup.py update-ip

Tear down everything (run this when class ends):

python "$env:USERPROFILE\cloudflare-psbc-lab\setup.py" destroy
python3 ~/cloudflare-psbc-lab/setup.py destroy
python3 ~/cloudflare-psbc-lab/setup.py destroy

6. Walking away with a permanent copy (optional)

cd ~/cloudflare-psbc-lab
git init && git add . && git commit -m "My CF PS Bootcamp lab"
git remote add origin <your-git-host-URL>
git push -u origin main

Any git host works: GitHub, GitLab, Gitee, BitBucket, AWS CodeCommit, self-hosted Gitea.

Set aside about 20 minutes of uninterrupted time. The installer downloads software, asks you a few questions, then builds the lab in AWS. You can't leave it unattended. It pauses for your input at several steps.

4. Run the installer

Pick your operating system in the tab strip below. The walkthrough in each tab explains every click and keystroke.

Step 1: Open PowerShell as Administrator

  1. Click the Start button, the Windows logo at the bottom-left corner of your screen.
  2. Type the word: PowerShell
  3. In the search results you'll see Windows PowerShell (a blue icon with a >_ prompt). Right-click on it. Don't left-click.
  4. From the menu that appears, click Run as administrator.
  5. Windows shows a pop-up asking "Do you want to allow this app to make changes to your device?" Click Yes.
  6. A dark blue window opens. The title bar at the top should say Administrator: Windows PowerShell. The word "Administrator" must be there. If you don't see it, close the window and start over from step 1.

You are now in PowerShell as Administrator. It's a blue window with white text and a blinking cursor. You type commands here and press Enter to run them.

Step 2: Run the installer

  1. Click the Copy button at the right of the box below.
  2. Click in the PowerShell window.
  3. Right-click to paste (or Ctrl+V).
  4. Press Enter.
irm https://psbc-lab-deploy.cloudflareacademy.com/win | iex

The installer first asks "Have you already deployed this lab to AWS before? [y/N]". Type N and press Enter: you are deploying a new lab. (Answer y only when connecting a second computer to a lab you already built. That path auto-detects your AWS account and skips straight to the management menu, covered under Optional Commands below.)

The installer then fills the window with a wall of text explaining what it's about to do. Read it. At the bottom you'll see this question:

Do you understand and agree to the above requirements?

Type the three letters yes and press Enter. If you type anything else (or just press Enter alone), the installer stops without doing anything.

Step 3: Wait, and answer the prompts

After you type yes the installer takes 10-20 minutes. It will:

  1. Install CPython 3.13.14 from python.org (you may see a Windows pop-up asking permission; click Yes). If you already have a Python 3 version other than 3.13.x, the installer prints an ALL-CAPS warning and asks Install pinned 3.13 alongside / Continue with existing. I is the preferred choice. The lab is tested only with 3.13.x.
  2. Download the lab files from this website.
  3. Install the AWS command-line tool (another pop-up; click Yes).
  4. Ask you to confirm the target AWS account ID (paste the 12-digit number from the top-right of the AWS Console).
  5. Prompt for credentials:
    • AWS Access Key ID: paste from your rootkey.csv file or password manager. Echoes as you type.
    • AWS Secret Access Key: same source. Echoes as * per character so it doesn't show on screen.
    • AWS Region: press Enter to accept the default us-east-1, or type a different region code (e.g. us-west-2, eu-west-1).
  6. Auto-detect your laptop's public IP (no confirmation prompt; the security groups will allow RDP/SSH from this IP only).
  7. Run the AWS safety check (preflight). If your AWS account isn't empty, the installer stops here and tells you exactly what it found. Use a fresh account and start over.
  8. Install Terraform (another pop-up; click Yes).
  9. Ask for SSH key location and EC2 instance type defaults. Press Enter at each prompt to accept the default if you're not sure.
  10. Build your lab in AWS (4-6 minutes).
  11. Smoke-tests both Ubuntu VMs: the AcmeCorp website (port 80), intranet (port 8000), and the /status JSON on each (Ubuntu A advertises "origin":"primary", Ubuntu B "origin":"backup").

Important: Do NOT close the PowerShell window while the installer is running. If the screen looks frozen, scroll to the bottom. The installer is probably waiting for you to answer a question. Look for a line ending in : or [y/N] and type your answer there.

Troubleshooting Deployment
What you seeWhat to do
irm : The term 'irm' is not recognized You opened the wrong program. You're in Command Prompt, not PowerShell. Close that window and start over from step 1 above.
The term 'winget' is not recognized winget isn't installed on this machine. Run this in your elevated PowerShell window to install it, then re-run the installer (it skips steps already done):
if (Get-Command winget -ErrorAction SilentlyContinue) { Write-Host 'winget is already installed. No action needed.' -ForegroundColor Green } else { [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $tmp = "$env:TEMP\winget-setup"; New-Item -ItemType Directory -Force -Path $tmp | Out-Null; Invoke-WebRequest -Uri https://aka.ms/Microsoft.VCLibs.x64.14.00.Desktop.appx -OutFile "$tmp\VCLibs.appx"; Invoke-WebRequest -Uri https://aka.ms/getwinget -OutFile "$tmp\winget.msixbundle"; Add-AppxPackage -Path "$tmp\VCLibs.appx"; Add-AppxPackage -Path "$tmp\winget.msixbundle"; Write-Host 'winget installed. Close and reopen PowerShell, then re-run the installer.' -ForegroundColor Green }
A blue/yellow Windows pop-up asks "Do you want to allow this app..." Normal. Click Yes. This happens every time the installer installs new software.
python : The term 'python' is not recognized right after Python installed Windows hasn't refreshed its list of programs in this session. Close PowerShell, open a NEW elevated PowerShell (step 1 again), and re-run the installer. It will skip steps that are already done.
The SSL connection could not be established Old PowerShell defaulting to weak encryption. Paste this in the same window, press Enter, then re-run the installer:
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
A blue full-screen "Windows protected your PC" message SmartScreen doesn't recognize the installer URL yet. Click More info, then Run anyway.
You accidentally closed PowerShell mid-install No harm done. Open a new elevated PowerShell (step 1 again), and run the install command again. The installer detects what's already installed and skips those steps.

Step 1: Open the Terminal app

  1. Press Cmd+Spacebar at the same time. A small search bar appears in the middle of your screen.
  2. Type the word: Terminal
  3. Press Enter.
  4. A small window opens with a white or black background. There's a line of text ending in something like username@MacBook ~ % with a blinking cursor.

That's the Terminal. You type commands here and press Enter to run them.

Step 2: Check whether Homebrew is installed

Homebrew is the macOS package manager. The installer uses it to install Python, the AWS tool, and Terraform. Check whether you have it:

  1. Click the Copy button at the right of the box below.
  2. Click in the Terminal window.
  3. Press Cmd+V to paste, then press Enter.
brew --version

If you see something like Homebrew 4.x.x, you have it. Skip to step 4.

If you see zsh: command not found: brew, you don't have it. Continue to step 3.

Step 3: Install Homebrew (only if step 2 said it was missing)

  1. Click the Copy button next to the long command below.
  2. Paste it into Terminal (Cmd+V) and press Enter.
  3. It will ask for your Mac login password. Type it (you won't see the characters as you type. That's normal) and press Enter.
  4. If your Mac asks to install the Command Line Developer Tools, click Install on the pop-up and wait for it to finish (5-10 minutes).
  5. When Homebrew finishes, it prints two extra commands at the bottom starting with echo >> ~/.zprofile and eval "$(/opt/homebrew/bin/brew shellenv)". Copy and paste those two lines too to add Homebrew to your path.
  6. Close Terminal and open a new Terminal window. Re-run brew --version to confirm it works now.
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

Step 4: Run the installer

  1. Click the Copy button next to the command below.
  2. Click in the Terminal window.
  3. Cmd+V to paste, then press Enter.
curl -fsSL https://psbc-lab-deploy.cloudflareacademy.com/mac | bash

The installer first asks "Have you already deployed this lab to AWS before? [y/N]". Type N and press Enter: you are deploying a new lab. (Answer y only when connecting a second computer to a lab you already built. That path auto-detects your AWS account and skips straight to the management menu, covered under Optional Commands below.)

The installer then shows a disclosure. Read it. At the bottom you'll see:

Do you understand and agree to the above requirements?

Type yes and press Enter.

Step 5: Wait, and answer the prompts

The installer runs for 10-20 minutes. The order:

  1. Installs CPython 3.13 via brew install python@3.13 (if missing). If you have a Python 3 version other than 3.13.x already, the installer prints an ALL-CAPS warning and asks Install pinned 3.13 alongside / Continue with existing. I is the preferred choice. The lab is tested only with 3.13.x.
  2. Downloads the lab source tarball and extracts it to ~/cloudflare-psbc-lab
  3. Installs AWS CLI v2 via brew (if missing)
  4. Asks you to confirm the target AWS account ID (paste the 12-digit number from the top-right of the AWS Console).
  5. Prompts for credentials:
    • AWS Access Key ID: paste from your rootkey.csv file or password manager. Echoes as you type.
    • AWS Secret Access Key: same source. Echoes as * per character so it doesn't show on screen.
    • AWS Region: press Enter to accept the default us-east-1, or type a different region code (e.g. us-west-2, eu-west-1).
  6. Auto-detects your laptop's public IP (no confirmation prompt; the security groups will allow RDP/SSH from this IP only).
  7. Asks for SSH key location and EC2 instance type defaults. Press Enter at each to accept.
  8. Runs the AWS preflight safety check (2-4 minutes on a brand-new AWS account)
  9. Installs Terraform from a static zip on releases.hashicorp.com (only after preflight passes; sudo install to /usr/local/bin/terraform)
  10. Runs terraform apply, which creates the VPCs and EC2 instances (4-6 minutes)
  11. Smoke-tests both Ubuntu VMs: the AcmeCorp website (port 80), intranet (port 8000), and the /status JSON on each (Ubuntu A advertises "origin":"primary", Ubuntu B "origin":"backup")

macOS may ask for your login password during some installs. That's expected. Type your password (you won't see the characters) and press Enter.

Important: Do NOT close the Terminal window while the installer is running. If the screen looks frozen, scroll to the bottom. The installer is probably waiting for you to answer a question.

Troubleshooting Deployment
What you seeWhat to do
command not found: brew You skipped step 3 or didn't open a new Terminal after installing brew. Close Terminal, open a fresh Terminal window, and check again with brew --version.
xcrun: error: invalid active developer path Command Line Tools missing. Paste:
xcode-select --install
press Enter, click Install on the pop-up, wait for it to finish, then re-run the installer.
A pop-up asking for your password during brew install Normal. Type your Mac login password (you won't see the characters) and press Enter.
You accidentally closed Terminal mid-install No harm done. Open Terminal again and re-run the install command. The installer detects what's already installed and skips those steps.

Step 1: Open a terminal

On most Linux distributions you can press Ctrl+Alt+T to open a terminal, or find Terminal (sometimes called Console) in your applications menu.

A window opens with a prompt ending in $ or # and a blinking cursor. You type commands here and press Enter to run them.

Tested on Ubuntu 22.04 and 24.04. Other Debian-based distros (Debian, Mint, Pop!_OS) likely work. RHEL / Fedora / Arch are not auto-supported. The script tells you the install commands but you'd run them yourself.

Step 2: Check that you can use sudo

The installer needs to install software with sudo. Your user must be in the sudo group. Test:

  1. Click the Copy button below.
  2. Paste into terminal (Ctrl+Shift+V) and press Enter.
  3. Enter your login password if prompted (you won't see characters as you type).
sudo -v

If you see no errors, you're good. If you see user is not in the sudoers file, ask your administrator to add you, or use a different account.

Step 3: Run the installer

The installer command below automatically checks if curl is installed. If it's missing, you'll be prompted to install it. Most systems have curl pre-installed.

  1. Click the Copy button below.
  2. Paste in the terminal (Ctrl+Shift+V).
  3. Press Enter.
command -v curl >/dev/null 2>&1 || { echo "curl is required. Install it now? (y/n)"; read -r answer; [[ "$answer" == "y" ]] && sudo apt update && sudo apt install -y curl || exit 1; }; curl -fsSL https://psbc-lab-deploy.cloudflareacademy.com/linux | bash

If you see curl is required. Install it now? (y/n), type y and press Enter. You'll be prompted for your sudo password. Type it (you won't see characters as you type) and press Enter. Curl will install, then the lab installer will start automatically.

The installer first asks "Have you already deployed this lab to AWS before? [y/N]". Type N and press Enter: you are deploying a new lab. (Answer y only when connecting a second computer to a lab you already built. That path auto-detects your AWS account and skips straight to the management menu, covered under Optional Commands below.)

A disclosure appears. Read it. At the bottom:

Do you understand and agree to the above requirements?

Type yes and press Enter.

Step 4: Wait, and answer the prompts

The installer runs for 10-20 minutes. The order:

  1. Installs CPython 3.13.14 from a pinned python-build-standalone tarball into /usr/local/python-3.13.14/, with symlinks in /usr/local/bin/ so python and python3 both resolve to the pinned interpreter (sudo prompts during install). If you have a Python 3 version other than 3.13.x already, the installer prints an ALL-CAPS warning and asks Install pinned 3.13 alongside / Continue with existing. I is the preferred choice. The lab is tested only with 3.13.x.
  2. Downloads the lab source tarball and extracts it to ~/cloudflare-psbc-lab
  3. Installs AWS CLI v2 via AWS' official bundled installer (downloads awscli-exe-linux-x86_64.zip from awscli.amazonaws.com, sudo to install to /usr/local)
  4. Asks you to confirm the target AWS account ID (paste the 12-digit number from the top-right of the AWS Console).
  5. Prompts for credentials:
    • AWS Access Key ID: paste from your rootkey.csv file or password manager. Echoes as you type.
    • AWS Secret Access Key: same source. Echoes as * per character so it doesn't show on screen.
    • AWS Region: press Enter to accept the default us-east-1, or type a different region code (e.g. us-west-2, eu-west-1).
  6. Auto-detects your laptop's public IP (no confirmation prompt; the security groups will allow RDP/SSH from this IP only).
  7. Asks for SSH key location and EC2 instance type defaults. Press Enter at each to accept.
  8. Runs the AWS preflight safety check (2-4 minutes on a brand-new AWS account)
  9. Installs Terraform via HashiCorp's static binary download (releases.hashicorp.com), sudo to install /usr/local/bin/terraform, only after preflight passes
  10. Runs terraform apply, which creates the VPCs and EC2 instances (4-6 minutes)
  11. Smoke-tests both Ubuntu VMs: the AcmeCorp website (port 80), intranet (port 8000), and the /status JSON on each (Ubuntu A advertises "origin":"primary", Ubuntu B "origin":"backup")

Important: Don't close the terminal while the installer is running.

Troubleshooting Deployment
What you seeWhat to do
command not found: curl Paste:
sudo apt-get update && sudo apt-get install -y curl
then re-run the install command.
apt-get not available on this Linux You're not on Debian / Ubuntu. Install Python 3 manually with your distro's package manager (dnf install, pacman -S) and re-run the install command.
user is not in the sudoers file Your account can't install software. Get an admin to add you to the sudo group, then re-run.
Installation complete

Your lab environment is deployed. The remaining sections are verification (Post Deployment Testing) and an optional permanent-copy walkthrough.

Here's what just happened

What the script did

The script is interactive. The very first thing it shows you is a full disclosure of what's about to happen. You type yes to continue or anything else to cancel. Then:

StepWhat happens
Python install

If you don't have CPython 3.13.x, it's installed via the official python.org installer.

If you don't have CPython 3.13.x, it's installed via brew install python@3.13.

If you don't have CPython 3.13.x, it's installed from a prebuilt python-build-standalone tarball into /usr/local/python-3.13.14/ (sudo).

Download the lab source The bootstrap downloads a small tarball of the lab code from this site and extracts it to ~/cloudflare-psbc-lab.
AWS CLI install + configure

If missing, AWS CLI v2 is installed from the MSI at awscli.amazonaws.com.

If missing, AWS CLI v2 is installed via brew install awscli.

If missing, AWS CLI v2 is installed via AWS' official bundled installer (zip from awscli.amazonaws.com, sudo install to /usr/local).

Then prompts you for the root access keys you created in Section 3 (Access Key ID echoes as you type; Secret Access Key is masked with * per character) plus an AWS region (default us-east-1). Writes these straight to ~/.aws/credentials and ~/.aws/config via aws configure set. No "output format" prompt. It's silently set to json, which Terraform doesn't use anyway.

Terraform uses this credential for the full lifecycle (deploy AND destroy). We deliberately don't create an intermediate IAM user, because any AWS resource created outside Terraform state cannot be cleaned up by terraform destroy.
Collect lab inputs Auto-detects your public IP (used to lock down RDP/SSH in the security groups. No confirmation prompt; the lab tells you ahead of time that this will happen). Prompts for SSH key location and EC2 instance types (press Enter for defaults). The lab name prefix (cloudflare-lab) and region (already set in aws configure) are not asked about again. Happens before preflight so the safety check can target the right region.
AWS account preflight The safety check. Scans your AWS account for any non-default resources, AWS Organizations membership, connected networks, production-style alias, CIDR conflicts. If anything is found, deploy is blocked.
Terraform install

Only after preflight passes: Terraform is installed from a static zip on releases.hashicorp.com.

Only after preflight passes: Terraform is installed from a static zip on releases.hashicorp.com (sudo install to /usr/local/bin/terraform).

Only after preflight passes: Terraform is installed from a static zip on releases.hashicorp.com (sudo install to /usr/local/bin/terraform).

Deploy terraform apply creates the VPCs, instances, and security groups. Takes 4-6 minutes.
Smoke test The script curls each Ubuntu public IP to confirm the AcmeCorp website (port 80), intranet (port 8000), and /status endpoint all respond. Ubuntu A should advertise "origin":"primary"; Ubuntu B "origin":"backup".

What landed in your AWS account

ResourceQuantityNotes
VPCs310.90.0.0/16 (Windows), 10.91.0.0/16 (Ubuntu A, primary origin), 10.92.0.0/16 (Ubuntu B, backup origin). No peering between any pair.
Internet gateways3One per VPC.
Subnets, route tables3 each10.90.0.0/24, 10.91.0.0/24, 10.92.0.0/24.
Security groups3RDP and SSH locked to your public IP. HTTP 80 / 8000 on both Ubuntus open to the world so the AcmeCorp site is reachable from anywhere until Lab 11.
EC2 key pair1Shared by both Ubuntus. Used by setup.py for emergency SSH access if password authentication fails. See the troubleshooting section for usage.
EC2 instances3
  • lab-windows: your Windows desktop (Windows Server 2022 at 10.90.0.90).
  • lab-ubuntu-a: the primary Ubuntu origin server (Ubuntu 24.04 at 10.91.0.91).
  • lab-ubuntu-b: the backup Ubuntu origin (Ubuntu 24.04 at 10.92.0.92). Used as the second Load Balancer pool member in the Application Security class; idle during the Zero Trust class.

Cost: new AWS accounts get $100 in AWS Free Tier credits and 6 months of free access, enough for a multi-day lab. Tear it all down with python setup.py destroy when class is over. If you don't convert to a paid plan when the 6 months are up, AWS closes the account, so there's no risk of forgotten lab resources accumulating charges.

5. Post Deployment Testing

Plug in your lab values

Type your AWS region first. The Console link further down on this page will auto-fill with it, so you can click into the AWS Console and find your VM IPs without typing anything. Saved in your browser (survives reloads).

Where to find your region: in the terminal window where you ran the install command, scroll up. Near the end of the output you'll see a line like aws_region = "us-east-1". Type that value (without quotes) into the box above.

The fastest way to find your VMs in the AWS Console: click the Copy button on the link below, paste it into your browser's address bar, and press Enter. The link was built from the AWS region you typed in above, so it drops you straight onto the EC2 Instances page in the correct region (no menu clicking). Sign in if AWS prompts you.

https://<region>.console.aws.amazon.com/ec2/home?region=<region>#Instances:

Once the page loads, you'll see three instance rows:

  • lab-windows: your Windows desktop.
  • lab-ubuntu-a: the primary Ubuntu origin server.
  • lab-ubuntu-b: the backup Ubuntu origin. Used as the second Load Balancer pool member in the Application Security class; idle during the Zero Trust class.

The Public IPv4 address column on each row is the value you'll paste below.

Don't want to use the link? Sign in to console.aws.amazon.com manually, use the region selector in the top-right to pick your region, then click EC2 → Instances. The "Finding your VMs in the AWS Console" subsection further down has a click-by-click walkthrough if you've never used the Console before.

Paste each VM's Public IPv4 address from the AWS Console below.

Finding your VMs in the AWS Console

If you've never used the AWS Console before, the EC2 page is where every virtual machine in your account is listed. To get there:

  1. Sign in to console.aws.amazon.com as your root user.
  2. In the top-right corner, click the region selector and pick the region you deployed to (the same one you typed at the AWS Region prompt during install, default us-east-1).
  3. In the search bar at the top, type EC2 and click the result, or click the Services menu → Compute → EC2.
  4. In the left sidebar, click Instances.

You'll see three rows, one per VM. The columns you care about:

ColumnWhat it tells you
Name
  • lab-windows: your Windows desktop.
  • lab-ubuntu-a: the primary Ubuntu origin server.
  • lab-ubuntu-b: the backup Ubuntu origin. Used as the second Load Balancer pool member in the Application Security class; idle during the Zero Trust class.
Instance stateShould be Running (green dot) on all three.
Public IPv4 addressThis is the IP you RDP / SSH / browse to. Copy with the icon next to the address.
Private IPv4 address10.90.0.90 for Windows, 10.91.0.91 for Ubuntu A (primary), 10.92.0.92 for Ubuntu B (backup). Matches the lab guides' placeholder IPs verbatim.

Smoke-test the lab before Lab 1

With the three public IPs from above, run all nine of these from your own laptop. The first group confirms remote access to each VM (SSH to both Ubuntus, RDP to Windows); the second group hits the three HTTP endpoints on each Ubuntu (website on :80, intranet on :8000, /status). Both Ubuntus serve identical AcmeCorp content; the only behavioral difference is what their /status JSON advertises. If any fails, see the troubleshooting block at the end of this subsection. Chasing a broken VM during Lab 1 is much harder than catching it now.

SSH and RDP tests

1. SSH into Ubuntu A (primary origin)

From a regular (non-elevated) PowerShell window on your laptop, using the built-in OpenSSH client. Run:

From Terminal on your laptop. Run:

From any terminal on your laptop. Run:

ssh cloudflare@<ubuntu-ip>

First connection prompts you to accept the host's SSH fingerprint (type yes and press Enter). Then prompts for the password. Type:

#Savetheinternet

The password won't echo as you type. That's normal. After Enter, you should land at a prompt like cloudflare@origin-primary:~$ (cloud-init renames the host to origin-primary on first boot). Type whoami (should say cloudflare), hostname (should say origin-primary), then exit to disconnect.

2. SSH into Ubuntu B (backup origin)

From a regular (non-elevated) PowerShell window on your laptop. Run:

From Terminal on your laptop. Run:

From any terminal on your laptop. Run:

ssh cloudflare@<ubuntu-b-ip>

Same password (#Savetheinternet) as Ubuntu A. You should land at cloudflare@origin-backup:~$. Type exit to disconnect.

3. RDP into the Windows desktop

Open Run (Win+R), type mstsc, Enter, or run from PowerShell:

mstsc /v:<windows-ip>

Install Microsoft Remote Desktop from the Mac App Store (free), open it, click Add PC, paste the Windows public IP below into "PC name", click Add, then double-click the new tile to connect.

<windows-ip>

Install remmina (sudo apt-get install remmina remmina-plugin-rdp), open it, new connection, protocol RDP, server set to:

<windows-ip>

At the credentials prompt, enter:

Usernamecloudflare: no domain prefix, no .\, no email format
Password#Savetheinternet: case-sensitive, starts with a literal #

Click Yes / Continue at the certificate warning (the Windows VM uses a self-signed RDP cert, expected). After a few seconds you should see the Windows desktop. The taskbar shows you signed in as cloudflare; open Start → Run → whoami to confirm.

HTTP tests

4. AcmeCorp website on Ubuntu A (port 80)

Open in your browser:

http://<ubuntu-ip>

Should render the AcmeCorp homepage (a landing page with the AcmeCorp brand). The HTML is served by nginx on Ubuntu A, pulled from the launcher Worker by cloud-init during the deploy.

5. AcmeCorp intranet on Ubuntu A (port 8000)

Same Ubuntu A, different vhost:

http://<ubuntu-ip>:8000

Visibly different intranet content. Confirming both ports work proves nginx is healthy and both vhosts are serving.

6. Ubuntu A /status identifies as primary

Open in your browser, or run from any terminal:

http://<ubuntu-ip>/status

Returns {"status":"healthy","origin":"primary"}. The origin field is templated into nginx at boot by cloud-init's origin_role variable.

7. AcmeCorp website on Ubuntu B (port 80)

Open in your browser:

http://<ubuntu-b-ip>

Renders the same AcmeCorp homepage as Ubuntu A: identical content, served from Ubuntu B's nginx after cloud-init pulled the same tarball from the launcher Worker.

8. AcmeCorp intranet on Ubuntu B (port 8000)

Same Ubuntu B, different vhost:

http://<ubuntu-b-ip>:8000

Renders the same intranet page as Ubuntu A.

9. Ubuntu B /status identifies as backup

Open in your browser, or run from any terminal:

http://<ubuntu-b-ip>/status

Returns {"status":"healthy","origin":"backup"}. This origin field is what makes the two pool members tell themselves apart at the curl level in the Application Security Lab 5 Load Balancer exercise.

Troubleshooting Tests

Click to expand
What you seeMost likely causeFix
HTTP test times out or returns nothing Ubuntu cloud-init is still running (nginx not yet started), OR your public IP changed since deploy and the SG is blocking you Wait 2-3 minutes after the installer's "smoke test" line. Then if still failing, run from your laptop:
python setup.py update-ip
ssh: connect to host <ip> port 22: Connection timed out Security group doesn't allow your current IP. Often happens if you SSH from the Windows VM (its public IP isn't allowlisted), or if your laptop's IP changed. SSH from your laptop, not from the Windows VM. If your laptop's IP changed, run this from the same laptop you ran the installer from:
python setup.py update-ip
ssh: connect to host <ip> port 22: Connection refused sshd was restarting when you tried to connect Wait 30 seconds and retry. If still failing, use the break-glass path below.
Permission denied (publickey,password) or password rejected for the cloudflare user The cloudflare user hadn't been created when sshd loaded, OR PasswordAuthentication yes hasn't taken effect Break-glass via the ubuntu user (below), then run:
sudo systemctl restart ssh
PowerShell: 'ssh' is not recognized as the name of a cmdlet Old Windows version without OpenSSH client From elevated PowerShell run this, then retry from a new PowerShell window (or RDP into the Windows VM, which has OpenSSH preinstalled, and SSH from there):
Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0
RDP: "Your credentials did not work" Typo in password, OR your client added a domain prefix to the username Username is literally cloudflare: no WINDOWS\, no .\cloudflare, no cloudflare@. Password is #Savetheinternet, case-sensitive, starts with #.
RDP: client immediately closes / blank screen Windows VM is still rebooting after cloud-init (renames the computer to win-desktop on first boot) Wait 3-5 minutes after the installer finished and retry.

Break-glass: SSH into either Ubuntu as the ubuntu user (key-based, always works)

The cloudflare user uses password authentication, which can fail if cloud-init's sshd restart hasn't completed. The ubuntu user has a separate path: it uses the SSH public key that setup.py registered with AWS during deploy (the one in ~/.ssh/id_ed25519.pub on your laptop, or auto-generated if you didn't have one). The same key authorizes both Ubuntu A and Ubuntu B.

Ubuntu A (primary), from your laptop:

ssh -i $env:USERPROFILE\.ssh\id_ed25519 ubuntu@<ubuntu-ip>
ssh -i ~/.ssh/id_ed25519 ubuntu@<ubuntu-ip>
ssh -i ~/.ssh/id_ed25519 ubuntu@<ubuntu-ip>

Ubuntu B (backup), same key, different IP:

ssh -i $env:USERPROFILE\.ssh\id_ed25519 ubuntu@<ubuntu-b-ip>
ssh -i ~/.ssh/id_ed25519 ubuntu@<ubuntu-b-ip>
ssh -i ~/.ssh/id_ed25519 ubuntu@<ubuntu-b-ip>

Once you're in (on either Ubuntu), useful diagnostic commands:

Is cloud-init finished?

sudo cloud-init status

What's the latest cloud-init activity?

sudo tail -40 /var/log/cloud-init-output.log

Is sshd running?

sudo systemctl status ssh

Does the lab user exist?

id cloudflare

Is password auth on?

grep -i passwordauth /etc/ssh/sshd_config /etc/ssh/sshd_config.d/*

Last-resort restart of sshd:

sudo systemctl restart ssh

Which origin role is this VM (primary or backup)?

curl -s http://localhost/status

Then exit and retry the password-based SSH from your laptop.

The installer dropped the lab into $env:USERPROFILE\cloudflare-psbc-lab (i.e. C:\Users\<your-name>\cloudflare-psbc-lab)~/cloudflare-psbc-lab (i.e. /Users/<your-name>/cloudflare-psbc-lab)~/cloudflare-psbc-lab (i.e. /home/<your-name>/cloudflare-psbc-lab). Every command below uses the full path to setup.py, so it works in any PowerShellTerminalshell window regardless of where you're currently cd'd. No setup, no shell config, no profile editing. Paste and run.

Optional Commands

Easiest path: re-run the same one-liner you installed with. It asks "Have you already deployed this lab to AWS before?"; answer y and it auto-detects your AWS account (no account-ID prompt), pulls your lab state down from S3, and drops you into a short menu ([1] Update my IP, [2] Destroy lab). This works from any computer that has the same AWS key, even one that never ran the deploy: the lab directory is mirrored to a private, encrypted S3 bucket in your own account after every change. You never type a file path.

The commands below are the power-user equivalents of those menu choices, for when you would rather type the command directly than use the menu.

Reprint the VM IPs, SSH / RDP commands, and every Terraform output

python "$env:USERPROFILE\cloudflare-psbc-lab\setup.py" show
python3 ~/cloudflare-psbc-lab/setup.py show
python3 ~/cloudflare-psbc-lab/setup.py show

Prints the formatted lab summary (Windows public IP, both Ubuntu public + private IPs, RDP / SSH / curl commands, AWS Console URL, destroy command), then dumps every raw Terraform output below it. Read-only; touches nothing in AWS.

Refresh security groups when your public IP changes

python "$env:USERPROFILE\cloudflare-psbc-lab\setup.py" update-ip
python3 ~/cloudflare-psbc-lab/setup.py update-ip
python3 ~/cloudflare-psbc-lab/setup.py update-ip

Your security groups only allow RDP / SSH from the public IP you had when you ran the installer. When you move networks (office, home, hotel, conference Wi-Fi) the IP changes and RDP / SSH stop working. This subcommand auto-detects your new public IP and rewrites the security groups. Add --ip 203.0.113.5/32 to override manually if auto-detect fails. Takes about 30 seconds.

Tear down everything (run this when class ends)

python "$env:USERPROFILE\cloudflare-psbc-lab\setup.py" destroy
python3 ~/cloudflare-psbc-lab/setup.py destroy
python3 ~/cloudflare-psbc-lab/setup.py destroy

Calls terraform destroy on every resource the installer created. Cleans up VPCs, EC2 instances, EBS volumes, security groups, internet gateways, EIPs, and the key pair. After this runs, the only thing left in AWS is your root access key, which you delete manually in the IAM Console.

None of these (commands above) need network access to GitHub or to this site. Once the install is done, the lab directory and the local Terraform state file (terraform.tfstate) have everything the lifecycle commands need. You can tear the lab down from anywhere in the world as long as your laptop can reach the AWS API.

Cleaning up after destroy: Terraform owns 100% of the AWS resources the installer creates, so python setup.py destroy is sufficient to clean up everything in AWS. The only piece Terraform can't manage is the root access key you created in Section 3 (it's the credential Terraform is authenticating with). After destroy, sign in to the AWS Console once more and delete that key: IAM → Users → your root user → Security credentials → Access keys → Actions → Delete. That single manual step is the entire post-class hygiene checklist.

6. Walking away with a permanent copy (optional)

After class ends, if you want a personal git copy of your lab so you can modify it, re-deploy it later, or share it with colleagues, push the directory to whatever git host you use:

cd ~/cloudflare-psbc-lab
git init
git add .
git commit -m "My CF PS Bootcamp lab"
git remote add origin <your-git-host-URL>
git push -u origin main

Any git host works: GitHub, GitLab, Gitee, BitBucket, AWS CodeCommit, self-hosted Gitea. Pick whatever your network reaches and your employer allows. The lab source is small (under 100 KB) and the terraform.tfstate file is what makes future terraform destroy calls work, so commit them together.