A step-by-step AWS lab with lots screenshots: create and delete an IAM user, host a site on S3, launch an EC2 server and run Jenkins on it, including the two errors I hit and how I fixed them.

Almost every AWS learning path starts with the same three moves: decide who is allowed to do what, store a file somewhere, and start a server. That is IAM, S3 and EC2, and nearly everything else on AWS sits on top of those three.

So I did all three for real, took a screenshot of every click, hit two errors along the way, fixed them, and then deleted everything at the end of each part so nothing was left running on my bill.

This post is the full walkthrough. By the end you will have:

  • Created an IAM user, signed in as that user, forced a password reset, and deleted the user
  • Created an S3 bucket, uploaded a website into it, made it public, and opened it in a browser
  • Launched an Ubuntu server on EC2, connected over SSH, installed Java and Jenkins, and unlocked Jenkins in the browser
  • Seen exactly what two common AWS errors look like, why they happen, and how to fix them
  • Cleaned up every resource so you do not pay for what you are not using

The plan

Part Service What I built How I cleaned up
1 IAM A user called 4thman with console access and a forced password reset Deleted the user
2 S3 A bucket called 4thman-bucket serving my CV website Emptied and deleted the bucket
3 EC2 An Ubuntu server called 4thman-server running Jenkins Stopped, then terminated the instance

Before you start

  • An AWS account. I worked as the root user for this lab (more on why that is not ideal in a moment).
  • A terminal with SSH. I used Git Bash on Windows. macOS and Linux terminals work the same way.
  • A small static website if you want to repeat the S3 part. I used my own CV website, a one-page site built from a Tooplate template called StackCraft: one index.html, one CSS file, one JavaScript file and an images folder. Any simple HTML, CSS and JS site will do.
  • A second browser window in incognito or private mode, for testing the IAM user.

A note on cost: everything here is small, but "small" only stays small if you clean up. That is why every part ends with a teardown. Free tier and credits differ between accounts, so check your own billing page.

Part 1: IAM, deciding who gets to do what

Why I did not just keep using root

Look at the top right corner of the next screenshot. It says root. The root user owns the account and can do absolutely anything, including closing it. You use it to get started, and then you stop using it. Day-to-day work belongs to an IAM user with only the permissions it needs. That principle is called least privilege, and IAM is the service that enforces it.

AWS Console Home, signed in as the root user

Step 1: Open IAM

In the search bar at the top of the console, type iam and pick IAM from the Services list.

Searching for IAM in the console search bar

Step 2: Create the user

In the sidebar, open IAM users. The count says (0), so this account has no IAM users yet. Click Create user.

IAM users page with no users yet and the Create user button

Step 3: Fill in the user details

This screen has four decisions on it:

  1. User name: 4thman
  2. Provide user access to the AWS Management Console: ticked, because I want this user to be able to sign in on the website and not just use the CLI
  3. Console password: I chose Custom password so I could pick my own. The rules are at least 8 characters and a mix of at least three of: uppercase, lowercase, numbers and symbols.
  4. Users must create a new password at next sign-in: ticked. AWS marks this as recommended, and it is a good habit. Whoever receives the temporary password has to replace it with their own on first login.

User details: name, console access, custom password and forced reset

Step 4: Permissions

The next step offers three ways to give permissions: add the user to a group, copy permissions from another user, or attach policies directly. AWS recommends groups, because you manage "the Developers" once instead of managing every developer separately.

For this lab I did not want this user to be able to do anything yet, so I left the defaults alone and clicked Next.

Set permissions step, left on the defaults

Step 5: Review and create

The review page shows the choices I made: custom password, password reset required. The part I want you to notice is Permissions summary. I never attached a policy, but one is listed: IAMUserChangePassword. AWS adds it automatically when you force a password reset, because the user needs permission to change their own password. It lets them do that and nothing else, which is exactly least privilege at work.

Click Create user.

Review page showing IAMUserChangePassword as the only policy

Step 6: Save the sign-in details

The next screen is the only time AWS shows you the console password. It also gives you the sign-in URL, which follows this pattern:

https://<your-account-id>.signin.aws.amazon.com/console 

Copy the details or click Download .csv file before leaving this page.

User created, with the console sign-in URL, user name and password

Step 7: Sign in as the new user

I opened an incognito window so I could stay signed in as root in my normal window. On the IAM user sign-in page I entered the account ID, the user name 4thman and the temporary password.

Signing in as the new IAM user in an incognito window

Straight after clicking Sign in, AWS showed this:

Password reset screen shown at first sign-in

That message says the password "has expired or requires a reset", which sounds like a problem. It is not. It is the "must create a new password" option from Step 3 doing its job. I entered the old password once, the new one twice, watched the green Matches appear, and clicked Confirm Password Change.

Password reset successful

Then I signed in again with the new password and landed on the console as 4thman. Notice the account menu at the top right now shows the user name instead of root.

Console Home as the 4thman IAM user

One thing to watch: this console opened in Europe (Stockholm), because the sign-in page had redirected me to a Stockholm sign-in endpoint. IAM users are global, so that does not affect the user. But most other services are regional, so always check the region selector before you create anything.

This user can open the console but cannot do much with it. The only policy attached lets them change their own password. That is the point of starting with nothing and adding permissions deliberately.

Step 8: Delete the user

Back in my root window, the IAM users list now shows 4thman with real activity: last activity and console sign-in a couple of minutes ago, password age of a few minutes. I ticked the box next to the user and clicked Delete user.

Selecting the user and clicking Delete user

AWS makes you type confirm before it deletes anything. It is a small speed bump on purpose. Deleting a user also removes its credentials, so there is no undo.

Typing confirm to delete the user

IAM done. One user created, tested and removed.

Part 2: S3, storing files and serving a website

What S3 actually is

S3 is object storage. You create a bucket (a container), and everything you put in it is an object. Two details are worth knowing before you start:

  • Bucket names are unique across all AWS accounts in the world, not just yours.
  • S3 has no real folders. When the console shows an images/ folder, that is just a shared prefix in the file names. You will see this when the folder appears in the object list later.

Step 1: Find S3 and create a bucket

Search for s3 buckets and open S3. If you have no buckets yet, the landing page shows a big Create bucket button.

Searching for S3 in the console

The S3 landing page and its Create bucket button

Step 2: Name the bucket

I confirmed the region was US East (N. Virginia) us-east-1, kept the type as General purpose, kept the default global namespace, and named the bucket 4thman-bucket. Bucket names must be 3 to 63 characters and can only contain lowercase letters, numbers, periods and hyphens.

Region, bucket type and the bucket name 4thman-bucket

Step 3: Leave the safe defaults alone

Scroll down and you will see two settings I deliberately did not change:

  • Object Ownership: ACLs disabled (recommended). Access is controlled through policies only.
  • Block all public access: ticked. All four sub-settings are on.

AWS builds buckets private by default, and that is a good thing. These two defaults are also the reason I hit an error in a few minutes, so keep them in mind.

Object Ownership with ACLs disabled and Block all public access ticked

At the bottom, default encryption is already on (SSE-S3, with a bucket key enabled). I left it and clicked Create bucket.

Default encryption settings and the final Create bucket button

Step 4: Upload the CV website

The bucket now exists and says Objects (0). Click Upload.

Bucket created and empty, with the Upload button

I added the four files that make up my CV website: index.html, tooplate-stackcraft-script.js, tooplate-stackcraft-style.css, and about.jpg inside an images/ folder. The upload summary reports 4 files totalling 229.1 KB with 0 failures. Notice the destination, s3://4thman-bucket, and that about.jpg shows images/ in the Folder column.

Upload succeeded: 4 files, 229.1 KB, 0 failed

The files are in, but because the bucket is private, nobody on the internet can open them yet. Time to change that.

Error 1: "Access denied" when making the files public

I went back to the object list, ticked every file and folder, and opened the Actions menu to choose Make public using ACL.

)

Selecting all objects and opening the Actions menu

The next page shows two banners, and both matter:

  • A green one: Successfully enabled ACLs (Bucket owner preferred). Because I created the bucket with ACLs disabled, and "Make public" works by setting an object ACL, the console switched the ownership setting for me.
  • A blue one: Public access is blocked because Block Public Access settings are turned on for this bucket.

I read the green one, ignored the blue one, and clicked Make public.

First Make public attempt, with the ACL banner and the Block Public Access warning

The result, for all four objects:

Failed to edit public access: Access denied on all four objects

Failed to edit public access: Access denied.

Reading the error properly

My first instinct with "Access denied" was to check my permissions. But I was the root user, so permissions were not the problem. The blue banner had already told me the cause.

There are two separate gates on public access in S3:

  1. Object ACLs and ownership, which decide how an individual object can be made public. The console fixed this one for me.
  2. Block Public Access, a bucket-level safety switch that overrides ACLs and bucket policies. It was still on, and it wins every time.

Even if the object says "public", Block Public Access says "no".

The fix

Open the bucket and go to the Permissions tab.

Opening the Permissions tab of the bucket

Under Block public access (bucket settings) it says Block all public access: On. Click Edit.

)

Block all public access is On, with the Edit button

Untick Block all public access. That clears all four boxes underneath it. Click Save changes and confirm the warning S3 shows you.

)

Block all public access unticked, then Save changes

Now repeat the original action. Back on the Objects tab I selected everything again and opened Actions.

Selecting all objects again and opening Actions

On the Make public page, I clicked Make public again. This time there was no blue warning.

Second Make public attempt, with no blocking warning

The summary now reads: Successfully edited public access, 4 objects, 229.1 KB, 0 failed.

Successfully edited public access: 4 objects, 0 failed

See it live

I ticked index.html only and clicked Copy URL. The console confirmed with Object URL Copied.

Copying the object URL of index.html

Pasting it into a new tab loaded my full CV website straight from S3, with my name, role, skills, work and experience sections all in place. The URL has this shape:

https://<bucket-name>.s3.<region>.amazonaws.com/index.html 

The CSS, JavaScript and image all loaded as well, because I made every object public, not just the HTML file.

My CV website loading straight from S3

A word of caution. Opening a whole bucket to the internet is fine for a throwaway demo. For a real site, keep the bucket private and put CloudFront in front of it, or at least use a bucket policy that only allows reading the specific objects you want public.

Cleanup: empty the bucket, then delete it

S3 will not delete a bucket that still has objects in it. I selected everything and clicked Delete.

Selecting all objects and clicking Delete

S3 asks you to type permanently delete to confirm. This cannot be undone. (The Empty button on the bucket list does the same job in one step.)

Typing permanently delete to confirm

With the bucket empty, I went back to the bucket list, selected 4thman-bucket and clicked Delete.

Selecting the empty bucket and clicking Delete

To confirm, I typed the bucket name. Read the warnings on this page: once a bucket is deleted, its name is released and another AWS user can claim it.

Typing the bucket name to confirm deletion

S3 done. A bucket created, a website served from it, one error fixed, everything removed.

Part 3: EC2, a server in the cloud, running Jenkins

The vocabulary you need first

Term What it means
EC2 instance A virtual server running in AWS
AMI The image the server starts from: operating system plus preinstalled software. I used an Ubuntu image
Instance type The size of the machine. I used t3.micro (2 vCPU, 1 GiB memory)
Key pair A public and private key used to log in over SSH. AWS keeps the public half. You keep the private .pem file
Security group A virtual firewall around the instance that decides which ports accept traffic
Jenkins An open source automation server, widely used to build, test and deploy code (CI/CD)

Step 1: Launch the wizard

Search for ec2 and open EC2. (The green banner partly visible behind the search box is the bucket deletion confirming.)

Searching for EC2 in the console

On the EC2 landing page, click Launch instance.

The EC2 landing page and its Launch instance button

AWS then offered a choice of walkthroughs. I picked Learn about EC2 and launch test instances. That is why a Tutorial panel appears on the right side of the next screenshots: it lists the steps and, handily, ends with a reminder to terminate your instance.

Choosing the Learn about EC2 walkthrough

Step 2: Name it and pick an image

I named the instance 4thman-server, chose Ubuntu from the Quick Start images, and left the number of instances at 1. The Summary panel on the right tracks everything you choose: image, instance type, firewall and storage.

Naming the instance, choosing Ubuntu and setting the number of instances

Step 3: Instance type and key pair

I picked t3.micro. Scrolling down to Key pair (login), I clicked Create new key pair.

Instance type and the Create new key pair link

I named it 4thman-key, kept RSA as the type and .pem as the format (the format for OpenSSH, which is what Git Bash uses), and clicked Create key pair.

Creating the key pair 4thman-key as RSA and .pem

The browser downloaded 4thman-key.pem immediately.

4thman-key.pem downloaded by the browser

Guard this file. AWS only keeps the public half of the key. This download is the only copy of the private half. Lose it and you cannot SSH into the server. Never commit it to GitHub.

Step 4: Network and security group

In Network settings:

  • Auto-assign public IP: Enable, so the server is reachable from the internet
  • Create security group (a new firewall for this instance)
  • Allow SSH traffic from: Anywhere (0.0.0.0/0), so I can log in
  • Allow HTTP traffic from the internet: ticked
  • Allow HTTPS traffic from the internet: left unticked

AWS shows a yellow warning that 0.0.0.0/0 means every IP address. For a short-lived lab that is acceptable. For anything longer, choose My IP instead of Anywhere.

Network settings: public IP, new security group, SSH and HTTP allowed

Step 5: Review and launch

Storage stayed at the default 8 GiB gp3 root volume. The Summary panel showed one instance, the Ubuntu image, t3.micro, a new security group and one 8 GiB volume. I clicked Launch instance.

Summary panel and the Launch instance button

A note on the screenshots in this section. Some of the launch screenshots (the key pair, security group, launch summary, success banner and running view) were captured on an earlier launch of the same setup, so the instance size, instance ID and IP address differ from the server used in the SSH and Jenkins steps. The clicks are identical. When I talk about the server's public IP from here on, use the one on your own instance page.

AWS replied with a green Success banner and the new instance ID. The page also suggests next steps, including Create billing usage alerts. Next time I will set that up before launching anything.

Instance launch initiated successfully

On the Instances page the new server went to Running, and the status checks passed. In the details panel, copy the Public IPv4 address. You need it to connect.

Instance running, with its public IPv4 address

Step 6: Lock down the key file and connect over SSH

While you are here, fix the key's permissions. SSH refuses to use a private key that other users on the machine can read, so restrict it:

cd Downloads ls -l 4thman-key.pem chmod 400 4thman-key.pem ls -l 4thman-key.pem 

chmod 400 means read-only for the file owner and nothing for anyone else. In the screenshot the listing goes from -rw-r--r-- to -r--r--r--. Because this is Git Bash on a Windows drive, permission bits do not map exactly like on Linux, but SSH accepted the key anyway.

Restricting the key file with chmod 400

Now connect. The default user on Ubuntu images is ubuntu:

ssh -i "4thman-key.pem" ubuntu@<your-public-ip> 

The first time, SSH asks whether you trust this host. Type yes. The Ubuntu welcome banner appears and the prompt changes to ubuntu@ip-172-31-25-206:~$, which means every command from now on runs on the server in AWS and not on my laptop.

Connected to the server over SSH

Step 7: Update the server

sudo apt update sudo apt upgrade -y 

apt update refreshes the list of available packages. apt upgrade -y installs updates without asking for confirmation. On a fresh image there was nothing pending, and apt reported All packages are up to date.

sudo apt update and sudo apt upgrade -y

Step 8: Install Java

Jenkins is a Java application, so Java comes first. fontconfig is a small dependency Jenkins needs for rendering fonts.

Source:https://dev.to/4thman/hands-on-aws-spin-up-iam-s3-and-a-jenkins-server-on-ec2-and-fix-what-breaks-45h4

Related posts